Support target-scoped trauma mechanics
This commit is contained in:
@@ -78,7 +78,7 @@ describe('AI mechanic authoring prompt', () => {
|
||||
recipeKeys: []
|
||||
}));
|
||||
|
||||
expect(AI_MECHANIC_PROMPT_VERSION).toBe(4);
|
||||
expect(AI_MECHANIC_PROMPT_VERSION).toBe(5);
|
||||
expect(input.canonicalCatalog.grammar.outcome.optional).toEqual(
|
||||
expect.arrayContaining(['delivery', 'durationSeconds'])
|
||||
);
|
||||
@@ -115,7 +115,7 @@ describe('AI mechanic authoring prompt', () => {
|
||||
expect.stringContaining('Do not replace continuous delivery with a periodic reducer')
|
||||
]));
|
||||
expect(AI_MECHANIC_AUTHORING_INSTRUCTIONS).toContain(
|
||||
'Prompt contract version: 4'
|
||||
'Prompt contract version: 5'
|
||||
);
|
||||
|
||||
const advertised = structuredClone(
|
||||
@@ -156,4 +156,89 @@ describe('AI mechanic authoring prompt', () => {
|
||||
});
|
||||
expect(parsed.ok).toBe(true);
|
||||
});
|
||||
|
||||
it('advertises Precise Trauma through target expiry without inventing target tick iteration', () => {
|
||||
const skillDamageId = 'skill-damage-catalog-id';
|
||||
const input = JSON.parse(buildAiMechanicAuthoringInput({
|
||||
request: {
|
||||
...request,
|
||||
context: {
|
||||
...request.context,
|
||||
sourceName: 'Precise Trauma',
|
||||
sourceDescription:
|
||||
'Hits apply a debuff for 1 second, up to 10 stacks. Body shots add 1 stack and headshots add 2 stacks.'
|
||||
}
|
||||
},
|
||||
attributes: [{
|
||||
id: skillDamageId,
|
||||
name: 'Skill Damage',
|
||||
description: null,
|
||||
unit: null,
|
||||
type: null,
|
||||
category: null,
|
||||
calculationType: null
|
||||
}],
|
||||
events: [{ key: 'weapon_hit', label: 'Weapon hit' }],
|
||||
actions: [
|
||||
{ key: 'body_shot', label: 'Body shot' },
|
||||
{ key: 'headshot', label: 'Headshot' }
|
||||
],
|
||||
recipeKeys: ['body_shot', 'headshot']
|
||||
}));
|
||||
const advertised = structuredClone(input.canonicalCatalog.grammar.preciseTraumaShape);
|
||||
const outcome = advertised.clauses[0].outcomes[0];
|
||||
const baseVariant = JSON.parse(JSON.stringify(
|
||||
outcome.variants['replace-with-each-required-variant-key']
|
||||
).replaceAll(
|
||||
'replace-with-the-exact-skill-damage-catalog-id',
|
||||
skillDamageId
|
||||
));
|
||||
outcome.variants = {
|
||||
I: structuredClone(baseVariant),
|
||||
II: structuredClone(baseVariant)
|
||||
};
|
||||
|
||||
expect(advertised.pools[0]).toMatchObject({
|
||||
scope: 'target',
|
||||
max: 10,
|
||||
expiresAfterSeconds: 1
|
||||
});
|
||||
expect(advertised.clauses[0].mutations[0]).toEqual({
|
||||
kind: 'add',
|
||||
pool: 'trauma',
|
||||
entity: { kind: 'event_target' },
|
||||
value: 1
|
||||
});
|
||||
expect(advertised.clauses[1].mutations[0]).toMatchObject({ value: 2 });
|
||||
expect(outcome).toMatchObject({
|
||||
phase: 'on_exit',
|
||||
kind: 'direct_damage',
|
||||
scope: 'target',
|
||||
statePool: 'trauma',
|
||||
perStack: true,
|
||||
variants: {
|
||||
I: {
|
||||
expression: {
|
||||
ref: {
|
||||
source: 'attribute',
|
||||
attributeId: skillDamageId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(input.examplesAndRules).toEqual(expect.arrayContaining([
|
||||
expect.stringContaining('uses the entity previous state'),
|
||||
expect.stringContaining('without inventing generic repeated target ticks')
|
||||
]));
|
||||
expect(JSON.stringify(input.canonicalCatalog.grammar.preciseTraumaShape))
|
||||
.not.toContain('entitiesFromPool');
|
||||
|
||||
const parsed = parseConditionalMechanicDraft(advertised, {
|
||||
variantKeys: ['I', 'II'],
|
||||
allowedAttributeIds: new Set([skillDamageId]),
|
||||
allowedRecipeKeys: new Set(['body_shot', 'headshot'])
|
||||
});
|
||||
expect(parsed.ok).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user