refactor: 重构技能弹窗系统,移除冗余技能池逻辑
1. 删除SkillBoxCardConfig相关类型、技能池配置和抽卡函数 2. 移除技能弹窗的刷新次数持久化逻辑与UI 3. 简化MissSkillsComp、SkillBoxComp的技能处理流程 4. 统一技能卡的添加和初始化逻辑,移除config专用初始化流程 5. 调整MissionCardComp的波次技能弹窗触发逻辑 6. 清理CardComp中冗余的技能描述缓存代码 7. 修正UIConfig中SkillBox预制体路径命名
This commit is contained in:
@@ -73,9 +73,6 @@ export class SingletonModuleComp extends ecs.Comp {
|
|||||||
max_mission: 4,//最大关卡
|
max_mission: 4,//最大关卡
|
||||||
coin: 0,
|
coin: 0,
|
||||||
time: 15 * 60,//游戏时间
|
time: 15 * 60,//游戏时间
|
||||||
// 技能三选一弹窗:刷新次数(跨波次保留,广告可叠加)
|
|
||||||
skill_box_refresh_remain: 1, // 初始赠送的刷新次数
|
|
||||||
skill_box_ad_refresh_remain: 0, // 看广告累计的额外次数
|
|
||||||
},
|
},
|
||||||
scores: {
|
scores: {
|
||||||
score: 0, // 基础得分
|
score: 0, // 基础得分
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import * as exp from "constants"
|
|||||||
import { HeroInfo, HeroList, HType } from "./heroSet"
|
import { HeroInfo, HeroList, HType } from "./heroSet"
|
||||||
import { FightSet } from "./GameSet"
|
import { FightSet } from "./GameSet"
|
||||||
import { oops } from "db://oops-framework/core/Oops"
|
import { oops } from "db://oops-framework/core/Oops"
|
||||||
import { SkillOverrides } from "./SkillSet"
|
|
||||||
|
|
||||||
class I18nString {
|
class I18nString {
|
||||||
constructor(private key: string, private params?: any[]) { }
|
constructor(private key: string, private params?: any[]) { }
|
||||||
@@ -63,25 +62,6 @@ export interface CardConfig {
|
|||||||
t_times?: number // 触发次数
|
t_times?: number // 触发次数
|
||||||
t_inv?: 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<number, number> = {
|
export const CardsUpSet: Record<number, number> = {
|
||||||
1: 50,
|
1: 50,
|
||||||
2: 100,
|
2: 100,
|
||||||
@@ -315,172 +295,3 @@ export const drawCardsByRule = (
|
|||||||
const picked = pickCards(pool, count)
|
const picked = pickCards(pool, count)
|
||||||
return picked
|
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<number, SkillBoxCardConfig[]> = {
|
|
||||||
// 第 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<number>();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -39,5 +39,5 @@ export var UIConfigData: { [key: number]: UIConfig } = {
|
|||||||
// [UIID.Talents]: { layer: LayerType.UI, prefab: "gui/element/talents" },
|
// [UIID.Talents]: { layer: LayerType.UI, prefab: "gui/element/talents" },
|
||||||
[UIID.Mission]: { layer: LayerType.UI, prefab: "gui/element/mission" },
|
[UIID.Mission]: { layer: LayerType.UI, prefab: "gui/element/mission" },
|
||||||
[UIID.HInfo]: { layer: LayerType.UI, prefab: "gui/element/hnode" },
|
[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" },
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,6 @@ import { FieldSkillType } from "../common/config/SkillSet";
|
|||||||
import { FieldSkillHelper } from "../hero/FieldSkillHelper";
|
import { FieldSkillHelper } from "../hero/FieldSkillHelper";
|
||||||
import { getLvColor } from "../common/config/GameSet";
|
import { getLvColor } from "../common/config/GameSet";
|
||||||
import { MissionEconomy } from "./MissionEconomy";
|
import { MissionEconomy } from "./MissionEconomy";
|
||||||
import { buildSkillDesc } from "../common/config/HeroSkillDesc";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -95,9 +94,6 @@ export class CardComp extends CCComp {
|
|||||||
@property(Node)
|
@property(Node)
|
||||||
hp_node = null!
|
hp_node = null!
|
||||||
|
|
||||||
/** 技能信息标签缓存引用(由 cacheLabels 在 onLoad 时初始化) */
|
|
||||||
private infoLabel: Label | null = null;
|
|
||||||
|
|
||||||
// ======================== 运行时状态 ========================
|
// ======================== 运行时状态 ========================
|
||||||
|
|
||||||
/** 当前卡牌的金币费用 */
|
/** 当前卡牌的金币费用 */
|
||||||
@@ -148,7 +144,6 @@ export class CardComp extends CCComp {
|
|||||||
onLoad() {
|
onLoad() {
|
||||||
/** 初始阶段只做UI状态准备,不触发业务逻辑 */
|
/** 初始阶段只做UI状态准备,不触发业务逻辑 */
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
this.cacheLabels();
|
|
||||||
this.restPosition = this.node.position.clone();
|
this.restPosition = this.node.position.clone();
|
||||||
this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
|
this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
|
||||||
this.opacityComp.opacity = 255;
|
this.opacityComp.opacity = 255;
|
||||||
@@ -705,8 +700,6 @@ export class CardComp extends CCComp {
|
|||||||
this.hp_node.active = true;
|
this.hp_node.active = true;
|
||||||
// 英雄卡:隐藏技能信息节点
|
// 英雄卡:隐藏技能信息节点
|
||||||
if (this.info_node) this.info_node.active = false;
|
if (this.info_node) this.info_node.active = false;
|
||||||
// 英雄卡:清空技能描述文本,避免切回时残留上一次的技能信息
|
|
||||||
if (this.infoLabel) this.infoLabel.string = "";
|
|
||||||
} else if (this.card_type === CardType.Skill) {
|
} else if (this.card_type === CardType.Skill) {
|
||||||
if (this.lvl_node) this.lvl_node.node.active = false;
|
if (this.lvl_node) this.lvl_node.node.active = false;
|
||||||
// 技能卡:显示技能名 + 品质后缀 + 描述
|
// 技能卡:显示技能名 + 品质后缀 + 描述
|
||||||
@@ -720,18 +713,6 @@ export class CardComp extends CCComp {
|
|||||||
this.hp_node.active = false;
|
this.hp_node.active = false;
|
||||||
// 技能卡:显示技能信息节点
|
// 技能卡:显示技能信息节点
|
||||||
if (this.info_node) this.info_node.active = true;
|
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 {
|
} else {
|
||||||
if (this.lvl_node) this.lvl_node.node.active = false;
|
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.ap_node.active = false;
|
||||||
this.hp_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;
|
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 中的帧名)。
|
* 根据卡牌类型和 UUID 解析出图标 ID(在 SpriteAtlas 中的帧名)。
|
||||||
|
|||||||
@@ -1,266 +1,83 @@
|
|||||||
/**
|
/**
|
||||||
* @file MSkillBoxComp.ts
|
* @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 { 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 { 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 { 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;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MSkillBoxComp —— 技能三选一弹窗控制器
|
* MSkillBoxComp —— 技能卡牌系统核心控制器
|
||||||
*
|
*
|
||||||
* 由 oops.gui.open(UIID.SkillBox, { wave, poolLv }) 实例化。
|
* 管理 3 个卡牌槽位的抽卡分发、卡池升级、金币费用、
|
||||||
|
* 在 1,5,10,15,20 波次弹出技能卡牌三选一
|
||||||
*/
|
*/
|
||||||
@ccclass('MSkillBoxComp')
|
@ccclass('MSkillBoxComp')
|
||||||
@ecs.register('MSkillBoxComp', false)
|
@ecs.register('MissionCard', false)
|
||||||
export class MSkillBoxComp extends CCComp {
|
export class MSkillBoxComp extends CCComp {
|
||||||
/** 是否启用调试日志 */
|
/** 是否启用调试日志 */
|
||||||
private debugMode: boolean = true;
|
private debugMode: boolean = false;
|
||||||
|
|
||||||
// ======================== 编辑器绑定节点 ========================
|
// ======================== 编辑器绑定节点 ========================
|
||||||
|
|
||||||
/** 卡牌槽位 1 节点(需挂 CardComp) */
|
/** 卡牌槽位 1 节点 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
card1: Node = null!
|
card1: Node = null!
|
||||||
/** 卡牌槽位 2 节点(需挂 CardComp) */
|
/** 卡牌槽位 2 节点 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
card2: Node = null!
|
card2: Node = null!
|
||||||
/** 卡牌槽位 3 节点(需挂 CardComp) */
|
/** 卡牌槽位 3 节点 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
card3: Node = null!
|
card3: Node = null!
|
||||||
/** 刷新按钮(消耗 1 次 refreshRemain 重抽 3 张) */
|
/** 刷新按钮 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
refreshBtn: Node = null!
|
refreshBtn: Node = null!
|
||||||
/** 看广告刷新按钮(回调留空,后续接入广告 SDK 后累加 adRefreshRemain +3) */
|
/** 看广告刷新按钮 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
adRefreshBtn: Node = null!
|
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() {
|
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() {
|
onDestroy() {
|
||||||
super.onDestroy();
|
|
||||||
this.unbindEvents();
|
|
||||||
oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMissionEnd() {
|
|
||||||
// 任务结束时强制关闭弹窗
|
|
||||||
oops.gui.remove(UIID.SkillBox);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// 弹窗组件无需额外 init,onAdded 阶段完成所有初始化
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
update(dt: number) {
|
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() {
|
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() {
|
reset() {
|
||||||
if (this.hasPicked) return;
|
|
||||||
if (this.getRefreshRemain() + this.getAdRefreshRemain() <= 0) {
|
|
||||||
oops.gui.toast("刷新次数已用完,请观看广告获取");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.consumeRefresh()) return;
|
|
||||||
this.reroll();
|
|
||||||
this.refreshRefreshCountUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
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("广告功能即将上线");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import { SkillBoxComp } from "./SkillBoxComp";
|
|||||||
import { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
import { GameEvent } from "../common/config/GameEvent";
|
import { GameEvent } from "../common/config/GameEvent";
|
||||||
import { smc } from "../common/SingletonModuleComp";
|
import { smc } from "../common/SingletonModuleComp";
|
||||||
import { SkillBoxCardConfig } from "../common/config/CardSet";
|
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
/** 技能槽位数据结构 */
|
/** 技能槽位数据结构 */
|
||||||
@@ -142,32 +141,13 @@ export class MissSkillsComp extends CCComp {
|
|||||||
* 处理使用技能卡事件:提取 uuid 和 card_lv 后调用 addSkill。
|
* 处理使用技能卡事件:提取 uuid 和 card_lv 后调用 addSkill。
|
||||||
* @param event 事件名
|
* @param event 事件名
|
||||||
* @param args 卡牌数据(含 uuid、card_lv)
|
* @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) {
|
private onUseSkillCard(event: string, args: any) {
|
||||||
const payload = args ?? event;
|
const payload = args ?? event;
|
||||||
const uuid = Number(payload?.uuid ?? 0);
|
const uuid = Number(payload?.uuid ?? 0);
|
||||||
const card_lv = Math.max(1, Math.floor(Number(payload?.card_lv ?? 1)));
|
const card_lv = Math.max(1, Math.floor(Number(payload?.card_lv ?? 1)));
|
||||||
if (!uuid) return;
|
if (!uuid) return;
|
||||||
if (this.isSkillBoxPayload(payload)) {
|
this.addSkill(uuid, card_lv);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
@@ -212,37 +192,6 @@ export class MissSkillsComp extends CCComp {
|
|||||||
comp.init(uuid, card_lv);
|
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 组件移除时销毁节点 */
|
/** ECS 组件移除时销毁节点 */
|
||||||
reset() {
|
reset() {
|
||||||
this.node.destroy();
|
this.node.destroy();
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, SpriteAtla
|
|||||||
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 { GameEvent } from "../common/config/GameEvent";
|
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 { CardComp } from "./CardComp";
|
||||||
import { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||||
@@ -48,7 +48,6 @@ import { FacSet, FightSet } from "../common/config/GameSet";
|
|||||||
import { MoveComp } from "../hero/MoveComp";
|
import { MoveComp } from "../hero/MoveComp";
|
||||||
import { MissionHeroComp } from "./MissionHeroComp";
|
import { MissionHeroComp } from "./MissionHeroComp";
|
||||||
import { MissionEconomy } from "./MissionEconomy";
|
import { MissionEconomy } from "./MissionEconomy";
|
||||||
import { UIID } from "../common/config/GameUIConfig";
|
|
||||||
|
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
@@ -324,35 +323,13 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 新一波:展开面板 → 刷新费用 UI → 重新抽卡分发 */
|
/** 新一波:展开面板 → 刷新费用 UI → 重新抽卡分发 */
|
||||||
private onNewWave(event?: string, args?: any) {
|
private onNewWave() {
|
||||||
this.isBattlePhase = false;
|
this.isBattlePhase = false;
|
||||||
this.enterPreparePhase();
|
this.enterPreparePhase();
|
||||||
this.updateCoinAndCostUI();
|
this.updateCoinAndCostUI();
|
||||||
this.layoutCardSlots();
|
this.layoutCardSlots();
|
||||||
const cards = this.buildDrawCards();
|
const cards = this.buildDrawCards();
|
||||||
this.dispatchCardsToSlots(cards);
|
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);
|
this.cardsHideScale = new Vec3(0, 0, this.cardsBaseScale.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 进入准备阶段:展开卡牌面板(立即恢复缩放,无动画)+ 显示抽卡按钮 */
|
/** 进入准备阶段:展开卡牌面板(立即恢复缩放,无动画) */
|
||||||
private enterPreparePhase() {
|
private enterPreparePhase() {
|
||||||
if (!this.cards_node || !this.cards_node.isValid) return;
|
if (!this.cards_node || !this.cards_node.isValid) return;
|
||||||
this.initCardsPanelPos();
|
this.initCardsPanelPos();
|
||||||
this.cards_node.active = true;
|
this.cards_node.active = true;
|
||||||
Tween.stopAllByTarget(this.cards_node);
|
Tween.stopAllByTarget(this.cards_node);
|
||||||
this.cards_node.setScale(this.cardsShowScale);
|
this.cards_node.setScale(this.cardsShowScale);
|
||||||
// 准备阶段:显示抽卡按钮
|
|
||||||
if (this.cards_chou && this.cards_chou.isValid) {
|
|
||||||
this.cards_chou.active = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enterBattlePhase() {
|
private enterBattlePhase() {
|
||||||
@@ -652,16 +625,16 @@ export class MissionCardComp extends CCComp {
|
|||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
// .start();
|
// .start();
|
||||||
// 战斗阶段:隐藏抽卡按钮
|
|
||||||
if (this.cards_chou && this.cards_chou.isValid) {
|
|
||||||
this.cards_chou.active = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 构建本次抽卡结果,保证最终可分发3条数据 */
|
/** 构建本次抽卡结果,保证最终可分发3条数据 */
|
||||||
private buildDrawCards(): CardConfig[] {
|
private buildDrawCards(): CardConfig[] {
|
||||||
// 技能卡已不再通过常规刷新分发,统一走 MSkillBoxComp 固定波次弹窗
|
let targetType: CardType | CardType[] | undefined = undefined;
|
||||||
const targetType: CardType[] = [CardType.Hero, CardType.SpecialRefresh];
|
if (this.isBattlePhase) {
|
||||||
|
targetType = CardType.Skill;
|
||||||
|
} else {
|
||||||
|
targetType = [CardType.Hero, CardType.SpecialRefresh];
|
||||||
|
}
|
||||||
const cards = getCardsByLv(this.poolLv, targetType);
|
const cards = getCardsByLv(this.poolLv, targetType);
|
||||||
|
|
||||||
/** 正常情况下直接取前3 */
|
/** 正常情况下直接取前3 */
|
||||||
|
|||||||
@@ -727,9 +727,6 @@ export class MissionComp extends CCComp {
|
|||||||
smc.vmdata.mission_data.coin = Math.max(0, Math.floor(CardInitCoins));
|
smc.vmdata.mission_data.coin = Math.max(0, Math.floor(CardInitCoins));
|
||||||
// 【评分系统 - 效率分】记录初始获得的金币收入
|
// 【评分系统 - 效率分】记录初始获得的金币收入
|
||||||
smc.vmdata.scores.gold_earned += smc.vmdata.mission_data.coin;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 波次管理 ========================
|
// ======================== 波次管理 ========================
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ import { mLogger } from "../common/Logger";
|
|||||||
import { _decorator, Node, Prefab, Sprite, Label, Vec3, resources, SpriteAtlas } from "cc";
|
import { _decorator, Node, Prefab, Sprite, Label, Vec3, resources, SpriteAtlas } 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 { CardPoolList, SkillBoxCardConfig } from "../common/config/CardSet";
|
import { CardPoolList } from "../common/config/CardSet";
|
||||||
import { SkillOverrides, SkillSet } from "../common/config/SkillSet";
|
import { SkillSet } from "../common/config/SkillSet";
|
||||||
import { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
import { GameEvent } from "../common/config/GameEvent";
|
import { GameEvent } from "../common/config/GameEvent";
|
||||||
import { smc } from "../common/SingletonModuleComp";
|
import { smc } from "../common/SingletonModuleComp";
|
||||||
@@ -68,8 +68,6 @@ export class SkillBoxComp extends CCComp {
|
|||||||
private trigger_times: number = 1;
|
private trigger_times: number = 1;
|
||||||
/** 触发间隔(秒,仅持续技能有效) */
|
/** 触发间隔(秒,仅持续技能有效) */
|
||||||
private trigger_interval: number = 0;
|
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_times = config.t_times ?? 1;
|
||||||
this.trigger_interval = config.t_inv ?? 0;
|
this.trigger_interval = config.t_inv ?? 0;
|
||||||
}
|
}
|
||||||
this.skill_overrides = null;
|
|
||||||
|
|
||||||
this.current_trigger_times = 0;
|
this.current_trigger_times = 0;
|
||||||
this.timer = 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:
|
* 更新 UI:
|
||||||
* - 图标:从 uicons 图集获取。
|
* - 图标:从 uicons 图集获取。
|
||||||
@@ -293,13 +257,11 @@ export class SkillBoxComp extends CCComp {
|
|||||||
const parentPos = this.node.parent ? this.node.parent.position : new Vec3(0, 0, 0);
|
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);
|
targetPos.set(parentPos.x + localPos.x, parentPos.y + localPos.y, 0);
|
||||||
|
|
||||||
// 将 SkillBoxCardConfig 的 overrides 一起派发,技能执行系统可按需合并到基础 SkillSet 配置
|
|
||||||
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
|
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
|
||||||
s_uuid: this.s_uuid,
|
s_uuid: this.s_uuid,
|
||||||
isCardSkill: true, // 标记为卡牌技能(区别于英雄自身技能)
|
isCardSkill: true, // 标记为卡牌技能(区别于英雄自身技能)
|
||||||
card_lv: this.card_lv,
|
card_lv: this.card_lv,
|
||||||
targetPos: targetPos,
|
targetPos: targetPos
|
||||||
overrides: this.skill_overrides ?? undefined
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user