init with react+axum+spacetimedb
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-26 18:06:23 +08:00
commit cbc27bad4a
20199 changed files with 883714 additions and 0 deletions

160
src/types/attributes.ts Normal file
View File

@@ -0,0 +1,160 @@
import type {WorldType} from './core';
export const WORLD_ATTRIBUTE_SLOT_IDS = [
'axis_a',
'axis_b',
'axis_c',
'axis_d',
'axis_e',
'axis_f',
] as const;
export type WorldAttributeSlotId = typeof WORLD_ATTRIBUTE_SLOT_IDS[number];
export type LegacyAttributeKey = 'strength' | 'agility' | 'intelligence' | 'spirit';
export type LegacyAttributeSet = Record<LegacyAttributeKey, number>;
export type AttributeVector = Record<string, number>;
export interface WorldAttributeSlot {
slotId: WorldAttributeSlotId;
name: string;
definition: string;
positiveSignals: string[];
negativeSignals: string[];
combatUseText: string;
socialUseText: string;
explorationUseText: string;
}
export interface WorldAttributeSchema {
id: string;
worldId: string;
schemaVersion: number;
generatedFrom: {
worldType: WorldType;
worldName: string;
settingSummary: string;
tone: string;
conflictCore: string;
};
schemaName?: string;
slots: WorldAttributeSlot[];
}
export interface RoleAttributeEvidence {
slotId: WorldAttributeSlotId;
reason: string;
}
export interface RoleAttributeProfile {
schemaId: string;
values: Record<string, number>;
topTraits: string[];
hiddenTraits?: string[];
evidence: RoleAttributeEvidence[];
}
export type RoleRelationStance =
| 'hostile'
| 'guarded'
| 'neutral'
| 'cooperative'
| 'bonded';
export interface RoleRelationState {
affinity: number;
stance: RoleRelationStance;
}
export interface RoleResourceProfile {
maxHp: number;
maxMana: number;
}
export interface RoleResourceState extends RoleResourceProfile {
hp: number;
mana: number;
cooldowns: Record<string, number>;
}
export type RoleInteractionMode =
| 'combat'
| 'dialogue'
| 'trade'
| 'gift'
| 'recruit'
| 'explore'
| 'leave';
export interface RoleBehaviorProfile {
defaultInteractionModes?: RoleInteractionMode[];
preferredActionIds?: string[];
tabooSlots?: string[];
}
export interface RoleNarrativeProfile {
title?: string;
identity?: string;
backgroundSummary?: string;
rumorText?: string[];
faction?: string;
}
export interface NarrativeRoleEntity {
id: string;
roleKind: 'player' | 'npc' | 'monster' | 'boss' | 'companion';
worldAttributeSchemaId: string;
attributeProfile: RoleAttributeProfile;
relationState: RoleRelationState;
resourceState: RoleResourceState;
behaviorProfile: RoleBehaviorProfile;
narrativeProfile: RoleNarrativeProfile;
}
export interface RoleActionDefinition {
id: string;
name: string;
intentVector: AttributeVector;
resistVector?: AttributeVector;
baseScore: number;
category: 'combat' | 'dialogue' | 'trade' | 'gift' | 'recruit' | 'explore';
}
export interface CombatActionAttributeProfile {
intentVector: AttributeVector;
resistVector?: AttributeVector;
}
export interface DialogueActionProfile {
actionId: string;
intentVector: AttributeVector;
relationBias: 'warm' | 'pressure' | 'truth' | 'distance';
}
export interface ItemAttributeResonance {
resonanceVector: AttributeVector;
explanation?: string;
}
export interface AttributeMigrationTrace {
sourceCharacterId: string;
schemaId: string;
oldAttributes?: LegacyAttributeSet;
inferredReasons: string[];
fallbackUsed: boolean;
}
export interface AttributeSchemaGenerationInput {
worldType: WorldType;
worldName: string;
settingText: string;
summary: string;
tone: string;
playerGoal: string;
majorFactions: string[];
coreConflicts: string[];
}
export interface AttributeSchemaGenerationOutput {
schemaName: string;
slots: WorldAttributeSlot[];
}

23
src/types/build.ts Normal file
View File

@@ -0,0 +1,23 @@
import type {AttributeVector} from './attributes';
export type BuildTagCategory = '流派' | '风格' | '资源' | '防御' | '元素' | '工艺';
export type BuildBuffSourceType = 'skill' | 'item' | 'forge';
export interface BuildTagDefinition {
id: string;
label: string;
category: BuildTagCategory;
aliases: string[];
description: string;
attributeAffinity: AttributeVector;
}
export interface TimedBuildBuff {
id: string;
sourceType: BuildBuffSourceType;
sourceId: string;
name: string;
tags: string[];
durationTurns: number;
maxStacks?: number;
}

157
src/types/characters.ts Normal file
View File

@@ -0,0 +1,157 @@
import type {
CombatActionAttributeProfile,
LegacyAttributeSet,
RoleAttributeProfile,
RoleResourceProfile,
} from './attributes';
import type {TimedBuildBuff} from './build';
import {
AnimationState,
type CharacterAnimationConfig,
type CharacterGender,
type CombatDelivery,
type ConversationGuardStyle,
type ConversationTruthStyle,
type ConversationWarmStyle,
type SkillEffectAnchor,
type SkillEffectMotion,
type SkillEffectPhase,
type SkillStyle,
WorldType,
} from './core';
import type { CustomWorldNpcVisual } from './customWorld';
export type SpriteSequenceDefinition =
| {
source: 'animation';
animation: AnimationState;
fps?: number;
}
| {
source: 'asset';
folder: string;
prefix?: string;
frames?: number;
startFrame?: number;
extension?: string;
file?: string;
fps?: number;
};
export interface CharacterSkillEffectDefinition {
phase: SkillEffectPhase;
anchor: SkillEffectAnchor;
motion?: SkillEffectMotion;
sequence: SpriteSequenceDefinition;
durationMs?: number;
scale?: number;
sizePx?: number;
startOffsetX?: number;
endOffsetX?: number;
startYOffset?: number;
endYOffset?: number;
}
export interface CharacterSkillDefinition {
id: string;
name: string;
animation: AnimationState;
casterAnimation?: AnimationState;
damage: number;
manaCost: number;
cooldownTurns: number;
range: number;
style: SkillStyle;
delivery?: CombatDelivery;
releaseDelayMs?: number;
buildBuffs?: TimedBuildBuff[];
effects?: CharacterSkillEffectDefinition[];
attributeProfile?: CombatActionAttributeProfile | null;
}
export interface CharacterAdventureOpening {
reason: string;
goal: string;
monologue: string;
surfaceHook?: string;
immediateConcern?: string;
guardedMotive?: string;
}
export interface CharacterBackstoryChapter {
id: string;
title: string;
affinityRequired: number;
teaser: string;
content: string;
contextSnippet: string;
}
export interface CharacterBackstoryRevealConfig {
publicSummary: string;
chapters: CharacterBackstoryChapter[];
privateChatUnlockAffinity?: number;
}
export interface CharacterConversationStyle {
guardStyle: ConversationGuardStyle;
warmStyle: ConversationWarmStyle;
truthStyle: ConversationTruthStyle;
}
export interface GeneratedCharacterVisualAsset {
id: string;
characterId: string;
sourceMode: 'text-to-image' | 'image-to-image' | 'upload';
promptText?: string;
masterImagePath: string;
previewImagePaths: string[];
width: number;
height: number;
facing: 'right';
locked: boolean;
}
export interface GeneratedCharacterAnimationAsset {
id: string;
animationSetId: string;
characterId: string;
visualAssetId: string;
action: string;
frameCount: number;
fps: number;
loop: boolean;
frameWidth: number;
frameHeight: number;
previewVideoPath?: string;
spriteSheetPath?: string;
framePaths: string[];
}
export interface Character {
id: string;
name: string;
title: string;
gender?: CharacterGender;
description: string;
backstory: string;
backstoryReveal?: CharacterBackstoryRevealConfig;
avatar: string;
portrait: string;
assetFolder: string;
assetVariant: string;
generatedVisualAssetId?: string;
generatedAnimationSetId?: string;
groundOffsetY?: number;
visual?: CustomWorldNpcVisual;
animationMap?: Partial<Record<AnimationState, CharacterAnimationConfig>>;
attributes: LegacyAttributeSet;
attributeProfile?: RoleAttributeProfile;
attributeProfiles?: Partial<Record<WorldType, RoleAttributeProfile>>;
resourceProfile?: RoleResourceProfile;
personality: string;
combatTags?: string[];
conversationStyle?: CharacterConversationStyle;
skills: CharacterSkillDefinition[];
adventureOpenings: Partial<Record<WorldType, CharacterAdventureOpening>>;
}

