Support live attribute restoration mechanics

This commit is contained in:
JB
2026-07-27 21:32:04 -04:00
parent 36dd018a4a
commit 568e46cd05
19 changed files with 979 additions and 31 deletions
+43
View File
@@ -156,6 +156,8 @@ function isReference(value: unknown): value is ValueReference {
&& (value.timing === undefined || value.timing === 'pre_event' || value.timing === 'current');
case 'equipment':
return typeof value.path === 'string' && Boolean(value.path.trim());
case 'attribute':
return typeof value.attributeId === 'string' && Boolean(value.attributeId.trim());
case 'reducer':
return true;
case 'pool':
@@ -167,6 +169,33 @@ function isReference(value: unknown): value is ValueReference {
}
}
function validateAttributeReferences(
value: unknown,
allowedAttributeIds: ReadonlySet<string> | undefined,
errors: string[],
path = 'Conditional mechanic'
) {
if (Array.isArray(value)) {
value.forEach((item, index) =>
validateAttributeReferences(item, allowedAttributeIds, errors, `${path} item ${index + 1}`));
return;
}
if (!object(value)) return;
if (
value.source === 'attribute'
&& typeof value.attributeId === 'string'
&& value.attributeId.trim()
&& allowedAttributeIds
&& !allowedAttributeIds.has(value.attributeId)
) {
errors.push(`${path} references unknown Attribute "${value.attributeId}".`);
return;
}
for (const [key, nested] of Object.entries(value)) {
validateAttributeReferences(nested, allowedAttributeIds, errors, `${path} ${key}`);
}
}
function isExpression(value: unknown): value is NumericExpression {
if (finite(value)) return true;
if (!object(value)) return false;
@@ -357,6 +386,19 @@ function parseOutcome(
} else if (value.resource !== undefined) {
errors.push(`${path} may only use a resource for restoration or resource cost.`);
}
const delivery = value.delivery ?? 'instant';
if (delivery !== 'instant' && delivery !== 'continuous') {
errors.push(`${path} has an invalid delivery mode.`);
} else if (delivery === 'continuous') {
if (kind !== 'restoration') {
errors.push(`${path} may only use continuous delivery for restoration.`);
}
if (!finite(value.durationSeconds) || value.durationSeconds <= 0) {
errors.push(`${path} continuous delivery requires a positive duration.`);
}
} else if (value.durationSeconds !== undefined) {
errors.push(`${path} may only set a duration for continuous delivery.`);
}
if (
value.combatTiming !== undefined
&& value.combatTiming !== 'pre_event'
@@ -547,6 +589,7 @@ export function parseConditionalMechanicDraft(
pools,
clauses
};
validateAttributeReferences(draft, options.allowedAttributeIds, errors);
const inputRequiresAssumption = scenarioInputs.some(
(input) => input.readiness === 'requires_assumption'
);