feat(shop): 新增商品/药品卡池与商店系统
1. 新增ICardSet.ts定义商品卡池配置,包含基础/高级恢复类、属性强化类道具 2. 为MissionCardComp添加商品商店逻辑,支持购买记录追踪与显隐切换 3. 重构ItemListComp为通用商店列表组件,适配装备与商品两种商店类型
This commit is contained in:
136
assets/script/game/common/config/ICardSet.ts
Normal file
136
assets/script/game/common/config/ICardSet.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @file ICardSet.ts
|
||||
* @description 商品/药品卡池独立配置
|
||||
*
|
||||
* 职责:
|
||||
* 定义所有商品/药品卡的静态数据,与技能卡池(SCardSet)和装备卡池(EquipSet)分离。
|
||||
* 商品/药品为一次性消耗品,购买后立即触发一次 buff 效果(如治疗、护盾、属性强化等)。
|
||||
*
|
||||
* 设计:
|
||||
* - 参考 SCardSet.ts 结构,但商品是"只触发一次的 buff 技能",而非"多次触发的攻击技能"。
|
||||
* - trigger_type 统一为 Instant(即时触发)。
|
||||
* - t_times 固定为 1(只触发一次)。
|
||||
* - kind 为 CKind.Potion(药水类)。
|
||||
*
|
||||
* 依赖:
|
||||
* - CardSet —— CardConfig / CardType / CKind / CardLV / CardTriggerType
|
||||
* - SkillSet —— SkillOverrides
|
||||
*/
|
||||
import { CardConfig, CardType, CardLV, CKind, CardTriggerType } from "./CardSet";
|
||||
import { SkillOverrides } from "./SkillSet";
|
||||
|
||||
/** 商品/药品卡原始数据(平铺字段,构建后转为 CardConfig) */
|
||||
interface ItemCardRaw {
|
||||
uuid: number;
|
||||
skill: number; // 关联的技能 UUID(buff 技能)
|
||||
name: string;
|
||||
info: string;
|
||||
cost: number; // 购买价格
|
||||
weight: number; // 抽取权重
|
||||
overrides?: SkillOverrides; // 技能参数覆写(如自定义治疗量、护盾值等)
|
||||
}
|
||||
|
||||
const ItemCardData: ItemCardRaw[] = [
|
||||
// ==================== 基础恢复类 ====================
|
||||
{ uuid: 9101, skill: 6302, name: "治疗药水", info: "恢复全体友方 300 点生命", cost: 3, weight: 20 },
|
||||
{ uuid: 9102, skill: 6301, name: "护盾药水", info: "为全体友方添加护盾,可抵挡 3 次伤害", cost: 4, weight: 15 },
|
||||
{ uuid: 9103, skill: 6303, name: "金币袋", info: "立即获得 5 金币", cost: 2, weight: 15, overrides: { gold: 5 } },
|
||||
|
||||
// ==================== 属性强化类(单次触发) ====================
|
||||
{ uuid: 9111, skill: 6401, name: "攻击药剂", info: "全体友方攻击力提升 5 点", cost: 3, weight: 12 },
|
||||
{ uuid: 9112, skill: 6402, name: "生命药剂", info: "全体友方最大生命值提升 20 点", cost: 3, weight: 12 },
|
||||
{ uuid: 9113, skill: 6403, name: "暴击药剂", info: "全体友方暴击率提升 10%", cost: 3, weight: 12 },
|
||||
{ uuid: 9114, skill: 6404, name: "暴伤药剂", info: "全体友方暴击伤害提升 20%", cost: 3, weight: 12 },
|
||||
{ uuid: 9115, skill: 6405, name: "击晕药剂", info: "全体友方击晕概率提升 10%", cost: 3, weight: 12 },
|
||||
{ uuid: 9116, skill: 6408, name: "穿刺药剂", info: "全体友方穿透概率提升 20%", cost: 3, weight: 12 },
|
||||
{ uuid: 9117, skill: 6409, name: "风怒药剂", info: "全体友方风怒次数提升 1 次", cost: 3, weight: 12 },
|
||||
|
||||
// ==================== 高级恢复类 ====================
|
||||
{ uuid: 9201, skill: 6302, name: "高级治疗药水", info: "恢复全体友方 600 点生命", cost: 6, weight: 8, overrides: { ap: 600 } },
|
||||
{ uuid: 9202, skill: 6301, name: "高级护盾药水", info: "为全体友方添加护盾,可抵挡 6 次伤害", cost: 8, weight: 6, overrides: { ap: 6 } },
|
||||
{ uuid: 9203, skill: 6303, name: "大金币袋", info: "立即获得 12 金币", cost: 5, weight: 6, overrides: { gold: 12 } },
|
||||
|
||||
// ==================== 高级属性强化类 ====================
|
||||
{ uuid: 9211, skill: 6401, name: "高级攻击药剂", info: "全体友方攻击力提升 10 点", cost: 6, weight: 5, overrides: { ap: 10 } },
|
||||
{ uuid: 9212, skill: 6402, name: "高级生命药剂", info: "全体友方最大生命值提升 40 点", cost: 6, weight: 5, overrides: { ap: 40 } },
|
||||
{ uuid: 9213, skill: 6403, name: "高级暴击药剂", info: "全体友方暴击率提升 20%", cost: 6, weight: 5, overrides: { ap: 2 } },
|
||||
{ uuid: 9214, skill: 6404, name: "高级暴伤药剂", info: "全体友方暴击伤害提升 40%", cost: 6, weight: 5, overrides: { ap: 2 } },
|
||||
];
|
||||
|
||||
/**
|
||||
* 商品/药品卡池(已构建为标准 CardConfig[])。
|
||||
* 所有商品卡统一配置:
|
||||
* - type: Skill(复用技能卡体系,由 MissSkillsComp 统一处理)
|
||||
* - kind: Potion(药水类,用于区分)
|
||||
* - trigger_type: Instant(即时触发,只生效一次)
|
||||
* - t_times: 1(只触发一次)
|
||||
* - is_inst: true(即时起效)
|
||||
*/
|
||||
export const ItemCardList: CardConfig[] = ItemCardData.map(data => ({
|
||||
uuid: data.uuid,
|
||||
skill: data.skill,
|
||||
type: CardType.Skill,
|
||||
cost: data.cost,
|
||||
weight: data.weight,
|
||||
pool_lv: CardLV.LV1,
|
||||
kind: CKind.Potion,
|
||||
card_lv: 1,
|
||||
name: data.name,
|
||||
info: data.info,
|
||||
is_inst: true,
|
||||
t_times: 1,
|
||||
t_inv: 0,
|
||||
keep_waves: 0,
|
||||
overrides: data.overrides,
|
||||
trigger_type: CardTriggerType.Instant,
|
||||
}));
|
||||
|
||||
/**
|
||||
* 按权重抽取一张商品卡(带权重随机)。
|
||||
*/
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 按权重抽取 N 张商品卡(unique 保证一次刷新内不重复,不足时允许重复补齐)。
|
||||
* @param count 需要抽取的数量
|
||||
*/
|
||||
export function drawItemCards(count: number): CardConfig[] {
|
||||
const safeCount = Math.max(0, Math.floor(count));
|
||||
if (ItemCardList.length === 0 || safeCount <= 0) return [];
|
||||
|
||||
const picked: CardConfig[] = [];
|
||||
let available = [...ItemCardList];
|
||||
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(ItemCardList);
|
||||
if (!fallback) break;
|
||||
filled.push(fallback);
|
||||
}
|
||||
return filled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 UUID 查找商品卡配置
|
||||
* @param uuid 商品卡 UUID
|
||||
*/
|
||||
export function findItemCardByUuid(uuid: number): CardConfig | undefined {
|
||||
return ItemCardList.find(e => e.uuid === uuid);
|
||||
}
|
||||
@@ -1,23 +1,25 @@
|
||||
/**
|
||||
* @file ItemListComp.ts
|
||||
* @description 装备商店单个装备项组件(UI 视图层 + 购买逻辑)
|
||||
* @description 商店列表项组件(UI 视图层 + 购买逻辑)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 渲染单个装备卡的显示信息(名称、等级、属性词条、图标)。
|
||||
* 2. 处理购买点击 → 扣费 → 派发 UseEquipCard 事件触发装备生效。
|
||||
* 1. 渲染单个商品/装备卡的显示信息(名称、等级、属性词条、图标)。
|
||||
* 2. 处理购买点击 → 扣费 → 根据卡牌类型派发对应事件触发购买效果。
|
||||
*
|
||||
* 装备使用机制:
|
||||
* 与技能卡使用流程一致——购买后派发 GameEvent.UseEquipCard,
|
||||
* 由 MissEquipComp 接收并创建 EquipBoxComp 实体,
|
||||
* EquipBoxComp 按 Field 类型被动驻场,属性加成由 FieldSkillHelper 全局聚合。
|
||||
* 支持类型:
|
||||
* - 装备卡(trigger_type=Field)→ 派发 UseEquipCard,由 MissEquipComp 处理。
|
||||
* - 商品/药品卡(kind=Potion)→ 派发 UseItemCard,由 MissSkillsComp 处理(一次性 buff)。
|
||||
*
|
||||
* 设计说明:
|
||||
* 本组件同时服务于装备商店和商品商店,通过 cardData 的 trigger_type / kind 自动判断类型。
|
||||
*/
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { _decorator, Node, Sprite, Label, NodeEventType } 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 } from "../common/config/CardSet";
|
||||
import { CardConfig, CardTriggerType, CKind } from "../common/config/CardSet";
|
||||
import { EquipPoolList } from "../common/config/EquipSet";
|
||||
import { FieldSkillSet } from "../common/config/SkillSet";
|
||||
import { FieldSkillSet, SkillSet } from "../common/config/SkillSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
@@ -26,10 +28,10 @@ import { MissionEconomy } from "./MissionEconomy";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* ItemListComp —— 装备商店单个装备项
|
||||
* ItemListComp —— 商店列表项(装备/商品通用)
|
||||
*
|
||||
* 由 MissionCardComp.populateEquipments() 实例化。
|
||||
* 以图标 + 名称 + 属性词条 + 购买按钮的形式呈现。
|
||||
* 由 MissionCardComp.populateEquipments() / populateShopItems() 实例化。
|
||||
* 以图标 + 名称 + 描述/属性词条 + 购买按钮的形式呈现。
|
||||
*/
|
||||
@ccclass('ItemListComp')
|
||||
@ecs.register('ItemListComp', false)
|
||||
@@ -38,7 +40,7 @@ export class ItemListComp extends CCComp {
|
||||
|
||||
// ======================== 编辑器绑定节点 ========================
|
||||
|
||||
/** 装备图标节点 */
|
||||
/** 商品图标节点 */
|
||||
@property({ type: Node })
|
||||
private icon_node: Node = null;
|
||||
|
||||
@@ -46,17 +48,17 @@ export class ItemListComp extends CCComp {
|
||||
@property({ type: Node })
|
||||
private bg_node: Node = null;
|
||||
|
||||
/** 装备名称 */
|
||||
/** 商品名称 */
|
||||
@property(Label)
|
||||
private name_label: Label = null;
|
||||
|
||||
/** 属性词条 1 */
|
||||
/** 描述/属性词条 1 */
|
||||
@property(Label)
|
||||
private fied1: Label = null;
|
||||
/** 属性词条 2 */
|
||||
/** 描述/属性词条 2 */
|
||||
@property(Label)
|
||||
private fied2: Label = null;
|
||||
/** 属性词条 3 */
|
||||
/** 描述/属性词条 3 */
|
||||
@property(Label)
|
||||
private fied3: Label = null;
|
||||
|
||||
@@ -70,7 +72,7 @@ export class ItemListComp extends CCComp {
|
||||
|
||||
// ======================== 运行时状态 ========================
|
||||
|
||||
/** 当前装备的卡牌配置 */
|
||||
/** 当前商品的卡牌配置 */
|
||||
private cardData: CardConfig | null = null;
|
||||
/** 是否已购买 */
|
||||
private isPurchased: boolean = false;
|
||||
@@ -91,9 +93,9 @@ export class ItemListComp extends CCComp {
|
||||
// ======================== 数据初始化 ========================
|
||||
|
||||
/**
|
||||
* 初始化装备卡数据并渲染 UI
|
||||
* 初始化商品卡数据并渲染 UI
|
||||
*
|
||||
* @param data 装备卡配置(必须为 trigger_type=Field 的装备卡)
|
||||
* @param data 商品/装备卡配置
|
||||
*/
|
||||
applyCardData(data: CardConfig): void {
|
||||
if (!data) return;
|
||||
@@ -107,11 +109,17 @@ export class ItemListComp extends CCComp {
|
||||
updateUI() {
|
||||
if (!this.cardData) return;
|
||||
|
||||
const cardConfig = EquipPoolList.find(c => c.uuid === this.cardData!.uuid);
|
||||
const isEquip = this.cardData.trigger_type === CardTriggerType.Field;
|
||||
const isPotion = this.cardData.kind === CKind.Potion;
|
||||
|
||||
// 名称
|
||||
// 名称:装备从 EquipPoolList 查找,商品直接用 cardData.name
|
||||
if (this.name_label) {
|
||||
this.name_label.string = cardConfig?.name || "";
|
||||
if (isEquip) {
|
||||
const equipConfig = EquipPoolList.find(c => c.uuid === this.cardData!.uuid);
|
||||
this.name_label.string = equipConfig?.name || this.cardData.name || "";
|
||||
} else {
|
||||
this.name_label.string = this.cardData.name || "";
|
||||
}
|
||||
}
|
||||
|
||||
// 等级标签(★ 表示卡牌等级)
|
||||
@@ -120,25 +128,63 @@ export class ItemListComp extends CCComp {
|
||||
this.level_label.string = lv >= 2 ? "★".repeat(lv - 1) : "";
|
||||
}
|
||||
|
||||
// 属性词条(最多显示 3 条,对应 fied1/fied2/fied3)
|
||||
// 描述/属性词条(最多显示 3 条)
|
||||
const fieldLabels = [this.fied1, this.fied2, this.fied3];
|
||||
const fields = this.cardData.field || [];
|
||||
for (let i = 0; i < fieldLabels.length; i++) {
|
||||
if (!fieldLabels[i]) continue;
|
||||
if (i < fields.length) {
|
||||
const fieldConfig = FieldSkillSet[fields[i]];
|
||||
fieldLabels[i].string = fieldConfig?.info || "";
|
||||
fieldLabels[i].node.active = true;
|
||||
} else {
|
||||
fieldLabels[i].string = "";
|
||||
fieldLabels[i].node.active = false;
|
||||
if (isEquip) {
|
||||
// 装备:显示 field 词条
|
||||
const fields = this.cardData.field || [];
|
||||
for (let i = 0; i < fieldLabels.length; i++) {
|
||||
if (!fieldLabels[i]) continue;
|
||||
if (i < fields.length) {
|
||||
const fieldConfig = FieldSkillSet[fields[i]];
|
||||
fieldLabels[i].string = fieldConfig?.info || "";
|
||||
fieldLabels[i].node.active = true;
|
||||
} else {
|
||||
fieldLabels[i].string = "";
|
||||
fieldLabels[i].node.active = false;
|
||||
}
|
||||
}
|
||||
} else if (isPotion) {
|
||||
// 商品:显示技能描述 + info
|
||||
const skillConfig = this.cardData.skill ? SkillSet[this.cardData.skill] : null;
|
||||
const skillInfo = skillConfig?.info || "";
|
||||
const cardInfo = this.cardData.info || "";
|
||||
|
||||
if (fieldLabels[0]) {
|
||||
fieldLabels[0].string = cardInfo;
|
||||
fieldLabels[0].node.active = !!cardInfo;
|
||||
}
|
||||
if (fieldLabels[1]) {
|
||||
fieldLabels[1].string = skillInfo;
|
||||
fieldLabels[1].node.active = !!skillInfo && skillInfo !== cardInfo;
|
||||
}
|
||||
if (fieldLabels[2]) {
|
||||
fieldLabels[2].string = "";
|
||||
fieldLabels[2].node.active = false;
|
||||
}
|
||||
} else {
|
||||
// 其他类型:显示 info
|
||||
for (let i = 0; i < fieldLabels.length; i++) {
|
||||
if (!fieldLabels[i]) continue;
|
||||
fieldLabels[i].string = i === 0 ? (this.cardData.info || "") : "";
|
||||
fieldLabels[i].node.active = i === 0 && !!this.cardData.info;
|
||||
}
|
||||
}
|
||||
|
||||
// 图标(取第一个 field 词条的图标)
|
||||
if (this.icon_node && fields.length > 0) {
|
||||
const iconId = FieldSkillSet[fields[0]]?.icon || "";
|
||||
this.updateIcon(this.icon_node, iconId);
|
||||
// 图标:装备取第一个 field 词条图标,商品取技能图标
|
||||
if (this.icon_node) {
|
||||
let iconId = "";
|
||||
if (isEquip) {
|
||||
const fields = this.cardData.field || [];
|
||||
if (fields.length > 0) {
|
||||
iconId = FieldSkillSet[fields[0]]?.icon || "";
|
||||
}
|
||||
} else if (isPotion && this.cardData.skill) {
|
||||
iconId = SkillSet[this.cardData.skill]?.icon || "";
|
||||
}
|
||||
if (iconId) {
|
||||
this.updateIcon(this.icon_node, iconId);
|
||||
}
|
||||
}
|
||||
|
||||
// 购买按钮费用显示
|
||||
@@ -168,12 +214,10 @@ export class ItemListComp extends CCComp {
|
||||
/**
|
||||
* 购买点击处理:
|
||||
* 1. 扣除金币(失败则提示并中止)。
|
||||
* 2. 派发 UseSkillCard 事件,触发与技能卡相同的生效流程。
|
||||
* 2. 根据卡牌类型派发对应事件:
|
||||
* - 装备卡(trigger_type=Field)→ UseEquipCard → MissEquipComp
|
||||
* - 商品/药品卡(kind=Potion)→ UseItemCard → MissSkillsComp(一次性 buff 技能)
|
||||
* 3. 标记已购买,隐藏购买按钮。
|
||||
*
|
||||
* 装备生效链路:
|
||||
* UseSkillCard → MissSkillsComp.addSkill() → SBox ECS 实体 → SkillBoxComp(Field 类型)
|
||||
* → FieldSkillHelper.getFieldSkillTotalValue() 全局聚合属性加成
|
||||
*/
|
||||
private onBuyClick() {
|
||||
if (!this.cardData || this.isPurchased) return;
|
||||
@@ -192,19 +236,39 @@ export class ItemListComp extends CCComp {
|
||||
return;
|
||||
}
|
||||
|
||||
// 派发 UseEquipCard,装备生效流程由 MissEquipComp 接收处理
|
||||
oops.message.dispatchEvent(GameEvent.UseEquipCard, this.cardData);
|
||||
// 根据类型派发对应事件
|
||||
const isEquip = this.cardData.trigger_type === CardTriggerType.Field;
|
||||
const isPotion = this.cardData.kind === CKind.Potion;
|
||||
|
||||
if (isEquip) {
|
||||
// 装备:被动驻场,由 MissEquipComp 处理
|
||||
oops.message.dispatchEvent(GameEvent.UseEquipCard, this.cardData);
|
||||
mLogger.log(this.debugMode, "ItemListComp", "equipment purchased", {
|
||||
uuid: this.cardData.uuid,
|
||||
cost
|
||||
});
|
||||
} else if (isPotion) {
|
||||
// 商品/药品:一次性 buff 技能,由 MissSkillsComp 处理
|
||||
oops.message.dispatchEvent(GameEvent.UseItemCard, this.cardData);
|
||||
mLogger.log(this.debugMode, "ItemListComp", "item purchased", {
|
||||
uuid: this.cardData.uuid,
|
||||
skill: this.cardData.skill,
|
||||
cost
|
||||
});
|
||||
} else {
|
||||
// 默认走技能卡流程
|
||||
oops.message.dispatchEvent(GameEvent.UseSkillCard, this.cardData);
|
||||
mLogger.log(this.debugMode, "ItemListComp", "card purchased", {
|
||||
uuid: this.cardData.uuid,
|
||||
cost
|
||||
});
|
||||
}
|
||||
|
||||
// 标记已购买
|
||||
this.isPurchased = true;
|
||||
if (this.buy_node) {
|
||||
this.buy_node.active = false;
|
||||
}
|
||||
|
||||
mLogger.log(this.debugMode, "ItemListComp", "equipment purchased", {
|
||||
uuid: this.cardData.uuid,
|
||||
cost
|
||||
});
|
||||
}
|
||||
|
||||
/** 标记为已购买状态(用于跨波次保持购买记录) */
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user