commit 95c629a42ec49ca11cbadcc51723ccdb55869fab Author: Keith Solomon Date: Sun Mar 15 09:41:22 2026 -0500 ✨feature: Initial commit diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..dd34e44 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "workbench.colorCustomizations": { + "tree.indentGuidesStroke": "#3d92ec", + "activityBar.background": "#092D5A", + "titleBar.activeBackground": "#0D3F7E", + "titleBar.activeForeground": "#F7FAFE", + "titleBar.inactiveBackground": "#092D5A", + "titleBar.inactiveForeground": "#F7FAFE", + "statusBar.background": "#092D5A", + "statusBar.foreground": "#F7FAFE", + "statusBar.debuggingBackground": "#092D5A", + "statusBar.debuggingForeground": "#F7FAFE", + "statusBar.noFolderBackground": "#092D5A", + "statusBar.noFolderForeground": "#F7FAFE" + } +} diff --git a/Notes/codex session.txt b/Notes/codex session.txt new file mode 100644 index 0000000..99c9c6f --- /dev/null +++ b/Notes/codex session.txt @@ -0,0 +1 @@ +codex resume 019cef5b-b2b0-7ad2-9267-f5cb63aca9cd diff --git a/Planning/DATA_MODEL.md b/Planning/DATA_MODEL.md new file mode 100644 index 0000000..cb05be7 --- /dev/null +++ b/Planning/DATA_MODEL.md @@ -0,0 +1,829 @@ +# 2D6 Dungeon Data Model + +## Purpose +This document defines the data structures for a web-based version of **2D6 Dungeon**. + +It translates the rules and content inventory into implementation-ready models that can be mirrored in: + +- TypeScript interfaces +- Zod schemas +- JSON content files +- application state stores + +The main design rule is: + +- **Core Rules become behavior** +- **Tables Codex becomes data** + +## Modeling Principles + +1. Keep static content separate from mutable game state. +2. Prefer normalized content with stable IDs over embedded freeform blobs. +3. Preserve raw dice values when rules depend on primary vs secondary die. +4. Make table references explicit so missing codex data can be validated. +5. Represent rules outcomes as structured effects where possible. +6. Keep room generation state deterministic enough to save, reload, and replay. + +## Data Layers + +### 1. Static Content +Read-only data encoded from the books. + +Examples: + +- weapons +- manoeuvres +- armour +- potions +- scrolls +- room tables +- loot tables +- creature cards +- town service tables + +### 2. Campaign State +Long-lived player progress across multiple delves. + +Examples: + +- adventurer progression +- unlocked levels +- stored treasure +- completed side quests +- campaign log + +### 3. Run State +The current dungeon expedition. + +Examples: + +- current level +- generated rooms +- current room +- active combat +- carried inventory +- temporary effects + +### 4. Rules Action/Result Layer +Transient structures used by the engine. + +Examples: + +- roll results +- table lookups +- combat actions +- generated room results +- effect application results + +## ID Conventions + +Use stable string IDs everywhere. + +Recommended formats: + +- `weapon.sword` +- `manoeuvre.exact-strike` +- `armour.leather-cuirass` +- `scroll.healing-word` +- `table.l1sr` +- `creature.level1.giant-rat` +- `room.level1.001` +- `run.current` + +Benefits: + +- easier JSON authoring +- easier save/load compatibility +- simpler references across content files + +## Static Content Models + +### ContentPack +Top-level grouping for encoded codex content. + +```ts +type ContentPack = { + version: string; + sourceBooks: string[]; + tables: TableDefinition[]; + weapons: WeaponDefinition[]; + manoeuvres: ManoeuvreDefinition[]; + armour: ArmourDefinition[]; + items: ItemDefinition[]; + potions: PotionDefinition[]; + scrolls: ScrollDefinition[]; + creatures: CreatureDefinition[]; + roomTemplates: RoomTemplate[]; + townServices: TownServiceDefinition[]; +}; +``` + +### TableDefinition +Generic representation for codex tables. + +```ts +type DiceKind = "d3" | "d6" | "2d6" | "d66"; + +type TableDefinition = { + id: string; + code: string; + name: string; + category: + | "generic" + | "random-list" + | "loot" + | "level" + | "optional" + | "town" + | "room"; + level?: number; + page: number; + diceKind: DiceKind; + usesModifiedRangesRule?: boolean; + entries: TableEntry[]; + notes?: string[]; + mvp: boolean; +}; + +type TableEntry = { + key: string; + min?: number; + max?: number; + exact?: number; + d66?: number; + label: string; + text?: string; + effects?: RuleEffect[]; + references?: ContentReference[]; +}; +``` + +### WeaponDefinition + +```ts +type WeaponDefinition = { + id: string; + name: string; + category: "melee" | "ranged"; + handedness: "one-handed" | "two-handed"; + baseDamage: number; + allowedManoeuvreIds: string[]; + tags: string[]; + startingOption: boolean; +}; +``` + +### ManoeuvreDefinition + +```ts +type ManoeuvreDefinition = { + id: string; + name: string; + weaponCategories: string[]; + shiftCost?: number; + disciplineModifier?: number; + precisionModifier?: number; + damageModifier?: number; + exactStrikeBonus?: boolean; + interruptRule?: string; + effectText?: string; + mvp: boolean; +}; +``` + +### ArmourDefinition + +```ts +type ArmourDefinition = { + id: string; + name: string; + armourValue: number; + penalties?: { + shift?: number; + discipline?: number; + precision?: number; + }; + deflectionRule?: string; + startingOption: boolean; + valueGp?: number; + mvp: boolean; +}; +``` + +### ItemDefinition +Base model for loot and utility items. + +```ts +type ItemDefinition = { + id: string; + name: string; + itemType: + | "gear" + | "treasure" + | "quest" + | "herb" + | "rune" + | "misc" + | "ration" + | "light-source"; + stackable: boolean; + consumable: boolean; + valueGp?: number; + weight?: number; + rulesText?: string; + effects?: RuleEffect[]; + mvp: boolean; +}; +``` + +### PotionDefinition + +```ts +type PotionDefinition = { + id: string; + name: string; + tableSource: string; + useTiming: "combat" | "exploration" | "town" | "any"; + effects: RuleEffect[]; + valueGp?: number; + mvp: boolean; +}; +``` + +### ScrollDefinition + +```ts +type ScrollDefinition = { + id: string; + name: string; + tableSource: string; + castCheck?: { + diceKind: "d6" | "2d6"; + successMin?: number; + successMax?: number; + }; + onSuccess: RuleEffect[]; + onFailureTableCode?: string; + valueGp?: number; + startingOption: boolean; + mvp: boolean; +}; +``` + +### CreatureDefinition + +```ts +type CreatureDefinition = { + id: string; + name: string; + level: number; + category: string; + hp: number; + attackProfile: { + discipline: number; + precision: number; + damage: number; + numberAppearing?: string; + }; + defenceProfile?: { + armour?: number; + specialRules?: string[]; + }; + xpReward?: number; + lootTableCodes?: string[]; + interruptRules?: string[]; + traits?: string[]; + sourcePage: number; + mvp: boolean; +}; +``` + +### RoomTemplate +Structured output for room generation tables. + +```ts +type RoomTemplate = { + id: string; + level: number; + roomClass: "normal" | "small" | "large" | "special" | "start" | "stairs"; + tableCode: string; + tableEntryKey: string; + title: string; + text?: string; + dimensions?: { + width: number; + height: number; + }; + exits?: ExitTemplate[]; + encounterRefs?: ContentReference[]; + objectRefs?: ContentReference[]; + tags: string[]; + mvp: boolean; +}; + +type ExitTemplate = { + direction?: "north" | "east" | "south" | "west"; + exitType: "open" | "door" | "locked" | "secret" | "shaft" | "stairs"; + destinationLevel?: number; +}; +``` + +### TownServiceDefinition + +```ts +type TownServiceDefinition = { + id: string; + name: string; + serviceType: "market" | "temple" | "tavern" | "healer" | "smith" | "quest"; + tableCodes?: string[]; + costRules?: string[]; + effects?: RuleEffect[]; + mvp: boolean; +}; +``` + +## Shared Supporting Models + +### ContentReference + +```ts +type ContentReference = { + type: + | "table" + | "weapon" + | "manoeuvre" + | "armour" + | "item" + | "potion" + | "scroll" + | "creature" + | "room" + | "service"; + id: string; +}; +``` + +### RuleEffect +Structured effects should be preferred over plain text whenever the outcome is mechanical. + +```ts +type RuleEffect = { + type: + | "gain-xp" + | "gain-gold" + | "heal" + | "take-damage" + | "modify-shift" + | "modify-discipline" + | "modify-precision" + | "apply-status" + | "remove-status" + | "add-item" + | "remove-item" + | "start-combat" + | "reveal-exit" + | "move-level" + | "log-only"; + amount?: number; + statusId?: string; + target?: "self" | "enemy" | "room" | "campaign"; + referenceId?: string; + notes?: string; +}; +``` + +## Campaign State Models + +### CampaignState + +```ts +type CampaignState = { + id: string; + createdAt: string; + updatedAt: string; + rulesVersion: string; + contentVersion: string; + adventurer: AdventurerState; + unlockedLevels: number[]; + completedLevels: number[]; + townState: TownState; + questState: QuestState[]; + campaignFlags: string[]; + runHistory: RunSummary[]; +}; +``` + +### AdventurerState + +```ts +type AdventurerState = { + id: string; + name: string; + level: number; + xp: number; + hp: { + current: number; + max: number; + base: number; + }; + stats: { + shift: number; + discipline: number; + precision: number; + }; + weaponId: string; + manoeuvreIds: string[]; + armourId?: string; + favour: Record; + statuses: StatusInstance[]; + inventory: InventoryState; + progressionFlags: string[]; +}; +``` + +### InventoryState + +```ts +type InventoryState = { + carried: InventoryEntry[]; + equipped: InventoryEntry[]; + stored: InventoryEntry[]; + currency: { + gold: number; + }; + rationCount: number; + lightSources: InventoryEntry[]; +}; + +type InventoryEntry = { + definitionId: string; + quantity: number; + identified?: boolean; + charges?: number; + notes?: string; +}; +``` + +### TownState + +```ts +type TownState = { + visits: number; + knownServices: string[]; + stash: InventoryEntry[]; + pendingSales: InventoryEntry[]; + serviceFlags: string[]; +}; +``` + +### QuestState + +```ts +type QuestState = { + id: string; + title: string; + status: "available" | "active" | "completed" | "failed"; + progressFlags: string[]; + rewardText?: string; +}; +``` + +### RunSummary + +```ts +type RunSummary = { + runId: string; + startedAt: string; + endedAt?: string; + deepestLevel: number; + roomsVisited: number; + creaturesDefeated: string[]; + xpGained: number; + treasureValue: number; + outcome: "escaped" | "defeated" | "saved-in-progress"; +}; +``` + +## Run State Models + +### RunState + +```ts +type RunState = { + id: string; + campaignId: string; + status: "active" | "paused" | "completed" | "failed"; + startedAt: string; + currentLevel: number; + currentRoomId?: string; + dungeon: DungeonState; + adventurerSnapshot: AdventurerState; + activeCombat?: CombatState; + log: LogEntry[]; + pendingEffects: RuleEffect[]; +}; +``` + +### DungeonState + +```ts +type DungeonState = { + levels: Record; + revealedPercentByLevel: Record; + globalFlags: string[]; +}; +``` + +### DungeonLevelState + +```ts +type DungeonLevelState = { + level: number; + themeName?: string; + rooms: Record; + discoveredRoomOrder: string[]; + stairsUpRoomId?: string; + stairsDownRoomId?: string; + secretDoorUsed?: boolean; + exhaustedExitSearch?: boolean; +}; +``` + +### RoomState + +```ts +type RoomState = { + id: string; + level: number; + templateId?: string; + position: { + x: number; + y: number; + }; + dimensions: { + width: number; + height: number; + }; + roomClass: "normal" | "small" | "large" | "special" | "start" | "stairs"; + exits: RoomExitState[]; + discovery: { + generated: boolean; + entered: boolean; + cleared: boolean; + searched: boolean; + }; + encounter?: EncounterState; + objects: RoomObjectState[]; + notes: string[]; + flags: string[]; +}; +``` + +### RoomExitState + +```ts +type RoomExitState = { + id: string; + direction: "north" | "east" | "south" | "west"; + exitType: "open" | "door" | "locked" | "secret" | "shaft" | "stairs"; + discovered: boolean; + traversable: boolean; + leadsToRoomId?: string; + destinationLevel?: number; +}; +``` + +### EncounterState + +```ts +type EncounterState = { + id: string; + sourceTableCode?: string; + creatureIds: string[]; + resolved: boolean; + surprise?: boolean; + rewardPending?: boolean; +}; +``` + +### RoomObjectState + +```ts +type RoomObjectState = { + id: string; + objectType: "container" | "altar" | "corpse" | "hazard" | "feature" | "quest"; + sourceTableCode?: string; + interacted: boolean; + hidden?: boolean; + effects?: RuleEffect[]; + notes?: string; +}; +``` + +## Combat State Models + +### CombatState + +```ts +type CombatState = { + id: string; + round: number; + actingSide: "player" | "enemy"; + fatigueDie?: number; + player: CombatantState; + enemies: CombatantState[]; + selectedManoeuvreId?: string; + lastRoll?: RollResult; + pendingInterrupt?: InterruptState; + combatLog: LogEntry[]; +}; +``` + +### CombatantState + +```ts +type CombatantState = { + id: string; + name: string; + sourceDefinitionId?: string; + hpCurrent: number; + hpMax: number; + shift: number; + discipline: number; + precision: number; + armourValue?: number; + statuses: StatusInstance[]; + traits: string[]; +}; +``` + +### InterruptState + +```ts +type InterruptState = { + source: "player" | "enemy"; + trigger: string; + effectText: string; + resolved: boolean; +}; +``` + +## Rules Action/Result Models + +### RollResult + +```ts +type RollResult = { + diceKind: DiceKind; + rolls: number[]; + primary?: number; + secondary?: number; + total?: number; + modifier?: number; + modifiedTotal?: number; + clamped?: boolean; +}; +``` + +### TableLookupResult + +```ts +type TableLookupResult = { + tableId: string; + entryKey: string; + roll: RollResult; + entry: TableEntry; +}; +``` + +### RoomGenerationResult + +```ts +type RoomGenerationResult = { + room: RoomState; + consumedLookups: TableLookupResult[]; + createdConnections: string[]; + warnings: string[]; +}; +``` + +### ActionResolution + +```ts +type ActionResolution = { + success: boolean; + effects: RuleEffect[]; + logEntries: LogEntry[]; + warnings?: string[]; +}; +``` + +### StatusInstance + +```ts +type StatusInstance = { + id: string; + source?: string; + duration?: "round" | "combat" | "room" | "run" | "permanent"; + value?: number; + notes?: string; +}; +``` + +### LogEntry + +```ts +type LogEntry = { + id: string; + at: string; + type: + | "system" + | "roll" + | "combat" + | "loot" + | "room" + | "town" + | "progression"; + text: string; + relatedIds?: string[]; +}; +``` + +## MVP Priority Models + +For the first playable version, fully define these first: + +- `WeaponDefinition` +- `ManoeuvreDefinition` +- `ArmourDefinition` +- `PotionDefinition` +- `ScrollDefinition` +- `CreatureDefinition` +- `TableDefinition` +- `CampaignState` +- `RunState` +- `RoomState` +- `CombatState` +- `RollResult` + +The following can start simplified: + +- `QuestState` +- `TownServiceDefinition` +- `RoomObjectState` +- favour tracking +- optional table effects + +## Recommended File Layout + +Suggested future code structure: + +```text +src/ + data/ + content-pack.ts + tables/ + creatures/ + rooms/ + items/ + rules/ + dice/ + tables/ + dungeon/ + combat/ + progression/ + state/ + campaign/ + run/ + combat/ + types/ + content.ts + state.ts + rules.ts +``` + +## Validation Rules + +Add schema validation for these cases early: + +1. Every referenced content ID must exist. +2. Every table code used by rules must be present in the content pack. +3. Every room exit must be reciprocal after generation. +4. HP values must never exceed max unless a rule explicitly allows it. +5. Inventory quantities must never drop below zero. +6. Level-specific tables must match their dungeon level. +7. Creature source pages and table codes should remain traceable to the books. + +## Immediate Next Build Step + +After this document, the most useful implementation artifact is: + +- `IMPLEMENTATION_NOTES.md` + +That file should capture the digital-only rulings the books leave ambiguous, especially: + +- map generation heuristics +- secret door fallback behavior +- room geometry/rendering rules +- handling of subjective or flavor-heavy outcomes +- what gets automated versus explicitly player-confirmed diff --git a/Planning/GAME_SPEC.md b/Planning/GAME_SPEC.md new file mode 100644 index 0000000..3a32fab --- /dev/null +++ b/Planning/GAME_SPEC.md @@ -0,0 +1,798 @@ +# 2D6 Dungeon Game Spec + +## Purpose +This document translates the rulebooks into a software-oriented specification for a web-based version of **2D6 Dungeon**. + +It is based on: + +- `2D6 Dungeon - Core Rules.pdf` +- `2D6 Dungeon - Tables Codex.pdf` + +The goal is not to reproduce the books page-for-page. The goal is to define: + +- the gameplay loop +- the rules engine responsibilities +- the data/content model +- the app state model +- the MVP boundaries +- the places where digital adaptation decisions are required + +## Product Definition +2D6 Dungeon is a **solo dungeon-crawling campaign game** with: + +- procedural room generation +- table-driven events +- deterministic combat resolution around dice combinations +- long-term character progression +- repeated dungeon delves separated by town phases + +The web version should act as: + +- rules engine +- campaign tracker +- character sheet +- dungeon map sheet +- combat assistant +- table resolver + +## Design Goals + +1. Preserve the core paper-game rules where practical. +2. Remove manual bookkeeping and repeated table lookups. +3. Make game state transparent through logs and visible calculations. +4. Support long-running campaigns with reliable save/resume. +5. Keep rules logic separate from content data. + +## Non-Goals For MVP + +- full content parity across all 10 levels +- expansion support +- multiplayer +- realtime action gameplay +- heavy audiovisual presentation + +## Canonical Gameplay Loop + +The top-level loop is: + +1. Create or continue an adventurer. +2. Enter a dungeon level. +3. Generate and reveal rooms as exploration advances. +4. Resolve room encounters, creatures, objects, and events. +5. Fight creatures through manoeuvre-based combat. +6. Gain loot, XP, items, conditions, and favour. +7. Continue deeper, or leave the dungeon through an exit shaft. +8. Return to town for selling, buying, services, and preparation. +9. Re-enter the dungeon and continue campaign progress. +10. Complete all 10 levels and claim legendary reward. + +## Core Systems + +### 1. Dice System + +Supported roll types: + +- `D3` +- `D6` +- `2D6` +- `D66` + +Important rules: + +- `D66` uses two distinguishable dice. +- Primary die is the tens digit. +- Secondary die is the ones digit. +- Some systems require preserving both values individually, not just the combined number. +- The **Modified Ranges Rule** clamps modified values to the nearest valid table result when a modifier pushes the result out of range. + +Software requirement: + +- represent raw dice, modified dice, and combined result separately +- keep roll history for audit/logging + +### 2. Adventurer Model + +The player controls a single adventurer with: + +- name +- level +- XP +- HP +- baseline HP +- Shift +- Discipline +- Precision +- weapon +- manoeuvres +- worn armour +- inventory +- potions +- scrolls +- quest items +- favour points +- temporary statuses +- town storage / loot lockup + +Key creation rules from Core Rules: + +- starts at Level 1 +- starts with 10 HP baseline +- starts with `+2 Shift`, `+1 Discipline`, `0 Precision` +- chooses one main weapon +- chooses starting manoeuvres tied to weapon +- chooses one starting armour piece +- starts with flint and steel, lantern, 3 rations, pouch, wax sealing kit, backpack +- starts with one starting scroll +- starts with one healing potion + +### 3. Progression System + +The game is a long-form campaign, not a single roguelike run. + +Progression includes: + +- XP from kills and special actions +- level ups during dungeon exploration +- immediate HP gain on level-up +- unlocking stronger manoeuvres +- accumulating wealth and equipment +- clearing dungeon levels over multiple visits +- completing side quests +- earning legendary status after completing all 10 levels + +Software requirement: + +- campaign progress and run progress must be stored separately + +### 4. Dungeon Structure + +The main dungeon consists of 10 levels. + +From the Core Rules and Tables Codex: + +- Level 1: The Entry +- Level 2: The Domain +- Level 3: The Crypt +- Level 4: The Haunted +- Level 5: The Infernal +- Level 6: The Cultist Den +- Level 7: The Menagerie +- Level 8: The Monster Maze +- Level 9: The Cursed +- Level 10: The Dungeon Lords + +Each level has: + +- its own room tables +- its own small room table +- its own large room table +- its own exit type table +- level-specific encounter tables +- level-specific thematic content + +### 5. Room Generation + +Exploration is driven by generating rooms as encountered. + +Rules to support: + +- dungeon level has an outer boundary +- entrance room is generated with special rules +- other rooms use `D66` dimensions +- doubles may expand room size +- exits are added based on rules and/or exit tables +- small rooms are handled by dedicated small room tables +- large room tables are separate +- the final reachable room gets stairs to next level +- secret door fallback is used if forward progress stalls before enough map reveal +- no erasing map; room state becomes permanent once generated + +Digital adaptation requirement: + +- store both logical connectivity and rendered geometry +- do not rely on user drawing + +### 6. Room Resolution + +Once a room is generated, the app resolves: + +- room identity from the level room table +- room description / flavor +- encounter instructions +- exits and exit types +- uniqueness constraints +- special unique room percentage gating + +Room resolution may trigger: + +- combat +- loot tables +- item discovery +- prisoner encounters +- side quests +- environment interactions +- dungeon events + +### 7. Combat System + +Combat is the most mechanics-heavy system in the game. + +Core concepts: + +- all encountered enemies are typically hostile +- player generally attacks first +- manoeuvres correspond to exact 2-die combinations +- Shift Points can move dice values up or down +- successful exact strike gives damage bonus +- creatures may have interrupt stats that reduce or alter damage +- armour can deflect damage if dice sets match +- fatigue alters combat over time +- combat supports multiple enemies +- thrown weapons can be used and later recovered or lost +- potion bombs can be used before attacks + +Combat must support: + +- player attack resolution +- enemy attack resolution +- damage calculation +- interrupt matching +- armour deflection +- fatigue die progression +- prime attack and mishap cases +- special attacks +- death and unconsciousness rules + +### 8. Magic System + +There are three major magical subsystems: + +- scrolls +- potions +- favour of the gods + +Scrolls: + +- used at the start of combat or outside combat +- require a successful double on `D66` +- Discipline can shift toward a double +- may have dispel doubles +- may invoke failure tables when cast incorrectly + +Potions: + +- can be used at any point, including combat +- occupy potion capacity +- include healing and other magical effects + +Favour: + +- tied to specific subterranean gods +- earned through offerings and game events +- spent through a favour roll +- usable in and out of combat +- governed by per-level usage restrictions + +### 9. Inventory and Item Systems + +The app must track: + +- coins by denomination +- gems by type and quality +- armour +- large/heavy items +- regular items +- potions +- scrolls +- herbs +- runes +- quest items +- containers +- worship items + +Important capacity rules: + +- no hard limit on treasure and small items +- max 10 large/heavy items +- max 5 potions +- max 3 magic scrolls + +### 10. Status and Condition Systems + +At minimum the engine must support: + +- Bloodied +- Soaked +- fever / HP loss from unresolved conditions +- damaged armour +- destroyed armour +- temporary stat bonuses +- temporary effects from town or magic + +### 11. Town Phase + +When the player exits the dungeon, they can return to town. + +Town systems include: + +- market buying +- market selling and bartering +- engraver for runes +- herbalist +- tavern +- temple +- town hall arm wrestle +- loot lockup storage +- prisoner liberation rewards + +Town is a rules-heavy phase, not just a menu. + +### 12. Narrative / Judgment Systems + +Some rules rely on interpretation rather than strict table lookup. + +Examples: + +- inventive usage +- making common-sense map decisions +- deciding the last room in a level +- contextual interactions with room objects + +Digital adaptation strategy: + +- make the default outcome deterministic where possible +- allow developer-configured override hooks +- keep an action log explaining why a result was chosen + +## Content Structure From The Tables Codex + +The Tables Codex divides content into major categories. + +### Generic Tables + +These include: + +- armour +- enchanted armour +- failed casting +- gem combinations +- magic items +- magic potions +- magic scrolls +- portcullis levers +- recovery from unconsciousness +- stolen items +- starting armour +- starting scrolls +- gem values +- miscellaneous item values +- weapon manoeuvres + +### Random Lists Tables + +These include: + +- random armour +- empty containers +- gems +- gods +- half ornate items +- herbs +- metal items +- potion lists +- amulets +- magical items +- rings +- wands +- scroll lists +- symbol selection +- worship items +- wrecked items + +### Loot Tables + +These include: + +- bags +- body search +- chests +- pouches +- religious pouches +- religious artifacts +- rubbish piles +- runes +- secret hatches +- sarcophagi +- tables +- tea chests +- urns + +### Level Table Groups + +The codex is grouped by pairs of dungeon levels: + +- Levels 1 and 2 +- Levels 3 and 4 +- Levels 5 and 6 +- Levels 7 and 8 +- Levels 9 and 10 + +Each group includes some combination of: + +- prisoner encounter tables +- exit type tables +- interruptions/unexpected tables +- patrol or creature tables +- trap tables +- large room tables +- small room tables +- level room tables +- special level-only tables + +### Creature Cards + +Creature content begins in the bestiary section and is used continuously by the combat engine. + +Each creature card contains at least: + +- name +- level +- type +- HP +- XP reward +- Shift +- treasure references +- interrupt stats +- manoeuvres +- mishap/prime attack results +- special attacks +- description + +### Optional Tables + +Optional tables exist and should be modeled as disabled-by-default modules. + +## Proposed Digital Domain Model + +### Core Entities + +Suggested first-pass entities: + +- `Campaign` +- `RunState` +- `Adventurer` +- `DungeonLevelState` +- `RoomState` +- `ExitState` +- `Encounter` +- `CombatState` +- `CreatureInstance` +- `Weapon` +- `Manoeuvre` +- `Armour` +- `Item` +- `Potion` +- `Scroll` +- `Rune` +- `Herb` +- `Quest` +- `God` +- `TownVisit` +- `RollRecord` +- `TableReference` + +### Table Data Model + +Every table entry should be machine-readable. + +Recommended shape: + +- `id` +- `tableId` +- `rollMin` +- `rollMax` +- `conditions` +- `resultType` +- `payload` +- `rulesText` + +This allows: + +- lookup by dice result +- modifier application +- rule-driven dispatch +- UI display using original wording where needed + +### Table Reference Model + +Many rules point to other tables rather than producing direct outcomes. + +Use a normalized reference such as: + +- `tableId` +- `rollType` +- `modifierSource` +- `notes` + +Examples: + +- room directs engine to creature encounter table +- scroll failure points to failed-cast table +- defeated creature points to treasure table + +### Roll Record Model + +The app should preserve a history of important rolls: + +- roll type +- raw dice +- modified dice +- modifiers applied +- final result +- source action +- downstream table used + +This is important for trust, debugging, and rules verification. + +## State Model + +### Campaign State + +Persistent across sessions: + +- adventurer identity and long-term stats +- completed dungeon levels +- loot lockup contents +- permanent favour or unlocked states +- campaign history + +### Run State + +Specific to the current active delve: + +- current dungeon level +- generated rooms +- current position / current room +- discovered exits +- unresolved encounters +- active inventory carried on run +- temporary effects +- current HP +- active combat + +### Room State + +Each room needs: + +- coordinates / geometry +- room table result +- room type +- room description +- exits +- discovered status +- cleared status +- uniqueness flags +- generated encounters +- placed objects / interactables +- notes and markers + +### Combat State + +Combat requires: + +- combatants +- turn / round number +- fatigue die value +- current attack actor +- current dice roll +- available shift +- selected manoeuvre +- damage breakdown +- interrupt events +- armour deflection events +- pending special effects + +## Rules Engine Boundaries + +The rules engine should be pure and UI-agnostic where possible. + +Good engine responsibilities: + +- roll dice +- validate actions +- resolve table lookups +- generate rooms +- resolve combat +- apply rewards and penalties +- advance campaign state + +UI responsibilities: + +- collect player choices +- show map and character sheet +- show logs and calculations +- present available actions +- render table results nicely + +## MVP Definition + +The first playable version should cover: + +- new game +- character creation +- one active adventurer +- Level 1 only +- room generation +- room identification +- encounter resolution +- creature combat +- loot and XP +- levelling +- inventory tracking +- potions and scrolls +- leave dungeon flow +- town visit flow +- save/load + +MVP content can use: + +- Level 1 room tables +- a subset of creature cards +- a subset of armour, potions, scrolls, and loot + +## Recommended Build Sequence + +### Step 1: Encode rules constants and enums + +Start with: + +- roll types +- item categories +- damage modifiers +- statuses +- level themes +- room sizes +- exit types +- creature types + +### Step 2: Encode foundational tables + +Before level-specific content, encode: + +- starting armour +- starting scrolls +- weapon manoeuvres +- armour table +- magic potion table +- magic scroll table +- failed-to-cast table +- gem values +- miscellaneous item values + +### Step 3: Build dice and modifier engine + +This is the base for: + +- combat +- room generation +- table lookups +- town actions + +### Step 4: Build character creation and sheet logic + +This allows the app to: + +- create legal starting characters +- compute carrying limits +- track stats and loadout + +### Step 5: Build Level 1 room generation and resolution + +Use these content families: + +- `EXT1` +- `L1LR` +- `Level 1 Rooms` +- `L1SR` +- Level 1 encounter tables + +### Step 6: Build combat against a controlled subset of creature cards + +Use a small but varied subset first: + +- one humanoid +- one animal +- one patrol/multi-enemy case +- one creature with interrupts + +### Step 7: Build post-room rewards and town return + +Support: + +- treasure +- XP +- inventory updates +- exit shaft search +- market +- temple or tavern + +## Implementation Decisions To Make + +These are not fully dictated by the books and should be decided explicitly. + +### 1. How literal should map generation be? + +Options: + +- simulate full geometric placement +- simplify to node-and-edge rooms while preserving rules + +Recommendation: + +- use full geometry internally for rules fidelity +- render a simplified clean map visually + +### 2. How should inventive usage work? + +Options: + +- fully manual freeform input +- fixed list of context-sensitive actions + +Recommendation: + +- use context-sensitive actions for MVP +- keep a future manual override/admin action + +### 3. How should ambiguous “common sense” rules be handled? + +Recommendation: + +- codify deterministic heuristics +- log them clearly +- document all heuristics in a separate implementation notes file + +## Open Questions + +These should be resolved before a full production implementation: + +1. Will the digital version aim for strict parity with every table text outcome, or allow some streamlining? +2. Should room descriptions be reproduced verbatim in-game, or summarized into structured effects plus flavor text? +3. Should the app keep the original D66-first tactile feel with visible primary/secondary dice, even when automation could hide it? +4. Should optional tables be available as campaign settings at world creation? + +## Immediate Next Deliverables + +After this spec, the most useful follow-up files are: + +1. `content-checklist.json` +2. `DATA_MODEL.md` +3. `IMPLEMENTATION_NOTES.md` + +Suggested order: + +1. create the content checklist from the Tables Codex +2. define exact TypeScript interfaces and schemas +3. record digital-only rules decisions and heuristics + +## Summary + +The web version should be built as a **rules engine plus structured content system**. + +The Core Rules define: + +- flow +- player rules +- combat rules +- progression rules +- town rules + +The Tables Codex defines: + +- lookup content +- room content +- item content +- creature content +- level content + +That separation should be reflected directly in the codebase and content pipeline. diff --git a/Planning/PROJECT_PLAN.md b/Planning/PROJECT_PLAN.md new file mode 100644 index 0000000..57ff1f4 --- /dev/null +++ b/Planning/PROJECT_PLAN.md @@ -0,0 +1,424 @@ +# 2D6 Dungeon Web Adaptation Plan + +## Goal +Build a web-based version of **2D6 Dungeon** that preserves the solo dungeon-crawl loop from the core rules while removing paper bookkeeping and table lookups. + +This plan is based on the core systems described in: + +- `2D6 Dungeon - Core Rules.pdf`, especially: + - character creation and stats + - dungeon room generation + - encounters and combat + - treasure, XP, levelling, and town visits + +## Important Constraint +The Core Rules repeatedly depend on a separate **Tables Codex** for room tables, creature data, item tables, scrolls, treasures, and many event outcomes. That means the project should be split into: + +1. **Rules engine and UI** +2. **Game content/data entry** + +Do not block the app build on full content entry. Use placeholder data for development, then swap in the real tables. + +## Product Vision +The web version should feel like: + +- a guided solo campaign tracker +- a map-and-journal interface +- a fast combat assistant +- a persistent progression game with saves between dungeon runs + +The app should handle: + +- dice rolling and modifiers +- map generation and room state +- combat flow +- inventory and item usage +- XP, levelling, HP, conditions, and favour +- leaving the dungeon and returning to town +- saving and resuming long campaigns + +## Suggested MVP Scope +Build the first playable version around **Level 1 only** plus **town return/save flow**. + +MVP includes: + +- one adventurer +- character creation +- map generation +- room reveal flow +- encounters +- combat with manoeuvres, shift, fatigue, interrupts, armour deflection +- inventory, potions, scrolls, rations +- XP and levelling +- leave dungeon and visit town +- save/load run + +MVP excludes: + +- all 10 levels +- full item catalog +- full god/favour content +- every special room and narrative edge case +- expansions and alternate dungeon ancestries + +## Recommended Build Order + +### Phase 1: Nail down the source material +Outcome: a usable design spec instead of rulebook ambiguity. + +Steps: + +1. Extract the game systems from the Core Rules into a structured design doc. +2. List every referenced external table that lives in the Tables Codex. +3. Build a spreadsheet or JSON checklist for: + - room tables + - small room tables + - creature cards + - armour + - manoeuvres + - scrolls + - potions + - treasures + - market tables + - tavern and temple tables +4. Mark each rule as one of: + - required for MVP + - defer until post-MVP + - content only +5. Decide where the web version will intentionally streamline play without changing balance. + +Definition of done: + +- one concise design spec exists +- one content inventory exists +- MVP rule boundaries are agreed + +### Phase 2: Choose the technical foundation +Outcome: a stack that supports a state-heavy single-player game. + +Recommended stack: + +- Frontend: React + TypeScript +- App framework: Next.js or Vite +- Styling: Tailwind or CSS modules +- State: Zustand or Redux Toolkit +- Data validation: Zod +- Persistence: browser local storage first, optional cloud saves later +- Testing: Vitest + React Testing Library + +Suggested architecture: + +- `rules/` for pure game logic +- `data/` for encoded tables and cards +- `state/` for campaign/run state +- `ui/` for screens and panels + +Definition of done: + +- repo created +- TypeScript configured +- state and data folder structure in place + +### Phase 3: Model the game data +Outcome: rules can run against structured content instead of ad hoc text. + +Create schemas for: + +- adventurer +- weapon +- manoeuvre +- armour +- room template +- creature +- item +- potion +- scroll +- rune +- herb +- god favour +- town action +- dungeon level state +- room state +- combat state + +Key rule-specific fields to support: + +- D6, 2D6, and D66 rolls +- primary vs secondary die +- modified ranges rule +- exact strike +- shift points +- discipline and precision modifiers +- interrupt matches +- fatigue die +- unique room rules +- room reveal percentage rules + +Definition of done: + +- all MVP entities have typed schemas +- sample Level 1 data can be loaded without UI + +### Phase 4: Build the rules engine +Outcome: the game can run in code before the interface is polished. + +Implement in this order: + +1. Dice utilities + - D3 + - D6 + - 2D6 + - D66 + - modifier clamping +2. Character creation + - starting stats + - starting weapon/manoeuvres + - starting armour + - starting scroll/potion/items +3. Dungeon generation + - outer boundary + - entrance room + - room dimensions + - exits + - small room detection + - last room / stairs logic + - secret door fallback +4. Room encounter resolution + - identify room + - mark unique rooms + - resolve exits + - queue encounters and interactables +5. Combat engine + - initiative flow + - manoeuvre matching + - shifting + - exact strike + - interrupt reduction + - armour deflection + - fatigue die progression + - multiple enemies + - thrown weapons and potion bombs +6. Rewards and progression + - XP gain + - levelling + - treasure + - inventory capacity + - HP rules +7. Dungeon traversal rules + - backtracking + - rations between levels + - leaving the dungeon +8. Town loop + - market + - engraver + - herbalist + - tavern + - temple + - town hall + +Definition of done: + +- a text-mode test harness can complete a small mock dungeon +- the engine supports save/load state + +### Phase 5: Build the first playable UI +Outcome: a user can complete a short run entirely in the browser. + +Recommended screens: + +1. Start screen + - new run + - continue run +2. Character creation + - choose name, weapon, armour, starting manoeuvres, starting scroll +3. Main dungeon screen + - map panel + - room log + - current encounter panel + - character sheet sidebar +4. Combat modal or panel + - dice rolled + - available shifts + - selected manoeuvre + - damage calculation + - enemy response +5. Town screen + - sell/buy + - services + - evening choice +6. Save summary/history + - current level + - completed levels + - inventory and stats + +UI priority: + +- clarity over visual flourish at first +- remove manual arithmetic wherever possible +- keep a visible action log so players can verify the rules + +Definition of done: + +- one full Level 1 run is playable without touching code + +### Phase 6: Add persistence and campaign progression +Outcome: the app supports the real long-form game structure. + +Steps: + +1. Save campaign state automatically after each action. +2. Keep separate records for: + - current run + - completed dungeon levels + - town inventory/storage + - discovered permanent upgrades +3. Add import/export save support as JSON. +4. Add a run history log for debugging and player trust. + +Definition of done: + +- browser refresh does not lose progress +- campaign can resume from town or dungeon + +### Phase 7: Expand content safely +Outcome: the app grows without turning brittle. + +Steps: + +1. Add the remaining dungeon levels one at a time. +2. Add full creature card coverage. +3. Add gods, herbs, runes, potion bombs, and optional rules. +4. Add side quests and unique room edge cases. +5. Add tool-assisted content validation to catch broken table links. + +Definition of done: + +- all core rulebook content used by the web app is encoded and reachable + +## Concrete Task List + +### Week 1: Pre-production + +- Read the Core Rules and produce a short rules summary. +- Create a missing-content list for the Tables Codex. +- Decide MVP scope. +- Choose framework and state library. +- Set up repo and base TypeScript app. + +### Week 2: Data model and utilities + +- Create TypeScript types and Zod schemas. +- Build dice helpers. +- Build rule helper functions for modifiers and D66 handling. +- Create sample JSON for one weapon set, a few enemies, and a few rooms. + +### Week 3: Dungeon and room flow + +- Implement dungeon grid state. +- Implement room generation. +- Implement room reveal and exit resolution. +- Render a simple map view and room log. + +### Week 4: Combat + +- Implement manoeuvre matching and shift logic. +- Implement enemy attacks, interrupts, armour deflection, and fatigue. +- Build the combat UI panel. +- Add tests for combat edge cases. + +### Week 5: Inventory and progression + +- Add items, rations, potions, scrolls, and capacity rules. +- Add XP, levelling, HP updates, and conditions. +- Add treasure and post-combat rewards. + +### Week 6: Town and persistence + +- Build leave-dungeon flow. +- Build market and service screens. +- Add save/load and autosave. +- Add campaign summary UI. + +### Week 7+: Content expansion + +- Enter full Level 1 data. +- Playtest and fix rule mismatches. +- Add Level 2 and beyond incrementally. + +## Risks To Handle Early + +### 1. Missing data source +The Core Rules alone are not enough for a full digital implementation because they reference the Tables Codex for many outcomes. + +Mitigation: + +- treat content entry as its own track +- start with stub tables +- add a validation layer that checks every table reference exists + +### 2. Rules ambiguity +Some paper-game rules rely on human judgment, especially: + +- inventive usage +- identifying the “last room” +- map common-sense decisions +- narrative interpretation of room text + +Mitigation: + +- make subjective rulings explicit in a digital design doc +- keep a manual override or “GM ruling” option during development + +### 3. Overbuilding too early +Trying to implement all ten levels and all tables before a playable loop exists will slow the project down. + +Mitigation: + +- ship Level 1 first +- use placeholder content where needed +- prioritize end-to-end playability over completeness + +## Testing Strategy + +Test the project at three levels: + +1. Unit tests + - dice logic + - modifiers + - combat resolution + - room generation constraints +2. Integration tests + - create character -> enter dungeon -> resolve room -> fight -> gain loot +3. Manual playtests + - compare digital outcomes against the rulebook + - record every mismatch in a balance/rules log + +Important test cases: + +- D66 primary/secondary ordering +- exact strike bonus +- shift usage limits +- fatigue die progression after round 6 +- multiple enemies +- levelling during combat +- leaving and re-entering a level +- secret door generation when stuck early + +## Nice-To-Have Features After MVP + +- animated dice +- mobile-friendly layout +- codex viewer for room/creature reference +- optional “assist mode” that explains why a rule fired +- seeded runs +- cloud saves +- campaign analytics + +## Best Next Step +If starting today, do these three things first: + +1. Create a `GAME_SPEC.md` summarizing the rules from the PDF. +2. Create a `content-checklist.json` listing every table/card/data dependency from the Tables Codex. +3. Scaffold a TypeScript frontend with a pure `rules/` module and implement dice utilities plus a minimal character state. + +That gets the project moving quickly without waiting for all game content to be entered. diff --git a/Planning/content-checklist.json b/Planning/content-checklist.json new file mode 100644 index 0000000..7509c0b --- /dev/null +++ b/Planning/content-checklist.json @@ -0,0 +1,44 @@ +{ + "metadata": { + "project": "2D6 Dungeon", + "generated_from": [ + "2D6 Dungeon - Core Rules.pdf", + "2D6 Dungeon - Tables Codex.pdf" + ], + "purpose": "Checklist of rules content that must be encoded for the web adaptation.", + "status_values": [ + "pending-entry", + "in-progress", + "entered", + "verified", + "deferred" + ] + }, + "mvp_priorities": [ + "Encode all starting tables and foundational generic tables first.", + "Complete Level 1 tables, Level 1 room tables, and a subset of Level 1 creatures next.", + "Defer optional tables and deep-level content until the Level 1 loop is playable." + ], + "sections": { + "generic_tables": [], + "random_list_tables": [], + "loot_tables": [], + "level_tables": { + "level_1": [], + "level_2": [], + "level_3": [], + "level_4": [], + "level_5": [], + "level_6": [], + "level_7": [], + "level_8": [], + "level_9": [], + "level_10": [] + }, + "optional_tables": [], + "creature_cards": { + "source_pages": {}, + "entries": [] + } + } +} diff --git a/Source/2D6 Dungeon - Core Rules.pdf b/Source/2D6 Dungeon - Core Rules.pdf new file mode 100644 index 0000000..6183edb Binary files /dev/null and b/Source/2D6 Dungeon - Core Rules.pdf differ diff --git a/Source/2D6 Dungeon - Tables Codex.pdf b/Source/2D6 Dungeon - Tables Codex.pdf new file mode 100644 index 0000000..feab8d3 Binary files /dev/null and b/Source/2D6 Dungeon - Tables Codex.pdf differ