feat: support extra health state mechanics
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
parseConditionalMechanicDraft,
|
||||
readCurrentHealthPercentBand,
|
||||
readEventFieldComparison,
|
||||
registeredEventFields,
|
||||
readStatePoolThreshold,
|
||||
statePoolThresholdPredicate,
|
||||
type ConditionalMechanicClauseDraft,
|
||||
@@ -171,6 +172,41 @@
|
||||
: options;
|
||||
}
|
||||
|
||||
function eventPredicateFields(clause: ConditionalMechanicClauseDraft) {
|
||||
return registeredEventFields(clause.event);
|
||||
}
|
||||
|
||||
function selectedEventPredicateField(clause: ConditionalMechanicClauseDraft) {
|
||||
const comparison = readEventFieldComparison(clause.if);
|
||||
return eventPredicateFields(clause).find((field) => field.path === comparison?.path);
|
||||
}
|
||||
|
||||
function eventPredicateOperatorOptions(clause: ConditionalMechanicClauseDraft) {
|
||||
return selectedEventPredicateField(clause)?.type === 'number'
|
||||
? predicateOperatorOptions
|
||||
: predicateOperatorOptions.filter((option) => option.value === 'eq' || option.value === 'ne');
|
||||
}
|
||||
|
||||
function setClauseEvent(clauseIndex: number, event: string) {
|
||||
const nextEvent = event as ConditionalMechanicClauseDraft['event'];
|
||||
const clause = rule.clauses[clauseIndex];
|
||||
const fields = registeredEventFields(nextEvent);
|
||||
const comparison = readEventFieldComparison(clause.if);
|
||||
if (!comparison || !fields.length) {
|
||||
updateClause(clauseIndex, { event: nextEvent });
|
||||
return;
|
||||
}
|
||||
const field = fields[0];
|
||||
updateClause(clauseIndex, {
|
||||
event: nextEvent,
|
||||
if: eventFieldComparisonPredicate({
|
||||
path: field.path,
|
||||
operator: 'eq',
|
||||
value: field.type === 'boolean' ? true : field.type === 'number' ? 0 : ''
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function setConditionModel(clauseIndex: number, model: string) {
|
||||
if (model === 'always') updateClause(clauseIndex, { if: { kind: 'true' } });
|
||||
if (model === 'current_health_band') {
|
||||
@@ -179,11 +215,12 @@
|
||||
});
|
||||
}
|
||||
if (model === 'event_field') {
|
||||
const field = eventPredicateFields(rule.clauses[clauseIndex])[0];
|
||||
updateClause(clauseIndex, {
|
||||
if: eventFieldComparisonPredicate({
|
||||
path: 'values.state',
|
||||
path: field?.path ?? 'values.state',
|
||||
operator: 'eq',
|
||||
value: 'moving'
|
||||
value: field?.type === 'boolean' ? true : field?.type === 'number' ? 0 : 'moving'
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -243,6 +280,18 @@
|
||||
updateEventFieldComparison(clauseIndex, { value });
|
||||
}
|
||||
|
||||
function setRegisteredEventField(clauseIndex: number, path: string) {
|
||||
const clause = rule.clauses[clauseIndex];
|
||||
const field = eventPredicateFields(clause).find((item) => item.path === path);
|
||||
if (!field) return;
|
||||
const current = readEventFieldComparison(clause.if);
|
||||
updateEventFieldComparison(clauseIndex, {
|
||||
path,
|
||||
operator: field.type === 'number' ? current?.operator ?? 'eq' : 'eq',
|
||||
value: field.type === 'boolean' ? true : field.type === 'number' ? 0 : ''
|
||||
});
|
||||
}
|
||||
|
||||
function updateStatePoolThreshold(
|
||||
clauseIndex: number,
|
||||
patch: Partial<StatePoolThresholdComparison>
|
||||
@@ -839,6 +888,8 @@
|
||||
{#each rule.clauses as clause, clauseIndex}
|
||||
{@const conditionBand = readCurrentHealthPercentBand(clause.if)}
|
||||
{@const eventFieldCondition = readEventFieldComparison(clause.if)}
|
||||
{@const registeredPredicateField = selectedEventPredicateField(clause)}
|
||||
{@const registeredPredicateFields = eventPredicateFields(clause)}
|
||||
{@const statePoolCondition = readStatePoolThreshold(clause.if)}
|
||||
<section class="branch">
|
||||
<header>
|
||||
@@ -846,7 +897,7 @@
|
||||
<button type="button" class="remove" onclick={() => setRule({...rule,clauses:rule.clauses.filter((_,index) => index !== clauseIndex)})}>REMOVE BRANCH</button>
|
||||
</header>
|
||||
<div class="branch-grid">
|
||||
<div class="field"><span>DIRECT EVENT</span><SearchableAssignment items={eventItems} mode="single" compact subjectLabel="EVENT" bind:value={clause.event}/></div>
|
||||
<div class="field"><span>DIRECT EVENT</span><SearchableAssignment items={eventItems} mode="single" compact subjectLabel="EVENT" value={clause.event} onvaluechange={(value) => setClauseEvent(clauseIndex,value)}/></div>
|
||||
<div class="branch-readout"><span>MATCH POLICY</span><strong>{reducerItems.find((item) => item.id === clause.reducer.kind)?.name ?? clause.reducer.kind}</strong></div>
|
||||
<label class="field">COOLDOWN · SECONDS<input type="number" min="0.01" step="any" value={clause.cooldownSeconds ?? ''} oninput={(event) => updateClause(clauseIndex,{cooldownSeconds:event.currentTarget.value ? Number(event.currentTarget.value) : undefined})}/></label>
|
||||
</div>
|
||||
@@ -865,22 +916,34 @@
|
||||
</div>
|
||||
{:else if eventFieldCondition}
|
||||
<div class="predicate-fields event-predicate-fields">
|
||||
<label class="field">EVENT FIELD PATH<input value={eventFieldCondition.path} placeholder="values.state" oninput={(event) => updateEventFieldComparison(clauseIndex,{path:event.currentTarget.value})}/></label>
|
||||
{#if registeredPredicateFields.length && registeredPredicateField}
|
||||
<SegmentedChoice
|
||||
legend="EVENT FIELD"
|
||||
name={`${idPrefix}-event-field-${clauseIndex}`}
|
||||
options={registeredPredicateFields.map((field) => ({value:field.path,label:field.label.toUpperCase()}))}
|
||||
value={registeredPredicateField.path}
|
||||
onchange={(values) => setRegisteredEventField(clauseIndex,values[0])}
|
||||
/>
|
||||
{:else}
|
||||
<label class="field">EVENT FIELD PATH<input value={eventFieldCondition.path} placeholder="values.state" oninput={(event) => updateEventFieldComparison(clauseIndex,{path:event.currentTarget.value})}/></label>
|
||||
{/if}
|
||||
<SegmentedChoice
|
||||
legend="COMPARISON"
|
||||
name={`${idPrefix}-event-operator-${clauseIndex}`}
|
||||
options={predicateOperatorOptions}
|
||||
options={eventPredicateOperatorOptions(clause)}
|
||||
value={eventFieldCondition.operator}
|
||||
columns={6}
|
||||
columns={eventPredicateOperatorOptions(clause).length}
|
||||
onchange={(values) => updateEventFieldComparison(clauseIndex,{operator:values[0] as PredicateComparisonOperator})}
|
||||
/>
|
||||
<SegmentedChoice
|
||||
legend="VALUE TYPE"
|
||||
name={`${idPrefix}-event-value-type-${clauseIndex}`}
|
||||
options={predicateScalarTypeOptions}
|
||||
value={predicateScalarType(eventFieldCondition.value)}
|
||||
onchange={(values) => setEventFieldScalarType(clauseIndex,values[0])}
|
||||
/>
|
||||
{#if !registeredPredicateField}
|
||||
<SegmentedChoice
|
||||
legend="VALUE TYPE"
|
||||
name={`${idPrefix}-event-value-type-${clauseIndex}`}
|
||||
options={predicateScalarTypeOptions}
|
||||
value={predicateScalarType(eventFieldCondition.value)}
|
||||
onchange={(values) => setEventFieldScalarType(clauseIndex,values[0])}
|
||||
/>
|
||||
{/if}
|
||||
{#if typeof eventFieldCondition.value === 'number'}
|
||||
<label class="field">COMPARE VALUE<input type="number" step="any" value={eventFieldCondition.value} oninput={(event) => updateEventFieldComparison(clauseIndex,{value:Number(event.currentTarget.value)})}/></label>
|
||||
{:else if typeof eventFieldCondition.value === 'boolean'}
|
||||
|
||||
Reference in New Issue
Block a user