1
This commit is contained in:
@@ -18,10 +18,9 @@ import { readFileAsDataUrl } from '../asset-studio/characterAssetWorkflowModel';
|
||||
import {
|
||||
type CharacterAssetWorkflowCache,
|
||||
type CharacterVisualDraft,
|
||||
fetchCharacterWorkflowCache,
|
||||
saveCharacterWorkflowCache,
|
||||
putCharacterRoleAssetWorkflow,
|
||||
resolveCharacterRoleAssetWorkflow,
|
||||
} from '../asset-studio/characterAssetWorkflowPersistence';
|
||||
import { buildDefaultRolePromptBundle } from '../asset-studio/customWorldRolePromptDefaults';
|
||||
import { buildProjectPixelStyleReferenceBoard } from '../asset-studio/projectPixelStyleReference';
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
import { CharacterAnimator } from '../CharacterAnimator';
|
||||
@@ -51,42 +50,6 @@ function clampAnimationPlaybackRate(value: number) {
|
||||
);
|
||||
}
|
||||
|
||||
function buildDefaultAnimationPromptTextByKey(defaultText: string) {
|
||||
return CORE_ACTIONS.reduce<Partial<Record<AnimationState, string>>>(
|
||||
(result, action) => ({
|
||||
...result,
|
||||
[action.animation]: defaultText,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
function pickCachedAnimationPromptTextByKey(
|
||||
cache: CharacterAssetWorkflowCache,
|
||||
fallbackText: string,
|
||||
preferFreshRoleText: boolean,
|
||||
) {
|
||||
const fromCache = cache.animationPromptTextByKey ?? {};
|
||||
|
||||
return CORE_ACTIONS.reduce<Partial<Record<AnimationState, string>>>(
|
||||
(result, action) => {
|
||||
const cachedText = fromCache[action.animation]?.trim();
|
||||
const legacyText = cache.animationPromptText?.trim();
|
||||
return {
|
||||
...result,
|
||||
[action.animation]: preferFreshRoleText
|
||||
? fallbackText
|
||||
: cachedText && !isLegacyGeneratedActionDescription(cachedText)
|
||||
? cachedText
|
||||
: legacyText && !isLegacyGeneratedActionDescription(legacyText)
|
||||
? legacyText
|
||||
: fallbackText,
|
||||
};
|
||||
},
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
function roundAnimationFps(value: number) {
|
||||
return Math.round(value * 100) / 100;
|
||||
}
|
||||
@@ -321,37 +284,6 @@ function buildRoleCharacterBrief(
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function isLegacyGeneratedVisualDescription(value: string) {
|
||||
const normalized = value.trim();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
'2D 横版 RPG',
|
||||
'纯绿色绿幕',
|
||||
'2 到 2.5 头身',
|
||||
'深色粗轮廓',
|
||||
'身体整体朝右',
|
||||
'脚底完整可见',
|
||||
].some((marker) => normalized.includes(marker));
|
||||
}
|
||||
|
||||
function isLegacyGeneratedActionDescription(value: string) {
|
||||
const normalized = value.trim();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
'动作气质参考:',
|
||||
'发力起手明确',
|
||||
'收招利落',
|
||||
'动作表现偏向',
|
||||
'起手克制',
|
||||
].some((marker) => normalized.includes(marker));
|
||||
}
|
||||
|
||||
function mergeRole<T extends EditableCustomWorldRole>(
|
||||
role: T,
|
||||
patch: Partial<T>,
|
||||
@@ -561,13 +493,9 @@ export function RpgCreationRoleAssetStudioModal({
|
||||
role.visualDescription,
|
||||
],
|
||||
);
|
||||
const initialPromptBundle = useMemo(
|
||||
() => buildDefaultRolePromptBundle(baseRole),
|
||||
[baseRole],
|
||||
);
|
||||
const [visualPromptText, setVisualPromptText] = useState(
|
||||
initialPromptBundle.visualPromptText,
|
||||
);
|
||||
const [defaultAnimationPromptText, setDefaultAnimationPromptText] =
|
||||
useState('');
|
||||
const [visualPromptText, setVisualPromptText] = useState('');
|
||||
const [referenceImageDataUrls, setReferenceImageDataUrls] = useState<
|
||||
string[]
|
||||
>([]);
|
||||
@@ -586,11 +514,7 @@ export function RpgCreationRoleAssetStudioModal({
|
||||
);
|
||||
const [animationPromptTextByKey, setAnimationPromptTextByKey] = useState<
|
||||
Partial<Record<AnimationState, string>>
|
||||
>(() =>
|
||||
buildDefaultAnimationPromptTextByKey(
|
||||
initialPromptBundle.animationPromptText,
|
||||
),
|
||||
);
|
||||
>({});
|
||||
const [animationStatusByKey, setAnimationStatusByKey] = useState<
|
||||
Partial<Record<AnimationState, string | null>>
|
||||
>({});
|
||||
@@ -655,7 +579,7 @@ export function RpgCreationRoleAssetStudioModal({
|
||||
CORE_ACTIONS[0]!;
|
||||
const animationPromptText =
|
||||
animationPromptTextByKey[selectedAnimation] ??
|
||||
initialPromptBundle.animationPromptText;
|
||||
defaultAnimationPromptText;
|
||||
const previewCharacter = useMemo(
|
||||
() =>
|
||||
buildAnimationPreviewCharacter({
|
||||
@@ -727,12 +651,9 @@ export function RpgCreationRoleAssetStudioModal({
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setWorkingRole(baseRole);
|
||||
setVisualPromptText(initialPromptBundle.visualPromptText);
|
||||
setAnimationPromptTextByKey(
|
||||
buildDefaultAnimationPromptTextByKey(
|
||||
initialPromptBundle.animationPromptText,
|
||||
),
|
||||
);
|
||||
setDefaultAnimationPromptText('');
|
||||
setVisualPromptText('');
|
||||
setAnimationPromptTextByKey({});
|
||||
setReferenceImageDataUrls([]);
|
||||
setVisualDrafts([]);
|
||||
setSelectedVisualDraftId('');
|
||||
@@ -744,50 +665,52 @@ export function RpgCreationRoleAssetStudioModal({
|
||||
setSaveStatus(null);
|
||||
setIsHydratingCache(true);
|
||||
|
||||
void fetchCharacterWorkflowCache(baseRole.id, cacheScopeId)
|
||||
void resolveCharacterRoleAssetWorkflow({
|
||||
characterId: baseRole.id,
|
||||
cacheScopeId,
|
||||
role: {
|
||||
...baseRole,
|
||||
animationMap:
|
||||
(baseRole.animationMap as Record<string, unknown> | undefined) ??
|
||||
null,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
if (cancelled || !result.cache) {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cache = result.cache;
|
||||
if (cacheScopeId && cache.cacheScopeId !== cacheScopeId) {
|
||||
return;
|
||||
}
|
||||
const { workflow } = result;
|
||||
const nextRole = mergeRole(baseRole, {
|
||||
imageSrc: cache.imageSrc ?? baseRole.imageSrc,
|
||||
imageSrc: workflow.imageSrc ?? baseRole.imageSrc,
|
||||
generatedVisualAssetId:
|
||||
cache.generatedVisualAssetId ?? baseRole.generatedVisualAssetId,
|
||||
workflow.generatedVisualAssetId ?? baseRole.generatedVisualAssetId,
|
||||
generatedAnimationSetId:
|
||||
cache.generatedAnimationSetId ?? baseRole.generatedAnimationSetId,
|
||||
workflow.generatedAnimationSetId ??
|
||||
baseRole.generatedAnimationSetId,
|
||||
animationMap:
|
||||
(cache.animationMap as EditableCustomWorldRole['animationMap']) ??
|
||||
(workflow.animationMap as EditableCustomWorldRole['animationMap']) ??
|
||||
baseRole.animationMap,
|
||||
});
|
||||
setWorkingRole(nextRole);
|
||||
setVisualPromptText(
|
||||
!baseRole.visualDescription?.trim() &&
|
||||
cache.visualPromptText &&
|
||||
!isLegacyGeneratedVisualDescription(cache.visualPromptText)
|
||||
? cache.visualPromptText
|
||||
: initialPromptBundle.visualPromptText,
|
||||
setDefaultAnimationPromptText(
|
||||
workflow.defaultPromptBundle.animationPromptText,
|
||||
);
|
||||
setVisualPromptText(workflow.visualPromptText);
|
||||
setAnimationPromptTextByKey(
|
||||
pickCachedAnimationPromptTextByKey(
|
||||
cache,
|
||||
initialPromptBundle.animationPromptText,
|
||||
Boolean(baseRole.actionDescription?.trim()),
|
||||
),
|
||||
workflow.animationPromptTextByKey as Partial<
|
||||
Record<AnimationState, string>
|
||||
>,
|
||||
);
|
||||
setVisualDrafts(cache.visualDrafts ?? []);
|
||||
setVisualDrafts(workflow.visualDrafts ?? []);
|
||||
setSelectedVisualDraftId(
|
||||
cache.selectedVisualDraftId || cache.visualDrafts?.[0]?.id || '',
|
||||
workflow.selectedVisualDraftId || workflow.visualDrafts?.[0]?.id || '',
|
||||
);
|
||||
setSelectedAnimation(
|
||||
CORE_ACTIONS.some(
|
||||
(item) => item.animation === cache.selectedAnimation,
|
||||
(item) => item.animation === workflow.selectedAnimation,
|
||||
)
|
||||
? (cache.selectedAnimation as AnimationState)
|
||||
? (workflow.selectedAnimation as AnimationState)
|
||||
: (CORE_ACTIONS[0]?.animation ?? AnimationState.IDLE),
|
||||
);
|
||||
})
|
||||
@@ -801,7 +724,7 @@ export function RpgCreationRoleAssetStudioModal({
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [baseRole, cacheScopeId, initialPromptBundle, roleSnapshotKey]);
|
||||
}, [baseRole, cacheScopeId, roleSnapshotKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isHydratingCache) {
|
||||
@@ -826,7 +749,7 @@ export function RpgCreationRoleAssetStudioModal({
|
||||
unknown
|
||||
> | null,
|
||||
};
|
||||
void saveCharacterWorkflowCache(payload).catch(() => undefined);
|
||||
void putCharacterRoleAssetWorkflow(payload).catch(() => undefined);
|
||||
}, 350);
|
||||
|
||||
return () => {
|
||||
|
||||
Reference in New Issue
Block a user