fix(map): 修复抽卡重复英雄和英雄站位偏移问题
1. 新增英雄卡UUID去重逻辑,避免单次抽卡出现重复英雄 2. 调整英雄初始站位坐标,修正布局偏移问题 3. 统一代码格式,修复缩进和类型注解不规范问题
This commit is contained in:
@@ -98,7 +98,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
/** 抽卡(刷新)按钮节点 */
|
/** 抽卡(刷新)按钮节点 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
cards_chou: Node = null!
|
cards_chou: Node = null!
|
||||||
|
|
||||||
/** 英雄卡牌池cards_node显示隐藏 */
|
/** 英雄卡牌池cards_node显示隐藏 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
showHeros: Node = null!
|
showHeros: Node = null!
|
||||||
@@ -768,6 +768,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
|
|
||||||
const result: CardConfig[] = [];
|
const result: CardConfig[] = [];
|
||||||
const usedUpgradeTargets = new Set<number>();
|
const usedUpgradeTargets = new Set<number>();
|
||||||
|
const usedHeroUuids = new Set<number>();
|
||||||
|
|
||||||
// 必出 1 张升级卡,按 UUID 升序轮询,放在返回数组首位(对应最左侧卡槽)
|
// 必出 1 张升级卡,按 UUID 升序轮询,放在返回数组首位(对应最左侧卡槽)
|
||||||
if (upgradeCards.length > 0) {
|
if (upgradeCards.length > 0) {
|
||||||
@@ -778,23 +779,36 @@ export class MissionCardComp extends CCComp {
|
|||||||
usedUpgradeTargets.add(mandatory.target_hero_eid ?? 0);
|
usedUpgradeTargets.add(mandatory.target_hero_eid ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 剩余张数从混合池抽取,排除已选升级卡 target_hero_eid
|
// 剩余张数从混合池抽取,排除已选升级卡 target_hero_eid 和已选英雄卡 uuid
|
||||||
const remainingCount = 3 - result.length;
|
const remainingCount = 3 - result.length;
|
||||||
if (remainingCount > 0) {
|
if (remainingCount > 0) {
|
||||||
const remainingPool = mixedPool.filter(c => {
|
const remainingPool = mixedPool.filter(c => {
|
||||||
if (c.type === CardType.SpecialUpgrade && c.target_hero_eid) {
|
if (c.type === CardType.SpecialUpgrade && c.target_hero_eid) {
|
||||||
return !usedUpgradeTargets.has(c.target_hero_eid);
|
return !usedUpgradeTargets.has(c.target_hero_eid);
|
||||||
}
|
}
|
||||||
|
if (c.type === CardType.Hero) {
|
||||||
|
return !usedHeroUuids.has(c.uuid);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
const rest = this.pickMixedCards(remainingPool, remainingCount, upgradeCards);
|
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) {
|
while (result.length < 3) {
|
||||||
if (mixedPool.length === 0) break;
|
const fillCard = availableHeroCards.find(c => !usedHeroUuids.has(c.uuid));
|
||||||
result.push(mixedPool[result.length % mixedPool.length]);
|
if (!fillCard) break;
|
||||||
|
result.push(fillCard);
|
||||||
|
usedHeroUuids.add(fillCard.uuid);
|
||||||
}
|
}
|
||||||
return result.slice(0, 3);
|
return result.slice(0, 3);
|
||||||
}
|
}
|
||||||
@@ -802,17 +816,22 @@ export class MissionCardComp extends CCComp {
|
|||||||
/**
|
/**
|
||||||
* 从混合池中按权重抽取 n 张卡。
|
* 从混合池中按权重抽取 n 张卡。
|
||||||
* 升级卡之间强制 unique(同一个 target_hero_eid 只能出现一次)。
|
* 升级卡之间强制 unique(同一个 target_hero_eid 只能出现一次)。
|
||||||
|
* 英雄卡之间强制 unique(同一个 uuid 只能出现一次),避免同一次发牌出现重复英雄。
|
||||||
*/
|
*/
|
||||||
private pickMixedCards(pool: CardConfig[], count: number, upgradeCards: CardConfig[]): CardConfig[] {
|
private pickMixedCards(pool: CardConfig[], count: number, upgradeCards: CardConfig[]): CardConfig[] {
|
||||||
if (pool.length === 0 || count <= 0) return [];
|
if (pool.length === 0 || count <= 0) return [];
|
||||||
const selected: CardConfig[] = [];
|
const selected: CardConfig[] = [];
|
||||||
const usedUpgradeTargets = new Set<number>();
|
const usedUpgradeTargets = new Set<number>();
|
||||||
|
const usedHeroUuids = new Set<number>();
|
||||||
|
|
||||||
while (selected.length < count) {
|
while (selected.length < count) {
|
||||||
const available = pool.filter(c => {
|
const available = pool.filter(c => {
|
||||||
if (c.type === CardType.SpecialUpgrade && c.target_hero_eid) {
|
if (c.type === CardType.SpecialUpgrade && c.target_hero_eid) {
|
||||||
return !usedUpgradeTargets.has(c.target_hero_eid);
|
return !usedUpgradeTargets.has(c.target_hero_eid);
|
||||||
}
|
}
|
||||||
|
if (c.type === CardType.Hero) {
|
||||||
|
return !usedHeroUuids.has(c.uuid);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (available.length === 0) break;
|
if (available.length === 0) break;
|
||||||
@@ -822,6 +841,9 @@ export class MissionCardComp extends CCComp {
|
|||||||
if (pick.type === CardType.SpecialUpgrade && pick.target_hero_eid) {
|
if (pick.type === CardType.SpecialUpgrade && pick.target_hero_eid) {
|
||||||
usedUpgradeTargets.add(pick.target_hero_eid);
|
usedUpgradeTargets.add(pick.target_hero_eid);
|
||||||
}
|
}
|
||||||
|
if (pick.type === CardType.Hero) {
|
||||||
|
usedHeroUuids.add(pick.uuid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return selected;
|
return selected;
|
||||||
}
|
}
|
||||||
@@ -907,6 +929,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
count: 3,
|
count: 3,
|
||||||
type: CardType.Hero,
|
type: CardType.Hero,
|
||||||
heroType,
|
heroType,
|
||||||
|
unique: true, // 保证一次刷新内的英雄卡不重复
|
||||||
});
|
});
|
||||||
// 过滤掉场上已召唤英雄的普通英雄卡(已召唤的英雄不再重复刷出)
|
// 过滤掉场上已召唤英雄的普通英雄卡(已召唤的英雄不再重复刷出)
|
||||||
const aliveHeroUuids = this.getAliveHeroUuids();
|
const aliveHeroUuids = this.getAliveHeroUuids();
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ export class MissionHeroComp extends CCComp {
|
|||||||
|
|
||||||
/** 硬编码的6个英雄占位点 */
|
/** 硬编码的6个英雄占位点 */
|
||||||
public static readonly HERO_POSITIONS: Vec3[] = [
|
public static readonly HERO_POSITIONS: Vec3[] = [
|
||||||
v3(-180, BoxSet.GAME_LINE, 0), // index 0 (node_index 1): Top Front 第二
|
v3(-200, 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(-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, 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 + 100, 0), // index 3 (node_index 4): Top Back
|
||||||
// v3(-280, BoxSet.GAME_LINE, 0), // index 4 (node_index 5): Mid 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 */
|
/** 当前处理的英雄 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.FightReady, this.fight_ready, this)
|
||||||
this.on(GameEvent.Zhaohuan,this.zhao_huan,this)
|
this.on(GameEvent.Zhaohuan, this.zhao_huan, this)
|
||||||
this.on(GameEvent.MissionEnd,this.clear_heros,this)
|
this.on(GameEvent.MissionEnd, this.clear_heros, this)
|
||||||
// 注册全局消息
|
// 注册全局消息
|
||||||
oops.message.on(GameEvent.CallHero,this.call_hero,this)
|
oops.message.on(GameEvent.CallHero, this.call_hero, this)
|
||||||
oops.message.on("PhasePrepareStart",this.fight_ready,this)
|
oops.message.on("PhasePrepareStart", this.fight_ready, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(){
|
onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
// 清理全部监听
|
// 清理全部监听
|
||||||
oops.message.off(GameEvent.CallHero,this.call_hero,this)
|
oops.message.off(GameEvent.CallHero, this.call_hero, this)
|
||||||
oops.message.off("PhasePrepareStart",this.fight_ready,this)
|
oops.message.off("PhasePrepareStart", this.fight_ready, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
@@ -102,7 +102,7 @@ export class MissionHeroComp extends CCComp {
|
|||||||
// ======================== 事件处理 ========================
|
// ======================== 事件处理 ========================
|
||||||
|
|
||||||
/** 关卡结束时清理全部英雄 ECS 实体 */
|
/** 关卡结束时清理全部英雄 ECS 实体 */
|
||||||
clear_heros(){
|
clear_heros() {
|
||||||
const heroes = this.getAllHeroes();
|
const heroes = this.getAllHeroes();
|
||||||
for (let i = 0; i < heroes.length; i++) {
|
for (let i = 0; i < heroes.length; i++) {
|
||||||
heroes[i].destroy();
|
heroes[i].destroy();
|
||||||
@@ -110,7 +110,7 @@ export class MissionHeroComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 战斗准备阶段:重置出战英雄计数,恢复满血重新登场 */
|
/** 战斗准备阶段:重置出战英雄计数,恢复满血重新登场 */
|
||||||
fight_ready(){
|
fight_ready() {
|
||||||
const heroes = this.getAllHeroes();
|
const heroes = this.getAllHeroes();
|
||||||
smc.vmdata.mission_data.hero_num = heroes.length;
|
smc.vmdata.mission_data.hero_num = heroes.length;
|
||||||
for (let i = 0; i < heroes.length; i++) {
|
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 event 事件名
|
||||||
* @param args { uuid, hero_lv, pool_lv }
|
* @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 payload = args ?? event;
|
||||||
const uuid = Number(payload?.uuid ?? 1001);
|
const uuid = Number(payload?.uuid ?? 1001);
|
||||||
const hero_lv = Math.max(1, Number(payload?.hero_lv ?? 1));
|
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);
|
const m = h.get(HeroAttrsComp);
|
||||||
if (m && m.posIndex >= 0) occupied.add(m.posIndex);
|
if (m && m.posIndex >= 0) occupied.add(m.posIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先中前(1) -> 上前(0) -> 下前(2) -> 中后(4) -> 上后(3) -> 下后(5)
|
// 优先中前(1) -> 上前(0) -> 下前(2) -> 中后(4) -> 上后(3) -> 下后(5)
|
||||||
const slotPriority = [1, 0, 2, 4, 3, 5];
|
const slotPriority = [1, 0, 2, 4, 3, 5];
|
||||||
for (const idx of slotPriority) {
|
for (const idx of slotPriority) {
|
||||||
@@ -204,7 +204,7 @@ export class MissionHeroComp extends CCComp {
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 溢出:默认中前
|
// 溢出:默认中前
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -219,14 +219,14 @@ export class MissionHeroComp extends CCComp {
|
|||||||
* @param pool_lv 卡池等级(历史遗留,新机制下不再使用)
|
* @param pool_lv 卡池等级(历史遗留,新机制下不再使用)
|
||||||
* @returns 创建的 Hero 实体
|
* @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>(Hero);
|
let hero = ecs.getEntity<Hero>(Hero);
|
||||||
let scale = 1
|
let scale = 1
|
||||||
const posIndex = this.pickPositionIndexForHero();
|
const posIndex = this.pickPositionIndexForHero();
|
||||||
const landingPos = MissionHeroComp.HERO_POSITIONS[posIndex];
|
const landingPos = MissionHeroComp.HERO_POSITIONS[posIndex];
|
||||||
let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
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);
|
hero.load(spawnPos, scale, uuid, landingPos.y, hero_lv, pool_lv, posIndex);
|
||||||
|
|
||||||
// 召唤完成后,派发事件以更新英雄面板
|
// 召唤完成后,派发事件以更新英雄面板
|
||||||
const model = hero.get(HeroAttrsComp);
|
const model = hero.get(HeroAttrsComp);
|
||||||
if (model) {
|
if (model) {
|
||||||
@@ -235,7 +235,7 @@ export class MissionHeroComp extends CCComp {
|
|||||||
model: model
|
model: model
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return hero;
|
return hero;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ export class MissionHeroComp extends CCComp {
|
|||||||
this.addHero(uuid, hero_lv, pool_lv);
|
this.addHero(uuid, hero_lv, pool_lv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** ECS 组件移除时触发(当前不销毁节点,保留引用) */
|
/** ECS 组件移除时触发(当前不销毁节点,保留引用) */
|
||||||
reset() {
|
reset() {
|
||||||
// this.node.destroy();
|
// this.node.destroy();
|
||||||
|
|||||||
Reference in New Issue
Block a user