diff --git a/gdd.md b/gdd.md index 6638cae..fa2b769 100644 --- a/gdd.md +++ b/gdd.md @@ -8,6 +8,11 @@ * Education * Supply chains +## Clock + +Ticks can be applied manually or they can be applied over time. +Add a tick counter as a UI element. + ## Grid Add a 10x10 grid. @@ -17,13 +22,6 @@ Tiles are highlighted a different colour when the player hovers over them with t Tiles in the grid contain floating point values. These values propogate to neighbouring cells a tick happens. -Ticks can be applied manually or they can be applied over time. -Add a tick counter as a UI element. - -* [ ] understand different options for representing the rate of diffusion - -Diffusing should use 'pull' operations; tiles should pull values from surrounding tiles. -This is an attempt to think forwards to parallelising computation of diffusiong - ideally using the GPU. #### Diffusion Algorithm @@ -46,6 +44,20 @@ private float TransferHeat(float t0, float alpha, float nx, float ny, float px, } ``` +Diffusing uses 'pull' operations; tiles pull values from surrounding tiles. +This is an attempt to think forwards to parallelising computation of diffusiong - ideally using the GPU. + +## Overlays + +The current overlay can be cycled with a UI button or by pressing `tab`. +Overlays display tile information by displaying tiles in different colours. +Normally, tile colours are displayed depending on their type and health. +This is the 'normal' view with no overlay. + +Other overlays include: + +* [Heat overlay](#heat-overlay) + ## Build Mode Build mode can be entered by pressing the `b` key. @@ -71,8 +83,12 @@ In build mode, the player can click on a wile tile to turn it into a developed t Developed tiles produce heat while wild tiles absorb it. Every tile has a floating-point heat value which diffuses to its neighbours. -Add a heat map view which can be toggled or cycled to. + +### Heat overlay + +A heat map view can be toggled or cycled to. It is possible to cycle the view using a UI element. +Tile colours are displayed as a value along a gradient from cold to hot tiles. ## Tile Health diff --git a/half-earth/nodes/game.tscn b/half-earth/nodes/game.tscn index 40b7b54..023b6a7 100644 --- a/half-earth/nodes/game.tscn +++ b/half-earth/nodes/game.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=2] +[gd_scene load_steps=13 format=2] [ext_resource path="res://nodes/grid.tscn" type="PackedScene" id=1] [ext_resource path="res://nodes/grid_cursor.tscn" type="PackedScene" id=2] @@ -7,8 +7,10 @@ [ext_resource path="res://nodes/interaction_modes/interaction_mode.tscn" type="PackedScene" id=5] [ext_resource path="res://nodes/ui/build_mode_ui.tscn" type="PackedScene" id=6] [ext_resource path="res://nodes/interaction_modes/build_mode.tscn" type="PackedScene" id=7] -[ext_resource path="res://resources/tile_types/wild.tres" type="Resource" id=8] +[ext_resource path="res://nodes/ui/overlays UI.tscn" type="PackedScene" id=8] [ext_resource path="res://nodes/ui/mode_selection_ui.tscn" type="PackedScene" id=9] +[ext_resource path="res://nodes/overlays.tscn" type="PackedScene" id=10] +[ext_resource path="res://resources/overlays/overlays.tres" type="Resource" id=11] [sub_resource type="Curve" id=1] _data = [ Vector2( 0, 0 ), 0.0, 5.0, 0, 0, Vector2( 0.5, 1 ), 0.0, 0.0, 0, 0, Vector2( 1, 0 ), -4.0, 0.0, 0, 0 ] @@ -18,21 +20,24 @@ _data = [ Vector2( 0, 0 ), 0.0, 5.0, 0, 0, Vector2( 0.5, 1 ), 0.0, 0.0, 0, 0, Ve [node name="Clock" parent="." instance=ExtResource( 4 )] _autoTick = true -[node name="Grid" parent="." instance=ExtResource( 1 )] +[node name="World Grid" parent="." instance=ExtResource( 1 )] DiffusionCoefficient = 0.1 -_startTileTypeResource = ExtResource( 8 ) [node name="Cursor" parent="." instance=ExtResource( 2 )] -Grid = NodePath("../Grid") +Grid = NodePath("../World Grid") _pulseShape = SubResource( 1 ) [node name="Interaction Modes" parent="." instance=ExtResource( 5 )] _buildModePath = NodePath("Build Mode") [node name="Build Mode" parent="Interaction Modes" instance=ExtResource( 7 )] -_gridPath = NodePath("../../Grid") +_gridPath = NodePath("../../World Grid") _cursorPath = NodePath("../../Cursor") +[node name="Overlays" parent="." instance=ExtResource( 10 )] +_overlaysResource = ExtResource( 11 ) +_worldGridPath = NodePath("../World Grid") + [node name="UI" type="Control" parent="."] anchor_right = 1.0 anchor_bottom = 1.0 @@ -45,15 +50,19 @@ _buildModePath = NodePath("../../Interaction Modes/Build Mode") [node name="Mode Selection" parent="UI" instance=ExtResource( 9 )] +[node name="Overlays UI" parent="UI" instance=ExtResource( 8 )] +_overlaysPath = NodePath("../../Overlays") + [connection signal="OnPauseChanged" from="Clock" to="UI/Debug" method="_on_Clock_OnPauseChanged"] -[connection signal="OnTick" from="Clock" to="Grid" method="_on_Clock_OnTick"] +[connection signal="OnTick" from="Clock" to="World Grid" method="_on_Clock_OnTick"] [connection signal="OnTick" from="Clock" to="UI/Debug" method="_on_Clock_OnTick"] [connection signal="OnInteractionModeChanged" from="Interaction Modes" to="Cursor" method="_on_Interaction_Mode_OnInteractionModeChanged"] [connection signal="OnInteractionModeChanged" from="Interaction Modes" to="UI/Debug" method="_on_Interaction_Mode_OnInteractionModeChanged"] [connection signal="OnModeEntered" from="Interaction Modes/Build Mode" to="Interaction Modes" method="_on_Build_Mode_OnModeEntered"] [connection signal="OnModeEntered" from="Interaction Modes/Build Mode" to="UI/Mode Selection" method="EnableBuildMode"] [connection signal="OnModeExited" from="Interaction Modes/Build Mode" to="Interaction Modes" method="_on_Build_Mode_OnModeExited"] +[connection signal="OnModeExited" from="Interaction Modes/Build Mode" to="UI/Mode Selection" method="Enable"] [connection signal="SelectedTileTypeChanged" from="Interaction Modes/Build Mode" to="UI/Build Mode" method="UpdateButtonToggleState"] +[connection signal="OverlayCycled" from="Overlays" to="UI/Overlays UI" method="UpdateOverlayText"] [connection signal="OnExit" from="UI/Build Mode" to="Interaction Modes" method="Reset"] -[connection signal="OnExit" from="UI/Build Mode" to="UI/Mode Selection" method="Enable"] [connection signal="BuildModeEnabled" from="UI/Mode Selection" to="Interaction Modes/Build Mode" method="Enable"] diff --git a/half-earth/nodes/grid.tscn b/half-earth/nodes/grid.tscn index 3a19d57..3a7f10f 100644 --- a/half-earth/nodes/grid.tscn +++ b/half-earth/nodes/grid.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=3 format=2] +[gd_scene load_steps=4 format=2] [ext_resource path="res://scripts/WorldGrid.cs" type="Script" id=1] [ext_resource path="res://nodes/tile.tscn" type="PackedScene" id=2] +[ext_resource path="res://resources/tiles/tiles.tres" type="Resource" id=3] [node name="Grid" type="Node2D"] script = ExtResource( 1 ) TileScene = ExtResource( 2 ) -Size = 10 CellSize = 64 +_tileGridResource = ExtResource( 3 ) diff --git a/half-earth/nodes/interaction_modes/build_mode.tscn b/half-earth/nodes/interaction_modes/build_mode.tscn index 4c2ca0e..c3f0f36 100644 --- a/half-earth/nodes/interaction_modes/build_mode.tscn +++ b/half-earth/nodes/interaction_modes/build_mode.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=3 format=2] [ext_resource path="res://scripts/interaction_modes/BuildMode.cs" type="Script" id=1] -[ext_resource path="res://resources/tile_types/tile_type_collections/buildable_tiles.tres" type="Resource" id=2] +[ext_resource path="res://resources/tiles/tile_types/tile_type_collections/buildable_tiles.tres" type="Resource" id=2] [node name="Build Mode" type="Node"] script = ExtResource( 1 ) diff --git a/half-earth/nodes/overlays.tscn b/half-earth/nodes/overlays.tscn new file mode 100644 index 0000000..1e6405b --- /dev/null +++ b/half-earth/nodes/overlays.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=5 format=2] + +[ext_resource path="res://scripts/overlays/Overlays.cs" type="Script" id=1] +[ext_resource path="res://resources/overlays/heat_overlay.tres" type="Resource" id=2] +[ext_resource path="res://resources/overlays/no_overlay.tres" type="Resource" id=3] +[ext_resource path="res://resources/tiles/tiles.tres" type="Resource" id=4] + +[node name="Overlays" type="Node"] +script = ExtResource( 1 ) +_cycleOverlayKey = 16777218 +_overlayResources = [ ExtResource( 3 ), ExtResource( 2 ) ] +_tileGridResource = ExtResource( 4 ) diff --git a/half-earth/nodes/ui/mode_selection_ui.tscn b/half-earth/nodes/ui/mode_selection_ui.tscn index 1916be8..e80162e 100644 --- a/half-earth/nodes/ui/mode_selection_ui.tscn +++ b/half-earth/nodes/ui/mode_selection_ui.tscn @@ -14,6 +14,6 @@ margin_left = 2.0 margin_top = 562.0 margin_right = 38.0 margin_bottom = 598.0 -text = "Build Mode" +text = "[B] Build Mode" [connection signal="pressed" from="Build Mode" to="." method="EnableBuildMode"] diff --git a/half-earth/nodes/ui/overlays UI.tscn b/half-earth/nodes/ui/overlays UI.tscn new file mode 100644 index 0000000..6489675 --- /dev/null +++ b/half-earth/nodes/ui/overlays UI.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://scripts/ui/OverlaysUI.cs" type="Script" id=1] + +[node name="Overlays UI" type="Node"] +script = ExtResource( 1 ) +_cycleOverlayButtonPath = NodePath("Button") + +[node name="Button" type="Button" parent="."] +anchor_top = 1.0 +anchor_bottom = 1.0 +margin_top = -90.0 +margin_right = 42.0 +margin_bottom = -52.0 +grow_vertical = 0 +text = "[Tab] Cycle Overlay" + +[connection signal="pressed" from="Button" to="." method="CycleOverlay"] diff --git a/half-earth/resources/overlays/heat_overlay.tres b/half-earth/resources/overlays/heat_overlay.tres new file mode 100644 index 0000000..5f1c269 --- /dev/null +++ b/half-earth/resources/overlays/heat_overlay.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" load_steps=2 format=2] + +[ext_resource path="res://scripts/overlays/HeatOverlay.cs" type="Script" id=1] + +[resource] +script = ExtResource( 1 ) +_coldColor = Color( 0, 0, 0, 1 ) +_hotColor = Color( 1, 0.654902, 0, 1 ) diff --git a/half-earth/resources/overlays/no_overlay.tres b/half-earth/resources/overlays/no_overlay.tres new file mode 100644 index 0000000..d4a783f --- /dev/null +++ b/half-earth/resources/overlays/no_overlay.tres @@ -0,0 +1,6 @@ +[gd_resource type="Resource" load_steps=2 format=2] + +[ext_resource path="res://scripts/overlays/NoOverlay.cs" type="Script" id=1] + +[resource] +script = ExtResource( 1 ) diff --git a/half-earth/resources/overlays/overlays.tres b/half-earth/resources/overlays/overlays.tres new file mode 100644 index 0000000..6eb6676 --- /dev/null +++ b/half-earth/resources/overlays/overlays.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" load_steps=4 format=2] + +[ext_resource path="res://scripts/overlays/OverlayCollection.cs" type="Script" id=1] +[ext_resource path="res://resources/overlays/no_overlay.tres" type="Resource" id=2] +[ext_resource path="res://resources/overlays/heat_overlay.tres" type="Resource" id=3] + +[resource] +script = ExtResource( 1 ) +_resources = [ ExtResource( 2 ), ExtResource( 3 ) ] diff --git a/half-earth/resources/tile_types/tile_type_collections/buildable_tiles.tres b/half-earth/resources/tile_types/tile_type_collections/buildable_tiles.tres deleted file mode 100644 index 0491e19..0000000 --- a/half-earth/resources/tile_types/tile_type_collections/buildable_tiles.tres +++ /dev/null @@ -1,8 +0,0 @@ -[gd_resource type="Resource" load_steps=3 format=2] - -[ext_resource path="res://scripts/TileTypeCollection.cs" type="Script" id=1] -[ext_resource path="res://resources/tile_types/developed.tres" type="Resource" id=2] - -[resource] -script = ExtResource( 1 ) -_tileTypeResources = [ ExtResource( 2 ) ] diff --git a/half-earth/resources/tile_types/developed.tres b/half-earth/resources/tiles/tile_types/developed.tres similarity index 64% rename from half-earth/resources/tile_types/developed.tres rename to half-earth/resources/tiles/tile_types/developed.tres index 011fa7b..8f5f1d1 100644 --- a/half-earth/resources/tile_types/developed.tres +++ b/half-earth/resources/tiles/tile_types/developed.tres @@ -1,10 +1,10 @@ [gd_resource type="Resource" load_steps=2 format=2] -[ext_resource path="res://scripts/TileType.cs" type="Script" id=1] +[ext_resource path="res://scripts/tile/TileType.cs" type="Script" id=1] [resource] script = ExtResource( 1 ) Name = "Developed" -BuildLabel = "[D]eveloped" Key = 68 Color = Color( 0.372549, 0.372549, 0.372549, 1 ) +HeatGeneration = 0.4 diff --git a/half-earth/resources/tiles/tile_types/tile_type_collections/buildable_tiles.tres b/half-earth/resources/tiles/tile_types/tile_type_collections/buildable_tiles.tres new file mode 100644 index 0000000..af140a6 --- /dev/null +++ b/half-earth/resources/tiles/tile_types/tile_type_collections/buildable_tiles.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" load_steps=3 format=2] + +[ext_resource path="res://scripts/tile/TileTypeCollection.cs" type="Script" id=1] +[ext_resource path="res://resources/tiles/tile_types/developed.tres" type="Resource" id=2] + +[resource] +script = ExtResource( 1 ) +_resources = [ ExtResource( 2 ) ] diff --git a/half-earth/resources/tile_types/wild.tres b/half-earth/resources/tiles/tile_types/wild.tres similarity index 62% rename from half-earth/resources/tile_types/wild.tres rename to half-earth/resources/tiles/tile_types/wild.tres index 16ebc36..370bb83 100644 --- a/half-earth/resources/tile_types/wild.tres +++ b/half-earth/resources/tiles/tile_types/wild.tres @@ -1,10 +1,10 @@ [gd_resource type="Resource" load_steps=2 format=2] -[ext_resource path="res://scripts/TileType.cs" type="Script" id=1] +[ext_resource path="res://scripts/tile/TileType.cs" type="Script" id=1] [resource] script = ExtResource( 1 ) Name = "Wild" -BuildLabel = "[W]ild" Key = 87 Color = Color( 0, 0.545098, 0.0196078, 1 ) +HeatGeneration = -0.2 diff --git a/half-earth/resources/tiles/tiles.tres b/half-earth/resources/tiles/tiles.tres new file mode 100644 index 0000000..288c93f --- /dev/null +++ b/half-earth/resources/tiles/tiles.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" load_steps=3 format=2] + +[ext_resource path="res://scripts/tile/TileGrid.cs" type="Script" id=1] +[ext_resource path="res://resources/tiles/tile_types/wild.tres" type="Resource" id=2] + +[resource] +script = ExtResource( 1 ) +Size = 10 +_startTileTypeResource = ExtResource( 2 ) diff --git a/half-earth/scripts/ReadOnlyResourceList.cs b/half-earth/scripts/ReadOnlyResourceList.cs new file mode 100644 index 0000000..4b9f2fb --- /dev/null +++ b/half-earth/scripts/ReadOnlyResourceList.cs @@ -0,0 +1,33 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; + +public abstract class ReadOnlyResourceList : Resource, IReadOnlyList where T : Resource +{ + [Export] + private List _resources = new List(); + private List Collection + { + get + { + if (_collection != null) + return _collection; + + _collection = new List(); + foreach (var resource in _resources) + { + _collection.Add((T)resource); + } + return _collection; + } + } + private List _collection = null; + + public T this[int index] => Collection[index]; + + public int Count => Collection.Count; + + public IEnumerator GetEnumerator() => Collection.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator(); +} \ No newline at end of file diff --git a/half-earth/scripts/TileTypeCollection.cs b/half-earth/scripts/TileTypeCollection.cs deleted file mode 100644 index d30c6e8..0000000 --- a/half-earth/scripts/TileTypeCollection.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Godot; -using System; -using System.Collections; -using System.Collections.Generic; - -public class TileTypeCollection : Resource, IReadOnlyList -{ - [Export] - private List _tileTypeResources = new List(); - - private List TileTypes - { - get - { - if (_tileTypes != null) - return _tileTypes; - - _tileTypes = new List(); - - foreach (var resource in _tileTypeResources) - { - if (!(resource is TileType)) - throw new InvalidCastException($"{resource} must be a {typeof(TileType)}"); - - _tileTypes.Add((TileType)resource); - } - - return _tileTypes; - } - } - private List _tileTypes = null; - - public TileType this[int index] => TileTypes[index]; - - public int Count => TileTypes.Count; - - public IEnumerator GetEnumerator() - { - return TileTypes.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return TileTypes.GetEnumerator(); - } -} diff --git a/half-earth/scripts/WorldGrid.cs b/half-earth/scripts/WorldGrid.cs index 661b202..6a7b01a 100644 --- a/half-earth/scripts/WorldGrid.cs +++ b/half-earth/scripts/WorldGrid.cs @@ -7,11 +7,8 @@ public class WorldGrid : Node2D private PackedScene TileScene { get; set; } [Export] - private Resource _startTileTypeResource; - private TileType _startTileType; - - [Export] - public int Size { get; set; } + private Resource _tileGridResource; + private TileGrid _tileGrid; [Export] public int CellSize { get; set; } @@ -19,65 +16,50 @@ public class WorldGrid : Node2D [Export] public float DiffusionCoefficient { get; set; } = 0.01f; - private Tile[,] _tiles; - private float[,] _nextValues = null; + private float[] _nextValues = null; + + public struct TileView + { + public ShaderMaterial material; + } + private TileView[] _tileViews; // Called when the node enters the scene tree for the first time. public override void _Ready() { - _startTileType = (TileType)_startTileTypeResource; + _tileGrid = (TileGrid)_tileGridResource; - GenerateGrid(Size); - } - - public override void _Process(float delta) - { - base._Process(delta); - - for (int x = 0; x < Size; x++) - { - for (int y = 0; y < Size; y++) - { - var tile = _tiles[x, y]; - var material = tile.material; - material.SetShaderParam("t", tile.value); - } - } - } - - public bool IsInBounds(int x, int y) - { - return x >= 0 && x < Size && y >= 0 && y < Size; - } - - public void ToggleTileHighlight(int x, int y) - { - if (!IsInBounds(x, y)) - return; - - _tiles[x, y].isHighlighted ^= true; - } - - public void SetTileType(int x, int y, TileType tileType) - { - if (!IsInBounds(x, y)) - return; - - GD.Print($"set ({x}, {y}) to {tileType}"); - _tiles[x, y].type = tileType; - _tiles[x, y].material.SetShaderParam("lowColor", tileType.Color); + GenerateCanvasItems(_tileGrid); } + #region Positioning public void GetGridPos(Vector2 position, out int x, out int y) { x = Mathf.FloorToInt(position.x / CellSize); y = Mathf.FloorToInt(position.y / CellSize); } + public bool IsInBounds(int x, int y) => _tileGrid.IsInBounds(x, y); + #endregion + + #region Interaction + public void SetTileType(int x, int y, TileType tileType) + { + if (!_tileGrid.IsInBounds(x, y)) + return; + + _tileGrid.MapPosition(x, y, out var idx); + + var tile = _tileGrid[idx]; + tile.type = tileType; + _tileGrid[x, y] = tile; + + _tileViews[idx].material.SetShaderParam("lowColor", tileType.Color); + } + #endregion + private void Clear() { - _tiles = null; - int children = this.GetChildCount(); GD.Print(children); for (int i = 0; i < children; i++) @@ -85,43 +67,46 @@ public class WorldGrid : Node2D var child = this.GetChild(i); child.QueueFree(); } + + _tileViews = null; } - private void GenerateGrid(int size) + private void GenerateCanvasItems(TileGrid grid) { this.Clear(); - _tiles = new Tile[size, size]; - _nextValues = new float[size, size]; + _tileViews = new TileView[grid.Count]; + _nextValues = new float[grid.Count]; - for (int x = 0; x < size; x++) + for (int i = 0; i < grid.Count; i++) { - for (int y = 0; y < size; y++) + _tileGrid.MapIndex(i, out var x, out var y); + + var node = TileScene.Instance(); + this.AddChild(node); + var position = new Vector2 { - var tile = new Tile(); - tile.isHighlighted = false; - tile.value = 0.0f; - tile.type = _startTileType; + x = (x + .5f) * CellSize, + y = (y + .5f) * CellSize + }; + node.Position = position; + var canvasItem = (CanvasItem)node; - var node = TileScene.Instance(); - this.AddChild(node); - var position = new Vector2 - { - x = (x + .5f) * CellSize, - y = (y + .5f) * CellSize - }; - node.Position = position; - var canvasItem = (CanvasItem)node; - tile.material = (ShaderMaterial)canvasItem.Material; - tile.material.SetShaderParam("lowColor", _startTileType.Color); + var tile = _tileGrid[i]; - _tiles[x, y] = tile; - } + TileView view = default; + view.material = (ShaderMaterial)canvasItem.Material; + view.material.SetShaderParam("lowColor", _tileGrid.StartTileType.Color); + _tileViews[i] = view; } } + public ShaderMaterial GetTileMaterial(int idx) => _tileViews[idx].material; + + #region Simulation public void _on_Clock_OnTick(int ticks) { + GenerateHeat(); Diffuse(); } @@ -135,42 +120,56 @@ public class WorldGrid : Node2D private void GetNeighbourTemperatures(int x, int y, out float nx, out float ny, out float px, out float py) { // default value - var t = _tiles[x, y].value; + var t = _tileGrid[x, y].temperature; - nx = x > 0 ? _tiles[x - 1, y].value : t; - px = x < Size - 1 ? _tiles[x + 1, y].value : t; + nx = x > 0 ? _tileGrid[x - 1, y].temperature : t; + px = x < _tileGrid.Size - 1 ? _tileGrid[x + 1, y].temperature : t; - ny = y > 0 ? _tiles[x, y - 1].value : t; - py = y < Size - 1 ? _tiles[x, y + 1].value : t; + ny = y > 0 ? _tileGrid[x, y - 1].temperature : t; + py = y < _tileGrid.Size - 1 ? _tileGrid[x, y + 1].temperature : t; + } + + private void GenerateHeat() + { + for (int i = 0; i < _tileGrid.Count; i++) + { + var tile = _tileGrid[i]; + var type = tile.type; + tile.temperature += type.HeatGeneration; + _tileGrid[i] = tile; + } } private void Diffuse() { - for (int x = 0; x < Size; x++) + var D = DiffusionCoefficient; + + for (int i = 0; i < _tileGrid.Count; i++) { - for (int y = 0; y < Size; y++) - { - float t = _tiles[x, y].value; - var D = DiffusionCoefficient; + float t = _tileGrid[i].temperature; - GetNeighbourTemperatures( - x, y, - out var nx, - out var ny, - out var px, - out var py); + _tileGrid.MapIndex(i, out var x, out var y); - // current value - _nextValues[x, y] = TransferHeat(t, D, nx, ny, px, py); - } + GetNeighbourTemperatures( + x, y, + out var nx, + out var ny, + out var px, + out var py); + + var temperature = TransferHeat(t, D, nx, ny, px, py); + + // TODO: what if it's really really cold out? + temperature = Mathf.Max(0, temperature); + _nextValues[i] = temperature; } - for (int x = 0; x < Size; x++) + for (int i = 0; i < _tileGrid.Count; i++) { - for (int y = 0; y < Size; y++) - { - _tiles[x, y].value = _nextValues[x, y]; - } + var tile = _tileGrid[i]; + tile.temperature = _nextValues[i]; + _tileGrid[i] = tile; } } + #endregion } diff --git a/half-earth/scripts/overlays/HeatOverlay.cs b/half-earth/scripts/overlays/HeatOverlay.cs new file mode 100644 index 0000000..671fdca --- /dev/null +++ b/half-earth/scripts/overlays/HeatOverlay.cs @@ -0,0 +1,18 @@ +using Godot; +using System; + +public class HeatOverlay : Overlay +{ + [Export] + private Color _coldColor; + + [Export] + private Color _hotColor; + + public override void Apply(Tile tile, ShaderMaterial material) + { + material.SetShaderParam("lowColor", _coldColor); + material.SetShaderParam("highColor", _hotColor); + material.SetShaderParam("t", tile.temperature); + } +} diff --git a/half-earth/scripts/overlays/NoOverlay.cs b/half-earth/scripts/overlays/NoOverlay.cs new file mode 100644 index 0000000..49f8b1b --- /dev/null +++ b/half-earth/scripts/overlays/NoOverlay.cs @@ -0,0 +1,12 @@ +using Godot; + +public class NoOverlay : Overlay +{ + // in this view we want to draw tiles with their normal colour + public override void Apply(Tile tile, ShaderMaterial material) + { + var type = tile.type; + material.SetShaderParam("lowColor", type.Color); + material.SetShaderParam("t", 0); + } +} \ No newline at end of file diff --git a/half-earth/scripts/overlays/Overlay.cs b/half-earth/scripts/overlays/Overlay.cs new file mode 100644 index 0000000..10167b8 --- /dev/null +++ b/half-earth/scripts/overlays/Overlay.cs @@ -0,0 +1,7 @@ +using Godot; + +public abstract class Overlay : Resource +{ + public string Name => GetType().ToString(); + public abstract void Apply(Tile tile, ShaderMaterial material); +} \ No newline at end of file diff --git a/half-earth/scripts/overlays/OverlayCollection.cs b/half-earth/scripts/overlays/OverlayCollection.cs new file mode 100644 index 0000000..a357676 --- /dev/null +++ b/half-earth/scripts/overlays/OverlayCollection.cs @@ -0,0 +1,3 @@ +public class OverlayCollection : ReadOnlyResourceList +{ +} \ No newline at end of file diff --git a/half-earth/scripts/overlays/Overlays.cs b/half-earth/scripts/overlays/Overlays.cs new file mode 100644 index 0000000..7901b75 --- /dev/null +++ b/half-earth/scripts/overlays/Overlays.cs @@ -0,0 +1,67 @@ +using Godot; +using System; + +public class Overlays : Node +{ + [Signal] + delegate void OverlayCycled(Overlay overlay); + + [Export] + private KeyList _cycleOverlayKey; + + [Export] private Resource _overlaysResource; + public Overlay Current => _overlays[_overlayIdx]; + private OverlayCollection _overlays; + private int _overlayIdx = 0; + + [Export] + private Resource _tileGridResource; + private TileGrid _tileGrid; + + [Export] + private NodePath _worldGridPath; + private WorldGrid _worldGrid; + + public override void _Ready() + { + _overlays = (OverlayCollection)_overlaysResource; + _tileGrid = (TileGrid)_tileGridResource; + _worldGrid = GetNode(_worldGridPath); + } + + public override void _Input(InputEvent @event) + { + base._Input(@event); + + if (!(@event is InputEventKey keyEvent)) + return; + + if (!(keyEvent.Pressed)) + return; + + if ((KeyList)keyEvent.Scancode != _cycleOverlayKey) + return; + + CycleOverlay(); + } + + public override void _Process(float delta) + { + base._Process(delta); + + for (int i = 0; i < _tileGrid.Count; i++) + { + var tile = _tileGrid[i]; + var material = _worldGrid.GetTileMaterial(i); + + Current.Apply(tile, material); + } + } + + public void CycleOverlay() + { + _overlayIdx++; + _overlayIdx %= _overlays.Count; + EmitSignal(nameof(OverlayCycled), Current); + } +} diff --git a/half-earth/scripts/Tile.cs b/half-earth/scripts/tile/Tile.cs similarity index 60% rename from half-earth/scripts/Tile.cs rename to half-earth/scripts/tile/Tile.cs index 70560a0..662c72c 100644 --- a/half-earth/scripts/Tile.cs +++ b/half-earth/scripts/tile/Tile.cs @@ -3,7 +3,6 @@ using Godot; public struct Tile { public bool isHighlighted; - public float value; - public ShaderMaterial material; + public float temperature; public TileType type; } \ No newline at end of file diff --git a/half-earth/scripts/tile/TileGrid.cs b/half-earth/scripts/tile/TileGrid.cs new file mode 100644 index 0000000..7a2eb08 --- /dev/null +++ b/half-earth/scripts/tile/TileGrid.cs @@ -0,0 +1,92 @@ +using Godot; +using System; +using System.Collections; +using System.Collections.Generic; + +public class TileGrid : Resource, IReadOnlyList +{ + [Export] + public int Size { get; private set; } = 10; + + [Export] + private Resource _startTileTypeResource; + public TileType StartTileType => (TileType)_startTileTypeResource; + + public int Count => Size * Size; + + public Tile this[int index] + { + get => Tiles[index]; + set => Tiles[index] = value; + } + + public Tile this[int x, int y] + { + get + { + MapPosition(x, y, out int idx); + return Tiles[idx]; + } + set + { + MapPosition(x, y, out int idx); + Tiles[idx] = value; + } + } + + private List Tiles + { + get + { + if (_tiles == null) + { + _tiles = new List(); + Generate(_tiles); + } + + return _tiles; + } + } + private List _tiles = null; + + private void Generate(List tiles) + { + tiles.Clear(); + for (int i = 0; i < Count; i++) + { + var tile = new Tile(); + tile.isHighlighted = false; + tile.temperature = 0.0f; + tile.type = StartTileType; + + tiles.Add(tile); + } + } + + public IEnumerator GetEnumerator() + { + return Tiles.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Tiles.GetEnumerator(); + } + + public void MapIndex(int idx, out int x, out int y) + { + y = idx / Size; + x = idx % Size; + } + + public void MapPosition(int x, int y, out int idx) + { + idx = y * Size + x; + } + + public bool IsInBounds(int x, int y) + { + return x >= 0 && x < Size && y >= 0 && y < Size; + } +} + diff --git a/half-earth/scripts/TileType.cs b/half-earth/scripts/tile/TileType.cs similarity index 60% rename from half-earth/scripts/TileType.cs rename to half-earth/scripts/tile/TileType.cs index c6dc2ab..c2824a1 100644 --- a/half-earth/scripts/TileType.cs +++ b/half-earth/scripts/tile/TileType.cs @@ -5,8 +5,7 @@ public class TileType : Resource [Export] public string Name { get; private set; } - [Export] - public string BuildLabel { get; private set; } + public string BuildLabel => $"[{Key}] {Name}"; [Export] public KeyList Key { get; private set; } @@ -14,8 +13,8 @@ public class TileType : Resource [Export] public Color Color { get; private set; } - public override string ToString() - { - return Name; - } + [Export] + public float HeatGeneration { get; private set; } + + public override string ToString() => Name; } \ No newline at end of file diff --git a/half-earth/scripts/tile/TileTypeCollection.cs b/half-earth/scripts/tile/TileTypeCollection.cs new file mode 100644 index 0000000..ffb7066 --- /dev/null +++ b/half-earth/scripts/tile/TileTypeCollection.cs @@ -0,0 +1,3 @@ +public class TileTypeCollection : ReadOnlyResourceList +{ +} diff --git a/half-earth/scripts/ui/OverlaysUI.cs b/half-earth/scripts/ui/OverlaysUI.cs new file mode 100644 index 0000000..af97c7c --- /dev/null +++ b/half-earth/scripts/ui/OverlaysUI.cs @@ -0,0 +1,36 @@ +using Godot; +using System; +using System.Collections.Generic; + +public class OverlaysUI : Node +{ + [Export] + private NodePath _cycleOverlayButtonPath; + private Button _cycleOverlayButton; + + [Export] + private NodePath _overlaysPath; + private Overlays _overlays; + + private string _mainText; + + public override void _Ready() + { + base._Ready(); + + _cycleOverlayButton = GetNode