160
src/types/attributes.ts
Normal file
160
src/types/attributes.ts
Normal 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
23
src/types/build.ts
Normal 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;
|
||||
}
|
||||
155
src/types/characters.ts
Normal file
155
src/types/characters.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
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';
|
||||
|
||||
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;
|
||||
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>>;
|
||||
}
|
||||
93
src/types/core.ts
Normal file
93
src/types/core.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
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;
|
||||
}
|
||||
|
||||
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_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';
|
||||
100
src/types/customWorld.ts
Normal file
100
src/types/customWorld.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import type {
|
||||
ItemAttributeResonance,
|
||||
RoleAttributeProfile,
|
||||
WorldAttributeSchema,
|
||||
} from './attributes';
|
||||
import {
|
||||
type EquipmentSlotId,
|
||||
type ItemRarity,
|
||||
type WorldTemplateType,
|
||||
} from './core';
|
||||
import type {ItemStatProfile, ItemUseProfile} from './items';
|
||||
|
||||
export interface CustomWorldPlayableNpc {
|
||||
id: string;
|
||||
name: string;
|
||||
title: string;
|
||||
description: string;
|
||||
backstory: string;
|
||||
personality: string;
|
||||
combatStyle: string;
|
||||
tags: string[];
|
||||
templateCharacterId?: string;
|
||||
attributeProfile?: RoleAttributeProfile;
|
||||
}
|
||||
|
||||
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 CustomWorldNpc {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
description: string;
|
||||
motivation: string;
|
||||
relationshipHooks: string[];
|
||||
imageSrc?: string;
|
||||
visual?: CustomWorldNpcVisual;
|
||||
attributeProfile?: RoleAttributeProfile;
|
||||
}
|
||||
|
||||
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 interface CustomWorldLandmark {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
dangerLevel: string;
|
||||
imageSrc?: string;
|
||||
}
|
||||
|
||||
export interface CustomWorldProfile {
|
||||
id: string;
|
||||
settingText: string;
|
||||
name: string;
|
||||
subtitle: string;
|
||||
summary: string;
|
||||
tone: string;
|
||||
playerGoal: string;
|
||||
templateWorldType: WorldTemplateType;
|
||||
attributeSchema: WorldAttributeSchema;
|
||||
playableNpcs: CustomWorldPlayableNpc[];
|
||||
storyNpcs: CustomWorldNpc[];
|
||||
items: CustomWorldItem[];
|
||||
landmarks: CustomWorldLandmark[];
|
||||
}
|
||||
82
src/types/game.ts
Normal file
82
src/types/game.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
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';
|
||||
|
||||
export interface GameRuntimeStats {
|
||||
playTimeMs: number;
|
||||
lastPlayTickAt: string | null;
|
||||
hostileNpcsDefeated: number;
|
||||
questsAccepted: number;
|
||||
itemsUsed: number;
|
||||
scenesTraveled: number;
|
||||
}
|
||||
|
||||
export interface GameState {
|
||||
worldType: WorldType | null;
|
||||
customWorldProfile: CustomWorldProfile | null;
|
||||
playerCharacter: Character | null;
|
||||
runtimeStats: GameRuntimeStats;
|
||||
currentScene: string;
|
||||
storyHistory: StoryMoment[];
|
||||
characterChats: Record<string, CharacterChatRecord>;
|
||||
ambientIdleMode?: 'observe_signs';
|
||||
lastObserveSignsSceneId?: string | null;
|
||||
lastObserveSignsReport?: string | null;
|
||||
animationState: AnimationState;
|
||||
currentEncounter: Encounter | null;
|
||||
npcInteractionActive: boolean;
|
||||
currentScenePreset: ScenePresetInfo | null;
|
||||
sceneMonsters: SceneHostileNpc[];
|
||||
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
110
src/types/items.ts
Normal 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
1
src/types/navigation.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type BottomTab = 'character' | 'adventure' | 'inventory';
|
||||
108
src/types/runtimeItem.ts
Normal file
108
src/types/runtimeItem.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import type {WorldType} from './core';
|
||||
import type {CustomWorldProfile} from './customWorld';
|
||||
import type {InventoryItem} from './items';
|
||||
import type {Encounter, NpcPersistentState, ScenePresetInfo} from './scene';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
relatedScene: Pick<ScenePresetInfo, 'id' | 'name' | 'description' | 'treasureHints'> | null;
|
||||
recentStorySummary: string;
|
||||
recentActions: string[];
|
||||
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;
|
||||
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';
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
190
src/types/scene.ts
Normal file
190
src/types/scene.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
import type {
|
||||
RoleActionDefinition,
|
||||
RoleAttributeProfile,
|
||||
RoleRelationState,
|
||||
} from './attributes';
|
||||
import type {Character} from './characters';
|
||||
import {
|
||||
AnimationState,
|
||||
type CharacterGender,
|
||||
type CombatActionMode,
|
||||
type CombatDelivery,
|
||||
type FacingDirection,
|
||||
type HostileNpcRenderAnimation,
|
||||
type NpcFunctionType,
|
||||
} from './core';
|
||||
import type {InventoryItem} from './items';
|
||||
|
||||
export interface NpcPersistentState {
|
||||
affinity: number;
|
||||
relationState?: RoleRelationState;
|
||||
helpUsed: boolean;
|
||||
chattedCount: number;
|
||||
giftsGiven: number;
|
||||
inventory: InventoryItem[];
|
||||
recruited: boolean;
|
||||
revealedFacts?: string[];
|
||||
knownAttributeRumors?: string[];
|
||||
firstMeaningfulContactResolved?: boolean;
|
||||
seenBackstoryChapterIds?: string[];
|
||||
}
|
||||
|
||||
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;
|
||||
hostileNpcPresetId?: string;
|
||||
monsterPresetId?: string;
|
||||
initialAffinity?: number;
|
||||
hostile?: boolean;
|
||||
attributeProfile?: RoleAttributeProfile;
|
||||
}
|
||||
|
||||
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[];
|
||||
}
|
||||
|
||||
export type SceneMonster = SceneHostileNpc;
|
||||
|
||||
export interface SceneNpc {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
avatar: string;
|
||||
role: string;
|
||||
gender?: CharacterGender;
|
||||
characterId?: string;
|
||||
hostileNpcPresetId?: string;
|
||||
monsterPresetId?: string;
|
||||
initialAffinity?: number;
|
||||
hostile?: boolean;
|
||||
functions?: NpcFunctionType[];
|
||||
recruitable?: boolean;
|
||||
attributeProfile?: RoleAttributeProfile;
|
||||
}
|
||||
|
||||
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[];
|
||||
hostileNpcIds?: string[];
|
||||
monsterIds?: string[];
|
||||
npcs?: SceneNpc[];
|
||||
treasureHints?: string[];
|
||||
}
|
||||
127
src/types/story.ts
Normal file
127
src/types/story.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
type NpcInteractionAction,
|
||||
type QuestObjectiveKind,
|
||||
type QuestStatus,
|
||||
type TreasureInteractionAction,
|
||||
} from './core';
|
||||
import type {InventoryItem} from './items';
|
||||
import type {SceneDirective} from './scene';
|
||||
|
||||
export interface StoryOption {
|
||||
functionId: string;
|
||||
actionText: string;
|
||||
text?: string;
|
||||
detailText?: string;
|
||||
priority?: number;
|
||||
visuals: SceneDirective;
|
||||
skillProbabilities?: Record<string, number>;
|
||||
interaction?: StoryOptionInteraction;
|
||||
}
|
||||
|
||||
export interface QuestReward {
|
||||
affinityBonus: number;
|
||||
currency: number;
|
||||
items: InventoryItem[];
|
||||
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;
|
||||
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[];
|
||||
}
|
||||
|
||||
export interface StoryDialogueTurn {
|
||||
speaker: 'player' | 'npc' | 'companion';
|
||||
speakerName?: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
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[];
|
||||
historyRole?: StoryHistoryRole;
|
||||
}
|
||||
|
||||
export type StoryOptionInteraction =
|
||||
| {
|
||||
kind: 'npc';
|
||||
npcId: string;
|
||||
action: NpcInteractionAction;
|
||||
questId?: string;
|
||||
}
|
||||
| {
|
||||
kind: 'treasure';
|
||||
action: TreasureInteractionAction;
|
||||
};
|
||||
Reference in New Issue
Block a user