Files
TheriapolisV3/Theriapolis.Game/Rendering/TileAtlas.cs
T
Christopher Wiebe b451f83174 Initial commit: Theriapolis baseline at port/godot branch point
Captures the pre-Godot-port state of the codebase. This is the rollback
anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md).
All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:40:51 -07:00

140 lines
4.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Theriapolis.Core.Data;
using Theriapolis.Core.World;
namespace Theriapolis.Game.Rendering;
/// <summary>
/// Manages the biome tile textures used by the world-map renderer.
/// For Phase 0/1, all tiles are generated at runtime as flat-colour 32×32 squares
/// with a 1px darker border and a single centered letter.
/// Final art will replace these by swapping file contents; no code changes needed.
/// </summary>
public sealed class TileAtlas : IDisposable
{
private readonly GraphicsDevice _gd;
private readonly Dictionary<BiomeId, Texture2D> _textures = new();
private readonly Dictionary<int, Texture2D> _settlementIcons = new(); // keyed by tier
private SpriteFont? _font;
private bool _disposed;
public GraphicsDevice GraphicsDevice => _gd;
// Fallback solid-color texture for any biome that has no dedicated entry
private Texture2D? _fallback;
public TileAtlas(GraphicsDevice gd)
{
_gd = gd;
}
/// <summary>
/// Generate all placeholder textures from the loaded BiomeDef array.
/// Call once after content is loaded.
/// </summary>
public void GeneratePlaceholders(BiomeDef[] biomes, SpriteFont? font = null)
{
_font = font;
foreach (var def in biomes)
{
var biomeId = BiomeAssignHelper.ParseBiomeId(def.Id);
if (_textures.ContainsKey(biomeId)) continue;
_textures[biomeId] = MakeTile(def);
}
_fallback = MakeSolidColor(Color.HotPink); // obvious "missing art" colour
GenerateSettlementIcons();
}
/// <summary>Returns the settlement icon texture for the given tier (15).</summary>
public Texture2D GetSettlementIcon(int tier)
{
if (_settlementIcons.TryGetValue(tier, out var tex)) return tex;
return _fallback ?? _textures.Values.First();
}
private void GenerateSettlementIcons()
{
// Tier 1: large gold diamond (capital)
_settlementIcons[1] = MakeSettlementIcon(20, new Color(255, 215, 0), diamond: true);
// Tier 2: white square (city)
_settlementIcons[2] = MakeSettlementIcon(14, new Color(230, 230, 230), diamond: false);
// Tier 3: light-blue circle (town)
_settlementIcons[3] = MakeSettlementIcon(10, new Color(150, 200, 255), diamond: false);
// Tier 4: pale dot (village)
_settlementIcons[4] = MakeSettlementIcon(6, new Color(200, 200, 200), diamond: false);
// Tier 5 (PoI): small red circle
_settlementIcons[5] = MakeSettlementIcon(5, new Color(200, 60, 60), diamond: false);
}
private Texture2D MakeSettlementIcon(int size, Color fill, bool diamond)
{
var tex = new Texture2D(_gd, size, size);
var pixels = new Color[size * size];
float cx = (size - 1) * 0.5f;
float cy = (size - 1) * 0.5f;
for (int py = 0; py < size; py++)
for (int px = 0; px < size; px++)
{
float nx = px - cx, ny = py - cy;
bool inside = diamond
? (MathF.Abs(nx) + MathF.Abs(ny)) <= size * 0.5f
: (nx * nx + ny * ny) <= (cx * cx);
pixels[py * size + px] = inside ? fill : Color.Transparent;
}
tex.SetData(pixels);
return tex;
}
/// <summary>Returns the texture for the given biome, falling back to the error texture.</summary>
public Texture2D GetTile(BiomeId biome)
{
if (_textures.TryGetValue(biome, out var tex)) return tex;
return _fallback ?? _textures.Values.First();
}
// ── Texture generation helpers ────────────────────────────────────────────
private Texture2D MakeTile(BiomeDef def)
{
int size = Theriapolis.Core.C.WORLD_TILE_PIXELS;
var (r, g, b) = def.ParsedColor();
var fillColor = new Color(r, g, b);
var tex = new Texture2D(_gd, size, size);
var pixels = new Color[size * size];
Array.Fill(pixels, fillColor);
tex.SetData(pixels);
return tex;
}
private Texture2D MakeSolidColor(Color color)
{
int size = Theriapolis.Core.C.WORLD_TILE_PIXELS;
var tex = new Texture2D(_gd, size, size);
var pixels = new Color[size * size];
Array.Fill(pixels, color);
tex.SetData(pixels);
return tex;
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
foreach (var tex in _textures.Values) tex.Dispose();
foreach (var tex in _settlementIcons.Values) tex.Dispose();
_fallback?.Dispose();
}
}
/// <summary>Internal helper to avoid exposing the private static method on BiomeAssignStage.</summary>
internal static class BiomeAssignHelper
{
public static BiomeId ParseBiomeId(string id)
=> Theriapolis.Core.World.Generation.Stages.BiomeAssignStage.ParseBiomeId(id);
}