diff --git a/assets/script/game/common/SingletonModuleComp.ts b/assets/script/game/common/SingletonModuleComp.ts index 56e9b337..02ec9163 100644 --- a/assets/script/game/common/SingletonModuleComp.ts +++ b/assets/script/game/common/SingletonModuleComp.ts @@ -73,9 +73,6 @@ export class SingletonModuleComp extends ecs.Comp { max_mission: 4,//最大关卡 coin: 0, time: 15 * 60,//游戏时间 - // 技能三选一弹窗:刷新次数(跨波次保留,广告可叠加) - skill_box_refresh_remain: 1, // 初始赠送的刷新次数 - skill_box_ad_refresh_remain: 0, // 看广告累计的额外次数 }, scores: { score: 0, // 基础得分 diff --git a/assets/script/game/common/config/CardSet.ts b/assets/script/game/common/config/CardSet.ts index cbd08fcf..f81d7a82 100644 --- a/assets/script/game/common/config/CardSet.ts +++ b/assets/script/game/common/config/CardSet.ts @@ -2,7 +2,6 @@ import * as exp from "constants" import { HeroInfo, HeroList, HType } from "./heroSet" import { FightSet } from "./GameSet" import { oops } from "db://oops-framework/core/Oops" -import { SkillOverrides } from "./SkillSet" class I18nString { constructor(private key: string, private params?: any[]) { } @@ -63,25 +62,6 @@ export interface CardConfig { t_times?: number // 触发次数 t_inv?: number // 触发间隔(秒) } - -/** - * 技能卡池专用卡牌配置(MSkillBoxComp 三选一弹窗使用) - * - * 在普通 CardConfig 基础上扩展: - * - s_uuid : 实际生效的 SkillSet 条目 uuid - * - t_num : 触发次数(可选,默认从 SkillSet 读取) - * - overrides : 参数覆盖,参考 heroSet 中触发技能的 overrides 字段格式 - * - * uuid 范围 9000+ 用于和普通 CardPoolList 区分,MissSkillsComp 据此识别。 - */ -export interface SkillBoxCardConfig extends CardConfig { - /** 实际生效的 SkillSet 技能 uuid */ - s_uuid: number - /** 触发次数(可选,默认 1) */ - t_num?: number - /** 技能参数覆盖,参考 SkillOverrides */ - overrides?: SkillOverrides -} export const CardsUpSet: Record = { 1: 50, 2: 100, @@ -315,172 +295,3 @@ export const drawCardsByRule = ( const picked = pickCards(pool, count) return picked } - -/** - * 固定波次弹出技能三选一弹窗(MissionCardComp 触发) - * 5 个波次:1 / 5 / 10 / 15 / 20 - */ -export const SKILL_BOX_TRIGGER_WAVES: readonly number[] = [1, 5, 10, 15, 20] as const - -/** - * 技能卡池(MSkillBoxComp 三选一) - * - * 5 个硬编码独立池,key 对应触发波次,value 是该波次可刷出的 SkillBoxCardConfig 列表。 - * 每条配置: - * - uuid 唯一且 >= 9000,用于 MissSkillsComp 识别 - * - s_uuid 指向 SkillSet 中实际生效的技能 - * - overrides 参考 heroSet 触发技能格式,对技能参数做具体覆盖 - * - cost 强制 0(免费领取) - * - is_inst / t_times / t_inv 控制技能使用后行为 - */ -export const SkillBoxPool: Record = { - // 第 1 波:基础技能(伤害/治疗/护盾基础数值) - 1: [ - { - uuid: 9001, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV1, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9001"), info: t("skillbox_info_9001"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6401, t_num: 1, overrides: { ap: 120 } - }, - { - uuid: 9002, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV1, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9002"), info: t("skillbox_info_9002"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6402, t_num: 1, overrides: { ap: 150 } - }, - { - uuid: 9003, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV1, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9003"), info: t("skillbox_info_9003"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6403, t_num: 1, overrides: { ap: 100, hit_count: 2 } - }, - { - uuid: 9004, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV1, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9004"), info: t("skillbox_info_9004"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6404, t_num: 1, overrides: { ap: 110 } - }, - ], - // 第 5 波:中级技能(伤害提升,带附加效果) - 5: [ - { - uuid: 9005, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV2, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9005"), info: t("skillbox_info_9005"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6405, t_num: 1, overrides: { ap: 130, crt: 10 } - }, - { - uuid: 9006, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV2, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9006"), info: t("skillbox_info_9006"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6406, t_num: 1, overrides: { ap: 140, hit_count: 2 } - }, - { - uuid: 9007, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV2, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9007"), info: t("skillbox_info_9007"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6304, t_num: 1, overrides: { ap: 160 } - }, - { - uuid: 9008, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV2, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9008"), info: t("skillbox_info_9008"), - is_inst: false, t_times: 3, t_inv: 2, - s_uuid: 6401, t_num: 3, overrides: { ap: 100 } - }, - ], - // 第 10 波:高级技能(高伤害/多段/持续) - 10: [ - { - uuid: 9009, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9009"), info: t("skillbox_info_9009"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6305, t_num: 1, overrides: { ap: 200 } - }, - { - uuid: 9010, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9010"), info: t("skillbox_info_9010"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6405, t_num: 1, overrides: { ap: 220, hit_count: 3, crt: 15 } - }, - { - uuid: 9011, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9011"), info: t("skillbox_info_9011"), - is_inst: false, t_times: 4, t_inv: 1.5, - s_uuid: 6402, t_num: 4, overrides: { ap: 130, frz: 10 } - }, - { - uuid: 9012, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 1, - name: t("skillbox_name_9012"), info: t("skillbox_info_9012"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6406, t_num: 1, overrides: { ap: 240, bck: 20 } - }, - ], - // 第 15 波:精英技能(高额暴击/多段) - 15: [ - { - uuid: 9013, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 2, - name: t("skillbox_name_9013"), info: t("skillbox_info_9013"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6304, t_num: 1, overrides: { ap: 280, hit_count: 3 } - }, - { - uuid: 9014, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 2, - name: t("skillbox_name_9014"), info: t("skillbox_info_9014"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6305, t_num: 1, overrides: { ap: 320, crt: 25 } - }, - { - uuid: 9015, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 2, - name: t("skillbox_name_9015"), info: t("skillbox_info_9015"), - is_inst: false, t_times: 5, t_inv: 1.2, - s_uuid: 6406, t_num: 5, overrides: { ap: 200, hit_count: 2, bck: 30 } - }, - ], - // 第 20 波:终极技能(全屏/极限数值) - 20: [ - { - uuid: 9016, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 3, - name: t("skillbox_name_9016"), info: t("skillbox_info_9016"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6304, t_num: 1, overrides: { ap: 400, hit_count: 4, crt: 30 } - }, - { - uuid: 9017, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 3, - name: t("skillbox_name_9017"), info: t("skillbox_info_9017"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6305, t_num: 1, overrides: { ap: 500, crt: 40, frz: 20 } - }, - { - uuid: 9018, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 3, - name: t("skillbox_name_9018"), info: t("skillbox_info_9018"), - is_inst: true, t_times: 1, t_inv: 0, - s_uuid: 6406, t_num: 1, overrides: { ap: 600, hit_count: 5, bck: 50 } - }, - ], -} - -/** - * 从指定波次的技能池中抽取 N 张卡(加权随机,去重,不足循环补齐) - * - * @param wave 触发波次(1/5/10/15/20),若该波次无池则返回空数组 - * @param count 期望返回卡牌数(默认 3) - * @returns 抽中的 SkillBoxCardConfig 列表(长度 = count) - */ -export function getSkillBoxCards(wave: number, count: number = 3): SkillBoxCardConfig[] { - const pool = SkillBoxPool[wave] || []; - if (pool.length === 0) return []; - const result: SkillBoxCardConfig[] = []; - const usedIndexes = new Set(); - for (let i = 0; i < count; i++) { - const available: number[] = []; - for (let j = 0; j < pool.length; j++) { - if (!usedIndexes.has(j)) available.push(j); - } - // 池内卡片不足时,允许重复 - const candidates = available.length > 0 ? available : Array.from({ length: pool.length }, (_, k) => k); - const pick = candidates[Math.floor(Math.random() * candidates.length)]; - usedIndexes.add(pick); - result.push(pool[pick]); - } - return result; -} diff --git a/assets/script/game/common/config/GameUIConfig.ts b/assets/script/game/common/config/GameUIConfig.ts index 0176b224..8037b5c2 100644 --- a/assets/script/game/common/config/GameUIConfig.ts +++ b/assets/script/game/common/config/GameUIConfig.ts @@ -39,5 +39,5 @@ export var UIConfigData: { [key: number]: UIConfig } = { // [UIID.Talents]: { layer: LayerType.UI, prefab: "gui/element/talents" }, [UIID.Mission]: { layer: LayerType.UI, prefab: "gui/element/mission" }, [UIID.HInfo]: { layer: LayerType.UI, prefab: "gui/element/hnode" }, - [UIID.SkillBox]: { layer: LayerType.UI, prefab: "gui/element/MSkillBox" }, + [UIID.SkillBox]: { layer: LayerType.UI, prefab: "gui/element/skillbox" }, } \ No newline at end of file diff --git a/assets/script/game/map/CardComp.ts b/assets/script/game/map/CardComp.ts index 89c987ce..148d9e44 100644 --- a/assets/script/game/map/CardComp.ts +++ b/assets/script/game/map/CardComp.ts @@ -37,7 +37,6 @@ import { FieldSkillType } from "../common/config/SkillSet"; import { FieldSkillHelper } from "../hero/FieldSkillHelper"; import { getLvColor } from "../common/config/GameSet"; import { MissionEconomy } from "./MissionEconomy"; -import { buildSkillDesc } from "../common/config/HeroSkillDesc"; @@ -95,9 +94,6 @@ export class CardComp extends CCComp { @property(Node) hp_node = null! - /** 技能信息标签缓存引用(由 cacheLabels 在 onLoad 时初始化) */ - private infoLabel: Label | null = null; - // ======================== 运行时状态 ======================== /** 当前卡牌的金币费用 */ @@ -148,7 +144,6 @@ export class CardComp extends CCComp { onLoad() { /** 初始阶段只做UI状态准备,不触发业务逻辑 */ this.bindEvents(); - this.cacheLabels(); this.restPosition = this.node.position.clone(); this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity); this.opacityComp.opacity = 255; @@ -705,8 +700,6 @@ export class CardComp extends CCComp { this.hp_node.active = true; // 英雄卡:隐藏技能信息节点 if (this.info_node) this.info_node.active = false; - // 英雄卡:清空技能描述文本,避免切回时残留上一次的技能信息 - if (this.infoLabel) this.infoLabel.string = ""; } else if (this.card_type === CardType.Skill) { if (this.lvl_node) this.lvl_node.node.active = false; // 技能卡:显示技能名 + 品质后缀 + 描述 @@ -720,18 +713,6 @@ export class CardComp extends CCComp { this.hp_node.active = false; // 技能卡:显示技能信息节点 if (this.info_node) this.info_node.active = true; - // 技能卡:填充技能描述文本(沿用 HInfoComp.buildSkillDesc 模式) - // 注意:技能卡 uuid 来自 SkillSet 而非 HeroInfo,故以 SkillConfig 作为数据源; - // 用 buildSkillDesc 兼容调用,结果为空时回退到 SkillConfig.info 字段 - if (this.infoLabel) { - const skill = SkillSet[this.card_uuid]; - if (skill) { - const desc = buildSkillDesc(skill as any) || skill.info || ""; - this.infoLabel.string = desc; - } else { - this.infoLabel.string = ""; - } - } } else { if (this.lvl_node) this.lvl_node.node.active = false; // 特殊卡(升级 / 刷新):显示卡名 + 品质后缀 + 描述 @@ -744,8 +725,6 @@ export class CardComp extends CCComp { this.ap_node.active = false; this.hp_node.active = false; - // 特殊卡:清空技能描述文本,避免切到特殊卡时残留上一次的技能信息 - if (this.infoLabel) this.infoLabel.string = ""; } // ---- 费用标签 ---- @@ -927,25 +906,6 @@ export class CardComp extends CCComp { if (label) label.string = value; } - /** - * 缓存 info_node 子树中的 Label 引用。 - * 与 HInfoComp.cacheLabels() 同源实现:若 info_node 下没有 Label 则动态创建, - * 并设置与 HInfoComp 一致的字号 / 行高 / 对齐 / 溢出策略。 - */ - private cacheLabels() { - if (this.infoLabel || !this.info_node) return; - this.infoLabel = this.info_node.getComponentInChildren(Label); - if (!this.infoLabel) { - const child = this.info_node.children[0] || this.info_node; - this.infoLabel = child.getComponent(Label) || child.addComponent(Label); - this.infoLabel.fontSize = 20; - this.infoLabel.lineHeight = 28; - this.infoLabel.overflow = Label.Overflow.SHRINK; - this.infoLabel.horizontalAlign = Label.HorizontalAlign.LEFT; - this.infoLabel.verticalAlign = Label.VerticalAlign.TOP; - } - } - /** * 根据卡牌类型和 UUID 解析出图标 ID(在 SpriteAtlas 中的帧名)。 diff --git a/assets/script/game/map/MSkillBoxComp.ts b/assets/script/game/map/MSkillBoxComp.ts index f3326c46..085840ac 100644 --- a/assets/script/game/map/MSkillBoxComp.ts +++ b/assets/script/game/map/MSkillBoxComp.ts @@ -1,266 +1,83 @@ /** * @file MSkillBoxComp.ts - * @description 固定波次触发的「技能卡三选一」弹窗组件(UI 视图层 + 流程控制) + * @description 技能卡牌选择器 * * 职责: - * 1. 由 MissionCardComp 在 NewWave 事件且当前波次 ∈ {1, 5, 10, 15, 20} 时 - * 通过 `oops.gui.open(UIID.SkillBox, { wave, poolLv })` 弹出。 - * 2. 从 SkillBoxPool[wave] 抽 3 张卡,渲染到 card1 / card2 / card3 三个 CardComp 槽位。 - * 3. 玩家点中其中一张卡后,直接以该卡为 payload 分发 UseSkillCard 事件, - * 由 MissSkillsComp 接管并实例化 SkillBoxComp(走 SkillBoxCardConfig 路径)。 - * 4. 提供「刷新」按钮(扣除 refreshRemain 一次)和「看广告刷新」按钮(回调留空, - * 待接入广告 SDK 后会把 adRefreshRemain 累加 +3)。 + * * 关键设计: - * - **强制必选**:不绑定关闭按钮,玩家只能点中 3 张卡之一才能关闭弹窗。 - * - **免费领取**:cost 强制 0,dispatchEvent(UseSkillCard) 不走 CoinAdd 扣费。 - * - **次数持久化**:refreshRemain / adRefreshRemain 存于 smc.vmdata.mission_data, - * 跨波次保留,每局 mission 开始时由 MissionComp.data_init() 重置为 1/0。 + * * 依赖: - * - CardComp —— 复用其渲染/交互逻辑(免费版:card_cost=0) - * - SkillBoxPool / getSkillBoxCards (CardSet) —— 5 级硬编码技能池 - * - GameEvent.UseSkillCard —— 技能使用事件 - * - UIID.SkillBox (GameUIConfig) —— 弹窗 prefab 路径 - * - smc.vmdata.mission_data —— 刷新次数持久化 + */ import { mLogger } from "../common/Logger"; -import { _decorator, Label, Node, Button } from "cc"; +import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, SpriteAtlas, Tween, tween, Vec3, Widget } 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 { GameEvent } from "../common/config/GameEvent"; -import { oops } from "db://oops-framework/core/Oops"; -import { smc } from "../common/SingletonModuleComp"; -import { getSkillBoxCards, SkillBoxCardConfig } from "../common/config/CardSet"; -import { UIID } from "../common/config/GameUIConfig"; -import { CardComp } from "./CardComp"; const { ccclass, property } = _decorator; + /** - * MSkillBoxComp —— 技能三选一弹窗控制器 + * MSkillBoxComp —— 技能卡牌系统核心控制器 * - * 由 oops.gui.open(UIID.SkillBox, { wave, poolLv }) 实例化。 + * 管理 3 个卡牌槽位的抽卡分发、卡池升级、金币费用、 + * 在 1,5,10,15,20 波次弹出技能卡牌三选一 */ @ccclass('MSkillBoxComp') -@ecs.register('MSkillBoxComp', false) +@ecs.register('MissionCard', false) export class MSkillBoxComp extends CCComp { /** 是否启用调试日志 */ - private debugMode: boolean = true; + private debugMode: boolean = false; // ======================== 编辑器绑定节点 ======================== - /** 卡牌槽位 1 节点(需挂 CardComp) */ + /** 卡牌槽位 1 节点 */ @property(Node) card1: Node = null! - /** 卡牌槽位 2 节点(需挂 CardComp) */ + /** 卡牌槽位 2 节点 */ @property(Node) card2: Node = null! - /** 卡牌槽位 3 节点(需挂 CardComp) */ + /** 卡牌槽位 3 节点 */ @property(Node) card3: Node = null! - /** 刷新按钮(消耗 1 次 refreshRemain 重抽 3 张) */ + /** 刷新按钮 */ @property(Node) refreshBtn: Node = null! - /** 看广告刷新按钮(回调留空,后续接入广告 SDK 后累加 adRefreshRemain +3) */ + /** 看广告刷新按钮 */ @property(Node) adRefreshBtn: Node = null! - /** 刷新次数显示标签(可选,显示 "1/1" 形式) */ - @property(Label) - refreshCountLabel: Label = null! - // ======================== 运行时状态 ======================== - - /** 当前波次(1/5/10/15/20) */ - private currentWave: number = 0; - /** 当前卡池等级(预留扩展,目前未使用) */ - private currentPoolLv: number = 1; - /** 3 个 CardComp 子控制器引用(有序) */ - private cardComps: CardComp[] = []; - /** 是否已派发 UseSkillCard(防重复触发关闭) */ - private hasPicked: boolean = false; - - // ======================== 生命周期 ======================== + /** + * 组件加载: + */ onLoad() { - // 监听任务结束,自动关闭弹窗(避免玩家关游戏时残留) - oops.message.on(GameEvent.MissionEnd, this.onMissionEnd, this); - this.bindEvents(); - } - onAdded(args: { wave?: number; poolLv?: number }) { - this.currentWave = Math.max(0, Math.floor(Number(args?.wave ?? 0))); - this.currentPoolLv = Math.max(1, Math.floor(Number(args?.poolLv ?? 1))); - this.hasPicked = false; - this.cacheCardComps(); - this.rollAndRender(); - this.refreshRefreshCountUI(); - mLogger.log(this.debugMode, "MSkillBoxComp", "opened", { - wave: this.currentWave, - poolLv: this.currentPoolLv, - refreshRemain: this.getRefreshRemain(), - adRefreshRemain: this.getAdRefreshRemain(), - }); } + onAdded(args: any) { + } onDestroy() { - super.onDestroy(); - this.unbindEvents(); - oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this); - } - onMissionEnd() { - // 任务结束时强制关闭弹窗 - oops.gui.remove(UIID.SkillBox); } init() { - // 弹窗组件无需额外 init,onAdded 阶段完成所有初始化 } + + update(dt: number) { - // 弹窗无帧更新逻辑 } - reset() { - // ECS 组件移除时清理 - } - - // ======================== 内部:CardComp 缓存 ======================== - - /** - * 缓存 3 个卡槽上的 CardComp 引用。 - * 与 MissionCardComp.cacheCardComps() 同源实现,确保即开即用。 - */ - private cacheCardComps() { - this.cardComps = []; - const slots: (Node | null)[] = [this.card1, this.card2, this.card3]; - for (let i = 0; i < slots.length; i++) { - const node = slots[i]; - if (!node) continue; - const comp = node.getComponent(CardComp) || node.addComponent(CardComp); - this.cardComps.push(comp); - } - } - - // ======================== 内部:抽卡与渲染 ======================== - - /** - * 从 SkillBoxPool[currentWave] 抽 3 张并渲染到 3 个卡槽。 - * 渲染时强制 cost=0,触发 free 路径。 - */ - private rollAndRender() { - const cards = getSkillBoxCards(this.currentWave, 3); - for (let i = 0; i < this.cardComps.length; i++) { - const comp = this.cardComps[i]; - if (!comp) continue; - if (i < cards.length) { - comp.applyDrawCard(cards[i]); - comp.card_cost = 0; // 强制免费 - } else { - comp.clearBySystem(); - } - } - } - - /** 重新抽 3 张(玩家点 refreshBtn) */ - private reroll() { - if (this.hasPicked) return; - this.rollAndRender(); - mLogger.log(this.debugMode, "MSkillBoxComp", "reroll", { - refreshRemain: this.getRefreshRemain(), - adRefreshRemain: this.getAdRefreshRemain(), - }); - } - - // ======================== 内部:刷新次数持久化 ======================== - - /** 读取当前总可用刷新次数(普通 + 广告奖励) */ - private getRefreshRemain(): number { - const d = smc.vmdata?.mission_data; - if (!d) return 0; - return Math.max(0, Math.floor(Number(d.skill_box_refresh_remain ?? 0))); - } - - /** 读取广告奖励次数(待接入 SDK 后使用) */ - private getAdRefreshRemain(): number { - const d = smc.vmdata?.mission_data; - if (!d) return 0; - return Math.max(0, Math.floor(Number(d.skill_box_ad_refresh_remain ?? 0))); - } - - /** 消耗一次刷新(优先用普通次数,再用广告奖励) */ - private consumeRefresh(): boolean { - const d = smc.vmdata?.mission_data; - if (!d) return false; - const remain = this.getRefreshRemain(); - const adRemain = this.getAdRefreshRemain(); - if (remain + adRemain <= 0) return false; - if (remain > 0) { - d.skill_box_refresh_remain = remain - 1; - } else { - d.skill_box_ad_refresh_remain = adRemain - 1; - } - return true; - } - - /** 同步刷新次数显示 */ - private refreshRefreshCountUI() { - if (!this.refreshCountLabel) return; - const total = this.getRefreshRemain() + this.getAdRefreshRemain(); - this.refreshCountLabel.string = `${total}`; - } - - // ======================== 内部:玩家选择 ======================== - - /** - * 监听每个 CardComp 的 UseSkillCard 派发,以关闭弹窗。 - * 由于 CardComp.useCard 内部已经 dispatchEvent(UseSkillCard, payload), - * 这里只需监听事件并在识别为本弹窗的卡时关闭。 - */ - private onSkillCardUsed(event: string, args: any) { - if (this.hasPicked) return; - const payload = args ?? event; - if (!payload) return; - const uuid = Number(payload?.uuid ?? 0); - // 仅处理本弹窗抽出的 SkillBox 卡(uuid >= 9000) - if (uuid < 9000) return; - this.hasPicked = true; - mLogger.log(this.debugMode, "MSkillBoxComp", "player picked skill card", { uuid }); - oops.gui.remove(UIID.SkillBox); - } - - // ======================== 按钮事件 ======================== private bindEvents() { - this.refreshBtn?.on(Button.EventType.CLICK, this.onRefreshClick, this); - this.adRefreshBtn?.on(Button.EventType.CLICK, this.onAdRefreshClick, this); - oops.message.on(GameEvent.UseSkillCard, this.onSkillCardUsed, this); + } - private unbindEvents() { - this.refreshBtn?.off(Button.EventType.CLICK, this.onRefreshClick, this); - this.adRefreshBtn?.off(Button.EventType.CLICK, this.onAdRefreshClick, this); - oops.message.off(GameEvent.UseSkillCard, this.onSkillCardUsed, this); - } - private onRefreshClick() { - if (this.hasPicked) return; - if (this.getRefreshRemain() + this.getAdRefreshRemain() <= 0) { - oops.gui.toast("刷新次数已用完,请观看广告获取"); - return; - } - if (!this.consumeRefresh()) return; - this.reroll(); - this.refreshRefreshCountUI(); - } + reset() { - private onAdRefreshClick() { - // TODO: 接入广告 SDK 后,在广告播放成功回调中: - // smc.vmdata.mission_data.skill_box_ad_refresh_remain += 3; - // this.refreshRefreshCountUI(); - // 当前为占位实现,仅打印日志提示 - mLogger.log(this.debugMode, "MSkillBoxComp", "onAdRefreshClick", "TODO: 接入广告 SDK,成功后 adRefreshRemain += 3"); - oops.gui.toast("广告功能即将上线"); } } diff --git a/assets/script/game/map/MissSkillsComp.ts b/assets/script/game/map/MissSkillsComp.ts index 32b1cd18..2e325443 100644 --- a/assets/script/game/map/MissSkillsComp.ts +++ b/assets/script/game/map/MissSkillsComp.ts @@ -28,7 +28,6 @@ import { SkillBoxComp } from "./SkillBoxComp"; import { oops } from "db://oops-framework/core/Oops"; import { GameEvent } from "../common/config/GameEvent"; import { smc } from "../common/SingletonModuleComp"; -import { SkillBoxCardConfig } from "../common/config/CardSet"; const { ccclass, property } = _decorator; /** 技能槽位数据结构 */ @@ -142,32 +141,13 @@ export class MissSkillsComp extends CCComp { * 处理使用技能卡事件:提取 uuid 和 card_lv 后调用 addSkill。 * @param event 事件名 * @param args 卡牌数据(含 uuid、card_lv) - * - * 兼容两种数据源: - * - 普通卡池(uuid < 9000):走 addSkill(uuid, card_lv) 旧流程 - * - SkillBox 卡池(uuid >= 9000 且 payload 含 s_uuid): - * 视为 SkillBoxCardConfig,使用 addSkillByConfig 走新流程 */ private onUseSkillCard(event: string, args: any) { const payload = args ?? event; const uuid = Number(payload?.uuid ?? 0); const card_lv = Math.max(1, Math.floor(Number(payload?.card_lv ?? 1))); if (!uuid) return; - if (this.isSkillBoxPayload(payload)) { - this.addSkillByConfig(payload as SkillBoxCardConfig); - } else { - this.addSkill(uuid, card_lv); - } - } - - /** - * 判断 payload 是否为 SkillBox 三选一弹窗的卡牌配置。 - * 识别规则:uuid >= 9000 且 payload 含 s_uuid 字段。 - */ - private isSkillBoxPayload(payload: any): boolean { - if (!payload) return false; - const uuid = Number(payload.uuid ?? 0); - return uuid >= 9000 && Number(payload.s_uuid ?? 0) > 0; + this.addSkill(uuid, card_lv); } start() { @@ -212,37 +192,6 @@ export class MissSkillsComp extends CCComp { comp.init(uuid, card_lv); } - /** - * 在场上添加一个 SkillBox 弹窗产出的技能卡(uuid >= 9000): - * 流程与 addSkill 相同,但初始化走 initWithConfig 以支持 overrides / s_uuid。 - * - * @param skillBoxCard SkillBoxCardConfig 完整卡牌配置 - */ - addSkillByConfig(skillBoxCard: SkillBoxCardConfig) { - var parent = smc.map.MapView.scene.entityLayer!.node!.getChildByName("SKILL")!; - - if (!this.skill_box) { - mLogger.error(this.debugMode, "MissSkillsComp", "skill_box prefab not set"); - return; - } - - const emptyIndex = this.slots.findIndex(slot => !slot.used); - if (emptyIndex === -1) { - mLogger.warn(this.debugMode, "MissSkillsComp", "skill_box slots are full"); - return; - } - - const node = instantiate(this.skill_box); - node.parent = parent; - node.setPosition(new Vec3(this.slots[emptyIndex].x, this.slots[emptyIndex].y, 0)); - - this.slots[emptyIndex].used = true; - this.slots[emptyIndex].node = node; - - const comp = node.getComponent(SkillBoxComp) || node.addComponent(SkillBoxComp); - comp.initWithConfig(skillBoxCard); - } - /** ECS 组件移除时销毁节点 */ reset() { this.node.destroy(); diff --git a/assets/script/game/map/MissionCardComp.ts b/assets/script/game/map/MissionCardComp.ts index 2a262d32..525a0b2e 100644 --- a/assets/script/game/map/MissionCardComp.ts +++ b/assets/script/game/map/MissionCardComp.ts @@ -37,7 +37,7 @@ import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, SpriteAtla import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; import { GameEvent } from "../common/config/GameEvent"; -import { CARD_POOL_INIT_LEVEL, CARD_POOL_MAX_LEVEL, CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE, CardConfig, CardType, CardsUpSet, drawCardsByRule, getCardsByLv, SKILL_BOX_TRIGGER_WAVES, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet"; +import { CARD_POOL_INIT_LEVEL, CARD_POOL_MAX_LEVEL, CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE, CardConfig, CardType, CardsUpSet, drawCardsByRule, getCardsByLv, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet"; import { CardComp } from "./CardComp"; import { oops } from "db://oops-framework/core/Oops"; import { HeroAttrsComp } from "../hero/HeroAttrsComp"; @@ -48,7 +48,6 @@ import { FacSet, FightSet } from "../common/config/GameSet"; import { MoveComp } from "../hero/MoveComp"; import { MissionHeroComp } from "./MissionHeroComp"; import { MissionEconomy } from "./MissionEconomy"; -import { UIID } from "../common/config/GameUIConfig"; const { ccclass, property } = _decorator; @@ -324,35 +323,13 @@ export class MissionCardComp extends CCComp { } /** 新一波:展开面板 → 刷新费用 UI → 重新抽卡分发 */ - private onNewWave(event?: string, args?: any) { + private onNewWave() { this.isBattlePhase = false; this.enterPreparePhase(); this.updateCoinAndCostUI(); this.layoutCardSlots(); const cards = this.buildDrawCards(); this.dispatchCardsToSlots(cards); - - // 固定波次(1/5/10/15/20)弹出技能三选一弹窗 - const wave = Number(args?.wave ?? 0); - if (this.isSkillBoxTriggerWave(wave)) { - this.openSkillBox(wave); - } - } - - /** 判断 wave 是否属于技能弹窗触发波次 */ - private isSkillBoxTriggerWave(wave: number): boolean { - if (!wave || wave <= 0) return false; - return SKILL_BOX_TRIGGER_WAVES.includes(wave); - } - - /** 打开技能三选一弹窗(MSkillBoxComp) */ - private openSkillBox(wave: number) { - if (smc.map?.MapView) { - oops.gui.open(UIID.SkillBox, { wave, poolLv: this.poolLv }); - mLogger.log(this.debugMode, "MissionCardComp", "openSkillBox", { wave, poolLv: this.poolLv }); - } else { - mLogger.warn(this.debugMode, "MissionCardComp", "openSkillBox skipped, smc.map.MapView not ready"); - } } /** 解除按钮监听,避免节点销毁后回调泄漏 */ @@ -626,17 +603,13 @@ export class MissionCardComp extends CCComp { this.cardsHideScale = new Vec3(0, 0, this.cardsBaseScale.z); } - /** 进入准备阶段:展开卡牌面板(立即恢复缩放,无动画)+ 显示抽卡按钮 */ + /** 进入准备阶段:展开卡牌面板(立即恢复缩放,无动画) */ private enterPreparePhase() { if (!this.cards_node || !this.cards_node.isValid) return; this.initCardsPanelPos(); this.cards_node.active = true; Tween.stopAllByTarget(this.cards_node); this.cards_node.setScale(this.cardsShowScale); - // 准备阶段:显示抽卡按钮 - if (this.cards_chou && this.cards_chou.isValid) { - this.cards_chou.active = true; - } } private enterBattlePhase() { @@ -652,16 +625,16 @@ export class MissionCardComp extends CCComp { // } // }) // .start(); - // 战斗阶段:隐藏抽卡按钮 - if (this.cards_chou && this.cards_chou.isValid) { - this.cards_chou.active = false; - } } /** 构建本次抽卡结果,保证最终可分发3条数据 */ private buildDrawCards(): CardConfig[] { - // 技能卡已不再通过常规刷新分发,统一走 MSkillBoxComp 固定波次弹窗 - const targetType: CardType[] = [CardType.Hero, CardType.SpecialRefresh]; + let targetType: CardType | CardType[] | undefined = undefined; + if (this.isBattlePhase) { + targetType = CardType.Skill; + } else { + targetType = [CardType.Hero, CardType.SpecialRefresh]; + } const cards = getCardsByLv(this.poolLv, targetType); /** 正常情况下直接取前3 */ diff --git a/assets/script/game/map/MissionComp.ts b/assets/script/game/map/MissionComp.ts index 0d259ded..a8e71bd1 100644 --- a/assets/script/game/map/MissionComp.ts +++ b/assets/script/game/map/MissionComp.ts @@ -727,9 +727,6 @@ export class MissionComp extends CCComp { smc.vmdata.mission_data.coin = Math.max(0, Math.floor(CardInitCoins)); // 【评分系统 - 效率分】记录初始获得的金币收入 smc.vmdata.scores.gold_earned += smc.vmdata.mission_data.coin; - // 技能三选一弹窗:每局重置 1 次普通刷新 + 清零广告累计次数 - smc.vmdata.mission_data.skill_box_refresh_remain = 1; - smc.vmdata.mission_data.skill_box_ad_refresh_remain = 0; } // ======================== 波次管理 ======================== diff --git a/assets/script/game/map/SkillBoxComp.ts b/assets/script/game/map/SkillBoxComp.ts index 9da64d4f..12465dd0 100644 --- a/assets/script/game/map/SkillBoxComp.ts +++ b/assets/script/game/map/SkillBoxComp.ts @@ -29,8 +29,8 @@ import { mLogger } from "../common/Logger"; import { _decorator, Node, Prefab, Sprite, Label, Vec3, resources, SpriteAtlas } 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 { CardPoolList, SkillBoxCardConfig } from "../common/config/CardSet"; -import { SkillOverrides, SkillSet } from "../common/config/SkillSet"; +import { CardPoolList } from "../common/config/CardSet"; +import { SkillSet } from "../common/config/SkillSet"; import { oops } from "db://oops-framework/core/Oops"; import { GameEvent } from "../common/config/GameEvent"; import { smc } from "../common/SingletonModuleComp"; @@ -68,8 +68,6 @@ export class SkillBoxComp extends CCComp { private trigger_times: number = 1; /** 触发间隔(秒,仅持续技能有效) */ private trigger_interval: number = 0; - /** 技能参数覆盖(来自 SkillBoxCardConfig.overrides,触发时随事件派发) */ - private skill_overrides: SkillOverrides | null = null; // ======================== 运行时状态 ======================== @@ -125,7 +123,6 @@ export class SkillBoxComp extends CCComp { this.trigger_times = config.t_times ?? 1; this.trigger_interval = config.t_inv ?? 0; } - this.skill_overrides = null; this.current_trigger_times = 0; this.timer = 0; @@ -146,39 +143,6 @@ export class SkillBoxComp extends CCComp { } } - /** - * 使用 SkillBoxCardConfig 初始化(MSkillBoxComp 三选一弹窗场景) - * - * 与 init(uuid, card_lv) 的区别: - * - 直接以 s_uuid 字段作为实际生效技能 - * - 触发参数(is_inst / t_times / t_inv)取自 SkillBoxCardConfig - * - overrides 在 triggerSkill 时随事件一起派发,由技能执行系统按需应用 - */ - initWithConfig(skillBoxCard: SkillBoxCardConfig) { - this.s_uuid = skillBoxCard.s_uuid; - this.card_lv = Math.max(1, Math.floor(skillBoxCard.card_lv ?? 1)); - this.is_instant = skillBoxCard.is_inst ?? true; - this.trigger_times = skillBoxCard.t_num ?? skillBoxCard.t_times ?? 1; - this.trigger_interval = skillBoxCard.t_inv ?? 0; - this.skill_overrides = skillBoxCard.overrides ?? null; - - this.current_trigger_times = 0; - this.timer = 0; - this.initialized = true; - - this.updateUI(); - - if (this.is_instant) { - this.triggerSkill(); - this.current_trigger_times++; - if (this.current_trigger_times >= this.trigger_times) { - this.scheduleOnce(() => { - this.node.destroy(); - }, 1.0); - } - } - } - /** * 更新 UI: * - 图标:从 uicons 图集获取。 @@ -293,13 +257,11 @@ export class SkillBoxComp extends CCComp { const parentPos = this.node.parent ? this.node.parent.position : new Vec3(0, 0, 0); targetPos.set(parentPos.x + localPos.x, parentPos.y + localPos.y, 0); - // 将 SkillBoxCardConfig 的 overrides 一起派发,技能执行系统可按需合并到基础 SkillSet 配置 oops.message.dispatchEvent(GameEvent.TriggerSkill, { s_uuid: this.s_uuid, isCardSkill: true, // 标记为卡牌技能(区别于英雄自身技能) card_lv: this.card_lv, - targetPos: targetPos, - overrides: this.skill_overrides ?? undefined + targetPos: targetPos }); }