105
src/types/core.ts Normal file
View File

@@ -0,0 +1,105 @@
export enum WorldType {
WUXIA = 'WUXIA',
XIANXIA = 'XIANXIA',
CUSTOM = 'CUSTOM',
}
export type WorldTemplateType = Exclude<WorldType, WorldType.CUSTOM>;
export enum AnimationState {
IDLE = 'idle',
ACQUIRE = 'acquire',
ATTACK = 'attack',
RUN = 'run',
JUMP = 'jump',
DOUBLE_JUMP = 'double jump',
JUMP_ATTACK = 'jump attack',
DASH = 'dash',
HURT = 'hurt',
DIE = 'die',
CLIMB = 'climb',
SKILL1 = 'skill1',
SKILL1_JUMP = 'skill1 jump',
SKILL1_BULLET = 'skill1 bullet',
SKILL1_BULLET_FX = 'skill1 bullet FX',
SKILL2 = 'skill2',
SKILL2_JUMP = 'skill2 jump',
SKILL3 = 'skill3',
SKILL3_JUMP = 'skill3 jump',
SKILL3_BULLET = 'skill3 bullet',
SKILL3_BULLET_FX = 'skill3 bullet FX',
SKILL4 = 'skill4',
WALL_SLIDE = 'Wall Slide',
}
export interface CharacterAnimationConfig {
folder: string;
prefix: string;
frames: number;
startFrame?: number;
extension?: string;
file?: string;
basePath?: string;
frameWidth?: number;
frameHeight?: number;
fps?: number;
loop?: boolean;
previewVideoPath?: string;
}
export type SkillStyle = 'burst' | 'steady' | 'mobility' | 'finisher' | 'projectile';
export type PlayerStateMode = 'battle' | 'idle';
export type FunctionCategory = 'battle' | 'escape' | 'idle' | 'recovery';
export type ItemRarity = 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary';
export type EquipmentSlotId = 'weapon' | 'armor' | 'relic';
export type CharacterGender = 'male' | 'female' | 'unknown';
export type ItemWorldAffinity = 'neutral' | 'wuxia' | 'xianxia';
export type ConversationGuardStyle = 'blunt' | 'wary' | 'evasive' | 'measured';
export type ConversationWarmStyle = 'dry' | 'steady' | 'gentle' | 'teasing';
export type ConversationTruthStyle = 'direct' | 'fragmented' | 'deflecting';
export type NpcDisclosureStage = 'guarded' | 'partial' | 'honest' | 'deep';
export type NpcWarmthStage = 'distant' | 'neutral' | 'cooperative' | 'warm';
export type NpcAnswerMode = 'situational_only' | 'half_truth' | 'true_but_incomplete' | 'candid';
export type ConversationSituation =
| 'first_contact_cautious'
| 'camp_first_contact'
| 'camp_followup'
| 'post_battle_breath'
| 'shared_danger_coordination'
| 'private_followup';
export type ConversationPressure = 'high' | 'medium' | 'low';
export type NpcFunctionType = 'trade' | 'fight' | 'spar' | 'help' | 'chat' | 'recruit' | 'gift' | 'quest';
export type QuestObjectiveKind =
| 'defeat_hostile_npc'
| 'inspect_treasure'
| 'spar_with_npc'
| 'talk_to_npc'
| 'reach_scene'
| 'deliver_item';
export type QuestStatus =
| 'discovered'
| 'active'
| 'ready_to_turn_in'
| 'completed'
| 'turned_in'
| 'failed'
| 'expired';
export type NpcInteractionAction =
| NpcFunctionType
| 'leave'
| 'quest_accept'
| 'quest_offer_abandon'
| 'quest_offer_replace'
| 'quest_offer_view'
| 'quest_turn_in';
export type TreasureInteractionAction = 'secure' | 'inspect' | 'leave';
export type NpcBattleMode = 'fight' | 'spar';
export type NpcBattleOutcome = 'fight_victory' | 'spar_complete';
export type CombatDelivery = 'melee' | 'ranged';
export type CombatActionMode = 'idle' | CombatDelivery;
export type SkillEffectPhase = 'cast' | 'travel' | 'impact';
export type SkillEffectAnchor = 'caster' | 'target';
export type SkillEffectMotion = 'stationary' | 'projectile';
export type HostileNpcRenderAnimation = 'idle' | 'move' | 'attack' | 'die';
export type MonsterRenderAnimation = HostileNpcRenderAnimation;
export type FacingDirection = 'left' | 'right';

438
src/types/customWorld.ts Normal file
View File

