feat(卡牌系统): 重构卡牌选择逻辑并添加属性卡类型支持

- 在GameSet枚举中添加Attr卡牌类型
- 在GameEvent中添加UseAttrCard事件
- 重构CardSet模块,统一使用GameSet中的CardType枚举
- 重构MissionCardComp模块,支持混合模式卡牌选择和强制类型获取
- 添加等级升级事件处理,优化卡牌获取逻辑
This commit is contained in:
panw
2026-01-14 20:37:40 +08:00
parent 6ddfe7e2c4
commit 5c5954b7d5
4 changed files with 116 additions and 100 deletions

View File

@@ -2,16 +2,7 @@ import { AttrCards, AttrInfo, CanSelectAttrs } from "./AttrSet";
import { talConf, ItalConf, CanSelectTalents } from "./TalSet";
import { SkillSet, SkillConfig, CanSelectSkills } from "./SkillSet";
import { HeroInfo, heroInfo, CanSelectHeros } from "./heroSet";
/**
* 卡牌类型枚举
*/
export enum CardType {
Skill = 1, // 技能
Talent = 2, // 天赋
Attr = 3, // 属性
Hero = 4 // 英雄(伙伴)
}
import { CardType } from "./GameSet";
/**
* 统一卡牌信息接口 (用于UI显示和逻辑处理)
@@ -58,7 +49,7 @@ export const LevelPoolConfigs: Record<number, IPoolConfig[]> = {
3: [{ type: CardType.Talent, poolWeight: 50 }, { type: CardType.Attr, poolWeight: 50, tag: "special" }], // 天赋或特殊属性
4: [{ type: CardType.Attr, poolWeight: 100 }],
5: [{ type: CardType.Talent, poolWeight: 100 }],
6: [{ type: CardType.Hero, poolWeight: 100 }], // 伙伴节点
6: [{ type: CardType.Partner, poolWeight: 100 }], // 伙伴节点
7: [{ type: CardType.Attr, poolWeight: 80 }, { type: CardType.Skill, poolWeight: 20 }],
8: [{ type: CardType.Attr, poolWeight: 80 }, { type: CardType.Skill, poolWeight: 20 }],
9: [{ type: CardType.Attr, poolWeight: 50, tag: "special" }, { type: CardType.Talent, poolWeight: 50 }],
@@ -110,7 +101,7 @@ function getCardBaseInfo(type: CardType, uuid: number): ICardInfo | null {
desc = baseInfo.info;
icon = baseInfo.icon;
break;
case CardType.Hero:
case CardType.Partner:
baseInfo = HeroInfo[uuid];
if (!baseInfo) return null;
name = baseInfo.name;
@@ -175,7 +166,7 @@ function getDefaultPool(type: CardType, level: number = 1): IPoolItem[] {
Object.keys(SkillSet).forEach(key => items.push({ id: Number(key), weight: 80 }));
}
break;
case CardType.Hero:
case CardType.Partner:
// 优先使用 CanSelectHeros 中的配置
if (CanSelectHeros[level]) {
CanSelectHeros[level].forEach(id => items.push({ id, weight: 100 }));
@@ -199,11 +190,14 @@ function getDefaultPool(type: CardType, level: number = 1): IPoolItem[] {
* @param level 当前等级
* @param count 选项数量 (默认3个)
* @param excludeUuids 排除的卡牌UUID列表 (用于去重或排除已拥有)
* @param forcedType 强制指定卡牌类型 (用于特殊获取,如商店、技能书等)
*/
export function getCardOptions(level: number, count: number = 3, excludeUuids: number[] = []): ICardInfo[] {
export function getCardOptions(level: number, count: number = 3, excludeUuids: number[] = [], forcedType?: CardType): ICardInfo[] {
// 1. 获取该等级的池配置
// 必须复制一份,因为我们可能需要修改它(比如移除空的池子)
const initialPoolConfigs = LevelPoolConfigs[level] || [{ type: CardType.Attr, poolWeight: 100 }];
// 如果强制指定类型,则构造一个只包含该类型的配置
const initialPoolConfigs = forcedType
? [{ type: forcedType, poolWeight: 100 }]
: (LevelPoolConfigs[level] || [{ type: CardType.Attr, poolWeight: 100 }]);
const result: ICardInfo[] = [];
const excludeSet = new Set(excludeUuids);

View File

@@ -49,6 +49,7 @@ export enum GameEvent {
TalentSelect = "TalentSelect",
UseTalentCard = "UseTalentCard",
UseItemCard = "UseItemCard",
UseAttrCard = "UseAttrCard",
NewWave = "NewWave",
AD_BACK_TRUE = "AD_BACK_TRUE",
AD_BACK_FALSE = "AD_BACK_FALSE",

View File

@@ -26,7 +26,8 @@ export enum CardType {
Talent = 1,
Skill = 2,
Potion = 3,
Partner = 4
Partner = 4,
Attr = 5
}
/**

View File

@@ -3,15 +3,17 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ec
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { talConf, ItalConf } from "../common/config/TalSet";
import { CanSelectHeros, HeroInfo } from "../common/config/heroSet";
import { CanSelectSkills, SkillSet } from "../common/config/SkillSet";
import { ItemSet } from "../common/config/ItemSet";
import { smc } from "../common/SingletonModuleComp";
import { CardType } from "../common/config/GameSet";
import { getCardOptions, ICardInfo } from "../common/config/CardSet";
const { ccclass, property } = _decorator;
interface ICardEvent {
type?: CardType;
level?: number;
}
/** 视图层对象 */
@ccclass('MissionCardComp')
@ecs.register('MissionCard', false)
@@ -36,13 +38,13 @@ export class MissionCardComp extends CCComp {
@property(Node)
noStop: Node = null!
card1_data:any = null!
card2_data:any = null!
card3_data:any = null!
card4_data:any = null!
card1_data: ICardInfo = null!
card2_data: ICardInfo = null!
card3_data: ICardInfo = null!
card4_data: ICardInfo = null!
// 当前卡片类型
curCardType: CardType = CardType.Talent;
// 当前卡片类型 (用于特殊获取模式)
curCardType: CardType | null = null;
// 是否处于锁定状态
private isLocked: boolean = true;
@@ -60,6 +62,7 @@ export class MissionCardComp extends CCComp {
oops.message.on(GameEvent.MissionStart, this.onMissionStart, this);
oops.message.on(GameEvent.MissionEnd, this.onMissionEnd, this);
oops.message.on(GameEvent.ToCallFriend, this.onCallFriend, this);
oops.message.on(GameEvent.CanUpdateLv, this.onLevelUp, this);
}
@@ -74,6 +77,7 @@ export class MissionCardComp extends CCComp {
oops.message.off(GameEvent.MissionStart, this.onMissionStart, this);
oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this);
oops.message.off(GameEvent.ToCallFriend, this.onCallFriend, this);
oops.message.off(GameEvent.CanUpdateLv, this.onLevelUp, this);
this.ent.destroy();
}
@@ -130,25 +134,31 @@ export class MissionCardComp extends CCComp {
private hasSelected: boolean = false;
// 事件队列
private eventQueue: CardType[] = [];
private eventQueue: ICardEvent[] = [];
private onShopOpen(event: string, args: any) {
this.eventQueue.push(CardType.Potion);
this.eventQueue.push({ type: CardType.Potion });
this.checkQueue();
}
private onTalentSelect(event: string, args: any) {
this.eventQueue.push(CardType.Talent);
this.eventQueue.push({ type: CardType.Talent });
this.checkQueue();
}
private onHeroSkillSelect(event: string, args: any) {
this.eventQueue.push(CardType.Skill);
this.eventQueue.push({ type: CardType.Skill });
this.checkQueue();
}
private onCallFriend(event: string, args: any) {
this.eventQueue.push(CardType.Partner);
this.eventQueue.push({ type: CardType.Partner });
this.checkQueue();
}
private onLevelUp(event: string, args: any) {
// args.lv 是当前等级
this.eventQueue.push({ level: args.lv });
this.checkQueue();
}
@@ -156,18 +166,42 @@ export class MissionCardComp extends CCComp {
if (this.node.active) return;
if (this.eventQueue.length === 0) return;
const type = this.eventQueue.shift();
if (type) {
this.showCardType(type);
const event = this.eventQueue.shift();
if (event) {
if (event.type !== undefined) {
this.showCardType(event.type);
} else if (event.level !== undefined) {
this.showLevelCards(event.level);
}
}
}
/**
* 显示指定类型的卡牌(特殊获取模式)
*/
private showCardType(type: CardType) {
this.curCardType = type;
// 获取当前英雄等级作为参考或者默认1级
const level = smc.vmdata.hero.lv || 1;
this.fetchCards(level, type);
this.openUI();
}
/**
* 显示等级对应的卡牌(正常升级模式)
*/
private showLevelCards(level: number) {
this.curCardType = null; // 混合模式,无单一类型
this.fetchCards(level);
this.openUI();
}
private openUI() {
this.node.active = true;
this.hasSelected = false;
this.curCardType = type;
// 根据锁定状态显示 Lock 节点
// 根据锁定状态显示 Lock 节点 (仅在特殊模式下可能需要锁定?或者统一逻辑)
// 原逻辑Lock.active = this.isLocked
if (this.Lock) {
this.Lock.active = this.isLocked;
}
@@ -184,9 +218,9 @@ export class MissionCardComp extends CCComp {
}
this.resetCardStates();
this.refCards();
this.playShowAnimation();
}
checkNoStop(){
this.noStop.getChildByName("no").active=!smc.data.noStop
@@ -212,42 +246,30 @@ export class MissionCardComp extends CCComp {
});
}
refCards(){
// 根据当前类型获取数据
let allData: any[] = [];
if (this.curCardType === CardType.Talent) {
allData = Object.values(talConf);
} else if (this.curCardType === CardType.Skill) {
// 过滤掉怪物技能 (uuid >= 6200)
// allData = Object.values(SkillSet).filter((s:any) => s.uuid < 6200);
allData = CanSelectSkills.map(id => SkillSet[id]);
} else if (this.curCardType === CardType.Partner) {
allData = CanSelectHeros.map(id => HeroInfo[id]);
} else if (this.curCardType === CardType.Potion) {
allData = Object.values(ItemSet);
}
// 后续扩展其他类型
// else if (this.curCardType === CardType.Skill) { ... }
/**
* 专门的获取卡牌方法
* @param level 等级
* @param forcedType 强制类型 (可选)
*/
fetchCards(level: number, forcedType?: CardType){
// 使用 CardSet 的 getCardOptions 获取卡牌
// 这里我们要获取 4 张卡牌
const options = getCardOptions(level, 4, [], forcedType);
const result: any[] = [];
const temp = [...allData];
// 更新卡片数据
if (options.length > 0) this.updateCardData(1, options[0]);
if (options.length > 1) this.updateCardData(2, options[1]);
if (options.length > 2) this.updateCardData(3, options[2]);
if (options.length > 3) this.updateCardData(4, options[3]);
// 简单的随机抽取算法
for (let i = 0; i < 4 && temp.length > 0; i++) {
const index = Math.floor(Math.random() * temp.length);
result.push(temp[index]);
temp.splice(index, 1);
}
// 更新卡片
if (result.length > 0) this.updateCardData(1, result[0]);
if (result.length > 1) this.updateCardData(2, result[1]);
if (result.length > 2) this.updateCardData(3, result[2]);
if (result.length > 3) this.updateCardData(4, result[3]);
// 如果获取不足4张隐藏多余的卡片节点 (UI可能需要处理空数据)
if (options.length < 4 && this.card4) this.card4.active = false;
if (options.length < 3 && this.card3) this.card3.active = false;
if (options.length < 2 && this.card2) this.card2.active = false;
if (options.length < 1 && this.card1) this.card1.active = false;
}
updateCardInfo(card:Node, data:any){
updateCardInfo(card:Node, data: ICardInfo){
if(!card) return
card.active = true;
// 隐藏选中状态
@@ -260,24 +282,13 @@ export class MissionCardComp extends CCComp {
}
let info = card.getChildByName("info")?.getChildByName("Label")
if(info){
// 根据类型显示不同描述目前天赋用desc
let desc = "";
if (this.curCardType === CardType.Talent) {
desc = data.desc || "";
} else if (this.curCardType === CardType.Skill) {
desc = data.info || "";
} else if (this.curCardType === CardType.Partner) {
desc = data.info || "";
} else {
desc = data.desc || "";
}
info.getComponent(Label)!.string = desc;
// ICardInfo 已经标准化了 desc直接使用
info.getComponent(Label)!.string = data.desc || "";
}
}
updateCardData(index: number, data: any) {
// 使用动态属性访问,简化 switch 冗余代码
updateCardData(index: number, data: ICardInfo) {
// 使用动态属性访问
(this as any)[`card${index}_data`] = data;
this.updateCardInfo((this as any)[`card${index}`], data);
}
@@ -288,13 +299,13 @@ export class MissionCardComp extends CCComp {
// 如果已经选择过,则不再处理
if (this.hasSelected) return;
// 动态获取数据和节点,简化 switch 逻辑
let selectedData: any = (this as any)[`card${_index}_data`];
// 动态获取数据和节点
let selectedData: ICardInfo = (this as any)[`card${_index}_data`];
let selectedCardNode: Node | null = (this as any)[`card${_index}`];
if (selectedData && selectedCardNode) {
this.hasSelected = true;
console.log("选择卡片:", selectedData.name, "类型:", this.curCardType);
console.log("选择卡片:", selectedData.name, "类型:", selectedData.type);
// 未选中的卡片缩小
const cards = [this.card1, this.card2, this.card3, this.card4];
@@ -319,18 +330,27 @@ export class MissionCardComp extends CCComp {
.delay(0.5)
.call(() => {
// 根据类型发送不同事件
if (this.curCardType === CardType.Talent) {
smc.addTalentRecord(selectedData.uuid);
oops.message.dispatchEvent(GameEvent.UseTalentCard, selectedData.uuid);
} else if (this.curCardType === CardType.Skill) {
smc.addSkillRecord(selectedData.uuid);
oops.message.dispatchEvent(GameEvent.UseSkillCard, selectedData.uuid);
} else if (this.curCardType === CardType.Partner) {
oops.message.dispatchEvent(GameEvent.CallFriend, { uuid: selectedData.uuid });
} else if (this.curCardType === CardType.Potion) {
oops.message.dispatchEvent(GameEvent.UseItemCard, selectedData.id);
switch (selectedData.type) {
case CardType.Talent:
smc.addTalentRecord(selectedData.uuid);
oops.message.dispatchEvent(GameEvent.UseTalentCard, selectedData.uuid);
break;
case CardType.Skill:
smc.addSkillRecord(selectedData.uuid);
oops.message.dispatchEvent(GameEvent.UseSkillCard, selectedData.uuid);
break;
case CardType.Partner:
oops.message.dispatchEvent(GameEvent.CallFriend, { uuid: selectedData.uuid });
break;
case CardType.Potion:
// Potion 在 CardSet 中也是 uuid
oops.message.dispatchEvent(GameEvent.UseItemCard, selectedData.uuid);
break;
case CardType.Attr:
oops.message.dispatchEvent(GameEvent.UseAttrCard, selectedData.uuid);
break;
}
// 后续扩展其他类型事件
this.close();
})
.start();
@@ -449,4 +469,4 @@ export class MissionCardComp extends CCComp {
reset() {
this.node.destroy();
}
}
}