✨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