@@ -0,0 +1,438 @@
import type { EightAnchorContent } from '../../packages/shared/src/contracts/customWorldAgent';
import type {
ItemAttributeResonance,
RoleAttributeProfile,
WorldAttributeSchema,
} from './attributes';
import type { CharacterBackstoryRevealConfig } from './characters';
import {
AnimationState,
type CharacterAnimationConfig,
type EquipmentSlotId,
type ItemRarity,
type WorldTemplateType,
} from './core';
import type {ItemStatProfile, ItemUseProfile} from './items';
import type {
ActorNarrativeProfile,
KnowledgeFact,
SceneNarrativeResidue,
ThemePack,
ThreadContract,
WorldStoryGraph,
} from './storyEngine';
export type CustomWorldCreatorInputMode = 'freeform' | 'card';
export type CustomWorldGenerationMode = 'fast' | 'full';
export type CustomWorldGenerationStatus = 'key_only' | 'complete';
export type CustomWorldCoverSourceType = 'default' | 'uploaded' | 'generated';
export type CustomWorldAgentUiState = {
activeSessionId?: string | null;
activeOperationId?: string | null;
customWorldGenerationSource?: 'agent-draft-foundation' | null;
ownerUserId?: string | null;
};
export interface CustomWorldCoverProfile {
sourceType: CustomWorldCoverSourceType;
imageSrc?: string | null;
characterRoleIds?: string[];
}
export interface CustomWorldCoverCropRect {
x: number;
y: number;
width: number;
height: number;
}
export interface CreatorFactionSeed {
id: string;
name: string;
publicGoal: string;
tension: string;
notes: string;
locked?: boolean;
}
export interface CreatorCharacterSeed {
id: string;
name: string;
role: string;
publicMask: string;
hiddenHook: string;
relationToPlayer: string;
notes: string;
locked?: boolean;
}
export interface CreatorLandmarkSeed {
id: string;
name: string;
purpose: string;
mood: string;
secret: string;
locked?: boolean;
}
export interface ActorAnchor {
id: string;
name: string;
summary: string;
}
export interface LandmarkAnchor {
id: string;
name: string;
summary: string;
}
export interface CustomWorldCreatorIntent {
sourceMode: CustomWorldCreatorInputMode;
rawSettingText: string;
worldHook: string;
themeKeywords: string[];
toneDirectives: string[];
playerPremise: string;
openingSituation: string;
coreConflicts: string[];
keyFactions: CreatorFactionSeed[];
keyCharacters: CreatorCharacterSeed[];
keyLandmarks: CreatorLandmarkSeed[];
iconicElements: string[];
forbiddenDirectives: string[];
}
export interface CustomWorldAnchorPack {
worldSummary: string;
creatorIntentSummary: string;
lockedAnchorIds: string[];
keyConflictSummaries: string[];
keyFactionSummaries: string[];
keyCharacterAnchors: ActorAnchor[];
keyLandmarkAnchors: LandmarkAnchor[];
motifDirectives: string[];
}
export interface CustomWorldLockState {
worldLockedFields: string[];
lockedCharacterIds: string[];
lockedLandmarkIds: string[];
lockedConflictIds: string[];
lockedFactionIds: string[];
}
export interface CustomWorldSemanticAnchor {
genreSignals: string[];
conflictForms: string[];
institutionTypes: string[];
tabooTypes: string[];
carrierTypes: string[];
forceSystemTypes: string[];
atmosphereTags: string[];
}
export interface CustomWorldRuleProfile {
attributeSchema: WorldAttributeSchema;
resourceLabels: {
hp: string;
mp: string;
maxHp: string;
maxMp: string;
damage: string;
guard: string;
range: string;
cooldown: string;
manaCost: string;
currency: string;
};
economyProfile: {
initialCurrency: number;
};
}
export interface RoleArchetypeProfile {
id: string;
label: string;
combatFocus: string;
narrativeFunction: string;
sourceRoleIds: string[];
sourceTemplateCharacterIds: string[];
tags: string[];
}
export interface SceneArchetypeBucket {
id: string;
label: string;
moodTags: string[];
keywords: string[];
referenceLandmarkIds: string[];
}
export interface CreatureArchetypeProfile {
id: string;
label: string;
threatStyle: string;
keywords: string[];
}
export interface CustomWorldReferenceProfile {
roleArchetypes: RoleArchetypeProfile[];
sceneBuckets: SceneArchetypeBucket[];
creatureArchetypes: CreatureArchetypeProfile[];
}
export interface CustomWorldExpressionProfile {
themePack: ThemePack;
presentationTone: string[];
namingDirectives: string[];
clueDirectives: string[];
revealDirectives: string[];
}
export interface CustomWorldCompatibilityProfile {
compatibilityTemplateWorldType?: WorldTemplateType | null;
legacyTemplateWorldType?: WorldTemplateType | null;
migrationVersion: string;
}
export interface CustomWorldOwnedSettingLayers {
semanticAnchor: CustomWorldSemanticAnchor;
ruleProfile: CustomWorldRuleProfile;
expressionProfile: CustomWorldExpressionProfile;
referenceProfile: CustomWorldReferenceProfile;
compatibilityProfile?: CustomWorldCompatibilityProfile | null;
}
export interface CustomWorldRoleSkill {
id: string;
name: string;
summary: string;
style: string;
actionPromptText?: string;
actionPreviewConfig?: CharacterAnimationConfig;
}
export interface CustomWorldRoleInitialItem {
id: string;
name: string;
category: string;
quantity: number;
rarity: ItemRarity;
description: string;
tags: string[];
iconSrc?: string;
}
export interface CustomWorldRoleRelation {
id: string;
targetRoleId: string;
summary: string;
}
export interface CustomWorldRoleProfile {
id: string;
name: string;
title: string;
role: string;
description: string;
visualDescription?: string;
actionDescription?: string;
sceneVisualDescription?: string;
backstory: string;
personality: string;
motivation: string;
combatStyle: string;
initialAffinity: number;
relationshipHooks: string[];
relations?: CustomWorldRoleRelation[];
tags: string[];
backstoryReveal: CharacterBackstoryRevealConfig;
skills: CustomWorldRoleSkill[];
initialItems: CustomWorldRoleInitialItem[];
imageSrc?: string;
generatedVisualAssetId?: string;
generatedAnimationSetId?: string;
animationMap?: Partial<Record<AnimationState, CharacterAnimationConfig>>;
attributeProfile?: RoleAttributeProfile;
narrativeProfile?: ActorNarrativeProfile | null;
}
export type CustomWorldNpcVisualRace = 'human' | 'elf' | 'orc' | 'goblin';
export type CustomWorldNpcVisualGearType = 'cloth' | 'leather' | 'metal' | 'melee' | 'magic' | 'ranged';
export interface CustomWorldNpcVisualGear {
type: CustomWorldNpcVisualGearType;
file: string;
frameIndex: number;
}
export interface CustomWorldNpcVisual {
race: CustomWorldNpcVisualRace;
bodyColor: string;
headIndex: number;
hairColorIndex: number;
hairStyleFrame: number;
facialHairEnabled: boolean;
facialHairColorIndex: number;
facialHairStyleFrame: number;
headgear?: CustomWorldNpcVisualGear | null;
mainHand?: CustomWorldNpcVisualGear | null;
offHand?: CustomWorldNpcVisualGear | null;
}
export interface CustomWorldPlayableNpc extends CustomWorldRoleProfile {}
export interface CustomWorldNpc extends CustomWorldRoleProfile {
visual?: CustomWorldNpcVisual;
}
export interface CustomWorldItem {
id: string;
name: string;
category: string;
rarity: ItemRarity;
description: string;
tags: string[];
iconSrc?: string;
sourcePath?: string;
origin?: 'generated' | 'catalog';
equipmentSlotId?: EquipmentSlotId | null;
statProfile?: ItemStatProfile | null;
useProfile?: ItemUseProfile | null;
value?: number;
attributeResonance?: ItemAttributeResonance | null;
}
export type CustomWorldSceneRelativePosition =
| 'forward'
| 'back'
| 'left'
| 'right'
| 'north'
| 'south'
| 'east'
| 'west'
| 'up'
| 'down'
| 'inside'
| 'outside'
| 'portal';
export interface CustomWorldSceneConnection {
targetLandmarkId: string;
relativePosition: CustomWorldSceneRelativePosition;
summary: string;
}
export type SceneActStage =
| 'opening'
| 'expansion'
| 'turning_point'
| 'climax'
| 'aftermath';
export type SceneActAdvanceRule =
| 'after_primary_contact'
| 'after_active_step_complete'
| 'after_chapter_resolution';
export interface SceneActBlueprint {
id: string;
sceneId: string;
title: string;
summary: string;
stageCoverage: SceneActStage[];
backgroundPromptText?: string | null;
backgroundImageSrc?: string | null;
backgroundAssetId?: string | null;
encounterNpcIds: string[];
primaryNpcId: string;
/** 当前幕对面的角色,草稿阶段默认与 primaryNpcId 保持一致。 */
oppositeNpcId: string;
/** 当前幕发生的事件描述,需强绑定对面角色与场景主线压力。 */
eventDescription: string;
linkedThreadIds: string[];
advanceRule: SceneActAdvanceRule;
actGoal: string;
transitionHook: string;
}
export interface SceneChapterBlueprint {
id: string;
sceneId: string;
title: string;
summary: string;
/** 首次进入该场景时生成章节任务所需的核心上下文。 */
sceneTaskDescription: string;
linkedThreadIds: string[];
linkedLandmarkIds: string[];
acts: SceneActBlueprint[];
}
export interface CustomWorldCampScene {
id: string;
name: string;
description: string;
visualDescription?: string;
imageSrc?: string;
sceneNpcIds: string[];
connections: CustomWorldSceneConnection[];
narrativeResidues?: SceneNarrativeResidue[] | null;
}
export interface CustomWorldLandmark {
id: string;
name: string;
description: string;
visualDescription?: string;
imageSrc?: string;
sceneNpcIds: string[];
connections: CustomWorldSceneConnection[];
narrativeResidues?: SceneNarrativeResidue[] | null;
}
export interface CustomWorldProfile {
id: string;
settingText: string;
name: string;
subtitle: string;
summary: string;
tone: string;
playerGoal: string;
/**
* 发布门槛直接读取的世界一句话钩子。
* Agent 结果页回写 session 时需要保留该字段,避免只剩 UI 归一化字段导致后端误判缺失。
*/
worldHook?: string | null;
/**
* 发布门槛直接读取的玩家身份与切入前提。
* 即使 creatorIntent / anchorContent 中已有结构化信息,也要保留顶层字段作为 SpacetimeDB 发布快照的稳定兼容槽位。
*/
playerPremise?: string | null;
cover?: CustomWorldCoverProfile | null;
templateWorldType: WorldTemplateType;
compatibilityTemplateWorldType?: WorldTemplateType | null;
majorFactions: string[];
coreConflicts: string[];
attributeSchema: WorldAttributeSchema;
playableNpcs: CustomWorldPlayableNpc[];
storyNpcs: CustomWorldNpc[];
items: CustomWorldItem[];
camp?: CustomWorldCampScene | null;
landmarks: CustomWorldLandmark[];
themePack?: ThemePack | null;
storyGraph?: WorldStoryGraph | null;
knowledgeFacts?: KnowledgeFact[] | null;
threadContracts?: ThreadContract[] | null;
sceneChapterBlueprints?: SceneChapterBlueprint[] | null;
anchorContent?: EightAnchorContent | null;
creatorIntent?: CustomWorldCreatorIntent | null;
anchorPack?: CustomWorldAnchorPack | null;
lockState?: CustomWorldLockState | null;
ownedSettingLayers?: CustomWorldOwnedSettingLayers | null;
generationMode?: CustomWorldGenerationMode | null;
generationStatus?: CustomWorldGenerationStatus | null;
scenarioPackId?: string | null;
campaignPackId?: string | null;
}

