Files
TheriapolisV3/Theriapolis.Game/Rendering/WorldMapRenderer.cs
T

103 lines
3.3 KiB
C#
Raw Normal View History

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Theriapolis.Core;
using Theriapolis.Core.World;
using Theriapolis.Core.World.Generation;
namespace Theriapolis.Game.Rendering;
/// <summary>
/// Renders the world map: biome tiles, rivers, roads, rail, and settlement icons.
/// Draw order: terrain → roads → rivers → rail → settlements.
/// </summary>
public sealed class WorldMapRenderer : IMapView, IDisposable
{
private readonly WorldGenContext _ctx;
private readonly TileAtlas _atlas;
private readonly LineFeatureRenderer _lineRenderer;
private bool _disposed;
// Zoom level below which settlement labels are hidden to avoid clutter
private const float LabelMinZoom = 0.5f;
// Min tier to show at low zoom (hide tier 4 villages when zoomed out)
private const float SettleHideZoom = 0.08f;
public WorldMapRenderer(WorldGenContext ctx, TileAtlas atlas)
{
_ctx = ctx;
_atlas = atlas;
_lineRenderer = new LineFeatureRenderer(atlas.GraphicsDevice, ctx);
}
public void Draw(SpriteBatch sb, Camera2D camera, GameTime gameTime)
{
DrawTerrain(sb, camera);
_lineRenderer.Draw(sb, camera);
DrawSettlements(sb, camera);
}
private void DrawTerrain(SpriteBatch sb, Camera2D camera)
{
var (x0, y0, x1, y1) = camera.VisibleTileRect();
int tilePixels = C.WORLD_TILE_PIXELS;
sb.Begin(
transformMatrix: camera.TransformMatrix,
samplerState: SamplerState.PointClamp,
sortMode: SpriteSortMode.Deferred);
for (int ty = y0; ty <= y1; ty++)
for (int tx = x0; tx <= x1; tx++)
{
ref var tile = ref _ctx.World.TileAt(tx, ty);
var tex = _atlas.GetTile(tile.Biome);
var dest = new Rectangle(tx * tilePixels, ty * tilePixels, tilePixels, tilePixels);
sb.Draw(tex, dest, Color.White);
}
sb.End();
}
private void DrawSettlements(SpriteBatch sb, Camera2D camera)
{
if (_ctx.World.Settlements.Count == 0) return;
sb.Begin(
transformMatrix: camera.TransformMatrix,
samplerState: SamplerState.LinearClamp,
sortMode: SpriteSortMode.Deferred,
blendState: BlendState.AlphaBlend);
bool hideSmall = camera.Zoom < SettleHideZoom;
foreach (var s in _ctx.World.Settlements)
{
if (hideSmall && s.Tier >= 4) continue;
var icon = _atlas.GetSettlementIcon(s.Tier);
float wx = s.TileX * C.WORLD_TILE_PIXELS + C.WORLD_TILE_PIXELS * 0.5f;
float wy = s.TileY * C.WORLD_TILE_PIXELS + C.WORLD_TILE_PIXELS * 0.5f;
var origin = new Vector2(icon.Width * 0.5f, icon.Height * 0.5f);
sb.Draw(icon,
position: new Vector2(wx, wy),
sourceRectangle: null,
color: Color.White,
rotation: 0f,
origin: origin,
scale: 1f,
effects: SpriteEffects.None,
layerDepth: 0f);
}
sb.End();
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_lineRenderer.Dispose();
}
}