86 lines
2.5 KiB
TypeScript
86 lines
2.5 KiB
TypeScript
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;
|
|
}
|