105
src/types/game.ts Normal file
View File

@@ -0,0 +1,105 @@
import type { TimedBuildBuff } from './build';
import type { Character } from './characters';
import {
AnimationState,
type CombatActionMode,
type NpcBattleMode,
type NpcBattleOutcome,
WorldType,
} from './core';
import type { CustomWorldProfile } from './customWorld';
import type { EquipmentLoadout, InventoryItem } from './items';
import type {
CombatVisualEffect,
CompanionState,
Encounter,
NpcPersistentState,
SceneEncounterResult,
SceneHostileNpc,
ScenePresetInfo,
} from './scene';
import type { CharacterChatRecord, QuestLogEntry, StoryMoment } from './story';
import type {
CampaignState,
ChapterState,
StoryEngineMemoryState,
} from './storyEngine';
export interface GameRuntimeStats {
playTimeMs: number;
lastPlayTickAt: string | null;
hostileNpcsDefeated: number;
questsAccepted: number;
itemsUsed: number;
scenesTraveled: number;
}
export type PlayerProgressionGrantSource = 'quest' | 'hostile_npc';
export interface PlayerProgressionState {
level: number;
currentLevelXp: number;
totalXp: number;
xpToNextLevel: number;
pendingLevelUps?: number;
lastGrantedSource?: PlayerProgressionGrantSource | null;
}
export interface GameState {
worldType: WorldType | null;
customWorldProfile: CustomWorldProfile | null;
playerCharacter: Character | null;
runtimeSessionId?: string | null;
runtimeActionVersion?: number;
runtimeStats: GameRuntimeStats;
playerProgression?: PlayerProgressionState | null;
currentScene: string;
storyHistory: StoryMoment[];
storyEngineMemory?: StoryEngineMemoryState;
chapterState?: ChapterState | null;
campaignState?: CampaignState | null;
activeScenarioPackId?: string | null;
activeCampaignPackId?: string | null;
characterChats: Record<string, CharacterChatRecord>;
ambientIdleMode?: 'observe_signs';
lastObserveSignsSceneId?: string | null;
lastObserveSignsReport?: string | null;
animationState: AnimationState;
currentEncounter: Encounter | null;
npcInteractionActive: boolean;
currentScenePreset: ScenePresetInfo | null;
sceneHostileNpcs: SceneHostileNpc[];
playerX: number;
playerOffsetY: number;
playerFacing: 'left' | 'right';
playerActionMode: CombatActionMode;
scrollWorld: boolean;
inBattle: boolean;
playerHp: number;
playerMaxHp: number;
playerMana: number;
playerMaxMana: number;
playerSkillCooldowns: Record<string, number>;
activeBuildBuffs?: TimedBuildBuff[];
activeCombatEffects: CombatVisualEffect[];
playerCurrency: number;
playerInventory: InventoryItem[];
playerEquipment: EquipmentLoadout;
npcStates: Record<string, NpcPersistentState>;
quests: QuestLogEntry[];
roster: CompanionState[];
companions: CompanionState[];
currentBattleNpcId: string | null;
currentNpcBattleMode: NpcBattleMode | null;
currentNpcBattleOutcome: NpcBattleOutcome | null;
sparReturnEncounter: Encounter | null;
sparPlayerHpBefore: number | null;
sparPlayerMaxHpBefore: number | null;
sparStoryHistoryBefore: StoryMoment[] | null;
}
export interface AIResponse {
storyText: string;
options: StoryMoment['options'];
encounter?: SceneEncounterResult;
}

110
src/types/items.ts Normal file
View File

@@ -0,0 +1,110 @@
import type {AttributeVector, ItemAttributeResonance} from './attributes';
import type {TimedBuildBuff} from './build';
import {
type EquipmentSlotId,
type ItemRarity,
type ItemWorldAffinity,
WorldType,
} from './core';
import type {RuntimeItemMetadata} from './runtimeItem';
export interface InventoryItem {
id: string;
category: string;
name: string;
quantity: number;
rarity: ItemRarity;
tags: string[];
catalogId?: string;
iconSrc?: string;
description?: string;
worldAffinity?: ItemWorldAffinity;
equipmentSlotId?: EquipmentSlotId | null;
worldProfiles?: Partial<Record<WorldType, ItemWorldProfile>>;
statProfile?: ItemStatProfile | null;
useProfile?: ItemUseProfile | null;
buildProfile?: ItemBuildProfile | null;
value?: number;
attributeResonance?: ItemAttributeResonance | null;
runtimeMetadata?: RuntimeItemMetadata | null;
}
export interface MonsterLootEntry {
id: string;
item: InventoryItem;
dropRate: number;
}
export interface ItemCatalogEntry {
id: string;
sourcePath: string;
iconSrc: string;
name: string;
category: string;
rarity: ItemRarity;
tags: string[];
description: string;
worldAffinity: ItemWorldAffinity;
equipmentSlotId?: EquipmentSlotId | null;
worldProfiles?: Partial<Record<WorldType, ItemWorldProfile>>;
statProfile?: ItemStatProfile | null;
useProfile?: ItemUseProfile | null;
buildProfile?: ItemBuildProfile | null;
value?: number;
attributeResonance?: ItemAttributeResonance | null;
runtimeMetadata?: RuntimeItemMetadata | null;
}
export interface ItemCatalogOverride {
name?: string;
category?: string;
rarity?: ItemRarity;
tags?: string[];
description?: string;
worldAffinity?: ItemWorldAffinity;
equipmentSlotId?: EquipmentSlotId | null;
worldProfiles?: Partial<Record<WorldType, ItemWorldProfile>>;
statProfile?: ItemStatProfile | null;
useProfile?: ItemUseProfile | null;
buildProfile?: ItemBuildProfile | null;
value?: number;
attributeResonance?: ItemAttributeResonance | null;
runtimeMetadata?: RuntimeItemMetadata | null;
}
export interface ItemWorldProfile {
name: string;
description: string;
}
export interface ItemStatProfile {
maxHpBonus?: number;
maxManaBonus?: number;
outgoingDamageBonus?: number;
incomingDamageMultiplier?: number;
}
export interface ItemUseProfile {
hpRestore?: number;
manaRestore?: number;
cooldownReduction?: number;
buildBuffs?: TimedBuildBuff[];
}
export interface ItemBuildProfile {
role: string;
tags: string[];
setId?: string;
setName?: string;
pieceName?: string;
synergy?: string[];
craftTags?: string[];
forgeRank?: number;
resonanceVector?: AttributeVector;
}
export interface EquipmentLoadout {
weapon: InventoryItem | null;
armor: InventoryItem | null;
relic: InventoryItem | null;
}

