diff --git a/half-earth/resources/overlays/no_overlay.tres b/half-earth/resources/overlays/no_overlay.tres index d4a783f..5f79807 100644 --- a/half-earth/resources/overlays/no_overlay.tres +++ b/half-earth/resources/overlays/no_overlay.tres @@ -4,3 +4,5 @@ [resource] script = ExtResource( 1 ) +_healthy = Color( 0.0431373, 0.541176, 0.0235294, 1 ) +_depleted = Color( 0.670588, 0.572549, 0.27451, 1 ) diff --git a/half-earth/resources/tiles/tile_types/developed.tres b/half-earth/resources/tiles/tile_types/developed.tres index 8f5f1d1..d9ffdac 100644 --- a/half-earth/resources/tiles/tile_types/developed.tres +++ b/half-earth/resources/tiles/tile_types/developed.tres @@ -7,4 +7,4 @@ script = ExtResource( 1 ) Name = "Developed" Key = 68 Color = Color( 0.372549, 0.372549, 0.372549, 1 ) -HeatGeneration = 0.4 +HeatGeneration = 0.15 diff --git a/half-earth/resources/tiles/tile_types/wild.tres b/half-earth/resources/tiles/tile_types/wild.tres index 370bb83..20cc915 100644 --- a/half-earth/resources/tiles/tile_types/wild.tres +++ b/half-earth/resources/tiles/tile_types/wild.tres @@ -1,10 +1,11 @@ [gd_resource type="Resource" load_steps=2 format=2] -[ext_resource path="res://scripts/tile/TileType.cs" type="Script" id=1] +[ext_resource path="res://scripts/tile/Wild.cs" type="Script" id=1] [resource] script = ExtResource( 1 ) Name = "Wild" Key = 87 -Color = Color( 0, 0.545098, 0.0196078, 1 ) -HeatGeneration = -0.2 +Color = Color( 0.14902, 1, 0, 0 ) +HeatGeneration = -0.02 +Threshold = 0.6 diff --git a/half-earth/scripts/HeatSystem.cs b/half-earth/scripts/HeatSystem.cs new file mode 100644 index 0000000..ebeb79d --- /dev/null +++ b/half-earth/scripts/HeatSystem.cs @@ -0,0 +1,100 @@ +using Godot; + +public class HeatSystem +{ + private readonly TileGrid _tiles; + private readonly float _diffusionCoefficient; + + private float[] _nextTemperatures = null; + + public HeatSystem(TileGrid tiles, float diffusionCoefficient) + { + _tiles = tiles; + _diffusionCoefficient = diffusionCoefficient; + _nextTemperatures = new float[tiles.Count]; + } + + public void Process() + { + GenerateHeat(); + Diffuse(); + } + + + private float TransferHeat(float t0, float alpha, float nx, float ny, float px, float py) + { + float d2tdx2 = nx - 2 * t0 + px; + float d2tdy2 = ny - 2 * t0 + py; + return t0 + alpha * (d2tdx2 + d2tdy2); + } + + 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].temperature; + + nx = x > 0 ? _tiles[x - 1, y].temperature : t; + px = x < _tiles.Size - 1 ? _tiles[x + 1, y].temperature : t; + + ny = y > 0 ? _tiles[x, y - 1].temperature : t; + py = y < _tiles.Size - 1 ? _tiles[x, y + 1].temperature : t; + } + private void GenerateHeat() + { + for (int i = 0; i < _tiles.Count; i++) + { + var tile = _tiles[i]; + var type = tile.type; + tile.temperature += type.HeatGeneration; + ApplyHeatDamage(ref tile); + _tiles[i] = tile; + } + } + + private void ApplyHeatDamage(ref Tile tile) + { + if (!(tile.type is IDamageable damageable)) + return; + + var surplus = tile.temperature - damageable.Threshold; + if (surplus < 0) + return; + + // TODO: parameterised balancing + tile.currentHealth -= surplus; + tile.currentHealth = Mathf.Clamp(tile.currentHealth, 0, 1); + } + + private void Diffuse() + { + var D = _diffusionCoefficient; + + for (int i = 0; i < _tiles.Count; i++) + { + float t = _tiles[i].temperature; + + _tiles.MapIndex(i, out var x, out var y); + + 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); + _nextTemperatures[i] = temperature; + } + + for (int i = 0; i < _tiles.Count; i++) + { + var tile = _tiles[i]; + tile.temperature = _nextTemperatures[i]; + _tiles[i] = tile; + } + } + +} diff --git a/half-earth/scripts/WorldGrid.cs b/half-earth/scripts/WorldGrid.cs index 6a7b01a..fb78f04 100644 --- a/half-earth/scripts/WorldGrid.cs +++ b/half-earth/scripts/WorldGrid.cs @@ -16,13 +16,12 @@ public class WorldGrid : Node2D [Export] public float DiffusionCoefficient { get; set; } = 0.01f; - private float[] _nextValues = null; - public struct TileView { public ShaderMaterial material; } private TileView[] _tileViews; + private HeatSystem _heat; // Called when the node enters the scene tree for the first time. public override void _Ready() @@ -30,6 +29,8 @@ public class WorldGrid : Node2D _tileGrid = (TileGrid)_tileGridResource; GenerateCanvasItems(_tileGrid); + + _heat = new HeatSystem(_tileGrid, DiffusionCoefficient); } #region Positioning @@ -53,8 +54,6 @@ public class WorldGrid : Node2D var tile = _tileGrid[idx]; tile.type = tileType; _tileGrid[x, y] = tile; - - _tileViews[idx].material.SetShaderParam("lowColor", tileType.Color); } #endregion @@ -76,7 +75,6 @@ public class WorldGrid : Node2D this.Clear(); _tileViews = new TileView[grid.Count]; - _nextValues = new float[grid.Count]; for (int i = 0; i < grid.Count; i++) { @@ -96,80 +94,11 @@ public class WorldGrid : Node2D 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(); - } - - private float TransferHeat(float t0, float alpha, float nx, float ny, float px, float py) - { - float d2tdx2 = nx - 2 * t0 + px; - float d2tdy2 = ny - 2 * t0 + py; - return t0 + alpha * (d2tdx2 + d2tdy2); - } - - private void GetNeighbourTemperatures(int x, int y, out float nx, out float ny, out float px, out float py) - { - // default value - var t = _tileGrid[x, y].temperature; - - nx = x > 0 ? _tileGrid[x - 1, y].temperature : t; - px = x < _tileGrid.Size - 1 ? _tileGrid[x + 1, y].temperature : 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() - { - var D = DiffusionCoefficient; - - for (int i = 0; i < _tileGrid.Count; i++) - { - float t = _tileGrid[i].temperature; - - _tileGrid.MapIndex(i, out var x, out var y); - - 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 i = 0; i < _tileGrid.Count; i++) - { - var tile = _tileGrid[i]; - tile.temperature = _nextValues[i]; - _tileGrid[i] = tile; - } - } - #endregion + public void _on_Clock_OnTick(int ticks) => _heat.Process(); } diff --git a/half-earth/scripts/overlays/NoOverlay.cs b/half-earth/scripts/overlays/NoOverlay.cs index 49f8b1b..1222cec 100644 --- a/half-earth/scripts/overlays/NoOverlay.cs +++ b/half-earth/scripts/overlays/NoOverlay.cs @@ -2,10 +2,25 @@ using Godot; public class NoOverlay : Overlay { + [Export] + private Color _healthy; + + [Export] + private Color _depleted; + // in this view we want to draw tiles with their normal colour public override void Apply(Tile tile, ShaderMaterial material) { var type = tile.type; + + if (type is Wild wild) + { + material.SetShaderParam("lowColor", _depleted); + material.SetShaderParam("highColor", _healthy); + material.SetShaderParam("t", tile.currentHealth); + return; + } + material.SetShaderParam("lowColor", type.Color); material.SetShaderParam("t", 0); } diff --git a/half-earth/scripts/tile/IDamageable.cs b/half-earth/scripts/tile/IDamageable.cs new file mode 100644 index 0000000..6279a0d --- /dev/null +++ b/half-earth/scripts/tile/IDamageable.cs @@ -0,0 +1,4 @@ +public interface IDamageable +{ + float Threshold { get; } +} \ No newline at end of file diff --git a/half-earth/scripts/tile/Tile.cs b/half-earth/scripts/tile/Tile.cs index 662c72c..3a51b55 100644 --- a/half-earth/scripts/tile/Tile.cs +++ b/half-earth/scripts/tile/Tile.cs @@ -5,4 +5,5 @@ public struct Tile public bool isHighlighted; public float temperature; public TileType type; + public float currentHealth; } \ No newline at end of file diff --git a/half-earth/scripts/tile/TileGrid.cs b/half-earth/scripts/tile/TileGrid.cs index 7a2eb08..ef2c812 100644 --- a/half-earth/scripts/tile/TileGrid.cs +++ b/half-earth/scripts/tile/TileGrid.cs @@ -10,7 +10,7 @@ public class TileGrid : Resource, IReadOnlyList [Export] private Resource _startTileTypeResource; - public TileType StartTileType => (TileType)_startTileTypeResource; + private TileType StartTileType => (TileType)_startTileTypeResource; public int Count => Size * Size; @@ -58,6 +58,7 @@ public class TileGrid : Resource, IReadOnlyList tile.isHighlighted = false; tile.temperature = 0.0f; tile.type = StartTileType; + tile.currentHealth = 1.0f; tiles.Add(tile); } diff --git a/half-earth/scripts/tile/Wild.cs b/half-earth/scripts/tile/Wild.cs new file mode 100644 index 0000000..5cfef22 --- /dev/null +++ b/half-earth/scripts/tile/Wild.cs @@ -0,0 +1,7 @@ +using Godot; + +public class Wild : TileType, IDamageable +{ + [Export] + public float Threshold { get; private set; } +} \ No newline at end of file