✨feature: Initial commit
This commit is contained in:
16
.vscode/settings.json
vendored
Normal file
16
.vscode/settings.json
vendored
Normal 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
1
Notes/codex session.txt
Normal file
@@ -0,0 +1 @@
|
||||
codex resume 019cef5b-b2b0-7ad2-9267-f5cb63aca9cd
|
||||
829
Planning/DATA_MODEL.md
Normal file
829
Planning/DATA_MODEL.md
Normal 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
798
Planning/GAME_SPEC.md
Normal 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
424
Planning/PROJECT_PLAN.md
Normal 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.
|
||||
44
Planning/content-checklist.json
Normal file
44
Planning/content-checklist.json
Normal 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": []
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Source/2D6 Dungeon - Core Rules.pdf
Normal file
BIN
Source/2D6 Dungeon - Core Rules.pdf
Normal file
Binary file not shown.
BIN
Source/2D6 Dungeon - Tables Codex.pdf
Normal file
BIN
Source/2D6 Dungeon - Tables Codex.pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user