feat: add wooden fish play template

This commit is contained in:
2026-05-21 23:34:07 +08:00
parent ef09a23c35
commit 5b0f9f3763
121 changed files with 11580 additions and 159 deletions

View File

@@ -16,6 +16,10 @@ import type {
CustomWorldGenerationStep,
} from '../../packages/shared/src/contracts/runtime';
import type { SquareHoleSessionSnapshot } from '../../packages/shared/src/contracts/squareHoleAgent';
import type {
WoodenFishSessionSnapshotResponse,
WoodenFishWorkspaceCreateRequest,
} from '../../packages/shared/src/contracts/woodenFish';
import type { CustomWorldStructuredAnchorEntry } from './customWorldAgentGenerationProgress';
import type {
CreateJumpHopSessionRequest,
@@ -28,7 +32,8 @@ export type MiniGameDraftGenerationKind =
| 'square-hole'
| 'match3d'
| 'baby-object-match'
| 'jump-hop';
| 'jump-hop'
| 'wooden-fish';
export type MiniGameDraftGenerationPhase =
| 'idle'
@@ -59,6 +64,10 @@ export type MiniGameDraftGenerationPhase =
| 'jump-hop-tile-atlas'
| 'jump-hop-slice-tiles'
| 'jump-hop-write-draft'
| 'wooden-fish-draft'
| 'wooden-fish-hit-object'
| 'wooden-fish-hit-sound'
| 'wooden-fish-write-draft'
| 'puzzle-images'
| 'puzzle-ui-background'
| 'puzzle-select-image'
@@ -313,6 +322,35 @@ const JUMP_HOP_STEPS = [
const JUMP_HOP_ESTIMATED_WAIT_MS = 5 * 60_000;
const WOODEN_FISH_STEPS = [
{
id: 'wooden-fish-draft',
label: '整理玩法草稿',
detail: '保存作品信息、敲击物、音效和飘字配置。',
weight: 10,
},
{
id: 'wooden-fish-hit-object',
label: '生成敲击物图案',
detail: '使用 image2 生成最终运行态敲击物图案。',
weight: 48,
},
{
id: 'wooden-fish-hit-sound',
label: '准备敲击音效',
detail: '生成或写回短促敲击音效资产。',
weight: 30,
},
{
id: 'wooden-fish-write-draft',
label: '写入正式草稿',
detail: '保存图案、音效、飘字和封面摘要。',
weight: 12,
},
] as const satisfies ReadonlyArray<MiniGameStepDefinition>;
const WOODEN_FISH_ESTIMATED_WAIT_MS = 5 * 60_000;
function clampProgress(value: number) {
return Math.max(0, Math.min(100, Math.round(value)));
}
@@ -333,6 +371,9 @@ function getStepDefinitions(kind: MiniGameDraftGenerationKind) {
if (kind === 'jump-hop') {
return JUMP_HOP_STEPS;
}
if (kind === 'wooden-fish') {
return WOODEN_FISH_STEPS;
}
return BIG_FISH_STEPS;
}
@@ -390,8 +431,10 @@ export function createMiniGameDraftGenerationState(
? 'match3d-work-title'
: kind === 'baby-object-match'
? 'baby-object-draft'
: kind === 'jump-hop'
? 'jump-hop-draft'
: kind === 'jump-hop'
? 'jump-hop-draft'
: kind === 'wooden-fish'
? 'wooden-fish-draft'
: 'compile',
startedAtMs: Date.now(),
completedAssetCount: 0,
@@ -481,6 +524,21 @@ function resolveJumpHopPhaseByElapsedMs(
return 'jump-hop-draft';
}
function resolveWoodenFishPhaseByElapsedMs(
elapsedMs: number,
): MiniGameDraftGenerationPhase {
if (elapsedMs >= 270_000) {
return 'wooden-fish-write-draft';
}
if (elapsedMs >= 185_000) {
return 'wooden-fish-hit-sound';
}
if (elapsedMs >= 12_000) {
return 'wooden-fish-hit-object';
}
return 'wooden-fish-draft';
}
function resolvePuzzleTimelineByElapsedMs(elapsedMs: number) {
let elapsedBeforePhase = 0;
@@ -566,6 +624,13 @@ export function buildMiniGameDraftGenerationProgress(
...state,
phase: resolveJumpHopPhaseByElapsedMs(elapsedMs),
}
: state.kind === 'wooden-fish' &&
state.phase !== 'failed' &&
state.phase !== 'ready'
? {
...state,
phase: resolveWoodenFishPhaseByElapsedMs(elapsedMs),
}
: state;
const steps = getStepDefinitions(normalizedState.kind);
@@ -597,6 +662,8 @@ export function buildMiniGameDraftGenerationProgress(
? 0.52
: normalizedState.kind === 'jump-hop'
? 0.5
: normalizedState.kind === 'wooden-fish'
? 0.5
: 0;
const overallProgress =
normalizedState.phase === 'failed'
@@ -630,6 +697,8 @@ export function buildMiniGameDraftGenerationProgress(
? '宝贝识物草稿已准备完成,可进入结果页继续发布。'
: normalizedState.kind === 'jump-hop'
? '跳一跳草稿已准备完成,可进入结果页试玩或发布。'
: normalizedState.kind === 'wooden-fish'
? '敲木鱼草稿已准备完成,可进入结果页试玩或发布。'
: '首关草稿与正式图已准备完成,可进入结果页补作品信息。'
: activeStep.detail),
batchLabel: activeStep.label,
@@ -655,6 +724,8 @@ export function buildMiniGameDraftGenerationProgress(
)
: normalizedState.kind === 'jump-hop'
? Math.max(0, JUMP_HOP_ESTIMATED_WAIT_MS - elapsedMs)
: normalizedState.kind === 'wooden-fish'
? Math.max(0, WOODEN_FISH_ESTIMATED_WAIT_MS - elapsedMs)
: null,
activeStepIndex,
steps: buildMiniGameProgressSteps(
@@ -725,6 +796,57 @@ export function buildJumpHopGenerationAnchorEntries(
.filter((entry) => entry.value.trim());
}
export function buildWoodenFishGenerationAnchorEntries(
session: WoodenFishSessionSnapshotResponse | null | undefined,
formPayload: WoodenFishWorkspaceCreateRequest | null | undefined = null,
): CustomWorldStructuredAnchorEntry[] {
const draft = session?.draft;
const entries: Array<MiniGameAnchorSource | null> = [
{
key: 'wooden-fish-hit-object',
label: '敲击物',
value:
formPayload?.hitObjectPrompt?.trim() ||
draft?.hitObjectPrompt?.trim() ||
'',
},
{
key: 'wooden-fish-hit-sound',
label: '音效',
value:
formPayload?.hitSoundPrompt?.trim() ||
draft?.hitSoundPrompt?.trim() ||
draft?.hitSoundAsset?.prompt?.trim() ||
'',
},
{
key: 'wooden-fish-words',
label: '飘字',
value:
formPayload?.floatingWords
?.map((word) => word.trim())
.filter(Boolean)
.slice(0, 8)
.join('、') ||
draft?.floatingWords
?.map((word) => word.trim())
.filter(Boolean)
.slice(0, 8)
.join('、') ||
'',
},
];
return entries
.filter((entry): entry is MiniGameAnchorSource => Boolean(entry))
.map((entry) => ({
id: entry.key,
label: entry.label,
value: entry.value,
}))
.filter((entry) => entry.value.trim());
}
export function buildPuzzleGenerationAnchorEntries(
session: PuzzleAgentSessionSnapshot | null | undefined,
formPayload: CreatePuzzleAgentSessionRequest | null | undefined = null,