Phase 3: combat depth

- Condition durations + auto-expiry: timed conditions tick down on the affected
  combatant's turn and drop at 0 (engine tickConditions; UI rounds field + (Nr) chip)
- Initiative: Roll-all (d20 + per-combatant bonus, re-sort anchored) + group/quantity add
- Combat log: per-round event feed (turns, round changes, damage/heal, expiries,
  removals) + in-memory Undo of the last change
- Encounter difficulty budget: 5e XP thresholds + PF2e level budget (pure lib +
  tests); live difficulty/XP-award readout from monster combatants vs the party
- Combatants carry initBonus + cr/level; compendium add-to-combat passes them

18 new unit tests (durations, applyInitiatives, budget), new combat-depth e2e.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 01:23:13 +02:00
parent 1c87b0e3aa
commit 3e5bdc06e2
13 changed files with 472 additions and 35 deletions
+10
View File
@@ -39,6 +39,16 @@ export class TtrpgDatabase extends Dexie {
}
});
});
// v3 — Phase 3 combat depth: backfill the encounter event log.
this.version(3).stores({}).upgrade(async (tx) => {
await tx
.table('encounters')
.toCollection()
.modify((e: Record<string, unknown>) => {
if (e.log === undefined) e.log = [];
});
});
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ describe('validation on write', () => {
const c = await campaignsRepo.create({ name: 'C', system: '5e', description: '' });
const enc = await encountersRepo.create(c.id, 'E');
enc.combatants.push({
id: 'x', name: 'Goblin', kind: 'monster', initiative: 12, ac: 15,
id: 'x', name: 'Goblin', kind: 'monster', initiative: 12, initBonus: 0, ac: 15,
hp: { current: Number.NaN, max: 7, temp: 0 }, conditions: [], notes: '',
});
await expect(encountersRepo.save(enc)).rejects.toThrow();