Implement scene-based chapter quest progression
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { resolveCustomWorldCampScene } from '../services/customWorldCamp';
|
||||
import { buildCustomCampSceneName } from '../services/customWorldPresentation';
|
||||
import { resolveCustomWorldAnchorWorldType } from '../services/customWorldTheme';
|
||||
import {
|
||||
buildFallbackActorNarrativeProfile,
|
||||
normalizeActorNarrativeProfile,
|
||||
@@ -26,7 +26,11 @@ import {
|
||||
} from './characterPresets';
|
||||
import { resolveCustomWorldNpcMonsterPreset } from './customWorldNpcMonsters';
|
||||
import { getRuntimeCustomWorldProfile, resolveRuleWorldType } from './customWorldRuntime';
|
||||
import { getMonsterPresetById } from './hostileNpcPresets';
|
||||
import {
|
||||
resolveCustomWorldCampSceneImage,
|
||||
resolveCustomWorldLandmarkImageMap,
|
||||
} from './customWorldVisuals';
|
||||
import { getMonsterPresetById, getMonsterPresetsByWorld } from './hostileNpcPresets';
|
||||
import sceneNpcOverridesJson from './sceneNpcOverrides.json';
|
||||
import sceneOverridesJson from './sceneOverrides.json';
|
||||
|
||||
@@ -124,18 +128,6 @@ function collectWorldImagePool(worldType: WorldType, requiredCount: number) {
|
||||
return refs;
|
||||
}
|
||||
|
||||
function collectAllImagePool() {
|
||||
const refs: string[] = [];
|
||||
|
||||
for (const pack of PACKS) {
|
||||
for (let imageNumber = 1; imageNumber <= pack.count; imageNumber += 1) {
|
||||
refs.push(buildImagePath(pack.packName, imageNumber));
|
||||
}
|
||||
}
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
function uniqueStrings(values: string[]) {
|
||||
return [...new Set(values.filter(Boolean))];
|
||||
}
|
||||
@@ -305,7 +297,6 @@ export function buildEncounterFromSceneNpc(
|
||||
function buildCustomSceneNpc(
|
||||
npc: CustomWorldProfile['storyNpcs'][number],
|
||||
profile: CustomWorldProfile,
|
||||
anchorWorldType: WorldType,
|
||||
): SceneNpc {
|
||||
const themePack = profile.themePack ?? buildThemePackFromWorldProfile(profile);
|
||||
const storyGraph =
|
||||
@@ -316,7 +307,7 @@ function buildCustomSceneNpc(
|
||||
);
|
||||
const monsterPreset =
|
||||
npc.initialAffinity < 0
|
||||
? resolveCustomWorldNpcMonsterPreset(npc, anchorWorldType)
|
||||
? resolveCustomWorldNpcMonsterPreset(npc)
|
||||
: null;
|
||||
const hostile = npc.initialAffinity < 0 || Boolean(monsterPreset);
|
||||
const attributeProfile = monsterPreset?.attributeProfile
|
||||
@@ -339,6 +330,7 @@ function buildCustomSceneNpc(
|
||||
|
||||
return {
|
||||
id: npc.id,
|
||||
characterId: npc.id,
|
||||
name: npc.name,
|
||||
title: npc.title,
|
||||
role: npc.role,
|
||||
@@ -384,11 +376,10 @@ function buildCustomSceneId(kind: 'camp' | 'landmark', index = 0) {
|
||||
}
|
||||
|
||||
function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
const allImages = collectAllImagePool();
|
||||
const imageOffset = hashText(profile.id || profile.name) % Math.max(1, allImages.length);
|
||||
const anchorWorldType = resolveCustomWorldAnchorWorldType(profile);
|
||||
const baseMonsterPool: string[] = getScenePresetsByWorld(anchorWorldType)
|
||||
.flatMap((scene: ScenePreset) => getSceneHostileNpcPresetIds(scene))
|
||||
const campSceneProfile = resolveCustomWorldCampScene(profile);
|
||||
const landmarkImageMap = resolveCustomWorldLandmarkImageMap(profile);
|
||||
const baseMonsterPool: string[] = getMonsterPresetsByWorld(WorldType.CUSTOM)
|
||||
.map((monster) => monster.id)
|
||||
.filter((monsterId: string, index: number, array: string[]) => array.indexOf(monsterId) === index);
|
||||
const fallbackMonsterIds: string[] = baseMonsterPool.length > 0 ? baseMonsterPool : [];
|
||||
const playableCharacters = buildCustomWorldPlayableCharacters(profile);
|
||||
@@ -423,16 +414,16 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
sceneId: landmarkSceneIds[index] ?? '',
|
||||
relativePosition:
|
||||
index === 0 ? 'forward' : index === 1 ? 'left' : 'right',
|
||||
summary: `从营地可直接通往${landmark.name}`,
|
||||
summary: `从${campSceneProfile.name}可直接通往${landmark.name}`,
|
||||
}))
|
||||
.filter((connection) => connection.sceneId) as SceneConnectionInfo[];
|
||||
const customScenes: ScenePreset[] = [
|
||||
{
|
||||
id: campSceneId,
|
||||
name: buildCustomCampSceneName(profile),
|
||||
description: `你在${profile.name}的临时营地整备行装。${profile.summary}`,
|
||||
description: campSceneProfile.description,
|
||||
worldType: WorldType.CUSTOM,
|
||||
imageSrc: profile.landmarks[0]?.imageSrc ?? allImages[imageOffset] ?? '',
|
||||
imageSrc: resolveCustomWorldCampSceneImage(profile),
|
||||
connectedSceneIds: campConnections.map((connection) => connection.sceneId),
|
||||
connections: campConnections,
|
||||
forwardSceneId: pickForwardSceneIdFromConnections(campConnections),
|
||||
@@ -452,7 +443,7 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
.map((npcId) => customStoryNpcById.get(npcId))
|
||||
.filter(Boolean)
|
||||
.map((npc) =>
|
||||
buildCustomSceneNpc(npc!, profile, anchorWorldType),
|
||||
buildCustomSceneNpc(npc!, profile),
|
||||
);
|
||||
if (sceneNpcs.length < 3) {
|
||||
profile.storyNpcs
|
||||
@@ -461,7 +452,7 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
)
|
||||
.slice(0, 3 - sceneNpcs.length)
|
||||
.forEach((npc) =>
|
||||
sceneNpcs.push(buildCustomSceneNpc(npc, profile, anchorWorldType)),
|
||||
sceneNpcs.push(buildCustomSceneNpc(npc, profile)),
|
||||
);
|
||||
}
|
||||
const landmarkConnections = landmark.connections
|
||||
@@ -489,7 +480,7 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
? ({
|
||||
sceneId: campSceneId,
|
||||
relativePosition: 'back',
|
||||
summary: '可回到临时营地整备',
|
||||
summary: `可回到${campSceneProfile.name}整备`,
|
||||
} satisfies SceneConnectionInfo)
|
||||
: null;
|
||||
const connections = [
|
||||
@@ -502,7 +493,7 @@ 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), anchorWorldType, monsterId))
|
||||
.map((monsterId: string) => buildHostileSceneNpc(buildCustomSceneId('landmark', index), WorldType.CUSTOM, monsterId))
|
||||
.filter(Boolean) as SceneNpc[];
|
||||
const combinedNpcs = [...sceneNpcs, ...hostileNpcs];
|
||||
|
||||
@@ -511,7 +502,7 @@ function buildCustomScenePresets(profile: CustomWorldProfile): ScenePreset[] {
|
||||
name: landmark.name,
|
||||
description: landmark.description,
|
||||
worldType: WorldType.CUSTOM,
|
||||
imageSrc: landmark.imageSrc ?? allImages[(imageOffset + index + 1) % Math.max(1, allImages.length)] ?? '',
|
||||
imageSrc: landmarkImageMap.get(landmark.id) ?? '',
|
||||
connectedSceneIds,
|
||||
connections,
|
||||
forwardSceneId: pickForwardSceneIdFromConnections(connections),
|
||||
@@ -1090,6 +1081,3 @@ export function buildSceneEntityCatalogText(worldType: WorldType, sceneId: strin
|
||||
`当前场景残痕:${residueText}`,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user