Refine NPC interactions and runtime item generation

This commit is contained in:
2026-04-05 17:13:07 +08:00
parent c49c64896a
commit 89cecda7da
58 changed files with 4199 additions and 1562 deletions

View File

@@ -10,6 +10,7 @@ import {
getCharacterNpcSceneIds,
PRESET_CHARACTERS,
} from './characterPresets';
import { resolveCustomWorldNpcMonsterPreset } from './customWorldNpcMonsters';
import { getRuntimeCustomWorldProfile, resolveRuleWorldType } from './customWorldRuntime';
import { getMonsterPresetById } from './hostileNpcPresets';
import sceneNpcOverridesJson from './sceneNpcOverrides.json';
@@ -191,12 +192,32 @@ export function buildEncounterFromSceneNpc(
};
}
function buildCustomSceneNpc(npc: CustomWorldProfile['storyNpcs'][number], profile: CustomWorldProfile): SceneNpc {
const attributeProfile = npc.attributeProfile
function buildCustomSceneNpc(
npc: CustomWorldProfile['storyNpcs'][number],
profile: CustomWorldProfile,
anchorWorldType: WorldType,
): SceneNpc {
const monsterPreset =
npc.initialAffinity < 0
? resolveCustomWorldNpcMonsterPreset(npc, anchorWorldType)
: null;
const hostile = npc.initialAffinity < 0 || Boolean(monsterPreset);
const attributeProfile = monsterPreset?.attributeProfile
?? npc.attributeProfile
?? buildRoleAttributeProfileFromLegacyData({
entityId: npc.id,
schema: resolveAttributeSchema(WorldType.CUSTOM, profile),
textBlocks: [npc.role, npc.description, npc.motivation],
textBlocks: [
npc.title,
npc.role,
npc.description,
npc.backstory,
npc.personality,
npc.motivation,
npc.combatStyle,
...npc.relationshipHooks,
...npc.tags,
],
}).profile;
return {
@@ -206,8 +227,14 @@ function buildCustomSceneNpc(npc: CustomWorldProfile['storyNpcs'][number], profi
avatar: npc.name.slice(0, 1) || '?',
description: `${npc.description} 动机:${npc.motivation}`,
gender: inferCustomNpcGender(npc.id, npc.name),
recruitable: true,
functions: ['trade', 'fight', 'spar', 'help', 'chat', 'recruit', 'gift'],
monsterPresetId: monsterPreset?.id,
hostileNpcPresetId: monsterPreset?.id,
initialAffinity: npc.initialAffinity,
hostile,
recruitable: !hostile,
functions: hostile
? ['fight']
: ['trade', 'fight', 'spar', 'help', 'chat', 'recruit', 'gift'],
attributeProfile,
};
}
@@ -238,7 +265,9 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
: null;
}).filter(Boolean) as SceneNpc[];
const customStoryNpcs = profile.storyNpcs.map(npc => buildCustomSceneNpc(npc, profile));
const customStoryNpcs = profile.storyNpcs.map(npc =>
buildCustomSceneNpc(npc, profile, anchorWorldType),
);
const chunkSize = Math.max(4, Math.ceil(customStoryNpcs.length / Math.max(1, profile.landmarks.length)));
const customScenes: ScenePreset[] = [
{
@@ -394,7 +423,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-13', 'monster-08'],
connectedSceneIds: ['wuxia-mountain-gate', 'wuxia-mist-woods', 'wuxia-ferry-bridge'],
forwardSceneId: 'wuxia-mountain-gate',
treasureHints: ['???????', '??????'],
treasureHints: ['竹根旁半埋的刀鞘', '倒竹间的旧药囊'],
extraNpcs: [
makeNpc('wuxia-npc-bamboo-woodcutter', '樵夫老周', '樵夫', '樵', '常在竹海边缘砍柴,对附近路数和兽踪了如指掌。'),
],
@@ -407,7 +436,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-04', 'monster-06'],
connectedSceneIds: ['wuxia-temple-forecourt', 'wuxia-border-camp', 'wuxia-bamboo-road'],
forwardSceneId: 'wuxia-temple-forecourt',
treasureHints: ['瑁傜紳涓殑閾滃專', '鐭崇嫯搴曞骇鏃侀仐钀界殑浠ょ墝'],
treasureHints: ['裂缝里的铜钥', '石狮座下遗落的令牌'],
extraNpcs: [
makeNpc('wuxia-npc-gate-disciple', '守山弟子', '门派弟子', '守', '一直盯着石阶尽头的动静,像在等某位重要来客。'),
],
@@ -420,7 +449,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-11', 'monster-07'],
connectedSceneIds: ['wuxia-ferry-bridge', 'wuxia-palace-court', 'wuxia-ruined-village'],
forwardSceneId: 'wuxia-ferry-bridge',
treasureHints: ['???????', '????????'],
treasureHints: ['灯檐下浸湿的布包', '排水沟边翻起的账册残页'],
extraNpcs: [
makeNpc('wuxia-npc-night-vendor', '夜灯摊主', '摊主', '灯', '深夜仍在街口守着灯摊,见过太多不该见的人。'),
],
@@ -433,7 +462,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-03', 'monster-07'],
connectedSceneIds: ['wuxia-mist-woods', 'wuxia-rain-street', 'wuxia-border-camp'],
forwardSceneId: 'wuxia-border-camp',
treasureHints: ['????????', '????????'],
treasureHints: ['断墙后压着的木匣', '枯井边散落的旧簪'],
extraNpcs: [
makeNpc('wuxia-npc-village-remnant', '守村妇人', '遗民', '民', '不肯离开这片断垣,似乎还在等某个人归来。'),
],
@@ -446,7 +475,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-04', 'monster-11'],
connectedSceneIds: ['wuxia-rain-street', 'wuxia-bamboo-road', 'wuxia-border-camp'],
forwardSceneId: 'wuxia-border-camp',
treasureHints: ['???????', '????????'],
treasureHints: ['桥柱缝里的油纸包', '渡船板下藏着的旧钱袋'],
extraNpcs: [
makeNpc('wuxia-npc-ferryman', '老渡工', '渡工', '渡', '常年摆渡,看人看路都很准,有些话只肯对识货的人说。'),
],
@@ -459,7 +488,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-08', 'monster-13', 'monster-07'],
connectedSceneIds: ['wuxia-bamboo-road', 'wuxia-ruined-village', 'wuxia-temple-forecourt'],
forwardSceneId: 'wuxia-ruined-village',
treasureHints: ['缂犲湪鏍戞牴涓婄殑閿﹀泭', '琚浘姘存场婀跨殑鍦板浘娈嬮〉'],
treasureHints: ['缠在树根上的锦囊', '被雾水泡湿的地图残页'],
extraNpcs: [
makeNpc('wuxia-npc-hunter', '追迹猎户', '猎户', '猎', '脚边总带着兽夹和草药,对林中异动非常敏感。'),
],
@@ -472,7 +501,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-18', 'monster-11'],
connectedSceneIds: ['wuxia-ferry-bridge', 'wuxia-mountain-gate', 'wuxia-ruined-village'],
forwardSceneId: 'wuxia-rain-street',
treasureHints: ['????????', '?????????'],
treasureHints: ['废营帐里的箭囊', '火盆旁埋着的军需匣'],
extraNpcs: [
makeNpc('wuxia-npc-quartermaster', '军需官', '营地官', '营', '管着兵器和粮草,对各路来客始终保持戒心。'),
],
@@ -485,7 +514,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-03', 'monster-06'],
connectedSceneIds: ['wuxia-temple-forecourt', 'wuxia-mine-depths', 'wuxia-palace-court'],
forwardSceneId: 'wuxia-mine-depths',
treasureHints: ['???????', '????????'],
treasureHints: ['砖缝里的陪葬铜匣', '石灯底座后的残卷'],
extraNpcs: [
makeNpc('wuxia-npc-tomb-scholar', '探碑书生', '学者', '碑', '抱着拓本在地宫里转来转去,似乎在找某段缺失铭文。'),
],
@@ -498,7 +527,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-04', 'monster-03'],
connectedSceneIds: ['wuxia-mountain-gate', 'wuxia-crypt-passage', 'wuxia-mist-woods'],
forwardSceneId: 'wuxia-crypt-passage',
treasureHints: ['????????', '???????'],
treasureHints: ['香炉灰里的玉珠', '石灯下压着的签牌'],
extraNpcs: [
makeNpc('wuxia-npc-temple-host', '守庙僧', '僧人', '僧', '白日扫院夜里守灯,似乎知道地宫里曾封过什么。'),
],
@@ -511,7 +540,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-06', 'monster-18'],
connectedSceneIds: ['wuxia-crypt-passage', 'wuxia-forge-works', 'wuxia-border-camp'],
forwardSceneId: 'wuxia-forge-works',
treasureHints: ['鐭胯溅澶瑰眰閲岀殑閾剁洅', '鍩嬪湪鐭挎福涓殑绮鹃搧'],
treasureHints: ['矿车夹层里的银匣', '埋在碎矿中的精铁'],
extraNpcs: [
makeNpc('wuxia-npc-miner', '老矿头', '矿工', '矿', '靠耳朵分辨坑道深处的回响,比谁都先知道危险会从哪边来。'),
],
@@ -524,7 +553,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-18', 'monster-04'],
connectedSceneIds: ['wuxia-mine-depths', 'wuxia-palace-court', 'wuxia-border-camp'],
forwardSceneId: 'wuxia-palace-court',
treasureHints: ['????????', '?????????'],
treasureHints: ['淬火池旁的铁匣', '风箱后压着的旧兵谱'],
extraNpcs: [
makeNpc('wuxia-npc-blacksmith', '老铸匠', '铸匠', '铸', '看一眼兵器缺口就知道你刚从什么地方杀出来。'),
],
@@ -537,7 +566,7 @@ const WUXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-11', 'monster-13'],
connectedSceneIds: ['wuxia-forge-works', 'wuxia-rain-street', 'wuxia-crypt-passage'],
forwardSceneId: 'wuxia-rain-street',
treasureHints: ['????????', '?????????'],
treasureHints: ['回廊暗格里的香囊', '花圃石座下的旧金牌'],
extraNpcs: [
makeNpc('wuxia-npc-maid', '旧宫侍女', '宫人', '侍', '嘴上说得少,却总知道哪条回廊最近不该过去。'),
],
@@ -553,7 +582,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-02', 'monster-16'],
connectedSceneIds: ['xianxia-floating-isle', 'xianxia-celestial-corridor', 'xianxia-star-vessel'],
forwardSceneId: 'xianxia-celestial-corridor',
treasureHints: ['?????????', '????????'],
treasureHints: ['云阶尽头的灵符匣', '门阙阴影里的玉牌'],
extraNpcs: [
makeNpc('xianxia-npc-gate-attendant', '守门灵官', '门官', '门', '站在门阙侧旁观来者,像在等一份迟迟未到的回报。'),
],
@@ -566,7 +595,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-12', 'monster-16'],
connectedSceneIds: ['xianxia-cloud-gate', 'xianxia-waterfall-cliff', 'xianxia-moon-lake'],
forwardSceneId: 'xianxia-moon-lake',
treasureHints: ['??????????', '???????????'],
treasureHints: ['浮岛边缘的灵羽匣', '云藤下悬着的小玉瓶'],
extraNpcs: [
makeNpc('xianxia-npc-cloud-hermit', '云栖散修', '散修', '云', '常坐在浮岛边缘打坐,对天风和禁制的变化很敏感。'),
],
@@ -579,7 +608,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-02', 'monster-14'],
connectedSceneIds: ['xianxia-cloud-gate', 'xianxia-thunder-altar', 'xianxia-ancient-ruins'],
forwardSceneId: 'xianxia-thunder-altar',
treasureHints: ['??????????', '?????????'],
treasureHints: ['廊柱暗槽里的玉简', '风铃后藏着的封签'],
extraNpcs: [
makeNpc('xianxia-npc-palace-page', '抄经侍者', '侍者', '卷', '抱着卷册在廊下快步穿行,像是在躲某种会翻页的东西。'),
],
@@ -592,7 +621,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-15', 'monster-05'],
connectedSceneIds: ['xianxia-jade-cavern', 'xianxia-sacred-tree', 'xianxia-moon-lake'],
forwardSceneId: 'xianxia-sacred-tree',
treasureHints: ['????????', '?????????'],
treasureHints: ['药圃深处的灵壶', '花架下压着的采录册'],
extraNpcs: [
makeNpc('xianxia-npc-herbal-keeper', '药圃执事', '药师', '药', '守着花圃记录灵植开谢,也清楚哪些地方最近长出了怪东西。'),
],
@@ -605,7 +634,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-10', 'monster-12', 'monster-20'],
connectedSceneIds: ['xianxia-herb-garden', 'xianxia-moon-lake', 'xianxia-ancient-ruins'],
forwardSceneId: 'xianxia-moon-lake',
treasureHints: ['瀵掔帀瑁傞殭閲岀殑鐏甸珦', '鍐伴潰涓嬮棯鐫€鍏夌殑璐濆專'],
treasureHints: ['寒玉裂隙里的灵髓', '冰面下闪着光的贝匣'],
extraNpcs: [
makeNpc('xianxia-npc-cold-scholar', '寒洞客', '访客', '玉', '在洞天里采样寒玉碎屑,像在研究更深处的封禁。'),
],
@@ -618,7 +647,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-14', 'monster-10'],
connectedSceneIds: ['xianxia-thunder-altar', 'xianxia-waterfall-cliff', 'xianxia-jade-cavern'],
forwardSceneId: 'xianxia-waterfall-cliff',
treasureHints: ['????????', '?????????'],
treasureHints: ['熔岩边冷却的矿匣', '焦岩后藏着的火纹石'],
extraNpcs: [
makeNpc('xianxia-npc-fire-forger', '熔炉匠修', '炼匠', '炉', '在热浪里锻器不歇,见惯灵火失控的后果。'),
],
@@ -631,7 +660,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-02', 'monster-16'],
connectedSceneIds: ['xianxia-celestial-corridor', 'xianxia-molten-realm', 'xianxia-star-vessel'],
forwardSceneId: 'xianxia-star-vessel',
treasureHints: ['????????', '?????????'],
treasureHints: ['祭坛角落的雷纹匣', '断碑背面的青铜铃'],
extraNpcs: [
makeNpc('xianxia-npc-thunder-keeper', '祭雷守使', '守使', '雷', '总站在祭坛边缘看天,像在确认下一道雷会落到哪里。'),
],
@@ -644,7 +673,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-12', 'monster-16', 'monster-02'],
connectedSceneIds: ['xianxia-thunder-altar', 'xianxia-cloud-gate', 'xianxia-floating-isle'],
forwardSceneId: 'xianxia-floating-isle',
treasureHints: ['????????', '??????????'],
treasureHints: ['舵台后的星图匣', '甲板缝里卡着的灵罗盘'],
extraNpcs: [
makeNpc('xianxia-npc-helmsman', '星舟舵手', '舵手', '舟', '守着老旧星舟的航线图,对高空中的异动异常敏感。'),
],
@@ -657,7 +686,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-20', 'monster-14', 'monster-15'],
connectedSceneIds: ['xianxia-jade-cavern', 'xianxia-floating-isle', 'xianxia-herb-garden'],
forwardSceneId: 'xianxia-herb-garden',
treasureHints: ['婀栧哺杈规紓鏉ョ殑鐜夌洅', '鏈堣壊涓嬭嫢闅愯嫢鐜扮殑閾堕搩'],
treasureHints: ['湖岸边漂来的玉匣', '月色下若隐若现的银铃'],
extraNpcs: [
makeNpc('xianxia-npc-lake-watcher', '湖畔琴师', '琴师', '琴', '常在月湖边抚琴,像在等某段旋律把什么引出来。'),
],
@@ -670,7 +699,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-02', 'monster-05', 'monster-12'],
connectedSceneIds: ['xianxia-celestial-corridor', 'xianxia-jade-cavern', 'xianxia-sacred-tree'],
forwardSceneId: 'xianxia-sacred-tree',
treasureHints: ['娈嬮樀涓績鍩嬬潃鐨勭帀绠€', '鍊掑纰戞煴閲岀殑灏忓專'],
treasureHints: ['残阵中心埋着的玉简', '倒塌碑柱里的小匣'],
extraNpcs: [
makeNpc('xianxia-npc-ruin-scholar', '寻迹司录', '司录', '录', '拿着一卷旧图在断墙间比对,像快要找到重要坐标。'),
],
@@ -683,7 +712,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-15', 'monster-05'],
connectedSceneIds: ['xianxia-herb-garden', 'xianxia-ancient-ruins', 'xianxia-waterfall-cliff'],
forwardSceneId: 'xianxia-waterfall-cliff',
treasureHints: ['?????????', '??????????'],
treasureHints: ['盘根间的木纹匣', '树洞深处垂着的灵种'],
extraNpcs: [
makeNpc('xianxia-npc-tree-ward', '守木灵侍', '灵侍', '木', '一直绕着古树巡看,像是担心有人惊动树心。'),
],
@@ -696,7 +725,7 @@ const XIANXIA_SCENES: SceneTemplate[] = [
monsterIds: ['monster-12', 'monster-20', 'monster-16'],
connectedSceneIds: ['xianxia-sacred-tree', 'xianxia-molten-realm', 'xianxia-floating-isle'],
forwardSceneId: 'xianxia-cloud-gate',
treasureHints: ['鐎戝箷鍚庨棯鐫€鍏夌殑鐭冲專', '宕栬竟钘や笂鎸傜潃鐨勬姢韬搩'],
treasureHints: ['瀑幕后闪着光的石匣', '崖边藤上挂着的护身铃'],
extraNpcs: [
makeNpc('xianxia-npc-cliff-scout', '崖巡女修', '巡修', '崖', '长期在飞瀑边巡看,脚步轻得像从不曾碰到过石面。'),
],