import { ACHIEVEMENT_TITLE_CONFIG, AchievementTitleConfigItem, INFINITE_ACHIEVEMENT_TITLE } from '../config/AchievementTitleConfig'; export interface AchievementTitleInfo { readonly titleText: string; readonly nextTitleText: string; readonly nextTitleProgress: number; readonly progressText: string; readonly completedLevelCount: number; readonly currentTitleStartCount: number; readonly nextTitleRequiredCount: number; readonly remainingToNextTitle: number; } interface AchievementTitleStage { readonly titleText: string; readonly clearsToNext: number; readonly startCount: number; } export class AchievementTitleManager { public static getTitleInfo(completedLevelCount: number): AchievementTitleInfo { const safeCompletedCount = AchievementTitleManager.normalizeCompletedCount(completedLevelCount); const stages = AchievementTitleManager.buildFiniteStages(); const finiteResult = AchievementTitleManager.findFiniteStage(safeCompletedCount, stages); if (finiteResult) { return AchievementTitleManager.createTitleInfo(safeCompletedCount, finiteResult.currentStage, finiteResult.nextStage); } return AchievementTitleManager.createInfiniteTitleInfo(safeCompletedCount, stages[stages.length - 1]); } private static normalizeCompletedCount(completedLevelCount: number): number { if (!Number.isFinite(completedLevelCount) || completedLevelCount < 0) { return 0; } return Math.floor(completedLevelCount); } private static buildFiniteStages(): AchievementTitleStage[] { let startCount = 0; return ACHIEVEMENT_TITLE_CONFIG.map((item: AchievementTitleConfigItem) => { const stage: AchievementTitleStage = { titleText: item.levelName, clearsToNext: item.clearsToNext, startCount }; startCount += item.clearsToNext; return stage; }); } private static findFiniteStage(completedLevelCount: number, stages: AchievementTitleStage[]): { currentStage: AchievementTitleStage; nextStage: AchievementTitleStage } | null { for (let index = 0; index < stages.length; index++) { const currentStage = stages[index]; const nextStage = stages[index + 1] ?? { titleText: `${INFINITE_ACHIEVEMENT_TITLE.levelNamePrefix}1级`, clearsToNext: INFINITE_ACHIEVEMENT_TITLE.clearsToNext, startCount: currentStage.startCount + currentStage.clearsToNext }; if (completedLevelCount < nextStage.startCount) { return { currentStage, nextStage }; } } return null; } private static createInfiniteTitleInfo(completedLevelCount: number, lastFiniteStage: AchievementTitleStage): AchievementTitleInfo { const infiniteStartCount = lastFiniteStage.startCount + lastFiniteStage.clearsToNext; const completedAfterInfiniteStart = Math.max(0, completedLevelCount - infiniteStartCount); const currentInfiniteLevel = Math.floor(completedAfterInfiniteStart / INFINITE_ACHIEVEMENT_TITLE.clearsToNext) + 1; const currentStageStartCount = infiniteStartCount + (currentInfiniteLevel - 1) * INFINITE_ACHIEVEMENT_TITLE.clearsToNext; const nextStage: AchievementTitleStage = { titleText: `${INFINITE_ACHIEVEMENT_TITLE.levelNamePrefix}${currentInfiniteLevel + 1}级`, clearsToNext: INFINITE_ACHIEVEMENT_TITLE.clearsToNext, startCount: currentStageStartCount + INFINITE_ACHIEVEMENT_TITLE.clearsToNext }; return AchievementTitleManager.createTitleInfo( completedLevelCount, { titleText: `${INFINITE_ACHIEVEMENT_TITLE.levelNamePrefix}${currentInfiniteLevel}级`, clearsToNext: INFINITE_ACHIEVEMENT_TITLE.clearsToNext, startCount: currentStageStartCount }, nextStage ); } private static createTitleInfo(completedLevelCount: number, currentStage: AchievementTitleStage, nextStage: AchievementTitleStage): AchievementTitleInfo { const interval = Math.max(1, nextStage.startCount - currentStage.startCount); const completedInCurrentTitle = Math.max(0, completedLevelCount - currentStage.startCount); const remainingToNextTitle = Math.max(0, nextStage.startCount - completedLevelCount); const nextTitleProgress = Math.max(0, Math.min(1, completedInCurrentTitle / interval)); return { titleText: currentStage.titleText, nextTitleText: nextStage.titleText, nextTitleProgress, progressText: `还差${remainingToNextTitle}题获得${nextStage.titleText}`, completedLevelCount, currentTitleStartCount: currentStage.startCount, nextTitleRequiredCount: nextStage.startCount, remainingToNextTitle }; } }