feat: 添加段位和闯关奖励领取系统

1. 新增REWARD_CLAIMED游戏事件用于通知奖励领取完成
2. 新建RewardSet配置文件,定义段位和闯关奖励数据结构与具体配置
3. 在SingletonModuleComp中新增奖励领取相关逻辑:
   - 新增奖励领取状态存档字段
   - 实现可领取奖励查询、奖励领取、通关标记等方法
   - 新增统一的奖励发放工具方法
This commit is contained in:
pan
2026-09-11 17:08:55 +08:00
parent 6afb8af05e
commit 88ac1337a4
3 changed files with 219 additions and 0 deletions

View File

@@ -10,6 +10,8 @@ import { mLogger } from "./Logger";
import { gameDataSync } from "./GameDataSync";
import { HERO_UPGRADE_NEED_FRAGMENTS } from "./config/GameSet";
import { TalentSet, TALENT_LEVEL_MAX, getTalentLevelExp, getTalentUpgradeCost, isTalentTierUnlocked } from "./config/TalentSet";
import { ScoreRankList, ScoreTier, getRankByScore } from "./config/ScoreRankSet";
import { RANK_REWARDS, STAGE_REWARDS, RewardItem, pickRandomHeroUuid } from "./config/RewardSet";
/**
* 用远程数据覆盖本地数据(统一方法)
@@ -52,6 +54,15 @@ export interface GameDate {
stage: number,
section: number,
},
/** 奖励领取状态:段位/闯关首达奖励的已领取标记(玩家点击领取) */
reward_claimed?: {
/** 已领取的段位(ScoreTier 值) */
rank: string[],
/** 已领取的大关号 */
stage: number[],
},
/** 已通关大关的显式标记(覆盖存档停留场景,如第 10 关通关后进度停留 1-10-10) */
passed_stages?: number[],
}
export interface CloudData {
openid: string;
@@ -157,6 +168,13 @@ export class SingletonModuleComp extends ecs.Comp {
stage: 1,
section: 1,
},
/** 奖励领取状态(玩家点击领取,跨局保留) */
reward_claimed: {
rank: [],
stage: [],
},
/** 已通关大关的显式标记 */
passed_stages: [],
}
vmdata: any = {
@@ -370,6 +388,129 @@ export class SingletonModuleComp extends ecs.Comp {
}
oops.message.dispatchEvent(GameEvent.STAGE_PROGRESS_UPDATE);
}
// ==================== 奖励领取(段位 / 闯关) ====================
/** 查询当前已达、未领取的段位列表(升序;跨档时可连领多份) */
public getClaimableRankRewards(): ScoreTier[] {
const current = getRankByScore(this.data.score ?? 0);
const curIdx = ScoreRankList.findIndex(r => r.tier === current.tier);
const claimed = this.data.reward_claimed?.rank ?? [];
const result: ScoreTier[] = [];
// 段位只涨不跌:当前段位即历史最高,低位段位全部视为已达
for (let i = 0; i <= curIdx; i++) {
const tier = ScoreRankList[i].tier;
if (!claimed.includes(tier)) {
result.push(tier);
}
}
return result;
}
/** 查询已通关、未领取的大关号列表(升序) */
public getClaimableStageRewards(): number[] {
const claimed = this.data.reward_claimed?.stage ?? [];
const result: number[] = [];
for (const stage of Object.keys(STAGE_REWARDS).map(Number)) {
if (!claimed.includes(stage) && this.isStagePassed(stage)) {
result.push(stage);
}
}
return result.sort((a, b) => a - b);
}
/** 是否存在待领取奖励(UI 红点总开关) */
public hasUnclaimedRewards(): boolean {
return this.getClaimableRankRewards().length > 0
|| this.getClaimableStageRewards().length > 0;
}
/**
* 领取段位奖励(玩家点击触发)
* @param tier 目标段位
* @returns 实际发放的奖励列表;段位未达/已领取/配置缺失时返回 null
*/
public claimRankReward(tier: ScoreTier): RewardItem[] | null {
if (!this.getClaimableRankRewards().includes(tier)) return null;
const rewards = RANK_REWARDS[tier];
if (!rewards || rewards.length === 0) return null;
this.grantRewardItems(rewards);
if (!this.data.reward_claimed) this.data.reward_claimed = { rank: [], stage: [] };
this.data.reward_claimed.rank.push(tier);
gameDataSync.markDataDirty();
oops.message.dispatchEvent(GameEvent.REWARD_CLAIMED);
return rewards;
}
/**
* 领取闯关奖励(玩家点击触发)
* @param stage 目标大关号(1~10)
* @returns 实际发放的奖励列表;未通关/已领取/配置缺失时返回 null
*/
public claimStageReward(stage: number): RewardItem[] | null {
if (!this.getClaimableStageRewards().includes(stage)) return null;
const rewards = STAGE_REWARDS[stage];
if (!rewards || rewards.length === 0) return null;
this.grantRewardItems(rewards);
if (!this.data.reward_claimed) this.data.reward_claimed = { rank: [], stage: [] };
this.data.reward_claimed.stage.push(stage);
gameDataSync.markDataDirty();
oops.message.dispatchEvent(GameEvent.REWARD_CLAIMED);
return rewards;
}
/**
* 标记大关已通关(通关点调用,覆盖存档停留场景)
* Why: 第 10 关通关后存档停留 1-10-10,无法从 stage_progress 推导已通状态,
* 需在通关时显式写入。
* @param stage 大关号
*/
public markStagePassed(stage: number): void {
if (!this.data.passed_stages) this.data.passed_stages = [];
if (!this.data.passed_stages.includes(stage)) {
this.data.passed_stages.push(stage);
gameDataSync.markDataDirty();
}
}
/** 判定大关是否已通关:显式标记优先,存档推进兜底(已进入后续关即已通关) */
private isStagePassed(stage: number): boolean {
if ((this.data.passed_stages ?? []).includes(stage)) return true;
return (this.data.stage_progress?.stage ?? 1) > stage;
}
/**
* 统一发放奖励条目(复用现有货币/成长入口,自动落 data 持久层并标脏)
* @param items 奖励条目列表
*/
private grantRewardItems(items: RewardItem[]): void {
for (const item of items) {
switch (item.type) {
case 'gold':
if (item.value > 0) this.updateGold(item.value);
break;
case 'silver':
if (item.value > 0) this.updateSilver(item.value);
break;
case 'diamond':
if (item.value > 0) this.updateDiamond(item.value);
break;
case 'talent_exp':
if (item.value > 0) this.addTalentExp(item.value);
break;
case 'fragment': {
const uuid = item.heroUuid ?? pickRandomHeroUuid();
if (item.value > 0 && uuid > 0) {
this.addHeroFragments(uuid, item.value);
}
break;
}
}
}
}
getGameDate() {
let data: GameDate = {
...this.data,