refactor: 重构关卡战斗流程与AI逻辑,改为单场Boss战模式
1. 近战AI新增警戒线机制,无威胁时退回编队 2. 替换多回合波次为单场Boss战,简化刷怪与回合逻辑 3. 重构招募系统为开局免费三选一流程 4. 调整英雄与怪物基础属性平衡 5. 精简HUD与配置文件冗余代码
This commit is contained in:
@@ -192,13 +192,10 @@ export class MissionCardComp extends CCComp {
|
||||
private itemCardComps: ItemListComp[] = [];
|
||||
/** 是否已召唤过英雄(用于控制其他面板按钮是否可点击) */
|
||||
private hasCalledHero: boolean = false;
|
||||
/**
|
||||
* 招募计数:每次点击招募 +1。
|
||||
* 节奏为「每 4 次一循环」:前 3 次直接随机召唤,第 4 次走三选一。
|
||||
*/
|
||||
private recruitCount: number = 0;
|
||||
/** 全远程阵容时近战卡的权重放大倍数(近战保底) */
|
||||
private static readonly MELEE_PITY_WEIGHT_MUL = 3;
|
||||
/** 开局免费连续三选一的总次数 */
|
||||
private static readonly OPENING_FREE_PICK_COUNT = 3;
|
||||
/** 开局免费三选一剩余次数(>0 时招募不扣费,选完 1 张自动弹出下一组) */
|
||||
private openingFreePicks: number = 0;
|
||||
|
||||
// ======================== 面板划出状态 ========================
|
||||
|
||||
@@ -362,6 +359,10 @@ export class MissionCardComp extends CCComp {
|
||||
// 暂不启用 herobox 同步显示英雄信息(后续可能恢复),保留代码备查
|
||||
// this.refreshHeroBoxSlots();
|
||||
|
||||
// 重置开局免费三选一次数,随后立即弹出第 1 组三选一
|
||||
this.openingFreePicks = MissionCardComp.OPENING_FREE_PICK_COUNT;
|
||||
this.showCardsPanel();
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "mission start");
|
||||
}
|
||||
|
||||
@@ -876,8 +877,18 @@ export class MissionCardComp extends CCComp {
|
||||
if (heroIdx >= 0) {
|
||||
const cards = this.buildDrawCards();
|
||||
this.dispatchCardsToSlots(cards);
|
||||
// 抽 1 张后关闭英雄卡池面板
|
||||
this.hideCardsPanel();
|
||||
// 开局免费三选一次数未用完时保持面板展开,连续弹出下一组三选一
|
||||
if (this.openingFreePicks > 0) {
|
||||
this.openingFreePicks--;
|
||||
if (this.openingFreePicks <= 0) {
|
||||
// 最后一组选完,免费次数耗尽,收起面板并通知自动进入战斗
|
||||
this.hideCardsPanel();
|
||||
oops.message.dispatchEvent("OpeningPicksDone");
|
||||
}
|
||||
} else {
|
||||
// 抽 1 张后关闭英雄卡池面板
|
||||
this.hideCardsPanel();
|
||||
}
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "refresh hero pool after buy", {
|
||||
triggerSlot: heroIdx
|
||||
});
|
||||
@@ -1226,40 +1237,22 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 英雄招募按钮(每 4 次一循环):
|
||||
* - 前 3 次:扣面板开启费后直接随机召唤 1 个英雄(不开面板,无需玩家选)。
|
||||
* - 第 4 次:扣费后展开三选一面板(缩放 0→1),玩家选 1。
|
||||
* 英雄招募按钮:每次都展开三选一面板,玩家选 1;
|
||||
* 开局免费三选一次数未用完时不扣面板开启费。
|
||||
* 满员(HERO_MAX_NUM)时气泡提示并拦截,不扣费、不计数。
|
||||
*/
|
||||
private onShowHerosClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
// 满员拦截:随机招募与三选一都禁止
|
||||
// 满员拦截
|
||||
if (this.getAliveHeroCount() >= this.getMissionHeroMaxNum()) {
|
||||
this.showSmallTip("panel_hero_full", "英雄槽已满");
|
||||
return;
|
||||
}
|
||||
// 随机招募与三选一都需支付面板开启费,金币不足则提示并中止
|
||||
if (!this.tryPayPanelOpenCost(0)) return;
|
||||
|
||||
// 先计数,再按节奏分支:count % 4 == 0 展开三选一,其余直接随机召唤
|
||||
this.recruitCount++;
|
||||
const isPickOneOfThree = (this.recruitCount % 4) === 0;
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "hero recruit", {
|
||||
recruitCount: this.recruitCount,
|
||||
mode: isPickOneOfThree ? "pick_one_of_three" : "random_summon"
|
||||
});
|
||||
|
||||
// 收起装备/技能/药品覆盖面板,回到英雄面板
|
||||
// 开局免费次数内不扣面板开启费
|
||||
if (this.openingFreePicks <= 0 && !this.tryPayPanelOpenCost(0)) return;
|
||||
this.slideToPanel(0);
|
||||
|
||||
if (isPickOneOfThree) {
|
||||
// 第 4 次:展开英雄抽卡面板(缩放 0→1,点亮按钮 active 高亮)
|
||||
this.showCardsPanel();
|
||||
} else {
|
||||
// 前 3 次:直接随机召唤,不开面板
|
||||
this.randomSummonOne();
|
||||
}
|
||||
this.showCardsPanel();
|
||||
}
|
||||
|
||||
// ======================== 英雄信息盒(herosBoxNode) ========================
|
||||
@@ -1812,60 +1805,11 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接随机召唤 1 个英雄:
|
||||
* - 全英雄卡池按权重随机(不过滤已召唤英雄,配合二合一合成)。
|
||||
* - 近战保底:场上已有英雄且全部为远程/中程(无近战)时,近战卡权重放大。
|
||||
*/
|
||||
private randomSummonOne() {
|
||||
const pool = this.buildRandomSummonPool();
|
||||
if (pool.length === 0) return;
|
||||
const pick = this.weightedPick(pool);
|
||||
if (!pick) return;
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "random summon", { uuid: pick.uuid });
|
||||
oops.message.dispatchEvent(GameEvent.CallHero, {
|
||||
uuid: pick.uuid,
|
||||
hero_lv: 1,
|
||||
card_lv: pick.card_lv ?? 1
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建随机召唤卡池:全英雄卡,必要时按近战保底放大近战权重。
|
||||
* @returns 带运行时权重的卡配置数组(weight 已按保底调整)
|
||||
* 构建随机召唤卡池:全英雄卡。
|
||||
* @returns 卡配置数组
|
||||
*/
|
||||
private buildRandomSummonPool(): CardConfig[] {
|
||||
const heroCards = CardPoolList.filter(c => c.type === CardType.Hero);
|
||||
if (heroCards.length === 0) return [];
|
||||
|
||||
// 全远程判定:有存活英雄但没有任何近战 → 触发放大近战权重
|
||||
const meleeMul = this.isAllRangedFormation()
|
||||
? MissionCardComp.MELEE_PITY_WEIGHT_MUL
|
||||
: 1;
|
||||
if (meleeMul === 1) return heroCards;
|
||||
|
||||
return heroCards.map(c => {
|
||||
const hero = HeroInfo[c.uuid];
|
||||
const isMelee = hero && hero.type === HType.Melee;
|
||||
// 复制一份避免污染卡池原始 weight
|
||||
return isMelee ? { ...c, weight: (c.weight ?? 0) * meleeMul } : c;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前场上是否为「全远程阵容」:
|
||||
* 有至少 1 个存活英雄,且没有任何近战(Melee)英雄。
|
||||
*/
|
||||
private isAllRangedFormation(): boolean {
|
||||
let heroCount = 0;
|
||||
let meleeCount = 0;
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||||
const model = entity.get(HeroAttrsComp);
|
||||
if (!model || model.fac !== FacSet.HERO || model.is_dead) return;
|
||||
heroCount++;
|
||||
if (model.type === HType.Melee) meleeCount++;
|
||||
});
|
||||
return heroCount > 0 && meleeCount === 0;
|
||||
return CardPoolList.filter(c => c.type === CardType.Hero);
|
||||
}
|
||||
|
||||
// ======================== 阶段切换 ========================
|
||||
|
||||
Reference in New Issue
Block a user