1
src/types/navigation.ts Normal file
View File

@@ -0,0 +1 @@
export type BottomTab = 'character' | 'adventure' | 'inventory';

122
src/types/runtimeItem.ts Normal file
View File

@@ -0,0 +1,122 @@
import type {WorldType} from './core';
import type {CustomWorldProfile} from './customWorld';
import type {InventoryItem} from './items';
import type {Encounter, NpcPersistentState, ScenePresetInfo} from './scene';
import type {
ActorNarrativeProfile,
CarrierStoryFingerprint,
} from './storyEngine';
export type RuntimeItemGenerationChannel =
| 'treasure'
| 'npc_trade'
| 'npc_reward'
| 'monster_drop'
| 'quest_reward'
| 'discovery';
export type RuntimeItemOrigin = 'catalog' | 'procedural' | 'ai_compiled';
export type RuntimeItemKind = 'equipment' | 'consumable' | 'material' | 'relic' | 'quest';
export type RuntimeItemPermanence = 'permanent' | 'timed' | 'resource';
export type RuntimeNarrativeWeight = 'light' | 'medium' | 'heavy';
export type RuntimeItemPlanSlot = 'primary' | 'secondary' | 'support';
export type RuntimeRelationAnchor =
| {type: 'npc'; npcId?: string; npcName: string; roleText?: string}
| {type: 'scene'; sceneId?: string; sceneName: string}
| {type: 'landmark'; landmarkName: string}
| {type: 'monster'; monsterId?: string; monsterName: string}
| {type: 'faction'; factionName: string}
| {type: 'quest'; questId?: string; questName: string};
export interface RuntimeItemMetadata {
origin: RuntimeItemOrigin;
generationChannel: RuntimeItemGenerationChannel;
seedKey: string;
relationAnchor?: RuntimeRelationAnchor;
sourceReason: string;
recentEventHook?: string;
storyFingerprint?: CarrierStoryFingerprint | null;
}
export interface RuntimeItemGenerationContext {
worldType: WorldType | null;
customWorldProfile: CustomWorldProfile | null;
sceneId: string | null;
sceneName: string | null;
sceneDescription: string | null;
sceneTags: string[];
treasureHints: string[];
encounter: Encounter | null;
encounterNpcId: string | null;
encounterNpcName: string | null;
encounterContextText: string | null;
relatedNpcState: NpcPersistentState | null;
relatedNpcNarrativeProfile?: ActorNarrativeProfile | null;
relatedScene: Pick<ScenePresetInfo, 'id' | 'name' | 'description' | 'treasureHints'> | null;
recentStorySummary: string;
recentActions: string[];
activeThreadIds?: string[] | null;
playerCharacterId: string;
playerBuildTags: string[];
playerBuildGaps: string[];
playerEquipmentTags: string[];
generationChannel: RuntimeItemGenerationChannel;
}
export interface RuntimeItemPlan {
slot: RuntimeItemPlanSlot;
itemKind: RuntimeItemKind;
permanence: RuntimeItemPermanence;
narrativeWeight: RuntimeNarrativeWeight;
targetBuildDirection: string[];
relationAnchor: RuntimeRelationAnchor;
}
export interface RuntimeItemAiPromptInput {
worldSummary: string;
sceneSummary: string;
encounterSummary: string;
relatedNpcSummary: string;
recentStorySummary: string;
activeThreadSummary?: string;
generationChannel: RuntimeItemGenerationChannel;
playerBuildDirection: string[];
playerBuildGaps: string[];
desiredItemKind: string;
permanence: string;
}
export interface RuntimeItemAiIntent {
shortNameSeed: string;
sourcePhrase: string;
reasonToAppear: string;
relationHooks: string[];
desiredBuildTags: string[];
desiredFunctionalBias: Array<'heal' | 'mana' | 'cooldown' | 'guard' | 'damage'>;
tone: 'grim' | 'mysterious' | 'martial' | 'ritual' | 'survival';
visibleClue?: string;
witnessMark?: string;
unfinishedBusiness?: string;
hiddenHook?: string;
reactionHooks?: string[];
namingPattern?: string;
}
export interface RuntimeItemCompileBudget {
rarity: InventoryItem['rarity'];
buildTagLimit: number;
timedBuffTagLimit: number;
timedBuffDuration: number;
statBudgetTier: number;
}
export interface DirectedRuntimeReward {
primaryItem?: InventoryItem | null;
supportItems: InventoryItem[];
hp?: number;
mana?: number;
currency?: number;
storyHint?: string;
}

259
src/types/scene.ts Normal file
View File

