Compare commits
4 Commits
c335df9b95
...
b647d68dde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b647d68dde | ||
|
|
fd056b4433 | ||
|
|
e17ffeb2fd | ||
|
|
e9813d2c01 |
3542
assets/resources/gui/element/ebox.prefab
Normal file
3542
assets/resources/gui/element/ebox.prefab
Normal file
File diff suppressed because it is too large
Load Diff
13
assets/resources/gui/element/ebox.prefab.meta
Normal file
13
assets/resources/gui/element/ebox.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "66d7902d-2a85-43d5-8cbb-671cc7dbb275",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "ebox"
|
||||
}
|
||||
}
|
||||
@@ -68,10 +68,13 @@
|
||||
},
|
||||
{
|
||||
"__id__": 3234
|
||||
},
|
||||
{
|
||||
"__id__": 3236
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 3236
|
||||
"__id__": 3238
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -61425,6 +61428,28 @@
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "08cRUjxXhBTb80XPuG1XkA"
|
||||
},
|
||||
{
|
||||
"__type__": "da70cWddIFAWrAp8y0Z3tP6",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 3237
|
||||
},
|
||||
"equip_box": {
|
||||
"__uuid__": "66d7902d-2a85-43d5-8cbb-671cc7dbb275",
|
||||
"__expectedType__": "cc.Prefab"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "92T2HyCYRIT5ZAbyLMEEkH"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
|
||||
@@ -3,7 +3,6 @@ import { HeroInfo, HeroList, HType } from "./heroSet"
|
||||
import { FightSet, SKILL_CARD_WAVES } from "./GameSet"
|
||||
import { oops } from "db://oops-framework/core/Oops"
|
||||
import { SkillOverrides, TGroup } from "./SkillSet"
|
||||
import { EquipCardData, isEquipmentCard, getEquipmentFieldCount } from "./EquipSet"
|
||||
|
||||
class I18nString {
|
||||
constructor(private key: string, private params?: any[]) { }
|
||||
@@ -166,7 +165,11 @@ const waveToCardLv: Record<number, number> = {};
|
||||
SKILL_CARD_WAVES.forEach((w, i) => { waveToCardLv[w] = i + 1; });
|
||||
|
||||
const SkillCardData: any[] = [
|
||||
// ==================== wave 5 档(范围攻击卡,ap 递增,间隔缩短) ====================
|
||||
// ==================== wave 1 档(基础强度) ====================
|
||||
// 装备卡(Field 类型)已迁移至 EquipSet.ts,此处仅保留技能卡
|
||||
|
||||
// ==================== wave 5 档(强度 ×2) ====================
|
||||
// --- 范围攻击卡(ap 递增,间隔缩短) ---
|
||||
{ uuid: 8261, skill: 6201, wave: SKILL_CARD_WAVES[1], name: "雷墙+", info: "召唤雷墙阻挡敌人,有概率击晕", is_inst: false, t_inv: 5, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 150 } },
|
||||
{ uuid: 8262, skill: 6202, wave: SKILL_CARD_WAVES[1], name: "火墙+", info: "召唤火墙阻挡敌人,有概率击晕", is_inst: false, t_inv: 5, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 150 } },
|
||||
{ uuid: 8263, skill: 6203, wave: SKILL_CARD_WAVES[1], name: "飓风+", info: "召唤飓风攻击敌人,有概率击晕", is_inst: false, t_inv: 5, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 150 } },
|
||||
@@ -174,13 +177,16 @@ const SkillCardData: any[] = [
|
||||
{ uuid: 8265, skill: 6205, wave: SKILL_CARD_WAVES[1], name: "风墙+", info: "召唤风墙困住敌人,有概率击晕", is_inst: false, t_inv: 5, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 150 } },
|
||||
{ uuid: 8266, skill: 6206, wave: SKILL_CARD_WAVES[1], name: "陨石术+", info: "召唤陨石范围攻击敌人,有概率击晕", is_inst: false, t_inv: 5, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 150 } },
|
||||
|
||||
// ==================== wave 8 档(范围攻击卡,ap 最高,间隔最短) ====================
|
||||
|
||||
// ==================== wave 8 档(强度 ×3) ====================
|
||||
{ uuid: 8361, skill: 6201, wave: SKILL_CARD_WAVES[2], name: "雷墙++", info: "召唤雷墙阻挡敌人,有概率击晕", is_inst: false, t_inv: 4, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 250 } },
|
||||
{ uuid: 8362, skill: 6202, wave: SKILL_CARD_WAVES[2], name: "火墙++", info: "召唤火墙阻挡敌人,有概率击晕", is_inst: false, t_inv: 4, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 250 } },
|
||||
{ uuid: 8363, skill: 6203, wave: SKILL_CARD_WAVES[2], name: "飓风++", info: "召唤飓风攻击敌人,有概率击晕", is_inst: false, t_inv: 4, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 250 } },
|
||||
{ uuid: 8364, skill: 6204, wave: SKILL_CARD_WAVES[2], name: "水墙++", info: "召唤水墙阻挡敌人,有概率击晕", is_inst: false, t_inv: 4, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 250 } },
|
||||
{ uuid: 8365, skill: 6205, wave: SKILL_CARD_WAVES[2], name: "风墙++", info: "召唤风墙困住敌人,有概率击晕", is_inst: false, t_inv: 4, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 250 } },
|
||||
{ uuid: 8366, skill: 6206, wave: SKILL_CARD_WAVES[2], name: "陨石术++", info: "召唤陨石范围攻击敌人,有概率击晕", is_inst: false, t_inv: 4, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 250 } },
|
||||
|
||||
// --- 范围攻击卡(ap 最高,间隔最短) ---
|
||||
];
|
||||
|
||||
SkillCardData.forEach(data => {
|
||||
@@ -209,29 +215,6 @@ SkillCardData.forEach(data => {
|
||||
});
|
||||
});
|
||||
|
||||
// 装备卡统一从 EquipSet 注册到 CardPoolList(trigger_type=Field,keep_waves=-1)
|
||||
EquipCardData.forEach(data => {
|
||||
const cardLv = waveToCardLv[data.wave] ?? 1;
|
||||
CardPoolList.push({
|
||||
uuid: data.uuid,
|
||||
type: CardType.Skill,
|
||||
cost: data.cost ?? 0,
|
||||
weight: 10,
|
||||
pool_lv: CardLV.LV1,
|
||||
wave: data.wave,
|
||||
kind: CKind.Skill,
|
||||
card_lv: cardLv,
|
||||
name: data.name,
|
||||
info: data.info,
|
||||
is_inst: false,
|
||||
t_times: 999,
|
||||
t_inv: 0,
|
||||
keep_waves: -1,
|
||||
field: data.field,
|
||||
trigger_type: CardTriggerType.Field,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
export enum SpecialRefreshHeroType {
|
||||
Any = 0,
|
||||
@@ -291,9 +274,8 @@ const clampCardLv = (lv: number): CardLV => {
|
||||
return CardLV.LV1;
|
||||
}
|
||||
|
||||
// ============ 装备(光环类技能卡)语义化封装 ============
|
||||
// 装备判定逻辑已迁移至 EquipSet.ts,此处 re-export 保持外部引用兼容。
|
||||
export { isEquipmentCard, getEquipmentFieldCount };
|
||||
// ============ 装备相关语义已迁移至 EquipSet.ts ============
|
||||
// 装备卡池(EquipPoolList)独立管理,不再混入 CardPoolList
|
||||
|
||||
/** 单次按权重抽取一张卡 */
|
||||
const weightedPick = (cards: CardConfig[]): CardConfig | null => {
|
||||
@@ -376,7 +358,6 @@ export const drawCardsByRule = (
|
||||
targetPoolLv?: number
|
||||
wave?: number
|
||||
unique?: boolean
|
||||
excludeEquipment?: boolean
|
||||
} = {}
|
||||
): CardConfig[] => {
|
||||
const count = Math.max(0, Math.floor(options.count ?? 4))
|
||||
@@ -406,11 +387,6 @@ export const drawCardsByRule = (
|
||||
})
|
||||
}
|
||||
|
||||
// 装备卡已改为商店直购,不再参与抽卡
|
||||
if (options.excludeEquipment) {
|
||||
pool = pool.filter(card => !isEquipmentCard(card));
|
||||
}
|
||||
|
||||
const picked = pickCards(pool, count, options.unique)
|
||||
return picked
|
||||
}
|
||||
|
||||
@@ -3,45 +3,38 @@
|
||||
* @description 装备卡池独立配置
|
||||
*
|
||||
* 职责:
|
||||
* 1. 定义所有装备卡的原始数据(EquipCardData)。
|
||||
* 2. 提供 isEquipmentCard / getEquipmentFieldCount 工具函数。
|
||||
* 定义所有装备卡的静态数据,与技能卡池(CardSet.CardPoolList)彻底分离。
|
||||
* 装备卡不再参与抽卡,改为商店直购,任务开始时一次性全部展示。
|
||||
*
|
||||
* 设计说明:
|
||||
* - 装备卡在运行时仍以 CardConfig 形式注册到 CardPoolList(由 CardSet.ts 统一映射),
|
||||
* 保证 SkillBoxComp.init() 和 EquipListComp.applyCardData() 能通过 CardPoolList 查询配置。
|
||||
* - 装备卡统一 trigger_type = Field,keep_waves = -1(持续到战斗结束)。
|
||||
* - wave 档位区分强度:wave 1 (×1) / wave 5 (×2) / wave 8 (×3),
|
||||
* field uuid 分别对应 FieldSkillSet 的 70xx / 72xx / 74xx 段。
|
||||
* 装备生效机制:
|
||||
* 购买 → 派发 GameEvent.UseEquipCard → MissEquipComp 接收 → EBox ECS 实体
|
||||
* → EquipBoxComp(trigger_type=Field,被动驻场)→ FieldSkillHelper 全局聚合属性加成
|
||||
*
|
||||
* 依赖:
|
||||
* - GameSet.SKILL_CARD_WAVES —— wave 档位定义
|
||||
* - CardConfig / CardTriggerType / CardType / CKind / CardLV(CardSet)
|
||||
* - SKILL_CARD_WAVES(GameSet)—— wave 档位定义
|
||||
* - FieldSkillSet(SkillSet)—— 属性词条配置
|
||||
*/
|
||||
import { CardConfig, CardType, CardLV, CKind, CardTriggerType } from "./CardSet";
|
||||
import { SKILL_CARD_WAVES } from "./GameSet";
|
||||
|
||||
/**
|
||||
* 装备卡原始数据(不含 CardType/CKind 等框架字段,由 CardSet.ts 统一映射为 CardConfig)。
|
||||
* trigger_type 固定为 Field,keep_waves 固定为 -1。
|
||||
*/
|
||||
export interface EquipCardRaw {
|
||||
/** 装备唯一标识 */
|
||||
/** wave → card_lv 映射(wave 1→lv1, wave 5→lv2, wave 8→lv3) */
|
||||
const waveToCardLv: Record<number, number> = {};
|
||||
SKILL_CARD_WAVES.forEach((w, i) => { waveToCardLv[w] = i + 1; });
|
||||
|
||||
/** 装备卡原始数据(仅装备相关字段,由 EquipSet 内部转换为 CardConfig) */
|
||||
interface EquipRawData {
|
||||
uuid: number;
|
||||
/** 所属波次档位 */
|
||||
wave: number;
|
||||
/** 装备名称 */
|
||||
name: string;
|
||||
/** 装备描述 */
|
||||
info: string;
|
||||
/** 属性词条数组(指向 FieldSkillSet uuid),支持单卡多词条 */
|
||||
field: number[];
|
||||
/** 购买费用(默认 0) */
|
||||
cost?: number;
|
||||
}
|
||||
|
||||
// ==================== 装备卡数据 ====================
|
||||
|
||||
export const EquipCardData: EquipCardRaw[] = [
|
||||
/** 装备卡原始数据表 */
|
||||
const EquipRawList: EquipRawData[] = [
|
||||
// ==================== wave 1 档(基础强度) ====================
|
||||
|
||||
{ uuid: 8701, wave: SKILL_CARD_WAVES[0], name: "战后恢复", info: "战斗结束生命回复量+10%", field: [7001] },
|
||||
{ uuid: 8702, wave: SKILL_CARD_WAVES[0], name: "攻击加成", info: "英雄攻击力+10%", field: [7002] },
|
||||
{ uuid: 8703, wave: SKILL_CARD_WAVES[0], name: "击晕加成", info: "英雄击晕概率+10%", field: [7003] },
|
||||
@@ -51,14 +44,12 @@ export const EquipCardData: EquipCardRaw[] = [
|
||||
{ uuid: 8707, wave: SKILL_CARD_WAVES[0], name: "生命加成", info: "英雄最大生命+10%", field: [7007] },
|
||||
{ uuid: 8708, wave: SKILL_CARD_WAVES[0], name: "风怒加成", info: "英雄风怒概率+10%", field: [7008] },
|
||||
{ uuid: 8709, wave: SKILL_CARD_WAVES[0], name: "穿刺加成", info: "英雄穿刺概率+10%", field: [7009] },
|
||||
|
||||
{ uuid: 8710, wave: SKILL_CARD_WAVES[0], name: "金币收益", info: "每回合金币收益+1", field: [7010] },
|
||||
{ uuid: 8711, wave: SKILL_CARD_WAVES[0], name: "出售强化", info: "卖出英雄金币+1", field: [7011] },
|
||||
// --- 多词条装备 ---
|
||||
// 多词条装备
|
||||
{ uuid: 8720, wave: SKILL_CARD_WAVES[0], name: "勇者护符", info: "英雄攻击力+10%, 暴击率+10%, 暴击伤害+20%", field: [7002, 7004, 7005] },
|
||||
|
||||
// ==================== wave 5 档(强度 ×2,field uuid 72xx 段) ====================
|
||||
|
||||
// ==================== wave 5 档(强度 ×2) ====================
|
||||
{ uuid: 8751, wave: SKILL_CARD_WAVES[1], name: "战后恢复+", info: "战斗结束生命回复量+20%", field: [7201] },
|
||||
{ uuid: 8752, wave: SKILL_CARD_WAVES[1], name: "攻击加成+", info: "英雄攻击力+20%", field: [7202] },
|
||||
{ uuid: 8753, wave: SKILL_CARD_WAVES[1], name: "击晕加成+", info: "英雄击晕概率+20%", field: [7203] },
|
||||
@@ -68,15 +59,13 @@ export const EquipCardData: EquipCardRaw[] = [
|
||||
{ uuid: 8757, wave: SKILL_CARD_WAVES[1], name: "生命加成+", info: "英雄最大生命+20%", field: [7207] },
|
||||
{ uuid: 8758, wave: SKILL_CARD_WAVES[1], name: "风怒加成+", info: "英雄风怒概率+20%", field: [7208] },
|
||||
{ uuid: 8759, wave: SKILL_CARD_WAVES[1], name: "穿刺加成+", info: "英雄穿刺概率+20%", field: [7209] },
|
||||
|
||||
{ uuid: 8760, wave: SKILL_CARD_WAVES[1], name: "金币收益+", info: "每回合金币收益+2", field: [7210] },
|
||||
{ uuid: 8761, wave: SKILL_CARD_WAVES[1], name: "购买优惠+", info: "购买卡牌费用-2金币", field: [7212] },
|
||||
{ uuid: 8762, wave: SKILL_CARD_WAVES[1], name: "刷新优惠", info: "刷新卡牌费用-1金币", field: [7213] },
|
||||
// --- 多词条装备 ---
|
||||
// 多词条装备
|
||||
{ uuid: 8770, wave: SKILL_CARD_WAVES[1], name: "守卫护甲", info: "英雄最大生命+20%, 战后生命回复+20%, 攻速+20%", field: [7207, 7201, 7206] },
|
||||
|
||||
// ==================== wave 8 档(强度 ×3,field uuid 74xx 段) ====================
|
||||
|
||||
// ==================== wave 8 档(强度 ×3) ====================
|
||||
{ uuid: 8801, wave: SKILL_CARD_WAVES[2], name: "战后恢复++", info: "战斗结束生命回复量+30%", field: [7401] },
|
||||
{ uuid: 8802, wave: SKILL_CARD_WAVES[2], name: "攻击加成++", info: "英雄攻击力+30%", field: [7402] },
|
||||
{ uuid: 8803, wave: SKILL_CARD_WAVES[2], name: "击晕加成++", info: "英雄击晕概率+30%", field: [7403] },
|
||||
@@ -86,38 +75,51 @@ export const EquipCardData: EquipCardRaw[] = [
|
||||
{ uuid: 8807, wave: SKILL_CARD_WAVES[2], name: "生命加成++", info: "英雄最大生命+30%", field: [7407] },
|
||||
{ uuid: 8808, wave: SKILL_CARD_WAVES[2], name: "风怒加成++", info: "英雄风怒概率+30%", field: [7408] },
|
||||
{ uuid: 8809, wave: SKILL_CARD_WAVES[2], name: "穿刺加成++", info: "英雄穿刺概率+30%", field: [7409] },
|
||||
|
||||
{ uuid: 8810, wave: SKILL_CARD_WAVES[2], name: "金币收益++", info: "每回合金币收益+3", field: [7410] },
|
||||
|
||||
// 触发次数强化
|
||||
{ uuid: 8811, wave: SKILL_CARD_WAVES[2], name: "召唤强化++", info: "召唤触发技能次数+1", field: [7014] },
|
||||
{ uuid: 8812, wave: SKILL_CARD_WAVES[2], name: "死亡强化++", info: "死亡触发技能次数+1", field: [7015] },
|
||||
{ uuid: 8813, wave: SKILL_CARD_WAVES[2], name: "开场强化++", info: "战斗开始触发技能次数+1", field: [7016] },
|
||||
{ uuid: 8814, wave: SKILL_CARD_WAVES[2], name: "结束强化++", info: "战斗结束触发技能次数+1", field: [7017] },
|
||||
{ uuid: 8815, wave: SKILL_CARD_WAVES[2], name: "攻击强化++", info: "攻击触发技能次数+1", field: [7018] },
|
||||
{ uuid: 8816, wave: SKILL_CARD_WAVES[2], name: "受击强化++", info: "被攻击触发技能次数+1", field: [7019] },
|
||||
// --- 多词条装备 ---
|
||||
// 多词条装备
|
||||
{ uuid: 8820, wave: SKILL_CARD_WAVES[2], name: "神圣之刃", info: "英雄攻击力+30%, 暴击率+30%, 暴击伤害+60%, 攻速+30%", field: [7402, 7404, 7405, 7406] },
|
||||
];
|
||||
|
||||
// ==================== 工具函数 ====================
|
||||
|
||||
/**
|
||||
* 判断卡牌是否为「装备」(光环类技能卡)。
|
||||
* 判定条件:trigger_type 为 Field(3),且 field 数组非空。
|
||||
* 装备卡池列表(转换为 CardConfig 格式)
|
||||
*
|
||||
* 使用数字字面量 3 避免对 CardSet.CardTriggerType 的运行时依赖,
|
||||
* 由 CardSet.ts 注册时统一填充 trigger_type = CardTriggerType.Field。
|
||||
* 所有装备卡统一配置:
|
||||
* - type: Skill(复用卡牌体系)
|
||||
* - trigger_type: Field(被动驻场)
|
||||
* - keep_waves: -1(持续到战斗结束)
|
||||
* - is_inst: false(不主动施法)
|
||||
* - cost: 由原始数据指定,默认 0
|
||||
*/
|
||||
export const isEquipmentCard = (card: { trigger_type?: number; field?: number[] }): boolean => {
|
||||
return card.trigger_type === 3
|
||||
&& !!card.field
|
||||
&& card.field.length > 0;
|
||||
}
|
||||
export const EquipPoolList: CardConfig[] = EquipRawList.map(data => ({
|
||||
uuid: data.uuid,
|
||||
type: CardType.Skill,
|
||||
cost: data.cost ?? 0,
|
||||
weight: 10,
|
||||
pool_lv: CardLV.LV1,
|
||||
wave: data.wave,
|
||||
kind: CKind.Skill,
|
||||
card_lv: waveToCardLv[data.wave] ?? 1,
|
||||
name: data.name,
|
||||
info: data.info,
|
||||
is_inst: false,
|
||||
t_times: 999,
|
||||
t_inv: 0,
|
||||
keep_waves: -1,
|
||||
field: data.field,
|
||||
trigger_type: CardTriggerType.Field,
|
||||
}));
|
||||
|
||||
/**
|
||||
* 获取装备卡的属性词条数量。
|
||||
* 非装备卡返回 0;装备卡返回 field 数组长度。
|
||||
* 按 UUID 查找装备卡配置
|
||||
* @param uuid 装备卡 UUID
|
||||
*/
|
||||
export const getEquipmentFieldCount = (card: { trigger_type?: number; field?: number[] }): number => {
|
||||
return isEquipmentCard(card) ? (card.field?.length ?? 0) : 0;
|
||||
export function findEquipByUuid(uuid: number): CardConfig | undefined {
|
||||
return EquipPoolList.find(e => e.uuid === uuid);
|
||||
}
|
||||
|
||||
9
assets/script/game/common/config/EquipSet.ts.meta
Normal file
9
assets/script/game/common/config/EquipSet.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "efed3c93-e996-4e93-9801-e948cf268e3c",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -82,5 +82,7 @@ export enum GameEvent {
|
||||
CardPoolUpgrade = "CardPoolUpgrade",
|
||||
TriggerSkill = "TriggerSkill", // 瞬间触发施法事件
|
||||
RemoveSkillBox = "RemoveSkillBox", // 技能盒销毁事件
|
||||
UseEquipCard = "UseEquipCard", // 装备卡购买使用事件
|
||||
RemoveEquipBox = "RemoveEquipBox", // 装备盒销毁事件
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { HeroInfo } from "../common/config/heroSet";
|
||||
import { FieldSkillSet, FieldSkillType } from "../common/config/SkillSet";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { SkillBoxComp } from "../map/SkillBoxComp";
|
||||
import { EquipBoxComp } from "../map/EquipBoxComp";
|
||||
|
||||
/**
|
||||
* 驻场技能(光环属性)计算辅助类
|
||||
@@ -16,7 +17,7 @@ export class FieldSkillHelper {
|
||||
/** 获取指定驻场技能类型的总加成值(计算存活的友方英雄 + 场上的驻场技能卡) */
|
||||
public static getFieldSkillTotalValue(type: FieldSkillType): number {
|
||||
let total = 0;
|
||||
|
||||
|
||||
// 1. 统计英雄带来的驻场技能加成
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||||
const model = entity.get(HeroAttrsComp);
|
||||
@@ -36,7 +37,7 @@ export class FieldSkillHelper {
|
||||
ecs.query(ecs.allOf(SkillBoxComp)).forEach((entity: ecs.Entity) => {
|
||||
const skillBox = entity.get(SkillBoxComp);
|
||||
if (!skillBox || !skillBox.field || skillBox.field.length === 0) return;
|
||||
|
||||
|
||||
for (const skillUuid of skillBox.field) {
|
||||
const skillConfig = FieldSkillSet[skillUuid];
|
||||
if (skillConfig && skillConfig.type === type) {
|
||||
@@ -45,6 +46,19 @@ export class FieldSkillHelper {
|
||||
}
|
||||
});
|
||||
|
||||
// 3. 统计装备盒(装备卡)带来的驻场技能加成
|
||||
ecs.query(ecs.allOf(EquipBoxComp)).forEach((entity: ecs.Entity) => {
|
||||
const equipBox = entity.get(EquipBoxComp);
|
||||
if (!equipBox || !equipBox.field || equipBox.field.length === 0) return;
|
||||
|
||||
for (const skillUuid of equipBox.field) {
|
||||
const skillConfig = FieldSkillSet[skillUuid];
|
||||
if (skillConfig && skillConfig.type === type) {
|
||||
total += skillConfig.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
52
assets/script/game/map/EBox.ts
Normal file
52
assets/script/game/map/EBox.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { instantiate, Prefab, Vec3, Node } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { EquipBoxComp } from "./EquipBoxComp";
|
||||
|
||||
/** EBox 实体:负责技能卡效果节点创建、初始化与销毁流程 */
|
||||
@ecs.register(`EBox`)
|
||||
export class EBox extends ecs.Entity {
|
||||
/** 技能盒子视图和逻辑组件引用 */
|
||||
SkillBox!: EquipBoxComp;
|
||||
|
||||
protected init() {
|
||||
// 如果有纯逻辑数据组件可以在这里 addComponents
|
||||
}
|
||||
|
||||
/** 销毁实体并释放视图节点,防止残留 */
|
||||
destroy(): void {
|
||||
const view = this.get(EquipBoxComp);
|
||||
if (view && view.node && view.node.isValid) {
|
||||
view.node.destroy();
|
||||
}
|
||||
|
||||
this.remove(EquipBoxComp);
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载并初始化技能盒子
|
||||
* 1) 创建节点并挂到 SKILL 层
|
||||
* 2) 初始化表现与属性数据
|
||||
*/
|
||||
load(uuid: number, card_lv: number, pos: Vec3, prefab: Prefab): Node {
|
||||
let node = instantiate(prefab);
|
||||
let scene = smc.map.MapView.scene;
|
||||
|
||||
// 统一挂到实体显示层 SKILL 节点下
|
||||
let parent = scene.entityLayer!.node!.getChildByName("SKILL");
|
||||
if (parent) {
|
||||
node.parent = parent;
|
||||
}
|
||||
node.setPosition(pos);
|
||||
|
||||
// 获取并注册组件
|
||||
let eboxComp = node.getComponent(EquipBoxComp) || node.addComponent(EquipBoxComp);
|
||||
this.add(eboxComp);
|
||||
|
||||
// 初始化业务逻辑
|
||||
eboxComp.init(uuid, card_lv);
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/EBox.ts.meta
Normal file
9
assets/script/game/map/EBox.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "dc85b25e-2b65-464b-8091-07ae23d04bb3",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
564
assets/script/game/map/EquipBoxComp.ts
Normal file
564
assets/script/game/map/EquipBoxComp.ts
Normal file
@@ -0,0 +1,564 @@
|
||||
/**
|
||||
* @file EquipBoxComp.ts
|
||||
* @description 单个技能卡效果控制组件(UI 视图层 + 逻辑层)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 表示一张已使用的技能卡在战场上的 **可视化实体**。
|
||||
* 2. 按 trigger_type 类型化分发触发逻辑(即时 / 定时 / 驻场 / 事件型)。
|
||||
* 3. 显示技能图标和剩余触发次数。
|
||||
* 4. 触发结束后自动销毁。
|
||||
*
|
||||
* 触发类型(CardTriggerType):
|
||||
* - Instant (1):init 时立即触发一次(按 t_times 控制次数,跨波次 NewWave 时再次触发)
|
||||
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每波次数)
|
||||
* - Field (3):被动生效,不主动施法(实际由 FieldSkillSet 处理)
|
||||
* - FightStart (4):监听 FightStart 事件,按 trigger_limit 全局累计上限
|
||||
* - FightEnd (5):监听 FightEnd 事件(每波结束派发),按 trigger_limit 全局累计上限
|
||||
* - HeroDead (6):监听 HeroDead 事件(仅英雄阵营派发,怪物死亡不触发)
|
||||
* - HeroCall (7):监听 MasterCalled(主角/技能召唤)+ ReviveSuccess(复活)
|
||||
*
|
||||
* 关键设计:
|
||||
* - 事件型(4-7)统一走 onEventTrigger 入口,仅作触发信号,不读取 payload
|
||||
* - 触发上限:Instant/Interval 按 t_times(每波内),事件型按 trigger_limit(全局)
|
||||
* - 跨波次:keep_waves 控制存活;事件型 trigger_count 不随波次重置
|
||||
* - 销毁时通过 GameEvent.RemoveSkillBox 通知 MissSkillsComp 回收槽位
|
||||
*
|
||||
* 依赖:
|
||||
* - CardTriggerType(CardSet)—— 卡牌触发类型定义
|
||||
* - EquipPoolList(EquipSet)—— 装备卡池配置(查询触发配置)
|
||||
* - SkillSet —— 技能静态配置(icon 字段)
|
||||
* - GameEvent —— 各类游戏事件
|
||||
* - smc.mission —— 游戏运行状态
|
||||
*/
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { _decorator, Node, Prefab, Sprite, Label, Vec3, resources, SpriteAtlas, tween, v3, Tween, 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 { CardTriggerType } from "../common/config/CardSet";
|
||||
import { EquipPoolList } from "../common/config/EquipSet";
|
||||
import { SkillSet, SkillOverrides, FieldSkillSet } from "../common/config/SkillSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { UIID } from "../common/config/GameUIConfig";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* EquipBoxComp —— 单个技能卡效果视图 + 逻辑组件
|
||||
*
|
||||
* 由 MissSkillsComp.addSkill() 实例化并初始化。
|
||||
* 在战场上以图标 + 剩余次数的形式呈现。
|
||||
*/
|
||||
@ccclass('EquipBoxComp')
|
||||
@ecs.register('EquipBoxComp', false)
|
||||
export class EquipBoxComp extends CCComp {
|
||||
/** 调试日志开关 */
|
||||
private debugMode: boolean = true;
|
||||
|
||||
/** 技能图标节点 */
|
||||
@property({ type: Node })
|
||||
private icon_node: Node = null;
|
||||
|
||||
/** 技能图标节点:下面有 green blue purple red yellow 5个不同颜色节点*/
|
||||
@property({ type: Node })
|
||||
private bg_node: Node = null;
|
||||
|
||||
/** 剩余次数标签 */
|
||||
@property(Label)
|
||||
private info_label: Label = null;
|
||||
|
||||
/** cd计时遮罩 */
|
||||
@property({ type: Node })
|
||||
private cd_mask: Node = null;
|
||||
// ======================== 技能配置 ========================
|
||||
|
||||
/** 技能 UUID */
|
||||
private s_uuid: number = 0;
|
||||
/** 卡牌等级 */
|
||||
private card_lv: number = 1;
|
||||
/** 是否为即时技能(true=使用后立即触发,false=战斗中定时触发) */
|
||||
private is_instant: boolean = true;
|
||||
/** 总触发次数 */
|
||||
private trigger_times: number = 1;
|
||||
/** 触发间隔(秒,仅持续技能有效) */
|
||||
private trigger_interval: number = 0;
|
||||
/** 维持的波次数(-1表示直到战斗结束,0表示不跨波次,>0表示维持的具体波次数) */
|
||||
private keep_waves: number = 0;
|
||||
/** 技能覆写参数(自定义伤害、Buff等) */
|
||||
private overrides?: SkillOverrides;
|
||||
/** 驻场技能 UUID 列表 */
|
||||
public field: number[] = [];
|
||||
/** 卡牌自定义图标ID(优先级最高,未定义则按 trigger_type 自动取) */
|
||||
private card_icon?: string;
|
||||
|
||||
// ======================== 触发类型化扩展 ========================
|
||||
|
||||
/** 触发类型(默认即时,保持向后兼容) */
|
||||
private trigger_type: CardTriggerType = CardTriggerType.Instant;
|
||||
/** 事件型触发的全局次数上限(Infinity 表示无上限) */
|
||||
private trigger_limit: number = Infinity;
|
||||
/** 事件型已触发次数 */
|
||||
private trigger_count: number = 0;
|
||||
|
||||
// ======================== 运行时状态 ========================
|
||||
|
||||
/** 已触发次数 */
|
||||
private current_trigger_times: number = 0;
|
||||
/** 当前计时器(秒) */
|
||||
private timer: number = 0;
|
||||
/** 是否处于战斗中(仅战斗中持续技能才计时) */
|
||||
private in_combat: boolean = false;
|
||||
/** 是否已初始化 */
|
||||
private initialized: boolean = false;
|
||||
|
||||
// ======================== 生命周期 ========================
|
||||
|
||||
/**
|
||||
* 注册全局事件:
|
||||
* - MissionEnd:所有类型都需要监听,任务结束时强制销毁
|
||||
* - NewWave:处理 keep_waves 跨波次逻辑(所有类型统一)
|
||||
* - 其它触发事件由 registerTrigger 按 trigger_type 动态注册
|
||||
*/
|
||||
onLoad() {
|
||||
oops.message.on(GameEvent.MissionEnd, this.onMissionEnd, this);
|
||||
this.node.on(GameEvent.NewWave, this.onNewWave, this);
|
||||
oops.message.on(GameEvent.NewWave, this.onNewWaveGlobal, this);
|
||||
this.node.on(NodeEventType.TOUCH_END, this.onNodeClicked, this);
|
||||
}
|
||||
|
||||
/** 销毁时移除所有事件监听并通知槽位管理器回收 */
|
||||
onDestroy() {
|
||||
super.onDestroy();
|
||||
// 统一 off 所有可能订阅的事件(即使未订阅也无副作用)
|
||||
// 注意:FightStart 可能由两种回调订阅(Interval→onFightStart / FightStart触发型→onEventTrigger),都需要 off
|
||||
oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this);
|
||||
oops.message.off(GameEvent.FightStart, this.onFightStart, this);
|
||||
oops.message.off(GameEvent.FightStart, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.FightEnd, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.HeroDead, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.MasterCalled, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.ReviveSuccess, this.onEventTrigger, this);
|
||||
if (this.node && this.node.isValid) {
|
||||
this.node.off(GameEvent.NewWave, this.onNewWave, this);
|
||||
this.node.off(NodeEventType.TOUCH_END, this.onNodeClicked, this);
|
||||
}
|
||||
oops.message.off(GameEvent.NewWave, this.onNewWaveGlobal, this);
|
||||
// 通知 MissEquipComp 回收该节点占用的槽位
|
||||
oops.message.dispatchEvent(GameEvent.RemoveEquipBox, this.node);
|
||||
}
|
||||
|
||||
private onNodeClicked() {
|
||||
if (!this.initialized) return;
|
||||
oops.audio.playEffect("music/button");
|
||||
// 点击装备盒弹出 HInfoComp,从 EquipPoolList 查询配置
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
oops.gui.open(UIID.HInfo, { heroUuid: this.s_uuid, heroLv: this.card_lv, poolLv: 1, isSkillCard: true, isEquipCard: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化技能卡效果:
|
||||
* 1. 从 EquipPoolList 查询装备卡的触发配置(含 trigger_type)。
|
||||
* 2. 更新 UI 显示(图标 + 次数)。
|
||||
* 3. 按 trigger_type 注册对应事件监听并执行首次触发。
|
||||
*
|
||||
* @param uuid 卡牌 UUID
|
||||
* @param card_lv 技能卡等级
|
||||
*/
|
||||
init(uuid: number, card_lv: number) {
|
||||
this.card_lv = card_lv;
|
||||
|
||||
// 装备卡配置已迁移至 EquipPoolList
|
||||
const config = EquipPoolList.find(c => c.uuid === uuid);
|
||||
if (config) {
|
||||
this.s_uuid = config.skill ?? uuid; // 获取实际的技能 UUID
|
||||
this.is_instant = config.is_inst ?? true;
|
||||
this.trigger_times = config.t_times ?? 1;
|
||||
this.trigger_interval = config.t_inv ?? 0;
|
||||
this.keep_waves = config.keep_waves ?? 0;
|
||||
this.overrides = config.overrides;
|
||||
this.field = config.field || [];
|
||||
this.card_icon = config.icon; // 保存卡牌自定义图标(优先级最高)
|
||||
// 读取触发类型与上限(兜底默认值,避免 undefined)
|
||||
this.trigger_type = config.trigger_type ?? CardTriggerType.Instant;
|
||||
this.trigger_limit = config.trigger_limit ?? Infinity;
|
||||
} else {
|
||||
this.s_uuid = uuid;
|
||||
}
|
||||
|
||||
this.current_trigger_times = 0;
|
||||
this.trigger_count = 0;
|
||||
this.timer = 0;
|
||||
this.initialized = true;
|
||||
|
||||
this.updateUI();
|
||||
|
||||
// 按 trigger_type 注册事件监听 + 执行首次触发
|
||||
this.registerTrigger();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 trigger_type 注册对应事件监听:
|
||||
* - Instant: init 时立即触发一次(保持旧行为)
|
||||
* - Interval: 监听 FightStart,进入战斗后由 update 帧驱动计时
|
||||
* - Field: 不主动施法(实际生效由 FieldSkillSet 处理)
|
||||
* - FightStart: 监听 FightStart 事件
|
||||
* - FightEnd: 监听 FightEnd 事件
|
||||
* - HeroDead: 监听 HeroDead 事件(已在派发处做阵营过滤)
|
||||
* - HeroCall: 监听 MasterCalled(主角/技能召唤)+ ReviveSuccess(复活)
|
||||
*
|
||||
* 注意:MasterCalled 各派发点 payload 不一致,onEventTrigger 仅作触发信号使用。
|
||||
*/
|
||||
private registerTrigger(): void {
|
||||
switch (this.trigger_type) {
|
||||
case CardTriggerType.Instant:
|
||||
// 即时技能:立即触发一次
|
||||
this.onEventTrigger();
|
||||
break;
|
||||
|
||||
case CardTriggerType.Interval:
|
||||
// 定时循环:监听 FightStart 进入战斗后启动计时
|
||||
oops.message.on(GameEvent.FightStart, this.onFightStart, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.Field:
|
||||
// 驻场光环:不主动施法,由 FieldSkillSet 处理
|
||||
break;
|
||||
|
||||
case CardTriggerType.FightStart:
|
||||
oops.message.on(GameEvent.FightStart, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.FightEnd:
|
||||
oops.message.on(GameEvent.FightEnd, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.HeroDead:
|
||||
oops.message.on(GameEvent.HeroDead, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.HeroCall:
|
||||
// 同时监听召唤和复活两类英雄上场事件
|
||||
oops.message.on(GameEvent.MasterCalled, this.onEventTrigger, this);
|
||||
oops.message.on(GameEvent.ReviveSuccess, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
default:
|
||||
mLogger.warn(true, 'EquipBoxComp', `[registerTrigger] unknown trigger_type: ${this.trigger_type}, fallback to Instant`);
|
||||
this.onEventTrigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件型触发的统一入口:
|
||||
* - Instant 类型:按 trigger_times 上限判定,复用 current_trigger_times 跟踪
|
||||
* (保持与原 is_instant 行为一致,且 NewWave 中也用 current_trigger_times)
|
||||
* - 事件型(FightStart/FightEnd/HeroDead/HeroCall):按 trigger_limit 上限判定,使用 trigger_count 跟踪
|
||||
*
|
||||
* 注意:本方法不读取事件 payload,仅作触发信号使用(避免 MasterCalled 不同 payload 字段引发的兼容问题)。
|
||||
*/
|
||||
private onEventTrigger(): void {
|
||||
if (!this.initialized) return;
|
||||
|
||||
if (this.trigger_type === CardTriggerType.Instant) {
|
||||
// 即时触发:上限由 trigger_times 控制(保持旧行为)
|
||||
if (this.current_trigger_times >= this.trigger_times) {
|
||||
this.destroySelf();
|
||||
return;
|
||||
}
|
||||
this.triggerSkill();
|
||||
this.current_trigger_times++;
|
||||
this.updateUI();
|
||||
|
||||
// 单次触发 + 不跨波次维持 → 延迟销毁(保留短暂视觉反馈)
|
||||
if (this.keep_waves === 0 && this.current_trigger_times >= this.trigger_times) {
|
||||
this.scheduleOnce(() => this.destroySelf(), 1.0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 事件型:上限由 trigger_limit 控制(全局累计,跨波次不重置)
|
||||
if (this.trigger_count >= this.trigger_limit) {
|
||||
this.destroySelf();
|
||||
return;
|
||||
}
|
||||
|
||||
this.triggerSkill();
|
||||
this.trigger_count++;
|
||||
this.updateUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的节点销毁封装:
|
||||
* 优先通过 ECS 实体销毁;否则直接销毁节点。
|
||||
*/
|
||||
private destroySelf(): void {
|
||||
if (this.ent) {
|
||||
(this.ent as ecs.Entity).destroy();
|
||||
} else if (this.node && this.node.isValid) {
|
||||
this.node.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据技能等级切换 bg_node 下的颜色子节点显示。
|
||||
* 等级与颜色对应关系(技能卡 wave 档位):
|
||||
* card_lv 1 (wave 1) → green
|
||||
* card_lv 2 (wave 5) → blue
|
||||
* card_lv 3 (wave 8) → purple
|
||||
*/
|
||||
private updateBgNode() {
|
||||
if (!this.bg_node) return;
|
||||
const lvToColor: Record<number, string> = {
|
||||
1: "green",
|
||||
2: "blue",
|
||||
3: "purple",
|
||||
};
|
||||
const targetColor = lvToColor[this.card_lv];
|
||||
this.bg_node.children.forEach(child => {
|
||||
child.active = (child.name === targetColor);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 UI:
|
||||
* - 背景:根据技能等级切换对应颜色子节点显示
|
||||
* - 图标:从 uicons 图集获取
|
||||
* - 剩余次数标签:
|
||||
* * Interval / 事件型:显示剩余次数(按各自上限计算)
|
||||
* * Instant / Field:不显示
|
||||
* - CD 遮罩:仅 Interval 类型展示冷却进度
|
||||
*/
|
||||
updateUI() {
|
||||
// 按技能等级切换背景颜色节点(与 getLvColor 等级配色一致)
|
||||
this.updateBgNode();
|
||||
|
||||
// 加载技能图标
|
||||
if (this.icon_node) {
|
||||
// 优先使用卡牌自定义 icon;未定义则按 trigger_type 自动取
|
||||
let iconId: string | undefined = this.card_icon;
|
||||
if (!iconId) {
|
||||
if (this.trigger_type === CardTriggerType.Interval) {
|
||||
iconId = this.s_uuid ? SkillSet[this.s_uuid]?.icon : undefined;
|
||||
} else if (this.trigger_type === CardTriggerType.Field) {
|
||||
const fieldUuid = this.field?.[0];
|
||||
iconId = fieldUuid ? FieldSkillSet[fieldUuid]?.icon : undefined;
|
||||
}
|
||||
}
|
||||
if (this.icon_node && this.icon_node.isValid) {
|
||||
let sprite = this.icon_node.getComponent(Sprite) || this.icon_node.addComponent(Sprite);
|
||||
if (smc.uiconsAtlas && iconId) {
|
||||
const frame = smc.uiconsAtlas.getSpriteFrame(iconId);
|
||||
sprite.spriteFrame = frame || null; // 取不到时清空,避免残留
|
||||
} else {
|
||||
sprite.spriteFrame = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 是否需要展示剩余次数
|
||||
const showRemainCount =
|
||||
this.trigger_type === CardTriggerType.Interval ||
|
||||
this.trigger_type === CardTriggerType.FightStart ||
|
||||
this.trigger_type === CardTriggerType.FightEnd ||
|
||||
this.trigger_type === CardTriggerType.HeroDead ||
|
||||
this.trigger_type === CardTriggerType.HeroCall;
|
||||
|
||||
if (this.info_label) {
|
||||
if (showRemainCount) {
|
||||
// 事件型按 trigger_limit;Interval 按 t_times
|
||||
const isEvent =
|
||||
this.trigger_type === CardTriggerType.FightStart ||
|
||||
this.trigger_type === CardTriggerType.FightEnd ||
|
||||
this.trigger_type === CardTriggerType.HeroDead ||
|
||||
this.trigger_type === CardTriggerType.HeroCall;
|
||||
const used = isEvent ? this.trigger_count : this.current_trigger_times;
|
||||
const total = isEvent ? this.trigger_limit : this.trigger_times;
|
||||
if (isEvent && !isFinite(total)) {
|
||||
// 无上限:显示已触发次数
|
||||
this.info_label.string = `${used}`;
|
||||
} else {
|
||||
const remain = Math.max(0, Math.floor(total) - used);
|
||||
this.info_label.string = `${remain}`;
|
||||
}
|
||||
} else {
|
||||
this.info_label.string = "";
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化或重置 CD 遮罩表现(仅 Interval 类型有冷却进度)
|
||||
if (this.cd_mask && this.cd_mask.isValid) {
|
||||
let sprite = this.cd_mask.getComponent(Sprite);
|
||||
if (sprite) {
|
||||
if (this.trigger_type === CardTriggerType.Interval && this.trigger_interval > 0) {
|
||||
sprite.fillRange = Math.max(0, 1 - (this.timer / this.trigger_interval));
|
||||
} else {
|
||||
sprite.fillRange = 0; // 非冷却类型直接归 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 战斗状态事件 ========================
|
||||
|
||||
/**
|
||||
* 战斗开始回调:
|
||||
* - 仅 Interval 类型在 registerTrigger 中订阅此事件
|
||||
* - 标记进入战斗状态,启动计时器(实际触发由 update 帧驱动)
|
||||
*
|
||||
* 注意:FightStart 触发型(CardTriggerType.FightStart)的事件回调是 onEventTrigger,不是本方法。
|
||||
*/
|
||||
private onFightStart() {
|
||||
if (!this.initialized) return;
|
||||
this.in_combat = true;
|
||||
this.timer = 0; // 重置计时器
|
||||
}
|
||||
|
||||
/** 节点级新一波事件处理 */
|
||||
private onNewWave() {
|
||||
this.handleNewWave();
|
||||
}
|
||||
|
||||
/** 全局级新一波事件处理 */
|
||||
private onNewWaveGlobal() {
|
||||
this.handleNewWave();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新一波:退出战斗状态。
|
||||
* 处理维持波次逻辑:递减剩余波次,或者重置触发次数。
|
||||
*
|
||||
* 各类型在新一波的行为:
|
||||
* - Instant/Interval/FightStart/FightEnd:按 keep_waves 决定维持/销毁,并在新一波开始时重置本地计数
|
||||
* - Field:被动生效,跟随 keep_waves 决定存活
|
||||
* - HeroDead/HeroCall:跨波次触发的事件型,trigger_count(全局)不重置,仅 keep_waves 控制存活
|
||||
*/
|
||||
private handleNewWave() {
|
||||
if (!this.initialized) return;
|
||||
this.in_combat = false;
|
||||
|
||||
// 事件型触发(HeroDead / HeroCall):trigger_count 全局累计,不随波次重置
|
||||
const isGlobalEventType =
|
||||
this.trigger_type === CardTriggerType.HeroDead ||
|
||||
this.trigger_type === CardTriggerType.HeroCall;
|
||||
|
||||
if (this.keep_waves !== 0) {
|
||||
if (this.keep_waves > 0) {
|
||||
this.keep_waves--;
|
||||
if (this.keep_waves <= 0) {
|
||||
this.destroySelf();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 跨波次维持:重置本地计数与计时器(事件型 trigger_count 不重置)
|
||||
if (!isGlobalEventType) {
|
||||
this.current_trigger_times = 0;
|
||||
this.trigger_count = 0;
|
||||
}
|
||||
this.timer = 0;
|
||||
|
||||
// 即时/事件型触发一次(保持旧行为:Instant 在新一波开始立即触发一次)
|
||||
if (this.trigger_type === CardTriggerType.Instant) {
|
||||
this.triggerSkill();
|
||||
this.current_trigger_times++;
|
||||
}
|
||||
this.updateUI();
|
||||
} else {
|
||||
// 不跨波次维持:达到上限即销毁
|
||||
// - Interval / Instant:按 t_times 判定
|
||||
// - 事件型:按 trigger_limit 判定
|
||||
const reachedLimit = isGlobalEventType
|
||||
? this.trigger_count >= this.trigger_limit
|
||||
: this.current_trigger_times >= this.trigger_times;
|
||||
|
||||
if (reachedLimit) {
|
||||
this.destroySelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 任务结束:强制销毁 */
|
||||
private onMissionEnd() {
|
||||
this.destroySelf();
|
||||
}
|
||||
|
||||
// ======================== 帧更新 ========================
|
||||
|
||||
/**
|
||||
* 每帧更新:
|
||||
* - 仅 Interval 类型走帧驱动计时逻辑(其它类型提前 return)
|
||||
* - 累加计时器,达到 trigger_interval 时触发一次技能
|
||||
* - 触发后重置计时器并更新 UI
|
||||
* - 总次数用完后延迟销毁
|
||||
*/
|
||||
update(dt: number) {
|
||||
// 收窄:仅 Interval 类型走帧驱动
|
||||
if (this.trigger_type !== CardTriggerType.Interval) return;
|
||||
if (!this.initialized || !this.in_combat) return;
|
||||
if (!smc.mission.play || smc.mission.pause) return;
|
||||
|
||||
if (this.current_trigger_times < this.trigger_times) {
|
||||
this.timer += dt;
|
||||
|
||||
// 更新 CD 遮罩 (fillRange 从 1 降到 0)
|
||||
if (this.cd_mask && this.cd_mask.isValid && this.trigger_interval > 0) {
|
||||
let sprite = this.cd_mask.getComponent(Sprite);
|
||||
if (sprite) {
|
||||
sprite.fillRange = Math.max(0, 1 - (this.timer / this.trigger_interval));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.timer >= this.trigger_interval) {
|
||||
this.timer = 0;
|
||||
this.triggerSkill();
|
||||
this.current_trigger_times++;
|
||||
this.updateUI();
|
||||
|
||||
// 次数用完且不跨波次维持 → 延迟销毁
|
||||
if (this.keep_waves === 0 && this.current_trigger_times >= this.trigger_times) {
|
||||
this.scheduleOnce(() => this.destroySelf(), 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 技能触发 ========================
|
||||
|
||||
/**
|
||||
* 触发技能效果:
|
||||
* - 计算触发位置(节点局部坐标 + 父节点偏移)。
|
||||
* - 通过 GameEvent.TriggerSkill 事件将技能数据分发给技能系统。
|
||||
*/
|
||||
private triggerSkill() {
|
||||
let targetPos = new Vec3();
|
||||
const localPos = this.node.position;
|
||||
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);
|
||||
|
||||
// 播放技能释放缓动动画:图标放大再缩回
|
||||
if (this.node && this.node.isValid) {
|
||||
// 停止之前的缓动,防止快速重复触发导致缩放异常
|
||||
Tween.stopAllByTarget(this.node);
|
||||
this.node.scale = v3(1, 1, 1);
|
||||
tween(this.node)
|
||||
.to(0.1, { scale: v3(1.3, 1.3, 1.3) })
|
||||
.to(0.15, { scale: v3(1, 1, 1) })
|
||||
.start();
|
||||
}
|
||||
|
||||
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
|
||||
s_uuid: this.s_uuid,
|
||||
isCardSkill: true, // 标记为卡牌技能(区别于英雄自身技能)
|
||||
card_lv: this.card_lv,
|
||||
targetPos: targetPos,
|
||||
overrides: this.overrides
|
||||
});
|
||||
}
|
||||
|
||||
/** ECS 组件移除时销毁节点 */
|
||||
reset() {
|
||||
if (this.node && this.node.isValid) {
|
||||
this.node.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/EquipBoxComp.ts.meta
Normal file
9
assets/script/game/map/EquipBoxComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "f689f41b-0ba2-4d72-ad29-adc5818f7e13",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -4,18 +4,19 @@
|
||||
*
|
||||
* 职责:
|
||||
* 1. 渲染单个装备卡的显示信息(名称、等级、属性词条、图标)。
|
||||
* 2. 处理购买点击 → 扣费 → 派发 UseSkillCard 事件触发装备生效。
|
||||
* 2. 处理购买点击 → 扣费 → 派发 UseEquipCard 事件触发装备生效。
|
||||
*
|
||||
* 装备使用机制:
|
||||
* 与技能卡使用流程一致——购买后派发 GameEvent.UseSkillCard,
|
||||
* 由 MissSkillsComp 接收并创建 SkillBoxComp 实体,
|
||||
* SkillBoxComp 按 Field 类型被动驻场,属性加成由 FieldSkillHelper 全局聚合。
|
||||
* 与技能卡使用流程一致——购买后派发 GameEvent.UseEquipCard,
|
||||
* 由 MissEquipComp 接收并创建 EquipBoxComp 实体,
|
||||
* EquipBoxComp 按 Field 类型被动驻场,属性加成由 FieldSkillHelper 全局聚合。
|
||||
*/
|
||||
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, CardPoolList } from "../common/config/CardSet";
|
||||
import { CardConfig } from "../common/config/CardSet";
|
||||
import { EquipPoolList } from "../common/config/EquipSet";
|
||||
import { FieldSkillSet } from "../common/config/SkillSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
@@ -106,7 +107,7 @@ export class EquipListComp extends CCComp {
|
||||
updateUI() {
|
||||
if (!this.cardData) return;
|
||||
|
||||
const cardConfig = CardPoolList.find(c => c.uuid === this.cardData!.uuid);
|
||||
const cardConfig = EquipPoolList.find(c => c.uuid === this.cardData!.uuid);
|
||||
|
||||
// 名称
|
||||
if (this.name_label) {
|
||||
@@ -191,8 +192,8 @@ export class EquipListComp extends CCComp {
|
||||
return;
|
||||
}
|
||||
|
||||
// 派发 UseSkillCard,装备生效流程与技能卡一致
|
||||
oops.message.dispatchEvent(GameEvent.UseSkillCard, this.cardData);
|
||||
// 派发 UseEquipCard,装备生效流程由 MissEquipComp 接收处理
|
||||
oops.message.dispatchEvent(GameEvent.UseEquipCard, this.cardData);
|
||||
|
||||
// 标记已购买
|
||||
this.isPurchased = true;
|
||||
|
||||
@@ -36,6 +36,7 @@ import { MissionHeroComp } from "./MissionHeroComp";
|
||||
import { MoveComp } from "../hero/MoveComp";
|
||||
import { FacSet, getLvColor } from "../common/config/GameSet";
|
||||
import { CKind, CardPoolList, CardConfig, CardTriggerType } from "../common/config/CardSet";
|
||||
import { EquipPoolList } from "../common/config/EquipSet";
|
||||
import { SkillSet } from "../common/config/SkillSet";
|
||||
import { CardBgComp } from "./CardBgComp";
|
||||
import { MissionEconomy } from "./MissionEconomy";
|
||||
@@ -100,6 +101,8 @@ export class HInfoComp extends CCComp {
|
||||
private previewLv: number = 1;
|
||||
private previewPoolLv: number = 1;
|
||||
private isSkillCard: boolean = false;
|
||||
/** 是否为装备卡预览(决定从 EquipPoolList 查询配置) */
|
||||
private isEquipCard: boolean = false;
|
||||
/** 英雄名字标签缓存引用 */
|
||||
private nameLabel: Label | null = null;
|
||||
/** 技能信息标签缓存引用 */
|
||||
@@ -126,9 +129,9 @@ export class HInfoComp extends CCComp {
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
onAdded(args: { eid?: number; heroUuid?: number; heroLv?: number; poolLv?: number; isSkillCard?: boolean }) {
|
||||
onAdded(args: { eid?: number; heroUuid?: number; heroLv?: number; poolLv?: number; isSkillCard?: boolean, isEquipCard?: boolean }) {
|
||||
if (args?.heroUuid) {
|
||||
this.bindPreviewData(args.heroUuid, args.heroLv ?? 1, args.poolLv ?? 1, args.isSkillCard ?? false);
|
||||
this.bindPreviewData(args.heroUuid, args.heroLv ?? 1, args.poolLv ?? 1, args.isSkillCard ?? false, args.isEquipCard ?? false);
|
||||
return;
|
||||
}
|
||||
const eid = args?.eid ?? 0;
|
||||
@@ -182,7 +185,10 @@ export class HInfoComp extends CCComp {
|
||||
if (this.icon_node) this.icon_node.active = false;
|
||||
if (this.skill_icon_node) this.skill_icon_node.active = true;
|
||||
|
||||
const config = CardPoolList.find(c => c.uuid === heroUuid);
|
||||
// 根据来源从对应卡池查询配置
|
||||
const config = this.isEquipCard
|
||||
? EquipPoolList.find(c => c.uuid === heroUuid)
|
||||
: CardPoolList.find(c => c.uuid === heroUuid);
|
||||
if (!config) return;
|
||||
|
||||
if (this.nameLabel) this.nameLabel.string = config.name ?? "";
|
||||
@@ -285,12 +291,13 @@ export class HInfoComp extends CCComp {
|
||||
* 绑定静态预览数据(卡牌点击查看)。
|
||||
* 通过 HeroInfo/CardPoolList 配置 × 等级计算初始值,不绑定 ECS 实体。
|
||||
*/
|
||||
bindPreviewData(heroUuid: number, heroLv: number, poolLv: number, isSkillCard: boolean = false) {
|
||||
bindPreviewData(heroUuid: number, heroLv: number, poolLv: number, isSkillCard: boolean = false, isEquipCard: boolean = false) {
|
||||
this.isPreview = true;
|
||||
this.previewUuid = heroUuid;
|
||||
this.previewLv = heroLv;
|
||||
this.previewPoolLv = poolLv;
|
||||
this.isSkillCard = isSkillCard;
|
||||
this.isEquipCard = isEquipCard;
|
||||
if (this.sell_node) this.sell_node.active = false;
|
||||
if (this.close_node) this.close_node.active = true;
|
||||
this.cacheLabels();
|
||||
|
||||
191
assets/script/game/map/MissEquipComp.ts
Normal file
191
assets/script/game/map/MissEquipComp.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
/**
|
||||
* @file MissEquipComp.ts
|
||||
* @description 场上装备卡槽位管理器组件(UI 视图层)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 管理场上已使用的 **装备卡** 的可视化槽位(最多 10 个)。
|
||||
* 2. 监听 UseEquipCard 事件,当玩家使用装备卡时实例化 EquipBoxComp 并放入空闲槽位。
|
||||
* 3. 监听 RemoveEquipBox 事件,当装备生效完毕后回收槽位并重新排列。
|
||||
*
|
||||
* 关键设计:
|
||||
* - slots 数组预定义了 10 个固定坐标位(2 行 × 5 列),s
|
||||
* 每个槽位记录是否占用及对应节点引用。
|
||||
* - 当某个 EquipBox 销毁时,触发 rearrangeSlots 将剩余节点
|
||||
* 紧凑地重排到前置槽位,避免视觉空洞。
|
||||
* - EquipBox 的实例化使用 equip_box Prefab,在编辑器中绑定。
|
||||
*
|
||||
* 依赖:
|
||||
* - EquipBoxComp(EquipBoxComp.ts)—— 单个装备卡的效果控制组件
|
||||
* - GameEvent.UseEquipCard —— 装备卡使用事件
|
||||
* - GameEvent.RemoveEquipBox —— 装备卡移除事件
|
||||
* - smc.map.MapView.scene.entityLayer —— 装备节点的父容器(EQUIP 节点)
|
||||
*/
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { _decorator, Node, Prefab, instantiate, Vec3 } 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 { EBox } from "./EBox";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** 技能槽位数据结构 */
|
||||
interface SkillBoxSlot {
|
||||
/** 该槽位的固定 X 坐标 */
|
||||
x: number;
|
||||
/** 该槽位的固定 Y 坐标 */
|
||||
y: number;
|
||||
/** 是否已被占用 */
|
||||
used: boolean;
|
||||
/** 占用该槽位的节点引用 */
|
||||
node: Node | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* MissEquipComp —— 场上装备卡槽位管理器
|
||||
*
|
||||
* 在战斗场景中管理已激活的装备卡显示位置。
|
||||
* 2 行 × 5 列 = 10 个槽位,不足时提示已满。
|
||||
*/
|
||||
@ccclass('MissEquipComp')
|
||||
@ecs.register('MissEquipComp', false)
|
||||
export class MissEquipComp extends CCComp {
|
||||
/** 调试日志开关 */
|
||||
private debugMode: boolean = true;
|
||||
|
||||
/** 技能卡 Prefab(在编辑器中赋值) */
|
||||
@property({ type: Prefab })
|
||||
private equip_box: Prefab = null;
|
||||
/**
|
||||
* 预定义的 10 个槽位坐标(2 行 × 5 列):
|
||||
* 第 1 行 y=240:x = -320, -240, -160, -80, 0
|
||||
* 第 2 行 y=320:x = -320, -240, -160, -80, 0
|
||||
*/
|
||||
private slots: SkillBoxSlot[] = [
|
||||
{ x: -310, y: 300, used: false, node: null },
|
||||
{ x: -220, y: 300, used: false, node: null },
|
||||
{ x: -130, y: 300, used: false, node: null },
|
||||
{ x: -40, y: 300, used: false, node: null },
|
||||
{ x: 50, y: 300, used: false, node: null },
|
||||
{ x: -310, y: 400, used: false, node: null },
|
||||
{ x: -220, y: 400, used: false, node: null },
|
||||
{ x: -130, y: 400, used: false, node: null },
|
||||
{ x: -40, y: 400, used: false, node: null },
|
||||
{ x: 50, y: 400, used: false, node: null },
|
||||
];
|
||||
|
||||
/** 注册事件监听 */
|
||||
onLoad() {
|
||||
oops.message.on(GameEvent.UseEquipCard, this.onUseEquipCard, this);
|
||||
oops.message.on(GameEvent.RemoveEquipBox, this.onRemoveEquipBox, this);
|
||||
}
|
||||
|
||||
/** 移除事件监听 */
|
||||
onDestroy() {
|
||||
super.onDestroy();
|
||||
oops.message.off(GameEvent.UseEquipCard, this.onUseEquipCard, this);
|
||||
oops.message.off(GameEvent.RemoveEquipBox, this.onRemoveEquipBox, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理装备卡移除事件:
|
||||
* 1. 在 slots 中找到对应节点并释放。
|
||||
* 2. 调用 rearrangeSlots 紧凑重排。
|
||||
*
|
||||
* @param event 事件名
|
||||
* @param args 要移除的节点引用
|
||||
*/
|
||||
private onRemoveEquipBox(event: string, args: any) {
|
||||
const node = args as Node;
|
||||
let removed = false;
|
||||
for (let i = 0; i < this.slots.length; i++) {
|
||||
if (this.slots[i].node === node) {
|
||||
this.slots[i].used = false;
|
||||
this.slots[i].node = null;
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
this.rearrangeSlots();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 紧凑重排:将所有有效节点按顺序移到前置槽位。
|
||||
* 确保视觉上不会出现中间空洞。
|
||||
*/
|
||||
private rearrangeSlots() {
|
||||
// 收集所有有效节点
|
||||
const validNodes: Node[] = [];
|
||||
for (let i = 0; i < this.slots.length; i++) {
|
||||
if (this.slots[i].used && this.slots[i].node && this.slots[i].node.isValid) {
|
||||
validNodes.push(this.slots[i].node);
|
||||
}
|
||||
this.slots[i].used = false;
|
||||
this.slots[i].node = null;
|
||||
}
|
||||
// 按顺序重新分配
|
||||
for (let i = 0; i < validNodes.length; i++) {
|
||||
if (i < this.slots.length) {
|
||||
this.slots[i].used = true;
|
||||
this.slots[i].node = validNodes[i];
|
||||
validNodes[i].setPosition(new Vec3(this.slots[i].x, this.slots[i].y, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理使用装备卡事件:提取 uuid 和 card_lv 后调用 addEquip。
|
||||
* @param event 事件名
|
||||
* @param args 卡牌数据(含 uuid、card_lv)
|
||||
*/
|
||||
private onUseEquipCard(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;
|
||||
this.addSkill(uuid, card_lv);
|
||||
}
|
||||
|
||||
start() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 在场上添加一个装备卡:
|
||||
* 1. 在 slots 中查找空闲位。
|
||||
* 2. 实例化 equip_box Prefab 并放置在空闲位坐标。
|
||||
* 3. 获取或添加 EquipBoxComp 并初始化。
|
||||
*
|
||||
* @param uuid 装备 UUID
|
||||
* @param card_lv 装备卡等级
|
||||
*/
|
||||
addSkill(uuid: number, card_lv: number) {
|
||||
if (!this.equip_box) {
|
||||
mLogger.error(this.debugMode, "MissEquipComp", "equip_box prefab not set");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找空闲槽位
|
||||
const emptyIndex = this.slots.findIndex(slot => !slot.used);
|
||||
if (emptyIndex === -1) {
|
||||
mLogger.warn(this.debugMode, "MissEquipComp", "equip_box slots are full");
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用 ECS 实体创建装备节点
|
||||
let ebox = ecs.getEntity<EBox>(EBox);
|
||||
let pos = new Vec3(this.slots[emptyIndex].x, this.slots[emptyIndex].y, 0);
|
||||
let node = ebox.load(uuid, card_lv, pos, this.equip_box);
|
||||
|
||||
this.slots[emptyIndex].used = true;
|
||||
this.slots[emptyIndex].node = node;
|
||||
}
|
||||
|
||||
/** ECS 组件移除时销毁节点 */
|
||||
reset() {
|
||||
this.node.destroy();
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/MissEquipComp.ts.meta
Normal file
9
assets/script/game/map/MissEquipComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "da70c59d-7481-405a-b029-f32d19ded3fa",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -39,7 +39,8 @@ import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, Tween, twe
|
||||
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 { CardConfig, CardPoolList, CardType, drawCardsByRule, isEquipmentCard, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet";
|
||||
import { CardConfig, CardPoolList, CardType, drawCardsByRule, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet";
|
||||
import { EquipPoolList } from "../common/config/EquipSet";
|
||||
import { CardComp } from "./CardComp";
|
||||
import { SCardComp } from "./SCardComp";
|
||||
import { EquipListComp } from "./EquipListComp";
|
||||
@@ -319,6 +320,8 @@ export class MissionCardComp extends CCComp {
|
||||
oops.message.on(GameEvent.HeroSell, this.onHeroSell, this);
|
||||
oops.message.on(GameEvent.UseHeroCard, this.onUseHeroCard, this);
|
||||
oops.message.on(GameEvent.UseSkillCard, this.onUseSkillCard, this);
|
||||
// 监听装备购买事件,追踪已购记录
|
||||
oops.message.on(GameEvent.UseEquipCard, this.onUseEquipCard, 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);
|
||||
@@ -475,12 +478,6 @@ export class MissionCardComp extends CCComp {
|
||||
// 由 onCardUsed 立即向被使用的槽位补充一张新技能卡。
|
||||
// 弹窗由外部(按钮/阶段切换)控制显隐。
|
||||
|
||||
// 追踪装备购买记录(EquipListComp 购买后也会派发此事件)
|
||||
const usedCard = args as CardConfig;
|
||||
if (usedCard && isEquipmentCard(usedCard)) {
|
||||
this.purchasedEquipUuids.add(usedCard.uuid);
|
||||
}
|
||||
|
||||
// 修复:独立判断 guide2 的关闭 和 guide3 的开启
|
||||
if (!smc.finish_guides.includes(2)) {
|
||||
smc.finish_guides.push(2);
|
||||
@@ -495,6 +492,14 @@ export class MissionCardComp extends CCComp {
|
||||
this.scheduleOnce(() => this.updateCoinAndCostUI(), 0);
|
||||
}
|
||||
|
||||
/** 装备购买事件回调:记录已购 UUID,防止重复购买 */
|
||||
private onUseEquipCard(event: string, args: any) {
|
||||
const usedCard = args as CardConfig;
|
||||
if (usedCard) {
|
||||
this.purchasedEquipUuids.add(usedCard.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
/** 解除按钮监听,避免节点销毁后回调泄漏 */
|
||||
private unbindEvents() {
|
||||
oops.message.off(GameEvent.CoinAdd, this.onCoinAdd, this);
|
||||
@@ -503,6 +508,7 @@ export class MissionCardComp extends CCComp {
|
||||
oops.message.off(GameEvent.HeroSell, this.onHeroSell, this);
|
||||
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.UseSpecialCard, this.onUseSpecialCard, this);
|
||||
oops.message.off(GameEvent.ShowSmallTip, this.onShowSmallTip, this);
|
||||
oops.message.off(GameEvent.CardUsed, this.onCardUsed, this);
|
||||
@@ -744,10 +750,8 @@ export class MissionCardComp extends CCComp {
|
||||
// 清空旧列表
|
||||
this.equipsBoxNode.removeAllChildren();
|
||||
|
||||
// 获取所有装备卡
|
||||
const equipments = CardPoolList.filter(c => isEquipmentCard(c));
|
||||
|
||||
for (const equip of equipments) {
|
||||
// 从独立装备卡池获取所有装备
|
||||
for (const equip of EquipPoolList) {
|
||||
const node = instantiate(this.equipPrefab);
|
||||
this.equipsBoxNode.addChild(node);
|
||||
const comp = node.getComponent(EquipListComp) || node.addComponent(EquipListComp);
|
||||
@@ -765,7 +769,7 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "populate equipments", {
|
||||
count: equipments.length
|
||||
count: EquipPoolList.length
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1082,13 +1086,11 @@ export class MissionCardComp extends CCComp {
|
||||
private buildSkillDrawCards(): CardConfig[] {
|
||||
const currentWave = this.getCurrentWave();
|
||||
// 使用明确规则的 drawCardsByRule,指定只要 3 张技能卡,并且过滤对应 wave
|
||||
// 装备卡已改为商店直购,不再参与抽卡
|
||||
const cards = drawCardsByRule(1, {
|
||||
count: 3,
|
||||
type: CardType.Skill,
|
||||
wave: currentWave,
|
||||
unique: true, // 保证技能牌不重复
|
||||
excludeEquipment: true, // 排除装备卡
|
||||
});
|
||||
|
||||
if (cards.length >= 3) return cards.slice(0, 3);
|
||||
@@ -1099,7 +1101,6 @@ export class MissionCardComp extends CCComp {
|
||||
type: CardType.Skill,
|
||||
wave: currentWave,
|
||||
unique: true,
|
||||
excludeEquipment: true,
|
||||
});
|
||||
if (fallback.length === 0) break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user