refactor: 重构关卡经济与战斗流程
1. 移除战斗阶段限制,允许玩家在战斗中操作卡牌 2. 新增波次间5秒自动倒计时机制 3. 将固定波次金币奖励改为怪物死亡掉落 4. 重构卡牌面板逻辑,统一抽卡按钮状态判断 5. 清理冗余的战斗阶段状态变量与相关代码
This commit is contained in:
@@ -15,6 +15,8 @@ import { FieldSkillType } from "../common/config/SkillSet";
|
|||||||
|
|
||||||
import { mLogger } from "../common/Logger";
|
import { mLogger } from "../common/Logger";
|
||||||
import { SkillTriggerHelper } from "./SkillTriggerHelper";
|
import { SkillTriggerHelper } from "./SkillTriggerHelper";
|
||||||
|
import { getMonsterGoldDrop } from "../map/RogueConfig";
|
||||||
|
import { MissionEconomy } from "../map/MissionEconomy";
|
||||||
|
|
||||||
/** 最终伤害数据接口
|
/** 最终伤害数据接口
|
||||||
* 用于封装一次攻击计算的所有结果数据
|
* 用于封装一次攻击计算的所有结果数据
|
||||||
@@ -39,8 +41,8 @@ interface FinalData {
|
|||||||
* - 属性来源规范:攻击判定用施法者,防御判定用被攻击者
|
* - 属性来源规范:攻击判定用施法者,防御判定用被攻击者
|
||||||
*/
|
*/
|
||||||
@ecs.register('HeroAtkSystem')
|
@ecs.register('HeroAtkSystem')
|
||||||
export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||||
|
|
||||||
private debugMode: boolean = false; // 是否启用调试模式
|
private debugMode: boolean = false; // 是否启用调试模式
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,22 +57,22 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
* 处理伤害队列中的所有伤害事件
|
* 处理伤害队列中的所有伤害事件
|
||||||
*/
|
*/
|
||||||
update(e: ecs.Entity): void {
|
update(e: ecs.Entity): void {
|
||||||
if(!smc.mission.play ) return
|
if (!smc.mission.play) return
|
||||||
if(smc.mission.pause) return
|
if (smc.mission.pause) return
|
||||||
const TAttrsComp = e.get(HeroAttrsComp)
|
const TAttrsComp = e.get(HeroAttrsComp)
|
||||||
const damageQueue = e.get(DamageQueueComp)
|
const damageQueue = e.get(DamageQueueComp)
|
||||||
|
|
||||||
if (!TAttrsComp || !damageQueue || damageQueue.isEmpty()) return;
|
if (!TAttrsComp || !damageQueue || damageQueue.isEmpty()) return;
|
||||||
|
|
||||||
// 标记正在处理
|
// 标记正在处理
|
||||||
damageQueue.isProcessing = true;
|
damageQueue.isProcessing = true;
|
||||||
|
|
||||||
// 处理队列中的所有伤害事件
|
// 处理队列中的所有伤害事件
|
||||||
let processedCount = 0;
|
let processedCount = 0;
|
||||||
while (!damageQueue.isEmpty()) {
|
while (!damageQueue.isEmpty()) {
|
||||||
const damageEvent = damageQueue.getNextDamageEvent();
|
const damageEvent = damageQueue.getNextDamageEvent();
|
||||||
if (!damageEvent) break;
|
if (!damageEvent) break;
|
||||||
|
|
||||||
// 处理单个伤害事件
|
// 处理单个伤害事件
|
||||||
this.doAttack(e, damageEvent);
|
this.doAttack(e, damageEvent);
|
||||||
processedCount++;
|
processedCount++;
|
||||||
@@ -82,11 +84,11 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果队列已空,移除伤害队列组件
|
// 如果队列已空,移除伤害队列组件
|
||||||
if (damageQueue.isEmpty()) {
|
if (damageQueue.isEmpty()) {
|
||||||
e.remove(DamageQueueComp);
|
e.remove(DamageQueueComp);
|
||||||
|
|
||||||
if (processedCount > 0) {
|
if (processedCount > 0) {
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` ${TAttrsComp.hero_name} 伤害队列处理完成,共处理 ${processedCount} 个伤害事件`);
|
mLogger.log(this.debugMode, 'HeroAtkSystem', ` ${TAttrsComp.hero_name} 伤害队列处理完成,共处理 ${processedCount} 个伤害事件`);
|
||||||
}
|
}
|
||||||
@@ -109,9 +111,9 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
private doAttack(target: ecs.Entity, damageEvent: DamageEvent): FinalData {
|
private doAttack(target: ecs.Entity, damageEvent: DamageEvent): FinalData {
|
||||||
const TAttrsComp = target.get(HeroAttrsComp);
|
const TAttrsComp = target.get(HeroAttrsComp);
|
||||||
const targetView = target.get(HeroViewComp);
|
const targetView = target.get(HeroViewComp);
|
||||||
let reDate:FinalData={
|
let reDate: FinalData = {
|
||||||
damage:0,
|
damage: 0,
|
||||||
isCrit:false,
|
isCrit: false,
|
||||||
}
|
}
|
||||||
if (!TAttrsComp || TAttrsComp.is_dead || TAttrsComp.is_reviving) return reDate;
|
if (!TAttrsComp || TAttrsComp.is_dead || TAttrsComp.is_reviving) return reDate;
|
||||||
|
|
||||||
@@ -120,30 +122,30 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
// 获取技能配置
|
// 获取技能配置
|
||||||
const skillConf = SkillSet[damageEvent.s_uuid];
|
const skillConf = SkillSet[damageEvent.s_uuid];
|
||||||
if (!skillConf) return reDate;
|
if (!skillConf) return reDate;
|
||||||
|
|
||||||
// 触发被攻击事件
|
// 触发被攻击事件
|
||||||
this.onAttacked(target);
|
this.onAttacked(target);
|
||||||
|
|
||||||
// 暴击判定
|
// 暴击判定
|
||||||
// 使用施法者的暴击率属性(damageEvent.Attrs 快照),- 被攻击者的暴击抗性属
|
// 使用施法者的暴击率属性(damageEvent.Attrs 快照),- 被攻击者的暴击抗性属
|
||||||
const criticalChance = (damageEvent.Attrs[Attrs.critical] || 0) - (TAttrsComp.critical_res || 0);
|
const criticalChance = (damageEvent.Attrs[Attrs.critical] || 0) - (TAttrsComp.critical_res || 0);
|
||||||
const isCrit = this.checkChance(criticalChance);
|
const isCrit = this.checkChance(criticalChance);
|
||||||
|
|
||||||
// 计算基础伤害
|
// 计算基础伤害
|
||||||
let damage = this.dmgCount(damageEvent,TAttrsComp);
|
let damage = this.dmgCount(damageEvent, TAttrsComp);
|
||||||
|
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " dmgCount",damage)
|
mLogger.log(this.debugMode, 'HeroAtkSystem', " dmgCount", damage)
|
||||||
if (isCrit) {
|
if (isCrit) {
|
||||||
const critDamageBonus = damageEvent.Attrs[Attrs.critical_damage] || 0;
|
const critDamageBonus = damageEvent.Attrs[Attrs.critical_damage] || 0;
|
||||||
damage = Math.floor(damage * (1 + (FightSet.CRIT_DAMAGE + critDamageBonus) / 100));
|
damage = Math.floor(damage * (1 + (FightSet.CRIT_DAMAGE + critDamageBonus) / 100));
|
||||||
reDate.isCrit=true;
|
reDate.isCrit = true;
|
||||||
if (damageEvent.Attrs.fac === FacSet.HERO) {
|
if (damageEvent.Attrs.fac === FacSet.HERO) {
|
||||||
// 【评分系统 - 输出分】统计暴击次数与暴击造成的总伤害
|
// 【评分系统 - 输出分】统计暴击次数与暴击造成的总伤害
|
||||||
smc.vmdata.scores.crt_count++;
|
smc.vmdata.scores.crt_count++;
|
||||||
smc.vmdata.scores.crit_dmg_total += damage;
|
smc.vmdata.scores.crit_dmg_total += damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " after crit",damage)
|
mLogger.log(this.debugMode, 'HeroAtkSystem', " after crit", damage)
|
||||||
// 护盾吸收
|
// 护盾吸收
|
||||||
const shieldResult = this.absorbShield(TAttrsComp, damage);
|
const shieldResult = this.absorbShield(TAttrsComp, damage);
|
||||||
damage = shieldResult.remainingDamage;
|
damage = shieldResult.remainingDamage;
|
||||||
@@ -151,16 +153,16 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
// 【评分系统 - 防御分】统计护盾成功抵挡伤害的次数
|
// 【评分系统 - 防御分】统计护盾成功抵挡伤害的次数
|
||||||
smc.vmdata.scores.shield_block_count += shieldResult.absorbedDamage;
|
smc.vmdata.scores.shield_block_count += shieldResult.absorbedDamage;
|
||||||
}
|
}
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " after shield",damage)
|
mLogger.log(this.debugMode, 'HeroAtkSystem', " after shield", damage)
|
||||||
// 显示护盾吸收飘字
|
// 显示护盾吸收飘字
|
||||||
if (shieldResult.absorbedDamage > 0 && targetView) {
|
if (shieldResult.absorbedDamage > 0 && targetView) {
|
||||||
targetView.shield_tip(shieldResult.absorbedDamage);
|
targetView.shield_tip(shieldResult.absorbedDamage);
|
||||||
}
|
}
|
||||||
if (damage <= 0) return reDate;
|
if (damage <= 0) return reDate;
|
||||||
|
|
||||||
// TAttrsComp.hp -= damage; // 应用伤害到数据层
|
// TAttrsComp.hp -= damage; // 应用伤害到数据层
|
||||||
TAttrsComp.add_hp(-damage); // 使用 add_hp 以触发 dirty_hp 和 UI 更新
|
TAttrsComp.add_hp(-damage); // 使用 add_hp 以触发 dirty_hp 和 UI 更新
|
||||||
|
|
||||||
if (damageEvent.Attrs.fac === FacSet.HERO) {
|
if (damageEvent.Attrs.fac === FacSet.HERO) {
|
||||||
// 【评分系统 - 输出分】统计团队造成的总伤害以及单次最高伤害记录
|
// 【评分系统 - 输出分】统计团队造成的总伤害以及单次最高伤害记录
|
||||||
smc.vmdata.scores.total_dmg += damage;
|
smc.vmdata.scores.total_dmg += damage;
|
||||||
@@ -168,7 +170,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
smc.vmdata.scores.highest_dmg = damage;
|
smc.vmdata.scores.highest_dmg = damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 增加受击计数并触发 atked 技能
|
// 增加受击计数并触发 atked 技能
|
||||||
TAttrsComp.atked_count++;
|
TAttrsComp.atked_count++;
|
||||||
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
|
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
|
||||||
@@ -178,12 +180,12 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
// targetView.back();
|
// targetView.back();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 英雄${TAttrsComp.hero_name} (uuid: ${TAttrsComp.hero_uuid}) 受到 eid:${casterEid} 的 伤害 ${damage},${isCrit?"暴击":"普通"}攻击,技能ID ${damageEvent.s_uuid}`);
|
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 英雄${TAttrsComp.hero_name} (uuid: ${TAttrsComp.hero_uuid}) 受到 eid:${casterEid} 的 伤害 ${damage},${isCrit ? "暴击" : "普通"}攻击,技能ID ${damageEvent.s_uuid}`);
|
||||||
|
|
||||||
// 冰冻判定
|
// 冰冻判定
|
||||||
const freezeChance = (damageEvent.Attrs[Attrs.freeze_chance] || 0) - (TAttrsComp.freeze_res || 0);
|
const freezeChance = (damageEvent.Attrs[Attrs.freeze_chance] || 0) - (TAttrsComp.freeze_res || 0);
|
||||||
const isFrost = !TAttrsComp.isFrost() && this.checkChance(freezeChance);
|
const isFrost = !TAttrsComp.isFrost() && this.checkChance(freezeChance);
|
||||||
|
|
||||||
// 击晕判定
|
// 击晕判定
|
||||||
const stunChance = (damageEvent.Attrs[Attrs.stun_chance] || 0) - (TAttrsComp.stun_res || 0);
|
const stunChance = (damageEvent.Attrs[Attrs.stun_chance] || 0) - (TAttrsComp.stun_res || 0);
|
||||||
const isStun = !TAttrsComp.isStun() && this.checkChance(stunChance);
|
const isStun = !TAttrsComp.isStun() && this.checkChance(stunChance);
|
||||||
@@ -191,9 +193,9 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
// 击退判定
|
// 击退判定
|
||||||
const knockbackChance = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.knockback_res || 0);
|
const knockbackChance = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.knockback_res || 0);
|
||||||
const isKnockback = this.checkChance(knockbackChance);
|
const isKnockback = this.checkChance(knockbackChance);
|
||||||
|
|
||||||
// ✅ 触发视图层表现(伤害数字、受击动画、冰冻、击晕、击退)
|
// ✅ 触发视图层表现(伤害数字、受击动画、冰冻、击晕、击退)
|
||||||
if (targetView) {
|
if (targetView) {
|
||||||
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, false);
|
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, false);
|
||||||
targetView.playEnd(skillConf.endAnm);
|
targetView.playEnd(skillConf.endAnm);
|
||||||
if (isFrost) {
|
if (isFrost) {
|
||||||
@@ -214,8 +216,8 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
targetView.back(damageEvent.Attrs[Attrs.knockback_distance] || 0);
|
targetView.back(damageEvent.Attrs[Attrs.knockback_distance] || 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 检查死亡
|
// 检查死亡
|
||||||
if (TAttrsComp.hp <= 0) {
|
if (TAttrsComp.hp <= 0) {
|
||||||
// 先触发死亡技能(如亡语),不管后续是否复活都应触发
|
// 先触发死亡技能(如亡语),不管后续是否复活都应触发
|
||||||
@@ -245,7 +247,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
// 根据技能配置恢复生命值
|
// 根据技能配置恢复生命值
|
||||||
TAttrsComp.hp = Math.floor(TAttrsComp.hp_max * (reviveHpPercent / 100));
|
TAttrsComp.hp = Math.floor(TAttrsComp.hp_max * (reviveHpPercent / 100));
|
||||||
TAttrsComp.dirty_hp = true;
|
TAttrsComp.dirty_hp = true;
|
||||||
|
|
||||||
// 触发复活动画
|
// 触发复活动画
|
||||||
if (targetView && reviveSkillConf) {
|
if (targetView && reviveSkillConf) {
|
||||||
targetView.playReady(reviveSkillConf.readyAnm);
|
targetView.playReady(reviveSkillConf.readyAnm);
|
||||||
@@ -269,14 +271,14 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
targetView.do_dead();
|
targetView.do_dead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` ${TAttrsComp.hero_name} 受到 ${damage} 点伤害 (暴击: ${isCrit})`);
|
mLogger.log(this.debugMode, 'HeroAtkSystem', ` ${TAttrsComp.hero_name} 受到 ${damage} 点伤害 (暴击: ${isCrit})`);
|
||||||
|
|
||||||
|
|
||||||
reDate.damage=damage;
|
reDate.damage = damage;
|
||||||
return reDate;
|
return reDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 详细伤害计算核心方法
|
* 详细伤害计算核心方法
|
||||||
*
|
*
|
||||||
@@ -295,23 +297,23 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
* @important 注意事项:
|
* @important 注意事项:
|
||||||
|
|
||||||
*/
|
*/
|
||||||
private dmgCount(damageEvent:DamageEvent,TAttrsComp:HeroAttrsComp){
|
private dmgCount(damageEvent: DamageEvent, TAttrsComp: HeroAttrsComp) {
|
||||||
// 1. 获取技能配置 - 如果技能不存在,直接返回0伤害
|
// 1. 获取技能配置 - 如果技能不存在,直接返回0伤害
|
||||||
const CAttrs=damageEvent.Attrs;
|
const CAttrs = damageEvent.Attrs;
|
||||||
const TAttrs=TAttrsComp;
|
const TAttrs = TAttrsComp;
|
||||||
let sConf = SkillSet[damageEvent.s_uuid];
|
let sConf = SkillSet[damageEvent.s_uuid];
|
||||||
if (!sConf) return 0;
|
if (!sConf) return 0;
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 伤害处理对象`,CAttrs,TAttrs);
|
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 伤害处理对象`, CAttrs, TAttrs);
|
||||||
// 2. 计算原始物理伤害和魔法伤害
|
// 2. 计算原始物理伤害和魔法伤害
|
||||||
// 物理伤害基础值 = 技能物理倍率 * (施法者物理攻击力 + 额外伤害) / 100 * 额外伤害比例
|
// 物理伤害基础值 = 技能物理倍率 * (施法者物理攻击力 + 额外伤害) / 100 * 额外伤害比例
|
||||||
let apBase = (sConf.ap||0)*(CAttrs[Attrs.ap]+damageEvent.ext_dmg)/100*damageEvent.dmg_ratio;
|
let apBase = (sConf.ap || 0) * (CAttrs[Attrs.ap] + damageEvent.ext_dmg) / 100 * damageEvent.dmg_ratio;
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 物理伤害基础值: ${apBase}, 技能ap=${sConf.ap},施法者物理攻击力: ${CAttrs[Attrs.ap]},}
|
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 物理伤害基础值: ${apBase}, 技能ap=${sConf.ap},施法者物理攻击力: ${CAttrs[Attrs.ap]},}
|
||||||
额外伤害:${damageEvent.ext_dmg}, 额外伤害比例:${damageEvent.dmg_ratio}`);
|
额外伤害:${damageEvent.ext_dmg}, 额外伤害比例:${damageEvent.dmg_ratio}`);
|
||||||
|
|
||||||
// 4. 确保伤害值非负
|
// 4. 确保伤害值非负
|
||||||
let total = Math.max(0, apBase);
|
let total = Math.max(0, apBase);
|
||||||
|
|
||||||
if (this.debugMode) mLogger.log(this.debugMode, 'HeroAtkSystem', ` 最终伤害: ${total} (Base: ${apBase}`);
|
if (this.debugMode) mLogger.log(this.debugMode, 'HeroAtkSystem', ` 最终伤害: ${total} (Base: ${apBase}`);
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,12 +352,12 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
private doDead(entity: ecs.Entity): void {
|
private doDead(entity: ecs.Entity): void {
|
||||||
const TAttrsComp = entity.get(HeroAttrsComp);
|
const TAttrsComp = entity.get(HeroAttrsComp);
|
||||||
if (!TAttrsComp || TAttrsComp.is_dead) return;
|
if (!TAttrsComp || TAttrsComp.is_dead) return;
|
||||||
|
|
||||||
TAttrsComp.is_dead = true;
|
TAttrsComp.is_dead = true;
|
||||||
|
|
||||||
// 触发死亡事件
|
// 触发死亡事件
|
||||||
this.onDeath(entity);
|
this.onDeath(entity);
|
||||||
|
|
||||||
if (this.debugMode) {
|
if (this.debugMode) {
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` ${TAttrsComp.hero_name} 死亡`);
|
mLogger.log(this.debugMode, 'HeroAtkSystem', ` ${TAttrsComp.hero_name} 死亡`);
|
||||||
}
|
}
|
||||||
@@ -405,10 +407,10 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
* @param damage 原始伤害值
|
* @param damage 原始伤害值
|
||||||
* @returns {remainingDamage, absorbedDamage} 剩余伤害值和吸收的护盾值
|
* @returns {remainingDamage, absorbedDamage} 剩余伤害值和吸收的护盾值
|
||||||
*/
|
*/
|
||||||
private absorbShield(TAttrsComp: HeroAttrsComp, damage: number): {remainingDamage: number, absorbedDamage: number} {
|
private absorbShield(TAttrsComp: HeroAttrsComp, damage: number): { remainingDamage: number, absorbedDamage: number } {
|
||||||
if (TAttrsComp.shield <= 0) {
|
if (TAttrsComp.shield <= 0) {
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " 护盾值小于等于0,无法吸收伤害");
|
mLogger.log(this.debugMode, 'HeroAtkSystem', " 护盾值小于等于0,无法吸收伤害");
|
||||||
return {remainingDamage: damage, absorbedDamage: 0};
|
return { remainingDamage: damage, absorbedDamage: 0 };
|
||||||
}
|
}
|
||||||
TAttrsComp.shield = Math.max(0, TAttrsComp.shield - 1);
|
TAttrsComp.shield = Math.max(0, TAttrsComp.shield - 1);
|
||||||
if (TAttrsComp.shield <= 0) {
|
if (TAttrsComp.shield <= 0) {
|
||||||
@@ -416,7 +418,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
}
|
}
|
||||||
TAttrsComp.dirty_shield = true;
|
TAttrsComp.dirty_shield = true;
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 护盾抵挡1次伤害,剩余次数 ${TAttrsComp.shield}`);
|
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 护盾抵挡1次伤害,剩余次数 ${TAttrsComp.shield}`);
|
||||||
return {remainingDamage: 0, absorbedDamage: 1};
|
return { remainingDamage: 0, absorbedDamage: 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -437,7 +439,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
TAttrsComp.atked_count++;
|
TAttrsComp.atked_count++;
|
||||||
// 这里可以添加被攻击时的特殊处理逻辑
|
// 这里可以添加被攻击时的特殊处理逻辑
|
||||||
if (TAttrsComp.fac === FacSet.MON) return;
|
if (TAttrsComp.fac === FacSet.MON) return;
|
||||||
|
|
||||||
// 例如:触发某些天赋效果、反击逻辑等
|
// 例如:触发某些天赋效果、反击逻辑等
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,7 +464,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
private onDeath(entity: ecs.Entity): void {
|
private onDeath(entity: ecs.Entity): void {
|
||||||
const TAttrsComp = entity.get(HeroAttrsComp);
|
const TAttrsComp = entity.get(HeroAttrsComp);
|
||||||
if (!TAttrsComp) return;
|
if (!TAttrsComp) return;
|
||||||
|
|
||||||
if (TAttrsComp.fac === FacSet.MON) {
|
if (TAttrsComp.fac === FacSet.MON) {
|
||||||
// 怪物死亡处理
|
// 怪物死亡处理
|
||||||
this.scheduleDrop(entity);
|
this.scheduleDrop(entity);
|
||||||
@@ -473,24 +475,28 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 延迟执行掉落逻辑
|
* 怪物死亡掉落金币
|
||||||
*
|
*
|
||||||
* 采用延迟执行的原因:
|
* 实现说明:
|
||||||
* 1. 避免在伤害计算过程中阻塞主线程
|
* - 通过 hero_uuid 查 HeroInfo 获取 monType
|
||||||
* 2. 给死亡动画播放留出时间
|
* - 按怪物类型查 MonsterGoldSet 获取固定金币掉落数(Boss 有独立的高额奖励)
|
||||||
* 3. 可以批量处理多个掉落,优化性能
|
* - 调用 MissionEconomy.addCoin 统一发放金币(会自动触发 UI 更新和评分统计)
|
||||||
*
|
*
|
||||||
* @param entity 死亡的怪物实体
|
* @param entity 死亡的怪物实体
|
||||||
*
|
|
||||||
* @todo 具体实现可以包括:
|
|
||||||
* - 根据怪物等级计算基础掉落
|
|
||||||
* - 幸运值影响掉落品质
|
|
||||||
* - 特殊事件(双倍掉落、稀有掉落等)
|
|
||||||
* - 掉落物在场景中的生成位置计算
|
|
||||||
*/
|
*/
|
||||||
private scheduleDrop(entity: ecs.Entity): void {
|
private scheduleDrop(entity: ecs.Entity): void {
|
||||||
// 这里可以添加掉落逻辑
|
const TAttrsComp = entity.get(HeroAttrsComp);
|
||||||
// 例如:延迟一段时间后生成掉落物品
|
if (!TAttrsComp) return;
|
||||||
|
|
||||||
|
// 通过 hero_uuid 查配置获取怪物类型
|
||||||
|
const info = HeroInfo[TAttrsComp.hero_uuid];
|
||||||
|
const monType = info?.monType ?? 0;
|
||||||
|
const gold = getMonsterGoldDrop(monType, !!TAttrsComp.is_boss);
|
||||||
|
if (gold <= 0) return;
|
||||||
|
|
||||||
|
MissionEconomy.addCoin(gold);
|
||||||
|
mLogger.log(this.debugMode, 'HeroAtkSystem',
|
||||||
|
` ${TAttrsComp.hero_name} 死亡掉落金币 ${gold}(monType=${monType}, isBoss=${TAttrsComp.is_boss})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -520,7 +526,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
enableDebug() {
|
enableDebug() {
|
||||||
this.debugMode = true;
|
this.debugMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 禁用调试模式
|
* 禁用调试模式
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -135,9 +135,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
|
|
||||||
// ======================== 运行时状态 ========================
|
// ======================== 运行时状态 ========================
|
||||||
|
|
||||||
/** 当前是否为战斗阶段 */
|
|
||||||
private isBattlePhase: boolean = false;
|
|
||||||
|
|
||||||
/** 四个槽位对应的 CardComp 控制器缓存(有序数组) */
|
/** 四个槽位对应的 CardComp 控制器缓存(有序数组) */
|
||||||
private cardComps: CardComp[] = [];
|
private cardComps: CardComp[] = [];
|
||||||
/** 技能卡槽控制器缓存 */
|
/** 技能卡槽控制器缓存 */
|
||||||
@@ -203,7 +200,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
* 6. 执行首次抽卡并分发到 4 个槽位。
|
* 6. 执行首次抽卡并分发到 4 个槽位。
|
||||||
*/
|
*/
|
||||||
onMissionStart() {
|
onMissionStart() {
|
||||||
this.isBattlePhase = false;
|
|
||||||
this.enterPreparePhase();
|
this.enterPreparePhase();
|
||||||
this.poolLv = CARD_POOL_INIT_LEVEL;
|
this.poolLv = CARD_POOL_INIT_LEVEL;
|
||||||
const missionData = this.getMissionData();
|
const missionData = this.getMissionData();
|
||||||
@@ -326,11 +322,9 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 战斗开始:不收起面板,不再强制清空卡牌 */
|
/** 战斗开始:保留卡牌面板,允许玩家在战斗阶段继续抽卡和召唤英雄 */
|
||||||
private onFightStart() {
|
private onFightStart() {
|
||||||
this.isBattlePhase = true;
|
|
||||||
this.enterBattlePhase();
|
this.enterBattlePhase();
|
||||||
this.clearAllCards();
|
|
||||||
|
|
||||||
// 第一次进入战斗阶段,关闭guide4
|
// 第一次进入战斗阶段,关闭guide4
|
||||||
if (!smc.finish_guides.includes(4)) {
|
if (!smc.finish_guides.includes(4)) {
|
||||||
@@ -392,10 +386,10 @@ export class MissionCardComp extends CCComp {
|
|||||||
if (tipNode) {
|
if (tipNode) {
|
||||||
tipNode.active = true;
|
tipNode.active = true;
|
||||||
Tween.stopAllByTarget(tipNode);
|
Tween.stopAllByTarget(tipNode);
|
||||||
|
|
||||||
// 设置初始状态:缩放为 0
|
// 设置初始状态:缩放为 0
|
||||||
tipNode.setScale(new Vec3(0, 0, 1));
|
tipNode.setScale(new Vec3(0, 0, 1));
|
||||||
|
|
||||||
tween(tipNode)
|
tween(tipNode)
|
||||||
// 1. 弹出动画(微放大再回弹)
|
// 1. 弹出动画(微放大再回弹)
|
||||||
.to(0.15, { scale: new Vec3(1.1, 1.1, 1) }, { easing: 'quadOut' })
|
.to(0.15, { scale: new Vec3(1.1, 1.1, 1) }, { easing: 'quadOut' })
|
||||||
@@ -418,7 +412,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
|
|
||||||
/** 新一波:展开面板 → 刷新费用 UI → 重新抽卡分发 */
|
/** 新一波:展开面板 → 刷新费用 UI → 重新抽卡分发 */
|
||||||
private onNewWave() {
|
private onNewWave() {
|
||||||
this.isBattlePhase = false;
|
|
||||||
this.enterPreparePhase();
|
this.enterPreparePhase();
|
||||||
this.updateCoinAndCostUI();
|
this.updateCoinAndCostUI();
|
||||||
this.layoutCardSlots();
|
this.layoutCardSlots();
|
||||||
@@ -495,13 +488,13 @@ export class MissionCardComp extends CCComp {
|
|||||||
// 如果我们把它包在 `if (!smc.finish_guides.includes(2))` 里,
|
// 如果我们把它包在 `if (!smc.finish_guides.includes(2))` 里,
|
||||||
// 当玩家点击 guide2 把它关掉时(finish_guides 存入了 2),
|
// 当玩家点击 guide2 把它关掉时(finish_guides 存入了 2),
|
||||||
// 再点技能卡触发这个方法,外层 if 就会进不去,guide3 就永远弹不出来了!
|
// 再点技能卡触发这个方法,外层 if 就会进不去,guide3 就永远弹不出来了!
|
||||||
|
|
||||||
// 修复:独立判断 guide2 的关闭 和 guide3 的开启
|
// 修复:独立判断 guide2 的关闭 和 guide3 的开启
|
||||||
if (!smc.finish_guides.includes(2)) {
|
if (!smc.finish_guides.includes(2)) {
|
||||||
smc.finish_guides.push(2);
|
smc.finish_guides.push(2);
|
||||||
oops.gui.remove(UIID.Guide2);
|
oops.gui.remove(UIID.Guide2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!smc.finish_guides.includes(3)) {
|
if (!smc.finish_guides.includes(3)) {
|
||||||
oops.gui.open(UIID.Guide3);
|
oops.gui.open(UIID.Guide3);
|
||||||
}
|
}
|
||||||
@@ -563,7 +556,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
smc.finish_guides.push(3);
|
smc.finish_guides.push(3);
|
||||||
oops.gui.remove(UIID.Guide3);
|
oops.gui.remove(UIID.Guide3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!smc.finish_guides.includes(4)) {
|
if (!smc.finish_guides.includes(4)) {
|
||||||
oops.gui.open(UIID.Guide4);
|
oops.gui.open(UIID.Guide4);
|
||||||
}
|
}
|
||||||
@@ -589,12 +582,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
const payload = args ?? event;
|
const payload = args ?? event;
|
||||||
if (!payload) return;
|
if (!payload) return;
|
||||||
|
|
||||||
if (this.isBattlePhase) {
|
// 战斗阶段也允许召唤英雄(无需额外费用),仅校验英雄数量上限
|
||||||
payload.cancel = true;
|
|
||||||
payload.reason = "battle_phase";
|
|
||||||
oops.gui.toast("战斗阶段无法召唤英雄");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const current = this.getAliveHeroCount();
|
const current = this.getAliveHeroCount();
|
||||||
this.syncMissionHeroData(current);
|
this.syncMissionHeroData(current);
|
||||||
@@ -781,10 +769,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
* 3. 重新布局槽位 → 从卡池构建 4 张卡 → 分发到槽位。
|
* 3. 重新布局槽位 → 从卡池构建 4 张卡 → 分发到槽位。
|
||||||
*/
|
*/
|
||||||
private onClickDraw() {
|
private onClickDraw() {
|
||||||
if (this.isBattlePhase) {
|
// 战斗阶段和倒计时阶段均允许刷新抽卡
|
||||||
oops.gui.toast("战斗阶段无法抽卡");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const cost = MissionEconomy.getRefreshCost(this.refreshCost);
|
const cost = MissionEconomy.getRefreshCost(this.refreshCost);
|
||||||
const success = MissionEconomy.executeRefresh(this.refreshCost);
|
const success = MissionEconomy.executeRefresh(this.refreshCost);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@@ -868,22 +853,13 @@ export class MissionCardComp extends CCComp {
|
|||||||
private enterBattlePhase() {
|
private enterBattlePhase() {
|
||||||
if (!this.cards_node || !this.cards_node.isValid) return;
|
if (!this.cards_node || !this.cards_node.isValid) return;
|
||||||
this.initCardsPanelPos();
|
this.initCardsPanelPos();
|
||||||
|
// 战斗阶段允许抽卡:nobg 按"金币是否足够"判断,而非强制置灰
|
||||||
if (this.cards_chou && this.cards_chou.isValid) {
|
if (this.cards_chou && this.cards_chou.isValid) {
|
||||||
const nobg = this.cards_chou.getChildByName("nobg");
|
const nobg = this.cards_chou.getChildByName("nobg");
|
||||||
if (nobg) {
|
if (nobg) {
|
||||||
nobg.active = true;
|
nobg.active = !this.canDrawCards();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 战斗阶段不再隐藏抽卡面板
|
|
||||||
// Tween.stopAllByTarget(this.cards_node);
|
|
||||||
// tween(this.cards_node)
|
|
||||||
// .to(this.cardsPanelMoveDuration, { scale: this.cardsHideScale })
|
|
||||||
// .call(() => {
|
|
||||||
// if (this.cards_node && this.cards_node.isValid) {
|
|
||||||
// this.cards_node.active = false;
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 构建本次抽卡结果,保证最终可分发3条数据 */
|
/** 构建本次抽卡结果,保证最终可分发3条数据 */
|
||||||
@@ -924,7 +900,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
unique: true
|
unique: true
|
||||||
});
|
});
|
||||||
if (fallback.length === 0) break;
|
if (fallback.length === 0) break;
|
||||||
|
|
||||||
// 如果池子数量不足,只能被迫允许重复,但尽量拿没被抽到的
|
// 如果池子数量不足,只能被迫允许重复,但尽量拿没被抽到的
|
||||||
const fPick = fallback.find(c => !filled.some(fc => fc.uuid === c.uuid));
|
const fPick = fallback.find(c => !filled.some(fc => fc.uuid === c.uuid));
|
||||||
if (fPick) {
|
if (fPick) {
|
||||||
@@ -1046,7 +1022,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
label.string = `lv.${lv}`;
|
label.string = `lv.${lv}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextNode = this.pool_lv_node.getChildByName("next");
|
const nextNode = this.pool_lv_node.getChildByName("next");
|
||||||
if (nextNode) {
|
if (nextNode) {
|
||||||
const nextLabel = nextNode.getComponent(Label);
|
const nextLabel = nextNode.getComponent(Label);
|
||||||
@@ -1098,10 +1074,11 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateDrawCostUI() {
|
private updateDrawCostUI() {
|
||||||
|
// 战斗阶段也允许抽卡,nobg 统一按"金币是否足够"判断
|
||||||
if (this.cards_chou) {
|
if (this.cards_chou) {
|
||||||
const nobg = this.cards_chou.getChildByName("nobg");
|
const nobg = this.cards_chou.getChildByName("nobg");
|
||||||
if (nobg) {
|
if (nobg) {
|
||||||
nobg.active = this.isBattlePhase ? true : !this.canDrawCards();
|
nobg.active = !this.canDrawCards();
|
||||||
}
|
}
|
||||||
const coinNode = this.cards_chou.getChildByName("coin");
|
const coinNode = this.cards_chou.getChildByName("coin");
|
||||||
const numLabel = coinNode?.getChildByName("num")?.getComponent(Label);
|
const numLabel = coinNode?.getChildByName("num")?.getComponent(Label);
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ import { Timer } from "db://oops-framework/core/common/timer/Timer";
|
|||||||
import { FieldSkillType } from "../common/config/SkillSet";
|
import { FieldSkillType } from "../common/config/SkillSet";
|
||||||
import { FieldSkillHelper } from "../hero/FieldSkillHelper";
|
import { FieldSkillHelper } from "../hero/FieldSkillHelper";
|
||||||
import { spawningEngine } from "./RogueConfig";
|
import { spawningEngine } from "./RogueConfig";
|
||||||
import { MissionEconomy } from "./MissionEconomy";
|
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
/** 任务(关卡)生命周期阶段 */
|
/** 任务(关卡)生命周期阶段 */
|
||||||
@@ -129,6 +128,10 @@ export class MissionComp extends CCComp {
|
|||||||
}
|
}
|
||||||
/**秒计时 */
|
/**秒计时 */
|
||||||
PhaseTime: Timer = new Timer(1)
|
PhaseTime: Timer = new Timer(1)
|
||||||
|
/** 波次间倒计时(秒) */
|
||||||
|
private waveCountdown: number = 0;
|
||||||
|
/** 波次间倒计时总时长(秒) */
|
||||||
|
private readonly WAVE_COUNTDOWN_DURATION: number = 5;
|
||||||
/** 上一次显示的时间字符串(避免重复设置) */
|
/** 上一次显示的时间字符串(避免重复设置) */
|
||||||
private lastTimeStr: string = "";
|
private lastTimeStr: string = "";
|
||||||
/** 上一次显示的秒数(避免重复计算) */
|
/** 上一次显示的秒数(避免重复计算) */
|
||||||
@@ -159,10 +162,10 @@ export class MissionComp extends CCComp {
|
|||||||
private currentWave: number = 0;
|
private currentWave: number = 0;
|
||||||
/** 是否为Boss波次 */
|
/** 是否为Boss波次 */
|
||||||
private isBossWave: boolean = false;
|
private isBossWave: boolean = false;
|
||||||
/** 上一次发放金币奖励的波数(防止重复发放) */
|
|
||||||
private lastPrepareCoinWave: number = 0;
|
|
||||||
/** 当前任务阶段 */
|
/** 当前任务阶段 */
|
||||||
public currentPhase: MissionPhase = MissionPhase.None;
|
public currentPhase: MissionPhase = MissionPhase.None;
|
||||||
|
/** 是否处于波次间倒计时状态 */
|
||||||
|
private isWaveCountdown: boolean = false;
|
||||||
|
|
||||||
// ======================== ECS 查询匹配器(预缓存) ========================
|
// ======================== ECS 查询匹配器(预缓存) ========================
|
||||||
|
|
||||||
@@ -219,9 +222,18 @@ export class MissionComp extends CCComp {
|
|||||||
// 如果是暂停状态,且不在 BattleEnd 阶段(全灭时需要播放完 fend 技能动画并自动流转),才真正停止 update 逻辑
|
// 如果是暂停状态,且不在 BattleEnd 阶段(全灭时需要播放完 fend 技能动画并自动流转),才真正停止 update 逻辑
|
||||||
if (smc.mission.pause && this.currentPhase !== MissionPhase.BattleEnd) return
|
if (smc.mission.pause && this.currentPhase !== MissionPhase.BattleEnd) return
|
||||||
|
|
||||||
|
// 波次间倒计时:PrepareStart 复用为倒计时阶段,5 秒后自动进入下一波
|
||||||
|
if (this.currentPhase === MissionPhase.PrepareStart) {
|
||||||
|
this.waveCountdown -= dt;
|
||||||
|
this.updateCountdownUI();
|
||||||
|
if (this.waveCountdown <= 0) {
|
||||||
|
this.autoNextPhase();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 处理过渡阶段的计时
|
// 处理过渡阶段的计时
|
||||||
if (this.currentPhase === MissionPhase.PrepareStart ||
|
if (this.currentPhase === MissionPhase.PrepareEnd ||
|
||||||
this.currentPhase === MissionPhase.PrepareEnd ||
|
|
||||||
this.currentPhase === MissionPhase.BattleStart ||
|
this.currentPhase === MissionPhase.BattleStart ||
|
||||||
this.currentPhase === MissionPhase.BattleEnd) {
|
this.currentPhase === MissionPhase.BattleEnd) {
|
||||||
if (this.PhaseTime.update(dt)) {
|
if (this.PhaseTime.update(dt)) {
|
||||||
@@ -260,6 +272,39 @@ export class MissionComp extends CCComp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======================== 波次倒计时 ========================
|
||||||
|
|
||||||
|
/** 进入波次间倒计时:重置倒计时并显示提示 */
|
||||||
|
private startWaveCountdown() {
|
||||||
|
this.waveCountdown = this.WAVE_COUNTDOWN_DURATION;
|
||||||
|
this.isWaveCountdown = true;
|
||||||
|
this.updateCountdownUI(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新倒计时 UI(显示剩余秒数) */
|
||||||
|
private updateCountdownUI(force: boolean = false) {
|
||||||
|
if (!this.isWaveCountdown) return;
|
||||||
|
const remain = Math.max(0, Math.ceil(this.waveCountdown));
|
||||||
|
if (!force && remain === this.lastTimeSecond) return;
|
||||||
|
this.lastTimeSecond = remain;
|
||||||
|
|
||||||
|
if (this.time_node && this.time_node.isValid) {
|
||||||
|
const phaseNode = this.time_node.getChildByPath("Phase/Label");
|
||||||
|
if (phaseNode) {
|
||||||
|
const label = phaseNode.getComponent(Label);
|
||||||
|
if (label) {
|
||||||
|
label.string = `下一波 ${remain}s`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 结束波次间倒计时 */
|
||||||
|
private stopWaveCountdown() {
|
||||||
|
this.isWaveCountdown = false;
|
||||||
|
this.lastTimeSecond = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// ======================== 奖励与广告 ========================
|
// ======================== 奖励与广告 ========================
|
||||||
|
|
||||||
/** 奖励发放(预留) */
|
/** 奖励发放(预留) */
|
||||||
@@ -307,7 +352,7 @@ export class MissionComp extends CCComp {
|
|||||||
loading.active = false
|
loading.active = false
|
||||||
}, 0.5)
|
}, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 播放战斗背景音乐,并稍微降低音量
|
// 播放战斗背景音乐,并稍微降低音量
|
||||||
oops.audio.volumeMusic = 0.5;
|
oops.audio.volumeMusic = 0.5;
|
||||||
oops.audio.playerMusicLoop("music/BATTLE");
|
oops.audio.playerMusicLoop("music/BATTLE");
|
||||||
@@ -425,7 +470,13 @@ export class MissionComp extends CCComp {
|
|||||||
smc.mission.in_fight = false;
|
smc.mission.in_fight = false;
|
||||||
smc.vmdata.mission_data.in_fight = false;
|
smc.vmdata.mission_data.in_fight = false;
|
||||||
smc.mission.stop_spawn_mon = true;
|
smc.mission.stop_spawn_mon = true;
|
||||||
// 不隐藏开始按钮,点击事件在 onStartFightBtnClick 内部做了阶段拦截
|
// 波次间倒计时:隐藏开始按钮(nobg 激活表示不可点击状态)
|
||||||
|
if (this.start_btn && this.start_btn.isValid) {
|
||||||
|
const nobg = this.start_btn.getChildByName("nobg");
|
||||||
|
if (nobg) nobg.active = true;
|
||||||
|
}
|
||||||
|
// 启动 5 秒倒计时,由 update 驱动自动进入下一波
|
||||||
|
this.startWaveCountdown();
|
||||||
oops.message.dispatchEvent("PhasePrepareStart");
|
oops.message.dispatchEvent("PhasePrepareStart");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -526,7 +577,9 @@ export class MissionComp extends CCComp {
|
|||||||
private autoNextPhase() {
|
private autoNextPhase() {
|
||||||
switch (this.currentPhase) {
|
switch (this.currentPhase) {
|
||||||
case MissionPhase.PrepareStart:
|
case MissionPhase.PrepareStart:
|
||||||
this.changePhase(MissionPhase.Prepare);
|
// 波次间倒计时结束,停止倒计时状态,直接进入 PrepareEnd(不再等待玩家点击)
|
||||||
|
this.stopWaveCountdown();
|
||||||
|
this.changePhase(MissionPhase.PrepareEnd);
|
||||||
break;
|
break;
|
||||||
case MissionPhase.PrepareEnd:
|
case MissionPhase.PrepareEnd:
|
||||||
this.changePhase(MissionPhase.BattleStart);
|
this.changePhase(MissionPhase.BattleStart);
|
||||||
@@ -643,7 +696,7 @@ export class MissionComp extends CCComp {
|
|||||||
if (!smc.mission.play) return;
|
if (!smc.mission.play) return;
|
||||||
if (smc.mission.pause) return;
|
if (smc.mission.pause) return;
|
||||||
if (this.currentPhase !== MissionPhase.Prepare) return;
|
if (this.currentPhase !== MissionPhase.Prepare) return;
|
||||||
|
|
||||||
oops.audio.playEffect("music/button");
|
oops.audio.playEffect("music/button");
|
||||||
this.to_fight();
|
this.to_fight();
|
||||||
}
|
}
|
||||||
@@ -728,7 +781,6 @@ export class MissionComp extends CCComp {
|
|||||||
this.heapTrendTimer = 0;
|
this.heapTrendTimer = 0;
|
||||||
this.heapTrendBaseMB = -1;
|
this.heapTrendBaseMB = -1;
|
||||||
this.monsterCountSyncTimer = 0;
|
this.monsterCountSyncTimer = 0;
|
||||||
this.lastPrepareCoinWave = 0;
|
|
||||||
|
|
||||||
spawningEngine.reset();
|
spawningEngine.reset();
|
||||||
|
|
||||||
@@ -746,8 +798,9 @@ export class MissionComp extends CCComp {
|
|||||||
* 新一波事件回调:
|
* 新一波事件回调:
|
||||||
* 1. 进入准备阶段。
|
* 1. 进入准备阶段。
|
||||||
* 2. 更新当前波数。
|
* 2. 更新当前波数。
|
||||||
* 3. 发放本波金币奖励。
|
* 3. 刷新时间显示。
|
||||||
* 4. 刷新时间显示。
|
*
|
||||||
|
* 注意:金币不再按波次固定发放,改为怪物死亡时掉落(见 HeroAtkSystem)。
|
||||||
*
|
*
|
||||||
* @param event 事件名
|
* @param event 事件名
|
||||||
* @param data { wave: number }
|
* @param data { wave: number }
|
||||||
@@ -772,7 +825,7 @@ export class MissionComp extends CCComp {
|
|||||||
|
|
||||||
this.currentWave = wave;
|
this.currentWave = wave;
|
||||||
smc.vmdata.mission_data.level = wave;
|
smc.vmdata.mission_data.level = wave;
|
||||||
this.grantPrepareCoinByWave(wave);
|
// 金币改为怪物死亡掉落(见 HeroAtkSystem),不再每波固定发放
|
||||||
this.lastTimeSecond = -1;
|
this.lastTimeSecond = -1;
|
||||||
this.clearTime = 0;
|
this.clearTime = 0;
|
||||||
this.update_time();
|
this.update_time();
|
||||||
@@ -794,26 +847,6 @@ export class MissionComp extends CCComp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 按波数发放固定金币奖励(外加技能加成)。
|
|
||||||
* 第1波不发放(使用初始金币),从第2波起发放。
|
|
||||||
* 仅在波数首次到达时发放,防止重复。
|
|
||||||
*
|
|
||||||
* @param wave 当前波数
|
|
||||||
*/
|
|
||||||
private grantPrepareCoinByWave(wave: number) {
|
|
||||||
if (wave <= 1) return;
|
|
||||||
if (wave <= this.lastPrepareCoinWave) return;
|
|
||||||
|
|
||||||
// 波次金币公式: baseReward + (wave-1) * waveGrow,且不超过 prepareMaxCoinReward
|
|
||||||
const calculatedReward = FightSet.WAVE_COIN_BASE + (wave - 1) * FightSet.WAVE_COIN_GROW;
|
|
||||||
const waveReward = Math.min(FightSet.WAVE_COIN_MAX, calculatedReward);
|
|
||||||
const reward = MissionEconomy.executeWaveGold(waveReward);
|
|
||||||
|
|
||||||
this.lastPrepareCoinWave = wave;
|
|
||||||
mLogger.log(this.debugMode, "MissionComp", "grantPrepareCoinByWave", { wave, waveReward, reward, coin: smc.vmdata.mission_data.coin });
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======================== 怪物数量管理 ========================
|
// ======================== 怪物数量管理 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ import { FacSet } from "../common/config/GameSet";
|
|||||||
* 待 HeroAttrsComp 升级后可启用 cd_mul / regen 扩展点。
|
* 待 HeroAttrsComp 升级后可启用 cd_mul / regen 扩展点。
|
||||||
*/
|
*/
|
||||||
export enum MonsterElite {
|
export enum MonsterElite {
|
||||||
Elite = 0, // 精英:全面强化
|
Elite = 0, // 精英:全面强化
|
||||||
Berserk = 1, // 狂暴:高攻低血
|
Berserk = 1, // 狂暴:高攻低血
|
||||||
Shield = 2, // 护盾:坦克
|
Shield = 2, // 护盾:坦克
|
||||||
Regen = 3, // 再生:高血偏弱攻
|
Regen = 3, // 再生:高血偏弱攻
|
||||||
Swift = 4, // 疾风:平衡偏输出
|
Swift = 4, // 疾风:平衡偏输出
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 个体强化效果定义 */
|
/** 个体强化效果定义 */
|
||||||
@@ -36,11 +36,11 @@ export const MonsterEliteSet: Record<MonsterElite, {
|
|||||||
hp_mul: number;
|
hp_mul: number;
|
||||||
ap_mul: number;
|
ap_mul: number;
|
||||||
}> = {
|
}> = {
|
||||||
[MonsterElite.Elite]: { name: "精英", hp_mul: 1.5, ap_mul: 1.30 },
|
[MonsterElite.Elite]: { name: "精英", hp_mul: 1.5, ap_mul: 1.30 },
|
||||||
[MonsterElite.Berserk]: { name: "狂暴", hp_mul: 0.9, ap_mul: 1.60 },
|
[MonsterElite.Berserk]: { name: "狂暴", hp_mul: 0.9, ap_mul: 1.60 },
|
||||||
[MonsterElite.Shield]: { name: "护盾", hp_mul: 1.8, ap_mul: 0.80 },
|
[MonsterElite.Shield]: { name: "护盾", hp_mul: 1.8, ap_mul: 0.80 },
|
||||||
[MonsterElite.Regen]: { name: "再生", hp_mul: 1.3, ap_mul: 0.95 },
|
[MonsterElite.Regen]: { name: "再生", hp_mul: 1.3, ap_mul: 0.95 },
|
||||||
[MonsterElite.Swift]: { name: "疾风", hp_mul: 1.0, ap_mul: 1.15 },
|
[MonsterElite.Swift]: { name: "疾风", hp_mul: 1.0, ap_mul: 1.15 },
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,16 +49,16 @@ export const MonsterEliteSet: Record<MonsterElite, {
|
|||||||
* 新代码请使用 MonsterElite。
|
* 新代码请使用 MonsterElite。
|
||||||
*/
|
*/
|
||||||
export enum AffixType {
|
export enum AffixType {
|
||||||
Elite = 0,
|
Elite = 0,
|
||||||
Berserk = 1,
|
Berserk = 1,
|
||||||
Shield = 2,
|
Shield = 2,
|
||||||
Regen = 3,
|
Regen = 3,
|
||||||
Swift = 4,
|
Swift = 4,
|
||||||
Giant = 5,
|
Giant = 5,
|
||||||
Chain = 6,
|
Chain = 6,
|
||||||
SummonerA = 7,
|
SummonerA = 7,
|
||||||
CritRes = 8,
|
CritRes = 8,
|
||||||
FreezeRes = 9,
|
FreezeRes = 9,
|
||||||
KnockbackRes = 10,
|
KnockbackRes = 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +76,40 @@ for (const key in HeroInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======================== 2.5 怪物金币掉落配置 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 怪物金币掉落配置(按 MonType 分类)
|
||||||
|
* - base: 普通怪物死亡时掉落的固定金币数
|
||||||
|
* - boss: Boss 怪物死亡时掉落的固定金币数(仅对 Boss 类型生效)
|
||||||
|
*
|
||||||
|
* 设计说明:
|
||||||
|
* 金币不再按波次固定发放,改为每只怪物死亡时掉落。
|
||||||
|
* Boss 提供高额固定金币奖励,作为战斗收益的核心来源。
|
||||||
|
*/
|
||||||
|
export const MonsterGoldSet: Record<number, { base: number; boss: number }> = {
|
||||||
|
[MonType.Melee]: { base: 1, boss: 0 },
|
||||||
|
[MonType.Heavy]: { base: 2, boss: 0 },
|
||||||
|
[MonType.Long]: { base: 2, boss: 0 },
|
||||||
|
[MonType.Support]: { base: 3, boss: 0 },
|
||||||
|
[MonType.Summoner]: { base: 3, boss: 0 },
|
||||||
|
[MonType.Assassin]: { base: 3, boss: 0 },
|
||||||
|
[MonType.MeleeBoss]: { base: 0, boss: 15 },
|
||||||
|
[MonType.LongBoss]: { base: 0, boss: 15 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定怪物类型的金币掉落数量
|
||||||
|
* @param monType 怪物类型(MonType)
|
||||||
|
* @param isBoss 是否为 Boss
|
||||||
|
* @returns 掉落的金币数量(≥0)
|
||||||
|
*/
|
||||||
|
export function getMonsterGoldDrop(monType: number, isBoss: boolean): number {
|
||||||
|
const cfg = MonsterGoldSet[monType];
|
||||||
|
if (!cfg) return 0;
|
||||||
|
return Math.max(0, Math.floor(isBoss ? cfg.boss : cfg.base));
|
||||||
|
}
|
||||||
|
|
||||||
// ======================== 3. 波次级强化库 ========================
|
// ======================== 3. 波次级强化库 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,11 +129,11 @@ export interface WaveEnchant {
|
|||||||
|
|
||||||
/** 波次强化库(硬编码) */
|
/** 波次强化库(硬编码) */
|
||||||
export const WaveEnchantLibrary: Record<string, WaveEnchant> = {
|
export const WaveEnchantLibrary: Record<string, WaveEnchant> = {
|
||||||
frenzy: { id: "frenzy", name: "狂热浪潮", desc: "全员攻击力+25%", ap_mul: 1.25, weight: 10 },
|
frenzy: { id: "frenzy", name: "狂热浪潮", desc: "全员攻击力+25%", ap_mul: 1.25, weight: 10 },
|
||||||
ironhide: { id: "ironhide", name: "铁皮大军", desc: "全员生命+40%", hp_mul: 1.40, weight: 10 },
|
ironhide: { id: "ironhide", name: "铁皮大军", desc: "全员生命+40%", hp_mul: 1.40, weight: 10 },
|
||||||
swift: { id: "swift", name: "疾风突袭", desc: "全员攻速+20%", cd_mul: 0.80, weight: 8 },
|
swift: { id: "swift", name: "疾风突袭", desc: "全员攻速+20%", cd_mul: 0.80, weight: 8 },
|
||||||
nightmare: { id: "nightmare", name: "梦魇来袭", desc: "个体强化率×2,HP+15%", elite_rate_mul: 2.0, hp_mul: 1.15, weight: 5 },
|
nightmare: { id: "nightmare", name: "梦魇来袭", desc: "个体强化率×2,HP+15%", elite_rate_mul: 2.0, hp_mul: 1.15, weight: 5 },
|
||||||
fortress: { id: "fortress", name: "钢铁堡垒", desc: "全员 HP+60% AP-15%", hp_mul: 1.60, ap_mul: 0.85, weight: 6 },
|
fortress: { id: "fortress", name: "钢铁堡垒", desc: "全员 HP+60% AP-15%", hp_mul: 1.60, ap_mul: 0.85, weight: 6 },
|
||||||
};
|
};
|
||||||
|
|
||||||
// ======================== 4. 小队模板库 ========================
|
// ======================== 4. 小队模板库 ========================
|
||||||
@@ -120,12 +154,12 @@ export interface SquadConfig {
|
|||||||
|
|
||||||
/** 小队模板库(硬编码) */
|
/** 小队模板库(硬编码) */
|
||||||
export const SquadLibrary: Record<string, SquadConfig> = {
|
export const SquadLibrary: Record<string, SquadConfig> = {
|
||||||
melee_grunt: { id: "melee_grunt", name: "近战步兵组", weight: 10, slots: [{ type: MonType.Melee, count: 3 }] },
|
melee_grunt: { id: "melee_grunt", name: "近战步兵组", weight: 10, slots: [{ type: MonType.Melee, count: 3 }] },
|
||||||
assassin_squad: { id: "assassin_squad", name: "刺客突袭组", weight: 6, slots: [{ type: MonType.Assassin, count: 2 }, { type: MonType.Support, count: 1 }] },
|
assassin_squad: { id: "assassin_squad", name: "刺客突袭组", weight: 6, slots: [{ type: MonType.Assassin, count: 2 }, { type: MonType.Support, count: 1 }] },
|
||||||
mixed_balanced: { id: "mixed_balanced", name: "平衡混合组", weight: 8, slots: [{ type: MonType.Melee, count: 1 }, { type: MonType.Long, count: 1 }, { type: MonType.Heavy, count: 1 }] },
|
mixed_balanced: { id: "mixed_balanced", name: "平衡混合组", weight: 8, slots: [{ type: MonType.Melee, count: 1 }, { type: MonType.Long, count: 1 }, { type: MonType.Heavy, count: 1 }] },
|
||||||
long_line: { id: "long_line", name: "远程线列组", weight: 7, slots: [{ type: MonType.Long, count: 2 }, { type: MonType.Support, count: 1 }] },
|
long_line: { id: "long_line", name: "远程线列组", weight: 7, slots: [{ type: MonType.Long, count: 2 }, { type: MonType.Support, count: 1 }] },
|
||||||
heavy_shield: { id: "heavy_shield", name: "重盾堡垒组", weight: 5, slots: [{ type: MonType.Heavy, count: 1 }, { type: MonType.Melee, count: 2 }] },
|
heavy_shield: { id: "heavy_shield", name: "重盾堡垒组", weight: 5, slots: [{ type: MonType.Heavy, count: 1 }, { type: MonType.Melee, count: 2 }] },
|
||||||
summoner_cult: { id: "summoner_cult", name: "召唤教派组", weight: 4, slots: [{ type: MonType.Summoner, count: 1 }, { type: MonType.Long, count: 2 }] },
|
summoner_cult: { id: "summoner_cult", name: "召唤教派组", weight: 4, slots: [{ type: MonType.Summoner, count: 1 }, { type: MonType.Long, count: 2 }] },
|
||||||
};
|
};
|
||||||
|
|
||||||
// ======================== 5. 波次配置表 ========================
|
// ======================== 5. 波次配置表 ========================
|
||||||
@@ -152,43 +186,43 @@ export interface WaveConfig {
|
|||||||
*/
|
*/
|
||||||
export const WaveConfigs: Record<number, WaveConfig> = {
|
export const WaveConfigs: Record<number, WaveConfig> = {
|
||||||
// ===== 教学期 =====
|
// ===== 教学期 =====
|
||||||
1: { base_count: 3, squad_pool: ["melee_grunt"] },
|
1: { base_count: 3, squad_pool: ["melee_grunt"] },
|
||||||
2: { base_count: 4, squad_pool: ["melee_grunt", "mixed_balanced"] },
|
2: { base_count: 4, squad_pool: ["melee_grunt", "mixed_balanced"] },
|
||||||
3: { base_count: 5, squad_pool: ["melee_grunt", "mixed_balanced", "long_line"] },
|
3: { base_count: 5, squad_pool: ["melee_grunt", "mixed_balanced", "long_line"] },
|
||||||
4: { base_count: 6, squad_pool: ["mixed_balanced", "long_line", "heavy_shield"] },
|
4: { base_count: 6, squad_pool: ["mixed_balanced", "long_line", "heavy_shield"] },
|
||||||
// ===== 第一 Boss =====
|
// ===== 第一 Boss =====
|
||||||
5: { base_count: 7, squad_pool: ["melee_grunt", "mixed_balanced", "heavy_shield"], boss_wave: true },
|
5: { base_count: 7, squad_pool: ["melee_grunt", "mixed_balanced", "heavy_shield"], boss_wave: true },
|
||||||
// ===== 引入波次强化 =====
|
// ===== 引入波次强化 =====
|
||||||
6: { base_count: 7, squad_pool: ["assassin_squad", "long_line", "mixed_balanced"], enchant_pool: ["frenzy"], elite_base_rate: 0.10 },
|
6: { base_count: 7, squad_pool: ["assassin_squad", "long_line", "mixed_balanced"], enchant_pool: ["frenzy"], elite_base_rate: 0.10 },
|
||||||
7: { base_count: 8, squad_pool: ["melee_grunt", "heavy_shield", "long_line"], enchant_pool: ["ironhide"], elite_base_rate: 0.12 },
|
7: { base_count: 8, squad_pool: ["melee_grunt", "heavy_shield", "long_line"], enchant_pool: ["ironhide"], elite_base_rate: 0.12 },
|
||||||
8: { base_count: 8, squad_pool: ["assassin_squad", "mixed_balanced", "summoner_cult"], enchant_pool: ["frenzy", "swift"], elite_base_rate: 0.14 },
|
8: { base_count: 8, squad_pool: ["assassin_squad", "mixed_balanced", "summoner_cult"], enchant_pool: ["frenzy", "swift"], elite_base_rate: 0.14 },
|
||||||
9: { base_count: 9, squad_pool: ["heavy_shield", "long_line", "mixed_balanced"], enchant_pool: ["ironhide", "frenzy"],elite_base_rate: 0.16 },
|
9: { base_count: 9, squad_pool: ["heavy_shield", "long_line", "mixed_balanced"], enchant_pool: ["ironhide", "frenzy"], elite_base_rate: 0.16 },
|
||||||
// ===== 第二 Boss =====
|
// ===== 第二 Boss =====
|
||||||
10: { base_count: 10, squad_pool: ["melee_grunt", "assassin_squad", "mixed_balanced"], enchant_pool: ["frenzy", "ironhide"],boss_wave: true, elite_base_rate: 0.18 },
|
10: { base_count: 10, squad_pool: ["melee_grunt", "assassin_squad", "mixed_balanced"], enchant_pool: ["frenzy", "ironhide"], boss_wave: true, elite_base_rate: 0.18 },
|
||||||
// ===== 组合多样化 =====
|
// ===== 组合多样化 =====
|
||||||
11: { base_count: 10, squad_pool: ["assassin_squad", "long_line", "summoner_cult", "mixed_balanced"],enchant_pool: ["swift", "frenzy"], elite_base_rate: 0.18 },
|
11: { base_count: 10, squad_pool: ["assassin_squad", "long_line", "summoner_cult", "mixed_balanced"], enchant_pool: ["swift", "frenzy"], elite_base_rate: 0.18 },
|
||||||
12: { base_count: 10, squad_pool: ["heavy_shield", "melee_grunt", "mixed_balanced", "long_line"], enchant_pool: ["ironhide"], elite_base_rate: 0.20 },
|
12: { base_count: 10, squad_pool: ["heavy_shield", "melee_grunt", "mixed_balanced", "long_line"], enchant_pool: ["ironhide"], elite_base_rate: 0.20 },
|
||||||
13: { base_count: 11, squad_pool: ["assassin_squad", "summoner_cult", "mixed_balanced"], enchant_pool: ["frenzy", "nightmare"], elite_base_rate: 0.22 },
|
13: { base_count: 11, squad_pool: ["assassin_squad", "summoner_cult", "mixed_balanced"], enchant_pool: ["frenzy", "nightmare"], elite_base_rate: 0.22 },
|
||||||
14: { base_count: 11, squad_pool: ["heavy_shield", "long_line", "melee_grunt"], enchant_pool: ["ironhide", "swift"], elite_base_rate: 0.24 },
|
14: { base_count: 11, squad_pool: ["heavy_shield", "long_line", "melee_grunt"], enchant_pool: ["ironhide", "swift"], elite_base_rate: 0.24 },
|
||||||
// ===== 第三 Boss(中期高潮) =====
|
// ===== 第三 Boss(中期高潮) =====
|
||||||
15: { base_count: 11, squad_pool: ["assassin_squad", "mixed_balanced", "summoner_cult"], enchant_pool: ["nightmare", "fortress"], boss_wave: true, elite_base_rate: 0.25 },
|
15: { base_count: 11, squad_pool: ["assassin_squad", "mixed_balanced", "summoner_cult"], enchant_pool: ["nightmare", "fortress"], boss_wave: true, elite_base_rate: 0.25 },
|
||||||
// ===== 高压阶段 =====
|
// ===== 高压阶段 =====
|
||||||
16: { base_count: 11, squad_pool: ["assassin_squad", "heavy_shield", "long_line"], enchant_pool: ["frenzy", "swift"], elite_base_rate: 0.26 },
|
16: { base_count: 11, squad_pool: ["assassin_squad", "heavy_shield", "long_line"], enchant_pool: ["frenzy", "swift"], elite_base_rate: 0.26 },
|
||||||
17: { base_count: 11, squad_pool: ["melee_grunt", "summoner_cult", "mixed_balanced"], enchant_pool: ["ironhide", "frenzy"], elite_base_rate: 0.27 },
|
17: { base_count: 11, squad_pool: ["melee_grunt", "summoner_cult", "mixed_balanced"], enchant_pool: ["ironhide", "frenzy"], elite_base_rate: 0.27 },
|
||||||
18: { base_count: 12, squad_pool: ["assassin_squad", "long_line", "heavy_shield"], enchant_pool: ["nightmare", "swift"], elite_base_rate: 0.28 },
|
18: { base_count: 12, squad_pool: ["assassin_squad", "long_line", "heavy_shield"], enchant_pool: ["nightmare", "swift"], elite_base_rate: 0.28 },
|
||||||
19: { base_count: 12, squad_pool: ["mixed_balanced", "summoner_cult", "melee_grunt"], enchant_pool: ["fortress"], elite_base_rate: 0.28 },
|
19: { base_count: 12, squad_pool: ["mixed_balanced", "summoner_cult", "melee_grunt"], enchant_pool: ["fortress"], elite_base_rate: 0.28 },
|
||||||
20: { base_count: 12, squad_pool: ["assassin_squad", "heavy_shield", "long_line"], enchant_pool: ["frenzy", "ironhide"], boss_wave: true, elite_base_rate: 0.30 },
|
20: { base_count: 12, squad_pool: ["assassin_squad", "heavy_shield", "long_line"], enchant_pool: ["frenzy", "ironhide"], boss_wave: true, elite_base_rate: 0.30 },
|
||||||
21: { base_count: 12, squad_pool: ["melee_grunt", "assassin_squad", "summoner_cult"], enchant_pool: ["swift", "nightmare"], elite_base_rate: 0.30 },
|
21: { base_count: 12, squad_pool: ["melee_grunt", "assassin_squad", "summoner_cult"], enchant_pool: ["swift", "nightmare"], elite_base_rate: 0.30 },
|
||||||
22: { base_count: 12, squad_pool: ["heavy_shield", "long_line", "mixed_balanced"], enchant_pool: ["ironhide", "fortress"], elite_base_rate: 0.30 },
|
22: { base_count: 12, squad_pool: ["heavy_shield", "long_line", "mixed_balanced"], enchant_pool: ["ironhide", "fortress"], elite_base_rate: 0.30 },
|
||||||
23: { base_count: 12, squad_pool: ["assassin_squad", "summoner_cult", "melee_grunt"], enchant_pool: ["frenzy", "nightmare"], elite_base_rate: 0.32 },
|
23: { base_count: 12, squad_pool: ["assassin_squad", "summoner_cult", "melee_grunt"], enchant_pool: ["frenzy", "nightmare"], elite_base_rate: 0.32 },
|
||||||
24: { base_count: 12, squad_pool: ["mixed_balanced", "heavy_shield", "long_line"], enchant_pool: ["fortress", "swift"], elite_base_rate: 0.32 },
|
24: { base_count: 12, squad_pool: ["mixed_balanced", "heavy_shield", "long_line"], enchant_pool: ["fortress", "swift"], elite_base_rate: 0.32 },
|
||||||
// ===== 第四 Boss =====
|
// ===== 第四 Boss =====
|
||||||
25: { base_count: 12, squad_pool: ["assassin_squad", "summoner_cult", "heavy_shield"], enchant_pool: ["nightmare", "fortress"], boss_wave: true, elite_base_rate: 0.35 },
|
25: { base_count: 12, squad_pool: ["assassin_squad", "summoner_cult", "heavy_shield"], enchant_pool: ["nightmare", "fortress"], boss_wave: true, elite_base_rate: 0.35 },
|
||||||
// ===== 终极阶段 =====
|
// ===== 终极阶段 =====
|
||||||
26: { base_count: 12, squad_pool: ["melee_grunt", "assassin_squad", "long_line", "summoner_cult"], enchant_pool: ["frenzy", "nightmare"], elite_base_rate: 0.35 },
|
26: { base_count: 12, squad_pool: ["melee_grunt", "assassin_squad", "long_line", "summoner_cult"], enchant_pool: ["frenzy", "nightmare"], elite_base_rate: 0.35 },
|
||||||
27: { base_count: 12, squad_pool: ["heavy_shield", "mixed_balanced", "summoner_cult"], enchant_pool: ["ironhide", "fortress"], elite_base_rate: 0.38 },
|
27: { base_count: 12, squad_pool: ["heavy_shield", "mixed_balanced", "summoner_cult"], enchant_pool: ["ironhide", "fortress"], elite_base_rate: 0.38 },
|
||||||
28: { base_count: 12, squad_pool: ["assassin_squad", "long_line", "melee_grunt"], enchant_pool: ["swift", "nightmare"], elite_base_rate: 0.40 },
|
28: { base_count: 12, squad_pool: ["assassin_squad", "long_line", "melee_grunt"], enchant_pool: ["swift", "nightmare"], elite_base_rate: 0.40 },
|
||||||
29: { base_count: 12, squad_pool: ["mixed_balanced", "heavy_shield", "summoner_cult"], enchant_pool: ["frenzy", "ironhide", "fortress"], elite_base_rate: 0.42 },
|
29: { base_count: 12, squad_pool: ["mixed_balanced", "heavy_shield", "summoner_cult"], enchant_pool: ["frenzy", "ironhide", "fortress"], elite_base_rate: 0.42 },
|
||||||
// ===== 最终 Boss =====
|
// ===== 最终 Boss =====
|
||||||
30: { base_count: 12, squad_pool: ["assassin_squad", "heavy_shield", "summoner_cult", "mixed_balanced"], enchant_pool: ["frenzy", "ironhide", "swift", "nightmare", "fortress"], boss_wave: true, elite_base_rate: 0.45 },
|
30: { base_count: 12, squad_pool: ["assassin_squad", "heavy_shield", "summoner_cult", "mixed_balanced"], enchant_pool: ["frenzy", "ironhide", "swift", "nightmare", "fortress"], boss_wave: true, elite_base_rate: 0.45 },
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user