This commit is contained in:
132
src/services/customWorldCover.ts
Normal file
132
src/services/customWorldCover.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import type {
|
||||
CustomWorldCoverProfile,
|
||||
CustomWorldPlayableNpc,
|
||||
CustomWorldProfile,
|
||||
} from '../types';
|
||||
|
||||
export type CustomWorldCoverRenderMode = 'image' | 'scene_with_roles';
|
||||
|
||||
export type CustomWorldCoverPresentation = {
|
||||
imageSrc: string | null;
|
||||
renderMode: CustomWorldCoverRenderMode;
|
||||
characterImageSrcs: string[];
|
||||
sourceType: CustomWorldCoverProfile['sourceType'];
|
||||
};
|
||||
|
||||
function resolveOpeningSceneFirstActImageSrc(profile: CustomWorldProfile) {
|
||||
return profile.sceneChapterBlueprints?.[0]?.acts?.[0]?.backgroundImageSrc?.trim() || null;
|
||||
}
|
||||
|
||||
function resolveOpeningSceneImageSrc(profile: CustomWorldProfile) {
|
||||
// 默认封面优先取开局场景第一幕图,避免草稿页与作品库继续沿用旧的营地兜底策略。
|
||||
const firstActImageSrc = resolveOpeningSceneFirstActImageSrc(profile);
|
||||
if (firstActImageSrc) {
|
||||
return firstActImageSrc;
|
||||
}
|
||||
|
||||
const campImageSrc = profile.camp?.imageSrc?.trim() || '';
|
||||
if (campImageSrc) {
|
||||
return campImageSrc;
|
||||
}
|
||||
|
||||
return (
|
||||
profile.landmarks
|
||||
.map((landmark) => landmark.imageSrc?.trim() || '')
|
||||
.find(Boolean) || null
|
||||
);
|
||||
}
|
||||
|
||||
function resolvePlayableCoverImageSrc(role: CustomWorldPlayableNpc) {
|
||||
const explicitImageSrc = role.imageSrc?.trim() || '';
|
||||
if (explicitImageSrc) {
|
||||
return explicitImageSrc;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeCoverCharacterRoleIds(
|
||||
profile: Pick<CustomWorldProfile, 'playableNpcs'>,
|
||||
roleIds?: string[] | null,
|
||||
) {
|
||||
const availableIds = new Set(
|
||||
profile.playableNpcs.map((role) => role.id.trim()).filter(Boolean),
|
||||
);
|
||||
const selectedIds = Array.isArray(roleIds)
|
||||
? [
|
||||
...new Set(
|
||||
roleIds
|
||||
.map((roleId) => roleId.trim())
|
||||
.filter((roleId) => roleId && availableIds.has(roleId)),
|
||||
),
|
||||
].slice(0, 3)
|
||||
: [];
|
||||
|
||||
if (selectedIds.length > 0) {
|
||||
return selectedIds;
|
||||
}
|
||||
|
||||
return profile.playableNpcs
|
||||
.map((role) => role.id.trim())
|
||||
.filter(Boolean)
|
||||
.slice(0, 3);
|
||||
}
|
||||
|
||||
export function buildDefaultCustomWorldCoverProfile(
|
||||
profile: Pick<CustomWorldProfile, 'playableNpcs'>,
|
||||
): CustomWorldCoverProfile {
|
||||
return {
|
||||
sourceType: 'default',
|
||||
imageSrc: null,
|
||||
characterRoleIds: normalizeCoverCharacterRoleIds(profile),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveCustomWorldCoverPresentation(
|
||||
profile: CustomWorldProfile,
|
||||
): CustomWorldCoverPresentation {
|
||||
const cover = profile.cover;
|
||||
const sourceType =
|
||||
cover?.sourceType === 'uploaded' || cover?.sourceType === 'generated'
|
||||
? cover.sourceType
|
||||
: 'default';
|
||||
const explicitImageSrc = cover?.imageSrc?.trim() || '';
|
||||
|
||||
if (sourceType !== 'default' && explicitImageSrc) {
|
||||
return {
|
||||
imageSrc: explicitImageSrc,
|
||||
renderMode: 'image',
|
||||
characterImageSrcs: [],
|
||||
sourceType,
|
||||
};
|
||||
}
|
||||
|
||||
const openingSceneImageSrc = resolveOpeningSceneImageSrc(profile);
|
||||
const roleById = new Map(
|
||||
profile.playableNpcs.map((role) => [role.id.trim(), role] as const),
|
||||
);
|
||||
const characterImageSrcs = normalizeCoverCharacterRoleIds(
|
||||
profile,
|
||||
cover?.characterRoleIds,
|
||||
)
|
||||
.map((roleId) => roleById.get(roleId))
|
||||
.map((role) => (role ? resolvePlayableCoverImageSrc(role) : null))
|
||||
.filter((imageSrc): imageSrc is string => Boolean(imageSrc));
|
||||
const leadPlayableImageSrc =
|
||||
profile.playableNpcs
|
||||
.map((role) => resolvePlayableCoverImageSrc(role))
|
||||
.find(Boolean) || null;
|
||||
|
||||
return {
|
||||
imageSrc: openingSceneImageSrc || leadPlayableImageSrc,
|
||||
renderMode:
|
||||
openingSceneImageSrc && characterImageSrcs.length > 0
|
||||
? 'scene_with_roles'
|
||||
: 'image',
|
||||
characterImageSrcs:
|
||||
openingSceneImageSrc && characterImageSrcs.length > 0
|
||||
? characterImageSrcs
|
||||
: [],
|
||||
sourceType: 'default',
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user