1
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-20 15:45:14 +08:00
parent 8a7bd90458
commit 1c72066bab
73 changed files with 7814 additions and 1018 deletions

View File

@@ -26,6 +26,7 @@ import {
scoreAttributeFit,
} from './attributeResolver';
import {
getCharacterAdventureOpening,
getCharacterById,
getCharacterCombatStats,
getCharacterEquipment,
@@ -985,6 +986,76 @@ function getFirstContactRelationStance(npcState: NpcPersistentState) {
return npcState.relationState?.stance ?? buildRelationState(npcState.affinity).stance;
}
function ensureDialogueSentence(text: string | null | undefined) {
const normalized = text?.trim() ?? '';
if (!normalized) {
return '';
}
return /[!?]$/u.test(normalized) ? normalized : `${normalized}`;
}
export function buildNpcChatOpeningText(
encounter: Encounter,
npcState: NpcPersistentState,
worldType: WorldType | null,
recruitCharacterOverride?: Character | null,
) {
const recruitCharacter =
recruitCharacterOverride ?? resolveEncounterRecruitCharacter(encounter);
const opening = recruitCharacter
? getCharacterAdventureOpening(recruitCharacter, worldType)
: null;
const stance = getFirstContactRelationStance(npcState);
if (isNpcFirstMeaningfulContact(encounter, npcState)) {
const greeting =
stance === 'guarded' ? '先打个招呼。' : '先和你打个招呼。';
const surfaceHook = ensureDialogueSentence(opening?.surfaceHook);
const immediateConcern = ensureDialogueSentence(opening?.immediateConcern);
const guardedMotive = ensureDialogueSentence(opening?.guardedMotive);
const fallbackLine =
stance === 'bonded'
? '这一步我既然亲自来了,就说明眼前这件事得先和你对齐。'
: stance === 'cooperative'
? '我先来和你碰个头,眼下这局势最好别各说各话。'
: stance === 'neutral'
? '我会出现在这里不是没有缘由,不过咱们最好先把眼前情况看清。'
: '前面的动静不太对,我想先看看你会怎么开口。';
if (
encounter.specialBehavior === 'camp_companion'
|| encounter.specialBehavior === 'initial_companion'
) {
return [
greeting,
surfaceHook || immediateConcern || fallbackLine,
surfaceHook && immediateConcern && surfaceHook !== immediateConcern
? immediateConcern
: null,
guardedMotive,
]
.filter(Boolean)
.join('');
}
return [greeting, immediateConcern || surfaceHook || fallbackLine]
.filter(Boolean)
.join('');
}
switch (stance) {
case 'bonded':
return '又见面了。你想先从哪件事接着说?';
case 'cooperative':
return '你开口吧,我先听听你想聊哪一件。';
case 'neutral':
return '先说吧,你想从哪里问起?';
default:
return '说吧,你想先问什么?';
}
}
export function getNpcFirstContactTopics(
encounter: Encounter,
npcState: NpcPersistentState,

View File

@@ -169,10 +169,14 @@ export function getWorldAttributeSchema(
customWorldProfile?: CustomWorldProfile | null,
) {
if (worldType === WorldType.CUSTOM && customWorldProfile) {
return (
resolveCustomWorldRuleProfile(customWorldProfile)?.attributeSchema
?? customWorldProfile.attributeSchema
);
try {
return (
resolveCustomWorldRuleProfile(customWorldProfile)?.attributeSchema
?? customWorldProfile.attributeSchema
);
} catch {
return customWorldProfile.attributeSchema;
}
}
if (worldType === WorldType.XIANXIA) {