feat: expand ISAC content modeling
Validate and deploy production / validate (push) Canceled after 0s
Validate and deploy production / deploy (push) Canceled after 0s
Validate and deploy staging / validate (push) Successful in 28s
Validate and deploy staging / deploy (push) Successful in 1m3s

This commit is contained in:
jb
2026-08-03 01:23:20 -04:00
parent d9924a95b9
commit 57068b61cd
90 changed files with 9635 additions and 1270 deletions
+107 -2
View File
@@ -3,7 +3,7 @@ import { CONDITIONAL_MECHANIC_OUTCOME_CAPABILITIES } from '$lib/calculations/mec
import { MECHANIC_COMBAT_REFERENCE_REGISTRY } from '$lib/calculations/mechanic';
import type { AiMechanicAttribute } from './mechanic-authoring-context';
export const AI_MECHANIC_PROMPT_VERSION = 7;
export const AI_MECHANIC_PROMPT_VERSION = 10;
const mechanicsGrammar = {
version: 1,
@@ -40,7 +40,14 @@ const mechanicsGrammar = {
},
clause: {
required: ['key', 'event', 'if', 'reducer', 'mutations', 'outcomes'],
optional: ['label', 'recipeKey', 'cooldownSeconds', 'weight'],
optional: [
'label',
'recipeKey',
'cooldownSeconds',
'weight',
'procChancePercent',
'requiresAssignedAbilityActive',
],
},
predicateKinds: ['true', 'exists', 'compare', 'all', 'any', 'not'],
compareOperators: ['eq', 'ne', 'gt', 'gte', 'lt', 'lte'],
@@ -63,6 +70,8 @@ const mechanicsGrammar = {
'derive',
'clear',
'transfer',
'extend_duration',
'start_cooldown_on_max',
],
entitySelectors: ['agent', 'event_actor', 'event_target', 'event_field'],
valueReferences: [
@@ -70,6 +79,7 @@ const mechanicsGrammar = {
'scenario',
'combat',
'equipment',
'assigned_ability',
'attribute',
'reducer',
'pool',
@@ -97,6 +107,7 @@ const mechanicsGrammar = {
},
},
{ ref: { source: 'equipment', path: 'protocolTalentUpgrades' } },
{ ref: { source: 'assigned_ability', path: 'remainingDurationSeconds' } },
{
ref: {
source: 'attribute',
@@ -200,6 +211,16 @@ const mechanicsGrammar = {
value: 'all',
clearSource: true,
},
{
kind: 'extend_duration',
target: 'assigned_ability',
seconds: 0.3,
},
{
kind: 'start_cooldown_on_max',
stackPool: 'extension-stacks',
cooldownPool: 'extension-cooldown',
},
],
outcome: CONDITIONAL_MECHANIC_OUTCOME_CAPABILITIES,
continuousRestorationOutcomeShape: {
@@ -232,6 +253,83 @@ const mechanicsGrammar = {
},
},
},
probabilityOutcomeShape: {
key: 'recover-ammo',
label: 'Recover Ammo',
phase: 'after_event',
kind: 'restoration',
scope: 'self',
basis: 'flat',
resource: 'ammo',
probabilityPercent: 29,
variants: {
'replace-with-each-required-variant-key': { value: 1 },
},
},
aimedCriticalAreaDamageShape: {
version: 1,
simulationReadiness: 'ready',
scenarioInputs: [],
pools: [],
clauses: [
{
key: 'aimed-critical-explosion',
label: 'Aimed critical hit explosion',
event: 'weapon_hit',
if: {
kind: 'all',
predicates: [
{ kind: 'exists', value: { source: 'event', path: 'targetId' } },
{
kind: 'compare',
left: { source: 'event', path: 'values.critical' },
operator: 'eq',
right: true,
},
{
kind: 'compare',
left: { source: 'event', path: 'values.aiming' },
operator: 'eq',
right: true,
},
],
},
reducer: { kind: 'none' },
mutations: [],
outcomes: [
{
key: 'critical-explosion-damage',
label: 'Critical explosion damage',
phase: 'after_event',
kind: 'direct_damage',
scope: 'target',
basis: 'flat',
targeting: {
kind: 'event_target_area',
center: 'event_target',
validTargets: 'enemies',
radiusMeters: 5,
},
variants: {
'replace-with-each-required-variant-key': {
value: 2000,
expression: {
op: 'multiply',
left: 20,
right: {
ref: {
source: 'attribute',
attributeId: 'replace-with-an-exact-catalog-attribute-id',
},
},
},
},
},
},
],
},
],
},
preciseTraumaShape: {
version: 1,
simulationReadiness: 'ready',
@@ -453,9 +551,16 @@ export function buildAiMechanicAuthoringInput(input: {
'A target-scoped pool owns an independent value and expiry timer for each entity. Bind hit-created target state with entity:{kind:"event_target"}; do not collapse different enemies into one Agent-scoped pool.',
'Writing a target-scoped pool refreshes only that target entity timer. An on_exit outcome runs when that entity expires and uses the entity previous state for stack bands and perStack multiplication, even though the stored pool value has already reset.',
'When an outcome value is exactly a live Attribute amount, use the Attribute reference directly as the variant expression. Divide by 100 only when a displayed percentage is being used as a multiplier.',
'A weapon_hit may compare values.aiming with true only when the wording requires aiming down sights at hit resolution. Missing aiming evidence does not match; do not replace it with a scenario assumption.',
'An explosion centered on the hit target uses target-scoped direct_damage with targeting kind event_target_area, center event_target, validTargets enemies, and the stated positive radiusMeters. The runtime applies one equal damage emission to each authoritative valid enemy at or inside the radius. Do not use legacy within_radius metadata for explosion damage.',
'Precise Trauma is representable as one target-scoped stack pool with a one-second expiry: body shots add one stack, headshots add two, and one on_exit direct-damage outcome uses the expiring target previous stack count. This models the stated one-second debuff without inventing generic repeated target ticks.',
'OS Protocol upgrade scaling may use equipment.protocolTalentUpgrades only when allowProtocolUpgradeScaling is true.',
'Assigned-ability scope may be used only when allowAssignedAbility is true.',
'When allowAssignedAbility is true, a kill or assist that must occur while the source item\'s assigned ability is active uses enemy_killed or assist with {source:"event",path:"values.assignedAbilityActive"} compared to true. This field is optional on the event; a missing value does not match.',
'When allowAssignedAbility is true, requiresAssignedAbilityActive gates a clause on the runtime assigned-ability state. skill_deployed and signature_ability_deployed may supply values.assignedAbilityDurationSeconds to begin that state. At an exact expiry timestamp, the ability expires before a later clause can extend it.',
'procChancePercent is a clause-level independent percentage roll after its predicate and cooldown checks but before the reducer, mutations, and outcomes. Use it when a successful proc changes state. probabilityPercent is an outcome-level independent percentage roll for one outcome only.',
'Custom simulation uses a reproducible seeded random stream. Maximum Potential resolves every nonzero chance as successful. Never use clause weight as a proc chance.',
'extend_duration may target only assigned_ability and does nothing while the ability is inactive. start_cooldown_on_max starts a configured expiring Agent cooldown pool only when a mutation in the same clause transitions its capped stack pool to its maximum.',
'If cadence is not stated, ask rather than assuming a one-second interval.',
'If wording contains separate actions such as restoration on a periodic snapshot and a health cost on every shot, represent them as separate clauses.',
'A pool with expiresAfterSeconds refreshes the shared expiry for every current stack whenever any mutation writes that pool. Use it for mechanics where gaining a stack refreshes the duration of the full stack pool.',