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