Files
pixelheros/assets/script/game/map/CardComp.ts
walkpan d0e824e93b feat(卡牌系统): 实现任务卡牌抽卡与锁定功能
- 新增 MissionCardComp 作为卡牌面板控制器,管理四个固定卡槽
- 实现抽卡按钮逻辑,根据卡池等级抽取并分发卡牌到四个槽位
- 实现卡池升级按钮,提升抽卡品质但不影响已锁定卡牌
- 新增 CardComp 作为单卡控制器,支持卡牌使用与槽位锁定功能
- 锁定状态下卡槽将跳过抽卡更新,保持原有卡牌
- 添加任务开始/结束时的卡槽清理与界面显隐控制
- 修复预制体字段缺失问题,补充 instance 和 targetOverrides 字段
2026-03-14 09:18:45 +08:00

229 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { mLogger } from "../common/Logger";
import { _decorator, Label, Node, NodeEventType, Sprite, SpriteAtlas, resources } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { CardConfig, CardType } from "../common/config/CardSet";
const { ccclass, property } = _decorator;
/** 视图层对象 */
@ccclass('CardComp')
@ecs.register('CardComp', false)
export class CardComp extends CCComp {
private debugMode: boolean = true;
/** 锁定态图标节点(显示时表示本槽位锁定) */
@property(Node)
Lock: Node = null!
@property(Node)
unLock: Node = null!
@property(Node)
ap_node=null!
@property(Node)
hp_node=null!
@property(Node)
name_node=null!
@property(Node)
icon_node=null!
@property(Node)
cost_node=null!
card_cost:number=0
card_type:CardType=CardType.Hero
card_uuid:number=0
/** 是否处于锁定状态(锁定且有卡时,抽卡分发会被跳过) */
private isLocked: boolean = false;
/** 图标图集缓存(后续接图标资源时直接复用) */
private uiconsAtlas: SpriteAtlas | null = null;
/** 当前槽位承载的卡牌数据null 表示空槽 */
private cardData: CardConfig | null = null;
onLoad() {
/** 初始阶段只做UI状态准备不触发业务逻辑 */
this.bindEvents();
this.updateLockUI();
this.applyEmptyUI();
}
onDestroy() {
this.unbindEvents();
}
init(){
this.onMissionStart();
}
/** 游戏开始初始化 */
onMissionStart() {
}
/** 游戏结束清理 */
onMissionEnd() {
}
start() {
/** 单卡节点常驻,由数据控制显示内容 */
this.node.active = true;
}
/** 兼容旧接口:外部通过该入口更新卡牌 */
updateCardInfo(card:Node, data: CardConfig){
this.applyDrawCard(data);
}
private updateIcon(node: Node, iconId: string) {
}
/** 兼容旧接口:按索引更新卡牌(当前由 MissionCardComp 顺序分发) */
updateCardData(index: number, data: CardConfig) {
this.applyDrawCard(data);
}
/** 兼容按钮回调入口:触发单卡使用 */
selectCard(e: any, index: string) {
this.useCard();
}
/**
* 关闭界面
*/
close() {
}
/** 抽卡分发入口:返回 true 表示本次已成功接收新卡 */
applyDrawCard(data: CardConfig | null): boolean {
if (!data) return false;
/** 锁定且已有旧卡时,跳过本次刷新,保持老卡 */
if (this.isLocked && this.cardData) {
mLogger.log(this.debugMode, "CardComp", "slot locked, skip update", this.card_uuid);
return false;
}
this.cardData = data;
this.card_uuid = data.uuid;
this.card_type = data.type;
this.card_cost = data.cost;
this.node.active = true;
this.applyCardUI();
return true;
}
/** 使用当前卡牌仅做UI层清空不触发效果事件下一步再接 */
useCard(): CardConfig | null {
if (!this.cardData) return null;
const used = this.cardData;
this.clearAfterUse();
return used;
}
/** 查询槽位是否有卡 */
hasCard(): boolean {
return !!this.cardData;
}
/** 外部设置锁定态 */
setLocked(value: boolean) {
this.isLocked = value;
this.updateLockUI();
}
/** 外部读取当前锁定态 */
isSlotLocked(): boolean {
return this.isLocked;
}
/** 系统清槽:用于任务开始/结束等强制重置场景 */
clearBySystem() {
this.cardData = null;
this.card_uuid = 0;
this.card_cost = 0;
this.card_type = CardType.Hero;
this.isLocked = false;
this.updateLockUI();
this.applyEmptyUI();
}
/** 卡牌被玩家使用后的清槽行为 */
private clearAfterUse() {
this.cardData = null;
this.card_uuid = 0;
this.card_cost = 0;
this.card_type = CardType.Hero;
this.isLocked = false;
this.updateLockUI();
this.applyEmptyUI();
}
/** 绑定触控:卡面点击使用,锁按钮点击切换锁定 */
private bindEvents() {
this.node.on(NodeEventType.TOUCH_END, this.onCardTouchEnd, this);
this.Lock?.on(NodeEventType.TOUCH_END, this.onToggleLock, this);
this.unLock?.on(NodeEventType.TOUCH_END, this.onToggleLock, this);
}
/** 解绑触控,防止节点销毁后残留回调 */
private unbindEvents() {
this.node.off(NodeEventType.TOUCH_END, this.onCardTouchEnd, this);
this.Lock?.off(NodeEventType.TOUCH_END, this.onToggleLock, this);
this.unLock?.off(NodeEventType.TOUCH_END, this.onToggleLock, this);
}
/** 点击卡面执行单卡使用仅UI变化 */
private onCardTouchEnd() {
this.useCard();
}
/** 点击锁控件:切换锁态;空槽不允许锁定 */
private onToggleLock(event?: Event) {
if (!this.cardData) return;
this.isLocked = !this.isLocked;
this.updateLockUI();
event?.stopPropagation();
}
/** 根据锁态刷新 Lock / unLock 显示 */
private updateLockUI() {
if (this.Lock) this.Lock.active = this.isLocked;
if (this.unLock) this.unLock.active = !this.isLocked;
}
/** 根据当前 cardData 渲染卡面文字与图标 */
private applyCardUI() {
if (!this.cardData) {
this.applyEmptyUI();
return;
}
this.setLabel(this.name_node, `${CardType[this.card_type]}-${this.card_uuid}`);
this.setLabel(this.cost_node, `${this.card_cost}`);
if (this.ap_node) this.ap_node.active = false;
if (this.hp_node) this.hp_node.active = false;
this.updateIcon(this.icon_node, `${this.card_uuid}`);
}
/** 渲染空槽状态 */
private applyEmptyUI() {
this.setLabel(this.name_node, "");
this.setLabel(this.cost_node, "");
if (this.ap_node) this.ap_node.active = false;
if (this.hp_node) this.hp_node.active = false;
const sprite = this.icon_node?.getComponent(Sprite);
if (sprite) sprite.spriteFrame = null;
}
/** 安全设置文本,兼容节点上或子节点上的 Label */
private setLabel(node: Node | null, value: string) {
if (!node) return;
const label = node.getComponent(Label) || node.getComponentInChildren(Label);
if (label) label.string = value;
}
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() {
this.node.destroy();
}
}