diff --git a/assets/script/game/common/config/CardSet.ts b/assets/script/game/common/config/CardSet.ts index 9f0e8173..eea05e21 100644 --- a/assets/script/game/common/config/CardSet.ts +++ b/assets/script/game/common/config/CardSet.ts @@ -47,28 +47,32 @@ export enum CardSkillType { HeroCall = 6, // 场上己方英雄召唤上场时触发 } -/** 卡池等级定义 */ -export enum CardLV { - LV1 = 1, - LV2 = 2, - LV3 = 3, - LV4 = 4, - LV5 = 5, -} - /** * 卡牌技能触发类型 * - 命名对齐英雄侧 SkillTriggerType,便于跨模块认知统一 * - 枚举值从 1 开始,避免 0 的 falsy 坑(if (trigger_type) 判断出错) */ export enum CardTriggerType { - Instant = 1, // 即时触发:使用后立即生效一次 - Interval = 2, // 定时循环:战斗中按 t_inv 间隔重复触发 - Field = 3, // 驻场光环:被动生效(仅显式分类,仍由 field 字段驱动) + Instant = 1, // 即时触发:使用后立即生效一次 + Interval = 2, // 定时循环:战斗中按 t_inv 间隔重复触发 + Field = 3, // 驻场光环:被动生效(仅显式分类,仍由 field 字段驱动) FightStart = 4, // 战斗开始时触发 - FightEnd = 5, // 战斗结束时触发(每波结束) - HeroDead = 6, // 场上己方英雄死亡时触发 - HeroCall = 7, // 英雄上场时触发(主角召唤 + 技能召唤 + 复活) + FightEnd = 5, // 战斗结束时触发(每波结束) + HeroDead = 6, // 场上己方英雄死亡时触发 + HeroCall = 7, // 英雄上场时触发(主角召唤 + 技能召唤 + 复活) +} + +/** + * 卡池等级占位枚举(已废弃分层语义)。 + * 新机制下所有英雄卡均属 LV1,不再使用卡池升级。 + * 保留枚举仅为兼容历史代码引用。 + */ +export enum CardLV { + LV1 = 1, + LV2 = 2, + LV3 = 3, + LV4 = 4, + LV5 = 5, } /** 通用卡牌配置 */ @@ -104,7 +108,16 @@ export interface CardConfig { * 注意:与 t_times 语义不同——t_times 控制每波内 Interval 的次数 */ trigger_limit?: number; + + /** + * 动态升级卡专属:目标英雄 UUID。 + * 仅当 type === CardType.SpecialUpgrade 且该卡由 buildDrawCards 动态生成时使用。 + * 使用该卡时精确升级场上对应 UUID 的英雄。 + */ + target_hero_uuid?: number; } + +/** 升级卡折扣表(已废弃,保留以兼容历史引用) */ export const CardsUpSet: Record = { 1: 50, 2: 100, @@ -113,70 +126,43 @@ export const CardsUpSet: Record = { 5: 250, } -/** 卡池升级每波减免金额 */ +/** 卡池升级每波减免金额(已废弃,保留以兼容历史引用) */ export const CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE = 10 -/** 卡池默认初始等级 */ +/** 卡池默认初始等级(已废弃,所有卡牌统一 LV1) */ export const CARD_POOL_INIT_LEVEL = CardLV.LV1 -/** 卡池等级上限(统一由 FightSet.MAX_CARD_POOL_LEVEL 设定,保持单一数据源) */ -export const CARD_POOL_MAX_LEVEL = FightSet.MAX_CARD_POOL_LEVEL as unknown as CardLV -/** 英雄最高等级限制 */ +/** 卡池等级上限(已废弃,所有卡牌统一 LV1) */ +export const CARD_POOL_MAX_LEVEL = CardLV.LV1 +/** 英雄最高等级限制(已废弃,统一由 FightSet.HERO_MAX_LV 控制) */ export const CARD_HERO_MAX_LEVEL = 1 -/** 基础卡池(英雄、技能、功能) */ +/** 基础卡池(英雄、技能、功能) */ export const CardPoolList: CardConfig[] = []; -// 动态生成英雄卡池 +// 动态生成英雄卡池:所有英雄统一生成 lv1 卡牌 HeroList.forEach(uuid => { const hero = HeroInfo[uuid]; if (!hero) return; - const basePoolLv = hero.pool_lv || 1; - const baseHeroLv = hero.lv || 1; const baseCost = FightSet.BASE_COST; const baseWeight = 25; - // 生成从 basePoolLv 到 CARD_POOL_MAX_LEVEL 的卡牌 - for (let pLv = basePoolLv; pLv <= CARD_POOL_MAX_LEVEL; pLv++) { - const offset = pLv - basePoolLv; - const targetHeroLv = baseHeroLv + offset; - - // 【修改开始】永远只刷 lv1 等级的英雄卡牌,不再出现某英雄的 lv2 等级卡牌 - // 设置为 true 则开启该限制。保留原有代码逻辑以便后续有变直接引用。 - const ONLY_SPAWN_LV1_HERO = true; - if (ONLY_SPAWN_LV1_HERO && targetHeroLv > 1) { - break; - } - // 【修改结束】 - - // 英雄的最高等级 是MERGE_MAX-1 - if (targetHeroLv > FightSet.MERGE_MAX - 1) { - break; - } - - // cost = baseCost * 3^(lv-1): Lv1=5, Lv2=15, Lv3=45 - let cost = baseCost; - if (targetHeroLv > 1) { - cost = baseCost * Math.pow(FightSet.MERGE_NEED, targetHeroLv - 1); - } - - CardPoolList.push({ - uuid: hero.uuid, - type: CardType.Hero, - cost: cost, - weight: baseWeight, - pool_lv: pLv as CardLV, - kind: CKind.Hero, - hero_lv: targetHeroLv, - base_pool_lv: basePoolLv - }); - } + CardPoolList.push({ + uuid: hero.uuid, + type: CardType.Hero, + cost: baseCost, + weight: baseWeight, + pool_lv: CardLV.LV1, + kind: CKind.Hero, + hero_lv: 1, + base_pool_lv: 1, + }); }); // 添加非英雄卡牌 (技能、功能卡) // 体系:wave 由 SKILL_CARD_WAVES 统一配置,每档强度递增(Field 靠 field uuid 区分数值,Interval 靠 overrides 覆写) -// wave→pool_lv 映射由 SKILL_CARD_WAVES 索引+1 自动生成(wave 1→lv1, wave 5→lv2, wave 8→lv3) -const waveToPoolLv: Record = {}; -SKILL_CARD_WAVES.forEach((w, i) => { waveToPoolLv[w] = i + 1; }); +// wave→card_lv 映射由 SKILL_CARD_WAVES 索引+1 自动生成(wave 1→lv1, wave 5→lv2, wave 8→lv3) +const waveToCardLv: Record = {}; +SKILL_CARD_WAVES.forEach((w, i) => { waveToCardLv[w] = i + 1; }); const SkillCardData: any[] = [ // ==================== wave 1 档(基础强度) ==================== @@ -214,7 +200,7 @@ 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 } }, - + { uuid: 8760, skill: 0, wave: SKILL_CARD_WAVES[1], name: "金币收益+", info: "每回合金币收益+2", is_inst: false, keep_waves: -1, field: [7210], trigger_type: CardTriggerType.Field }, { uuid: 8761, skill: 0, wave: SKILL_CARD_WAVES[1], name: "购买优惠+", info: "购买卡牌费用-2金币", is_inst: false, keep_waves: -1, field: [7212], trigger_type: CardTriggerType.Field }, { uuid: 8762, skill: 0, wave: SKILL_CARD_WAVES[1], name: "刷新优惠", info: "刷新卡牌费用-1金币", is_inst: false, keep_waves: -1, field: [7213], trigger_type: CardTriggerType.Field }, @@ -239,7 +225,7 @@ const SkillCardData: any[] = [ { 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 } }, - + { uuid: 8810, skill: 0, wave: SKILL_CARD_WAVES[2], name: "金币收益++", info: "每回合金币收益+3", is_inst: false, keep_waves: -1, field: [7410], trigger_type: CardTriggerType.Field }, { uuid: 8811, skill: 0, wave: SKILL_CARD_WAVES[2], name: "召唤强化++", info: "召唤触发技能次数+1", is_inst: false, keep_waves: -1, field: [7014], trigger_type: CardTriggerType.Field }, @@ -252,27 +238,28 @@ const SkillCardData: any[] = [ ]; SkillCardData.forEach(data => { + const cardLv = waveToCardLv[data.wave] ?? 1; CardPoolList.push({ uuid: data.uuid, skill: data.skill || undefined, type: CardType.Skill, cost: 0, weight: 10, - pool_lv: waveToPoolLv[data.wave] as CardLV, + pool_lv: CardLV.LV1, wave: data.wave, kind: CKind.Skill, - card_lv: waveToPoolLv[data.wave], // wave 1→1, 5→2, 8→3 + card_lv: cardLv, name: data.name, info: data.info, - icon: data.icon, // 【新增】透传自定义图标ID(优先级最高) + icon: data.icon, is_inst: data.is_inst, t_times: data.t_times || (data.is_inst ? 1 : 999), t_inv: data.t_inv || 0, keep_waves: data.keep_waves, field: data.field, - overrides: data.overrides, // 【修复】原遗漏 - trigger_type: data.trigger_type, // 【新增】显式触发类型 - trigger_limit: data.trigger_limit, // 【新增】事件型触发次数上限 + overrides: data.overrides, + trigger_type: data.trigger_type, + trigger_limit: data.trigger_limit, }); }); @@ -283,11 +270,13 @@ export enum SpecialRefreshHeroType { Ranged = 2, } -/** 升级功能卡完整配置 */ +/** 升级功能卡完整配置(作为动态升级卡的基础模板) */ export interface SpecialUpgradeCardConfig extends CardConfig { name: string info: string + /** 模板字段:currentLv=0 表示动态卡(实际值由 target_hero_uuid 对应的英雄决定) */ currentLv: number + /** 模板字段:targetLv=0 表示"+1 级"(实际目标等级 = 当前等级 + 1) */ targetLv: number } @@ -299,18 +288,16 @@ export interface SpecialRefreshCardConfig extends CardConfig { refreshHeroType: SpecialRefreshHeroType } -/** 功能卡定义表 */ - - - +/** + * 升级卡模板。 + * 仅有 7001 一条记录,作为动态升级卡的基础配置(cost/weight/name/info)。 + * 实际使用时由 MissionCardComp.buildDrawCards 根据场上英雄动态生成 CardConfig, + * 通过 target_hero_uuid 字段绑定具体英雄。 + */ export const SpecialUpgradeCardList: Record = { 7001: { - uuid: 7001, type: CardType.SpecialUpgrade, cost: 10, weight: 16, pool_lv: CardLV.LV1, kind: CKind.Card, name: t("scard_name_7001"), info: t("scard_info_7001"), - currentLv: 1, targetLv: 2, - }, - 7002: { - uuid: 7002, type: CardType.SpecialUpgrade, cost: 28, weight: 14, pool_lv: CardLV.LV2, kind: CKind.Card, name: t("scard_name_7002"), info: t("scard_info_7002"), - currentLv: 2, targetLv: 3, + uuid: 7001, type: CardType.SpecialUpgrade, cost: 10, weight: 18, pool_lv: CardLV.LV1, kind: CKind.Card, name: t("scard_name_7001"), info: t("scard_info_7001"), + currentLv: 0, targetLv: 0, }, } @@ -324,19 +311,15 @@ export const SpecialRefreshCardList: Record = refreshLv: 0, refreshHeroType: SpecialRefreshHeroType.Ranged, }, 7103: { - uuid: 7103, type: CardType.SpecialRefresh, cost: 4, weight: 12, pool_lv: CardLV.LV2, kind: CKind.Card, name: t("scard_name_7103"), info: t("scard_info_7103"), - refreshLv: 3, refreshHeroType: SpecialRefreshHeroType.Any, + uuid: 7103, type: CardType.SpecialRefresh, cost: 4, weight: 12, pool_lv: CardLV.LV1, kind: CKind.Card, name: t("scard_name_7103"), info: t("scard_info_7103"), + refreshLv: 0, refreshHeroType: SpecialRefreshHeroType.Any, }, } - -/** 规范等级到合法区间 [LV1, LV6] */ +/** 规范等级到合法区间(保留以兼容历史调用,新机制下统一返回 LV1) */ const clampCardLv = (lv: number): CardLV => { - const value = Math.floor(lv) - if (value < CARD_POOL_INIT_LEVEL) return CARD_POOL_INIT_LEVEL - if (value > CARD_POOL_MAX_LEVEL) return CARD_POOL_MAX_LEVEL - return value as CardLV + return CardLV.LV1; } /** 单次按权重抽取一张卡 */ @@ -368,13 +351,13 @@ const pickCards = (cards: CardConfig[], count: number, unique: boolean = false): return selected } -/** 获取指定等级可出现的基础卡池 */ +/** + * 获取基础卡池(已废弃 lv 参数,新机制下返回完整卡池)。 + * @param lv 历史遗留参数,不再生效 + * @param onlyCurrentLv 历史遗留参数,不再生效 + */ export const getCardPoolByLv = (lv: number, onlyCurrentLv: boolean = false): CardConfig[] => { - const cardLv = clampCardLv(lv) - if (onlyCurrentLv) { - return CardPoolList.filter(card => card.pool_lv === cardLv) - } - return CardPoolList.filter(card => card.pool_lv <= cardLv) + return CardPoolList; } const normalizeTypeFilter = (type: CardType | CardType[]): Set => { @@ -382,7 +365,12 @@ const normalizeTypeFilter = (type: CardType | CardType[]): Set => { return new Set(list) } -/** 常规发牌:前 3 英雄 + 后 1 其他;支持按类型和等级模式过滤 */ +/** + * 常规发牌:前 3 英雄 + 后 1 其他;支持按类型过滤。 + * @param lv 历史遗留参数,不再生效 + * @param type 限定卡牌类型 + * @param onlyCurrentLv 历史遗留参数,不再生效 + */ export const getCardsByLv = ( lv: number, type?: CardType | CardType[], @@ -401,6 +389,9 @@ export const getCardsByLv = ( return [...heroes, ...others] } +/** + * 通用按规则抽卡(已废弃 lv/targetPoolLv/onlyCurrentLv 参数,仅为兼容保留)。 + */ export const drawCardsByRule = ( lv: number, options: { @@ -415,30 +406,11 @@ export const drawCardsByRule = ( } = {} ): CardConfig[] => { const count = Math.max(0, Math.floor(options.count ?? 4)) - const onlyCurrentLv = options.onlyCurrentLv ?? false - let pool = getCardPoolByLv(lv, onlyCurrentLv) + let pool = getCardPoolByLv(lv, options.onlyCurrentLv ?? false) if (options.type !== undefined) { const typeSet = normalizeTypeFilter(options.type) pool = pool.filter(card => typeSet.has(card.type)) } - if (options.targetPoolLv !== undefined) { - // 如果指定了目标卡池等级,则强制从所有配置中筛选该等级的卡牌,无视当前的卡池等级限制 - pool = CardPoolList.filter(card => card.pool_lv === options.targetPoolLv) - if (options.type !== undefined) { - const typeSet = normalizeTypeFilter(options.type) - pool = pool.filter(card => typeSet.has(card.type)) - } - - // 如果强制筛选后池子为空(比如开启了 ONLY_SPAWN_LV1_HERO 导致没有高等级英雄卡), - // 且需要抽取英雄,则兜底降级回 pool_lv 为 1 的卡池,保证系统不会卡死 - if (pool.length === 0) { - pool = CardPoolList.filter(card => card.pool_lv === 1); - if (options.type !== undefined) { - const typeSet = normalizeTypeFilter(options.type) - pool = pool.filter(card => typeSet.has(card.type)) - } - } - } if (options.heroType !== undefined || options.heroLv !== undefined) { pool = pool.filter(card => { if (card.type !== CardType.Hero) return false @@ -454,7 +426,6 @@ export const drawCardsByRule = ( if (options.wave !== undefined) { pool = pool.filter(card => { if (card.type === CardType.Skill) { - // 只有 wave 值严格等于当前 wave 的技能卡才会留在池中 return card.wave === options.wave; } return true; diff --git a/assets/script/game/common/config/GameSet.ts b/assets/script/game/common/config/GameSet.ts index a088e49c..fd1e34a0 100644 --- a/assets/script/game/common/config/GameSet.ts +++ b/assets/script/game/common/config/GameSet.ts @@ -27,9 +27,11 @@ export enum FightSet { CRIT_DAMAGE = 50,//暴击伤害 MORE_RC = 10,//更多次数 广告获取的次数 HEARTPOS = -320,//基地位置 - HERO_MAX_NUM = 6,//英雄最大数量 - MERGE_MAX = 2, //英雄最大等级 - MERGE_NEED = 3, //英雄升级需要的英雄数 + HERO_MAX_NUM = 3,//英雄最大数量 + /** 英雄可升级到的最高等级(通过升级卡提升) */ + HERO_MAX_LV = 6, + /** 英雄属性随等级成长的倍率(ap/hp = base * MULTIPLIER^(lv-1)) */ + HERO_LV_MULTIPLIER = 3, // BACK_RANG=30,//后退范围 BACK_RANG = 30,//后退范围 FiIGHT_TIME = 30,//战斗时间 @@ -45,20 +47,8 @@ export enum FightSet { REFRESH_COST = 2, BASE_COST = 5, INIT_COIN = 7, // 初始金币数 - // 刷新成本 - /** 卡池等级上限(对应 CardLV 最大值) */ - MAX_CARD_POOL_LEVEL = 5, } -/** - * 卡池升级波次配置(单一数据源)。 - * 索引 i 对应目标等级 = i + 2,即: - * - 第 1 个波次 → 升至 LV2 - * - 第 2 个波次 → 升至 LV3 - * - 依此类推,上限为 FightSet.MAX_CARD_POOL_LEVEL - */ -export const CARD_POOL_UPGRADE_WAVES: number[] = [4, 7, 10, 13]; - /** * 技能卡牌出现的波次配置(单一数据源)。 * 数组索引 i 对应卡牌档位 card_lv = i + 1: diff --git a/assets/script/game/common/config/heroSet.ts b/assets/script/game/common/config/heroSet.ts index f6387d1c..977942de 100644 --- a/assets/script/game/common/config/heroSet.ts +++ b/assets/script/game/common/config/heroSet.ts @@ -3,37 +3,37 @@ import { BoxSet, FacSet } from "./GameSet" import { SkillOverrides, TGroup } from "./SkillSet" export enum HType { - Melee = 0, - Mid = 1, - Long = 2, + Melee = 0, + Mid = 1, + Long = 2, } -export const HTypeName ={ - 0:"近战", - 1:"中程", - 2:"远程", +export const HTypeName = { + 0: "近战", + 1: "中程", + 2: "远程", } export enum MonType { - Melee = 0, - Heavy = 1, - Long = 2, - Support = 3, - Summoner = 5, - Assassin = 6, - MeleeBoss = 8, - LongBoss = 9, + Melee = 0, + Heavy = 1, + Long = 2, + Support = 3, + Summoner = 5, + Assassin = 6, + MeleeBoss = 8, + LongBoss = 9, } export const MonTypeName: Record = { - [MonType.Melee]: "近战", - [MonType.Heavy]: "重型", - [MonType.Long]: "远程", - [MonType.Support]: "辅助", - [MonType.Summoner]: "召唤师", - [MonType.Assassin]: "刺客", + [MonType.Melee]: "近战", + [MonType.Heavy]: "重型", + [MonType.Long]: "远程", + [MonType.Support]: "辅助", + [MonType.Summoner]: "召唤师", + [MonType.Assassin]: "刺客", [MonType.MeleeBoss]: "近战Boss", - [MonType.LongBoss]: "远程Boss", + [MonType.LongBoss]: "远程Boss", } /** @@ -43,38 +43,38 @@ export const MonTypeName: Record = { */ export enum AtkSpeedLv { VeryFast1 = 1, VeryFast2 = 2, VeryFast3 = 3, - Fast1 = 4, Fast2 = 5, Fast3 = 6, - Normal1 = 7, Normal2 = 8, Normal3 = 9, - Mid1 = 10, Mid2 = 11, Mid3 = 12, - Slow1 = 13, Slow2 = 14, Slow3 = 15, - VerySlow1 = 16,VerySlow2 = 17,VerySlow3 = 18, + Fast1 = 4, Fast2 = 5, Fast3 = 6, + Normal1 = 7, Normal2 = 8, Normal3 = 9, + Mid1 = 10, Mid2 = 11, Mid3 = 12, + Slow1 = 13, Slow2 = 14, Slow3 = 15, + VerySlow1 = 16, VerySlow2 = 17, VerySlow3 = 18, } export const AtkSpeedSet = { - [AtkSpeedLv.VeryFast1]: { name: "极速++", cd: 0.15 }, - [AtkSpeedLv.VeryFast2]: { name: "极速+", cd: 0.20 }, + [AtkSpeedLv.VeryFast1]: { name: "极速++", cd: 0.15 }, + [AtkSpeedLv.VeryFast2]: { name: "极速+", cd: 0.20 }, [AtkSpeedLv.VeryFast3]: { name: "极速", cd: 0.30 }, - [AtkSpeedLv.Fast1]: { name: "快速++", cd: 0.40 }, - [AtkSpeedLv.Fast2]: { name: "快速+", cd: 0.50 }, - [AtkSpeedLv.Fast3]: { name: "快速", cd: 0.70 }, - [AtkSpeedLv.Normal1]: { name: "中速++", cd: 0.80 }, - [AtkSpeedLv.Normal2]: { name: "中速+", cd: 0.90 }, - [AtkSpeedLv.Normal3]: { name: "中速", cd: 1.00 }, - [AtkSpeedLv.Mid1]: { name: "一般+", cd: 1.10 }, - [AtkSpeedLv.Mid2]: { name: "一般", cd: 1.20 }, - [AtkSpeedLv.Mid3]: { name: "一般-", cd: 1.30 }, - [AtkSpeedLv.Slow1]: { name: "慢", cd: 1.50 }, - [AtkSpeedLv.Slow2]: { name: "慢+", cd: 1.60 }, - [AtkSpeedLv.Slow3]: { name: "慢++", cd: 1.80 }, - [AtkSpeedLv.VerySlow1]: { name: "很慢", cd: 2.30 }, - [AtkSpeedLv.VerySlow2]: { name: "很慢+", cd: 2.50 }, + [AtkSpeedLv.Fast1]: { name: "快速++", cd: 0.40 }, + [AtkSpeedLv.Fast2]: { name: "快速+", cd: 0.50 }, + [AtkSpeedLv.Fast3]: { name: "快速", cd: 0.70 }, + [AtkSpeedLv.Normal1]: { name: "中速++", cd: 0.80 }, + [AtkSpeedLv.Normal2]: { name: "中速+", cd: 0.90 }, + [AtkSpeedLv.Normal3]: { name: "中速", cd: 1.00 }, + [AtkSpeedLv.Mid1]: { name: "一般+", cd: 1.10 }, + [AtkSpeedLv.Mid2]: { name: "一般", cd: 1.20 }, + [AtkSpeedLv.Mid3]: { name: "一般-", cd: 1.30 }, + [AtkSpeedLv.Slow1]: { name: "慢", cd: 1.50 }, + [AtkSpeedLv.Slow2]: { name: "慢+", cd: 1.60 }, + [AtkSpeedLv.Slow3]: { name: "慢++", cd: 1.80 }, + [AtkSpeedLv.VerySlow1]: { name: "很慢", cd: 2.30 }, + [AtkSpeedLv.VerySlow2]: { name: "很慢+", cd: 2.50 }, [AtkSpeedLv.VerySlow3]: { name: "很慢++", cd: 2.80 }, }; -export const HeroPos={ - 0:{pos:v3(-320,BoxSet.GAME_LINE,0)}, - 1:{pos:v3(0,BoxSet.GAME_LINE,0)}, - 2:{pos:v3(0,BoxSet.GAME_LINE,0)}, +export const HeroPos = { + 0: { pos: v3(-320, BoxSet.GAME_LINE, 0) }, + 1: { pos: v3(0, BoxSet.GAME_LINE, 0) }, + 2: { pos: v3(0, BoxSet.GAME_LINE, 0) }, } export const FormationPointX = { @@ -97,12 +97,12 @@ export const resolveFormationTargetX = (fac: FacSet, type: HType): number => { export enum MonStart { - SLINE_1=140, //上线y - SLINE_2=100, //下线y - SLINE_3=180, //下线y - SLINE_4=60, //y起始点 - START_X=320, //x起始点 - START_I=90, //x轴间隔 + SLINE_1 = 140, //上线y + SLINE_2 = 100, //下线y + SLINE_3 = 180, //下线y + SLINE_4 = 60, //y起始点 + START_X = 320, //x起始点 + START_I = 90, //x轴间隔 } @@ -147,41 +147,41 @@ export const SkillTriggerDesc = { * 英雄/怪物基础信息接口 */ export interface heroInfo { - uuid: number; // 唯一标识(英雄5000段,怪物5200段) - name: string; // 显示名称 - icon?: string; // 图标名称(对应美术资源名) - path: string; // 资源路径(对应美术资源名) - fac: FacSet; // 阵营(FacSet.HERO 或 FacSet.MON) - kind?: number; // 未使用 - lv: number; // 英雄等级 - pool_lv?: number; // 卡片等级 - type: HType; // 攻击定位(近战/中程/远程) - monType?: MonType; // 怪物专属类型 - hp: number; // 生命值上限 - ap: number; // 攻击力 - [SkillTriggerType.Call]?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 召唤后触发的技能配置 - [SkillTriggerType.Dead]?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 死亡后触发的技能配置 - [SkillTriggerType.FStart]?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 战斗开始时释放的技能配置 - [SkillTriggerType.FEnd]?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 战斗结束时释放的技能配置 - [SkillTriggerType.Field]?:number[]; // 驻场技能uuid列表,英雄在场时对全局生效 - [SkillTriggerType.Atking]?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数 - [SkillTriggerType.Atked]?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数 - [SkillTriggerType.Revive]?:{s_uuid:number,r_num:number,upr:number}; // 复活技能配置,s_uuid: 技能id, r_num: 触发所需的复活次数, upr 等级对复活次数的影响 - dis?: number; // 攻击距离(像素) - speed?: number; // 移动速度(像素/秒) - skills: Record ; // 携带技能ID列表 - evolve?: Record; // 等级进化配置,key=等级(2,3,...) - info: string; // 描述文案 + uuid: number; // 唯一标识(英雄5000段,怪物5200段) + name: string; // 显示名称 + icon?: string; // 图标名称(对应美术资源名) + path: string; // 资源路径(对应美术资源名) + fac: FacSet; // 阵营(FacSet.HERO 或 FacSet.MON) + kind?: number; // 未使用 + lv: number; // 英雄等级 + pool_lv?: number; // 卡片等级 + type: HType; // 攻击定位(近战/中程/远程) + monType?: MonType; // 怪物专属类型 + hp: number; // 生命值上限 + ap: number; // 攻击力 + [SkillTriggerType.Call]?: { s_uuid: number, t_num: number, overrides?: SkillOverrides }[]; // 召唤后触发的技能配置 + [SkillTriggerType.Dead]?: { s_uuid: number, t_num: number, overrides?: SkillOverrides }[]; // 死亡后触发的技能配置 + [SkillTriggerType.FStart]?: { s_uuid: number, t_num: number, overrides?: SkillOverrides }[]; // 战斗开始时释放的技能配置 + [SkillTriggerType.FEnd]?: { s_uuid: number, t_num: number, overrides?: SkillOverrides }[]; // 战斗结束时释放的技能配置 + [SkillTriggerType.Field]?: number[]; // 驻场技能uuid列表,英雄在场时对全局生效 + [SkillTriggerType.Atking]?: { s_uuid: number, t_num: number, overrides?: SkillOverrides }[]; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数 + [SkillTriggerType.Atked]?: { s_uuid: number, t_num: number, overrides?: SkillOverrides }[]; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数 + [SkillTriggerType.Revive]?: { s_uuid: number, r_num: number, upr: number }; // 复活技能配置,s_uuid: 技能id, r_num: 触发所需的复活次数, upr 等级对复活次数的影响 + dis?: number; // 攻击距离(像素) + speed?: number; // 移动速度(像素/秒) + skills: Record; // 携带技能ID列表 + evolve?: Record; // 等级进化配置,key=等级(2,3,...) + info: string; // 描述文案 } /** * 技能基础信息接口 */ export interface HSkillInfo { - uuid: number; // 唯一标识(技能6000段等) - lv:number; // 技能等级 - cd:number; // 技能cd - ccd:number; // 占位当前cd,用于cd计时 - overrides?: SkillOverrides; // 角色专属参数覆盖 + uuid: number; // 唯一标识(技能6000段等) + lv: number; // 技能等级 + cd: number; // 技能cd + ccd: number; // 占位当前cd,用于cd计时 + overrides?: SkillOverrides; // 角色专属参数覆盖 } /** * 英雄等级进化配置 @@ -192,11 +192,11 @@ export interface HeroEvolve { skill?: { s_uuid: number; cd?: number; overrides?: SkillOverrides }; /** 覆盖触发技能(完全替换该类型的触发配置) */ atking?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; - atked?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; - dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; - fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; - fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; - revive?: { s_uuid: number; r_num: number; upr: number }; + atked?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + revive?: { s_uuid: number; r_num: number; upr: number }; /** 额外属性加成(在等级倍率基础上叠加) */ ap_bonus?: number; hp_bonus?: number; @@ -221,242 +221,324 @@ export interface HeroEvolve { export const HeroInfo: Record = { // ========== atked 类(战士 · 自身强化) ========== - 5011:{uuid:5011,name:"小铁卫",path:"hk1", fac:FacSet.HERO,pool_lv:1,lv:1,type:HType.Melee,hp:300,ap:28, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, - atked:[{s_uuid:6301,t_num:3,overrides:{TGroup:TGroup.Self,ap:4}}], - info:"每受击3次为自身添加4层护盾"}, - 5012:{uuid:5012,name:"不死小强",path:"hk2", fac:FacSet.HERO,pool_lv:2,lv:1,type:HType.Melee,hp:600,ap:57, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, - atked:[{s_uuid:6302,t_num:3,overrides:{TGroup:TGroup.Self,ap:250}}], - info:"每受击3次为自身回复攻击力250%的生命值"}, - 5013:{uuid:5013,name:"铁骨头",path:"hk3", fac:FacSet.HERO,pool_lv:2,lv:1,type:HType.Melee,hp:600,ap:57, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, - atked:[{s_uuid:6402,t_num:5,overrides:{TGroup:TGroup.Self,ap:100}}], - info:"每受击5次永久提升自身最大生命值100点"}, - 5014:{uuid:5014,name:"怒火武者",path:"hk4", fac:FacSet.HERO,pool_lv:3,lv:1,type:HType.Melee,hp:900,ap:85, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow1].cd,ccd:0}}, - atked:[{s_uuid:6401,t_num:3,overrides:{TGroup:TGroup.Self,ap:12}}], - info:"每受击3次永久提升自身攻击力12点"}, - 5015:{uuid:5015,name:"血刃武者",path:"hk5", fac:FacSet.HERO,pool_lv:4,lv:1,type:HType.Melee,hp:1200,ap:113, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, - atked:[ - {s_uuid:6301,t_num:3,overrides:{TGroup:TGroup.Self,ap:3}}, - {s_uuid:6401,t_num:5,overrides:{TGroup:TGroup.Self,ap:15}} - ], - info:"每受击3次加3层护盾,每受击5次永久+15攻击力"}, - 5016:{uuid:5016,name:"狂血战士",path:"hc1", fac:FacSet.HERO,pool_lv:5,lv:1,type:HType.Melee,hp:1500,ap:142, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, - atked:[ - {s_uuid:6401,t_num:3,overrides:{TGroup:TGroup.Self,ap:10}}, - {s_uuid:6403,t_num:5,overrides:{TGroup:TGroup.Self,ap:15}} - ], - info:"每受击3次永久+10攻击力,每受击5次永久+15%暴击率"}, + 5011: { + uuid: 5011, name: "小铁卫", path: "hk1", fac: FacSet.HERO, pool_lv: 1, lv: 1, type: HType.Melee, hp: 300, ap: 28, + skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } }, + atked: [{ s_uuid: 6301, t_num: 3, overrides: { TGroup: TGroup.Self, ap: 4 } }], + info: "每受击3次为自身添加4层护盾" + }, + // 以下英雄暂时注释,保留待后续扩展 + // 5012:{uuid:5012,name:"不死小强",path:"hk2", fac:FacSet.HERO,pool_lv:2,lv:1,type:HType.Melee,hp:600,ap:57, + // skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, + // atked:[{s_uuid:6302,t_num:3,overrides:{TGroup:TGroup.Self,ap:250}}], + // info:"每受击3次为自身回复攻击力250%的生命值"}, + // 5013:{uuid:5013,name:"铁骨头",path:"hk3", fac:FacSet.HERO,pool_lv:2,lv:1,type:HType.Melee,hp:600,ap:57, + // skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, + // atked:[{s_uuid:6402,t_num:5,overrides:{TGroup:TGroup.Self,ap:100}}], + // info:"每受击5次永久提升自身最大生命值100点"}, + // 5014:{uuid:5014,name:"怒火武者",path:"hk4", fac:FacSet.HERO,pool_lv:3,lv:1,type:HType.Melee,hp:900,ap:85, + // skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow1].cd,ccd:0}}, + // atked:[{s_uuid:6401,t_num:3,overrides:{TGroup:TGroup.Self,ap:12}}], + // info:"每受击3次永久提升自身攻击力12点"}, + // 5015:{uuid:5015,name:"血刃武者",path:"hk5", fac:FacSet.HERO,pool_lv:4,lv:1,type:HType.Melee,hp:1200,ap:113, + // skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, + // atked:[ + // {s_uuid:6301,t_num:3,overrides:{TGroup:TGroup.Self,ap:3}}, + // {s_uuid:6401,t_num:5,overrides:{TGroup:TGroup.Self,ap:15}} + // ], + // info:"每受击3次加3层护盾,每受击5次永久+15攻击力"}, + // 5016:{uuid:5016,name:"狂血战士",path:"hc1", fac:FacSet.HERO,pool_lv:5,lv:1,type:HType.Melee,hp:1500,ap:142, + // skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, + // atked:[ + // {s_uuid:6401,t_num:3,overrides:{TGroup:TGroup.Self,ap:10}}, + // {s_uuid:6403,t_num:5,overrides:{TGroup:TGroup.Self,ap:15}} + // ], + // info:"每受击3次永久+10攻击力,每受击5次永久+15%暴击率"}, // ========== atking 类 — 刺客(自身强化) ========== - 5021:{uuid:5021,name:"小刺客",path:"hc1", fac:FacSet.HERO,pool_lv:1,lv:1,type:HType.Melee,hp:300,ap:28, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Fast2].cd,ccd:0}}, - atking:[{s_uuid:6401,t_num:5,overrides:{TGroup:TGroup.Self,ap:8}}], - info:"每攻击5次永久提升自身攻击力8点"}, - 5022:{uuid:5022,name:"嗜血剑客",path:"hc2", fac:FacSet.HERO,pool_lv:3,lv:1,type:HType.Melee,hp:900,ap:85, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Fast2].cd,ccd:0}}, - atking:[ - {s_uuid:6403,t_num:5,overrides:{TGroup:TGroup.Self,ap:10}}, - {s_uuid:6401,t_num:7,overrides:{TGroup:TGroup.Self,ap:12}} - ], - info:"每攻击5次永久+10%暴击率,每攻击7次永久+12攻击力"}, - 5023:{uuid:5023,name:"暗影杀手",path:"hc3", fac:FacSet.HERO,pool_lv:5,lv:1,type:HType.Melee,hp:1500,ap:142, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Fast1].cd,ccd:0}}, - atking:[ - {s_uuid:6403,t_num:5,overrides:{TGroup:TGroup.Self,ap:10}}, - {s_uuid:6404,t_num:7,overrides:{TGroup:TGroup.Self,ap:15}} - ], - info:"每攻击5次永久+10%暴击率,每攻击7次永久+15%暴伤"}, + 5021: { + uuid: 5021, name: "小刺客", path: "hc1", fac: FacSet.HERO, pool_lv: 1, lv: 1, type: HType.Melee, hp: 300, ap: 28, + skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast2].cd, ccd: 0 } }, + atking: [{ s_uuid: 6401, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 8 } }], + info: "每攻击5次永久提升自身攻击力8点" + }, + // 5022: { + // uuid: 5022, name: "嗜血剑客", path: "hc2", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Melee, hp: 900, ap: 85, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast2].cd, ccd: 0 } }, + // atking: [ + // { s_uuid: 6403, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 10 } }, + // { s_uuid: 6401, t_num: 7, overrides: { TGroup: TGroup.Self, ap: 12 } } + // ], + // info: "每攻击5次永久+10%暴击率,每攻击7次永久+12攻击力" + // }, + // 5023: { + // uuid: 5023, name: "暗影杀手", path: "hc3", fac: FacSet.HERO, pool_lv: 5, lv: 1, type: HType.Melee, hp: 1500, ap: 142, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast1].cd, ccd: 0 } }, + // atking: [ + // { s_uuid: 6403, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 10 } }, + // { s_uuid: 6404, t_num: 7, overrides: { TGroup: TGroup.Self, ap: 15 } } + // ], + // info: "每攻击5次永久+10%暴击率,每攻击7次永久+15%暴伤" + // }, // ========== atking 类 — 射手(队友强化,hit_count 控制目标数) ========== - 5031:{uuid:5031,name:"援护弓手",path:"ha1", fac:FacSet.HERO,pool_lv:1,lv:1,type:HType.Long,hp:143,ap:40, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Normal2].cd,ccd:0}}, - atking:[{s_uuid:6401,t_num:5,overrides:{TGroup:TGroup.Team,hit_count:1,ap:8}}], - info:"每攻击5次为随机1名队友永久提升攻击力8点"}, - 5032:{uuid:5032,name:"战术弓手",path:"ha2", fac:FacSet.HERO,pool_lv:3,lv:1,type:HType.Long,hp:430,ap:120, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Normal1].cd,ccd:0}}, - atking:[{s_uuid:6403,t_num:5,overrides:{TGroup:TGroup.Team,hit_count:3,ap:10}}], - info:"每攻击5次为随机3名队友永久提升暴击率10%"}, - 5033:{uuid:5033,name:"鹰眼弓将",path:"ha3", fac:FacSet.HERO,pool_lv:5,lv:1,type:HType.Long,hp:717,ap:200, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Fast3].cd,ccd:0}}, - atking:[ - {s_uuid:6401,t_num:5,overrides:{TGroup:TGroup.Team,hit_count:6,ap:8}}, - {s_uuid:6404,t_num:7,overrides:{TGroup:TGroup.Team,hit_count:6,ap:12}} - ], - info:"每攻击5次为随机6名队友永久+8攻击力,每攻击7次永久+12%暴伤"}, + 5031: { + uuid: 5031, name: "援护弓手", path: "ha1", fac: FacSet.HERO, pool_lv: 1, lv: 1, type: HType.Long, hp: 143, ap: 40, + skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal2].cd, ccd: 0 } }, + atking: [{ s_uuid: 6401, t_num: 5, overrides: { TGroup: TGroup.Team, hit_count: 1, ap: 8 } }], + info: "每攻击5次为随机1名队友永久提升攻击力8点" + }, + // 5032: { + // uuid: 5032, name: "战术弓手", path: "ha2", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Long, hp: 430, ap: 120, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal1].cd, ccd: 0 } }, + // atking: [{ s_uuid: 6403, t_num: 5, overrides: { TGroup: TGroup.Team, hit_count: 3, ap: 10 } }], + // info: "每攻击5次为随机3名队友永久提升暴击率10%" + // }, + // 5033: { + // uuid: 5033, name: "鹰眼弓将", path: "ha3", fac: FacSet.HERO, pool_lv: 5, lv: 1, type: HType.Long, hp: 717, ap: 200, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast3].cd, ccd: 0 } }, + // atking: [ + // { s_uuid: 6401, t_num: 5, overrides: { TGroup: TGroup.Team, hit_count: 6, ap: 8 } }, + // { s_uuid: 6404, t_num: 7, overrides: { TGroup: TGroup.Team, hit_count: 6, ap: 12 } } + // ], + // info: "每攻击5次为随机6名队友永久+8攻击力,每攻击7次永久+12%暴伤" + // }, // ========== dead 类(战士+刺客 · 死亡遗产) ========== - 5041:{uuid:5041,name:"殉道卫士",path:"hk1", fac:FacSet.HERO,pool_lv:1,lv:1,type:HType.Melee,hp:300,ap:28, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, - dead:[{s_uuid:6301,t_num:1,overrides:{TGroup:TGroup.Team,ap:3}}], - info:"死亡时为全队添加3层护盾"}, - 5042:{uuid:5042,name:"遗志将军",path:"hk2", fac:FacSet.HERO,pool_lv:2,lv:1,type:HType.Melee,hp:600,ap:57, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, - dead:[ - {s_uuid:6401,t_num:1,overrides:{TGroup:TGroup.Team,ap:20}}, - {s_uuid:6402,t_num:1,overrides:{TGroup:TGroup.Team,ap:80}} - ], - revive:{s_uuid:6501,r_num:1,upr:0.3}, - info:"死亡时全队永久+20攻击力、+80最大生命值,死后复活一次"}, - 5043:{uuid:5043,name:"亡魂刺客",path:"hc1", fac:FacSet.HERO,pool_lv:3,lv:1,type:HType.Melee,hp:900,ap:85, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Normal1].cd,ccd:0}}, - dead:[{s_uuid:6405,t_num:1,overrides:{TGroup:TGroup.Team,ap:15}}], - info:"死亡时全队永久提升击晕概率15%"}, - 5044:{uuid:5044,name:"血誓剑客",path:"hc2", fac:FacSet.HERO,pool_lv:4,lv:1,type:HType.Melee,hp:1200,ap:113, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Fast3].cd,ccd:0}}, - dead:[ - {s_uuid:6403,t_num:1,overrides:{TGroup:TGroup.Team,ap:15}}, - {s_uuid:6404,t_num:1,overrides:{TGroup:TGroup.Team,ap:20}} - ], - info:"死亡时全队永久+15%暴击率、+20%暴伤"}, - 5045:{uuid:5045,name:"不灭战魂",path:"hk3", fac:FacSet.HERO,pool_lv:5,lv:1,type:HType.Melee,hp:1500,ap:142, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow3].cd,ccd:0}}, - dead:[ - {s_uuid:6301,t_num:1,overrides:{TGroup:TGroup.Team,ap:5}}, - {s_uuid:6401,t_num:1,overrides:{TGroup:TGroup.Team,ap:30}}, - {s_uuid:6402,t_num:1,overrides:{TGroup:TGroup.Team,ap:120}} - ], - revive:{s_uuid:6501,r_num:1,upr:0.5}, - info:"死亡时全队获得5层护盾、永久+30攻击力、永久+120最大生命值,死后复活一次"}, + // 5041: { + // uuid: 5041, name: "殉道卫士", path: "hk1", fac: FacSet.HERO, pool_lv: 1, lv: 1, type: HType.Melee, hp: 300, ap: 28, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } }, + // dead: [{ s_uuid: 6301, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } }], + // info: "死亡时为全队添加3层护盾" + // }, + // 5042: { + // uuid: 5042, name: "遗志将军", path: "hk2", fac: FacSet.HERO, pool_lv: 2, lv: 1, type: HType.Melee, hp: 600, ap: 57, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } }, + // dead: [ + // { s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 20 } }, + // { s_uuid: 6402, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 80 } } + // ], + // revive: { s_uuid: 6501, r_num: 1, upr: 0.3 }, + // info: "死亡时全队永久+20攻击力、+80最大生命值,死后复活一次" + // }, + // 5043: { + // uuid: 5043, name: "亡魂刺客", path: "hc1", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Melee, hp: 900, ap: 85, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal1].cd, ccd: 0 } }, + // dead: [{ s_uuid: 6405, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 15 } }], + // info: "死亡时全队永久提升击晕概率15%" + // }, + // 5044: { + // uuid: 5044, name: "血誓剑客", path: "hc2", fac: FacSet.HERO, pool_lv: 4, lv: 1, type: HType.Melee, hp: 1200, ap: 113, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast3].cd, ccd: 0 } }, + // dead: [ + // { s_uuid: 6403, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 15 } }, + // { s_uuid: 6404, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 20 } } + // ], + // info: "死亡时全队永久+15%暴击率、+20%暴伤" + // }, + // 5045: { + // uuid: 5045, name: "不灭战魂", path: "hk3", fac: FacSet.HERO, pool_lv: 5, lv: 1, type: HType.Melee, hp: 1500, ap: 142, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } }, + // dead: [ + // { s_uuid: 6301, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } }, + // { s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 30 } }, + // { s_uuid: 6402, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 120 } } + // ], + // revive: { s_uuid: 6501, r_num: 1, upr: 0.5 }, + // info: "死亡时全队获得5层护盾、永久+30攻击力、永久+120最大生命值,死后复活一次" + // }, // ========== fstart 类(法师 · 战前增益) ========== - 5051:{uuid:5051,name:"占卜师",path:"hm1", fac:FacSet.HERO,pool_lv:1,lv:1,type:HType.Long,hp:143,ap:40, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Mid3].cd,ccd:0}}, - fstart:[{s_uuid:6401,t_num:1,overrides:{TGroup:TGroup.Team,ap:15}}], - info:"战斗开始时为全队永久提升攻击力15点"}, - 5052:{uuid:5052,name:"护盾牧师",path:"hm2", fac:FacSet.HERO,pool_lv:2,lv:1,type:HType.Long,hp:287,ap:80, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Slow1].cd,ccd:0}}, - fstart:[{s_uuid:6301,t_num:1,overrides:{TGroup:TGroup.Team,ap:2}}], - info:"战斗开始时为全队添加2层护盾"}, - 5053:{uuid:5053,name:"血盟法师",path:"hm3", fac:FacSet.HERO,pool_lv:2,lv:1,type:HType.Long,hp:287,ap:80, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Mid2].cd,ccd:0}}, - fstart:[{s_uuid:6402,t_num:1,overrides:{TGroup:TGroup.Team,ap:100}}], - info:"战斗开始时为全队永久提升最大生命值100点"}, - 5054:{uuid:5054,name:"暴击法师",path:"hm4", fac:FacSet.HERO,pool_lv:3,lv:1,type:HType.Long,hp:430,ap:120, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Mid3].cd,ccd:0}}, - fstart:[{s_uuid:6403,t_num:1,overrides:{TGroup:TGroup.Team,ap:20}}], - info:"战斗开始时为全队永久提升暴击率20%"}, - 5055:{uuid:5055,name:"毁灭法师",path:"hm5", fac:FacSet.HERO,pool_lv:4,lv:1,type:HType.Long,hp:573,ap:160, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Mid1].cd,ccd:0}}, - fstart:[ - {s_uuid:6404,t_num:1,overrides:{TGroup:TGroup.Team,ap:25}}, - {s_uuid:6401,t_num:1,overrides:{TGroup:TGroup.Team,ap:20}} - ], - info:"战斗开始时为全队永久+25%暴伤、+20攻击力"}, - 5056:{uuid:5056,name:"预言法师",path:"hm6", fac:FacSet.HERO,pool_lv:5,lv:1,type:HType.Long,hp:717,ap:200, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Normal3].cd,ccd:0}}, - fstart:[ - {s_uuid:6405,t_num:1,overrides:{TGroup:TGroup.Team,ap:20}}, - {s_uuid:6403,t_num:1,overrides:{TGroup:TGroup.Team,ap:15}}, - {s_uuid:6404,t_num:1,overrides:{TGroup:TGroup.Team,ap:20}} - ], - info:"战斗开始时为全队永久+20%击晕概率、+15%暴击率、+20%暴伤"}, + 5051: { + uuid: 5051, name: "占卜师", path: "hm1", fac: FacSet.HERO, pool_lv: 1, lv: 1, type: HType.Long, hp: 143, ap: 40, + skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } }, + fstart: [{ s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 15 } }], + info: "战斗开始时为全队永久提升攻击力15点" + }, + // 5052: { + // uuid: 5052, name: "护盾牧师", path: "hm2", fac: FacSet.HERO, pool_lv: 2, lv: 1, type: HType.Long, hp: 287, ap: 80, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow1].cd, ccd: 0 } }, + // fstart: [{ s_uuid: 6301, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 2 } }], + // info: "战斗开始时为全队添加2层护盾" + // }, + // 5053: { + // uuid: 5053, name: "血盟法师", path: "hm3", fac: FacSet.HERO, pool_lv: 2, lv: 1, type: HType.Long, hp: 287, ap: 80, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } }, + // fstart: [{ s_uuid: 6402, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 100 } }], + // info: "战斗开始时为全队永久提升最大生命值100点" + // }, + // 5054: { + // uuid: 5054, name: "暴击法师", path: "hm4", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Long, hp: 430, ap: 120, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } }, + // fstart: [{ s_uuid: 6403, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 20 } }], + // info: "战斗开始时为全队永久提升暴击率20%" + // }, + // 5055: { + // uuid: 5055, name: "毁灭法师", path: "hm5", fac: FacSet.HERO, pool_lv: 4, lv: 1, type: HType.Long, hp: 573, ap: 160, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid1].cd, ccd: 0 } }, + // fstart: [ + // { s_uuid: 6404, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 25 } }, + // { s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 20 } } + // ], + // info: "战斗开始时为全队永久+25%暴伤、+20攻击力" + // }, + // 5056: { + // uuid: 5056, name: "预言法师", path: "hm6", fac: FacSet.HERO, pool_lv: 5, lv: 1, type: HType.Long, hp: 717, ap: 200, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } }, + // fstart: [ + // { s_uuid: 6405, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 20 } }, + // { s_uuid: 6403, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 15 } }, + // { s_uuid: 6404, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 20 } } + // ], + // info: "战斗开始时为全队永久+20%击晕概率、+15%暴击率、+20%暴伤" + // }, // ========== field 类(法师 · 驻场光环) ========== - 5061:{uuid:5061,name:"亡语法师",path:"hm1", fac:FacSet.HERO,pool_lv:3,lv:1,type:HType.Long,hp:430,ap:120, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Mid2].cd,ccd:0}}, - field:[7015], - info:"驻场期间全队死亡触发技能次数+1,死亡后光环消失"}, + // 5061: { + // uuid: 5061, name: "亡语法师", path: "hm1", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Long, hp: 430, ap: 120, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } }, + // field: [7015], + // info: "驻场期间全队死亡触发技能次数+1,死亡后光环消失" + // }, // ========== fend + atking 类(辅助 · 治疗续航 + 波次增益) ========== - 5071:{uuid:5071,name:"治愈牧师",path:"hh1", fac:FacSet.HERO,pool_lv:1,lv:1,type:HType.Long,hp:143,ap:40, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Normal3].cd,ccd:0}}, - atking:[{s_uuid:6302,t_num:5,overrides:{TGroup:TGroup.Team,ap:200}}], - info:"每攻击5次治疗全队200%AP"}, - 5072:{uuid:5072,name:"小金库",path:"hh2", fac:FacSet.HERO,pool_lv:2,lv:1,type:HType.Long,hp:287,ap:80, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Mid1].cd,ccd:0}}, - atking:[{s_uuid:6302,t_num:5,overrides:{TGroup:TGroup.Team,ap:200}}], - fend:[{s_uuid:6303,t_num:1,overrides:{gold:1}}], - info:"每攻击5次治疗全队,每波结束获得1金币"}, - 5073:{uuid:5073,name:"强化牧师",path:"hh3", fac:FacSet.HERO,pool_lv:3,lv:1,type:HType.Long,hp:430,ap:120, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Normal3].cd,ccd:0}}, - atking:[{s_uuid:6302,t_num:5,overrides:{TGroup:TGroup.Team,ap:250}}], - fend:[{s_uuid:6401,t_num:1,overrides:{TGroup:TGroup.Team,ap:10}}], - info:"每攻击5次治疗全队,每波结束全队永久+10攻击力"}, - 5074:{uuid:5074,name:"生命牧师",path:"hh4", fac:FacSet.HERO,pool_lv:4,lv:1,type:HType.Long,hp:573,ap:160, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Normal3].cd,ccd:0}}, - atking:[{s_uuid:6302,t_num:5,overrides:{TGroup:TGroup.Team,ap:250}}], - fend:[{s_uuid:6402,t_num:1,overrides:{TGroup:TGroup.Team,ap:80}}], - info:"每攻击5次治疗全队,每波结束全队永久+80最大生命值"}, - 5075:{uuid:5075,name:"全能牧师",path:"hh5", fac:FacSet.HERO,pool_lv:5,lv:1,type:HType.Long,hp:717,ap:200, - skills:{6001:{uuid:6001,lv:1,cd:AtkSpeedSet[AtkSpeedLv.Normal2].cd,ccd:0}}, - atking:[{s_uuid:6302,t_num:5,overrides:{TGroup:TGroup.Team,ap:300}}], - fend:[ - {s_uuid:6401,t_num:1,overrides:{TGroup:TGroup.Team,ap:12}}, - {s_uuid:6402,t_num:1,overrides:{TGroup:TGroup.Team,ap:60}} - ], - info:"每攻击5次治疗全队300%AP,每波结束全队永久+12攻击力、+60最大生命值"}, + 5071: { + uuid: 5071, name: "治愈牧师", path: "hh1", fac: FacSet.HERO, pool_lv: 1, lv: 1, type: HType.Long, hp: 143, ap: 40, + skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } }, + atking: [{ s_uuid: 6302, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 200 } }], + info: "每攻击5次治疗全队200%AP" + }, + // 5072: { + // uuid: 5072, name: "小金库", path: "hh2", fac: FacSet.HERO, pool_lv: 2, lv: 1, type: HType.Long, hp: 287, ap: 80, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid1].cd, ccd: 0 } }, + // atking: [{ s_uuid: 6302, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 200 } }], + // fend: [{ s_uuid: 6303, t_num: 1, overrides: { gold: 1 } }], + // info: "每攻击5次治疗全队,每波结束获得1金币" + // }, + // 5073: { + // uuid: 5073, name: "强化牧师", path: "hh3", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Long, hp: 430, ap: 120, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } }, + // atking: [{ s_uuid: 6302, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 250 } }], + // fend: [{ s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 10 } }], + // info: "每攻击5次治疗全队,每波结束全队永久+10攻击力" + // }, + // 5074: { + // uuid: 5074, name: "生命牧师", path: "hh4", fac: FacSet.HERO, pool_lv: 4, lv: 1, type: HType.Long, hp: 573, ap: 160, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } }, + // atking: [{ s_uuid: 6302, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 250 } }], + // fend: [{ s_uuid: 6402, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 80 } }], + // info: "每攻击5次治疗全队,每波结束全队永久+80最大生命值" + // }, + // 5075: { + // uuid: 5075, name: "全能牧师", path: "hh5", fac: FacSet.HERO, pool_lv: 5, lv: 1, type: HType.Long, hp: 717, ap: 200, + // skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal2].cd, ccd: 0 } }, + // atking: [{ s_uuid: 6302, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 300 } }], + // fend: [ + // { s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 12 } }, + // { s_uuid: 6402, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 60 } } + // ], + // info: "每攻击5次治疗全队300%AP,每波结束全队永久+12攻击力、+60最大生命值" + // }, -/* - *=============怪物配置列表================ - * 基础近战型(lv:1) : SPEED:800 |AP:12 | HP:360 | skills[0].cd=0.65 - * 重型坦克型(lv:1) : SPEED:800 |AP:30 | HP:1050 | skills[0].cd=2 - * 远程dps(lv:1) : SPEED:800 |AP:45 | HP:240 | skills[0].cd=1.5 - * 远程辅助(lv:1) : SPEED:800 |AP:20 | HP:240 | skills[0].cd=1 - * 精英 (lv:1) : SPEED:800 |AP:20 | HP:4500 | skills[0].cd=1 - */ + /* + *=============怪物配置列表================ + * 基础近战型(lv:1) : SPEED:800 |AP:12 | HP:360 | skills[0].cd=0.65 + * 重型坦克型(lv:1) : SPEED:800 |AP:30 | HP:1050 | skills[0].cd=2 + * 远程dps(lv:1) : SPEED:800 |AP:45 | HP:240 | skills[0].cd=1.5 + * 远程辅助(lv:1) : SPEED:800 |AP:20 | HP:240 | skills[0].cd=1 + * 精英 (lv:1) : SPEED:800 |AP:20 | HP:4500 | skills[0].cd=1 + */ // 基础怪物 (全部固定点位站桩攻击,HType仅决定是前排还是后排) // 前排怪物 (站在前排,承受更多伤害) — v5: TD节奏CD,多而弱爽感设计 -6001:{uuid:6001,name:"兽人战士",path:"m1", fac:FacSet.MON,lv:1,type:HType.Melee,monType:MonType.Melee,hp:220,ap:10,speed:70, - skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"基础前排怪"}, -6002:{uuid:6002,name:"兽人精锐战士",path:"m2", fac:FacSet.MON,lv:1,type:HType.Melee,monType:MonType.Melee,hp:300,ap:14,speed:110, - skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"进阶前排怪,更快更痛"}, -6003:{uuid:6003,name:"兽人重装兵",path:"m3", fac:FacSet.MON,lv:1,type:HType.Melee,monType:MonType.Heavy,hp:850,ap:20,speed:50, - skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"重型坦克怪,高HP慢攻"}, + 6001: { + uuid: 6001, name: "兽人战士", path: "m1", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 220, ap: 10, speed: 70, + skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "基础前排怪" + }, + 6002: { + uuid: 6002, name: "兽人精锐战士", path: "m2", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 300, ap: 14, speed: 110, + skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "进阶前排怪,更快更痛" + }, + 6003: { + uuid: 6003, name: "兽人重装兵", path: "m3", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Heavy, hp: 850, ap: 20, speed: 50, + skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "重型坦克怪,高HP慢攻" + }, // 后排怪物 (站在后排,输出更高) -6004:{uuid:6004,name:"兽人射手",path:"m4", fac:FacSet.MON,lv:1,type:HType.Long,monType:MonType.Long,hp:190,ap:35,speed:70, - skills:{6008:{uuid:6008,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"后排高DPS怪"}, -6005:{uuid:6005,name:"兽人刺客",path:"m5", fac:FacSet.MON,lv:1,type:HType.Long,monType:MonType.Assassin,hp:210,ap:38,speed:130, - skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"高AP快速攻击刺客"}, + 6004: { + uuid: 6004, name: "兽人射手", path: "m4", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Long, hp: 190, ap: 35, speed: 70, + skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "后排高DPS怪" + }, + 6005: { + uuid: 6005, name: "兽人刺客", path: "m5", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Assassin, hp: 210, ap: 38, speed: 130, + skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "高AP快速攻击刺客" + }, // 特殊位怪物 -6006:{uuid:6006,name:"骷髅领主",path:"m6", fac:FacSet.MON,lv:1,type:HType.Melee,monType:MonType.MeleeBoss,hp:5000,ap:20,speed:60, - skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"前排MiniBoss级坦克"}, -6007:{uuid:6007,name:"兽人术士",path:"m7", fac:FacSet.MON,lv:1,type:HType.Long,monType:MonType.Support,hp:300,ap:24,speed:70, - skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"后排法师怪,魔法攻击"}, -6008:{uuid:6008,name:"兽人火法",path:"m8", fac:FacSet.MON,lv:1,type:HType.Long,monType:MonType.Summoner,hp:270,ap:32,speed:70, - skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"后排高输出法师怪"}, + 6006: { + uuid: 6006, name: "骷髅领主", path: "m6", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.MeleeBoss, hp: 5000, ap: 20, speed: 60, + skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "前排MiniBoss级坦克" + }, + 6007: { + uuid: 6007, name: "兽人术士", path: "m7", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Support, hp: 300, ap: 24, speed: 70, + skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "后排法师怪,魔法攻击" + }, + 6008: { + uuid: 6008, name: "兽人火法", path: "m8", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Summoner, hp: 270, ap: 32, speed: 70, + skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "后排高输出法师怪" + }, -// BOSS怪物 — Boss节奏1.2-1.5s,删除不存在的6206技能 -6101:{uuid:6101,name:"兽人首领-双刀战士",path:"mb1", fac:FacSet.MON,lv:6,type:HType.Melee,monType:MonType.MeleeBoss,hp:1900,ap:30,speed:120, - skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"前排Boss,高攻速"}, -6102:{uuid:6102,name:"兽人首领-斧头战士",path:"mb2", fac:FacSet.MON,lv:6,type:HType.Melee,monType:MonType.MeleeBoss,hp:7500,ap:26,speed:60, - skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"前排Boss,超高HP"}, -6103:{uuid:6103,name:"兽人首领-魔法师",path:"mb3", fac:FacSet.MON,lv:6,type:HType.Long,monType:MonType.LongBoss,hp:2250,ap:38,speed:110, - skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"后排法系Boss,高AP"}, -6104:{uuid:6104,name:"兽人首领-射手",path:"mb4", fac:FacSet.MON,lv:6,type:HType.Long,monType:MonType.LongBoss,hp:6800,ap:30,speed:70, - skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"后排位Boss,均衡型"}, -6105:{uuid:6105,name:"亡灵首领-法师",path:"mb5", fac:FacSet.MON,lv:6,type:HType.Long,monType:MonType.LongBoss,hp:2600,ap:42,speed:110, - skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"后排高AP Boss"}, -6106:{uuid:6106,name:"亡灵首领-骑马战士",path:"mb6", fac:FacSet.MON,lv:6,type:HType.Melee,monType:MonType.MeleeBoss,hp:9000,ap:26,speed:130, - skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"前排终极Boss,最高HP+高速"}, + // BOSS怪物 — Boss节奏1.2-1.5s,删除不存在的6206技能 + 6101: { + uuid: 6101, name: "兽人首领-双刀战士", path: "mb1", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 1900, ap: 30, speed: 120, + skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "前排Boss,高攻速" + }, + 6102: { + uuid: 6102, name: "兽人首领-斧头战士", path: "mb2", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 7500, ap: 26, speed: 60, + skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "前排Boss,超高HP" + }, + 6103: { + uuid: 6103, name: "兽人首领-魔法师", path: "mb3", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 2250, ap: 38, speed: 110, + skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "后排法系Boss,高AP" + }, + 6104: { + uuid: 6104, name: "兽人首领-射手", path: "mb4", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 6800, ap: 30, speed: 70, + skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "后排位Boss,均衡型" + }, + 6105: { + uuid: 6105, name: "亡灵首领-法师", path: "mb5", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 2600, ap: 42, speed: 110, + skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "后排高AP Boss" + }, + 6106: { + uuid: 6106, name: "亡灵首领-骑马战士", path: "mb6", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 9000, ap: 26, speed: 130, + skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "前排终极Boss,最高HP+高速" + }, - }; +}; export const HeroList: number[] = [ // atked 类(战士 · 自身强化) - 5011, 5012, 5013, 5014, 5015, 5016, + 5011, + // 5012, 5013, 5014, 5015, 5016, // atking 刺客(自身强化) - 5021, 5022, 5023, + 5021, + // 5022, 5023, // atking 射手(队友强化) - 5031, 5032, 5033, + 5031, + // 5032, 5033, // dead 类(死亡遗产) - 5041, 5042, 5043, 5044, 5045, + // 5041, 5042, 5043, 5044, 5045, // fstart 类(法师 · 战前增益) - 5051, 5052, 5053, 5054, 5055, 5056, + 5051, + // 5052, 5053, 5054, 5055, 5056, // field 类(法师 · 驻场光环) - 5061, + // 5061, // fend + atking 类(辅助 · 治疗 + 波次增益) - 5071, 5072, 5073, 5074, 5075, + 5071, + // 5072, 5073, 5074, 5075, ]; diff --git a/assets/script/game/hero/Hero.ts b/assets/script/game/hero/Hero.ts index 7e98429f..53590d48 100644 --- a/assets/script/game/hero/Hero.ts +++ b/assets/script/game/hero/Hero.ts @@ -136,9 +136,9 @@ export class Hero extends ecs.Entity { model.revive = hero.revive; // 基础属性按等级倍率初始化 - // 使用指数增长公式,等级2时为原来的3倍,等级3时为原来的9倍 (若需线性增长可改为 hero.ap * (1 + (model.lv - 1) * (FightSet.H_HERO_POW - 1))) - let base_ap = hero.ap * Math.pow(FightSet.MERGE_NEED, model.lv - 1); - let base_hp = hero.hp * Math.pow(FightSet.MERGE_NEED, model.lv - 1); + // 使用指数增长公式:等级 2 时为原来的 HERO_LV_MULTIPLIER 倍,等级 3 时为 HERO_LV_MULTIPLIER^2 倍 + let base_ap = hero.ap * Math.pow(FightSet.HERO_LV_MULTIPLIER, model.lv - 1); + let base_hp = hero.hp * Math.pow(FightSet.HERO_LV_MULTIPLIER, model.lv - 1); model.base_ap = base_ap; model.base_hp = base_hp; diff --git a/assets/script/game/hero/SCastSystem.ts b/assets/script/game/hero/SCastSystem.ts index 306184c7..d1ec7d16 100644 --- a/assets/script/game/hero/SCastSystem.ts +++ b/assets/script/game/hero/SCastSystem.ts @@ -98,7 +98,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate // 动态计算卡牌的虚拟攻击力: // 1. 根据卡牌等级给予基础成长(同英雄升级公式,基准设为 100) - let baseAp = 100 * Math.pow(FightSet.MERGE_NEED, cardLv - 1); + let baseAp = 100 * Math.pow(FightSet.HERO_LV_MULTIPLIER, cardLv - 1); let highestAp = baseAp; // 2. 获取场上最高攻击力的英雄,保证后期奶量/增益绝对够用 diff --git a/assets/script/game/map/CardComp.ts b/assets/script/game/map/CardComp.ts index a2f67bf8..d4d6deb4 100644 --- a/assets/script/game/map/CardComp.ts +++ b/assets/script/game/map/CardComp.ts @@ -764,14 +764,28 @@ export class CardComp extends CCComp { if (this.info_node) this.info_node.active = false; } else { if (this.lvl_node) this.lvl_node.node.active = false; - // 特殊卡(升级 / 刷新):显示卡名 + 品质后缀 + 描述 - const specialCard = this.card_type === CardType.SpecialUpgrade - ? SpecialUpgradeCardList[this.card_uuid] - : SpecialRefreshCardList[this.card_uuid]; - const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1)); - const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : ""; - this.setLabel(this.name_node, `${spSuffix}${specialCard?.name || ""}${spSuffix}`); - + // 动态升级卡:显示对应英雄名 + "升级"后缀 + 目标等级 + if (this.card_type === CardType.SpecialUpgrade && this.cardData.target_hero_uuid) { + const targetHero = HeroInfo[this.cardData.target_hero_uuid]; + const targetLv = Math.max(1, Math.floor(this.cardData.hero_lv ?? 1)); + this.setLabel(this.name_node, `${targetHero?.name || ""} 升级`); + if (this.info_node) { + this.info_node.active = true; + this.setLabel(this.info_node, `升至 Lv.${targetLv}`); + } + } else { + // 特殊卡(升级 / 刷新):显示卡名 + 品质后缀 + 描述 + const specialCard = this.card_type === CardType.SpecialUpgrade + ? SpecialUpgradeCardList[this.card_uuid] + : SpecialRefreshCardList[this.card_uuid]; + const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1)); + const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : ""; + this.setLabel(this.name_node, `${spSuffix}${specialCard?.name || ""}${spSuffix}`); + if (this.info_node) { + this.info_node.active = true; + this.setLabel(this.info_node, specialCard?.info || ""); + } + } this.ap_node.active = false; this.hp_node.active = false; } diff --git a/assets/script/game/map/CardLiteComp.ts b/assets/script/game/map/CardLiteComp.ts index 76dde728..51f753a6 100644 --- a/assets/script/game/map/CardLiteComp.ts +++ b/assets/script/game/map/CardLiteComp.ts @@ -246,12 +246,18 @@ export class CardLiteComp extends CCComp { this.setLabel(this.name_node, `${spSuffix}${skillCard?.name || skill?.name || ""}${spSuffix}`); } else { if (this.lvl_node) this.lvl_node.node.active = false; - const specialCard = this.card_type === CardType.SpecialUpgrade - ? SpecialUpgradeCardList[this.card_uuid] - : SpecialRefreshCardList[this.card_uuid]; - const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1)); - const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : ""; - this.setLabel(this.name_node, `${spSuffix}${specialCard?.name || ""}${spSuffix}`); + // 动态升级卡:显示对应英雄名 + "升级"后缀 + if (this.card_type === CardType.SpecialUpgrade && this.cardData.target_hero_uuid) { + const targetHero = HeroInfo[this.cardData.target_hero_uuid]; + this.setLabel(this.name_node, `${targetHero?.name || ""} 升级`); + } else { + const specialCard = this.card_type === CardType.SpecialUpgrade + ? SpecialUpgradeCardList[this.card_uuid] + : SpecialRefreshCardList[this.card_uuid]; + const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1)); + const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : ""; + this.setLabel(this.name_node, `${spSuffix}${specialCard?.name || ""}${spSuffix}`); + } } if (this.cost_node) { diff --git a/assets/script/game/map/MissionCardComp.ts b/assets/script/game/map/MissionCardComp.ts index 3fbb2b1a..0e910fc2 100644 --- a/assets/script/game/map/MissionCardComp.ts +++ b/assets/script/game/map/MissionCardComp.ts @@ -3,41 +3,43 @@ * @description 卡牌系统核心控制器(战斗 UI 层 + 业务逻辑层) * * 职责: - * 1. **卡牌分发管理** —— 从卡池抽取 4 张卡,分发到 4 个 CardComp 槽位。 - * 2. **卡池升级** —— 消耗金币提升卡池等级(poolLv),解锁更高稀有度的卡牌。 - * 3. **金币费用管理** —— 抽卡费用(refreshCost)、升级费用(CardsUpSet)、 - * 波次折扣(CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE)的计算与扣除。 - * 4. **英雄数量上限校验** —— 在 UseHeroCard 事件的 guard 阶段判断是否允许 - * 再召唤英雄(含合成后腾位的特殊判断 canUseHeroCardByMerge)。 - * 5. **场上英雄信息面板(HInfoComp 列表)同步** —— + * 1. **卡牌分发管理** —— 从卡池抽取 3 张卡,分发到 3 个 CardComp 槽位。 + * 抽卡规则:场上已有英雄时按权重混合"对应英雄的升级卡" + "其他英雄卡" + "刷新卡"; + * 场上无英雄时仅抽取英雄卡和刷新卡。 + * 2. **金币费用管理** —— 抽卡费用(refreshCost)的扣除。 + * 3. **英雄数量上限校验** —— 在 UseHeroCard 事件的 guard 阶段判断是否允许再召唤英雄。 + * 4. **场上英雄信息面板(HInfoComp 列表)同步** —— * 英雄上场时实例化面板,死亡时移除,定时刷新属性显示。 - * 6. **特殊卡执行** —— 处理英雄升级卡(SpecialUpgrade)和英雄刷新卡(SpecialRefresh)。 - * 7. **准备/战斗阶段切换** —— 控制卡牌面板的 展开/收起 动画。 + * 5. **特殊卡执行** —— 处理英雄升级卡(SpecialUpgrade,按 UUID 精确升级)和 + * 英雄刷新卡(SpecialRefresh)。 + * 6. **准备/战斗阶段切换** —— 控制卡牌面板的 展开/收起 动画。 * * 关键设计: - * - 4 个 CardComp 通过 cacheCardComps() 映射为有序数组 cardComps[], + * - 3 个 CardComp 通过 cacheCardComps() 映射为有序数组 cardComps[], * 之后所有分发、清空操作均通过此数组进行。 - * - buildDrawCards() 保证每次抽取 4 张,不足时循环补齐。 + * - buildDrawCards() 动态合并升级卡池和基础卡池后抽取 3 张,不足时循环补齐。 * - 英雄上限校验(onUseHeroCard)采用 guard/cancel 模式: * CardComp 发出 UseHeroCard 事件并传入 guard 对象, * 本组件可通过 guard.cancel=true 阻止使用。 - * - ensureHeroInfoPanel() 建立 EID → HInfoComp 的 Map 映射, - * 支持英雄合成升级后面板热更新。 + * + * 历史: + * 旧版本曾包含"卡池等级(poolLv)"机制和"三合一合成腾位"判断,已全部移除: + * - 卡牌不再分级,所有英雄卡统一 lv1。 + * - 英雄升级仅通过升级卡(SpecialUpgrade)触发,按 UUID 精确升级场上对应英雄。 * * 依赖: * - CardComp —— 单卡槽位 * - HInfoComp —— 英雄信息面板 * - CardSet 模块 —— 卡池配置、抽卡规则、特殊卡数据 - * - HeroAttrsComp —— 英雄属性(合成校验 / 升级) - * - MissionHeroComp —— 获取合成规则(needCount / maxLv) + * - HeroAttrsComp —— 英雄属性(升级) * - smc.vmdata.mission_data —— 局内数据(coin / hero_num / hero_max_num) */ import { mLogger } from "../common/Logger"; -import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, SpriteAtlas, Tween, tween, Vec3, Widget } from "cc"; +import { _decorator, Label, Node, NodeEventType, Tween, tween, 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 { 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, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet"; +import { CardConfig, CardPoolList, CardType, drawCardsByRule, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet"; import { CardComp } from "./CardComp"; import { SCardComp } from "./SCardComp"; import { oops } from "db://oops-framework/core/Oops"; @@ -45,11 +47,8 @@ import { HeroAttrsComp } from "../hero/HeroAttrsComp"; import { smc } from "../common/SingletonModuleComp"; import { HeroInfo, HType } from "../common/config/heroSet"; import { HeroViewComp } from "../hero/HeroViewComp"; -import { FacSet, FightSet, CARD_POOL_UPGRADE_WAVES, SKILL_CARD_WAVES } from "../common/config/GameSet"; -import { MoveComp } from "../hero/MoveComp"; -import { MissionHeroComp } from "./MissionHeroComp"; +import { FacSet, FightSet, SKILL_CARD_WAVES } from "../common/config/GameSet"; import { MissionEconomy } from "./MissionEconomy"; -import { MissionComp } from "./MissionComp"; import { UIID } from "../common/config/GameUIConfig"; const { ccclass, property } = _decorator; @@ -58,7 +57,7 @@ const { ccclass, property } = _decorator; /** * MissionCardComp —— 卡牌系统核心控制器 * - * 管理 4 个卡牌槽位的抽卡分发、卡池升级、金币费用、 + * 管理 3 个卡牌槽位的抽卡分发、金币费用、 * 英雄上限校验、场上英雄信息面板同步以及特殊卡执行。 */ @ccclass('MissionCardComp') @@ -99,13 +98,13 @@ export class MissionCardComp extends CCComp { /** 抽卡(刷新)按钮节点 */ @property(Node) cards_chou: Node = null! - /** 卡池升级按钮节点 */ + /** 卡池升级按钮节点(已废弃,保留节点引用防止 editor 报错) */ @property(Node) cards_up: Node = null! /** 金币显示节点(含 icon + num 子节点) */ @property(Node) coins_node: Node = null! - /** 卡池等级显示节点 */ + /** 卡池等级显示节点(已废弃,保留节点引用) */ @property(Node) pool_lv_node: Node = null! /** 英雄数量显示节点(含 icon + num 子节点) */ @@ -135,12 +134,10 @@ export class MissionCardComp extends CCComp { // ======================== 运行时状态 ======================== - /** 四个槽位对应的 CardComp 控制器缓存(有序数组) */ + /** 三个槽位对应的 CardComp 控制器缓存(有序数组) */ private cardComps: CardComp[] = []; /** 技能卡槽控制器缓存 */ private skillCardComps: SCardComp[] = []; - /** 当前卡池等级(仅影响抽卡来源,不直接改卡槽现有内容) */ - private poolLv: number = CARD_POOL_INIT_LEVEL; /** 是否已缓存卡牌面板基准缩放 */ private hasCachedCardsBaseScale: boolean = false; /** 卡牌面板基准缩放(从场景读取) */ @@ -157,20 +154,17 @@ export class MissionCardComp extends CCComp { /** * 组件加载: * 1. 绑定生命周期事件和按钮交互事件。 - * 2. 缓存 4 个 CardComp 子控制器引用。 + * 2. 缓存 3 个 CardComp 子控制器引用。 * 3. 计算并设置槽位水平布局。 * 4. 初始化卡牌面板缩放参数。 - * 5. 触发首次任务开始流程。 */ onLoad() { this.bindEvents(); this.cacheCardComps(); this.layoutCardSlots(); this.initCardsPanelPos(); - // this.onMissionStart(); // 移除 onLoad 自动触发,改为事件驱动 mLogger.log(this.debugMode, "MissionCardComp", "onLoad init", { slots: this.cardComps.length, - poolLv: this.poolLv }); } @@ -193,15 +187,13 @@ export class MissionCardComp extends CCComp { /** * 任务开始: * 1. 进入准备阶段(展开卡牌面板)。 - * 2. 重置卡池等级为初始值。 - * 3. 初始化局内数据(金币、英雄数量上限)。 - * 4. 清空旧英雄信息面板和卡牌槽位。 - * 5. 重置按钮状态和 UI 显示。 - * 6. 执行首次抽卡并分发到 4 个槽位。 + * 2. 初始化局内数据(金币、英雄数量上限)。 + * 3. 清空旧英雄信息面板和卡牌槽位。 + * 4. 重置按钮状态和 UI 显示。 + * 5. 执行首次抽卡并分发到 3 个槽位。 */ onMissionStart() { this.enterPreparePhase(); - this.poolLv = CARD_POOL_INIT_LEVEL; const missionData = this.getMissionData(); if (missionData) { missionData.coin = Math.max(0, Math.floor(missionData.coin ?? 0)); @@ -217,11 +209,7 @@ export class MissionCardComp extends CCComp { this.layoutCardSlots(); this.clearAllCards(); - // if (this.cards_up) { - // this.cards_up.active = true; - // } this.resetButtonScale(this.cards_chou); - // this.resetButtonScale(this.cards_up); this.updateCoinAndCostUI(); this.updateHeroNumUI(false, false); if (this.node && this.node.isValid) { @@ -235,12 +223,10 @@ export class MissionCardComp extends CCComp { this.showSkillCardPopup(); } - mLogger.log(this.debugMode, "MissionCardComp", "mission start", { - poolLv: this.poolLv - }); + mLogger.log(this.debugMode, "MissionCardComp", "mission start"); } - /** 任务结束:清空 4 槽 + 英雄面板并隐藏整个节点 */ + /** 任务结束:清空 3 槽 + 英雄面板并隐藏整个节点 */ onMissionEnd() { this.clearAllCards(); if (this.node && this.node.isValid) { @@ -271,7 +257,7 @@ export class MissionCardComp extends CCComp { * 绑定所有事件监听: * - 节点级事件:MissionStart / MissionEnd / NewWave / FightStart * - 全局消息:CoinAdd / MasterCalled / HeroDead / UseHeroCard / UseSpecialCard - * - 按钮触控:抽卡(cards_chou)、升级(cards_up) + * - 按钮触控:抽卡(cards_chou) */ private bindEvents() { /** 生命周期事件(节点级) */ @@ -288,10 +274,9 @@ export class MissionCardComp extends CCComp { oops.message.on(GameEvent.UseHeroCard, this.onUseHeroCard, this); oops.message.on(GameEvent.UseSkillCard, this.onUseSkillCard, this); oops.message.on(GameEvent.UseSpecialCard, this.onUseSpecialCard, this); - oops.message.on(GameEvent.CardPoolUpgrade, this.onCardPoolUpgrade, this); oops.message.on(GameEvent.ShowSmallTip, this.onShowSmallTip, this); - /** 按钮触控事件:抽卡与卡池升级 */ + /** 按钮触控事件:抽卡 */ this.cards_chou?.on(NodeEventType.TOUCH_START, this.onDrawTouchStart, this); this.cards_chou?.on(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this); this.cards_chou?.on(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, this); @@ -303,9 +288,6 @@ export class MissionCardComp extends CCComp { this.skill_ad_refresh?.on(NodeEventType.TOUCH_START, this.onSkillAdDrawTouchStart, this); this.skill_ad_refresh?.on(NodeEventType.TOUCH_END, this.onSkillAdDrawTouchEnd, this); this.skill_ad_refresh?.on(NodeEventType.TOUCH_CANCEL, this.onSkillAdDrawTouchCancel, this); - // this.cards_up?.on(NodeEventType.TOUCH_START, this.onUpgradeTouchStart, this); - // this.cards_up?.on(NodeEventType.TOUCH_END, this.onUpgradeTouchEnd, this); - // this.cards_up?.on(NodeEventType.TOUCH_CANCEL, this.onUpgradeTouchCancel, this); } // ======================== 事件回调 ======================== @@ -333,47 +315,17 @@ export class MissionCardComp extends CCComp { } } - /** - * 接收卡池升级事件: - * - 更新卡池等级 - * - 更新UI显示 - */ - private onCardPoolUpgrade(event: string, args: any) { - const targetLv = args?.targetLv; - if (!targetLv) return; - - if (targetLv > CARD_POOL_MAX_LEVEL) { - this.poolLv = CARD_POOL_MAX_LEVEL; - } else { - this.poolLv = targetLv; - } - - mLogger.log(this.debugMode, "MissionCardComp", "onCardPoolUpgrade", { - targetLv, - poolLv: this.poolLv - }); - - // 提示卡池升级 - this.showSmallTip("pool_upgrade"); - - // 更新UI - this.updatePoolLvUI(); - } - private onShowSmallTip(event: string, args: any) { const type = args as string; this.showSmallTip(type as any); } - public showSmallTip(type: "refresh_coin" | "pool_upgrade" | "buy_coin" | "hero_full") { + public showSmallTip(type: "refresh_coin" | "buy_coin" | "hero_full") { let targetNode: Node | null = null; switch (type) { case "refresh_coin": targetNode = this.cards_chou; break; - case "pool_upgrade": - targetNode = this.pool_lv_node; - break; case "buy_coin": targetNode = this.coins_node; break; @@ -481,14 +433,6 @@ export class MissionCardComp extends CCComp { this.skill_card_node.active = false; } - // 首次完成技能选取后 关闭guide2,打开guide3 - // 之前这里有个逻辑漏洞:玩家如果在弹出 guide2 之前就已经因为手快点掉了 guide2, - // smc.finish_guides 里就会有 2。 - // 但其实这里的本意是:只要发生了选取技能,并且 guide3 还没弹过,就弹 guide3。 - // 如果我们把它包在 `if (!smc.finish_guides.includes(2))` 里, - // 当玩家点击 guide2 把它关掉时(finish_guides 存入了 2), - // 再点技能卡触发这个方法,外层 if 就会进不去,guide3 就永远弹不出来了! - // 修复:独立判断 guide2 的关闭 和 guide3 的开启 if (!smc.finish_guides.includes(2)) { smc.finish_guides.push(2); @@ -500,7 +444,6 @@ export class MissionCardComp extends CCComp { } // 驻场技能可能影响刷新费用(如"刷新优惠"),延迟到下一帧刷新费用 UI - // 确保 MissSkillsComp 已创建 SkillBoxComp 并注册驻场效果 this.scheduleOnce(() => this.updateCoinAndCostUI(), 0); } @@ -513,7 +456,6 @@ export class MissionCardComp extends CCComp { oops.message.off(GameEvent.UseHeroCard, this.onUseHeroCard, this); oops.message.off(GameEvent.UseSkillCard, this.onUseSkillCard, this); oops.message.off(GameEvent.UseSpecialCard, this.onUseSpecialCard, this); - oops.message.off(GameEvent.CardPoolUpgrade, this.onCardPoolUpgrade, this); oops.message.off(GameEvent.ShowSmallTip, this.onShowSmallTip, this); if (this.cards_chou && this.cards_chou.isValid) { this.cards_chou.off(NodeEventType.TOUCH_START, this.onDrawTouchStart, this); @@ -530,9 +472,6 @@ export class MissionCardComp extends CCComp { this.skill_ad_refresh.off(NodeEventType.TOUCH_END, this.onSkillAdDrawTouchEnd, this); this.skill_ad_refresh.off(NodeEventType.TOUCH_CANCEL, this.onSkillAdDrawTouchCancel, this); } - // this.cards_up?.off(NodeEventType.TOUCH_START, this.onUpgradeTouchStart, this); - // this.cards_up?.off(NodeEventType.TOUCH_END, this.onUpgradeTouchEnd, this); - // this.cards_up?.off(NodeEventType.TOUCH_CANCEL, this.onUpgradeTouchCancel, this); } /** @@ -575,28 +514,18 @@ export class MissionCardComp extends CCComp { /** * 使用英雄卡的 guard 校验(由 CardComp 通过 UseHeroCard 事件调用): * - 当前英雄数 < 上限 → 允许使用。 - * - 已满但新卡可触发合成(腾位) → 允许使用。 - * - 已满且不可合成 → 阻止使用(cancel=true),弹 toast。 + * - 已满 → 阻止使用(cancel=true),弹 toast。 + * + * 注意:英雄不再支持合成腾位,满员时一律阻止。 */ private onUseHeroCard(event: string, args: any) { const payload = args ?? event; if (!payload) return; - // 战斗阶段也允许召唤英雄(无需额外费用),仅校验英雄数量上限 - const current = this.getAliveHeroCount(); this.syncMissionHeroData(current); const heroMax = this.getMissionHeroMaxNum(); if (current >= heroMax) { - const heroUuid = Number(payload?.uuid ?? 0); - const heroLv = Math.max(1, Math.floor(Number(payload?.hero_lv ?? 1))); - const cardLv = Math.max(1, Math.floor(Number(payload?.pool_lv ?? 1))); - // 检查是否可以通过合成腾出位置 - if (this.canUseHeroCardByMerge(heroUuid, heroLv)) { - payload.cancel = false; - payload.reason = ""; - return; - } payload.cancel = true; payload.reason = "hero_limit"; this.showSmallTip("hero_full"); @@ -604,53 +533,11 @@ export class MissionCardComp extends CCComp { } } - /** - * 判断新召唤的英雄是否能通过合成腾位: - * 场上同 UUID 同等级数量 + 1(新卡自身)>= 合成所需数量 → 可以合成。 - */ - private canUseHeroCardByMerge(heroUuid: number, heroLv: number): boolean { - if (!heroUuid) return false; - const mergeRule = this.getMergeRule(); - if (heroLv >= mergeRule.maxLv) return false; - const sameCount = this.countAliveHeroesByUuidAndLv(heroUuid, heroLv); - return sameCount + 1 >= mergeRule.needCount; - } - - /** 统计场上同 UUID 同等级的存活英雄数量 */ - private countAliveHeroesByUuidAndLv(heroUuid: number, heroLv: number): number { - let count = 0; - const actors = this.queryAliveHeroActors(); - for (let i = 0; i < actors.length; i++) { - const model = actors[i].model; - if (!model) continue; - if (model.hero_uuid !== heroUuid) continue; - if (model.lv !== heroLv) continue; - count += 1; - } - return count; - } - - /** - * 从 MissionHeroComp 实时读取合成规则。 - * 通过 ECS 查询获取,避免硬编码与 MissionHeroComp 不一致。 - * @returns { needCount: 合成所需数量, maxLv: 最大合成等级 } - */ - private getMergeRule(): { needCount: number, maxLv: number } { - let needCount = FightSet.MERGE_NEED ? FightSet.MERGE_NEED : 2 - let maxLv = Math.max(1, Math.floor(FightSet.MERGE_MAX ?? 3)); - ecs.query(ecs.allOf(MissionHeroComp)).forEach((entity: ecs.Entity) => { - const comp = entity.get(MissionHeroComp); - if (!comp) return; - needCount = comp.merge_need_count === 2 ? 2 : 3; - maxLv = Math.max(1, Math.floor(comp.merge_max_lv ?? 3)); - }); - return { needCount, maxLv }; - } - /** * 使用特殊卡事件回调: - * - SpecialUpgrade:随机选一个指定等级的英雄升级到目标等级。 - * - SpecialRefresh:按英雄类型 / 指定等级重新抽取英雄卡。 + * - SpecialUpgrade:按卡牌携带的 target_hero_uuid 精确升级场上对应英雄。 + * target_hero_uuid 缺失时回退为随机升级一个可升级的英雄。 + * - SpecialRefresh:按英雄类型重新抽取英雄卡。 */ private onUseSpecialCard(event: string, args: any) { const payload = args ?? event; @@ -659,14 +546,17 @@ export class MissionCardComp extends CCComp { if (!uuid) return; let success = false; if (type === CardType.SpecialUpgrade) { - const card = SpecialUpgradeCardList[uuid]; - if (!card) return; - success = this.tryUpgradeOneHero(card.currentLv, card.targetLv); - if (!success) oops.gui.toast(`场上没有可从${card.currentLv}级升到${card.targetLv}级的英雄`); + const template = SpecialUpgradeCardList[uuid]; + if (!template) return; + const targetHeroUuid = Number(payload?.target_hero_uuid ?? 0); + success = this.tryUpgradeHeroByUuid(targetHeroUuid); + if (!success) { + oops.gui.toast(`场上没有可升级的英雄`); + } } else if (type === CardType.SpecialRefresh) { const card = SpecialRefreshCardList[uuid]; if (!card) return; - success = this.tryRefreshHeroCardsByEffect(card.refreshHeroType, card.refreshLv); + success = this.tryRefreshHeroCardsByEffect(card.refreshHeroType); if (!success) oops.gui.toast("当前卡池无符合条件的英雄卡"); } mLogger.log(this.debugMode, "MissionCardComp", "use special card", { @@ -731,20 +621,8 @@ export class MissionCardComp extends CCComp { const cards = this.buildSkillDrawCards(); this.dispatchCardsToSkillSlots(cards); } - // /** 升级按钮按下反馈 */ - // private onUpgradeTouchStart() { - // this.playButtonPressAnim(this.cards_up); - // } - // /** 升级按钮释放 → 执行升级逻辑 */ - // private onUpgradeTouchEnd() { - // this.playButtonClickAnim(this.cards_up, () => this.onClickUpgrade()); - // } - // /** 升级按钮取消 → 恢复缩放 */ - // private onUpgradeTouchCancel() { - // this.playButtonResetAnim(this.cards_up); - // } - /** 将四个卡槽节点映射为 CardComp,形成固定顺序控制数组 */ + /** 将三个卡槽节点映射为 CardComp,形成固定顺序控制数组 */ private cacheCardComps() { if (this.card4) { this.card4.active = false; @@ -760,16 +638,15 @@ export class MissionCardComp extends CCComp { .filter((comp): comp is SCardComp => !!comp); } - // ======================== 核心业务:抽卡 & 升级 ======================== + // ======================== 核心业务:抽卡 ======================== /** * 抽卡按钮核心逻辑: * 1. 检查金币是否足够 → 不够则 toast 提示。 * 2. 扣除费用、播放金币动画。 - * 3. 重新布局槽位 → 从卡池构建 4 张卡 → 分发到槽位。 + * 3. 重新布局槽位 → 从卡池构建 3 张卡 → 分发到槽位。 */ private onClickDraw() { - // 战斗阶段和倒计时阶段均允许刷新抽卡 const cost = MissionEconomy.getRefreshCost(this.refreshCost); const success = MissionEconomy.executeRefresh(this.refreshCost); if (!success) { @@ -783,7 +660,6 @@ export class MissionCardComp extends CCComp { } mLogger.log(this.debugMode, "MissionCardComp", "click draw", { - poolLv: this.poolLv, cost, leftCoin: MissionEconomy.getCoin() }); @@ -792,35 +668,6 @@ export class MissionCardComp extends CCComp { this.dispatchCardsToSlots(cards); } - // /** 升级按钮:仅提升卡池等级,卡槽是否更新由下一次抽卡触发 */ - // private onClickUpgrade() { - // if (this.poolLv >= CARD_POOL_MAX_LEVEL) { - // mLogger.log(this.debugMode, "MissionCardComp", "pool already max", this.poolLv); - // return; - // } - // const cost = this.getUpgradeCost(this.poolLv); - // const currentCoin = this.getMissionCoin(); - // if (currentCoin < cost) { - // oops.gui.toast(`金币不足,升级需要${cost}`); - // this.updateCoinAndCostUI(); - // mLogger.log(this.debugMode, "MissionCardComp", "pool upgrade coin not enough", { - // poolLv: this.poolLv, - // currentCoin, - // cost - // }); - // return; - // } - // this.setMissionCoin(currentCoin - cost); - // this.poolLv += 1; - // this.playCoinChangeAnim(false); - // this.updateCoinAndCostUI(); - // mLogger.log(this.debugMode, "MissionCardComp", "pool level up", { - // poolLv: this.poolLv, - // cost, - // leftCoin: this.getMissionCoin() - // }); - // } - // ======================== 阶段切换 ======================== /** 缓存卡牌面板的基准缩放值(从场景初始状态读取,仅缓存一次) */ @@ -853,7 +700,7 @@ export class MissionCardComp extends CCComp { private enterBattlePhase() { if (!this.cards_node || !this.cards_node.isValid) return; this.initCardsPanelPos(); - // 战斗阶段允许抽卡:nobg 按"金币是否足够"判断,而非强制置灰 + // 战斗阶段允许抽卡:nobg 按"金币是否足够"判断 if (this.cards_chou && this.cards_chou.isValid) { const nobg = this.cards_chou.getChildByName("nobg"); if (nobg) { @@ -862,30 +709,140 @@ export class MissionCardComp extends CCComp { } } - /** 构建本次抽卡结果,保证最终可分发3条数据 */ + /** + * 构建本次抽卡结果,保证最终可分发 3 条数据。 + * + * 抽卡池构成: + * - 动态升级卡:扫描场上存活英雄,每个 UUID 至多生成一张升级卡 + * (已达到 HERO_MAX_LV 的英雄不出卡)。同 UUID 只出一张,避免重复。 + * - 基础英雄卡:所有英雄的 lv1 卡。 + * - 刷新功能卡:SpecialRefresh 卡。 + * + * 三类卡按 weight 权重混合抽 3 张,且升级卡之间 unique 去重。 + * + * 特殊规则:当场存活英雄数已达 HERO_MAX_NUM 时, + * 不再抽取基础英雄卡和刷新卡,只抽取场上已有英雄的升级卡 + * (若全部已满级则降级回混合池,避免卡池为空)。 + */ private buildDrawCards(): CardConfig[] { - const targetType = [CardType.Hero, CardType.SpecialRefresh]; - const cards = getCardsByLv(this.poolLv, targetType); + const upgradeCards = this.buildHeroUpgradeCards(); + const aliveHeroCount = this.getAliveHeroCount(); + const heroMax = this.getMissionHeroMaxNum(); - /** 正常情况下直接取前3 */ - if (cards.length >= 3) return cards.slice(0, 3); - /** 兜底:当返回不足3张时循环补齐,保证分发不缺位 */ - const filled = [...cards]; + // 英雄已满员且有可升级的英雄 → 只出升级卡 + if (aliveHeroCount >= heroMax && upgradeCards.length > 0) { + const picked = this.pickMixedCards(upgradeCards, 3, upgradeCards); + if (picked.length >= 3) return picked.slice(0, 3); + /** 兜底:不足 3 张时循环补齐 */ + const filled = [...picked]; + while (filled.length < 3) { + filled.push(upgradeCards[filled.length % upgradeCards.length]); + } + return filled; + } + + const heroCards = CardPoolList.filter(c => c.type === CardType.Hero); + const refreshCards: CardConfig[] = Object.values(SpecialRefreshCardList); + + const mixedPool: CardConfig[] = [...upgradeCards, ...heroCards, ...refreshCards]; + if (mixedPool.length === 0) return []; + + const picked = this.pickMixedCards(mixedPool, 3, upgradeCards); + if (picked.length >= 3) return picked.slice(0, 3); + + /** 兜底:不足 3 张时循环补齐 */ + const filled = [...picked]; while (filled.length < 3) { - const fallback = getCardsByLv(this.poolLv, targetType); - if (fallback.length === 0) break; - filled.push(fallback[filled.length % fallback.length]); + if (mixedPool.length === 0) break; + filled.push(mixedPool[filled.length % mixedPool.length]); } return filled; } + /** + * 从混合池中按权重抽取 n 张卡。 + * 升级卡之间强制 unique(同一个 target_hero_uuid 只能出现一次)。 + */ + private pickMixedCards(pool: CardConfig[], count: number, upgradeCards: CardConfig[]): CardConfig[] { + if (pool.length === 0 || count <= 0) return []; + const selected: CardConfig[] = []; + const usedUpgradeTargets = new Set(); + + while (selected.length < count) { + const available = pool.filter(c => { + if (c.type === CardType.SpecialUpgrade && c.target_hero_uuid) { + return !usedUpgradeTargets.has(c.target_hero_uuid); + } + return true; + }); + if (available.length === 0) break; + const pick = this.weightedPick(available); + if (!pick) break; + selected.push(pick); + if (pick.type === CardType.SpecialUpgrade && pick.target_hero_uuid) { + usedUpgradeTargets.add(pick.target_hero_uuid); + } + } + return selected; + } + + /** 单次按权重抽取一张卡 */ + private weightedPick(cards: CardConfig[]): CardConfig | null { + if (cards.length === 0) return null; + const totalWeight = cards.reduce((total, card) => total + (card.weight ?? 0), 0); + let random = Math.random() * totalWeight; + for (const card of cards) { + random -= (card.weight ?? 0); + if (random <= 0) return card; + } + return cards[cards.length - 1]; + } + + /** + * 扫描场上存活英雄,为每个 UUID 至多生成一张升级卡。 + * - 已达 HERO_MAX_LV 的英雄不出卡。 + * - 同 UUID 多个英雄只生成一张(取等级最低的那个作为升级目标)。 + * - cost = 模板 cost + (当前等级 - 1) * BASE_COST,等级越高升级越贵。 + */ + private buildHeroUpgradeCards(): CardConfig[] { + const template = SpecialUpgradeCardList[7001]; + if (!template) return []; + + const actors = this.queryAliveHeroActors(); + if (actors.length === 0) return []; + + // 同 UUID 取等级最低的英雄 + const heroMap = new Map(); + for (const actor of actors) { + const uuid = actor.model.hero_uuid; + const lv = actor.model.lv; + const existing = heroMap.get(uuid); + if (!existing || lv < existing.lv) { + heroMap.set(uuid, { uuid, lv }); + } + } + + const result: CardConfig[] = []; + heroMap.forEach(({ uuid, lv }) => { + if (lv >= FightSet.HERO_MAX_LV) return; // 已达上限不再出升级卡 + const dynamicCost = template.cost + (lv - 1) * FightSet.BASE_COST; + result.push({ + ...template, + cost: dynamicCost, + weight: template.weight, + target_hero_uuid: uuid, + hero_lv: lv + 1, + }); + }); + return result; + } + private buildSkillDrawCards(): CardConfig[] { - const targetType = CardType.Skill; const currentWave = this.getCurrentWave(); // 使用明确规则的 drawCardsByRule,指定只要 3 张技能卡,并且过滤对应 wave - const cards = drawCardsByRule(this.poolLv, { + const cards = drawCardsByRule(1, { count: 3, - type: targetType, + type: CardType.Skill, wave: currentWave, unique: true // 保证技能牌不重复 }); @@ -893,9 +850,9 @@ export class MissionCardComp extends CCComp { if (cards.length >= 3) return cards.slice(0, 3); const filled = [...cards]; while (filled.length < 3) { - const fallback = drawCardsByRule(this.poolLv, { + const fallback = drawCardsByRule(1, { count: 3, - type: targetType, + type: CardType.Skill, wave: currentWave, unique: true }); @@ -912,12 +869,11 @@ export class MissionCardComp extends CCComp { return filled; } - private tryRefreshHeroCards(heroType?: HType, targetPoolLv?: number): boolean { - const cards = drawCardsByRule(this.poolLv, { + private tryRefreshHeroCards(heroType?: HType): boolean { + const cards = drawCardsByRule(1, { count: 3, type: CardType.Hero, heroType, - targetPoolLv }); if (cards.length <= 0) return false; this.layoutCardSlots(); @@ -925,10 +881,9 @@ export class MissionCardComp extends CCComp { return true; } - private tryRefreshHeroCardsByEffect(refreshHeroType: SpecialRefreshHeroType, refreshLv: number): boolean { + private tryRefreshHeroCardsByEffect(refreshHeroType: SpecialRefreshHeroType): boolean { const heroType = this.resolveRefreshHeroType(refreshHeroType); - const targetPoolLv = refreshLv > 0 ? refreshLv : undefined; - return this.tryRefreshHeroCards(heroType, targetPoolLv); + return this.tryRefreshHeroCards(heroType); } private resolveRefreshHeroType(refreshHeroType: SpecialRefreshHeroType): HType | undefined { @@ -937,7 +892,7 @@ export class MissionCardComp extends CCComp { return undefined; } - /** 全量分发给4槽;每个槽位是否接收由 CardComp 自己判断(锁定可跳过) */ + /** 全量分发给 3 槽;每个槽位是否接收由 CardComp 自己判断(锁定可跳过) */ private dispatchCardsToSlots(cards: CardConfig[]) { if (!this.cardComps) return; for (let i = 0; i < this.cardComps.length; i++) { @@ -952,7 +907,7 @@ export class MissionCardComp extends CCComp { } } - /** 系统清空4槽(用于任务切换) */ + /** 系统清空 3 槽(用于任务切换) */ private clearAllCards() { if (!this.cardComps) return; this.cardComps.forEach(comp => { @@ -1001,77 +956,10 @@ export class MissionCardComp extends CCComp { Tween.stopAllByTarget(node); node.setScale(this.buttonNormalScale, this.buttonNormalScale, 1); } - private canUpPool() { - if (this.poolLv >= CARD_POOL_MAX_LEVEL) return false; - const currentCoin = MissionEconomy.getCoin(); - return currentCoin >= this.getUpgradeCost(this.poolLv); - } private canDrawCards() { return MissionEconomy.getCoin() >= MissionEconomy.getRefreshCost(this.refreshCost); } - /** 更新升级按钮上的等级文案,反馈当前卡池层级 */ - private updatePoolLvUI() { - if (this.pool_lv_node) { - this.pool_lv_node.active = true; - const lv = Math.max(CARD_POOL_INIT_LEVEL, Math.min(CARD_POOL_MAX_LEVEL, Math.floor(this.poolLv))); - const lvNode = this.pool_lv_node.getChildByName("lv"); - if (lvNode) { - const label = lvNode.getComponent(Label); - if (label) { - label.string = `lv.${lv}`; - } - } - - const nextNode = this.pool_lv_node.getChildByName("next"); - if (nextNode) { - const nextLabel = nextNode.getComponent(Label); - if (nextLabel) { - if (this.poolLv >= CARD_POOL_MAX_LEVEL) { - nextLabel.string = `已满级`; - } else { - // 优先取 MissionComp 运行时配置,缺失时回退到全局常量 - let upgradeWaves: number[] = CARD_POOL_UPGRADE_WAVES; - ecs.query(ecs.allOf(MissionComp)).forEach((entity) => { - const mission = entity.get(MissionComp); - if (mission && mission.cardPoolUpgradeWaves && mission.cardPoolUpgradeWaves.length > 0) { - upgradeWaves = mission.cardPoolUpgradeWaves; - } - }); - - // 已完成的升级次数 = 当前等级 - 初始等级 - // 例:poolLv=2(INIT=1)→ 已升 1 次 → 下一升级对应 upgradeWaves[1] - const upgradedCount = Math.max(0, Math.floor(this.poolLv) - CARD_POOL_INIT_LEVEL); - const currentWave = this.getCurrentWave(); - - if (upgradedCount >= upgradeWaves.length) { - // 配置已耗尽但等级未到上限(配置缺陷) - nextLabel.string = `已满级`; - } else { - const nextWave = upgradeWaves[upgradedCount]; - if (nextWave > currentWave) { - const remain = nextWave - currentWave; - nextLabel.string = `${remain} 回合后升级`; - } else if (nextWave === currentWave) { - // 当前波次正好是升级波次(事件可能即将触发或刚刚触发) - nextLabel.string = `本回合升级`; - } else { - // nextWave < currentWave:异常状态,升级事件未按时触发 - nextLabel.string = `即将升级`; - } - } - } - } - } - - const peak = 1.2 - this.playHeroNumNodePop(this.pool_lv_node, peak); - } - mLogger.log(this.debugMode, "MissionCardComp", "pool lv ui update", { - poolLv: this.poolLv, - cost: this.getUpgradeCost(this.poolLv) - }); - } private updateDrawCostUI() { // 战斗阶段也允许抽卡,nobg 统一按"金币是否足够"判断 @@ -1101,7 +989,6 @@ export class MissionCardComp extends CCComp { } private updateCoinAndCostUI() { - this.updatePoolLvUI(); this.updateDrawCostUI(); } @@ -1116,13 +1003,6 @@ export class MissionCardComp extends CCComp { this.playHeroNumNodePop(num, peak); } - private getUpgradeCost(lv: number): number { - const baseCost = Math.max(0, Math.floor(CardsUpSet[lv] ?? 0)); - const completedWave = Math.max(0, this.getCurrentWave() - 1); - const discount = Math.max(0, Math.floor(CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE)) * completedWave; - return Math.max(0, baseCost - discount); - } - public setHeroMaxCount(max: number) { const missionData = this.getMissionData(); if (!missionData) return; @@ -1171,28 +1051,42 @@ export class MissionCardComp extends CCComp { return actors; } - private tryUpgradeOneHero(currentLv: number, targetLv: number): boolean { - const fromLv = Math.max(1, Math.floor(currentLv)); - const toLv = Math.max(1, Math.floor(targetLv)); - if (toLv <= fromLv) return false; - const candidates = this.queryAliveHeroActors().filter(item => item.model.lv === fromLv); + /** + * 按英雄 UUID 精确升级一个存活英雄。 + * 若同 UUID 多个英雄,取等级最低且未达上限的;若都达上限则失败。 + * + * @param heroUuid 要升级的英雄 UUID + * @returns true = 升级成功 + */ + private tryUpgradeHeroByUuid(heroUuid: number): boolean { + if (!heroUuid) return false; + const candidates = this.queryAliveHeroActors().filter(item => + item.model.hero_uuid === heroUuid && item.model.lv < FightSet.HERO_MAX_LV + ); if (candidates.length === 0) return false; - const target = candidates[Math.floor(Math.random() * candidates.length)]; - this.applyHeroLevel(target.model, toLv); + // 取等级最低的(先升级低等级英雄) + candidates.sort((a, b) => a.model.lv - b.model.lv); + const target = candidates[0]; + const nextLv = Math.min(FightSet.HERO_MAX_LV, target.model.lv + 1); + this.applyHeroLevel(target.model, nextLv); if (target.view) { target.view.playBuff("buff_lvup"); } return true; } + /** + * 应用英雄等级变化(属性按 HERO_LV_MULTIPLIER 重新计算)。 + * 沿用旧版本 SkillLvUp 的技能等级映射规则。 + */ private applyHeroLevel(model: HeroAttrsComp, targetLv: number) { const hero = HeroInfo[model.hero_uuid]; if (!hero) return; - const nextLv = Math.max(1, Math.min(3, Math.floor(targetLv))); + const nextLv = Math.max(1, Math.min(FightSet.HERO_MAX_LV, Math.floor(targetLv))); const hpRate = model.hp_max > 0 ? model.hp / model.hp_max : 1; model.lv = nextLv; - model.ap = hero.ap * nextLv; - model.hp_max = hero.hp * nextLv; + model.ap = hero.ap * Math.pow(FightSet.HERO_LV_MULTIPLIER, nextLv - 1); + model.hp_max = hero.hp * Math.pow(FightSet.HERO_LV_MULTIPLIER, nextLv - 1); model.hp = Math.max(1, Math.floor(model.hp_max * Math.max(0, Math.min(1, hpRate)))); model.skills = {}; for (const key in hero.skills) { @@ -1266,17 +1160,12 @@ export class MissionCardComp extends CCComp { return Math.max(0, Math.floor(missionData?.hero_num ?? 0)); } - - private getCurrentWave(): number { const missionData = this.getMissionData(); return Math.max(1, Math.floor(missionData?.level ?? 1)); } - - private getMissionHeroMaxNum(): number { - return FightSet.HERO_MAX_NUM } @@ -1290,7 +1179,6 @@ export class MissionCardComp extends CCComp { /** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */ reset() { this.resetButtonScale(this.cards_chou); - // this.resetButtonScale(this.cards_up); // 关键:在 reset/销毁 时将 Map 置空,彻底切断引用 this.cardComps = [] as any; diff --git a/assets/script/game/map/MissionComp.ts b/assets/script/game/map/MissionComp.ts index 57bbc5a5..1d1e24c5 100644 --- a/assets/script/game/map/MissionComp.ts +++ b/assets/script/game/map/MissionComp.ts @@ -39,7 +39,7 @@ import { HeroViewComp } from "../hero/HeroViewComp"; import { SkillTriggerHelper } from "../hero/SkillTriggerHelper"; import { UIID } from "../common/config/GameUIConfig"; import { SkillView } from "../skill/SkillView"; -import { FacSet, FightSet, CARD_POOL_UPGRADE_WAVES } from "../common/config/GameSet"; +import { FacSet, FightSet } from "../common/config/GameSet"; import { HeroInfo } from "../common/config/heroSet"; import { mLogger } from "../common/Logger"; import { Monster } from "../hero/Mon"; @@ -86,8 +86,6 @@ export class MissionComp extends CCComp { private maxMonsterCount: number = 80; /** 怪物数量恢复阈值(降至此值以下恢复刷怪) */ private resumeMonsterCount: number = 45; - /** 卡池升级波次配置(默认值来自 GameSet.CARD_POOL_UPGRADE_WAVES,保持全局统一) */ - public cardPoolUpgradeWaves: number[] = CARD_POOL_UPGRADE_WAVES; // ======================== 编辑器绑定节点 ======================== @@ -829,22 +827,6 @@ export class MissionComp extends CCComp { this.lastTimeSecond = -1; this.clearTime = 0; this.update_time(); - - // 检查并推送卡池升级事件 - this.checkCardPoolUpgrade(wave); - } - - /** 检查是否达到卡池升级波次,并推送升级事件 */ - private checkCardPoolUpgrade(wave: number) { - if (!this.cardPoolUpgradeWaves || this.cardPoolUpgradeWaves.length === 0) return; - const upgradeIndex = this.cardPoolUpgradeWaves.indexOf(wave); - if (upgradeIndex !== -1) { - // 根据配置的索引,计算目标等级(初始等级 + index + 1) - // 例如 index=0,对应等级为2;index=1,对应等级为3 - const targetLv = upgradeIndex + 2; - oops.message.dispatchEvent(GameEvent.CardPoolUpgrade, { wave, targetLv }); - mLogger.log(this.debugMode, 'MissionComp', "card pool upgrade event pushed", { wave, targetLv }); - } } // ======================== 怪物数量管理 ======================== diff --git a/assets/script/game/map/MissionHeroComp.ts b/assets/script/game/map/MissionHeroComp.ts index 5177e8bc..cd6b88bc 100644 --- a/assets/script/game/map/MissionHeroComp.ts +++ b/assets/script/game/map/MissionHeroComp.ts @@ -1,29 +1,24 @@ /** * @file MissionHeroComp.ts - * @description 英雄召唤与合成管理组件(逻辑层 + 视图层) + * @description 英雄召唤管理组件(逻辑层 + 视图层) * * 职责: * 1. 处理 **英雄召唤**:接收 CallHero 事件 → 通过串行队列执行召唤。 - * 2. 处理 **英雄合成**:检测同 UUID 同等级英雄是否达到合成条件 → - * 执行合成动画 → 销毁素材 → 生成高一级英雄。 - * 3. 支持 **链式合成**:合成完成后自动检测更高等级是否也满足合成条件。 - * 4. 管理英雄的出生点和掉落动画。 + * 2. 管理英雄的出生点和掉落动画。 * * 关键设计: - * - summon_queue + processSummonQueue() 确保召唤请求 **串行处理**, - * 避免同帧并发导致合成判断错误。 - * - handleSingleSummon() 在每次召唤后检测是否触发合成。 - * - mergeGroupHeroes() 执行完整合成流程: - * 聚合属性 → 向出生点汇聚动画 → 爆点特效 → 生成高级英雄。 - * - merge_need_count 控制合成所需数量(2 合 1 或 3 合 1)。 - * - merge_max_lv 控制合成上限等级。 + * - summon_queue + processSummonQueue() 确保召唤请求 **串行处理**。 + * - handleSingleSummon() 仅负责生成英雄;英雄升级由升级卡系统(MissionCardComp)独立处理。 + * + * 历史: + * 旧版本曾包含"三合一合成 + 链式合成"机制,已移除。 + * 英雄等级提升现在仅通过升级卡(SpecialUpgrade)实现。 * * 依赖: * - Hero(hero/Hero.ts)—— 英雄 ECS 实体类 * - HeroAttrsComp —— 英雄属性组件 * - HeroInfo / HeroPos / HType(heroSet)—— 英雄静态配置 - * - FightSet —— 战斗常量(MERGE_NEED / MERGE_MAX) - * - oneCom —— 一次性特效组件(控制爆点特效生命周期) + * - FightSet —— 战斗常量 */ import { _decorator, instantiate, Prefab, v3, Vec3, BoxCollider2D } from "cc"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; @@ -36,17 +31,16 @@ import { HeroInfo, HeroPos, HType } from "../common/config/heroSet"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { HeroAttrsComp } from "../hero/HeroAttrsComp"; import { FacSet, FightSet, BoxSet } from "../common/config/GameSet"; -import { oneCom } from "../skill/oncend"; import { HeroViewComp } from "../hero/HeroViewComp"; import { FieldSkillSet, FieldSkillType } from "../common/config/SkillSet"; import { MoveComp } from "../hero/MoveComp"; const { ccclass } = _decorator; /** - * MissionHeroComp —— 英雄召唤与合成管理器 + * MissionHeroComp —— 英雄召唤管理器 * - * 管理英雄的召唤请求队列、出生动画和合成系统。 - * 合成支持 2 合 1 或 3 合 1,且可链式合成至上限等级。 + * 管理英雄的召唤请求队列和出生动画。 + * 英雄升级由 MissionCardComp 的升级卡系统统一处理。 */ @ccclass('MissionHeroComp') @ecs.register('MissionHeroComp', false) @@ -76,15 +70,9 @@ export class MissionHeroComp extends CCComp { current_hero_uuid:number=0 /** 当前英雄数量缓存 */ current_hero_num:number=-1 - /** 合成规则:需要几个同级英雄才能合成(2 或 3) */ - merge_need_count:number=FightSet.MERGE_NEED - /** 允许合成的最高等级(合成产物不超过此等级) */ - merge_max_lv:number=FightSet.MERGE_MAX - /** 是否正在执行一次合成流程(防止并发) */ - is_merging:boolean=false /** 是否正在消费召唤队列(防止并发) */ is_processing_queue:boolean=false - /** 召唤请求队列:保证召唤与合成按顺序串行执行 */ + /** 召唤请求队列:保证召唤按顺序串行执行 */ summon_queue:{ uuid: number; hero_lv: number; pool_lv: number }[]=[] /** 预留英雄列表 */ heros:any=[] @@ -134,8 +122,7 @@ export class MissionHeroComp extends CCComp { view.alive(); const posIndex = this.pickPositionIndexForHero([hero.eid]); const landingPos = MissionHeroComp.HERO_POSITIONS[posIndex]; - // 不再直接设置位置,而是播放下落入场动画 - // 计算出出生点(空中) + // 计算出生点(空中) const spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0); view.node.setPosition(spawnPos); model.posIndex = posIndex; @@ -172,7 +159,7 @@ export class MissionHeroComp extends CCComp { /** * 动态分配英雄上场的位置 - * @param excludeEids 排除计算的实体ID数组(避免复活或合成时把自己算成占据的位置) + * @param excludeEids 排除计算的实体ID数组(避免复活时把自己算成占据的位置) */ private pickPositionIndexForHero(excludeEids: number[] = []): number { const heroes = this.getAllHeroes().filter(h => { @@ -205,11 +192,10 @@ export class MissionHeroComp extends CCComp { * * @param uuid 英雄 UUID * @param hero_lv 英雄等级 - * @param pool_lv 卡池等级 + * @param pool_lv 卡池等级(历史遗留,新机制下不再使用) * @returns 创建的 Hero 实体 */ private addHero(uuid:number=1001,hero_lv:number=1, pool_lv:number=1) { - console.log("addHero uuid:",uuid) let hero = ecs.getEntity(Hero); let scale = 1 const posIndex = this.pickPositionIndexForHero(); @@ -229,56 +215,6 @@ export class MissionHeroComp extends CCComp { return hero; } - - - /** - * 生成合成后的高级英雄,并覆盖为聚合后的属性。 - * - * @param uuid 英雄 UUID - * @param hero_lv 合成后等级 - * @param pool_lv 卡池等级 - * @param ap 聚合后攻击力 - * @param hp_max 聚合后最大生命值 - * @param targetPos 指定生成位置 - * @returns 实际生成的英雄等级 - */ - private addMergedHero(uuid:number, hero_lv:number, pool_lv:number, ap:number, hp_max:number, targetPosIndex?: number, targetPos?: Vec3): number { - console.log("addMergedHero uuid:",uuid) - let hero = ecs.getEntity(Hero); - let scale = 1 - - let posIndex = targetPosIndex; - let landingPos = targetPos; - if (posIndex === undefined || posIndex < 0 || !landingPos) { - posIndex = this.pickPositionIndexForHero(); - landingPos = MissionHeroComp.HERO_POSITIONS[posIndex]; - } - let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0); - hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv,pool_lv,posIndex); - - // 召唤完成后,派发事件以更新英雄面板 - const model = hero.get(HeroAttrsComp); - if (model) { - model.ap = Math.max(0, ap); - model.hp_max = Math.max(1, hp_max); - model.hp = model.hp_max; - model.dirty_hp = true; - - // 获取视图组件触发升级特效(包含描边更新) - const view = hero.get(HeroViewComp); - if (view && typeof view['lv_up'] === 'function') { - view['lv_up'](); - } - - oops.message.dispatchEvent(GameEvent.MasterCalled, { - eid: hero.eid, - model: model - }); - return model.lv; - } - return hero_lv; - } - // ======================== 英雄查询 ======================== /** 获取当前全部友方英雄 ECS 实体列表(包括存活和墓地) */ @@ -293,62 +229,6 @@ export class MissionHeroComp extends CCComp { return heroes; } - - - /** - * 从存活英雄中挑选可参与本次合成的英雄组。 - * - * @param aliveHeroes 存活英雄列表 - * @param uuid 目标英雄 UUID - * @param hero_lv 目标等级 - * @param needCount 合成需要数量 - * @returns 匹配的英雄数组(长度 = needCount 或不足) - */ - private pickMergeHeroes(aliveHeroes: Hero[], uuid: number, hero_lv: number, needCount: number = 3): Hero[] { - const mergeHeroes: Hero[] = []; - for (let i = 0; i < aliveHeroes.length; i++) { - const model = aliveHeroes[i].get(HeroAttrsComp); - if (!model) continue; - if (model.hero_uuid !== uuid) continue; - if (model.lv !== hero_lv) continue; - mergeHeroes.push(aliveHeroes[i]); - if (mergeHeroes.length === needCount) break; - } - return mergeHeroes; - } - - /** 统计满足同 UUID 同等级的可合成英雄数量 */ - private countMergeHeroes(aliveHeroes: Hero[], uuid: number, hero_lv: number): number { - let count = 0; - for (let i = 0; i < aliveHeroes.length; i++) { - const model = aliveHeroes[i].get(HeroAttrsComp); - if (!model) continue; - if (model.hero_uuid !== uuid) continue; - if (model.lv !== hero_lv) continue; - count += 1; - } - return count; - } - - // ======================== 合成规则 ======================== - - /** - * 读取合成所需数量(仅支持 2 或 3)。 - * 由 FightSet.MERGE_NEED 配置。 - */ - private getMergeNeedCount(): number { - return this.merge_need_count === 2 ? 2 : 3; - } - - /** - * 判断该等级是否还能继续向上合成。 - * @param hero_lv 当前等级 - * @returns true = 可以合成(未达上限) - */ - private canMergeLevel(hero_lv: number): boolean { - return hero_lv < Math.max(1, this.merge_max_lv); - } - // ======================== 召唤队列 ======================== /** @@ -371,159 +251,16 @@ export class MissionHeroComp extends CCComp { } /** - * 处理单次召唤: - * 1. 生成英雄。 - * 2. 检测是否满足合成条件。 - * 3. 满足则执行合成 + 链式合成。 + * 处理单次召唤:仅生成英雄,不再触发合成。 * * @param uuid 英雄 UUID * @param hero_lv 英雄等级 - * @param pool_lv 卡池等级 + * @param pool_lv 卡池等级(历史遗留) */ private async handleSingleSummon(uuid: number, hero_lv: number, pool_lv: number = 1) { this.addHero(uuid, hero_lv, pool_lv); - if (!this.canMergeLevel(hero_lv)) return; - const needCount = this.getMergeNeedCount(); - const aliveHeroes = this.getAllHeroes(); - const mergeHeroes = this.pickMergeHeroes(aliveHeroes, uuid, hero_lv, needCount); - if (mergeHeroes.length !== needCount) return; - this.is_merging = true; - try { - const mergedLv = await this.mergeGroupHeroes(mergeHeroes, uuid, hero_lv, pool_lv); - await this.tryChainMerge(uuid, mergedLv, pool_lv); - } finally { - this.is_merging = false; - } } - // ======================== 合成动画 ======================== - - /** - * 将一组合成素材英雄向出生点汇聚并销毁。 - * 所有素材动画完成后 Promise resolve。 - * - * @param mergeHeroes 合成素材英雄数组 - * @param spawnPos 汇聚目标位置 - */ - private mergeDestroyAtBirth(mergeHeroes: Hero[], spawnPos: Vec3): Promise { - return new Promise((resolve) => { - let doneCount = 0; - const total = mergeHeroes.length; - if (total <= 0) { - resolve(); - return; - } - const onDone = () => { - doneCount += 1; - if (doneCount >= total) { - resolve(); - } - }; - for (let i = 0; i < mergeHeroes.length; i++) { - mergeHeroes[i].mergeToBirthAndDestroy(spawnPos, onDone); - } - }); - } - - /** - * 播放合成爆点特效(使用 oneCom 控制生命周期)。 - * 延迟 0.4 秒后 resolve。 - * - * @param worldPos 特效播放位置 - */ - private playMergeBoomFx(worldPos: Vec3): Promise { - return new Promise((resolve) => { - const scene = smc.map?.MapView?.scene; - const layer = scene?.entityLayer?.node; - if (!layer || !layer.isValid) { - resolve(); - return; - } - const prefab: Prefab = oops.res.get("game/skill/end/dead", Prefab)!; - if (!prefab) { - resolve(); - return; - } - const fx = instantiate(prefab); - if (!fx || !fx.isValid) { - resolve(); - return; - } - fx.parent = layer; - fx.setPosition(worldPos); - fx.getComponent(oneCom) || fx.addComponent(oneCom); - this.scheduleOnce(() => resolve(), 0.4); - }); - } - - /** - * 执行一次完整合成流程: - * 1. 聚合素材的 AP 和 HP。 - * 2. 将素材向出生点汇聚并销毁。 - * 3. 播放爆点特效。 - * 4. 生成高一级英雄(属性为聚合值)。 - * - * @param mergeHeroes 合成素材 - * @param uuid 英雄 UUID - * @param hero_lv 素材等级 - * @param pool_lv 卡池等级 - * @returns 合成产物的实际等级 - */ - private async mergeGroupHeroes(mergeHeroes: Hero[], uuid: number, hero_lv: number, pool_lv: number): Promise { - // 聚合属性 - let sumAp = 0; - let sumHpMax = 0; - const mergeEids = []; - for (let i = 0; i < mergeHeroes.length; i++) { - const model = mergeHeroes[i].get(HeroAttrsComp); - mergeEids.push(mergeHeroes[i].eid); - if (!model) continue; - sumAp += model.ap; - sumHpMax += model.hp_max; - } - - const posIndex = this.pickPositionIndexForHero(mergeEids); - const landingPos = MissionHeroComp.HERO_POSITIONS[posIndex]; - const spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0); - - // 汇聚 → 特效 → 生成 - await this.mergeDestroyAtBirth(mergeHeroes, spawnPos); - await this.playMergeBoomFx(spawnPos); - return this.addMergedHero(uuid, Math.min(this.merge_max_lv, hero_lv + 1), pool_lv, sumAp, sumHpMax, posIndex, landingPos); - } - - /** - * 链式合成:合成完成后继续检测更高等级是否也满足条件。 - * 最多循环 20 次作为安全上限。 - * - * @param uuid 英雄 UUID - * @param startLv 起始检测等级 - * @param pool_lv 卡池等级 - */ - private async tryChainMerge(uuid: number, startLv: number, pool_lv: number) { - let checkLv = Math.max(1, startLv); - const needCount = this.getMergeNeedCount(); - let guard = 0; - while (guard < 20) { - guard += 1; - if (!this.canMergeLevel(checkLv)) { - break; - } - const aliveHeroes = this.getAllHeroes(); - const sameCount = this.countMergeHeroes(aliveHeroes, uuid, checkLv); - if (sameCount < needCount) { - break; - } - const mergeHeroes = this.pickMergeHeroes(aliveHeroes, uuid, checkLv, needCount); - if (mergeHeroes.length < needCount) { - break; - } - checkLv = await this.mergeGroupHeroes(mergeHeroes, uuid, checkLv, pool_lv); - } - } - - - /** ECS 组件移除时触发(当前不销毁节点,保留引用) */ reset() {