import type { GameState, PlayerStyleProfile, StoryOption, } from '../../types'; function clampWeight(value: number) { return Math.max(0, Math.min(100, Math.round(value))); } function resolveDominantStyle(weights: PlayerStyleProfile['preferenceWeights']) { const entries = Object.entries(weights) as Array< [keyof PlayerStyleProfile['preferenceWeights'], number] >; entries.sort((a, b) => b[1] - a[1]); const top = entries[0]?.[0] ?? 'story'; if (top === 'story') return 'story_first'; if (top === 'exploration') return 'explorer'; if (top === 'combat') return 'combat_driver'; if (top === 'companion') return 'companion_bond'; return 'collector'; } export function buildPlayerStyleProfile(state: GameState) { const existing = state.storyEngineMemory?.playerStyleProfile; if (existing) { return existing; } const weights = { story: 45, exploration: 40, combat: 35, companion: 35, collection: 30, }; return { id: 'player-style:default', preferenceWeights: weights, dominantStyle: resolveDominantStyle(weights), } satisfies PlayerStyleProfile; } export function updatePlayerStyleProfileFromAction(params: { current: PlayerStyleProfile | null | undefined; actionText: string; option?: StoryOption | null; }) { const current = params.current ?? ({ id: 'player-style:default', preferenceWeights: { story: 45, exploration: 40, combat: 35, companion: 35, collection: 30, }, dominantStyle: 'story_first', } satisfies PlayerStyleProfile); const nextWeights = { ...current.preferenceWeights }; const text = `${params.actionText} ${params.option?.functionId ?? ''}`; if (/聊|问|私聊|同伴|营地/u.test(text)) nextWeights.companion += 4; if (/探|观察|前进|调查|场景|线索/u.test(text)) nextWeights.exploration += 4; if (/战|攻击|切磋|压制|收割/u.test(text)) nextWeights.combat += 4; if (/文书|证据|残痕|任务|剧情/u.test(text)) nextWeights.story += 4; if (/拿|获得|收集|宝藏|拾取/u.test(text)) nextWeights.collection += 4; const normalizedWeights = { story: clampWeight(nextWeights.story), exploration: clampWeight(nextWeights.exploration), combat: clampWeight(nextWeights.combat), companion: clampWeight(nextWeights.companion), collection: clampWeight(nextWeights.collection), }; return { ...current, preferenceWeights: normalizedWeights, dominantStyle: resolveDominantStyle(normalizedWeights), } satisfies PlayerStyleProfile; }