refactor(game): 调整场上英雄数量机制为固定3人

1. 重构GameSet配置,移除HERO_MAX_NUM新增HERO_FIELD_NUM定义场上固定编制
2. 移除局内英雄上限相关的冗余数据与校验逻辑,玩家无法主动扩充英雄数量
3. 更新所有引用旧英雄上限配置的代码,统一使用新的固定编制常量
4. 调整抽卡、阵容计算等相关逻辑适配新的英雄数量规则
This commit is contained in:
panFD
2026-08-23 17:15:46 +08:00
parent fb2c67ccab
commit 8c81be7592
4 changed files with 13 additions and 50 deletions

View File

@@ -8,7 +8,7 @@ import { GameEvent } from "./config/GameEvent";
import { GameScoreStats } from "./config/HeroAttrs"; import { GameScoreStats } from "./config/HeroAttrs";
import { mLogger } from "./Logger"; import { mLogger } from "./Logger";
import { gameDataSync } from "./GameDataSync"; import { gameDataSync } from "./GameDataSync";
import { FightSet, HERO_UPGRADE_NEED_FRAGMENTS } from "./config/GameSet"; import { HERO_UPGRADE_NEED_FRAGMENTS } from "./config/GameSet";
/** /**
* 用远程数据覆盖本地数据(统一方法) * 用远程数据覆盖本地数据(统一方法)
@@ -121,8 +121,6 @@ export class SingletonModuleComp extends ecs.Comp {
pending_mon_num: 0,//待刷出怪物数量MissionMonComp 每帧写入,供回合结束检测与 HUD 进度) pending_mon_num: 0,//待刷出怪物数量MissionMonComp 每帧写入,供回合结束检测与 HUD 进度)
wave_early_skip: 0,//本回合清场加速累计节省的秒数MissionComp 用于还原 DDA 判定口径) wave_early_skip: 0,//本回合清场加速累计节省的秒数MissionComp 用于还原 DDA 判定口径)
hero_num: 0,//英雄数量 hero_num: 0,//英雄数量
hero_max_num: FightSet.HERO_MAX_NUM,//英雄可召唤上限
hero_extend_max_num: FightSet.HERO_MAX_NUM + 1,//英雄可拓展上限
wave_time_num: 0,//回合时间 wave_time_num: 0,//回合时间
in_fight: false, in_fight: false,
fight_time: 0,//战斗时间 fight_time: 0,//战斗时间

View File

@@ -38,7 +38,8 @@ export enum FightSet {
CRIT_DAMAGE = 50,//暴击伤害 CRIT_DAMAGE = 50,//暴击伤害
MORE_RC = 10,//更多次数 广告获取的次数 MORE_RC = 10,//更多次数 广告获取的次数
HEARTPOS = -320,//基地位置 HEARTPOS = -320,//基地位置
HERO_MAX_NUM = 10,//英雄最大数量 /** 场上英雄编制数:由开局 3 次三选一固定,玩家无法扩充 */
HERO_FIELD_NUM = 3,
/** 英雄可升级到的最高等级(通过升级卡提升) */ /** 英雄可升级到的最高等级(通过升级卡提升) */
HERO_MAX_LV = 6, HERO_MAX_LV = 6,
/** 英雄技能等级上限(无限升级下技能 5 级封顶,超出等级只长属性) */ /** 英雄技能等级上限(无限升级下技能 5 级封顶,超出等级只长属性) */

View File

