112 lines
4.9 KiB
TypeScript
112 lines
4.9 KiB
TypeScript
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
|
|
};
|
|
}
|
|
}
|