tile-health #18
							
								
								
									
										100
									
								
								half-earth/scripts/HeatSystem.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								half-earth/scripts/HeatSystem.cs
									
									
									
									
									
										Normal file
									
								
							@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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,95 +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 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 GenerateHeat()
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < _tileGrid.Count; i++)
 | 
			
		||||
        {
 | 
			
		||||
            var tile = _tileGrid[i];
 | 
			
		||||
            var type = tile.type;
 | 
			
		||||
            tile.temperature += type.HeatGeneration;
 | 
			
		||||
            ApplyHeatDamage(ref tile);
 | 
			
		||||
            _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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user