feat(shop): 新增商品/药品卡池与商店系统

1. 新增ICardSet.ts定义商品卡池配置,包含基础/高级恢复类、属性强化类道具
2. 为MissionCardComp添加商品商店逻辑,支持购买记录追踪与显隐切换
3. 重构ItemListComp为通用商店列表组件,适配装备与商品两种商店类型
This commit is contained in:
panFD
2026-07-22 21:42:00 +08:00
parent 90db23e7c9
commit afbb349c25
3 changed files with 352 additions and 50 deletions

View File

@@ -42,6 +42,7 @@ 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 { CardComp } from "./CardComp";
import { SCardComp } from "./SCardComp";
import { EquipListComp } from "./EquipListComp";
@@ -53,6 +54,7 @@ import { HeroViewComp } from "../hero/HeroViewComp";
import { FacSet, FightSet } from "../common/config/GameSet";
import { MissionEconomy } from "./MissionEconomy";
import { UIID } from "../common/config/GameUIConfig";
import { ItemListComp } from "./ItemListComp";
const { ccclass, property } = _decorator;
@@ -175,6 +177,8 @@ export class MissionCardComp extends CCComp {
private skillCardComps: SCardComp[] = [];
/** 已购买装备的 UUID 集合(跨波次保持,防止重复购买) */
private purchasedEquipUuids: Set<number> = new Set();
/** 已购买商品的 UUID 集合(跨波次保持,防止重复购买) */
private purchasedItemUuids: Set<number> = new Set();
/** 是否已缓存卡牌面板基准缩放 */
private hasCachedCardsBaseScale: boolean = false;
/** 卡牌面板基准缩放(从场景读取) */
@@ -236,6 +240,12 @@ export class MissionCardComp extends CCComp {
if (this.closeSkills && this.closeSkills.isValid) {
this.closeSkills.off(NodeEventType.TOUCH_END, this.onCloseSkillsClick, this);
}
if (this.showShop && this.showShop.isValid) {
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();
}
@@ -282,6 +292,10 @@ export class MissionCardComp extends CCComp {
this.purchasedEquipUuids.clear();
this.populateEquipments();
// 重置商品购买记录并填充商品商店
this.purchasedItemUuids.clear();
this.populateShopItems();
// 首次进入准备阶段自动抽取一次技能卡,后续刷新只能通过技能刷新按钮触发
this.initSkillCardsOnce();
@@ -292,6 +306,7 @@ export class MissionCardComp extends CCComp {
onMissionEnd() {
this.clearAllCards();
this.clearEquipments();
this.clearShopItems();
if (this.node && this.node.isValid) {
this.node.active = false;
}
@@ -337,6 +352,8 @@ export class MissionCardComp extends CCComp {
oops.message.on(GameEvent.UseSkillCard, this.onUseSkillCard, this);
// 监听装备购买事件,追踪已购记录
oops.message.on(GameEvent.UseEquipCard, this.onUseEquipCard, this);
// 监听商品购买事件,追踪已购记录
oops.message.on(GameEvent.UseItemCard, this.onUseItemCard, this);
oops.message.on(GameEvent.UseSpecialCard, this.onUseSpecialCard, this);
oops.message.on(GameEvent.ShowSmallTip, this.onShowSmallTip, this);
oops.message.on(GameEvent.CardUsed, this.onCardUsed, this);
@@ -361,6 +378,11 @@ export class MissionCardComp extends CCComp {
/** 关闭技能卡池按钮 */
this.closeSkills?.on(NodeEventType.TOUCH_END, this.onCloseSkillsClick, 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_END, this.onSkillDrawTouchEnd, this);
@@ -512,6 +534,70 @@ export class MissionCardComp extends CCComp {
this.skill_card_node.active = false;
}
// ======================== 商店面板 ========================
/**
* 商店显隐切换按钮回调:
* 切换 shopPanNode 的 active 状态。
*/
private onShowShopClick() {
if (!this.shopPanNode || !this.shopPanNode.isValid) return;
oops.audio.playEffect("music/button");
this.shopPanNode.active = !this.shopPanNode.active;
}
/** 关闭商店按钮回调 */
private onCloseShopClick() {
if (!this.shopPanNode || !this.shopPanNode.isValid) return;
oops.audio.playEffect("music/button");
this.shopPanNode.active = false;
}
/**
* 填充商品列表:
* 从 ICardSet 的 ItemCardList 获取所有商品卡,
* 实例化 itemPrefab 到 shopBoxNode
* 每个商品项由 ItemListComp 渲染并处理购买。
*
* 商品为一次性 buff 技能Instant 触发t_times=1
* 购买后立即生效,由 MissSkillsComp 统一处理技能逻辑。
*/
private populateShopItems() {
if (!this.shopBoxNode || !this.itemPrefab) return;
// 清空旧列表
this.shopBoxNode.removeAllChildren();
// 从商品卡池获取所有商品
for (const item of ItemCardList) {
const node = instantiate(this.itemPrefab);
this.shopBoxNode.addChild(node);
const comp = node.getComponent(ItemListComp) || node.addComponent(ItemListComp);
comp.applyCardData(item);
// 已购买的商品标记为已购
if (this.purchasedItemUuids.has(item.uuid)) {
comp.setPurchased();
}
}
// 默认显示商店面板
if (this.shopPanNode) {
this.shopPanNode.active = true;
}
mLogger.log(this.debugMode, "MissionCardComp", "populate shop items", {
count: ItemCardList.length
});
}
/** 清空商品列表 */
private clearShopItems() {
if (this.shopBoxNode && this.shopBoxNode.isValid) {
this.shopBoxNode.removeAllChildren();
}
}
private dispatchCardsToSkillSlots(cards: CardConfig[]) {
if (!this.skillCardComps) return;
for (let i = 0; i < this.skillCardComps.length; i++) {
@@ -548,6 +634,14 @@ export class MissionCardComp extends CCComp {
}
}
/** 商品购买事件回调:记录已购 UUID防止重复购买 */
private onUseItemCard(event: string, args: any) {
const usedCard = args as CardConfig;
if (usedCard) {
this.purchasedItemUuids.add(usedCard.uuid);
}
}
/** 解除按钮监听,避免节点销毁后回调泄漏 */
private unbindEvents() {
oops.message.off(GameEvent.CoinAdd, this.onCoinAdd, this);
@@ -557,6 +651,7 @@ export class MissionCardComp extends CCComp {
oops.message.off(GameEvent.UseHeroCard, this.onUseHeroCard, this);
oops.message.off(GameEvent.UseSkillCard, this.onUseSkillCard, this);
oops.message.off(GameEvent.UseEquipCard, this.onUseEquipCard, this);
oops.message.off(GameEvent.UseItemCard, this.onUseItemCard, this);
oops.message.off(GameEvent.UseSpecialCard, this.onUseSpecialCard, this);
oops.message.off(GameEvent.ShowSmallTip, this.onShowSmallTip, this);
oops.message.off(GameEvent.CardUsed, this.onCardUsed, this);
@@ -587,6 +682,12 @@ export class MissionCardComp extends CCComp {
this.skill_ad_refresh.off(NodeEventType.TOUCH_END, this.onSkillAdDrawTouchEnd, this);
this.skill_ad_refresh.off(NodeEventType.TOUCH_CANCEL, this.onSkillAdDrawTouchCancel, this);
}
if (this.showShop && this.showShop.isValid) {
this.showShop.off(NodeEventType.TOUCH_END, this.onShowShopClick, this);
}
if (this.closeShop && this.closeShop.isValid) {
this.closeShop.off(NodeEventType.TOUCH_END, this.onCloseShopClick, this);
}
}
/**
@@ -1467,6 +1568,7 @@ export class MissionCardComp extends CCComp {
this.cardComps = [] as any;
this.skillCardComps = [] as any;
this.purchasedEquipUuids.clear();
this.purchasedItemUuids.clear();
if (this.node && this.node.isValid) {
this.node.destroy();