160 lines
4.4 KiB
TypeScript
160 lines
4.4 KiB
TypeScript
import type { RpgCreationPreviewSource } from '../../../packages/shared/src/contracts/rpgCreationPreview';
|
|
import type { CustomWorldProfile } from '../../types';
|
|
|
|
export type PlatformRpgAgentResultBlockerView = {
|
|
code?: string | null;
|
|
message: string;
|
|
};
|
|
|
|
export type PlatformRpgAgentResultPublishGateView = {
|
|
blockers: string[];
|
|
publishReady: boolean;
|
|
};
|
|
|
|
const AGENT_RESULT_STRUCTURAL_BLOCKER_CODES = new Set([
|
|
'publish_missing_world_hook',
|
|
'publish_missing_player_premise',
|
|
'publish_missing_core_conflict',
|
|
'publish_missing_main_chapter',
|
|
'publish_missing_first_act',
|
|
]);
|
|
|
|
function readProfileTextField(
|
|
profile: CustomWorldProfile | null,
|
|
paths: string[],
|
|
) {
|
|
for (const path of paths) {
|
|
let current: unknown = profile;
|
|
for (const segment of path.split('.')) {
|
|
if (!current || typeof current !== 'object') {
|
|
current = null;
|
|
break;
|
|
}
|
|
current = (current as Record<string, unknown>)[segment];
|
|
}
|
|
if (typeof current === 'string' && current.trim()) {
|
|
return current.trim();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function hasProfileTextArray(profile: CustomWorldProfile | null, key: string) {
|
|
const value = profile
|
|
? (profile as unknown as Record<string, unknown>)[key]
|
|
: null;
|
|
return Array.isArray(value)
|
|
? value.some((entry) => typeof entry === 'string' && entry.trim())
|
|
: false;
|
|
}
|
|
|
|
function hasProfileArray(profile: CustomWorldProfile | null, key: string) {
|
|
const value = profile
|
|
? (profile as unknown as Record<string, unknown>)[key]
|
|
: null;
|
|
return Array.isArray(value) && value.length > 0;
|
|
}
|
|
|
|
function hasSceneAct(profile: CustomWorldProfile | null) {
|
|
const rawProfile = profile as unknown as Record<string, unknown> | null;
|
|
const chapters =
|
|
rawProfile &&
|
|
(Array.isArray(rawProfile.sceneChapterBlueprints)
|
|
? rawProfile.sceneChapterBlueprints
|
|
: Array.isArray(rawProfile.sceneChapters)
|
|
? rawProfile.sceneChapters
|
|
: []);
|
|
return Array.isArray(chapters)
|
|
? chapters.some((chapter) => {
|
|
const acts =
|
|
chapter && typeof chapter === 'object'
|
|
? (chapter as Record<string, unknown>).acts
|
|
: null;
|
|
return Array.isArray(acts) && acts.length > 0;
|
|
})
|
|
: false;
|
|
}
|
|
|
|
function isAgentResultStructuralBlockerResolved(
|
|
profile: CustomWorldProfile,
|
|
code: string | null | undefined,
|
|
) {
|
|
if (!code || !AGENT_RESULT_STRUCTURAL_BLOCKER_CODES.has(code)) {
|
|
return false;
|
|
}
|
|
|
|
if (code === 'publish_missing_world_hook') {
|
|
return Boolean(
|
|
readProfileTextField(profile, [
|
|
'worldHook',
|
|
'creatorIntent.worldHook',
|
|
'anchorContent.worldPromise',
|
|
'anchorContent.worldPromise.hook',
|
|
'settingText',
|
|
]),
|
|
);
|
|
}
|
|
if (code === 'publish_missing_player_premise') {
|
|
return Boolean(
|
|
readProfileTextField(profile, [
|
|
'playerPremise',
|
|
'creatorIntent.playerPremise',
|
|
'anchorContent.playerEntryPoint',
|
|
'anchorContent.playerEntryPoint.openingIdentity',
|
|
'anchorContent.playerEntryPoint.openingProblem',
|
|
'anchorContent.playerEntryPoint.entryMotivation',
|
|
]),
|
|
);
|
|
}
|
|
if (code === 'publish_missing_core_conflict') {
|
|
return hasProfileTextArray(profile, 'coreConflicts');
|
|
}
|
|
if (code === 'publish_missing_main_chapter') {
|
|
return (
|
|
hasProfileArray(profile, 'chapters') ||
|
|
hasProfileArray(profile, 'sceneChapterBlueprints') ||
|
|
hasProfileArray(profile, 'sceneChapters')
|
|
);
|
|
}
|
|
return hasSceneAct(profile);
|
|
}
|
|
|
|
export function buildPlatformRpgAgentResultPublishGateView(
|
|
profile: CustomWorldProfile | null,
|
|
fallbackBlockers: PlatformRpgAgentResultBlockerView[],
|
|
fallbackPublishReady: boolean,
|
|
): PlatformRpgAgentResultPublishGateView {
|
|
if (!profile) {
|
|
return {
|
|
blockers: fallbackBlockers.map((entry) => entry.message),
|
|
publishReady: fallbackPublishReady,
|
|
};
|
|
}
|
|
|
|
const blockers = fallbackBlockers
|
|
.filter(
|
|
(entry) => !isAgentResultStructuralBlockerResolved(profile, entry.code),
|
|
)
|
|
.map((entry) => entry.message);
|
|
|
|
return {
|
|
blockers,
|
|
publishReady: blockers.length === 0,
|
|
};
|
|
}
|
|
|
|
export function resolvePlatformRpgAgentResultPreviewSourceLabel(
|
|
source: RpgCreationPreviewSource | string | null | undefined,
|
|
) {
|
|
if (!source) {
|
|
return null;
|
|
}
|
|
if (source === 'published_profile') {
|
|
return '已发布世界';
|
|
}
|
|
if (source === 'session_preview') {
|
|
return '会话预览';
|
|
}
|
|
return '服务端预览';
|
|
}
|