Fixes batch 3: autosave, campaign-agnostic PCs, live push, builder
- Autosave: while signed in, a debounced full backup pushes whenever durable data
changes (useCloudAutosave watches a fingerprint of the main tables; high-churn
tables excluded). No-op signed out.
- Campaign-agnostic characters: PCs are now global — the Characters roster shows
every player character regardless of active campaign, and any PC can be added to
a fight. NPCs stay per-campaign. (charactersRepo.listAllPcs / useAllPcs; difficulty
fallback stays campaign-scoped.)
- Live "bring your own character": a joining player can push one of their own local
characters; the GM's grant inserts it into the campaign and seats them.
- Character builder: a system picker (5e / PF2e, defaults to the campaign) so creation
no longer assumes 5e; PF2e skill wording ("Trained" vs "proficient"); spell step now
explains how many to pick + that they're known spells prepared later.
230 unit + 34 e2e + 2 realtime green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@ import { createRng } from '@/lib/rng';
|
||||
import { rollDice } from '@/lib/dice/notation';
|
||||
import { getSystem, getConditions, type SystemId } from '@/lib/rules';
|
||||
import { computeBudget, DIFFICULTY_COLOR } from '@/lib/combat/budget';
|
||||
import { useCharacters } from '@/features/characters/hooks';
|
||||
import { useCharacters, useAllPcs } from '@/features/characters/hooks';
|
||||
import { useConditionGlossary } from './useConditionGlossary';
|
||||
import {
|
||||
addCombatant,
|
||||
@@ -48,6 +48,9 @@ import { EncounterTipCard } from '@/features/assistant/EncounterTipCard';
|
||||
|
||||
export function EncounterTracker({ encounter, campaign }: { encounter: Encounter; campaign: Campaign }) {
|
||||
const characters = useCharacters(campaign.id);
|
||||
// PCs are campaign-agnostic, so any PC can join a fight; NPCs stay campaign-scoped.
|
||||
const allPcs = useAllPcs();
|
||||
const addableChars = [...allPcs, ...characters.filter((c) => c.kind === 'npc')];
|
||||
const glossary = useConditionGlossary(campaign.system);
|
||||
|
||||
const undoStack = useRef<Encounter[]>([]);
|
||||
@@ -162,7 +165,7 @@ export function EncounterTracker({ encounter, campaign }: { encounter: Encounter
|
||||
|
||||
<EncounterTipCard campaign={campaign} encounter={encounter} />
|
||||
|
||||
<AddCombatantBar encounter={encounter} characters={characters} onAdd={(c) => mutate((e) => addCombatant(e, c))} />
|
||||
<AddCombatantBar encounter={encounter} characters={addableChars} onAdd={(c) => mutate((e) => addCombatant(e, c))} />
|
||||
|
||||
{/* Combatant list */}
|
||||
{encounter.combatants.length === 0 ? (
|
||||
|
||||
Reference in New Issue
Block a user