using Godot; using Theriapolis.Core; using Theriapolis.Core.Tactical; namespace Theriapolis.GodotHost.Rendering; /// /// One Node2D per tactical chunk. Positioned at (chunk.OriginX, chunk.OriginY) /// in world-pixel space; renders all CHUNK_SIZE x CHUNK_SIZE tiles via _Draw. /// /// Godot caches CanvasItem _Draw output, so _Draw runs once on first paint /// and the rasterised result is reused every frame thereafter. We only call /// QueueRedraw() if the chunk's delta changes (M4 doesn't yet — chunks are /// static once generated). /// /// Each tactical tile is 1 world pixel wide; the source sprite is /// TACTICAL_TILE_SPRITE_PX² and gets squashed into that 1×1 cell via /// DrawTextureRect's destination rect. The camera's zoom (32x for tactical /// view) magnifies the rasterisation back to native sprite resolution. /// public partial class TacticalChunkNode : Node2D { private TacticalChunk? _chunk; public void Bind(TacticalChunk chunk) { _chunk = chunk; Position = new Vector2(chunk.OriginX, chunk.OriginY); QueueRedraw(); } public override void _Draw() { if (_chunk is null) return; int size = C.TACTICAL_CHUNK_SIZE; // Pass 1: surfaces. for (int ly = 0; ly < size; ly++) for (int lx = 0; lx < size; lx++) { ref var t = ref _chunk.Tiles[lx, ly]; var tex = TacticalAtlas.GetSurface(t.Surface, t.Variant); DrawTextureRect(tex, new Rect2(lx, ly, 1, 1), false); } // Pass 2: decos on top. for (int ly = 0; ly < size; ly++) for (int lx = 0; lx < size; lx++) { ref var t = ref _chunk.Tiles[lx, ly]; var tex = TacticalAtlas.GetDeco(t.Deco, t.Variant); if (tex is null) continue; DrawTextureRect(tex, new Rect2(lx, ly, 1, 1), false); } } }