feat: 进入关卡时 toast 提示体力消耗,修复 StorageManager 接口位置和 WxSDK 访问级别
- 进入关卡成功后显示 toast 提示消耗体力及剩余体力 - 将 StorageManager 中 UserInfo 接口移至模块顶层,修复嵌套接口语法问题 - WxSDK.getWx() 改为 static 公开方法,便于外部调用
This commit is contained in:
@@ -2,13 +2,14 @@ import { _decorator, Node, EditBox, instantiate, Vec3, Button, Label, Sprite, Sp
|
||||
import { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||
import { StorageManager } from 'db://assets/scripts/utils/StorageManager';
|
||||
import { UserAssetsManager } from 'db://assets/scripts/utils/UserAssetsManager';
|
||||
import { StaminaManager } from 'db://assets/scripts/utils/StaminaManager';
|
||||
import { WxSDK } from 'db://assets/scripts/utils/WxSDK';
|
||||
import { LevelDataManager } from 'db://assets/scripts/utils/LevelDataManager';
|
||||
import { RuntimeLevelConfig } from 'db://assets/scripts/types/LevelTypes';
|
||||
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
|
||||
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
||||
import { PassModal } from 'db://assets/prefabs/PassModal';
|
||||
import { StaminaInfo } from 'db://assets/scripts/types/ApiTypes';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
@@ -60,7 +61,7 @@ export class PageLevel extends BaseView {
|
||||
@property(Label)
|
||||
clockLabel: Label | null = null;
|
||||
|
||||
/** 积分显示标签(prefab 中序列化名为 liveLabel,保持兼容) */
|
||||
/** 体力值显示标签(prefab 中序列化名为 liveLabel,保持兼容) */
|
||||
@property(Label)
|
||||
liveLabel: Label | null = null;
|
||||
|
||||
@@ -99,7 +100,7 @@ export class PageLevel extends BaseView {
|
||||
/** 是否正在切换关卡(防止重复提交) */
|
||||
private _isTransitioning: boolean = false;
|
||||
|
||||
/** 是否正在解锁提示(防止双击重复消耗积分) */
|
||||
/** 是否正在解锁提示(防止双击重复触发) */
|
||||
private _isUnlocking: boolean = false;
|
||||
|
||||
/** 通关弹窗实例 */
|
||||
@@ -108,6 +109,9 @@ export class PageLevel extends BaseView {
|
||||
/** 是否处于分享挑战模式 */
|
||||
private _isShareMode: boolean = false;
|
||||
|
||||
/** 体力恢复倒计时定时器 */
|
||||
private _staminaTimerId: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
/**
|
||||
* 页面首次加载时调用
|
||||
*/
|
||||
@@ -125,16 +129,14 @@ export class PageLevel extends BaseView {
|
||||
this.currentLevelIndex = StorageManager.getCurrentLevelIndex();
|
||||
console.log(`[PageLevel] 恢复关卡进度: 第 ${this.currentLevelIndex + 1} 关`);
|
||||
}
|
||||
this.updatePointsLabel();
|
||||
this.updateStaminaLabel();
|
||||
this.initIconSetting();
|
||||
this.initUnlockButtons();
|
||||
this.initSubmitButton();
|
||||
|
||||
// 异步加载关卡资源,完成后启动倒计时
|
||||
this.initLevel().then(() => {
|
||||
this.startCountdown();
|
||||
}).catch(err => {
|
||||
console.error('[PageLevel] 加载关卡失败:', err);
|
||||
// 异步加载关卡资源并调用进入关卡接口,完成后启动倒计时
|
||||
this._enterAndInitLevel().catch(err => {
|
||||
console.error('[PageLevel] 进入关卡失败:', err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -143,7 +145,8 @@ export class PageLevel extends BaseView {
|
||||
*/
|
||||
onViewShow(): void {
|
||||
console.log('[PageLevel] onViewShow');
|
||||
this.updatePointsLabel();
|
||||
this.updateStaminaLabel();
|
||||
this._startStaminaRecoverTimer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,6 +154,7 @@ export class PageLevel extends BaseView {
|
||||
*/
|
||||
onViewHide(): void {
|
||||
console.log('[PageLevel] onViewHide');
|
||||
this._stopStaminaRecoverTimer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,6 +165,7 @@ export class PageLevel extends BaseView {
|
||||
this.clearInputNodes();
|
||||
this.stopCountdown();
|
||||
this._closePassModal();
|
||||
this._stopStaminaRecoverTimer();
|
||||
|
||||
// 清理事件监听
|
||||
this.iconSetting?.off(Node.EventType.TOUCH_END, this.onIconSettingClick, this);
|
||||
@@ -170,16 +175,18 @@ export class PageLevel extends BaseView {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化关卡(从 API 数据加载,异步确保资源就绪)
|
||||
* 进入关卡并初始化
|
||||
* 1. 加载关卡图片资源
|
||||
* 2. 调用进入关卡接口(消耗体力,获取答案和线索)
|
||||
* 3. 启动倒计时
|
||||
*/
|
||||
private async initLevel(): Promise<void> {
|
||||
private async _enterAndInitLevel(): Promise<void> {
|
||||
// 先加载关卡图片资源
|
||||
let config: RuntimeLevelConfig | null = null;
|
||||
|
||||
if (this._isShareMode) {
|
||||
// 分享模式:从 ShareManager 获取关卡
|
||||
config = await ShareManager.instance.ensureShareLevelReady(this.currentLevelIndex);
|
||||
} else {
|
||||
// 正常模式:先尝试缓存,再异步加载
|
||||
config = LevelDataManager.instance.getLevelConfig(this.currentLevelIndex);
|
||||
if (!config) {
|
||||
console.log(`[PageLevel] 关卡 ${this.currentLevelIndex + 1} 资源未缓存,开始加载...`);
|
||||
@@ -192,8 +199,53 @@ export class PageLevel extends BaseView {
|
||||
return;
|
||||
}
|
||||
|
||||
// 非分享模式下,调用进入关卡接口获取答案和线索
|
||||
if (!this._isShareMode) {
|
||||
const levelId = LevelDataManager.instance.getLevelId(this.currentLevelIndex);
|
||||
if (levelId) {
|
||||
const enterData = await StaminaManager.instance.enterLevel(levelId);
|
||||
if (!enterData) {
|
||||
// 进入关卡失败(可能是体力不足)
|
||||
const stamina = StaminaManager.instance.getStamina();
|
||||
if (stamina.current <= 0) {
|
||||
ToastManager.show('体力不足,请等待恢复');
|
||||
this._startStaminaRecoverTimer();
|
||||
} else {
|
||||
ToastManager.show('进入关卡失败,请重试');
|
||||
}
|
||||
this.updateStaminaLabel();
|
||||
return;
|
||||
}
|
||||
|
||||
// 提示用户消耗体力
|
||||
ToastManager.show(`消耗1点体力,剩余 ${enterData.stamina.current}/${enterData.stamina.max}`);
|
||||
|
||||
// 用 enter 接口返回的数据更新关卡配置(填充答案和线索)
|
||||
LevelDataManager.instance.updateLevelDetails(
|
||||
this.currentLevelIndex,
|
||||
{
|
||||
answer: enterData.answer,
|
||||
hint1: enterData.hint1,
|
||||
hint2: enterData.hint2,
|
||||
hint3: enterData.hint3,
|
||||
}
|
||||
);
|
||||
|
||||
// 重新获取更新后的配置
|
||||
config = LevelDataManager.instance.getLevelConfig(this.currentLevelIndex);
|
||||
if (!config) {
|
||||
console.error('[PageLevel] 更新关卡详情后获取配置失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新体力显示
|
||||
this.updateStaminaLabel();
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[PageLevel] 初始化关卡 ${this.currentLevelIndex + 1}: ${config.name}`);
|
||||
this._applyLevelConfig(config);
|
||||
this.startCountdown();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,8 +264,10 @@ export class PageLevel extends BaseView {
|
||||
// 设置主图
|
||||
this.setMainImage(config.spriteFrame);
|
||||
|
||||
// 设置线索1(默认解锁)
|
||||
this.setClue(1, config.clue1);
|
||||
// 设置线索1(默认解锁,如果有的话)
|
||||
if (config.clue1) {
|
||||
this.setClue(1, config.clue1);
|
||||
}
|
||||
|
||||
// 隐藏线索2、3
|
||||
this.hideClue(2);
|
||||
@@ -224,7 +278,9 @@ export class PageLevel extends BaseView {
|
||||
this.showUnlockButton(3);
|
||||
|
||||
// 根据答案长度创建单个输入框
|
||||
this.createSingleInput(config.answer.length);
|
||||
if (config.answer) {
|
||||
this.createSingleInput(config.answer.length);
|
||||
}
|
||||
|
||||
// 更新倒计时显示
|
||||
this.updateClockLabel();
|
||||
@@ -239,7 +295,7 @@ export class PageLevel extends BaseView {
|
||||
LevelDataManager.instance.preloadNextLevel(this.currentLevelIndex);
|
||||
}
|
||||
|
||||
console.log(`[PageLevel] 初始化关卡 ${this.currentLevelIndex + 1}, 答案长度: ${config.answer.length}`);
|
||||
console.log(`[PageLevel] 初始化关卡 ${this.currentLevelIndex + 1}, 答案长度: ${config.answer?.length ?? 0}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,6 +369,11 @@ export class PageLevel extends BaseView {
|
||||
private clearInputNodes(): void {
|
||||
for (const node of this._inputNodes) {
|
||||
if (node.isValid) {
|
||||
const editBox = node.getComponent(EditBox);
|
||||
if (editBox) {
|
||||
editBox.node.off(EditBox.EventType.TEXT_CHANGED, this.onInputTextChanged, this);
|
||||
editBox.node.off(EditBox.EventType.EDITING_DID_ENDED, this.onInputEditingEnded, this);
|
||||
}
|
||||
node.destroy();
|
||||
}
|
||||
}
|
||||
@@ -501,38 +562,38 @@ export class PageLevel extends BaseView {
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击解锁线索
|
||||
* 点击解锁线索(观看激励视频广告后解锁)
|
||||
*/
|
||||
private async onUnlockClue(index: number): Promise<void> {
|
||||
// 防止双击重复消耗
|
||||
// 防止双击重复触发
|
||||
if (this._isUnlocking) return;
|
||||
|
||||
if (!this.hasPoints()) {
|
||||
ToastManager.show('积分不足,无法解锁提示!');
|
||||
return;
|
||||
}
|
||||
|
||||
this._isUnlocking = true;
|
||||
|
||||
try {
|
||||
const levelId = this._currentConfig?.id;
|
||||
const success = await UserAssetsManager.instance.consumePoint(levelId, index);
|
||||
if (!success) {
|
||||
ToastManager.show('积分不足,无法解锁提示!');
|
||||
// 检查线索是否存在
|
||||
if (!this._currentConfig) return;
|
||||
|
||||
const clueContent = index === 2 ? this._currentConfig.clue2 : this._currentConfig.clue3;
|
||||
if (!clueContent) {
|
||||
ToastManager.show('该提示暂未配置');
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用微信激励视频广告
|
||||
ToastManager.show('观看视频即可解锁提示');
|
||||
const adWatched = await WxSDK.showRewardedVideoAd();
|
||||
if (!adWatched) {
|
||||
ToastManager.show('需要看完视频才能解锁提示哦');
|
||||
return;
|
||||
}
|
||||
|
||||
this.updatePointsLabel();
|
||||
this.playClickSound();
|
||||
this.hideUnlockButton(index);
|
||||
this.showClue(index);
|
||||
this.setClue(index, clueContent);
|
||||
|
||||
if (this._currentConfig) {
|
||||
const clueContent = index === 2 ? this._currentConfig.clue2 : this._currentConfig.clue3;
|
||||
this.setClue(index, clueContent);
|
||||
}
|
||||
|
||||
console.log(`[PageLevel] 解锁线索${index}`);
|
||||
console.log(`[PageLevel] 通过观看广告解锁线索${index}`);
|
||||
} finally {
|
||||
this._isUnlocking = false;
|
||||
}
|
||||
@@ -639,17 +700,70 @@ export class PageLevel extends BaseView {
|
||||
// 可以在这里添加游戏结束逻辑
|
||||
}
|
||||
|
||||
// ========== 积分相关方法 ==========
|
||||
// ========== 体力值相关方法 ==========
|
||||
|
||||
private updatePointsLabel(): void {
|
||||
/** 上次显示的体力值,用于变更检测 */
|
||||
private _lastDisplayedStamina: number = -1;
|
||||
|
||||
/**
|
||||
* 更新体力值显示(仅值变化时更新 UI)
|
||||
*/
|
||||
private updateStaminaLabel(): void {
|
||||
if (this.liveLabel) {
|
||||
const points = StorageManager.getPoints();
|
||||
this.liveLabel.string = `x ${points}`;
|
||||
const stamina = StaminaManager.instance.getStamina();
|
||||
if (stamina.current !== this._lastDisplayedStamina) {
|
||||
this.liveLabel.string = `x ${stamina.current}`;
|
||||
this._lastDisplayedStamina = stamina.current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private hasPoints(): boolean {
|
||||
return StorageManager.hasPoints();
|
||||
/**
|
||||
* 启动体力恢复倒计时 UI
|
||||
*/
|
||||
private _startStaminaRecoverTimer(): void {
|
||||
this._stopStaminaRecoverTimer();
|
||||
|
||||
const stamina = StaminaManager.instance.getStamina();
|
||||
if (!stamina.nextRecoverAt || stamina.current >= stamina.max) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetTime = new Date(stamina.nextRecoverAt).getTime();
|
||||
if (isNaN(targetTime)) return;
|
||||
|
||||
this._staminaTimerId = setInterval(() => {
|
||||
if (targetTime - Date.now() > 0) return;
|
||||
|
||||
// 恢复一点体力
|
||||
const currentStamina = StaminaManager.instance.getStamina();
|
||||
const newCurrent = Math.min(currentStamina.current + 1, currentStamina.max);
|
||||
const newStamina: StaminaInfo = {
|
||||
...currentStamina,
|
||||
current: newCurrent,
|
||||
nextRecoverAt: newCurrent < currentStamina.max
|
||||
? new Date(Date.now() + 10 * 60 * 1000).toISOString()
|
||||
: null,
|
||||
};
|
||||
StaminaManager.instance.updateStamina(newStamina);
|
||||
this.updateStaminaLabel();
|
||||
|
||||
this._stopStaminaRecoverTimer();
|
||||
|
||||
if (newCurrent < currentStamina.max) {
|
||||
this._startStaminaRecoverTimer();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止体力恢复倒计时
|
||||
*/
|
||||
private _stopStaminaRecoverTimer(): void {
|
||||
if (this._staminaTimerId !== null) {
|
||||
clearInterval(this._staminaTimerId);
|
||||
this._staminaTimerId = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 答案提交与关卡切换 ==========
|
||||
@@ -674,7 +788,7 @@ export class PageLevel extends BaseView {
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示成功提示
|
||||
* 显示成功提示并上报通关
|
||||
*/
|
||||
private async showSuccess(): Promise<void> {
|
||||
console.log('[PageLevel] 答案正确!');
|
||||
@@ -692,8 +806,13 @@ export class PageLevel extends BaseView {
|
||||
const timeSpent = 60 - this._countdown;
|
||||
|
||||
if (!this._isShareMode) {
|
||||
await UserAssetsManager.instance.earnPoint(levelId, timeSpent);
|
||||
this.updatePointsLabel();
|
||||
// 上报通关耗时
|
||||
const result = await StaminaManager.instance.completeLevel(levelId, timeSpent);
|
||||
if (result) {
|
||||
console.log(`[PageLevel] 通关上报成功,首次通关: ${result.firstClear}`);
|
||||
}
|
||||
// 标记关卡为已通关(本地缓存)
|
||||
LevelDataManager.instance.markLevelCompleted(this.currentLevelIndex);
|
||||
} else {
|
||||
// fire-and-forget: errors are logged inside reportLevelProgress
|
||||
void ShareManager.instance.reportLevelProgress(levelId, true, timeSpent);
|
||||
@@ -812,11 +931,8 @@ export class PageLevel extends BaseView {
|
||||
return;
|
||||
}
|
||||
|
||||
// 重置并加载下一关,重新开始倒计时
|
||||
await this.initLevel();
|
||||
this.startCountdown();
|
||||
// 重置并加载下一关(包含进入关卡接口调用)
|
||||
await this._enterAndInitLevel();
|
||||
console.log(`[PageLevel] 进入关卡 ${this.currentLevelIndex + 1}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user