feat(engine): apply profile at game start
This commit is contained in:
parent
e23e69e0d0
commit
0e9809007d
3 changed files with 704 additions and 0 deletions
|
|
@ -69,6 +69,15 @@ import { PIECE_TYPE_REGISTRY } from "./presets/piece-type-registry.js";
|
|||
// Importing from the barrel guarantees every preset module's
|
||||
// side-effect registration has run before the first engine is created.
|
||||
import "./presets/index.js";
|
||||
// Side-effect import: registers every modifier descriptor AND the
|
||||
// `__modifier-profile-integration__` preset with PRESET_REGISTRY, so
|
||||
// the engine can activate it when a profile is supplied.
|
||||
import "./modifiers/index.js";
|
||||
import {
|
||||
applyProfileToSession,
|
||||
MODIFIER_INTEGRATION_PRESET_ID,
|
||||
} from "./modifiers/apply.js";
|
||||
import type { ModifierProfile } from "./modifiers/types.js";
|
||||
|
||||
type MoveGetter = (session: Session, pieceId: EntityId) => LegalMove[];
|
||||
|
||||
|
|
@ -299,6 +308,21 @@ class PresetStateImpl<T extends Record<string, unknown>>
|
|||
export interface EngineOptions {
|
||||
readonly activePresets?: ActivePresetSet;
|
||||
readonly layout?: StartingLayout;
|
||||
/**
|
||||
* Optional ModifierProfile to apply at game start. When supplied,
|
||||
* the engine:
|
||||
* 1. Applies the layout (as normal).
|
||||
* 2. Calls `applyProfileToSession(session, profile, layout)` to
|
||||
* seed all modifier facts on piece entities.
|
||||
* 3. Auto-activates the `__modifier-profile-integration__` preset
|
||||
* (scope=both, permanent) so CaptureFlags, DirectionAdditions,
|
||||
* and DamageResistance facts drive actual gameplay.
|
||||
*
|
||||
* Order: profile is applied BEFORE `activePresets` activation, so
|
||||
* preset `onActivate` hooks (e.g. piece-hp seeding Hp) observe any
|
||||
* HpBonus the profile contributed.
|
||||
*/
|
||||
readonly profile?: ModifierProfile;
|
||||
}
|
||||
|
||||
export class ChessEngine {
|
||||
|
|
@ -421,8 +445,39 @@ export class ChessEngine {
|
|||
|
||||
const layout = opts.layout ?? CLASSIC_LAYOUT;
|
||||
applyLayout(this.session, layout);
|
||||
|
||||
// Profile seeding runs BEFORE the position is recorded for
|
||||
// threefold repetition — the modifier facts are part of the
|
||||
// "initial position" from a repetition-tracking perspective, and
|
||||
// are not expected to change mid-game (profiles hot-swap via
|
||||
// turn-boundary replacement, which re-seeds).
|
||||
if (opts.profile) {
|
||||
applyProfileToSession(this.session, opts.profile, layout);
|
||||
}
|
||||
|
||||
recordPosition(this.session);
|
||||
this.activePresets = opts.activePresets ?? new ActivePresetSet();
|
||||
|
||||
// When a profile is present, auto-activate the integration preset
|
||||
// so its filterMoves / getExtraMoves / onDamage hooks fire. We
|
||||
// prepend it to whatever the caller provided so it always runs
|
||||
// FIRST in the preset-iteration order (cheap predicate — cheap to
|
||||
// short-circuit).
|
||||
if (opts.profile) {
|
||||
const existing = this.activePresets.list();
|
||||
this.activePresets.replaceAll([
|
||||
{
|
||||
id: MODIFIER_INTEGRATION_PRESET_ID,
|
||||
scope: "both",
|
||||
turnsRemaining: null,
|
||||
},
|
||||
...existing.map((e) => ({
|
||||
id: e.id,
|
||||
scope: e.scope,
|
||||
turnsRemaining: e.turnsRemaining,
|
||||
})),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue