Rework story engine flow and reorganize project docs
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
85
src/services/storyEngine/playerStyleProfiler.ts
Normal file
85
src/services/storyEngine/playerStyleProfiler.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user