From f313527b20f2847b7297741f53a9442e0cd89c95 Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Mon, 13 Apr 2026 15:18:18 -0700 Subject: [PATCH] wip 2 --- as9/AGENTS.md | 184 +++++++++++++++++++++++++++++++++++++++++++++ as9/GAME_DESIGN.md | 99 ++++++++++++++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 as9/AGENTS.md create mode 100644 as9/GAME_DESIGN.md diff --git a/as9/AGENTS.md b/as9/AGENTS.md new file mode 100644 index 0000000..19493c6 --- /dev/null +++ b/as9/AGENTS.md @@ -0,0 +1,184 @@ +# as9-island-dominion Architecture + +## Project Identity + +- Directory: `as9` +- CMake project name: `as9-island-dominion` +- Executable target name: `as9-island-dominion` +- Game name: `Island Dominion` + +## High-Level Structure + +The project is split into five main areas: + +- `src/ecs/`: the homegrown ECS, following the storage and lookup patterns from `as8` +- `src/components/`: plain data components used by the ECS +- `src/systems/`: free-function systems that execute in a fixed order from `main` +- `src/game/`: authoritative game-state structs and pure rules code +- `src/net/`: ASIO TCP networking and protocol serialization + +## ECS Conventions + +The ECS should stay very close to `as8`. + +- `Entity` is a numeric ID type (`size_t`) +- Component type IDs come from a single global component counter in a `.cpp` file +- Each component type uses sparse-set style storage with: +- `data` +- `packed_entities` +- `sparse_indices` +- The central registry owns all component storages via a vector of base pointers, like `Context` in `as8` +- Systems are free functions, not inheritance-heavy classes +- Main owns the registry and executes systems explicitly in order each frame + +`as8` did not need entity destruction, but this game does. `as9` should preserve the same sparse-set design while adding safe entity destruction and component removal for unit and fortress elimination. + +## Game State Split + +Two representations exist on purpose: + +- ECS state: runtime entities used for rendering, selection, and UI interactions +- `GameState`: plain serializable structs used for rules, host authority, and network snapshots + +Authoritative rules should operate on `GameState`, not directly on ECS entities. ECS entities are rebuilt or synchronized from snapshots/state transitions as needed. + +## Authoritative Networking Model + +- The host is always player 1 +- The client is always player 2 +- Both players run the same executable +- The host owns the authoritative `GameState` +- The client never mutates the game directly; it sends intents to the host +- After every accepted state change, the host sends a fresh `GAME_STATE` snapshot to the client + +This keeps validation and turn order in one place and avoids divergence between peers. + +## Networking Responsibilities + +`NetworkManager` owns: + +- the ASIO `io_context` +- host accept/connect logic +- async TCP read/write queues +- decoded inbound message queues for the game layer +- disconnect detection and recovery to the startup screen + +Protocol messages should stay small and explicit: + +- `CONNECT_REQUEST` +- `CONNECT_ACK` +- `GAME_STATE` +- `ACTION_REQUEST` +- `COMBAT_CHOICE` +- `GAME_OVER` + +TCP framing should use one consistent format throughout the project. A small length-prefixed binary format is preferred so snapshots remain predictable and easy to parse. + +## Privacy Rules + +Hands are private information. + +- A local player may always know their own hand +- A received remote snapshot must not expose the opponent's hand contents +- Public state still includes hand size, discard size, deck size, area control, units, fortresses, turn state, and combat state + +## Combat Flow + +Combat is blocking. + +- A move into an enemy-occupied area starts combat immediately +- No other actions are processed until the current combat resolves +- The host waits until both players have submitted a `COMBAT_CHOICE` +- After both choices arrive, the host resolves one combat round +- If both sides still have units, the host requests another round +- When combat ends, the host checks fortress capture/removal and win conditions, then broadcasts the updated snapshot + +## Rules Ownership + +`GameRules` should provide pure functions for: + +- board adjacency checks +- area controller calculation +- resource collection and cap enforcement +- action validation +- action application +- combat round resolution +- fortress capture/removal logic +- win-condition checks + +Pure rules code keeps the host logic testable and makes snapshot synchronization simpler. + +## Rendering and UI Ownership + +- `RenderSystem` draws the board, areas, units, fortresses, and highlights using raylib-cpp types +- `UISystem` draws the startup screen, HUD, combat overlay, disconnect notices, and game-over screen +- `InputSystem` translates local mouse/keyboard input into high-level actions or combat choices + +The local host can apply validated local actions directly through the host game-logic path. The client instead packages equivalent intents into `ACTION_REQUEST` or `COMBAT_CHOICE` messages. + +## Frame Update Order + +Each frame should follow a stable order: + +1. Pump network events and decode inbound messages +2. Update local input and build pending local intents +3. Apply host-side game logic if this instance is authoritative +4. Update ECS/view state from the latest authoritative snapshot +5. Draw world and UI + +This order keeps render state consistent with the latest confirmed game state. + +## Build and Library Layout + +- Root build file: `as9/CMakeLists.txt` +- Raylib wrapper comes from `../raylib-cpp` following the `as6` pattern +- ASIO lives in `as9/lib/asio` +- Include path should be `lib/asio/asio/include` +- Compile definitions should include: +- `ASIO_STANDALONE` +- `ASIO_NO_DEPRECATED` + +Platform link requirements: + +- Linux: `pthread` +- Windows: `ws2_32` and `mswsock` + +## Initial Source Layout + +Planned layout: + +- `src/ecs/Entity.hpp` +- `src/ecs/ComponentStorage.hpp` +- `src/ecs/Registry.hpp` +- `src/ecs/System.hpp` +- `src/components/TransformComponent.hpp` +- `src/components/RenderComponent.hpp` +- `src/components/UnitComponent.hpp` +- `src/components/FortressComponent.hpp` +- `src/components/AreaComponent.hpp` +- `src/components/PlayerComponent.hpp` +- `src/components/CardComponent.hpp` +- `src/components/NetworkComponent.hpp` +- `src/systems/RenderSystem.hpp/.cpp` +- `src/systems/InputSystem.hpp/.cpp` +- `src/systems/NetworkSystem.hpp/.cpp` +- `src/systems/GameLogicSystem.hpp/.cpp` +- `src/systems/CombatSystem.hpp/.cpp` +- `src/systems/UISystem.hpp/.cpp` +- `src/game/GameState.hpp` +- `src/game/GameRules.hpp/.cpp` +- `src/game/CardDeck.hpp/.cpp` +- `src/net/Protocol.hpp` +- `src/net/NetworkManager.hpp/.cpp` +- `src/main.cpp` + +## Implementation Priorities + +Build the project in this order: + +1. CMake and third-party wiring +2. ECS infrastructure split from the `as8` pattern +3. Serializable game state and rules +4. Networking protocol and manager +5. UI flow for startup, match, combat, disconnect, and game over +6. Rendering polish and interaction cleanup diff --git a/as9/GAME_DESIGN.md b/as9/GAME_DESIGN.md new file mode 100644 index 0000000..42b1087 --- /dev/null +++ b/as9/GAME_DESIGN.md @@ -0,0 +1,99 @@ +# Codex Prompt: Island Dominion + +## Project Overview + +**Name:** Island Dominion +**Type:** 2-player competitive strategy game (no AI opponent — human vs human over the network) +**Renderer:** raylib-cpp (C++ wrapper for raylib — prefer raylib-cpp types and methods over raw raylib where available) +**Networking:** ASIO (standalone, header-only, no Boost) +**Architecture:** ECS (homegrown, modelled after as8) +**Build system:** CMake (modelled after as6) +**Language:** C++17 or later + +--- + +## Game Design + +### Objective +Win by being the first player to control 9 of the 16 areas on a 4×4 grid, **or** by eliminating all of the opponent's units and fortresses. + +### Board +- 4×4 grid of areas (16 total). +- Each area can hold any number of units from one or both players, and at most one fortress. +- An area is **controlled** by a player if: + - They are the only player with units there, **OR** + - There are no units in the area but they have a fortress there. +- If both players have units in the same area, the area is **contested** (no controller). + +### Resources +- Single resource type (integer). No resource types — just a count per player. +- Each player starts with **8 resources**. +- At the **start of each turn**, the active player gains **1 resource per area they currently control**, capped at 20 total. + +### Units +Three unit tiers: + +| Type | Power | Build Cost | +|--------|-------|-----------| +| Light | 1 | 1 | +| Normal | 2 | 2 | +| Heavy | 3 | 3 | + +### Fortresses +- Build cost: **3 resources**. +- Only one fortress per area. +- Provides area control when no units are present. +- A fortress is **captured (removed)** when the opposing player wins combat in that area. +- A tie in combat does **not** remove the area's fortress. + +### Cards +- Shared deck, shuffled at game start. +- Card distribution: **eight +1 Power**, **five +2 Power**, **three +3 Power** cards. +- Each player draws **1 card at the start of their turn** (after collecting resources). +- Each player starts with **3 cards** dealt from the deck. +- Cards are played during combat (see below). Played cards are discarded. +- Cards are private information — opponents do not see your hand. + +### Turn Structure +Each turn: +1. **Collect resources** (controlled areas → +1 res each, cap 20). +2. **Draw a card** (1 from top of deck). +3. **Take 2 actions** (any combination, same action can be repeated): + - **Move a unit** — move one of your units to an adjacent area (orthogonally adjacent). If the destination contains enemy units, **combat is triggered immediately** and the move action ends (combat resolution happens before the next action). + - **Build a unit** — spend resources to place a new unit in any area you control. + - **Build a fortress** — spend 3 resources to place a fortress in any area you control that does not already have a fortress. + - **Pass** — do nothing, consuming one action. +4. **End turn** — control passes to the opponent. + +### Combat System +Combat occurs when a player moves a unit into an area containing enemy units. + +**Combat round (repeat until one side has no units):** + +1. Both players **secretly choose**: + - One of their units in the area to fight. + - A number of resources to spend (0 to min(their resources, 3)). + - Optionally, one card from their hand to play. + +2. **Simultaneous reveal** — both players' choices are shown at the same time. + +3. **Calculate Power:** + `Power = Unit tier + Resources spent + Card bonus (if played)` + +4. **Resolve:** + - Higher power → opponent's chosen unit is destroyed. + - Tie → both chosen units are destroyed. + +5. **Resources are spent** (deducted from both players) and **cards are discarded**. + +6. If both sides still have units, repeat from step 1. + +**After combat ends:** +- If one player has units remaining and the other does not: + - The winner controls the area. + - If the loser had a fortress there, it is removed. +- If both sides lost all units (series of ties): the area reverts to fortress control (if any), otherwise uncontrolled. + +### Win Condition (checked after every action and after every combat) +- First player to control **9 or more areas** wins. +- If a player has **zero units AND zero fortresses** on the board, they lose immediately.