@@ -0,0 +1,259 @@
import type {
RoleActionDefinition,
RoleAttributeProfile,
RoleRelationState,
} from './attributes';
import type {
Character,
CharacterBackstoryRevealConfig,
} from './characters';
import {
AnimationState,
type CharacterGender,
type CombatActionMode,
type CombatDelivery,
type FacingDirection,
type HostileNpcRenderAnimation,
type NpcFunctionType,
} from './core';
import type {
CustomWorldNpcVisual,
CustomWorldRoleInitialItem,
CustomWorldRoleSkill,
CustomWorldSceneRelativePosition,
} from './customWorld';
import type {InventoryItem} from './items';
import type {
ActorNarrativeProfile,
CompanionStanceProfile,
SceneNarrativeResidue,
} from './storyEngine';
export interface NpcPersistentState {
affinity: number;
relationState?: RoleRelationState;
helpUsed: boolean;
chattedCount: number;
giftsGiven: number;
inventory: InventoryItem[];
tradeStockSignature?: string | null;
recruited: boolean;
revealedFacts?: string[];
knownAttributeRumors?: string[];
firstMeaningfulContactResolved?: boolean;
seenBackstoryChapterIds?: string[];
stanceProfile?: CompanionStanceProfile;
}
export interface CompanionState {
npcId: string;
characterId: string;
joinedAtAffinity: number;
hp: number;
maxHp: number;
mana: number;
maxMana: number;
skillCooldowns: Record<string, number>;
animationState?: AnimationState;
actionMode?: CombatActionMode;
offsetX?: number;
offsetY?: number;
transitionMs?: number;
}
export interface CompanionRenderState {
npcId: string;
character: Character;
hp: number;
maxHp: number;
mana: number;
maxMana: number;
skillCooldowns: Record<string, number>;
animationState: AnimationState;
actionMode?: CombatActionMode;
slot: 'upper' | 'lower';
facing?: FacingDirection;
entryOffsetX?: number;
entryOffsetY?: number;
transitionMs?: number;
recruitToken?: number;
}
export interface Encounter {
id?: string;
kind?: 'npc' | 'treasure';
npcName: string;
npcDescription: string;
npcAvatar: string;
context: string;
gender?: CharacterGender;
specialBehavior?: 'initial_companion' | 'camp_companion';
xMeters?: number;
characterId?: string;
monsterPresetId?: string;
initialAffinity?: number;
hostile?: boolean;
attributeProfile?: RoleAttributeProfile;
title?: string;
backstory?: string;
personality?: string;
motivation?: string;
combatStyle?: string;
relationshipHooks?: string[];
tags?: string[];
backstoryReveal?: CharacterBackstoryRevealConfig;
skills?: CustomWorldRoleSkill[];
initialItems?: CustomWorldRoleInitialItem[];
imageSrc?: string;
visual?: CustomWorldNpcVisual;
narrativeProfile?: ActorNarrativeProfile | null;
levelProfile?: EntityLevelProfile;
experienceReward?: number;
}
export type ProgressionRole =
| 'guide'
| 'ambient'
| 'support'
| 'hostile_standard'
| 'hostile_elite'
| 'hostile_boss'
| 'rival';
export interface EntityLevelProfile {
level: number;
referenceStrength: number;
chapterId?: string | null;
chapterIndex?: number | null;
progressionRole: ProgressionRole;
source: 'chapter_auto' | 'preset_override' | 'manual';
}
export interface SceneHostileNpc {
id: string;
name: string;
action: string;
description: string;
animation: HostileNpcRenderAnimation;
xMeters: number;
yOffset: number;
facing: FacingDirection;
attackRange: number;
speed: number;
hp: number;
maxHp: number;
renderKind?: 'npc';
encounter?: Encounter;
characterAnimation?: AnimationState;
combatMode?: CombatDelivery;
combatTags?: string[];
attributeProfile?: RoleAttributeProfile;
behaviorVectors?: RoleActionDefinition[];
levelProfile?: EntityLevelProfile;
experienceReward?: number;
}
export interface SceneNpc {
id: string;
name: string;
description: string;
avatar: string;
role: string;
title?: string;
gender?: CharacterGender;
characterId?: string;
monsterPresetId?: string;
initialAffinity?: number;
hostile?: boolean;
functions?: NpcFunctionType[];
recruitable?: boolean;
attributeProfile?: RoleAttributeProfile;
backstory?: string;
personality?: string;
motivation?: string;
combatStyle?: string;
relationshipHooks?: string[];
tags?: string[];
backstoryReveal?: CharacterBackstoryRevealConfig;
skills?: CustomWorldRoleSkill[];
initialItems?: CustomWorldRoleInitialItem[];
imageSrc?: string;
visual?: CustomWorldNpcVisual;
narrativeProfile?: ActorNarrativeProfile | null;
levelProfile?: EntityLevelProfile;
}
export type SceneEncounterKind = 'npc' | 'treasure' | 'none';
export interface SceneEncounterResult {
kind: SceneEncounterKind;
npcId?: string;
treasureText?: string;
}
export interface CombatVisualEffect {
id: string;
frames: string[];
fps: number;
startX: number;
endX?: number;
startOrigin: 'player' | 'hostile_npc' | 'monster';
endOrigin?: 'player' | 'hostile_npc' | 'monster';
startMonsterId?: string;
endMonsterId?: string;
startHostileNpcId?: string;
endHostileNpcId?: string;
startAnchorOffsetY?: number;
endAnchorOffsetY?: number;
startOffsetX?: number;
endOffsetX?: number;
startYOffset: number;
endYOffset?: number;
durationMs: number;
sizePx: number;
scale?: number;
facing: FacingDirection;
zIndex?: number;
traveling?: boolean;
}
export interface SceneHostileNpcChange {
id: string;
action: string;
animation: HostileNpcRenderAnimation;
moveMeters?: number;
yOffset?: number;
}
export type SceneMonsterChange = SceneHostileNpcChange;
export interface SceneDirective {
playerAnimation: AnimationState;
playerMoveMeters: number;
playerOffsetY: number;
playerFacing: FacingDirection;
scrollWorld: boolean;
monsterChanges: SceneMonsterChange[];
hostileNpcChanges?: SceneHostileNpcChange[];
}
export interface ScenePresetInfo {
id: string;
name: string;
description: string;
imageSrc: string;
forwardSceneId?: string;
connectedSceneIds?: string[];
npcs?: SceneNpc[];
treasureHints?: string[];
connections?: SceneConnectionInfo[];
narrativeResidues?: SceneNarrativeResidue[];
mutationStateText?: string | null;
currentPressureLevel?: 'low' | 'medium' | 'high' | 'extreme';
}
export interface SceneConnectionInfo {
sceneId: string;
relativePosition: CustomWorldSceneRelativePosition;
summary: string;
}

184
src/types/story.ts Normal file
View File

@@ -0,0 +1,184 @@
import {
type NpcInteractionAction,
type QuestObjectiveKind,
type QuestStatus,
type TreasureInteractionAction,
} from './core';
import type { InventoryItem } from './items';
import type { SceneDirective, ScenePresetInfo } from './scene';
import type { StoryEngineMemoryState } from './storyEngine';
export interface StoryOptionGoalAffordance {
goalId: string;
relation: 'advance' | 'support' | 'detour';
label: string;
}
export interface StoryOption {
functionId: string;
actionText: string;
text?: string;
detailText?: string;
priority?: number;
visuals: SceneDirective;
skillProbabilities?: Record<string, number>;
interaction?: StoryOptionInteraction;
goalAffordance?: StoryOptionGoalAffordance | null;
runtimePayload?: Record<string, unknown>;
disabled?: boolean;
disabledReason?: string;
}
export interface QuestReward {
affinityBonus: number;
currency: number;
experience?: number;
items: InventoryItem[];
storyHint?: string;
intel?: {
codexEntry?: string;
rumorText?: string;
unlockedSceneId?: string;
};
}
export interface QuestObjective {
kind: QuestObjectiveKind;
targetHostileNpcId?: string;
targetNpcId?: string;
targetSceneId?: string;
targetItemId?: string;
requiredCount: number;
}
export type QuestNarrativeType =
| 'bounty'
| 'escort'
| 'investigation'
| 'retrieval'
| 'relationship'
| 'trial';
export interface QuestNarrativeBinding {
origin: 'fallback_builder' | 'ai_compiled';
narrativeType: QuestNarrativeType;
dramaticNeed: string;
issuerGoal: string;
playerHook: string;
worldReason: string;
followupHooks: string[];
}
export interface QuestStep extends QuestObjective {
id: string;
title: string;
progress: number;
revealText: string;
completeText: string;
}
export interface QuestLogEntry {
id: string;
issuerNpcId: string;
issuerNpcName: string;
sceneId: string | null;
chapterId?: string | null;
actId?: string | null;
threadId?: string | null;
contractId?: string | null;
title: string;
description: string;
summary: string;
objective: QuestObjective;
progress: number;
status: QuestStatus;
completionNotified?: boolean;
reward: QuestReward;
rewardText: string;
narrativeBinding?: QuestNarrativeBinding;
steps?: QuestStep[];
activeStepId?: string | null;
visibleStage?: number;
hiddenFlags?: string[];
discoveredFactIds?: string[];
relatedCarrierIds?: string[];
consequenceIds?: string[];
}
export interface StoryDialogueTurn {
speaker: 'player' | 'npc' | 'companion' | 'system';
speakerName?: string;
text: string;
affinityDelta?: number;
}
export interface StoryNpcAffinityEffect {
eventId: string;
npcId: string;
delta: number;
}
export interface StoryNpcChatState {
npcId: string;
npcName: string;
turnCount: number;
customInputPlaceholder?: string;
openingSource?: 'npc_initiated' | 'player_reply';
sceneActId?: string | null;
turnLimit?: number | null;
remainingTurns?: number | null;
limitReason?: 'negative_affinity' | null;
forceExitAfterTurn?: boolean;
terminationMode?: 'none' | 'hostile_model' | null;
terminationReason?: 'hostile_breakoff' | 'player_exit' | null;
isHostileChat?: boolean;
pendingQuestOffer?: {
quest: QuestLogEntry;
} | null;
combatContext?: {
summary: string;
logLines: string[];
battleOutcome: 'victory' | 'spar_complete';
} | null;
}
export interface CharacterChatTurn {
speaker: 'player' | 'character';
text: string;
}
export interface CharacterChatRecord {
history: CharacterChatTurn[];
summary: string;
updatedAt: string | null;
}
export type StoryHistoryRole = 'action' | 'result';
export interface StoryMoment {
text: string;
options: StoryOption[];
displayMode?: 'narrative' | 'dialogue';
dialogue?: StoryDialogueTurn[];
streaming?: boolean;
deferredOptions?: StoryOption[];
deferredRuntimeState?: {
currentScenePreset?: ScenePresetInfo | null;
storyEngineMemory?: StoryEngineMemoryState;
};
historyRole?: StoryHistoryRole;
npcChatState?: StoryNpcChatState;
npcAffinityEffect?: StoryNpcAffinityEffect | null;
}
export type StoryOptionInteraction =
| {
kind: 'npc';
npcId: string;
action: NpcInteractionAction;
questId?: string;
}
| {
kind: 'treasure';
action: TreasureInteractionAction;
};