@@ -866,7 +866,7 @@ export const HeroList: number[] = [
/** /**
* 推荐阵容条目。 * 推荐阵容条目。
* Why: 场上最多 3 个英雄FightSet.HERO_MAX_NUM围绕"发动机 → 增益 → 主C"铁三角结构 * Why: 场上固定 3 个英雄FightSet.HERO_FIELD_NUM围绕"发动机 → 增益 → 主C"铁三角结构
* 预置官方配合组合,供抽卡引导、阵容推荐 UI 与 AI 自动布阵消费。 * 预置官方配合组合,供抽卡引导、阵容推荐 UI 与 AI 自动布阵消费。
*/ */
export interface HeroComboInfo { export interface HeroComboInfo {
@@ -1048,7 +1048,7 @@ function getAttackCd(hero: heroInfo): number {
/** 计算目标群体范围系数 */ /** 计算目标群体范围系数 */
function calcScopeMul(group: TGroup): number { function calcScopeMul(group: TGroup): number {
return (group === TGroup.Ally || group === TGroup.Team) ? FightSet.HERO_MAX_NUM : 1; return (group === TGroup.Ally || group === TGroup.Team) ? FightSet.HERO_FIELD_NUM : 1;
} }
/** 计算目标时间窗内的普攻次数 */ /** 计算目标时间窗内的普攻次数 */
@@ -1306,7 +1306,7 @@ function calcFieldPowerDetailed(entries: FieldEntry[] | undefined, hero: heroInf
const weights = HERO_POWER_WEIGHTS; const weights = HERO_POWER_WEIGHTS;
const { hp: hpEff, ap: apEff } = getEffectiveStats(hero, targetLv); const { hp: hpEff, ap: apEff } = getEffectiveStats(hero, targetLv);
const cd = getAttackCd(hero); const cd = getAttackCd(hero);
const scopeMul = FightSet.HERO_MAX_NUM; const scopeMul = FightSet.HERO_FIELD_NUM;
let total = 0; let total = 0;
const items: FieldSkillEval[] = []; const items: FieldSkillEval[] = [];

View File

@@ -7,7 +7,8 @@
* 抽卡规则:从基础英雄卡池按权重抽取,同一次抽卡内英雄不重复; * 抽卡规则:从基础英雄卡池按权重抽取,同一次抽卡内英雄不重复;
* 过滤已上场英雄(卡池不足时自动回退,保证能抽满)。 * 过滤已上场英雄(卡池不足时自动回退,保证能抽满)。
* 2. **金币费用管理** —— 抽卡费用refreshCost的扣除。 * 2. **金币费用管理** —— 抽卡费用refreshCost的扣除。
* 3. **英雄数量上限校验** —— 在 UseHeroCard 事件的 guard 阶段判断是否允许再召唤英雄。 * 3. **英雄数量由机制固定** —— 开局 3 次免费三选一召唤满 3 个英雄,玩家无法扩充,
* UseHeroCard 事件的 guard 入口保留但不再做满员拦截。
* 4. **场上英雄信息面板HInfoComp 列表)同步** —— * 4. **场上英雄信息面板HInfoComp 列表)同步** ——
* 英雄上场时实例化面板,死亡时移除,定时刷新属性显示。 * 英雄上场时实例化面板,死亡时移除,定时刷新属性显示。
* 5. **特殊卡执行** —— 处理英雄升级卡SpecialUpgrade按 UUID 精确升级)和 * 5. **特殊卡执行** —— 处理英雄升级卡SpecialUpgrade按 UUID 精确升级)和
@@ -35,7 +36,7 @@
* - HInfoComp —— 英雄信息面板 * - HInfoComp —— 英雄信息面板
* - CardSet 模块 —— 卡池配置、抽卡规则、特殊卡数据 * - CardSet 模块 —— 卡池配置、抽卡规则、特殊卡数据
* - HeroAttrsComp —— 英雄属性(升级) * - HeroAttrsComp —— 英雄属性(升级)
* - smc.vmdata.mission_data —— 局内数据coin / hero_num / hero_max_num * - smc.vmdata.mission_data —— 局内数据coin / hero_num
*/ */
import { mLogger } from "../common/Logger"; import { mLogger } from "../common/Logger";
import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, Tween, tween, Vec3, UITransform, Widget } from "cc"; import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, Tween, tween, Vec3, UITransform, Widget } from "cc";
@@ -324,8 +325,6 @@ export class MissionCardComp extends CCComp {
if (missionData) { if (missionData) {
missionData.coin = Math.max(0, Math.floor(missionData.coin ?? 0)); missionData.coin = Math.max(0, Math.floor(missionData.coin ?? 0));
missionData.hero_num = 0; missionData.hero_num = 0;
missionData.hero_max_num = FightSet.HERO_MAX_NUM;
missionData.hero_extend_max_num = FightSet.HERO_MAX_NUM + 1;
} }
// 确保卡牌组件列表已被正确缓存 // 确保卡牌组件列表已被正确缓存
@@ -812,24 +811,13 @@ export class MissionCardComp extends CCComp {
/** /**
* 使用英雄卡的 guard 校验(由 CHeroComp 通过 UseHeroCard 事件调用): * 使用英雄卡的 guard 校验(由 CHeroComp 通过 UseHeroCard 事件调用):
* - 当前英雄数 < 上限 → 允许使用。 * 英雄数量由开局 3 次三选一固定,玩家无法主动扩充,此 guard 不再做满员拦截,
* - 已满 → 阻止使用cancel=true弹 toast * 仅保留事件入口兼容 CHeroComp 的调用契约
*
* 注意:英雄不再支持合成腾位,满员时一律阻止。
*/ */
private onUseHeroCard(event: string, args: any) { private onUseHeroCard(event: string, args: any) {
const payload = args ?? event; const payload = args ?? event;
if (!payload) return; if (!payload) return;
this.syncMissionHeroData();
const current = this.getAliveHeroCount();
this.syncMissionHeroData(current);
const heroMax = this.getMissionHeroMaxNum();
if (current >= heroMax) {
payload.cancel = true;
payload.reason = "hero_limit";
this.showSmallTip("hero_full", "英雄队伍已满");
this.playHeroNumDeniedAnim();
}
} }
/** /**
@@ -2088,28 +2076,8 @@ export class MissionCardComp extends CCComp {
if (num && num.isValid) this.playHeroNumNodePop(num, peak); if (num && num.isValid) this.playHeroNumNodePop(num, peak);
} }
public setHeroMaxCount(max: number) {
const missionData = this.getMissionData();
if (!missionData) return;
const min = FightSet.HERO_MAX_NUM;
const limit = Math.max(min, missionData.hero_extend_max_num ?? (FightSet.HERO_MAX_NUM + 1));
const next = Math.max(min, Math.min(limit, Math.floor(max || min)));
if (next === missionData.hero_max_num) return;
missionData.hero_max_num = next;
this.updateHeroNumUI(true, false);
}
public tryExpandHeroMax(add: number = 1): boolean {
const missionData = this.getMissionData();
if (!missionData) return false;
const before = this.getMissionHeroMaxNum();
const next = before + Math.max(0, Math.floor(add));
this.setHeroMaxCount(next);
return this.getMissionHeroMaxNum() > before;
}
public canUseHeroCard(): boolean { public canUseHeroCard(): boolean {
return this.getAliveHeroCount() < this.getMissionHeroMaxNum(); return true;
} }
private getAliveHeroCount(): number { private getAliveHeroCount(): number {
@@ -2300,10 +2268,6 @@ export class MissionCardComp extends CCComp {
return Math.max(0, Math.floor(missionData?.hero_num ?? 0)); return Math.max(0, Math.floor(missionData?.hero_num ?? 0));
} }
private getMissionHeroMaxNum(): number {
return FightSet.HERO_MAX_NUM
}
private syncMissionHeroData(count?: number) { private syncMissionHeroData(count?: number) {
const missionData = this.getMissionData(); const missionData = this.getMissionData();
if (!missionData) return; if (!missionData) return;