Compare commits
3 Commits
9c520cf917
...
40723174c9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40723174c9 | ||
|
|
4ef2e13798 | ||
|
|
ffafefcc72 |
File diff suppressed because it is too large
Load Diff
@@ -116,6 +116,46 @@ export const EquipPoolList: CardConfig[] = EquipRawList.map(data => ({
|
|||||||
trigger_type: CardTriggerType.Field,
|
trigger_type: CardTriggerType.Field,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按权重抽取 N 张装备卡(unique 保证一次刷新内不重复,不足时允许重复补齐)。
|
||||||
|
* @param count 需要抽取的数量
|
||||||
|
*/
|
||||||
|
export function drawEquipCards(count: number): CardConfig[] {
|
||||||
|
const safeCount = Math.max(0, Math.floor(count));
|
||||||
|
if (EquipPoolList.length === 0 || safeCount <= 0) return [];
|
||||||
|
|
||||||
|
const picked: CardConfig[] = [];
|
||||||
|
let available = [...EquipPoolList];
|
||||||
|
while (picked.length < safeCount) {
|
||||||
|
if (available.length === 0) break;
|
||||||
|
const pick = weightedPick(available);
|
||||||
|
if (!pick) break;
|
||||||
|
picked.push(pick);
|
||||||
|
available = available.filter(c => c.uuid !== pick.uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不足时允许重复补齐
|
||||||
|
const filled = [...picked];
|
||||||
|
while (filled.length < safeCount) {
|
||||||
|
const fallback = weightedPick(EquipPoolList);
|
||||||
|
if (!fallback) break;
|
||||||
|
filled.push(fallback);
|
||||||
|
}
|
||||||
|
return filled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 单次按权重抽取一张卡 */
|
||||||
|
function weightedPick(cards: CardConfig[]): CardConfig | null {
|
||||||
|
if (cards.length === 0) return null;
|
||||||
|
const totalWeight = cards.reduce((total, card) => total + (card.weight ?? 0), 0);
|
||||||
|
let random = Math.random() * totalWeight;
|
||||||
|
for (const card of cards) {
|
||||||
|
random -= (card.weight ?? 0);
|
||||||
|
if (random <= 0) return card;
|
||||||
|
}
|
||||||
|
return cards[cards.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按 UUID 查找装备卡配置
|
* 按 UUID 查找装备卡配置
|
||||||
* @param uuid 装备卡 UUID
|
* @param uuid 装备卡 UUID
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
* 6. **准备/战斗阶段切换** —— 控制卡牌面板的 展开/收起 动画。
|
* 6. **准备/战斗阶段切换** —— 控制卡牌面板的 展开/收起 动画。
|
||||||
*
|
*
|
||||||
* 关键设计:
|
* 关键设计:
|
||||||
* - 3 个 CardComp 通过 cacheCardComps() 映射为有序数组 cardComps[],
|
* - 3 个 CardComp 由 cardPrefab 动态实例化生成,通过 cacheCardComps() 映射为有序数组 cardComps[],
|
||||||
* 之后所有分发、清空操作均通过此数组进行。
|
* 之后所有分发、清空操作均通过此数组进行。
|
||||||
* - buildDrawCards() 动态合并升级卡池和基础卡池后抽取 3 张,不足时循环补齐。
|
* - buildDrawCards() 动态合并升级卡池和基础卡池后抽取 3 张,不足时循环补齐。
|
||||||
* - 英雄上限校验(onUseHeroCard)采用 guard/cancel 模式:
|
* - 英雄上限校验(onUseHeroCard)采用 guard/cancel 模式:
|
||||||
@@ -41,8 +41,8 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu
|
|||||||
import { GameEvent } from "../common/config/GameEvent";
|
import { GameEvent } from "../common/config/GameEvent";
|
||||||
import { CardConfig, CardPoolList, CardType, drawCardsByRule, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet";
|
import { CardConfig, CardPoolList, CardType, drawCardsByRule, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet";
|
||||||
import { drawSkillCards } from "../common/config/SCardSet";
|
import { drawSkillCards } from "../common/config/SCardSet";
|
||||||
import { EquipPoolList } from "../common/config/EquipSet";
|
import { drawEquipCards } from "../common/config/EquipSet";
|
||||||
import { ItemCardList, drawItemCards } from "../common/config/ICardSet";
|
import { drawItemCards } from "../common/config/ICardSet";
|
||||||
import { CardComp } from "./CardComp";
|
import { CardComp } from "./CardComp";
|
||||||
import { SCardComp } from "./SCardComp";
|
import { SCardComp } from "./SCardComp";
|
||||||
import { EquipListComp } from "./EquipListComp";
|
import { EquipListComp } from "./EquipListComp";
|
||||||
@@ -85,59 +85,67 @@ export class MissionCardComp extends CCComp {
|
|||||||
|
|
||||||
// ======================== 编辑器绑定节点 ========================
|
// ======================== 编辑器绑定节点 ========================
|
||||||
|
|
||||||
/** 卡牌面板根节点(战斗阶段收起,准备阶段展开) */
|
/** 英雄抽卡面板(战斗阶段收起,准备阶段展开) */
|
||||||
|
@property(Node)
|
||||||
|
showCards: Node = null!
|
||||||
|
@property(Node)
|
||||||
|
cardPanNode: Node = null!
|
||||||
@property(Node)
|
@property(Node)
|
||||||
cards_node: Node = null!
|
cards_node: Node = null!
|
||||||
/** 卡牌槽位 1 节点 */
|
/** 卡牌槽位预制体(动态实例化 3 个) */
|
||||||
@property(Node)
|
@property(Prefab)
|
||||||
card1: Node = null!
|
cardPrefab: Prefab = null!
|
||||||
/** 卡牌槽位 2 节点 */
|
|
||||||
@property(Node)
|
|
||||||
card2: Node = null!
|
|
||||||
/** 卡牌槽位 3 节点 */
|
|
||||||
@property(Node)
|
|
||||||
card3: Node = null!
|
|
||||||
/** 卡牌槽位 4 节点 */
|
|
||||||
@property(Node)
|
|
||||||
card4: Node = null!
|
|
||||||
/** 抽卡(刷新)按钮节点 */
|
/** 抽卡(刷新)按钮节点 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
cards_chou: Node = null!
|
cards_chou: Node = null!
|
||||||
|
@property(Node)
|
||||||
|
closeCards: Node = null!
|
||||||
|
|
||||||
@property(Node)
|
@property(Node)
|
||||||
nock_node: Node = null!
|
nock_node: Node = null!
|
||||||
|
|
||||||
/** 英雄卡牌池cards_node显示隐藏 */
|
|
||||||
|
/** 英雄面板显示按钮 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
showHeros: Node = null!
|
showHeros: Node = null!
|
||||||
@property(Node)
|
@property(Node)
|
||||||
closeHeros: Node = null!
|
herosPanNode: Node = null!
|
||||||
|
@property(Node)
|
||||||
|
herosBoxNode: Node = null!
|
||||||
|
@property(Prefab)
|
||||||
|
heroPrefab: Prefab = null!
|
||||||
|
|
||||||
|
/** 装备面板显示按钮 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
showEquips: Node = null!
|
showEquips: Node = null!
|
||||||
@property(Node)
|
|
||||||
closeEquips: Node = null!
|
|
||||||
@property(Node)
|
|
||||||
showSkills: Node = null!
|
|
||||||
@property(Node)
|
|
||||||
closeSkills: Node = null!
|
|
||||||
|
|
||||||
@property(Node)
|
@property(Node)
|
||||||
equipsPanNode: Node = null!
|
equipsPanNode: Node = null!
|
||||||
@property(Node)
|
@property(Node)
|
||||||
equipsBoxNode: Node = null!
|
equipsBoxNode: Node = null!
|
||||||
@property(Prefab)
|
@property(Prefab)
|
||||||
equipPrefab: Prefab = null!
|
equipPrefab: Prefab = null!
|
||||||
|
|
||||||
|
/** 技能面板显示按钮 */
|
||||||
|
@property(Node)
|
||||||
|
showSkills: Node = null!
|
||||||
|
@property(Node)
|
||||||
|
skillPanNode: Node = null!
|
||||||
|
@property(Node)
|
||||||
|
skillBoxNode: Node = null!
|
||||||
|
@property(Prefab)
|
||||||
|
skillPrefab: Prefab = null!
|
||||||
|
|
||||||
|
/** 药品面板显示按钮 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
showShop: Node = null!
|
showShop: Node = null!
|
||||||
@property(Node)
|
@property(Node)
|
||||||
closeShop: Node = null!
|
|
||||||
@property(Node)
|
|
||||||
shopPanNode: Node = null!
|
shopPanNode: Node = null!
|
||||||
@property(Node)
|
@property(Node)
|
||||||
shopBoxNode: Node = null!
|
shopBoxNode: Node = null!
|
||||||
@property(Prefab)
|
@property(Prefab)
|
||||||
itemPrefab: Prefab = null!
|
itemPrefab: Prefab = null!
|
||||||
/** 卡池升级按钮节点(已废弃,保留节点引用防止 editor 报错) */
|
/** 卡池升级按钮节点(已废弃,保留节点引用防止 editor 报错) */
|
||||||
|
|
||||||
@property(Node)
|
@property(Node)
|
||||||
cards_up: Node = null!
|
cards_up: Node = null!
|
||||||
/** 金币显示节点(含 icon + num 子节点) */
|
/** 金币显示节点(含 icon + num 子节点) */
|
||||||
@@ -171,6 +179,13 @@ export class MissionCardComp extends CCComp {
|
|||||||
@property(Node)
|
@property(Node)
|
||||||
skill_refresh_num_node: Node = null!
|
skill_refresh_num_node: Node = null!
|
||||||
|
|
||||||
|
/** 装备刷新按钮节点 */
|
||||||
|
@property(Node)
|
||||||
|
equip_refresh: Node = null!;
|
||||||
|
/** 药品刷新按钮节点 */
|
||||||
|
@property(Node)
|
||||||
|
shop_refresh: Node = null!;
|
||||||
|
|
||||||
// ======================== 运行时状态 ========================
|
// ======================== 运行时状态 ========================
|
||||||
|
|
||||||
/** 三个槽位对应的 CardComp 控制器缓存(有序数组) */
|
/** 三个槽位对应的 CardComp 控制器缓存(有序数组) */
|
||||||
@@ -194,8 +209,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
private panelBaseYs: number[] = [];
|
private panelBaseYs: number[] = [];
|
||||||
/** 是否已初始化面板布局 */
|
/** 是否已初始化面板布局 */
|
||||||
private hasInitPanelLayout: boolean = false;
|
private hasInitPanelLayout: boolean = false;
|
||||||
/** 面板是否处于隐藏状态(被 close 按钮关闭) */
|
|
||||||
private panelsHidden: boolean = false;
|
|
||||||
/** 是否已缓存卡牌面板基准缩放 */
|
/** 是否已缓存卡牌面板基准缩放 */
|
||||||
private hasCachedCardsBaseScale: boolean = false;
|
private hasCachedCardsBaseScale: boolean = false;
|
||||||
/** 卡牌面板基准缩放(从场景读取) */
|
/** 卡牌面板基准缩放(从场景读取) */
|
||||||
@@ -212,6 +225,13 @@ export class MissionCardComp extends CCComp {
|
|||||||
*/
|
*/
|
||||||
private upgradeRotationIndex: number = 0;
|
private upgradeRotationIndex: number = 0;
|
||||||
|
|
||||||
|
/** 卡牌面板显示位置 Y 坐标 */
|
||||||
|
private readonly cardPanelShowY: number = 0;
|
||||||
|
/** 卡牌面板隐藏位置 Y 坐标(向下 700) */
|
||||||
|
private readonly cardPanelHideY: number = -700;
|
||||||
|
/** 卡牌面板显示/隐藏动画时长 */
|
||||||
|
private readonly cardPanelAnimDuration: number = 0.25;
|
||||||
|
|
||||||
// ======================== 生命周期 ========================
|
// ======================== 生命周期 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -244,27 +264,21 @@ export class MissionCardComp extends CCComp {
|
|||||||
if (this.showHeros && this.showHeros.isValid) {
|
if (this.showHeros && this.showHeros.isValid) {
|
||||||
this.showHeros.off(NodeEventType.TOUCH_END, this.onShowHerosClick, this);
|
this.showHeros.off(NodeEventType.TOUCH_END, this.onShowHerosClick, this);
|
||||||
}
|
}
|
||||||
if (this.closeHeros && this.closeHeros.isValid) {
|
if (this.showCards && this.showCards.isValid) {
|
||||||
this.closeHeros.off(NodeEventType.TOUCH_END, this.onCloseHerosClick, this);
|
this.showCards.off(NodeEventType.TOUCH_END, this.onShowCardsClick, this);
|
||||||
|
}
|
||||||
|
if (this.closeCards && this.closeCards.isValid) {
|
||||||
|
this.closeCards.off(NodeEventType.TOUCH_END, this.onCloseCardsClick, this);
|
||||||
}
|
}
|
||||||
if (this.showEquips && this.showEquips.isValid) {
|
if (this.showEquips && this.showEquips.isValid) {
|
||||||
this.showEquips.off(NodeEventType.TOUCH_END, this.onShowEquipsClick, this);
|
this.showEquips.off(NodeEventType.TOUCH_END, this.onShowEquipsClick, this);
|
||||||
}
|
}
|
||||||
if (this.closeEquips && this.closeEquips.isValid) {
|
|
||||||
this.closeEquips.off(NodeEventType.TOUCH_END, this.onCloseEquipsClick, this);
|
|
||||||
}
|
|
||||||
if (this.showSkills && this.showSkills.isValid) {
|
if (this.showSkills && this.showSkills.isValid) {
|
||||||
this.showSkills.off(NodeEventType.TOUCH_END, this.onShowSkillsClick, this);
|
this.showSkills.off(NodeEventType.TOUCH_END, this.onShowSkillsClick, this);
|
||||||
}
|
}
|
||||||
if (this.closeSkills && this.closeSkills.isValid) {
|
|
||||||
this.closeSkills.off(NodeEventType.TOUCH_END, this.onCloseSkillsClick, this);
|
|
||||||
}
|
|
||||||
if (this.showShop && this.showShop.isValid) {
|
if (this.showShop && this.showShop.isValid) {
|
||||||
this.showShop.off(NodeEventType.TOUCH_END, this.onShowShopClick, this);
|
this.showShop.off(NodeEventType.TOUCH_END, this.onShowShopClick, this);
|
||||||
}
|
}
|
||||||
if (this.closeShop && this.closeShop.isValid) {
|
|
||||||
this.closeShop.off(NodeEventType.TOUCH_END, this.onCloseShopClick, this);
|
|
||||||
}
|
|
||||||
this.unbindEvents();
|
this.unbindEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,25 +400,22 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.cards_chou?.on(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this);
|
this.cards_chou?.on(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this);
|
||||||
this.cards_chou?.on(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, this);
|
this.cards_chou?.on(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, this);
|
||||||
|
|
||||||
/** 英雄卡池显示/隐藏切换按钮 */
|
/** 显示卡牌面板按钮 */
|
||||||
|
this.showCards?.on(NodeEventType.TOUCH_END, this.onShowCardsClick, this);
|
||||||
|
/** 隐藏卡牌面板按钮 */
|
||||||
|
this.closeCards?.on(NodeEventType.TOUCH_END, this.onCloseCardsClick, this);
|
||||||
|
|
||||||
|
/** 英雄面板显示按钮 */
|
||||||
this.showHeros?.on(NodeEventType.TOUCH_END, this.onShowHerosClick, this);
|
this.showHeros?.on(NodeEventType.TOUCH_END, this.onShowHerosClick, this);
|
||||||
/** 关闭英雄卡池按钮 */
|
|
||||||
this.closeHeros?.on(NodeEventType.TOUCH_END, this.onCloseHerosClick, this);
|
|
||||||
|
|
||||||
/** 装备商店显示/隐藏切换按钮 */
|
/** 装备面板显示按钮 */
|
||||||
this.showEquips?.on(NodeEventType.TOUCH_END, this.onShowEquipsClick, this);
|
this.showEquips?.on(NodeEventType.TOUCH_END, this.onShowEquipsClick, this);
|
||||||
/** 关闭装备商店按钮 */
|
|
||||||
this.closeEquips?.on(NodeEventType.TOUCH_END, this.onCloseEquipsClick, this);
|
|
||||||
|
|
||||||
/** 技能卡池显示按钮 */
|
/** 技能面板显示按钮 */
|
||||||
this.showSkills?.on(NodeEventType.TOUCH_END, this.onShowSkillsClick, this);
|
this.showSkills?.on(NodeEventType.TOUCH_END, this.onShowSkillsClick, this);
|
||||||
/** 关闭技能卡池按钮 */
|
|
||||||
this.closeSkills?.on(NodeEventType.TOUCH_END, this.onCloseSkillsClick, this);
|
|
||||||
|
|
||||||
/** 商店显示/隐藏切换按钮 */
|
/** 药品面板显示按钮 */
|
||||||
this.showShop?.on(NodeEventType.TOUCH_END, this.onShowShopClick, this);
|
this.showShop?.on(NodeEventType.TOUCH_END, this.onShowShopClick, this);
|
||||||
/** 关闭商店按钮 */
|
|
||||||
this.closeShop?.on(NodeEventType.TOUCH_END, this.onCloseShopClick, this);
|
|
||||||
|
|
||||||
/** 技能卡刷新按钮 */
|
/** 技能卡刷新按钮 */
|
||||||
this.skill_refresh?.on(NodeEventType.TOUCH_START, this.onSkillDrawTouchStart, this);
|
this.skill_refresh?.on(NodeEventType.TOUCH_START, this.onSkillDrawTouchStart, this);
|
||||||
@@ -413,6 +424,16 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.skill_ad_refresh?.on(NodeEventType.TOUCH_START, this.onSkillAdDrawTouchStart, this);
|
this.skill_ad_refresh?.on(NodeEventType.TOUCH_START, this.onSkillAdDrawTouchStart, this);
|
||||||
this.skill_ad_refresh?.on(NodeEventType.TOUCH_END, this.onSkillAdDrawTouchEnd, this);
|
this.skill_ad_refresh?.on(NodeEventType.TOUCH_END, this.onSkillAdDrawTouchEnd, this);
|
||||||
this.skill_ad_refresh?.on(NodeEventType.TOUCH_CANCEL, this.onSkillAdDrawTouchCancel, this);
|
this.skill_ad_refresh?.on(NodeEventType.TOUCH_CANCEL, this.onSkillAdDrawTouchCancel, this);
|
||||||
|
|
||||||
|
/** 装备刷新按钮 */
|
||||||
|
this.equip_refresh?.on(NodeEventType.TOUCH_START, this.onEquipDrawTouchStart, this);
|
||||||
|
this.equip_refresh?.on(NodeEventType.TOUCH_END, this.onEquipDrawTouchEnd, this);
|
||||||
|
this.equip_refresh?.on(NodeEventType.TOUCH_CANCEL, this.onEquipDrawTouchCancel, this);
|
||||||
|
|
||||||
|
/** 药品刷新按钮 */
|
||||||
|
this.shop_refresh?.on(NodeEventType.TOUCH_START, this.onShopDrawTouchStart, this);
|
||||||
|
this.shop_refresh?.on(NodeEventType.TOUCH_END, this.onShopDrawTouchEnd, this);
|
||||||
|
this.shop_refresh?.on(NodeEventType.TOUCH_CANCEL, this.onShopDrawTouchCancel, this);
|
||||||
}
|
}
|
||||||
// ======================== 事件回调 ========================
|
// ======================== 事件回调 ========================
|
||||||
|
|
||||||
@@ -543,11 +564,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.slideToPanel(2);
|
this.slideToPanel(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 关闭技能面板按钮回调:隐藏所有面板 */
|
|
||||||
private onCloseSkillsClick() {
|
|
||||||
this.hideAllPanels();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======================== 商店面板 ========================
|
// ======================== 商店面板 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -558,14 +574,9 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.slideToPanel(3);
|
this.slideToPanel(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 关闭商店面板按钮回调:隐藏所有面板 */
|
|
||||||
private onCloseShopClick() {
|
|
||||||
this.hideAllPanels();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 填充商品列表:
|
* 填充商品列表:
|
||||||
* 从 ICardSet 的 ItemCardList 获取所有商品卡,
|
* 从 ICardSet 按权重抽取 3 张商品卡,
|
||||||
* 实例化 itemPrefab 到 shopBoxNode,
|
* 实例化 itemPrefab 到 shopBoxNode,
|
||||||
* 每个商品项由 ItemListComp 渲染并处理购买。
|
* 每个商品项由 ItemListComp 渲染并处理购买。
|
||||||
*
|
*
|
||||||
@@ -578,8 +589,9 @@ export class MissionCardComp extends CCComp {
|
|||||||
// 清空旧列表
|
// 清空旧列表
|
||||||
this.shopBoxNode.removeAllChildren();
|
this.shopBoxNode.removeAllChildren();
|
||||||
|
|
||||||
// 从商品卡池获取所有商品
|
// 从商品卡池按权重抽取 3 张
|
||||||
for (const item of ItemCardList) {
|
const items = drawItemCards(3);
|
||||||
|
for (const item of items) {
|
||||||
const node = instantiate(this.itemPrefab);
|
const node = instantiate(this.itemPrefab);
|
||||||
this.shopBoxNode.addChild(node);
|
this.shopBoxNode.addChild(node);
|
||||||
const comp = node.getComponent(ItemListComp) || node.addComponent(ItemListComp);
|
const comp = node.getComponent(ItemListComp) || node.addComponent(ItemListComp);
|
||||||
@@ -592,7 +604,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 根据物品数量设置列表面板高度
|
// 根据物品数量设置列表面板高度
|
||||||
this.resizeListBox(this.shopBoxNode, ItemCardList.length);
|
this.resizeListBox(this.shopBoxNode, items.length);
|
||||||
|
|
||||||
// 默认显示商店面板
|
// 默认显示商店面板
|
||||||
if (this.shopPanNode) {
|
if (this.shopPanNode) {
|
||||||
@@ -600,7 +612,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mLogger.log(this.debugMode, "MissionCardComp", "populate shop items", {
|
mLogger.log(this.debugMode, "MissionCardComp", "populate shop items", {
|
||||||
count: ItemCardList.length
|
count: items.length
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,7 +627,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
if (!this.skillCardComps) return;
|
if (!this.skillCardComps) return;
|
||||||
for (let i = 0; i < this.skillCardComps.length; i++) {
|
for (let i = 0; i < this.skillCardComps.length; i++) {
|
||||||
if (this.skillCardComps[i]) {
|
if (this.skillCardComps[i]) {
|
||||||
this.skillCardComps[i].applyDrawCard(cards[i] ?? null);
|
this.skillCardComps[i].applyCardData(cards[i] ?? null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -676,15 +688,15 @@ export class MissionCardComp extends CCComp {
|
|||||||
if (this.showHeros && this.showHeros.isValid) {
|
if (this.showHeros && this.showHeros.isValid) {
|
||||||
this.showHeros.off(NodeEventType.TOUCH_END, this.onShowHerosClick, this);
|
this.showHeros.off(NodeEventType.TOUCH_END, this.onShowHerosClick, this);
|
||||||
}
|
}
|
||||||
if (this.closeHeros && this.closeHeros.isValid) {
|
if (this.showCards && this.showCards.isValid) {
|
||||||
this.closeHeros.off(NodeEventType.TOUCH_END, this.onCloseHerosClick, this);
|
this.showCards.off(NodeEventType.TOUCH_END, this.onShowCardsClick, this);
|
||||||
|
}
|
||||||
|
if (this.closeCards && this.closeCards.isValid) {
|
||||||
|
this.closeCards.off(NodeEventType.TOUCH_END, this.onCloseCardsClick, this);
|
||||||
}
|
}
|
||||||
if (this.showEquips && this.showEquips.isValid) {
|
if (this.showEquips && this.showEquips.isValid) {
|
||||||
this.showEquips.off(NodeEventType.TOUCH_END, this.onShowEquipsClick, this);
|
this.showEquips.off(NodeEventType.TOUCH_END, this.onShowEquipsClick, this);
|
||||||
}
|
}
|
||||||
if (this.closeEquips && this.closeEquips.isValid) {
|
|
||||||
this.closeEquips.off(NodeEventType.TOUCH_END, this.onCloseEquipsClick, this);
|
|
||||||
}
|
|
||||||
if (this.skill_refresh && this.skill_refresh.isValid) {
|
if (this.skill_refresh && this.skill_refresh.isValid) {
|
||||||
this.skill_refresh.off(NodeEventType.TOUCH_START, this.onSkillDrawTouchStart, this);
|
this.skill_refresh.off(NodeEventType.TOUCH_START, this.onSkillDrawTouchStart, this);
|
||||||
this.skill_refresh.off(NodeEventType.TOUCH_END, this.onSkillDrawTouchEnd, this);
|
this.skill_refresh.off(NodeEventType.TOUCH_END, this.onSkillDrawTouchEnd, this);
|
||||||
@@ -698,8 +710,15 @@ export class MissionCardComp extends CCComp {
|
|||||||
if (this.showShop && this.showShop.isValid) {
|
if (this.showShop && this.showShop.isValid) {
|
||||||
this.showShop.off(NodeEventType.TOUCH_END, this.onShowShopClick, this);
|
this.showShop.off(NodeEventType.TOUCH_END, this.onShowShopClick, this);
|
||||||
}
|
}
|
||||||
if (this.closeShop && this.closeShop.isValid) {
|
if (this.equip_refresh && this.equip_refresh.isValid) {
|
||||||
this.closeShop.off(NodeEventType.TOUCH_END, this.onCloseShopClick, this);
|
this.equip_refresh.off(NodeEventType.TOUCH_START, this.onEquipDrawTouchStart, this);
|
||||||
|
this.equip_refresh.off(NodeEventType.TOUCH_END, this.onEquipDrawTouchEnd, this);
|
||||||
|
this.equip_refresh.off(NodeEventType.TOUCH_CANCEL, this.onEquipDrawTouchCancel, this);
|
||||||
|
}
|
||||||
|
if (this.shop_refresh && this.shop_refresh.isValid) {
|
||||||
|
this.shop_refresh.off(NodeEventType.TOUCH_START, this.onShopDrawTouchStart, this);
|
||||||
|
this.shop_refresh.off(NodeEventType.TOUCH_END, this.onShopDrawTouchEnd, this);
|
||||||
|
this.shop_refresh.off(NodeEventType.TOUCH_CANCEL, this.onShopDrawTouchCancel, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -855,7 +874,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
|
|
||||||
/** 面板节点数组(顺序:英雄→装备→技能→药品) */
|
/** 面板节点数组(顺序:英雄→装备→技能→药品) */
|
||||||
private getPanelNodes(): Node[] {
|
private getPanelNodes(): Node[] {
|
||||||
return [this.cards_node, this.equipsPanNode, this.skill_card_node, this.shopPanNode];
|
return [this.herosPanNode, this.equipsPanNode, this.skillPanNode, this.shopPanNode];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 面板显示按钮数组(同上排序) */
|
/** 面板显示按钮数组(同上排序) */
|
||||||
@@ -910,14 +929,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
const panels = this.getPanelNodes();
|
const panels = this.getPanelNodes();
|
||||||
if (index < 0 || index >= panels.length) return;
|
if (index < 0 || index >= panels.length) return;
|
||||||
|
|
||||||
// 如果面板被隐藏(close),先恢复显示
|
|
||||||
if (this.panelsHidden) {
|
|
||||||
this.panelsHidden = false;
|
|
||||||
for (const p of panels) {
|
|
||||||
if (p && p.isValid) p.active = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentPanelIndex = index;
|
this.currentPanelIndex = index;
|
||||||
const duration = 0.25;
|
const duration = 0.25;
|
||||||
|
|
||||||
@@ -946,24 +957,11 @@ export class MissionCardComp extends CCComp {
|
|||||||
if (!btn || !btn.isValid) continue;
|
if (!btn || !btn.isValid) continue;
|
||||||
const activeChild = btn.getChildByName("active");
|
const activeChild = btn.getChildByName("active");
|
||||||
if (activeChild) {
|
if (activeChild) {
|
||||||
activeChild.active = (i === this.currentPanelIndex && !this.panelsHidden);
|
activeChild.active = (i === this.currentPanelIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 隐藏所有面板(通过 close 按钮关闭)。
|
|
||||||
*/
|
|
||||||
private hideAllPanels() {
|
|
||||||
oops.audio.playEffect("music/button");
|
|
||||||
this.panelsHidden = true;
|
|
||||||
const panels = this.getPanelNodes();
|
|
||||||
for (const p of panels) {
|
|
||||||
if (p && p.isValid) p.active = false;
|
|
||||||
}
|
|
||||||
this.updatePanelButtonStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新面板按钮的可交互状态。
|
* 更新面板按钮的可交互状态。
|
||||||
* 游戏刚开始时(未召唤英雄),只有英雄面板可打开;
|
* 游戏刚开始时(未召唤英雄),只有英雄面板可打开;
|
||||||
@@ -986,12 +984,33 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭英雄面板按钮回调:隐藏所有面板。
|
* 显示卡牌面板:从底部向上滑入到 Y=0
|
||||||
* 同步关闭已打开的英雄信息面板(HInfo),避免卡池收起后信息面板孤立悬浮。
|
|
||||||
*/
|
*/
|
||||||
private onCloseHerosClick() {
|
private onShowCardsClick() {
|
||||||
this.hideAllPanels();
|
oops.audio.playEffect("music/button");
|
||||||
oops.gui.remove(UIID.HInfo);
|
if (!this.cardPanNode || !this.cardPanNode.isValid) return;
|
||||||
|
this.cardPanNode.active = true;
|
||||||
|
Tween.stopAllByTarget(this.cardPanNode);
|
||||||
|
tween(this.cardPanNode)
|
||||||
|
.to(this.cardPanelAnimDuration, { position: new Vec3(0, this.cardPanelShowY, 0) }, { easing: 'quadOut' })
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隐藏卡牌面板:向下滑出到 Y=-700
|
||||||
|
*/
|
||||||
|
private onCloseCardsClick() {
|
||||||
|
oops.audio.playEffect("music/button");
|
||||||
|
if (!this.cardPanNode || !this.cardPanNode.isValid) return;
|
||||||
|
Tween.stopAllByTarget(this.cardPanNode);
|
||||||
|
tween(this.cardPanNode)
|
||||||
|
.to(this.cardPanelAnimDuration, { position: new Vec3(0, this.cardPanelHideY, 0) }, { easing: 'quadIn' })
|
||||||
|
.call(() => {
|
||||||
|
if (this.cardPanNode && this.cardPanNode.isValid) {
|
||||||
|
this.cardPanNode.active = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 装备商店面板 ========================
|
// ======================== 装备商店面板 ========================
|
||||||
@@ -1004,11 +1023,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.slideToPanel(1);
|
this.slideToPanel(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 关闭装备面板按钮回调:隐藏所有面板 */
|
|
||||||
private onCloseEquipsClick() {
|
|
||||||
this.hideAllPanels();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据子项数量设置容器高度。
|
* 根据子项数量设置容器高度。
|
||||||
* 单项高度 100,间隔 5,上下各加 25 冗余(共 50)。
|
* 单项高度 100,间隔 5,上下各加 25 冗余(共 50)。
|
||||||
@@ -1025,7 +1039,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 填充装备列表:
|
* 填充装备列表:
|
||||||
* 从 CardPoolList 获取所有装备卡(trigger_type=Field),
|
* 从 EquipPoolList 按权重抽取 3 张装备卡,
|
||||||
* 实例化 equipPrefab 到 equipsBoxNode,
|
* 实例化 equipPrefab 到 equipsBoxNode,
|
||||||
* 每个装备项由 EquipListComp 渲染并处理购买。
|
* 每个装备项由 EquipListComp 渲染并处理购买。
|
||||||
*
|
*
|
||||||
@@ -1037,8 +1051,9 @@ export class MissionCardComp extends CCComp {
|
|||||||
// 清空旧列表
|
// 清空旧列表
|
||||||
this.equipsBoxNode.removeAllChildren();
|
this.equipsBoxNode.removeAllChildren();
|
||||||
|
|
||||||
// 从独立装备卡池获取所有装备
|
// 从装备卡池按权重抽取 3 张
|
||||||
for (const equip of EquipPoolList) {
|
const equips = drawEquipCards(3);
|
||||||
|
for (const equip of equips) {
|
||||||
const node = instantiate(this.equipPrefab);
|
const node = instantiate(this.equipPrefab);
|
||||||
this.equipsBoxNode.addChild(node);
|
this.equipsBoxNode.addChild(node);
|
||||||
const comp = node.getComponent(EquipListComp) || node.addComponent(EquipListComp);
|
const comp = node.getComponent(EquipListComp) || node.addComponent(EquipListComp);
|
||||||
@@ -1051,7 +1066,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 根据物品数量设置列表面板高度
|
// 根据物品数量设置列表面板高度
|
||||||
this.resizeListBox(this.equipsBoxNode, EquipPoolList.length);
|
this.resizeListBox(this.equipsBoxNode, equips.length);
|
||||||
|
|
||||||
// 默认显示装备面板
|
// 默认显示装备面板
|
||||||
if (this.equipsPanNode) {
|
if (this.equipsPanNode) {
|
||||||
@@ -1059,7 +1074,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mLogger.log(this.debugMode, "MissionCardComp", "populate equipments", {
|
mLogger.log(this.debugMode, "MissionCardComp", "populate equipments", {
|
||||||
count: EquipPoolList.length
|
count: equips.length
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1110,15 +1125,71 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.dispatchCardsToSkillSlots(cards);
|
this.dispatchCardsToSkillSlots(cards);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 将三个卡槽节点映射为 CardComp,形成固定顺序控制数组 */
|
// ======================== 装备/药品刷新按钮回调 ========================
|
||||||
private cacheCardComps() {
|
|
||||||
if (this.card4) {
|
private onEquipDrawTouchStart() {
|
||||||
this.card4.active = false;
|
this.playButtonPressAnim(this.equip_refresh);
|
||||||
|
}
|
||||||
|
private onEquipDrawTouchEnd() {
|
||||||
|
oops.audio.playEffect("music/button");
|
||||||
|
this.playButtonClickAnim(this.equip_refresh, () => this.onClickEquipRefresh());
|
||||||
|
}
|
||||||
|
private onEquipDrawTouchCancel() {
|
||||||
|
this.playButtonResetAnim(this.equip_refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onShopDrawTouchStart() {
|
||||||
|
this.playButtonPressAnim(this.shop_refresh);
|
||||||
|
}
|
||||||
|
private onShopDrawTouchEnd() {
|
||||||
|
oops.audio.playEffect("music/button");
|
||||||
|
this.playButtonClickAnim(this.shop_refresh, () => this.onClickShopRefresh());
|
||||||
|
}
|
||||||
|
private onShopDrawTouchCancel() {
|
||||||
|
this.playButtonResetAnim(this.shop_refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 装备刷新:扣费后重新抽取 3 张装备展示 */
|
||||||
|
private onClickEquipRefresh() {
|
||||||
|
const cost = MissionEconomy.getRefreshCost(this.refreshCost);
|
||||||
|
const success = MissionEconomy.executeRefresh(this.refreshCost);
|
||||||
|
if (!success) {
|
||||||
|
this.showSmallTip("refresh_coin");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.populateEquipments();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 药品刷新:扣费后重新抽取 3 张商品展示 */
|
||||||
|
private onClickShopRefresh() {
|
||||||
|
const cost = MissionEconomy.getRefreshCost(this.refreshCost);
|
||||||
|
const success = MissionEconomy.executeRefresh(this.refreshCost);
|
||||||
|
if (!success) {
|
||||||
|
this.showSmallTip("refresh_coin");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.populateShopItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将 cardPrefab 实例化为 3 个卡槽并映射为 CardComp,形成固定顺序控制数组 */
|
||||||
|
private cacheCardComps() {
|
||||||
|
// 清理旧卡槽(如果存在)
|
||||||
|
for (const comp of this.cardComps) {
|
||||||
|
if (comp && comp.node && comp.node.isValid) {
|
||||||
|
comp.node.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cardComps = [];
|
||||||
|
|
||||||
|
// 从预制体实例化 3 个卡槽
|
||||||
|
if (this.cardPrefab && this.cards_node && this.cards_node.isValid) {
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const node = instantiate(this.cardPrefab);
|
||||||
|
this.cards_node.addChild(node);
|
||||||
|
const comp = node.getComponent(CardComp) || node.addComponent(CardComp);
|
||||||
|
this.cardComps.push(comp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const nodes = [this.card1, this.card2, this.card3];
|
|
||||||
this.cardComps = nodes
|
|
||||||
.map(node => node?.getComponent(CardComp))
|
|
||||||
.filter((comp): comp is CardComp => !!comp);
|
|
||||||
|
|
||||||
const skillNodes = [this.skill_card1, this.skill_card2, this.skill_card3];
|
const skillNodes = [this.skill_card1, this.skill_card2, this.skill_card3];
|
||||||
this.skillCardComps = skillNodes
|
this.skillCardComps = skillNodes
|
||||||
@@ -1175,6 +1246,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
* - 初始化面板轮播布局(如尚未初始化)。
|
* - 初始化面板轮播布局(如尚未初始化)。
|
||||||
* - 滑动到英雄面板(index=0),作为默认展示。
|
* - 滑动到英雄面板(index=0),作为默认展示。
|
||||||
* - 激活 showHeros 按钮可见性。
|
* - 激活 showHeros 按钮可见性。
|
||||||
|
* - 显示卡牌面板(从底部滑入)。
|
||||||
*/
|
*/
|
||||||
private enterPreparePhase() {
|
private enterPreparePhase() {
|
||||||
if (!this.cards_node || !this.cards_node.isValid) return;
|
if (!this.cards_node || !this.cards_node.isValid) return;
|
||||||
@@ -1193,6 +1265,11 @@ export class MissionCardComp extends CCComp {
|
|||||||
nobg.active = !this.canDrawCards();
|
nobg.active = !this.canDrawCards();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 显示卡牌面板(默认进入准备阶段时展开)
|
||||||
|
if (this.cardPanNode && this.cardPanNode.isValid) {
|
||||||
|
this.cardPanNode.active = true;
|
||||||
|
this.cardPanNode.setPosition(0, this.cardPanelShowY, 0);
|
||||||
|
}
|
||||||
// 滑动到英雄面板
|
// 滑动到英雄面板
|
||||||
this.slideToPanel(0);
|
this.slideToPanel(0);
|
||||||
}
|
}
|
||||||
@@ -1431,7 +1508,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
});
|
});
|
||||||
if (this.skillCardComps) {
|
if (this.skillCardComps) {
|
||||||
this.skillCardComps.forEach(comp => {
|
this.skillCardComps.forEach(comp => {
|
||||||
if (comp) comp.clearBySystem();
|
if (comp) comp.applyCardData(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1502,6 +1579,30 @@ export class MissionCardComp extends CCComp {
|
|||||||
numLabel.string = `${MissionEconomy.getRefreshCost(this.refreshCost)}`;
|
numLabel.string = `${MissionEconomy.getRefreshCost(this.refreshCost)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.equip_refresh) {
|
||||||
|
const nobg = this.equip_refresh.getChildByName("nobg");
|
||||||
|
if (nobg) {
|
||||||
|
nobg.active = !this.canDrawCards();
|
||||||
|
}
|
||||||
|
const coinNode = this.equip_refresh.getChildByName("coin");
|
||||||
|
const numLabel = coinNode?.getChildByName("num")?.getComponent(Label);
|
||||||
|
if (numLabel) {
|
||||||
|
numLabel.string = `${MissionEconomy.getRefreshCost(this.refreshCost)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.shop_refresh) {
|
||||||
|
const nobg = this.shop_refresh.getChildByName("nobg");
|
||||||
|
if (nobg) {
|
||||||
|
nobg.active = !this.canDrawCards();
|
||||||
|
}
|
||||||
|
const coinNode = this.shop_refresh.getChildByName("coin");
|
||||||
|
const numLabel = coinNode?.getChildByName("num")?.getComponent(Label);
|
||||||
|
if (numLabel) {
|
||||||
|
numLabel.string = `${MissionEconomy.getRefreshCost(this.refreshCost)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateCoinAndCostUI() {
|
private updateCoinAndCostUI() {
|
||||||
|
|||||||
@@ -1,392 +1,198 @@
|
|||||||
/**
|
/**
|
||||||
* @file SCardComp.ts
|
* @file SCardComp.ts
|
||||||
* @description 技能卡牌槽位组件(UI 视图层)
|
* @description 技能商店单个技能卡项组件(UI 视图层 + 购买逻辑)
|
||||||
*
|
*
|
||||||
* 职责:
|
* 职责:
|
||||||
* 1. 管理技能卡牌槽位的显示和交互(点击使用)。
|
* 1. 渲染单个技能卡的显示信息(名称、等级、描述词条、图标)。
|
||||||
* 2. 渲染技能卡面。
|
* 2. 处理购买点击 → 扣费 → 派发 UseSkillCard 事件触发技能生效。
|
||||||
* 3. 触发使用时扣除费用并分发 UseSkillCard 事件。
|
*
|
||||||
|
* 技能使用机制:
|
||||||
|
* 购买后派发 GameEvent.UseSkillCard,
|
||||||
|
* 由 MissSkillsComp 接收并创建 SBox 实体 / SkillBoxComp 执行技能逻辑。
|
||||||
*/
|
*/
|
||||||
import { mLogger } from "../common/Logger";
|
import { mLogger } from "../common/Logger";
|
||||||
import { _decorator, Animation, AnimationClip, EventTouch, Label, Node, NodeEventType, Sprite, SpriteAtlas, Tween, tween, UIOpacity, Vec3, UITransform, Widget } from "cc";
|
import { _decorator, Node, Sprite, Label, NodeEventType } from "cc";
|
||||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||||
import { CardConfig, CardType, CKind } from "../common/config/CardSet";
|
import { CardConfig } from "../common/config/CardSet";
|
||||||
import { SkillCardList } from "../common/config/SCardSet";
|
import { SkillCardList } from "../common/config/SCardSet";
|
||||||
import { CardBgComp } from "./CardBgComp";
|
import { FieldSkillSet, SkillSet, SkillConfig } from "../common/config/SkillSet";
|
||||||
import { FieldSkillSet, SkillSet } from "../common/config/SkillSet";
|
|
||||||
import { GameEvent } from "../common/config/GameEvent";
|
|
||||||
import { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
|
import { GameEvent } from "../common/config/GameEvent";
|
||||||
import { smc } from "../common/SingletonModuleComp";
|
import { smc } from "../common/SingletonModuleComp";
|
||||||
import { MissionEconomy } from "./MissionEconomy";
|
import { MissionEconomy } from "./MissionEconomy";
|
||||||
|
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SCardComp —— 技能商店单个技能卡项
|
||||||
|
*
|
||||||
|
* 由 MissionCardComp.dispatchCardsToSkillSlots() 实例化。
|
||||||
|
* 以图标 + 名称 + 描述词条 + 购买按钮的形式呈现。
|
||||||
|
*/
|
||||||
@ccclass('SCardComp')
|
@ccclass('SCardComp')
|
||||||
@ecs.register('SCardComp', false)
|
@ecs.register('SCardComp', false)
|
||||||
export class SCardComp extends CCComp {
|
export class SCardComp extends CCComp {
|
||||||
private debugMode: boolean = true;
|
private debugMode: boolean = false;
|
||||||
|
|
||||||
@property(Node)
|
// ======================== 编辑器绑定节点 ========================
|
||||||
name_node: Node = null!
|
|
||||||
@property(Node)
|
|
||||||
icon_node: Node = null!
|
|
||||||
@property(Node)
|
|
||||||
cost_node: Node = null!
|
|
||||||
@property(Node)
|
|
||||||
Ckind_node: Node = null!
|
|
||||||
@property(Node)
|
|
||||||
BG_node: Node = null!
|
|
||||||
@property(Node)
|
|
||||||
info_node: Node = null!
|
|
||||||
|
|
||||||
card_cost: number = 0
|
/** 技能图标节点 */
|
||||||
card_type: CardType = CardType.Skill
|
@property({ type: Node })
|
||||||
card_uuid: number = 0
|
private icon_node: Node = null;
|
||||||
|
|
||||||
|
/** 背景节点 */
|
||||||
|
@property({ type: Node })
|
||||||
|
private bg_node: Node = null;
|
||||||
|
|
||||||
|
/** 技能名称 */
|
||||||
|
@property(Label)
|
||||||
|
private name_label: Label = null;
|
||||||
|
|
||||||
|
/** 描述词条 1 */
|
||||||
|
@property(Label)
|
||||||
|
private fied1: Label = null;
|
||||||
|
/** 描述词条 2 */
|
||||||
|
@property(Label)
|
||||||
|
private fied2: Label = null;
|
||||||
|
/** 描述词条 3 */
|
||||||
|
@property(Label)
|
||||||
|
private fied3: Label = null;
|
||||||
|
|
||||||
|
/** 等级标签 */
|
||||||
|
@property(Label)
|
||||||
|
private level_label: Label = null;
|
||||||
|
|
||||||
|
/** 购买按钮节点 */
|
||||||
|
@property({ type: Node })
|
||||||
|
private buy_node: Node = null;
|
||||||
|
|
||||||
|
// ======================== 运行时状态 ========================
|
||||||
|
|
||||||
|
/** 当前技能的卡牌配置 */
|
||||||
private cardData: CardConfig | null = null;
|
private cardData: CardConfig | null = null;
|
||||||
private touchStartY: number = 0;
|
/** 是否已购买 */
|
||||||
private touchStartX: number = 0;
|
private isPurchased: boolean = false;
|
||||||
private isDragging: boolean = false;
|
|
||||||
private isUsing: boolean = false;
|
// ======================== 生命周期 ========================
|
||||||
private restPosition: Vec3 = new Vec3();
|
|
||||||
private hasFixedBasePosition: boolean = false;
|
|
||||||
private fixedBaseY: number = 0;
|
|
||||||
private fixedBaseZ: number = 0;
|
|
||||||
private opacityComp: UIOpacity | null = null;
|
|
||||||
private iconVisualToken: number = 0;
|
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.bindEvents();
|
this.buy_node?.on(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||||
this.restPosition = this.node.position.clone();
|
|
||||||
this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
|
|
||||||
this.opacityComp.opacity = 255;
|
|
||||||
this.applyEmptyUI();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy() {
|
onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
this.unbindEvents();
|
if (this.buy_node && this.buy_node.isValid) {
|
||||||
|
this.buy_node.off(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init() { }
|
// ======================== 数据初始化 ========================
|
||||||
start() {
|
|
||||||
this.node.active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
applyDrawCard(data: CardConfig | null): boolean {
|
/**
|
||||||
if (!data) return false;
|
* 初始化技能卡数据并渲染 UI
|
||||||
|
*
|
||||||
|
* @param data 技能卡配置,传 null 时清空显示
|
||||||
|
*/
|
||||||
|
applyCardData(data: CardConfig | null): void {
|
||||||
this.cardData = data;
|
this.cardData = data;
|
||||||
this.card_uuid = data.uuid;
|
this.isPurchased = false;
|
||||||
this.card_type = data.type;
|
if (data) {
|
||||||
this.card_cost = Math.floor(data.cost ?? 0);
|
this.node.active = true;
|
||||||
|
this.updateUI();
|
||||||
this.node.active = true;
|
} else {
|
||||||
this.applyCardUI();
|
|
||||||
this.playRefreshAnim();
|
|
||||||
mLogger.log(this.debugMode, "SCardComp", "skill card updated", {
|
|
||||||
uuid: this.card_uuid,
|
|
||||||
cost: this.card_cost
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
useCard(): CardConfig | null {
|
|
||||||
if (!this.cardData || this.isUsing) return null;
|
|
||||||
const cardCost = this.card_cost;
|
|
||||||
|
|
||||||
const success = MissionEconomy.spendCoin(cardCost);
|
|
||||||
if (!success) {
|
|
||||||
oops.message.dispatchEvent(GameEvent.ShowSmallTip, "buy_coin");
|
|
||||||
this.playReboundAnim();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
smc.vmdata.scores.refresh_hit_count++;
|
|
||||||
this.isUsing = true;
|
|
||||||
const used = this.cardData;
|
|
||||||
|
|
||||||
this.playUseDisappearAnim(() => {
|
|
||||||
this.clearAfterUse();
|
|
||||||
this.isUsing = false;
|
|
||||||
oops.message.dispatchEvent(GameEvent.UseSkillCard, used);
|
|
||||||
// 派发 CardUsed 让 MissionCardComp 刷新整个技能卡池
|
|
||||||
oops.message.dispatchEvent(GameEvent.CardUsed, this);
|
|
||||||
});
|
|
||||||
return used;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasCard(): boolean {
|
|
||||||
return !!this.cardData;
|
|
||||||
}
|
|
||||||
|
|
||||||
setSlotPosition(x: number) {
|
|
||||||
const current = this.node.position;
|
|
||||||
if (!this.hasFixedBasePosition) {
|
|
||||||
this.fixedBaseY = current.y;
|
|
||||||
this.fixedBaseZ = current.z;
|
|
||||||
this.hasFixedBasePosition = true;
|
|
||||||
}
|
|
||||||
this.restPosition = new Vec3(x, this.fixedBaseY, this.fixedBaseZ);
|
|
||||||
if (!this.isDragging && !this.isUsing) {
|
|
||||||
this.node.setPosition(this.restPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearBySystem() {
|
|
||||||
Tween.stopAllByTarget(this.node);
|
|
||||||
if (this.opacityComp) {
|
|
||||||
Tween.stopAllByTarget(this.opacityComp);
|
|
||||||
this.opacityComp.opacity = 255;
|
|
||||||
}
|
|
||||||
this.cardData = null;
|
|
||||||
this.card_uuid = 0;
|
|
||||||
this.card_cost = 0;
|
|
||||||
this.isDragging = false;
|
|
||||||
this.isUsing = false;
|
|
||||||
this.node.setPosition(this.restPosition);
|
|
||||||
this.node.setScale(new Vec3(1, 1, 1));
|
|
||||||
this.applyEmptyUI();
|
|
||||||
this.node.active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private clearAfterUse() {
|
|
||||||
Tween.stopAllByTarget(this.node);
|
|
||||||
if (this.opacityComp) {
|
|
||||||
Tween.stopAllByTarget(this.opacityComp);
|
|
||||||
this.opacityComp.opacity = 255;
|
|
||||||
}
|
|
||||||
this.cardData = null;
|
|
||||||
this.card_uuid = 0;
|
|
||||||
this.card_cost = 0;
|
|
||||||
this.isDragging = false;
|
|
||||||
this.node.setPosition(this.restPosition);
|
|
||||||
this.node.setScale(new Vec3(1, 1, 1));
|
|
||||||
this.applyEmptyUI();
|
|
||||||
this.node.active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bindEvents() {
|
|
||||||
this.node.on(NodeEventType.TOUCH_START, this.onCardTouchStart, this);
|
|
||||||
this.node.on(NodeEventType.TOUCH_MOVE, this.onCardTouchMove, this);
|
|
||||||
this.node.on(NodeEventType.TOUCH_END, this.onCardTouchEnd, this);
|
|
||||||
this.node.on(NodeEventType.TOUCH_CANCEL, this.onCardTouchCancel, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private unbindEvents() {
|
|
||||||
if (this.node && this.node.isValid) {
|
|
||||||
this.node.off(NodeEventType.TOUCH_START, this.onCardTouchStart, this);
|
|
||||||
this.node.off(NodeEventType.TOUCH_MOVE, this.onCardTouchMove, this);
|
|
||||||
this.node.off(NodeEventType.TOUCH_END, this.onCardTouchEnd, this);
|
|
||||||
this.node.off(NodeEventType.TOUCH_CANCEL, this.onCardTouchCancel, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onCardTouchStart(event: EventTouch) {
|
|
||||||
if (!this.cardData || this.isUsing) return;
|
|
||||||
this.touchStartY = event.getUILocation().y;
|
|
||||||
this.touchStartX = event.getUILocation().x;
|
|
||||||
this.isDragging = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private onCardTouchMove(event: EventTouch) {
|
|
||||||
if (!this.isDragging || !this.cardData || this.isUsing) return;
|
|
||||||
// 技能卡不支持上拉移动
|
|
||||||
}
|
|
||||||
|
|
||||||
private onCardTouchEnd(event: EventTouch) {
|
|
||||||
if (!this.isDragging || !this.cardData || this.isUsing) return;
|
|
||||||
const endY = event.getUILocation().y;
|
|
||||||
const endX = event.getUILocation().x;
|
|
||||||
const deltaY = endY - this.touchStartY;
|
|
||||||
const deltaX = endX - this.touchStartX;
|
|
||||||
this.isDragging = false;
|
|
||||||
|
|
||||||
// 点击触发
|
|
||||||
if (Math.abs(deltaY) < 20 && Math.abs(deltaX) < 20) {
|
|
||||||
const used = this.useCard();
|
|
||||||
if (!used) {
|
|
||||||
this.playReboundAnim();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.playReboundAnim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private onCardTouchCancel() {
|
|
||||||
if (!this.isDragging || this.isUsing) return;
|
|
||||||
this.isDragging = false;
|
|
||||||
this.playReboundAnim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private applyCardUI() {
|
|
||||||
if (!this.cardData) {
|
|
||||||
this.applyEmptyUI();
|
this.applyEmptyUI();
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.iconVisualToken += 1;
|
|
||||||
if (this.opacityComp) this.opacityComp.opacity = 255;
|
|
||||||
this.node.setPosition(this.restPosition.x, this.restPosition.y, this.restPosition.z);
|
|
||||||
|
|
||||||
const kindName = CKind[this.cardData.kind];
|
|
||||||
|
|
||||||
if (this.BG_node) {
|
|
||||||
const bgLv = this.cardData.base_pool_lv ?? this.cardData.pool_lv;
|
|
||||||
this.BG_node.children.forEach(child => {
|
|
||||||
child.active = (child.name === kindName);
|
|
||||||
const bg = child.getComponent(CardBgComp);
|
|
||||||
if (bg) child.active ? bg.apply(bgLv) : bg.clear();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.Ckind_node) {
|
|
||||||
this.Ckind_node.children.forEach(child => {
|
|
||||||
child.active = (child.name === kindName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.node.children.forEach(child => {
|
|
||||||
const widget = child.getComponent(Widget);
|
|
||||||
if (widget) widget.updateAlignment();
|
|
||||||
child.children.forEach(subChild => {
|
|
||||||
const subWidget = subChild.getComponent(Widget);
|
|
||||||
if (subWidget) subWidget.updateAlignment();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const s_uuid = this.cardData.skill ?? this.card_uuid;
|
|
||||||
const skill = SkillSet[s_uuid];
|
|
||||||
const skillCard = SkillCardList.find(c => c.uuid === this.card_uuid);
|
|
||||||
const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1));
|
|
||||||
const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : "";
|
|
||||||
this.setLabel(this.name_node, `${spSuffix}${skillCard?.name || skill?.name || ""}${spSuffix}`);
|
|
||||||
|
|
||||||
if (this.info_node) {
|
|
||||||
this.info_node.active = true;
|
|
||||||
// 驻场技能卡描述取 FieldSkillSet;其他卡牌取卡牌/技能配置
|
|
||||||
let desc = "";
|
|
||||||
if (this.cardData.field && this.cardData.field.length > 0) {
|
|
||||||
desc = FieldSkillSet[this.cardData.field[0]]?.info || "";
|
|
||||||
}
|
|
||||||
if (!desc) {
|
|
||||||
desc = skillCard?.info || skill?.info || this.cardData?.info || "";
|
|
||||||
}
|
|
||||||
// 与 HlistComp 保持一致:优先查找名为 "info" 的子节点上的 Label
|
|
||||||
const infoLabel = this.info_node.getChildByName("info")?.getComponent(Label)
|
|
||||||
|| this.info_node.getComponent(Label)
|
|
||||||
|| this.info_node.getComponentInChildren(Label);
|
|
||||||
if (infoLabel) infoLabel.string = desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.cost_node) {
|
|
||||||
this.cost_node.active = true;
|
|
||||||
const numNode = this.cost_node.getChildByName("num");
|
|
||||||
if (numNode) {
|
|
||||||
this.setLabel(numNode, `${this.card_cost}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const iconNode = this.icon_node as Node;
|
|
||||||
if (iconNode) {
|
|
||||||
iconNode.setScale(new Vec3(1, 1, 1));
|
|
||||||
this.clearIconAnimation(iconNode);
|
|
||||||
// 驻场技能卡(skill=undefined 但有 field)使用 FieldSkillSet 中的图标
|
|
||||||
let iconId: string;
|
|
||||||
if (!this.cardData.skill && this.cardData.field && this.cardData.field.length > 0) {
|
|
||||||
const fieldUuid = this.cardData.field[0];
|
|
||||||
iconId = FieldSkillSet[fieldUuid]?.icon || `${fieldUuid}`;
|
|
||||||
} else {
|
|
||||||
iconId = skill?.icon || `${s_uuid}`;
|
|
||||||
}
|
|
||||||
this.updateIcon(iconNode, iconId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private playRefreshAnim() {
|
/** 清空 UI 显示 */
|
||||||
Tween.stopAllByTarget(this.node);
|
|
||||||
this.node.setPosition(this.restPosition);
|
|
||||||
this.node.setScale(new Vec3(0.92, 0.92, 1));
|
|
||||||
tween(this.node)
|
|
||||||
.to(0.08, { scale: new Vec3(1.06, 1.06, 1) })
|
|
||||||
.to(0.1, { scale: new Vec3(1, 1, 1) })
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private playReboundAnim() {
|
|
||||||
Tween.stopAllByTarget(this.node);
|
|
||||||
tween(this.node)
|
|
||||||
.to(0.12, {
|
|
||||||
position: new Vec3(this.restPosition.x, this.restPosition.y, this.restPosition.z),
|
|
||||||
scale: new Vec3(1, 1, 1)
|
|
||||||
})
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private playUseDisappearAnim(onComplete: () => void) {
|
|
||||||
const targetPos = new Vec3(this.restPosition.x, this.restPosition.y + 120, this.restPosition.z);
|
|
||||||
Tween.stopAllByTarget(this.node);
|
|
||||||
if (this.opacityComp) {
|
|
||||||
Tween.stopAllByTarget(this.opacityComp);
|
|
||||||
this.opacityComp.opacity = 255;
|
|
||||||
tween(this.opacityComp)
|
|
||||||
.to(0.18, { opacity: 0 })
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
tween(this.node)
|
|
||||||
.to(0.18, {
|
|
||||||
position: targetPos,
|
|
||||||
scale: new Vec3(0.8, 0.8, 1)
|
|
||||||
})
|
|
||||||
.call(onComplete)
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private applyEmptyUI() {
|
private applyEmptyUI() {
|
||||||
if (this.BG_node) {
|
if (this.name_label) this.name_label.string = "";
|
||||||
this.BG_node.children.forEach(child => {
|
if (this.level_label) this.level_label.string = "";
|
||||||
child.active = false;
|
const fieldLabels = [this.fied1, this.fied2, this.fied3];
|
||||||
const bg = child.getComponent(CardBgComp);
|
for (const label of fieldLabels) {
|
||||||
if (bg) bg.clear();
|
if (label) {
|
||||||
});
|
label.string = "";
|
||||||
}
|
label.node.active = false;
|
||||||
this.node.children.forEach(child => {
|
|
||||||
const widget = child.getComponent(Widget);
|
|
||||||
if (widget) widget.updateAlignment();
|
|
||||||
child.children.forEach(subChild => {
|
|
||||||
const subWidget = subChild.getComponent(Widget);
|
|
||||||
if (subWidget) subWidget.updateAlignment();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.iconVisualToken += 1;
|
|
||||||
this.setLabel(this.name_node, "");
|
|
||||||
if (this.cost_node) {
|
|
||||||
const numNode = this.cost_node.getChildByName("num");
|
|
||||||
if (numNode) {
|
|
||||||
this.setLabel(numNode, "");
|
|
||||||
}
|
}
|
||||||
this.cost_node.active = false;
|
|
||||||
}
|
}
|
||||||
if (this.Ckind_node) {
|
|
||||||
this.Ckind_node.children.forEach(child => {
|
|
||||||
child.active = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.info_node) this.info_node.active = false;
|
|
||||||
|
|
||||||
if (this.icon_node) {
|
if (this.icon_node) {
|
||||||
(this.icon_node as Node).setScale(new Vec3(1, 1, 1));
|
|
||||||
this.clearIconAnimation(this.icon_node as Node);
|
|
||||||
const sprite = this.icon_node.getComponent(Sprite) || this.icon_node.getComponentInChildren(Sprite);
|
const sprite = this.icon_node.getComponent(Sprite) || this.icon_node.getComponentInChildren(Sprite);
|
||||||
if (sprite) sprite.spriteFrame = null;
|
if (sprite) sprite.spriteFrame = null;
|
||||||
}
|
}
|
||||||
|
if (this.buy_node) {
|
||||||
|
this.buy_node.active = false;
|
||||||
|
}
|
||||||
|
this.node.active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setLabel(node: Node | null, value: string) {
|
// ======================== UI 渲染 ========================
|
||||||
if (!node) return;
|
|
||||||
const label = node.getComponent(Label) || node.getComponentInChildren(Label);
|
updateUI() {
|
||||||
if (label) label.string = value;
|
if (!this.cardData) return;
|
||||||
|
|
||||||
|
const skillCard = SkillCardList.find(c => c.uuid === this.cardData!.uuid);
|
||||||
|
const skill = this.cardData.skill ? SkillSet[this.cardData.skill] : null;
|
||||||
|
|
||||||
|
// 名称
|
||||||
|
if (this.name_label) {
|
||||||
|
this.name_label.string = skillCard?.name || skill?.name || this.cardData.name || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等级标签(★ 表示卡牌等级)
|
||||||
|
if (this.level_label) {
|
||||||
|
const lv = this.cardData.card_lv ?? 1;
|
||||||
|
this.level_label.string = lv >= 2 ? "★".repeat(lv - 1) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 描述词条(最多显示 3 条,对应 fied1/fied2/fied3)
|
||||||
|
const fieldLabels = [this.fied1, this.fied2, this.fied3];
|
||||||
|
const desc = this.getDescription(skillCard, skill);
|
||||||
|
for (let i = 0; i < fieldLabels.length; i++) {
|
||||||
|
if (!fieldLabels[i]) continue;
|
||||||
|
fieldLabels[i].string = i === 0 ? desc : "";
|
||||||
|
fieldLabels[i].node.active = i === 0 && !!desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图标
|
||||||
|
if (this.icon_node) {
|
||||||
|
const iconId = this.getIconId(skill);
|
||||||
|
if (iconId) {
|
||||||
|
this.updateIcon(this.icon_node, iconId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 购买按钮费用显示
|
||||||
|
if (this.buy_node) {
|
||||||
|
this.buy_node.active = true;
|
||||||
|
const costLabel = this.buy_node.getChildByName("num")?.getComponent(Label)
|
||||||
|
|| this.buy_node.getComponentInChildren(Label);
|
||||||
|
if (costLabel) {
|
||||||
|
costLabel.string = `${this.cardData.cost ?? 0}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取技能描述:驻场技能取 FieldSkillSet,其余取技能/卡牌配置 */
|
||||||
|
private getDescription(skillCard: CardConfig | undefined, skill: SkillConfig | null): string {
|
||||||
|
if (!this.cardData) return "";
|
||||||
|
if (this.cardData.field && this.cardData.field.length > 0) {
|
||||||
|
return FieldSkillSet[this.cardData.field[0]]?.info || "";
|
||||||
|
}
|
||||||
|
return skillCard?.info || skill?.info || this.cardData.info || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取技能图标 id:驻场技能取 FieldSkillSet.icon,其余取 SkillSet.icon */
|
||||||
|
private getIconId(skill: SkillConfig | null): string {
|
||||||
|
if (!this.cardData) return "";
|
||||||
|
if (!this.cardData.skill && this.cardData.field && this.cardData.field.length > 0) {
|
||||||
|
const fieldUuid = this.cardData.field[0];
|
||||||
|
return FieldSkillSet[fieldUuid]?.icon || `${fieldUuid}`;
|
||||||
|
}
|
||||||
|
return skill?.icon || `${this.cardData.skill ?? this.cardData.uuid}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新图标精灵 */
|
||||||
private updateIcon(node: Node, iconId: string) {
|
private updateIcon(node: Node, iconId: string) {
|
||||||
if (!node || !iconId) return;
|
if (!node || !iconId) return;
|
||||||
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
||||||
@@ -397,17 +203,60 @@ export class SCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearIconAnimation(node: Node) {
|
// ======================== 购买逻辑 ========================
|
||||||
const anim = node?.getComponent(Animation);
|
|
||||||
if (!anim) return;
|
/**
|
||||||
anim.stop();
|
* 购买点击处理:
|
||||||
const clips = (anim as any).clips as AnimationClip[] | undefined;
|
* 1. 扣除金币(失败则提示并中止)。
|
||||||
if (!clips || clips.length === 0) return;
|
* 2. 派发 UseSkillCard 事件,触发技能生效流程。
|
||||||
[...clips].forEach(clip => anim.removeClip(clip, true));
|
* 3. 标记已购买,隐藏购买按钮。
|
||||||
|
*/
|
||||||
|
private onBuyClick() {
|
||||||
|
if (!this.cardData || this.isPurchased) return;
|
||||||
|
|
||||||
|
oops.audio.playEffect("music/button");
|
||||||
|
|
||||||
|
// 扣费
|
||||||
|
const cost = this.cardData.cost ?? 0;
|
||||||
|
const success = MissionEconomy.spendCoin(cost);
|
||||||
|
if (!success) {
|
||||||
|
oops.message.dispatchEvent(GameEvent.ShowSmallTip, "buy_coin");
|
||||||
|
mLogger.log(this.debugMode, "SCardComp", "purchase failed: not enough coin", {
|
||||||
|
uuid: this.cardData.uuid,
|
||||||
|
cost
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
smc.vmdata.scores.refresh_hit_count++;
|
||||||
|
|
||||||
|
// 派发 UseSkillCard,技能生效流程由 MissSkillsComp 接收处理
|
||||||
|
oops.message.dispatchEvent(GameEvent.UseSkillCard, this.cardData);
|
||||||
|
|
||||||
|
// 标记已购买
|
||||||
|
this.isPurchased = true;
|
||||||
|
if (this.buy_node) {
|
||||||
|
this.buy_node.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLogger.log(this.debugMode, "SCardComp", "skill card purchased", {
|
||||||
|
uuid: this.cardData.uuid,
|
||||||
|
cost
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ECS 组件移除时的释放钩子:销毁节点 */
|
/** 标记为已购买状态 */
|
||||||
|
setPurchased(): void {
|
||||||
|
this.isPurchased = true;
|
||||||
|
if (this.buy_node) {
|
||||||
|
this.buy_node.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ECS 组件移除时销毁节点 */
|
||||||
reset() {
|
reset() {
|
||||||
this.node.destroy();
|
if (this.node && this.node.isValid) {
|
||||||
|
this.node.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user