feat(victory): add defeat reward gate by clear time and kill ratio

Defeat rewards now require surviving 15s and killing at least one
monster. Qualified defeats grant a fixed survive reward plus a
kill-ratio-scaled bonus; unqualified defeats yield nothing.
Victory rewards remain unchanged.
This commit is contained in:
pan
2026-09-11 15:57:21 +08:00
parent a6dbda13b1
commit 5fe961846f
4 changed files with 107 additions and 21 deletions

View File

@@ -177,6 +177,9 @@ export class VictoryComp extends CCComp {
/** 是否胜利场(驱动主按钮文案与行为) */
private isVictory: boolean = false;
/** 失败奖励判定数据(MissionComp 传入:本节坚持时间 + 击杀统计) */
private defeatStats: { clear_time: number; kills: number; total: number } | null = null;
/** 加载时隐藏 loading 遮罩并绑定按钮事件 */
protected onLoad(): void {
this.node.getChildByName("loading").active = false
@@ -221,6 +224,7 @@ export class VictoryComp extends CCComp {
// 结算类型与胜负状态:缺省按失败处理
this.settleType = args.settle_type ?? "defeat";
this.isVictory = !!args.victory;
this.defeatStats = args.defeat_stats ?? null;
// 根据是否可复活决定按钮显示
this.node.getChildByName("btns").getChildByName("next").active = !args.can_revive
@@ -604,34 +608,66 @@ export class VictoryComp extends CCComp {
/**
* 计算并发放结算奖励,结果存入 _rewardResult。
* @param isVictory 是否胜利(胜利整体 ×1.2)
* 胜利:原公式不变(base + wave × per_wave,整体 ×1.2)。
* 失败:受"坚持时间 + 击杀比例"限制——
* - 坚持 < 15s 或本节一只怪未杀 → 无任何奖励;
* - 达标 → 坚持奖励(固定)+ 击杀比例奖励(基数 × 本节击杀比例),
* 银币/钻石/碎片同样受此门槛限制(不达标不发,达标正常发放)。
* @param isVictory 是否胜利
*/
private grantRewards(isVictory: boolean) {
const s = smc.vmdata.scores;
const cfg = VICTORY_REWARD_SET;
const mult = isVictory ? cfg.victory_multiplier : 1.0;
const wave = Math.max(0, s.wave_win_count || 0);
// 1. 金币 / 天赋经验(按波次计算,胜利加成)
const gold = Math.floor((cfg.gold.base + wave * cfg.gold.per_wave) * mult);
const talentExp = Math.floor((cfg.talent_exp.base + wave * cfg.talent_exp.per_wave) * mult);
// 失败奖励资格判定:坚持时间与击杀数不达标则整场无奖励
let defeatQualified = false;
let killRatio = 0;
if (!isVictory) {
const stats = this.defeatStats;
const clearTime = stats?.clear_time ?? 0;
const kills = stats?.kills ?? 0;
const total = stats?.total ?? 0;
defeatQualified = clearTime >= cfg.defeat.time_threshold && kills > 0;
killRatio = total > 0 ? Math.min(1, kills / total) : 0;
}
// 1.5 银币固定发放;钻石按输赢在 [min, max] 内随机(不吃胜利加成,避免与固定区间重复放大)
const silver = cfg.silver;
const dRange = isVictory ? cfg.diamond.victory : cfg.diamond.defeat;
const diamond = dRange.min + Math.floor(Math.random() * (dRange.max - dRange.min + 1));
// 1. 金币 / 天赋经验
let gold: number;
let talentExp: number;
if (isVictory) {
const mult = cfg.victory_multiplier;
gold = Math.floor((cfg.gold.base + wave * cfg.gold.per_wave) * mult);
talentExp = Math.floor((cfg.talent_exp.base + wave * cfg.talent_exp.per_wave) * mult);
} else if (defeatQualified) {
// 失败达标 = 坚持奖励(固定)+ 击杀比例奖励
gold = Math.floor(cfg.defeat.survive.gold + cfg.defeat.kill.gold * killRatio);
talentExp = Math.floor(cfg.defeat.survive.talent_exp + cfg.defeat.kill.talent_exp * killRatio);
} else {
gold = 0;
talentExp = 0;
}
// 2. 英雄碎片:出场英雄各保底 + 1 个随机英雄
const playedUuids = this.collectPlayedHeroUuids();
const playedSet = new Set(playedUuids);
const fragments: FragmentReward[] = playedUuids.map(uuid => ({
uuid,
count: cfg.played_fragment,
isPlayed: true,
}));
const randomUuid = this.pickRandomHeroUuid(playedSet);
if (randomUuid > 0) {
fragments.push({ uuid: randomUuid, count: cfg.random_fragment, isPlayed: false });
// 1.5 银币固定发放;钻石按输赢在 [min, max] 内随机(失败不达标时同样清零)
const silver = !isVictory && !defeatQualified ? 0 : cfg.silver;
let diamond = 0;
if (isVictory || defeatQualified) {
const dRange = isVictory ? cfg.diamond.victory : cfg.diamond.defeat;
diamond = dRange.min + Math.floor(Math.random() * (dRange.max - dRange.min + 1));
}
// 2. 英雄碎片:出场英雄各保底 + 1 个随机英雄(失败不达标时不发)
const fragments: FragmentReward[] = [];
if (isVictory || defeatQualified) {
const playedUuids = this.collectPlayedHeroUuids();
const playedSet = new Set(playedUuids);
for (const uuid of playedUuids) {
fragments.push({ uuid, count: cfg.played_fragment, isPlayed: true });
}
const randomUuid = this.pickRandomHeroUuid(playedSet);
if (randomUuid > 0) {
fragments.push({ uuid: randomUuid, count: cfg.random_fragment, isPlayed: false });
}
}
// 3. 落地发放
@@ -655,6 +691,7 @@ export class VictoryComp extends CCComp {
mLogger.log(this.debugMode, 'VictoryComp', '[VictoryComp] 结算奖励发放', {
isVictory, wave, gold, talentExp, silver, diamond, fragments,
defeatStats: this.defeatStats, defeatQualified, killRatio,
});
}