feat(mission): 新增装备和商店刷新功能,重构抽卡逻辑

1. 新增按权重抽取装备卡的工具函数,支持去重后补齐
2. 重构任务界面卡牌组件:
   - 拆分英雄/装备/商店面板节点绑定,调整属性顺序
   - 新增装备和商店刷新按钮的交互逻辑
   - 替换原固定加载装备/商品列表为按权重随机抽取3个
3. 重构技能卡牌组件,改为技能商店卡项实现,简化原有逻辑
4. 修复prefab中的节点属性偏移和引用关系
This commit is contained in:
panFD
2026-07-25 21:44:17 +08:00
parent 9c520cf917
commit ffafefcc72
4 changed files with 393 additions and 387 deletions

View File

@@ -41,8 +41,8 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu
import { GameEvent } from "../common/config/GameEvent";
import { CardConfig, CardPoolList, CardType, drawCardsByRule, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet";
import { drawSkillCards } from "../common/config/SCardSet";
import { EquipPoolList } from "../common/config/EquipSet";
import { ItemCardList, drawItemCards } from "../common/config/ICardSet";
import { drawEquipCards } from "../common/config/EquipSet";
import { drawItemCards } from "../common/config/ICardSet";
import { CardComp } from "./CardComp";
import { SCardComp } from "./SCardComp";
import { EquipListComp } from "./EquipListComp";
@@ -85,7 +85,9 @@ export class MissionCardComp extends CCComp {
// ======================== 编辑器绑定节点 ========================
/** 卡牌面板根节点(战斗阶段收起,准备阶段展开) */
/** 英雄卡牌面板根节点(战斗阶段收起,准备阶段展开) */
@property(Node)
showCards: Node = null!
@property(Node)
cards_node: Node = null!
/** 卡牌槽位 1 节点 */
@@ -97,36 +99,47 @@ export class MissionCardComp extends CCComp {
/** 卡牌槽位 3 节点 */
@property(Node)
card3: Node = null!
/** 卡牌槽位 4 节点 */
@property(Node)
card4: Node = null!
/** 抽卡(刷新)按钮节点 */
@property(Node)
cards_chou: Node = null!
@property(Node)
nock_node: Node = null!
/** 英雄卡牌池cards_node显示隐藏 */
/** 英雄面板cards_node显示隐藏 */
@property(Node)
showHeros: Node = null!
@property(Node)
closeHeros: Node = null!
@property(Node)
herosPanNode: Node = null!
@property(Node)
herosBoxNode: Node = null!
@property(Prefab)
heroPrefab: Prefab = null!
@property(Node)
showEquips: Node = null!
@property(Node)
closeEquips: Node = null!
@property(Node)
showSkills: Node = null!
@property(Node)
closeSkills: Node = null!
@property(Node)
equipsPanNode: Node = null!
@property(Node)
equipsBoxNode: Node = null!
@property(Prefab)
equipPrefab: Prefab = null!
@property(Node)
showSkills: Node = null!
@property(Node)
closeSkills: Node = null!
@property(Node)
skillPanNode: Node = null!
@property(Node)
skillBoxNode: Node = null!
@property(Prefab)
skillPrefab: Prefab = null!
@property(Node)
showShop: Node = null!
@property(Node)
@@ -138,6 +151,7 @@ export class MissionCardComp extends CCComp {
@property(Prefab)
itemPrefab: Prefab = null!
/** 卡池升级按钮节点(已废弃,保留节点引用防止 editor 报错) */
@property(Node)
cards_up: Node = null!
/** 金币显示节点(含 icon + num 子节点) */
@@ -171,6 +185,13 @@ export class MissionCardComp extends CCComp {
@property(Node)
skill_refresh_num_node: Node = null!
/** 装备刷新按钮节点 */
@property(Node)
equip_refresh: Node = null!;
/** 药品刷新按钮节点 */
@property(Node)
shop_refresh: Node = null!;
// ======================== 运行时状态 ========================
/** 三个槽位对应的 CardComp 控制器缓存(有序数组) */
@@ -413,6 +434,16 @@ export class MissionCardComp extends CCComp {
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_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);
}
// ======================== 事件回调 ========================
@@ -565,7 +596,7 @@ export class MissionCardComp extends CCComp {
/**
* 填充商品列表:
* 从 ICardSet 的 ItemCardList 获取所有商品卡,
* 从 ICardSet 按权重抽取 3 张商品卡,
* 实例化 itemPrefab 到 shopBoxNode
* 每个商品项由 ItemListComp 渲染并处理购买。
*
@@ -578,8 +609,9 @@ export class MissionCardComp extends CCComp {
// 清空旧列表
this.shopBoxNode.removeAllChildren();
// 从商品卡池获取所有商品
for (const item of ItemCardList) {
// 从商品卡池按权重抽取 3 张
const items = drawItemCards(3);
for (const item of items) {
const node = instantiate(this.itemPrefab);
this.shopBoxNode.addChild(node);
const comp = node.getComponent(ItemListComp) || node.addComponent(ItemListComp);
@@ -592,7 +624,7 @@ export class MissionCardComp extends CCComp {
}
// 根据物品数量设置列表面板高度
this.resizeListBox(this.shopBoxNode, ItemCardList.length);
this.resizeListBox(this.shopBoxNode, items.length);
// 默认显示商店面板
if (this.shopPanNode) {
@@ -600,7 +632,7 @@ export class MissionCardComp extends CCComp {
}
mLogger.log(this.debugMode, "MissionCardComp", "populate shop items", {
count: ItemCardList.length
count: items.length
});
}
@@ -615,7 +647,7 @@ export class MissionCardComp extends CCComp {
if (!this.skillCardComps) return;
for (let i = 0; i < this.skillCardComps.length; i++) {
if (this.skillCardComps[i]) {
this.skillCardComps[i].applyDrawCard(cards[i] ?? null);
this.skillCardComps[i].applyCardData(cards[i] ?? null);
}
}
}
@@ -701,6 +733,16 @@ export class MissionCardComp extends CCComp {
if (this.closeShop && this.closeShop.isValid) {
this.closeShop.off(NodeEventType.TOUCH_END, this.onCloseShopClick, this);
}
if (this.equip_refresh && this.equip_refresh.isValid) {
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);
}
}
/**
@@ -1025,7 +1067,7 @@ export class MissionCardComp extends CCComp {
/**
* 填充装备列表:
* 从 CardPoolList 获取所有装备卡trigger_type=Field
* 从 EquipPoolList 按权重抽取 3 张装备卡
* 实例化 equipPrefab 到 equipsBoxNode
* 每个装备项由 EquipListComp 渲染并处理购买。
*
@@ -1037,8 +1079,9 @@ export class MissionCardComp extends CCComp {
// 清空旧列表
this.equipsBoxNode.removeAllChildren();
// 从独立装备卡池获取所有装备
for (const equip of EquipPoolList) {
// 从装备卡池按权重抽取 3 张
const equips = drawEquipCards(3);
for (const equip of equips) {
const node = instantiate(this.equipPrefab);
this.equipsBoxNode.addChild(node);
const comp = node.getComponent(EquipListComp) || node.addComponent(EquipListComp);
@@ -1051,7 +1094,7 @@ export class MissionCardComp extends CCComp {
}
// 根据物品数量设置列表面板高度
this.resizeListBox(this.equipsBoxNode, EquipPoolList.length);
this.resizeListBox(this.equipsBoxNode, equips.length);
// 默认显示装备面板
if (this.equipsPanNode) {
@@ -1059,7 +1102,7 @@ export class MissionCardComp extends CCComp {
}
mLogger.log(this.debugMode, "MissionCardComp", "populate equipments", {
count: EquipPoolList.length
count: equips.length
});
}
@@ -1110,11 +1153,54 @@ export class MissionCardComp extends CCComp {
this.dispatchCardsToSkillSlots(cards);
}
// ======================== 装备/药品刷新按钮回调 ========================
private onEquipDrawTouchStart() {
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();
}
/** 将三个卡槽节点映射为 CardComp形成固定顺序控制数组 */
private cacheCardComps() {
if (this.card4) {
this.card4.active = false;
}
const nodes = [this.card1, this.card2, this.card3];
this.cardComps = nodes
.map(node => node?.getComponent(CardComp))
@@ -1431,7 +1517,7 @@ export class MissionCardComp extends CCComp {
});
if (this.skillCardComps) {
this.skillCardComps.forEach(comp => {
if (comp) comp.clearBySystem();
if (comp) comp.applyCardData(null);
});
}
}
@@ -1502,6 +1588,30 @@ export class MissionCardComp extends CCComp {
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() {