UnityTowerDefense/Project_Context.md

97 lines
9.3 KiB
Markdown
Raw Permalink 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.

# Unity Tower Defense — Project Context
## Purpose
A snapshot of **where the project is and how it works** — the authoritative reference for current architecture, implemented systems, conventions, and known debt. It pairs with [`Project_Roadmap.md`](Project_Roadmap.md), which is the forward-looking plan. When the two disagree, this document describes *what exists today*; the roadmap describes *what's planned next*.
Last substantial update: 2026-06-23.
---
## Game overview
A **co-op tower-defense / roguelike hybrid** for up to 9 players.
- **Maze defense (Wintermaul-style):** players build towers to force enemies along a longer path through their own zone. Lives are a **shared pool**; gold is **per-player**.
- **Roguelike layer (now core to the design):** every player starts with the **same three towers** and builds out a personal "deck"/"build" through a **3-option draft** presented at match start and after every wave. Choices span new towers, systemic upgrades, builder abilities, enemy debuffs, and relic quests. See [`Project_Roadmap.md`](Project_Roadmap.md) for the full design.
- **Target platform:** Steam (Windows / Linux / Steam Deck).
- **Visual direction (aspirational):** "painted tabletop miniature" look with Spider-Verse-style stepped (on-2s) enemy animation. Current visuals are **placeholder** (primitive meshes / cones, sourced creature models).
## Engine & tech
Unity **6.4 (6000.4.4f1)**, URP, IL2CPP, .NET Standard 2.1, Linear color space, new Input System, Force Text serialization. Netcode: **Netcode for GameObjects (NGO) 2.x**.
## Repository & collaboration
- Self-hosted **Forgejo**: `https://git.marlboro-bc.duckdns.org/yeahweregames/UnityTowerDefense` (git remote **`origin`**). A legacy GitHub mirror exists as remote **`github`**.
- **Multiple contributors** work in parallel on feature branches merged to `main` via PRs. Expect `main` to move between sessions; rebase/branch off the latest.
- **IP guardrail:** the prototype uses Games-Workshop-adjacent placeholder content (race names, sourced models). The repo stays **private**, no public builds/demos, until that content is replaced. Maintain a plain-text asset manifest of IP-derived assets to swap before any public release.
---
## Architecture & conventions
- **Server-authoritative gameplay; local-only UI/visual state.** Only gameplay-meaningful state is networked. Selection, placement ghosts, animation, and the paint cursor are client-local.
- **Data-driven via ScriptableObjects:** `TowerDefinition`, `EnemyDefinition`, `WaveDefinition`, `RaceDefinition`, `GoldConfig`, `BuffDefinition`/`BuffCategory`, `DraftOption` (+ `NewTowerDraftOption`). Designers tune stats in assets, not code.
- **Per-player state pattern:** `NetworkBehaviour`s on the **Player prefab** with a static `GetForClient(clientId)` / `Local` registry. Current set: `PlayerGoldManager`, `PlayerMatchState`, `PlayerBuffManager`, `PlayerTowerDeck`, `PlayerDraft`.
- **Networked identifiers are catalog indices.** `TowerTypeId` indexes `TowerPlacementManager.towerDefinitions[]`; `DraftOptionId` indexes `DraftPool`. Stable **within a match**, not across sessions — cross-match persistence will need stable IDs (asset GUID or a serialized StableId).
- **Namespaces:** `TD.Core` (enums, palettes, grid math), `TD.Gameplay` (builder, placement, match/wave/pathfinding state, economy, enemies, deck), `TD.Gameplay.Draft` (draft system), `TD.Combat` (TowerCombat/Projectile), `TD.Levels` (in-engine authoring + bake), `TD.UI`, `TD.Net`.
- **Scenes:** `MainMenu``Lobby` → a Match level (`9Player`, `Main`).
### Engineering principles (carried across sessions)
- **Debug to root cause.** No defensive workarounds or per-frame state-correcting hacks — find and fix the actual cause.
- **Restate design intent before coding.** Confirmed valuable repeatedly; prevents rework.
- **Server owns gameplay; clients render.** Authoritative state on the server, visualization derived locally on each peer.
---
## Implemented systems
### Match foundation
- `MatchState` (NetworkBehaviour scene singleton): phase (`Lobby`/`CountDown`/`Playing`/`Victory`/`Defeat`), current wave, shared lives. `PlayerMatchState` (per-player): slot allocation, race selection, ready state. Authoritative client-id → `PlayerSlot` mapping.
### Combat & towers
- `TowerCombat` (server targeting loop; `OnTargetAcquired`/`OnTargetLost`/`OnFire` events for future animation), `Projectile`, hitscan + projectile paths, Single/Splash/Chain target types, damage types, range indicator. **Air-targeting** done: `GroundedOnly` towers skip flying enemies. Range checks are **3D**.
- `TowerPlacementManager` (server-validated placement queue), `Builder` build queue with staged construction/pause/cancel/refund, `BuildSiteVisual` ghosts, owner tinting, RTS selection.
### Enemies & pathfinding
- `EnemyHealth` (replicated HP, damage types, `IsFlying`, held state), `EnemyStatus` (lingering effects: slow/DoT), `EnemyMovement` (A* path following, zone-leak attribution, death/sink sequence).
- `PathfindingService` (A* on the runtime walkability grid, octile heuristic, corner-cut prevention, line-of-sight path smoothing; re-paths on tower placement/removal).
- **Flying enemies:** path on the **baked terrain grid** (`LevelLoader.IsBaseWalkable`) so they soar over towers; compute once, never re-path; spawn elevated by `EnemyDefinition.FlightHeight`.
- Content: ~10 enemy definitions/prefabs (Crystal Golem / Cyclops / Ent variants, Undead Drake = the flying test enemy), 10 wave definitions.
### Lobby, connection & races
- `MainMenu` + `Lobby` scenes; Direct-IP via `NetworkBootstrap` (the single seam for the deferred Steam swap) + `UnityTransport`; `LobbyService`, `SessionFlow`; **Quick Start** dev shortcut.
- Race data + selection UI: `RaceDefinition`, `RaceRegistry`, 16-slot `RaceId`, `RaceSelectionOverlay`. Two placeholder races (both use the default builder). **Note:** "Race" is the code term; the roguelike design calls this concept "Builder" — naming reconciliation is an open decision.
### Economy
- `PlayerGoldManager` (per-player, server-validated spend/award), `GoldConfig` (starting gold, per-wave kill rewards, completion + no-leak bonuses), `WaveManager` orchestration (prep countdown, per-zone spawning, kill-gold attribution, lives pool, Victory/Defeat).
### Roguelike — tower deck & draft (the current focus)
- **`PlayerTowerDeck`** (per-player): the growable set of unlocked `TowerTypeId`s. Starts at the base set (seeded by `WaveManager` from `TowerPlacementManager.startingDeck`); placement is gated server-side (`TowerNotInDeck` rejection); the HUD build grid reads the local deck and rebuilds live on change. **Merged to `main`.**
- **Draft system (Slice 1 — spine + "new tower"):** `DraftOption` (abstract SO) / `NewTowerDraftOption`, `DraftPool` (scene singleton catalog), `PlayerDraft` (per-player offered set + pick/buy-roll RPCs), `DraftService` (server weighted-random generation + offer/auto-resolve). The **prep phase is the draft window** (wave 1's prep = the match-start draft); unpicked drafts auto-resolve at prep end. Gold **"Buy Roll"** purchases an extra roll any time. Non-modal HUD overlay. **On branch `feature/draft-system`, verified working in-engine; pending commit/merge as of session end.**
- Three base towers exist as data: **Basic Arrow** (ground+air single-target), **Siege Cannon** (ground-only splash), **Wall** (2×2, no damage, maze-shaping). *(Coworkers have begun replacing the placeholder cube visuals with cones.)*
### Paint system (PAUSED)
- In-match paint (R/G/B + Reset) recolors owned towers and drives effects (Red=Splash, Green=Poison, Blue=Cold), server-authoritative. **Frozen** pending the roguelike reconciliation decision (it overlaps with systemic damage-type upgrades).
### Level authoring
- In-engine `TD.Levels` authoring volumes (player zones, spawners, leak exits, goals) + `LevelData` + a bake pipeline producing walkability/placement/owner grids.
### HUD & dev tools
- `HUDController` (UI Toolkit): gold/wave/lives/leaks, scoreboard, minimap, selection portrait + context panel, build/paint command grid, build progress, match-end overlay, chat, buff menu, and the new **draft overlay**.
- `DevWaveControls`: **F9 / "Force Next Wave"**, **F8 / "Grant Next Tower"** (dev stand-in for deck growth).
---
## Known placeholders & technical debt
- **Visuals are placeholder** throughout (primitive/cone towers, sourced enemy models). See roadmap art TODOs.
- **Enemies lack idle animation support** — needs adding across all enemy prefabs.
- **Tower footprint visuals don't fill their 2×2 space** — the cone/mesh sits centered and reads smaller than the tile area it occupies; should visually reflect the full footprint.
- **Two redundant tower lists:** `TowerPlacementManager.towerDefinitions[]` (by-index catalog) and `TowerRegistry.definitions[]` (by-name, for instance resolution). Candidate for consolidation.
- **Catalog index 0 is a reserved sentinel** (valid `TowerTypeId`s start at 1) — easy to forget when wiring the catalog in the inspector.
- **Catalog-index identifiers aren't session-stable** — blocks cross-match persistence until a stable ID is added.
- **Paint system frozen**; **Race vs Builder** naming unresolved; the gold **"Buy Roll available any time"** rule is provisional and may change.
- **Stubbed/unbuilt:** tower Upgrade/Sell actions (HUD buttons disabled), enemy resistances/weaknesses, in-match race-pick countdown.