feat: 接入通关弹窗

This commit is contained in:
richarjiang
2026-04-26 16:20:37 +08:00
parent f5732b46a5
commit 5074706115
52 changed files with 8064 additions and 540 deletions

View File

@@ -0,0 +1,111 @@
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
};
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "a97f1641-1123-4458-9fb5-3b3f74e314a9",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -15,6 +15,8 @@ export class AuthManager {
private _isLoggedIn: boolean = false;
/** 服务端返回的已完成关卡 ID登录后暂存等 LevelDataManager 就绪后同步) */
private _completedLevelIds: string[] = [];
/** 服务端返回的已完成关卡数量,用于称号体系计算 */
private _completedLevelCount: number = 0;
static get instance(): AuthManager {
if (!this._instance) {
@@ -37,6 +39,14 @@ export class AuthManager {
return this._completedLevelIds;
}
get completedLevelCount(): number {
return this._completedLevelCount;
}
addCompletedLevelCount(delta: number = 1): void {
this._completedLevelCount = Math.max(0, this._completedLevelCount + delta);
}
/**
* 初始化认证:尝试恢复 token 或执行微信登录
*/
@@ -109,8 +119,9 @@ export class AuthManager {
this._isLoggedIn = true;
StorageManager.setStamina(gameData.user.stamina);
this._completedLevelIds = gameData.completedLevelIds;
this._completedLevelCount = this._resolveCompletedLevelCount(gameData);
console.log(`[AuthManager] Token 验证成功,体力: ${gameData.user.stamina.current}/${gameData.user.stamina.max},已完成: ${this._completedLevelIds.length}`);
console.log(`[AuthManager] Token 验证成功,体力: ${gameData.user.stamina.current}/${gameData.user.stamina.max},已完成: ${this._completedLevelCount}`);
return true;
}
@@ -121,6 +132,7 @@ export class AuthManager {
const gameData = await this._fetchGameData();
if (gameData) {
this._completedLevelIds = gameData.completedLevelIds;
this._completedLevelCount = this._resolveCompletedLevelCount(gameData);
StorageManager.setStamina(gameData.user.stamina);
}
}
@@ -140,4 +152,8 @@ export class AuthManager {
return null;
}
}
private _resolveCompletedLevelCount(gameData: GameData): number {
return gameData.completedLevelCount ?? gameData.completedLevelIds.length;
}
}