560
src/types/storyEngine.ts Normal file
View File

@@ -0,0 +1,560 @@
export type StoryThreadVisibility = 'visible' | 'hidden';
export type StoryMotifSemanticRole =
| 'institution'
| 'ritual'
| 'technology'
| 'taboo'
| 'ruin'
| 'memory'
| 'resource'
| 'creature';
export type SceneNarrativeRevealBudget = 'low' | 'medium' | 'high';
export type SceneNarrativeCadence =
| 'tense'
| 'curious'
| 'hostile'
| 'intimate'
| 'tragic'
| 'mysterious';
export interface ThemePack {
id: string;
displayName: string;
toneRange: string[];
institutionLexicon: string[];
tabooLexicon: string[];
artifactClasses: string[];
actorArchetypes: string[];
conflictForms: string[];
clueForms: string[];
namingPatterns: string[];
revealStyles: string[];
}
export interface StoryThread {
id: string;
title: string;
visibility: StoryThreadVisibility;
summary: string;
conflictType: string;
stakes: string;
involvedFactionIds: string[];
involvedActorIds: string[];
relatedLocationIds: string[];
}
export interface StoryScar {
id: string;
title: string;
pastEvent: string;
publicResidue: string;
hiddenTruth: string;
relatedActorIds: string[];
relatedLocationIds: string[];
}
export interface StoryMotif {
id: string;
label: string;
semanticRole: StoryMotifSemanticRole;
lexicalHints: string[];
}
export interface WorldStoryGraph {
visibleThreads: StoryThread[];
hiddenThreads: StoryThread[];
scars: StoryScar[];
motifs: StoryMotif[];
}
export interface ActorNarrativeProfile {
publicMask: string;
firstContactMask: string;
visibleLine: string;
hiddenLine: string;
contradiction: string;
debtOrBurden: string;
taboo: string;
immediatePressure: string;
relatedThreadIds: string[];
relatedScarIds: string[];
reactionHooks: string[];
}
export interface KnowledgeFact {
id: string;
title: string;
content: string;
ownerActorIds: string[];
relatedThreadIds: string[];
relatedScarIds: string[];
sourceType: 'actor' | 'item' | 'document' | 'scene' | 'monster' | 'quest';
visibility: 'public' | 'discoverable' | 'private' | 'forbidden';
sayability: 'direct' | 'indirect' | 'reactive_only';
aliases?: string[];
}
export interface VisibilitySlice {
factIds: string[];
sayableFactIds: string[];
inferredFactIds: string[];
forbiddenFactIds: string[];
misdirectionHints: string[];
}
export interface SceneNarrativeDirective {
primaryPressure: string;
activeThreadIds: string[];
foregroundActorIds: string[];
foregroundCarrierIds: string[];
revealBudget: SceneNarrativeRevealBudget;
emotionalCadence: SceneNarrativeCadence;
}
export type GoalSourceKind =
| 'quest'
| 'chapter'
| 'journey_beat'
| 'thread_contract'
| 'setpiece'
| 'relationship'
| 'survival';
export type GoalTrack =
| 'main'
| 'side'
| 'relationship'
| 'survival'
| 'exploration';
export type GoalStatus =
| 'teased'
| 'active'
| 'blocked'
| 'ready_to_resolve'
| 'resolved'
| 'archived';
export type GoalLayer =
| 'north_star'
| 'active_contract'
| 'immediate_step'
| 'support';
export interface GoalStackEntry {
id: string;
sourceKind: GoalSourceKind;
sourceId: string;
layer: GoalLayer;
track: GoalTrack;
title: string;
promiseText: string;
whyNow: string;
nextStepText: string;
sceneHint?: string | null;
npcHint?: string | null;
progressLabel?: string | null;
status: GoalStatus;
urgency: 'low' | 'medium' | 'high';
relatedThreadIds: string[];
}
export interface GoalStackState {
northStarGoal: GoalStackEntry | null;
activeGoal: GoalStackEntry | null;
immediateStepGoal: GoalStackEntry | null;
supportGoals: GoalStackEntry[];
}
export interface GoalHandoff {
goalId: string;
title: string;
detail: string;
track: GoalTrack;
}
export interface GoalPulseEvent {
id: string;
goalId: string;
pulseType: 'progress' | 'ready_to_turn_in' | 'resolved' | 'handoff';
title: string;
detail: string;
track: GoalTrack;
}
export interface CarrierStoryFingerprint {
visibleClue: string;
witnessMark: string;
unresolvedQuestion: string;
currentAppearanceReason: string;
relatedThreadIds: string[];
relatedScarIds: string[];
reactionHooks: string[];
}
export interface ThreadContractStep {
id: string;
title: string;
revealText: string;
completionSignalIds: string[];
optionalFactIds: string[];
}
export interface ThreadContract {
id: string;
threadId: string;
issuerActorId?: string | null;
narrativeType:
| 'investigation'
| 'escort'
| 'hunt'
| 'relationship'
| 'trial'
| 'recovery';
currentStepId: string | null;
visibleStage: number;
steps: ThreadContractStep[];
followupThreadIds: string[];
}
export interface StorySignal {
id: string;
signalType:
| 'enter_scene'
| 'leave_scene'
| 'talk_to_actor'
| 'obtain_carrier'
| 'inspect_scene'
| 'win_battle'
| 'give_item'
| 'accept_contract'
| 'resolve_contract_step';
actorId?: string | null;
sceneId?: string | null;
carrierId?: string | null;
threadIds?: string[];
}
export interface CompanionReactionRecord {
id: string;
characterId: string;
reactionType: 'approve' | 'disapprove' | 'concern' | 'silence' | 'curious';
reason: string;
relatedThreadIds: string[];
createdAt: string;
}
export interface SceneNarrativeResidue {
id: string;
title: string;
visibleClue: string;
linkedFactIds: string[];
linkedThreadIds: string[];
}
export interface ChapterState {
id: string;
title: string;
theme: string;
primaryThreadIds: string[];
stage: 'opening' | 'expansion' | 'turning_point' | 'climax' | 'aftermath';
chapterSummary: string;
sceneId?: string | null;
chapterQuestId?: string | null;
}
export interface SceneActRuntimeState {
sceneId: string;
chapterId: string;
currentActId: string;
currentActIndex: number;
completedActIds: string[];
visitedActIds: string[];
}
export interface JourneyBeat {
id: string;
beatType:
| 'approach'
| 'investigation'
| 'camp'
| 'conflict'
| 'boss_prelude'
| 'climax'
| 'recovery';
title: string;
triggerThreadIds: string[];
recommendedSceneIds: string[];
emotionalGoal: string;
}
export interface CompanionArcState {
characterId: string;
arcTheme: string;
currentStage: 'closed' | 'guarded' | 'opening' | 'conflicted' | 'bonded' | 'resolved';
activeConflictTags: string[];
pendingEventIds: string[];
resolvedEventIds: string[];
}
export interface CampEvent {
id: string;
eventType:
| 'private_talk'
| 'party_banter'
| 'conflict'
| 'comfort'
| 'reveal'
| 'decision';
title: string;
participantCharacterIds: string[];
triggerReason: string;
relatedThreadIds: string[];
}
export interface WorldMutation {
id: string;
mutationType:
| 'scene_text'
| 'npc_attitude'
| 'shop_style'
| 'enemy_pressure'
| 'route_lock'
| 'route_unlock';
targetId: string;
reason: string;
relatedThreadIds: string[];
}
export interface FactionTensionState {
factionId: string;
temperature: number;
pressureSummary: string;
activeConflictThreadIds: string[];
}
export interface ChronicleEntry {
id: string;
category: 'chapter' | 'thread' | 'companion' | 'carrier' | 'scene' | 'world_event';
title: string;
summary: string;
relatedIds: string[];
createdAt: string;
}
export interface SetpieceDirective {
id: string;
title: string;
setpieceType: 'boss_prelude' | 'showdown' | 'climax' | 'aftermath';
relatedThreadIds: string[];
sceneFocusId?: string | null;
dramaticQuestion: string;
}
export interface CampaignState {
id: string;
title: string;
currentActId: string | null;
currentActIndex: number;
resolvedEndingId?: string | null;
}
export interface ActState {
id: string;
title: string;
actIndex: number;
theme: string;
primaryThreadIds: string[];
status: 'opening' | 'midgame' | 'late_game' | 'finale' | 'resolved';
}
export interface ConsequenceRecord {
id: string;
category: 'thread' | 'companion' | 'faction' | 'world' | 'ending_flag';
title: string;
summary: string;
weight: number;
relatedIds: string[];
irreversible: boolean;
}
export interface CompanionResolution {
characterId: string;
resolutionType:
| 'bonded'
| 'reconciled'
| 'estranged'
| 'departed'
| 'sacrificed'
| 'neutral';
summary: string;
relatedThreadIds: string[];
}
export interface EndingState {
id: string;
title: string;
endingType: 'heroic' | 'tragic' | 'bitter_sweet' | 'fractured' | 'ascendant';
summary: string;
contributingThreadIds: string[];
companionResolutions: CompanionResolution[];
worldOutcomeSummary: string;
}
export interface AuthorialConstraintPack {
toneRules: string[];
noGoPatterns: string[];
branchBudget: {
maxMajorDivergences: number;
maxEndingFamilies: number;
};
mandatoryThemes: string[];
requiredPayoffs: string[];
}
export interface BranchBudgetStatus {
currentMajorDivergences: number;
maxMajorDivergences: number;
currentEndingFamilies: number;
maxEndingFamilies: number;
pressure: 'low' | 'medium' | 'high';
}
export interface NarrativeQaIssue {
id: string;
severity: 'low' | 'medium' | 'high';
category: 'consistency' | 'pacing' | 'payoff' | 'branch_budget' | 'reveal_leak';
summary: string;
relatedIds: string[];
}
export interface NarrativeQaReport {
generatedAt: string;
issues: NarrativeQaIssue[];
summary: string;
}
export interface ScenarioPack {
id: string;
title: string;
version: string;
worldPackIds: string[];
campaignIds: string[];
sharedConstraintPackIds: string[];
}
export interface CampaignPack {
id: string;
scenarioPackId: string;
title: string;
authoringStyle: string;
campaignStateSeed: CampaignState;
actTemplates: ActState[];
requiredCompanionIds: string[];
}
export interface PlayerStyleProfile {
id: string;
preferenceWeights: {
story: number;
exploration: number;
combat: number;
companion: number;
collection: number;
};
dominantStyle:
| 'story_first'
| 'explorer'
| 'combat_driver'
| 'companion_bond'
| 'collector';
}
export interface SimulationRunResult {
id: string;
scenarioPackId: string;
campaignPackId: string;
seed: string;
endingId?: string | null;
activeThreadCountPeak: number;
fracturedCompanionCount: number;
issueCount: number;
summary: string;
}
export interface ReleaseGateReport {
generatedAt: string;
status: 'pass' | 'warn' | 'block';
summary: string;
blockingIssues: string[];
simulationCoverage: number;
}
export interface SaveMigrationManifest {
version: string;
requiredTransforms: string[];
backwardCompatible: boolean;
}
export interface NarrativeCodexEntry {
id: string;
title: string;
summary: string;
category: 'fact' | 'document' | 'scene' | 'thread' | 'companion' | 'ending';
relatedIds: string[];
}
export interface NarrativeCodexSection {
id: string;
title: string;
entries: NarrativeCodexEntry[];
}
export interface CompanionStanceProfile {
trust: number;
warmth: number;
ideologicalFit: number;
fearOrGuard: number;
loyalty: number;
currentConflictTag?: string | null;
recentApprovals: string[];
recentDisapprovals: string[];
}
export interface StoryEngineMemoryState {
discoveredFactIds: string[];
inferredFactIds?: string[];
activeThreadIds: string[];
resolvedScarIds: string[];
recentCarrierIds: string[];
openedSceneChapterIds?: string[];
currentSceneActState?: SceneActRuntimeState | null;
recentSignalIds?: string[];
recentCompanionReactions?: CompanionReactionRecord[];
currentChapter?: ChapterState | null;
currentJourneyBeatId?: string | null;
currentJourneyBeat?: JourneyBeat | null;
companionArcStates?: CompanionArcState[];
worldMutations?: WorldMutation[];
chronicle?: ChronicleEntry[];
factionTensionStates?: FactionTensionState[];
currentCampEvent?: CampEvent | null;
currentSetpieceDirective?: SetpieceDirective | null;
continueGameDigest?: string | null;
campaignState?: CampaignState | null;
actState?: ActState | null;
consequenceLedger?: ConsequenceRecord[];
companionResolutions?: CompanionResolution[];
endingState?: EndingState | null;
authorialConstraintPack?: AuthorialConstraintPack | null;
branchBudgetStatus?: BranchBudgetStatus | null;
narrativeQaReport?: NarrativeQaReport | null;
narrativeCodex?: NarrativeCodexSection[];
playerStyleProfile?: PlayerStyleProfile | null;
simulationRunResults?: SimulationRunResult[];
releaseGateReport?: ReleaseGateReport | null;
saveMigrationManifest?: SaveMigrationManifest | null;
}