1
This commit is contained in:
@@ -381,6 +381,40 @@ function buildCustomSceneId(kind: 'camp' | 'landmark', index = 0) {
|
||||
return kind === 'camp' ? 'custom-scene-camp' : `custom-scene-landmark-${index + 1}`;
|
||||
}
|
||||
|
||||
function collectSceneActNpcIdsForScene(
|
||||
profile: CustomWorldProfile,
|
||||
sceneAliases: string[],
|
||||
) {
|
||||
const aliasSet = new Set(sceneAliases.map((alias) => alias.trim()).filter(Boolean));
|
||||
const npcIds: string[] = [];
|
||||
|
||||
const pushNpcId = (npcId: string | null | undefined) => {
|
||||
const normalizedNpcId = npcId?.trim() ?? '';
|
||||
if (normalizedNpcId && !npcIds.includes(normalizedNpcId)) {
|
||||
npcIds.push(normalizedNpcId);
|
||||
}
|
||||
};
|
||||
|
||||
profile.sceneChapterBlueprints?.forEach((chapter) => {
|
||||
const chapterSceneIds = [
|
||||
chapter.sceneId,
|
||||
...chapter.linkedLandmarkIds,
|
||||
...chapter.acts.map((act) => act.sceneId),
|
||||
].map((sceneId) => sceneId.trim()).filter(Boolean);
|
||||
if (!chapterSceneIds.some((sceneId) => aliasSet.has(sceneId))) {
|
||||
return;
|
||||
}
|
||||
|
||||
chapter.acts.forEach((act) => {
|
||||
pushNpcId(act.primaryNpcId);
|
||||
pushNpcId(act.oppositeNpcId);
|
||||
act.encounterNpcIds.forEach(pushNpcId);
|
||||
});
|
||||
});
|
||||
|
||||
return npcIds;
|
||||
}
|
||||
|
||||
function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
const campSceneProfile = resolveCustomWorldCampScene(profile);
|
||||
const landmarkImageMap = resolveCustomWorldLandmarkImageMap(profile);
|
||||
@@ -403,6 +437,37 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
const customStoryNpcById = new Map(
|
||||
profile.storyNpcs.map((npc) => [npc.id, npc]),
|
||||
);
|
||||
const buildCustomSceneNpcByRoleId = (roleId: string) => {
|
||||
const storyNpc = customStoryNpcById.get(roleId);
|
||||
if (storyNpc) {
|
||||
return buildCustomSceneNpc(storyNpc, profile);
|
||||
}
|
||||
|
||||
const playableNpc = profile.playableNpcs.find((npc) => npc.id === roleId);
|
||||
if (!playableNpc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return buildCharacterNpc(playableNpc.id, WorldType.CUSTOM, profile);
|
||||
};
|
||||
const pushUniqueSceneNpc = (sceneNpcs: SceneNpc[], npc: SceneNpc | null) => {
|
||||
if (!npc) {
|
||||
return;
|
||||
}
|
||||
|
||||
const candidateIds = [npc.id, npc.characterId].filter(Boolean);
|
||||
if (
|
||||
sceneNpcs.some((sceneNpc) =>
|
||||
[sceneNpc.id, sceneNpc.characterId]
|
||||
.filter(Boolean)
|
||||
.some((sceneNpcId) => candidateIds.includes(sceneNpcId)),
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
sceneNpcs.push(npc);
|
||||
};
|
||||
const campNpcs = playableCharacters.slice(1).map(character => {
|
||||
const npc = buildCharacterNpc(character.id, WorldType.CUSTOM, profile);
|
||||
return npc
|
||||
@@ -413,6 +478,12 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
}
|
||||
: null;
|
||||
}).filter(Boolean) as SceneNpc[];
|
||||
collectSceneActNpcIdsForScene(profile, [
|
||||
campSceneId,
|
||||
profile.camp?.id ?? '',
|
||||
]).forEach((npcId) =>
|
||||
pushUniqueSceneNpc(campNpcs, buildCustomSceneNpcByRoleId(npcId)),
|
||||
);
|
||||
|
||||
const campConnections = profile.landmarks
|
||||
.slice(0, 3)
|
||||
@@ -445,12 +516,20 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
npcs: campNpcs,
|
||||
},
|
||||
...profile.landmarks.map((landmark, index): ScenePreset => {
|
||||
const sceneNpcs = landmark.sceneNpcIds
|
||||
const runtimeSceneId = buildCustomSceneId('landmark', index);
|
||||
const sceneActNpcIds = collectSceneActNpcIdsForScene(profile, [
|
||||
landmark.id,
|
||||
runtimeSceneId,
|
||||
]);
|
||||
const sceneNpcs = [...sceneActNpcIds, ...landmark.sceneNpcIds]
|
||||
.map((npcId) => customStoryNpcById.get(npcId))
|
||||
.filter(Boolean)
|
||||
.map((npc) =>
|
||||
buildCustomSceneNpc(npc!, profile),
|
||||
);
|
||||
sceneActNpcIds.forEach((npcId) =>
|
||||
pushUniqueSceneNpc(sceneNpcs, buildCustomSceneNpcByRoleId(npcId)),
|
||||
);
|
||||
if (sceneNpcs.length < 3) {
|
||||
profile.storyNpcs
|
||||
.filter(
|
||||
@@ -499,12 +578,12 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
const monsterSliceStart = (index * 2) % Math.max(1, fallbackMonsterIds.length || 1);
|
||||
const seedMonsterIds: string[] = fallbackMonsterIds.slice(monsterSliceStart, monsterSliceStart + 2);
|
||||
const hostileNpcs = seedMonsterIds
|
||||
.map((monsterId: string) => buildHostileSceneNpc(buildCustomSceneId('landmark', index), WorldType.CUSTOM, monsterId))
|
||||
.map((monsterId: string) => buildHostileSceneNpc(runtimeSceneId, WorldType.CUSTOM, monsterId))
|
||||
.filter(Boolean) as SceneNpc[];
|
||||
const combinedNpcs = [...sceneNpcs, ...hostileNpcs];
|
||||
|
||||
return {
|
||||
id: buildCustomSceneId('landmark', index),
|
||||
id: runtimeSceneId,
|
||||
name: landmark.name,
|
||||
description: landmark.description,
|
||||
worldType: WorldType.CUSTOM,
|
||||
@@ -521,7 +600,7 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
landmark.narrativeResidues && landmark.narrativeResidues.length > 0
|
||||
? landmark.narrativeResidues
|
||||
: buildSceneNarrativeResidues({
|
||||
sceneId: buildCustomSceneId('landmark', index),
|
||||
sceneId: runtimeSceneId,
|
||||
sceneName: landmark.name,
|
||||
profile,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user