feature: Initial commit

This commit is contained in:
Keith Solomon
2026-03-15 09:41:22 -05:00
commit 95c629a42e
8 changed files with 2112 additions and 0 deletions

16
.vscode/settings.json vendored Normal file
View File

@@ -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"
}
}

1
Notes/codex session.txt Normal file
View File

@@ -0,0 +1 @@
codex resume 019cef5b-b2b0-7ad2-9267-f5cb63aca9cd

829
Planning/DATA_MODEL.md Normal file
View File

@@ -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<string, number>;
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<number, DungeonLevelState>;
revealedPercentByLevel: Record<number, number>;
globalFlags: string[];
};
```
### DungeonLevelState
```ts
type DungeonLevelState = {
level: number;
themeName?: string;
rooms: Record<string, RoomState>;
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

798
Planning/GAME_SPEC.md Normal file
View File

@@ -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.

424
Planning/PROJECT_PLAN.md Normal file
View File

@@ -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.

View File

@@ -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": []
}
}
}

Binary file not shown.

Binary file not shown.