b451f83174
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>
140 lines
4.9 KiB
C#
140 lines
4.9 KiB
C#
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 (1–5).</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);
|
||
}
|