import type { JsonObject } from './common'; export const SAVE_SNAPSHOT_VERSION = 2; export const DEFAULT_MUSIC_VOLUME = 0.42; export const DEFAULT_PLATFORM_THEME = 'light'; export const PLATFORM_THEMES = ['light', 'dark'] as const; export type PlatformTheme = (typeof PLATFORM_THEMES)[number]; export type SavedGameSnapshot< TGameState = unknown, TBottomTab extends string = string, TCurrentStory = unknown, > = { version: number; savedAt: string; gameState: TGameState; bottomTab: TBottomTab; currentStory: TCurrentStory | null; }; export type SavedGameSnapshotInput< TGameState = unknown, TBottomTab extends string = string, TCurrentStory = unknown, > = Omit< SavedGameSnapshot, 'savedAt' | 'version' > & { savedAt?: string; }; export type RuntimeSaveCheckpointInput = { sessionId: string; bottomTab: TBottomTab; savedAt?: string; }; export type RuntimeSettings = { musicVolume: number; platformTheme: PlatformTheme; }; export type BasicOkResult = { ok: true; }; export type ProfileDashboardCardKey = 'wallet' | 'playTime' | 'playedWorks'; export type ProfileDashboardSummary = { walletBalance: number; totalPlayTimeMs: number; playedWorldCount: number; updatedAt: string | null; }; export type ProfileWalletLedgerEntry = { id: string; amountDelta: number; balanceAfter: number; sourceType: | 'snapshot_sync' | 'new_user_registration_reward' | 'invite_inviter_reward' | 'invite_invitee_reward' | 'points_recharge' | 'asset_operation_consume' | 'asset_operation_refund' | 'redeem_code_reward' | 'puzzle_author_incentive_claim' | 'daily_task_reward'; createdAt: string; }; export type ProfileWalletLedgerResponse = { entries: ProfileWalletLedgerEntry[]; }; export type ProfileRechargeProductKind = 'points' | 'membership'; export type ProfileMembershipStatus = 'normal' | 'active'; export type ProfileMembershipTier = 'normal' | 'month' | 'season' | 'year'; export type ProfileRechargeOrderStatus = | 'pending' | 'paid' | 'failed' | 'closed' | 'refunded'; export type ProfileRechargeProduct = { productId: string; title: string; priceCents: number; kind: ProfileRechargeProductKind; pointsAmount: number; bonusPoints: number; durationDays: number; badgeLabel: string; description: string; tier: ProfileMembershipTier; }; export type ProfileMembershipBenefit = { benefitName: string; normalValue: string; monthValue: string; seasonValue: string; yearValue: string; }; export type ProfileMembership = { status: ProfileMembershipStatus; tier: ProfileMembershipTier; startedAt: string | null; expiresAt: string | null; updatedAt: string | null; }; export type ProfileRechargeOrder = { orderId: string; productId: string; productTitle: string; kind: ProfileRechargeProductKind; amountCents: number; status: ProfileRechargeOrderStatus; paymentChannel: string; paidAt: string | null; providerTransactionId: string | null; createdAt: string; pointsDelta: number; membershipExpiresAt: string | null; }; export type ProfileRechargeCenterResponse = { walletBalance: number; membership: ProfileMembership; pointProducts: ProfileRechargeProduct[]; membershipProducts: ProfileRechargeProduct[]; benefits: ProfileMembershipBenefit[]; latestOrder: ProfileRechargeOrder | null; hasPointsRecharged: boolean; }; export type WechatMiniProgramPayParams = { timeStamp: string; nonceStr: string; package: string; signType: 'RSA'; paySign: string; }; export type CreateProfileRechargeOrderRequest = { productId: string; paymentChannel?: string; }; export type CreateProfileRechargeOrderResponse = { order: ProfileRechargeOrder; center: ProfileRechargeCenterResponse; wechatMiniProgramPayParams?: WechatMiniProgramPayParams | null; }; export type ConfirmWechatProfileRechargeOrderResponse = { order: ProfileRechargeOrder; center: ProfileRechargeCenterResponse; }; export type ProfileFeedbackStatus = 'open'; export type ProfileFeedbackEvidenceItemInput = { fileName: string; contentType: string; sizeBytes: number; dataUrl: string; }; export type SubmitProfileFeedbackRequest = { description: string; contactPhone?: string | null; evidenceItems: ProfileFeedbackEvidenceItemInput[]; }; export type ProfileFeedbackEvidenceItem = { evidenceId: string; fileName: string; contentType: string; sizeBytes: number; }; export type ProfileFeedbackSubmission = { feedbackId: string; status: ProfileFeedbackStatus; createdAt: string; evidenceItems: ProfileFeedbackEvidenceItem[]; }; export type SubmitProfileFeedbackResponse = { feedback: ProfileFeedbackSubmission; }; export type ProfileReferralInviteCenterResponse = { inviteCode: string; inviteLinkPath: string; invitedCount: number; rewardedInviteCount: number; todayInviterRewardCount: number; todayInviterRewardRemaining: number; rewardPoints: number; invitedUsers: ProfileReferralInvitedUser[]; hasRedeemedCode: boolean; boundInviterUserId: string | null; boundAt: string | null; updatedAt: string; }; export type ProfileReferralInvitedUser = { userId: string; displayName: string; avatarUrl: string | null; boundAt: string; }; export type RedeemProfileReferralInviteCodeRequest = { inviteCode: string; }; export type RedeemProfileReferralInviteCodeResponse = { center: ProfileReferralInviteCenterResponse; inviteeRewardGranted: boolean; inviterRewardGranted: boolean; inviteeBalanceAfter: number; inviterBalanceAfter: number; }; export type RedeemProfileRewardCodeRequest = { code: string; }; export type RedeemProfileRewardCodeResponse = { walletBalance: number; amountGranted: number; ledgerEntry: ProfileWalletLedgerEntry; }; export type ProfileTaskCycle = 'daily'; export type TrackingScopeKind = 'site' | 'work' | 'module' | 'user'; export type AnalyticsGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year'; export type ProfileTaskStatus = | 'incomplete' | 'claimable' | 'claimed' | 'disabled'; export type ProfileTaskItem = { taskId: string; title: string; description: string; eventKey: string; cycle: ProfileTaskCycle; threshold: number; progressCount: number; rewardPoints: number; status: ProfileTaskStatus; dayKey: number; claimedAt: string | null; updatedAt: string; }; export type ProfileTaskCenterResponse = { dayKey: number; walletBalance: number; tasks: ProfileTaskItem[]; updatedAt: string; }; export type ClaimProfileTaskRewardResponse = { taskId: string; dayKey: number; rewardPoints: number; walletBalance: number; ledgerEntry: ProfileWalletLedgerEntry; center: ProfileTaskCenterResponse; }; export type ProfileTaskConfigAdminResponse = { taskId: string; title: string; description: string; eventKey: string; cycle: ProfileTaskCycle; scopeKind: TrackingScopeKind; threshold: number; rewardPoints: number; enabled: boolean; sortOrder: number; createdBy: string; createdAt: string; updatedBy: string; updatedAt: string; }; export type ProfileTaskConfigAdminListResponse = { entries: ProfileTaskConfigAdminResponse[]; }; export type AnalyticsMetricQueryRequest = { eventKey: string; scopeKind: TrackingScopeKind; scopeId: string; granularity: AnalyticsGranularity; }; export type AnalyticsBucketMetric = { bucketKey: string; bucketStartDateKey: number; bucketEndDateKey: number; value: number; }; export type AnalyticsMetricQueryResponse = { buckets: AnalyticsBucketMetric[]; }; export type AdminUpsertProfileTaskConfigRequest = { taskId: string; title: string; description?: string | null; eventKey: string; cycle: ProfileTaskCycle; scopeKind: TrackingScopeKind; threshold: number; rewardPoints: number; enabled?: boolean; sortOrder?: number; }; export type AdminDisableProfileTaskConfigRequest = { taskId: string; }; export type ProfileRedeemCodeMode = 'public' | 'unique' | 'private'; export type ProfileRedeemCodeAdminResponse = { code: string; mode: ProfileRedeemCodeMode; rewardPoints: number; maxUses: number; globalUsedCount: number; enabled: boolean; allowedUserIds: string[]; createdBy: string; createdAt: string; updatedAt: string; }; export type ProfileRedeemCodeAdminListResponse = { entries: ProfileRedeemCodeAdminResponse[]; }; export type AdminUpsertProfileRedeemCodeRequest = { code: string; mode: ProfileRedeemCodeMode; rewardPoints: number; maxUses: number; enabled?: boolean; allowedUserIds?: string[]; allowedPublicUserCodes?: string[]; }; export type AdminDisableProfileRedeemCodeRequest = { code: string; }; export type AdminUpsertProfileInviteCodeRequest = { inviteCode: string; metadata?: Record | null; startsAt?: string | null; expiresAt?: string | null; }; export type ProfileInviteCodeAdminResponse = { userId: string; inviteCode: string; metadata: Record; startsAt?: string | null; expiresAt?: string | null; status: 'pending' | 'active' | 'expired'; createdAt: string; updatedAt: string; }; export type ProfileInviteCodeAdminListResponse = { entries: ProfileInviteCodeAdminResponse[]; }; export type ProfilePlayedWorkSummary = { worldKey: string; ownerUserId: string | null; profileId: string | null; worldType: string | null; worldTitle: string; worldSubtitle: string; firstPlayedAt: string; lastPlayedAt: string; lastObservedPlayTimeMs: number; }; export type ProfilePlayStatsResponse = { totalPlayTimeMs: number; playedWorks: ProfilePlayedWorkSummary[]; updatedAt: string | null; }; export type ProfileSaveArchiveSummary = { worldKey: string; ownerUserId: string | null; profileId: string | null; worldType: string | null; worldName: string; subtitle: string; summaryText: string; coverImageSrc: string | null; lastPlayedAt: string; }; export type ProfileSaveArchiveListResponse = { entries: ProfileSaveArchiveSummary[]; }; export type ProfileSaveArchiveResumeResponse< TGameState = unknown, TBottomTab extends string = string, TCurrentStory = unknown, > = { entry: ProfileSaveArchiveSummary; snapshot: SavedGameSnapshot; }; export type CustomWorldPublicationStatus = 'draft' | 'published'; export type CustomWorldThemeMode = | 'martial' | 'arcane' | 'machina' | 'tide' | 'rift' | 'mythic'; export type CustomWorldProfileRecord = JsonObject & { id?: string; openingCg?: CustomWorldOpeningCgProfile | null; }; export type CustomWorldOpeningCgStatus = | 'not_started' | 'storyboard_generating' | 'video_generating' | 'ready' | 'failed'; export type CustomWorldOpeningCgProfile = { id: string; status: CustomWorldOpeningCgStatus; storyboardImageSrc?: string | null; storyboardAssetId?: string | null; videoSrc?: string | null; videoAssetId?: string | null; posterImageSrc?: string | null; posterAssetId?: string | null; storyboardPrompt?: string | null; videoPrompt?: string | null; imageModel: 'gpt-image-2'; videoModel: string; aspectRatio: '16:9'; imageSize: '2k'; videoResolution: '480p'; durationSeconds: 15; pointCost: 80; estimatedWaitMinutes: 10; generatedAt?: string | null; updatedAt: string; errorMessage?: string | null; }; export type CustomWorldLibraryEntry = { ownerUserId: string; profileId: string; publicWorkCode: string | null; authorPublicUserCode: string | null; profile: TProfile; visibility: CustomWorldPublicationStatus; publishedAt: string | null; updatedAt: string; authorDisplayName: string; worldName: string; subtitle: string; summaryText: string; coverImageSrc: string | null; themeMode: CustomWorldThemeMode; playableNpcCount: number; landmarkCount: number; playCount?: number; remixCount?: number; likeCount?: number; recentPlayCount7d?: number; }; export type CustomWorldGalleryCard = Omit< CustomWorldLibraryEntry, 'profile' >; export type CustomWorldLibraryResponse = { entries: CustomWorldLibraryEntry[]; }; export type CustomWorldLibraryMutationResponse< TProfile = CustomWorldProfileRecord, > = { entry: CustomWorldLibraryEntry; entries: CustomWorldLibraryEntry[]; }; export type CustomWorldGalleryResponse = { entries: CustomWorldGalleryCard[]; }; export type CustomWorldGalleryDetailResponse< TProfile = CustomWorldProfileRecord, > = { entry: CustomWorldLibraryEntry; }; export type PlatformBrowseHistoryEntry = { ownerUserId: string; profileId: string; worldName: string; subtitle: string; summaryText: string; coverImageSrc: string | null; themeMode: CustomWorldThemeMode; authorDisplayName: string; visitedAt: string; }; export type PlatformBrowseHistoryWriteEntry = Omit< PlatformBrowseHistoryEntry, 'visitedAt' > & { visitedAt?: string; }; export type PlatformBrowseHistoryResponse = { entries: PlatformBrowseHistoryEntry[]; }; export type PlatformBrowseHistoryBatchSyncRequest = { entries: PlatformBrowseHistoryWriteEntry[]; }; export const CUSTOM_WORLD_GENERATION_MODES = ['fast', 'full'] as const; export type CustomWorldGenerationMode = (typeof CUSTOM_WORLD_GENERATION_MODES)[number]; export const CUSTOM_WORLD_SESSION_STATUSES = [ 'clarifying', 'ready_to_generate', 'generating', 'completed', 'generation_error', ] as const; export type CustomWorldSessionStatus = (typeof CUSTOM_WORLD_SESSION_STATUSES)[number]; export type CustomWorldQuestion = { id: string; label: string; question: string; answer?: string; }; export type CustomWorldGenerationStep = { id: string; label: string; detail: string; completed: number; total: number; status: 'pending' | 'active' | 'completed'; }; export type CustomWorldGenerationProgress = { phaseId: string; phaseLabel: string; phaseDetail: string; batchLabel?: string; overallProgress: number; completedWeight: number; totalWeight: number; elapsedMs: number; estimatedRemainingMs: number | null; activeStepIndex: number; steps: CustomWorldGenerationStep[]; }; export type GenerateCustomWorldProfileOptions = { onProgress?: (progress: CustomWorldGenerationProgress) => void; signal?: AbortSignal; }; export type GenerateCustomWorldProfileInput = { settingText: string; creatorIntent?: JsonObject | null; generationMode?: CustomWorldGenerationMode; }; export type CreateCustomWorldSessionRequest = { settingText: string; creatorIntent?: JsonObject | null; generationMode: CustomWorldGenerationMode; }; export type AnswerCustomWorldSessionQuestionRequest = { questionId: string; answer: string; }; export type CustomWorldSessionSummary = { sessionId: string; status: CustomWorldSessionStatus; questions: CustomWorldQuestion[]; createdAt: string; updatedAt: string; }; export type CustomWorldSessionRecord = CustomWorldSessionSummary & { settingText: string; creatorIntent?: JsonObject | null; generationMode: CustomWorldGenerationMode; result?: JsonObject; lastError?: string; };