refactor: 重构英雄养成与卡牌系统,移除旧合成机制
1. 移除三合一英雄合成与链式合成逻辑,统一使用升级卡进行英雄等级提升 2. 重构卡池系统:移除卡池等级机制,所有英雄卡牌统一为LV1 3. 重构升级逻辑:改为按UUID精确升级场上英雄,动态生成对应升级卡 4. 更新配置常量:拆分并重构成长倍率、英雄上限等战斗配置 5. 简化抽卡逻辑:不再按卡池等级分发卡牌,改为动态混合基础卡与升级卡 6. 清理废弃代码:移除卡池升级相关的UI、逻辑与配置
This commit is contained in:
@@ -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<number, number> = {
|
||||
1: 50,
|
||||
2: 100,
|
||||
@@ -113,70 +126,43 @@ export const CardsUpSet: Record<number, number> = {
|
||||
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<number, number> = {};
|
||||
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<number, number> = {};
|
||||
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<number, SpecialUpgradeCardConfig> = {
|
||||
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<number, SpecialRefreshCardConfig> =
|
||||
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<CardType> => {
|
||||
@@ -382,7 +365,12 @@ const normalizeTypeFilter = (type: CardType | CardType[]): Set<CardType> => {
|
||||
return new Set<CardType>(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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<number, string> = {
|
||||
[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<number, string> = {
|
||||
*/
|
||||
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<number, HSkillInfo> ; // 携带技能ID列表
|
||||
evolve?: Record<number, HeroEvolve>; // 等级进化配置,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<number, HSkillInfo>; // 携带技能ID列表
|
||||
evolve?: Record<number, HeroEvolve>; // 等级进化配置,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<number, heroInfo> = {
|
||||
// ========== 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,
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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. 获取场上最高攻击力的英雄,保证后期奶量/增益绝对够用
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<number>();
|
||||
|
||||
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<number, { uuid: number, lv: number }>();
|
||||
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;
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 怪物数量管理 ========================
|
||||
|
||||
@@ -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>(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>(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<void> {
|
||||
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<void> {
|
||||
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<number> {
|
||||
// 聚合属性
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user