import type { CardPile, CardPileConfig } from "../../cards/cardPile.js"; import type { CommandRegistry } from "../../commands/commandRegistry.js"; import { type WalletState } from "../../economy/wallet.js"; import type { GameFeatures } from "../../game/defineGame.js"; import type { GameEvents } from "../../game/events.js"; import { type Social } from "../../game/social.js"; import { type InventoryLayout, type InventorySet, type ItemTraits } from "../../inventory/inventoryModel.js"; import type { StatValueMap } from "../../scene/entityStats.js"; import type { EntityStore } from "../../scene/entityStore.js"; import type { SpatialApi } from "../../scene/spatial.js"; import type { ObservableKeyedStore } from "../../store/observableKeyedStore.js"; import type { SnapshotModule } from "../worldSnapshot.js"; import type { TurnLoop, TurnLoopConfig } from "../../turn/turnLoop.js"; import type { RaceState, RaceStateConfig } from "../../game/race.js"; import type { GameContext, GameContextContent, GameContextEconomy } from "../gameContext.js"; /** * Shared wiring every optional-feature descriptor draws from — the live core subsystems (entities, * spatial, economy, inventory) plus reactive plumbing (`signalNotify`) and a `feature` reader for the * few features that reference another (quest reads unlocks). Handed to each {@link FeatureDescriptor}'s * `create` so a new opt-in subsystem plugs into one registration, never a new `features.x ?` branch. * @internal */ export interface FeatureDeps { features: GameFeatures; signalNotify: () => void; events: GameEvents; now: () => number; entities: EntityStore; spatial: SpatialApi; store: ObservableKeyedStore; commandRegistry: CommandRegistry; economy: GameContextEconomy; content: GameContextContent; activeUserId: () => string; walletOf: (userId: string) => WalletState; setWallet: (userId: string, state: WalletState) => void; layouts: Record; itemTraits: ItemTraits; inventoryFor: (userId: string) => InventorySet; ensureInstanceStats: (instanceId: string) => StatValueMap; seedUserPool: (userId: string, statId: string, pool: { current: number; max?: number; min?: number; }) => void; sharedSocial: () => Social; pile: (id: string, config?: CardPileConfig) => CardPile; loop: (id: string, config?: TurnLoopConfig) => TurnLoop; cardPiles: ReadonlyMap; turnLoops: ReadonlyMap; raceState: (id: string, config?: RaceStateConfig) => RaceState; feature: (key: keyof GameFeatures) => T | undefined; } /** @internal What a descriptor produces: the `ctx`-facing value plus its optional replication/save modules. */ export interface FeatureBuild { value: unknown; /** Registered into the host→client replication set when present (`ctx.snapshot`/`ctx.hydrate`). */ replicate?: SnapshotModule; /** Registered into the whole-world save-only set when present (`ctx.game.save`). */ save?: SnapshotModule; } /** * One opt-in subsystem expressed as data: which `features` flag turns it on, how it wires itself from * {@link FeatureDeps}, and whether it replicates or persists. `createGameContext` iterates the * descriptor list instead of hand-wiring each `features.x ? create : undefined` branch — the seam a * new feature extends through. * * Feature activation has **one path**: `definition.features` after `defineGame` has already * OR-merged explicit `features: { quest: true }` sugar with system-implied flags via * `mergeSystemFeatures`. Descriptors only call `enabled(features)` against that final map — never * re-check systems or a second flag source. * @internal */ export interface FeatureDescriptor { readonly key: keyof GameFeatures; enabled(features: GameFeatures): boolean; create(deps: FeatureDeps): FeatureBuild; } /** @internal */ export declare const featureDescriptors: readonly FeatureDescriptor[];