Biomes B: data-driven terrain + biome ground layer (foundation) #136

Closed
opened 2026-07-22 13:29:06 +02:00 by panda · 1 comment
Collaborator

Foundation slice of the Biomes system. Design: docs/game-design/systems/biomes.md (parent #135).

Turn the world's single grass field into multiple walkable biome grounds, replacing the hardcoded terrain predicates with a data-driven descriptor.

Scope

  • Shared: add a Biome enum (Grassland, DrySavanna, NorthernGrassland, Tundra, Desert{sand,salt}, Rocky{ground,mountain}, Volcanic, Marsh) + a static BiomeInfo descriptor (walkable, moveCostScale) as the single source (no IConfiguration, no magic literals).
  • Refactor TileMap to hold a biome[,] grid and make IsWalkable/IsObstacle/BlocksSight read the descriptor instead of == Grass/== Tree. Marsh becomes walkable (blocks-sight stays false).
  • Extend the ground legend (one char per biome) with back-compat: legacy . -> Grassland, ~ -> Marsh, S spawn (# fixture handled in issue D).
  • Author a small varied patch in content/maps/world.map (or a test map) so several biomes are reachable.
  • Client GridRenderer.DrawGround: pick the biome texture set per cell, select the _1..4 variant from Hash(x,y) (today always tiles[0]), apply the hash-driven horizontal flip.
  • Update the marsh "impassable" doc comments (TileMap.cs, TerrainType.cs) and the marsh assertions in TileMapTests.

Not here

Move-cost (C), fixtures/object-layer (D), per-biome scatter (E), art import (A). Terrain stays off the wire -> no ProtocolVersion bump (confirm no ServerMessage serializes terrain).

Definition of Done (delta beyond base DoD)

  • Walking a straight line crosses >=4 visually distinct biome grounds (placeholder colors if art absent).
  • Marsh is walkable (was impassable) -- demonstrable by walking onto a ~ cell.
  • Ground shows per-cell variant variety -- no single stamped texture across a region (deterministic from the hash).
  • TileMap walkability/sight is data-driven -- adding a biome is a descriptor row, no new if/switch in the predicates.
Foundation slice of the **Biomes** system. Design: `docs/game-design/systems/biomes.md` (parent #135). Turn the world's single grass field into multiple walkable biome grounds, replacing the hardcoded terrain predicates with a data-driven descriptor. ## Scope - `Shared`: add a `Biome` enum (Grassland, DrySavanna, NorthernGrassland, Tundra, Desert{sand,salt}, Rocky{ground,mountain}, Volcanic, Marsh) + a static `BiomeInfo` descriptor `(walkable, moveCostScale)` as the single source (no `IConfiguration`, no magic literals). - Refactor `TileMap` to hold a `biome[,]` grid and make `IsWalkable`/`IsObstacle`/`BlocksSight` read the descriptor instead of `== Grass`/`== Tree`. **Marsh becomes walkable** (blocks-sight stays false). - Extend the ground legend (one char per biome) with **back-compat**: legacy `.` -> Grassland, `~` -> Marsh, `S` spawn (`#` fixture handled in issue D). - Author a small varied patch in `content/maps/world.map` (or a test map) so several biomes are reachable. - Client `GridRenderer.DrawGround`: pick the biome texture set per cell, select the `_1..4` variant from `Hash(x,y)` (today always `tiles[0]`), apply the hash-driven horizontal flip. - Update the marsh "impassable" doc comments (`TileMap.cs`, `TerrainType.cs`) and the marsh assertions in `TileMapTests`. ## Not here Move-cost (C), fixtures/object-layer (D), per-biome scatter (E), art import (A). Terrain stays off the wire -> **no `ProtocolVersion` bump** (confirm no `ServerMessage` serializes terrain). ## Definition of Done (delta beyond base DoD) - Walking a straight line crosses **>=4 visually distinct biome grounds** (placeholder colors if art absent). - **Marsh is walkable** (was impassable) -- demonstrable by walking onto a `~` cell. - Ground shows **per-cell variant variety** -- no single stamped texture across a region (deterministic from the hash). - `TileMap` walkability/sight is **data-driven** -- adding a biome is a descriptor row, no new `if`/switch in the predicates.
Author
Collaborator

Technical design review — agreed (go-with-changes)

Ran critical-design-review before implementation. Base branch: feat/biomes-foundation (off main).

Decisions: extend TerrainType with biome ground values (interim — Tree stays a member; the clean Biome/Fixture two-grid split lands in issue D); add a small multi-biome patch to the real content/maps/world.map.

Approach: data-driven TerrainInfo descriptor (Walkable, BlocksSight) [MoveCost deferred to C]; predicates + legend become tables (no switch); Marsh.Walkable = true. Client: GridRenderer takes a Biome -> Texture2D[] map; per-cell variant+flip selection is a pure helper in IsoMmo.Client.Core (unit-tested); per-biome PlaceholderArt tinted fallback so the repo runs without assets.isoa.

Invariants Check (vs CLAUDE.md ## Design checklist)

  1. Scope — ✓ exactly issue #136; no extra system.
  2. Server-authoritative — ✓ walkability stays in TileMap.IsWalkable read server-side by MovementSystem; marsh-walkable is a server rule; no client positions; no new spammable intent.
  3. GM authorization — N/A — no admin action.
  4. Identity model — N/A — no account/identity change.
  5. Protocol versioned — ✓ no wire-shape/enum/DTO change; TerrainType absent from Protocol/+Networking/ (grep) → no bump.
  6. String catalog — N/A — no player-facing text (marsh notice is C).
  7. Single-threaded sim — ✓ TileMap immutable after Parse (startup); no World state/lock added.
  8. World.cs HARD GATE — ✓ logic in TileMap/TerrainInfo (Shared); World unchanged.
  9. Screen HARD GATE (client) — ✓ change is in GridRenderer component, not GameScreen.
  10. Client engine-independence — ✓ (with change) deterministic per-cell variant+flip selection lives in Client.Core, unit-tested; GridRenderer consumes it.
  11. Gameplay/Networking separation — N/A — changes Shared + client rendering only.
  12. Act on the instance — N/A — no per-instance state / side-collection.
  13. Extend by type, not switch — ✓ (with change) predicates = TerrainInfo table lookups; the char↔TerrainType legend becomes a data table (not the current Parse switch). New biome = one row.
  14. Server-paced actions — N/A — no timed/channeled action (slow is C).
  15. Persistence (GameServer) — N/A — TileMap is committed content loaded at startup, not entity serialization; no file access in the tick.
  16. Persistence (Auth) — N/A — no Auth DB change.
  17. Process separation — N/A — no Auth/GameServer contract change.
  18. Typed options — N/A — no new tunable (move-cost is C).
  19. Broadcasts absolute / AoI — N/A — no new observable entity/event; movement broadcasts unchanged.
  20. Multi-platform — ✓ TileMap pure .NET; rendering uses existing cross-platform MonoGame calls.
  21. Asset fallback — ✓ (to implement) each biome degrades to a tinted PlaceholderArt diamond when its texture set is empty → runs without assets.isoa.
  22. ModernUO as reference — ✓ follows ModernUO TileData per-tile flags (impassable/wet/…) as the data-driven descriptor; diverges by using our small enum + TerrainInfo instead of the full UO table.
  23. Docs & DoD same change — ✓ update the legend comment in TileMap.cs; DoD already in this issue.

No , no HARD GATE tripped. Implementation proceeds with TDD.

## Technical design review — agreed (go-with-changes) Ran `critical-design-review` before implementation. Base branch: `feat/biomes-foundation` (off `main`). **Decisions:** extend `TerrainType` with biome ground values (interim — `Tree` stays a member; the clean `Biome`/`Fixture` two-grid split lands in issue D); add a small multi-biome patch to the real `content/maps/world.map`. **Approach:** data-driven `TerrainInfo` descriptor `(Walkable, BlocksSight)` [MoveCost deferred to C]; predicates + legend become tables (no switch); `Marsh.Walkable = true`. Client: `GridRenderer` takes a `Biome -> Texture2D[]` map; per-cell variant+flip selection is a **pure helper in `IsoMmo.Client.Core`** (unit-tested); per-biome `PlaceholderArt` tinted fallback so the repo runs without `assets.isoa`. ### Invariants Check (vs CLAUDE.md ## Design checklist) 1. **Scope** — ✓ exactly issue #136; no extra system. 2. **Server-authoritative** — ✓ walkability stays in `TileMap.IsWalkable` read server-side by `MovementSystem`; marsh-walkable is a server rule; no client positions; no new spammable intent. 3. **GM authorization** — N/A — no admin action. 4. **Identity model** — N/A — no account/identity change. 5. **Protocol versioned** — ✓ no wire-shape/enum/DTO change; `TerrainType` absent from `Protocol/`+`Networking/` (grep) → no bump. 6. **String catalog** — N/A — no player-facing text (marsh notice is C). 7. **Single-threaded sim** — ✓ `TileMap` immutable after `Parse` (startup); no `World` state/lock added. 8. **World.cs HARD GATE** — ✓ logic in `TileMap`/`TerrainInfo` (Shared); `World` unchanged. 9. **Screen HARD GATE (client)** — ✓ change is in `GridRenderer` component, not `GameScreen`. 10. **Client engine-independence** — ✓ (with change) deterministic per-cell variant+flip selection lives in `Client.Core`, unit-tested; `GridRenderer` consumes it. 11. **Gameplay/Networking separation** — N/A — changes Shared + client rendering only. 12. **Act on the instance** — N/A — no per-instance state / side-collection. 13. **Extend by type, not switch** — ✓ (with change) predicates = `TerrainInfo` table lookups; the `char↔TerrainType` legend becomes a data table (not the current `Parse` switch). New biome = one row. 14. **Server-paced actions** — N/A — no timed/channeled action (slow is C). 15. **Persistence (GameServer)** — N/A — `TileMap` is committed content loaded at startup, not entity serialization; no file access in the tick. 16. **Persistence (Auth)** — N/A — no Auth DB change. 17. **Process separation** — N/A — no Auth/GameServer contract change. 18. **Typed options** — N/A — no new tunable (move-cost is C). 19. **Broadcasts absolute / AoI** — N/A — no new observable entity/event; movement broadcasts unchanged. 20. **Multi-platform** — ✓ `TileMap` pure .NET; rendering uses existing cross-platform MonoGame calls. 21. **Asset fallback** — ✓ (to implement) each biome degrades to a tinted `PlaceholderArt` diamond when its texture set is empty → runs without `assets.isoa`. 22. **ModernUO as reference** — ✓ follows ModernUO `TileData` per-tile flags (impassable/wet/…) as the data-driven descriptor; diverges by using our small enum + `TerrainInfo` instead of the full UO table. 23. **Docs & DoD same change** — ✓ update the legend comment in `TileMap.cs`; DoD already in this issue. No `✗`, no HARD GATE tripped. Implementation proceeds with TDD.
marco closed this issue 2026-07-22 14:06:06 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo#136
No description provided.