From 4d71675c322126e586017e84ae0268400561b756 Mon Sep 17 00:00:00 2001 From: pan Date: Tue, 21 Jul 2026 08:53:10 +0800 Subject: [PATCH] =?UTF-8?q?fix(map):=20=E4=BF=AE=E5=A4=8D=E6=8A=BD?= =?UTF-8?q?=E5=8D=A1=E9=87=8D=E5=A4=8D=E8=8B=B1=E9=9B=84=E5=92=8C=E8=8B=B1?= =?UTF-8?q?=E9=9B=84=E7=AB=99=E4=BD=8D=E5=81=8F=E7=A7=BB=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增英雄卡UUID去重逻辑,避免单次抽卡出现重复英雄 2. 调整英雄初始站位坐标,修正布局偏移问题 3. 统一代码格式,修复缩进和类型注解不规范问题 --- assets/script/game/map/MissionCardComp.ts | 35 ++++++++++--- assets/script/game/map/MissionHeroComp.ts | 60 +++++++++++------------ 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/assets/script/game/map/MissionCardComp.ts b/assets/script/game/map/MissionCardComp.ts index 0c56dae1..0ed1d9c9 100644 --- a/assets/script/game/map/MissionCardComp.ts +++ b/assets/script/game/map/MissionCardComp.ts @@ -98,7 +98,7 @@ export class MissionCardComp extends CCComp { /** 抽卡(刷新)按钮节点 */ @property(Node) cards_chou: Node = null! - + /** 英雄卡牌池cards_node显示隐藏 */ @property(Node) showHeros: Node = null! @@ -768,6 +768,7 @@ export class MissionCardComp extends CCComp { const result: CardConfig[] = []; const usedUpgradeTargets = new Set(); + const usedHeroUuids = new Set(); // 必出 1 张升级卡,按 UUID 升序轮询,放在返回数组首位(对应最左侧卡槽) if (upgradeCards.length > 0) { @@ -778,23 +779,36 @@ export class MissionCardComp extends CCComp { usedUpgradeTargets.add(mandatory.target_hero_eid ?? 0); } - // 剩余张数从混合池抽取,排除已选升级卡 target_hero_eid + // 剩余张数从混合池抽取,排除已选升级卡 target_hero_eid 和已选英雄卡 uuid const remainingCount = 3 - result.length; if (remainingCount > 0) { const remainingPool = mixedPool.filter(c => { if (c.type === CardType.SpecialUpgrade && c.target_hero_eid) { return !usedUpgradeTargets.has(c.target_hero_eid); } + if (c.type === CardType.Hero) { + return !usedHeroUuids.has(c.uuid); + } return true; }); const rest = this.pickMixedCards(remainingPool, remainingCount, upgradeCards); - result.push(...rest); + rest.forEach(c => { + result.push(c); + if (c.type === CardType.SpecialUpgrade && c.target_hero_eid) { + usedUpgradeTargets.add(c.target_hero_eid); + } + if (c.type === CardType.Hero) { + usedHeroUuids.add(c.uuid); + } + }); } - // 兜底:不足 3 张时循环补齐 + // 兜底:不足 3 张时从未使用的英雄卡中补齐,避免出现重复英雄 while (result.length < 3) { - if (mixedPool.length === 0) break; - result.push(mixedPool[result.length % mixedPool.length]); + const fillCard = availableHeroCards.find(c => !usedHeroUuids.has(c.uuid)); + if (!fillCard) break; + result.push(fillCard); + usedHeroUuids.add(fillCard.uuid); } return result.slice(0, 3); } @@ -802,17 +816,22 @@ export class MissionCardComp extends CCComp { /** * 从混合池中按权重抽取 n 张卡。 * 升级卡之间强制 unique(同一个 target_hero_eid 只能出现一次)。 + * 英雄卡之间强制 unique(同一个 uuid 只能出现一次),避免同一次发牌出现重复英雄。 */ private pickMixedCards(pool: CardConfig[], count: number, upgradeCards: CardConfig[]): CardConfig[] { if (pool.length === 0 || count <= 0) return []; const selected: CardConfig[] = []; const usedUpgradeTargets = new Set(); + const usedHeroUuids = new Set(); while (selected.length < count) { const available = pool.filter(c => { if (c.type === CardType.SpecialUpgrade && c.target_hero_eid) { return !usedUpgradeTargets.has(c.target_hero_eid); } + if (c.type === CardType.Hero) { + return !usedHeroUuids.has(c.uuid); + } return true; }); if (available.length === 0) break; @@ -822,6 +841,9 @@ export class MissionCardComp extends CCComp { if (pick.type === CardType.SpecialUpgrade && pick.target_hero_eid) { usedUpgradeTargets.add(pick.target_hero_eid); } + if (pick.type === CardType.Hero) { + usedHeroUuids.add(pick.uuid); + } } return selected; } @@ -907,6 +929,7 @@ export class MissionCardComp extends CCComp { count: 3, type: CardType.Hero, heroType, + unique: true, // 保证一次刷新内的英雄卡不重复 }); // 过滤掉场上已召唤英雄的普通英雄卡(已召唤的英雄不再重复刷出) const aliveHeroUuids = this.getAliveHeroUuids(); diff --git a/assets/script/game/map/MissionHeroComp.ts b/assets/script/game/map/MissionHeroComp.ts index 75ba0add..f119aad4 100644 --- a/assets/script/game/map/MissionHeroComp.ts +++ b/assets/script/game/map/MissionHeroComp.ts @@ -49,8 +49,8 @@ export class MissionHeroComp extends CCComp { /** 硬编码的6个英雄占位点 */ public static readonly HERO_POSITIONS: Vec3[] = [ - v3(-180, BoxSet.GAME_LINE, 0), // index 0 (node_index 1): Top Front 第二 - v3(-80, BoxSet.GAME_LINE, 0), // index 1 (node_index 2): Mid Front 第一 + v3(-200, BoxSet.GAME_LINE, 0), // index 0 (node_index 1): Top Front 第二 + v3(-120, BoxSet.GAME_LINE, 0), // index 1 (node_index 2): Mid Front 第一 v3(-280, BoxSet.GAME_LINE, 0), // index 2 (node_index 3): Bot Front 第三 // v3(-280, BoxSet.GAME_LINE + 100, 0), // index 3 (node_index 4): Top Back // v3(-280, BoxSet.GAME_LINE, 0), // index 4 (node_index 5): Mid Back @@ -63,37 +63,37 @@ export class MissionHeroComp extends CCComp { // ======================== 运行时属性 ======================== /** 预留计时器 */ - timer:Timer=new Timer(2) + timer: Timer = new Timer(2) /** 预留状态:友方是否全部死亡 */ - Friend_is_dead:boolean=false + Friend_is_dead: boolean = false /** 当前处理的英雄 uuid */ - current_hero_uuid:number=0 + current_hero_uuid: number = 0 /** 当前英雄数量缓存 */ - current_hero_num:number=-1 + current_hero_num: number = -1 /** 是否正在消费召唤队列(防止并发) */ - is_processing_queue:boolean=false + is_processing_queue: boolean = false /** 召唤请求队列:保证召唤按顺序串行执行 */ - summon_queue:{ uuid: number; hero_lv: number; pool_lv: number }[]=[] + summon_queue: { uuid: number; hero_lv: number; pool_lv: number }[] = [] /** 预留英雄列表 */ - heros:any=[] + heros: any = [] // ======================== 生命周期 ======================== - onLoad(){ + onLoad() { // 注册节点级事件 - this.on(GameEvent.FightReady,this.fight_ready,this) - this.on(GameEvent.Zhaohuan,this.zhao_huan,this) - this.on(GameEvent.MissionEnd,this.clear_heros,this) + this.on(GameEvent.FightReady, this.fight_ready, this) + this.on(GameEvent.Zhaohuan, this.zhao_huan, this) + this.on(GameEvent.MissionEnd, this.clear_heros, this) // 注册全局消息 - oops.message.on(GameEvent.CallHero,this.call_hero,this) - oops.message.on("PhasePrepareStart",this.fight_ready,this) + oops.message.on(GameEvent.CallHero, this.call_hero, this) + oops.message.on("PhasePrepareStart", this.fight_ready, this) } - onDestroy(){ + onDestroy() { super.onDestroy(); // 清理全部监听 - oops.message.off(GameEvent.CallHero,this.call_hero,this) - oops.message.off("PhasePrepareStart",this.fight_ready,this) + oops.message.off(GameEvent.CallHero, this.call_hero, this) + oops.message.off("PhasePrepareStart", this.fight_ready, this) } start() { @@ -102,7 +102,7 @@ export class MissionHeroComp extends CCComp { // ======================== 事件处理 ======================== /** 关卡结束时清理全部英雄 ECS 实体 */ - clear_heros(){ + clear_heros() { const heroes = this.getAllHeroes(); for (let i = 0; i < heroes.length; i++) { heroes[i].destroy(); @@ -110,7 +110,7 @@ export class MissionHeroComp extends CCComp { } /** 战斗准备阶段:重置出战英雄计数,恢复满血重新登场 */ - fight_ready(){ + fight_ready() { const heroes = this.getAllHeroes(); smc.vmdata.mission_data.hero_num = heroes.length; for (let i = 0; i < heroes.length; i++) { @@ -135,7 +135,7 @@ export class MissionHeroComp extends CCComp { } /** 预留:召唤事件扩展入口 */ - private zhao_huan(event: string, args: any){ + private zhao_huan(event: string, args: any) { } @@ -147,7 +147,7 @@ export class MissionHeroComp extends CCComp { * @param event 事件名 * @param args { uuid, hero_lv, pool_lv } */ - private async call_hero(event: string, args: any){ + private async call_hero(event: string, args: any) { const payload = args ?? event; const uuid = Number(payload?.uuid ?? 1001); const hero_lv = Math.max(1, Number(payload?.hero_lv ?? 1)); @@ -196,7 +196,7 @@ export class MissionHeroComp extends CCComp { const m = h.get(HeroAttrsComp); if (m && m.posIndex >= 0) occupied.add(m.posIndex); } - + // 优先中前(1) -> 上前(0) -> 下前(2) -> 中后(4) -> 上后(3) -> 下后(5) const slotPriority = [1, 0, 2, 4, 3, 5]; for (const idx of slotPriority) { @@ -204,7 +204,7 @@ export class MissionHeroComp extends CCComp { return idx; } } - + // 溢出:默认中前 return 1; } @@ -219,14 +219,14 @@ export class MissionHeroComp extends CCComp { * @param pool_lv 卡池等级(历史遗留,新机制下不再使用) * @returns 创建的 Hero 实体 */ - private addHero(uuid:number=1001,hero_lv:number=1, pool_lv:number=1) { + private addHero(uuid: number = 1001, hero_lv: number = 1, pool_lv: number = 1) { let hero = ecs.getEntity(Hero); let scale = 1 const posIndex = this.pickPositionIndexForHero(); const landingPos = MissionHeroComp.HERO_POSITIONS[posIndex]; - let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0); - hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv,pool_lv,posIndex); - + let spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0); + hero.load(spawnPos, scale, uuid, landingPos.y, hero_lv, pool_lv, posIndex); + // 召唤完成后,派发事件以更新英雄面板 const model = hero.get(HeroAttrsComp); if (model) { @@ -235,7 +235,7 @@ export class MissionHeroComp extends CCComp { model: model }); } - + return hero; } @@ -285,7 +285,7 @@ export class MissionHeroComp extends CCComp { this.addHero(uuid, hero_lv, pool_lv); } - + /** ECS 组件移除时触发(当前不销毁节点,保留引用) */ reset() { // this.node.destroy();