103 lines
3.3 KiB
C#
103 lines
3.3 KiB
C#
|
|
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();
|
||
|
|
}
|
||
|
|
}
|