Support target-scoped trauma mechanics

This commit is contained in:
JB
2026-07-27 22:20:52 -04:00
parent 7e78d75f85
commit 0b3be3587b
20 changed files with 1032 additions and 93 deletions
+25 -36
View File
@@ -42,28 +42,9 @@
simulationReadiness: 'captured_only',
scenarioInputs: [],
pools: [],
clauses: [{
key: 'primary',
label: 'Primary',
event: 'weapon_hit',
if: { kind: 'true' },
reducer: { kind: 'none' },
mutations: [],
outcomes: [{
key: 'effect-1',
label: 'Effect 1',
phase: 'while_active',
kind: 'attribute_modifier',
scope: 'equipped_weapon',
attributeId: '',
basis: 'flat',
perStack: false,
descriptionSlot: 1,
variants: { I: { value: 0 }, II: { value: 0 } }
}]
}]
clauses: []
});
const blankTalent = () => ({ id: '', name: '', iconUrl: '', category: 'Weapon Talent 1', descriptionTemplate: '', tierIDescription: '', tierIIDescription: '', conditionalRule: blankRule() });
const blankTalent = () => ({ id: '', name: '', iconUrl: '', category: 'Weapon Talent 1', descriptionTemplate: '', tierIDescription: '', tierIIDescription: '', conditionalRule: blankRule(), modelingOpen: false });
const editTalent = (row: any) => {
const tier = (name: string) => row.tiers.find((item: any) => item.tier === name) ?? { description: '', effects: [] };
const one = tier('I'), two = tier('II');
@@ -72,7 +53,8 @@
descriptionTemplate: row.descriptionTemplate || one.description,
tierIDescription: one.description,
tierIIDescription: two.description,
conditionalRule: row.conditionalRule ?? blankRule()
conditionalRule: row.conditionalRule ?? blankRule(),
modelingOpen: Boolean(row.conditionalRule?.clauses?.length)
};
};
const configured = (tiers: Array<{ tier: unknown }>, name: string) => tiers.some((item) => item.tier === name);
@@ -105,6 +87,7 @@
if (form.talentMode === 'add') iconResetToken += 1;
talent = form.talentMode === 'add' ? blankTalent() : null;
} else if (talentErrors.length) {
if (hasErrors(['mechanic', 'outcomes'])) talent.modelingOpen = true;
void tick().then(() => validationSummary?.focus());
}
});
@@ -143,20 +126,26 @@
</div>
<label id="talent-description" class="field template-field" class:validation-area-error={hasErrors(['description'])}>SHARED DESCRIPTION TEMPLATE<textarea name="descriptionTemplate" bind:value={talent.descriptionTemplate} placeholder={'Hitting an enemy {1} times grants +{2} for {duration}...'} required aria-invalid={hasErrors(['description']) ? 'true' : undefined}></textarea><small>Use {'{1}'}, {'{2}'}, and so on for effect values. Use {'{duration}'} and {'{max_stacks}'} for defined mechanics. Cooldown is displayed separately.</small>{#each errorsFor('description') as error}<small class="field-error">{error.message}</small>{/each}</label>
<div class="tier-preview"><div><span>TIER I PREVIEW</span><p>{preview('I') || 'Description preview'}</p></div><div><span>TIER II PREVIEW</span><p>{preview('II') || 'Description preview'}</p></div></div>
<div id="talent-mechanic" class:validation-area-error={hasErrors(['mechanic', 'outcomes'])}>
<MechanicProgramEditor
attributes={data.stats}
valueColumns={[{key:'tierI',variantKey:'I',label:'TIER I'},{key:'tierII',variantKey:'II',label:'TIER II'}]}
idPrefix={`talent-${talent.id || 'new'}`}
aiContext={{
sourceKind: 'talent',
sourceName: talent?.name ?? '',
sourceDescription: talent?.descriptionTemplate ?? ''
}}
bind:rule={talent.conditionalRule}
/>
{#each [...errorsFor('mechanic'), ...errorsFor('outcomes')] as error}<small class="field-error mechanic-field-error">{error.message}</small>{/each}
</div>
<details id="talent-mechanic" class="modeling-disclosure" class:validation-area-error={hasErrors(['mechanic', 'outcomes'])} bind:open={talent.modelingOpen}>
<summary>
<span><b>COMBAT MODELING</b><small>{talent.conditionalRule.simulationReadiness === 'captured_only' ? 'CAPTURE ONLY · MODEL LATER' : `${talent.conditionalRule.clauses.length} CONFIGURED ${talent.conditionalRule.clauses.length === 1 ? 'BRANCH' : 'BRANCHES'}`}</small></span>
<strong>{talent.modelingOpen ? 'COLLAPSE' : 'EXPAND'}</strong>
</summary>
<div class="modeling-content">
<MechanicProgramEditor
attributes={data.stats}
valueColumns={[{key:'tierI',variantKey:'I',label:'TIER I'},{key:'tierII',variantKey:'II',label:'TIER II'}]}
idPrefix={`talent-${talent.id || 'new'}`}
aiContext={{
sourceKind: 'talent',
sourceName: talent?.name ?? '',
sourceDescription: talent?.descriptionTemplate ?? ''
}}
bind:rule={talent.conditionalRule}
/>
{#each [...errorsFor('mechanic'), ...errorsFor('outcomes')] as error}<small class="field-error mechanic-field-error">{error.message}</small>{/each}
</div>
</details>
<div class="form-actions"><button type="button" class="secondary" onclick={() => talent = null}>CANCEL</button><button class="primary">SAVE TALENT →</button></div>
</PendingForm>
</div>