1. 重构英雄属性组件,将静态配置与运行时缓存分离,新增runtime_前缀的运行时解析字段 2. 移除旧的evolve进化配置处理逻辑,改为通过等级解析器动态获取当前等级生效的技能与属性 3. 统一怪物与英雄的技能触发逻辑,均使用运行时缓存字段 4. 更新测试模式配置结构,适配新的分组+等级数组配置格式 5. 优化技能描述生成逻辑,支持新的等级配置结构
626 lines
24 KiB
TypeScript
626 lines
24 KiB
TypeScript
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvReviveEntry, LvBonusEntry } from "../common/config/heroSet";
|
||
import { mLogger } from "../common/Logger";
|
||
import { FacSet, FightSet } from "../common/config/GameSet";
|
||
import { FieldSkillSet, FieldSkillType, SkillOverrides } from "../common/config/SkillSet";
|
||
import { smc } from "../common/SingletonModuleComp";
|
||
import { Attrs } from "../common/config/HeroAttrs";
|
||
import { FieldSkillHelper } from "./FieldSkillHelper";
|
||
import { BuffComp } from "./BuffComp";
|
||
import { BuffList, ModOp } from "../common/config/BuffSet";
|
||
import { buffManager } from "./BuffManager";
|
||
|
||
/** 控制类 buff 配置 id(对应 BuffList) */
|
||
const BUFF_ID_FROST = 7003;
|
||
const BUFF_ID_STUN = 7004;
|
||
@ecs.register('HeroAttrs')
|
||
export class HeroAttrsComp extends ecs.Comp {
|
||
public debugMode: boolean = false;
|
||
private static readonly percentRateThreshold = 1;
|
||
private static readonly minAttackCd = 0.05;
|
||
|
||
Ebus: any = null!
|
||
// ==================== 角色基础信息 ====================
|
||
hero_uuid: number = 1001;
|
||
hero_name: string = "hero";
|
||
lv: number = 1;
|
||
card_lv: number = 1;
|
||
base_card_lv: number = 1;
|
||
type: number = 0; // 0近战 1远程 2辅助
|
||
fac: number = 0; // 0:hero 1:monster
|
||
// ==================== 基础属性(有初始值) ====================
|
||
base_ap: number = 0; // 原始基础攻击(无任何加成)
|
||
base_hp: number = 0; // 原始基础血量(无任何加成)
|
||
ap: number = 0; // 基础攻击
|
||
hp: number = 100; // 基础血量
|
||
hp_max: number = 100; // 最大血量
|
||
speed: number = 100; // 基础移动速度
|
||
dis: number = 100; // 基础距离
|
||
shield: number = 0; // 当前护盾
|
||
|
||
// ==================== 攻击属性 (补充) ====================
|
||
skills: Record<number, HSkillInfo> = {};
|
||
|
||
// ==================== 触发类技能(静态配置,按 s_uuid 分组 + lv 数组) ====================
|
||
[SkillTriggerType.Call]?: TriggerGrouped;
|
||
[SkillTriggerType.Dead]?: TriggerGrouped;
|
||
[SkillTriggerType.FStart]?: TriggerGrouped;
|
||
[SkillTriggerType.FEnd]?: TriggerGrouped;
|
||
[SkillTriggerType.Atking]?: TriggerGrouped;
|
||
[SkillTriggerType.Atked]?: TriggerGrouped;
|
||
/** 驻场光环(按 lv 解锁,存活期间全局生效) */
|
||
[SkillTriggerType.Field]?: LvFieldEntry[];
|
||
[SkillTriggerType.Revive]?: LvReviveEntry[];
|
||
/** 额外攻击/生命加成(按 lv 累加) */
|
||
ap_bonus?: LvBonusEntry[];
|
||
hp_bonus?: LvBonusEntry[];
|
||
|
||
// ==================== 运行时解析缓存(按当前等级 resolve 后的有效列表) ====================
|
||
/** 当前等级下生效的攻击触发条目(供 SkillTriggerHelper 消费) */
|
||
runtime_atking?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||
/** 当前等级下生效的受击触发条目 */
|
||
runtime_atked?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||
/** 当前等级下生效的召唤触发条目 */
|
||
runtime_call?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||
/** 当前等级下生效的死亡触发条目 */
|
||
runtime_dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||
/** 当前等级下生效的战前触发条目 */
|
||
runtime_fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||
/** 当前等级下生效的战后触发条目 */
|
||
runtime_fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||
/** 当前等级下生效的驻场光环 uuid 列表 */
|
||
runtime_field?: number[];
|
||
/** 当前等级下生效的复活配置 */
|
||
runtime_revive?: { s_uuid: number; r_num: number; upr: number };
|
||
|
||
// ==================== 特殊属性 ====================
|
||
critical: number = 0; // 暴击率
|
||
critical_res: number = 0; // 暴击抗性
|
||
freeze_chance: number = 0; // 冰冻概率
|
||
freeze_res: number = 0; // 冰冻抗性
|
||
stun_chance: number = 0; // 击晕概率
|
||
stun_res: number = 0; // 击晕抗性
|
||
knockback_chance: number = 0; // 击退概率
|
||
knockback_distance: number = 0; // 击退距离强化
|
||
knockback_res: number = 0; // 击退抗性
|
||
crit_damage: number = 0; // 额外暴击伤害
|
||
puncture_chance: number = 0; // 穿透概率
|
||
wfuny: number = 0; // 风怒
|
||
|
||
revived_count: number = 0; // 已复活次数
|
||
invincible_time: number = 0;// 无敌时间
|
||
|
||
|
||
boom: boolean = false; // 自爆怪
|
||
|
||
// ==================== 脏标签标记 ====================
|
||
dirty_hp: boolean = false; // 血量变更标记
|
||
dirty_shield: boolean = false; // 护盾变更标记
|
||
dirty_lv: boolean = false; // 等级变更标记
|
||
|
||
// ==================== 技能距离缓存 ====================
|
||
maxSkillDistance: number = 0; // 最远技能攻击距离(缓存,受MP影响)
|
||
minSkillDistance: number = 0; // 最近技能攻击距离(缓存,不受MP影响,用于停止位置判断)
|
||
|
||
// ==================== 阵型位置 ====================
|
||
posIndex: number = -1;
|
||
|
||
// ==================== 标记状态 ====================
|
||
is_dead: boolean = false;
|
||
is_count_dead: boolean = false;
|
||
is_atking: boolean = false; // 是否正在攻击
|
||
is_stop: boolean = false; // 是否正在停止
|
||
is_boss: boolean = false;
|
||
is_big_boss: boolean = false;
|
||
is_master: boolean = false;
|
||
is_friend: boolean = false;
|
||
is_kalami: boolean = false;
|
||
is_reviving: boolean = false; // 是否正在复活中
|
||
// ==================== 计数统计 ====================
|
||
atk_count: number = 0; // 攻击次数
|
||
atked_count: number = 0; // 被攻击次数
|
||
killed_count: number = 0;
|
||
combat_target_eid: number = -1;
|
||
enemy_in_cast_range: boolean = false;
|
||
start() {
|
||
}
|
||
// ==================== BUFF 系统初始化 ====================
|
||
/**
|
||
* 初始化角色的 buff debuff
|
||
* 从 HeroInfo 读取初始配置,建立属性系统
|
||
*/
|
||
initAttrs() {
|
||
}
|
||
/*******************基础属性管理********************/
|
||
|
||
add_hp(value: number) {
|
||
const oldHp = this.hp;
|
||
let addValue = value;
|
||
this.hp += addValue;
|
||
// 血量钳制使用 getFinalHpMax(),让限时 hp_max buff 也生效于血量上限
|
||
this.hp = Math.max(0, Math.min(this.hp, this.getFinalHpMax()));
|
||
this.dirty_hp = true; // ✅ 仅标记需要更新
|
||
if (this.debugMode) {
|
||
mLogger.log(this.debugMode, 'HeroAttrs', ` HP变更: ${this.hero_name}, 变化=${addValue.toFixed(1)}, ${oldHp.toFixed(1)} -> ${this.hp.toFixed(1)}`);
|
||
}
|
||
return addValue;
|
||
}
|
||
add_shield(value: number) {
|
||
const oldShield = this.shield;
|
||
const addValue = Math.max(0, Math.floor(value));
|
||
if (addValue <= 0) return;
|
||
this.shield += addValue;
|
||
this.shield = Math.min(this.shield, FightSet.SHIELD_MAX); // 限制护盾最大层数
|
||
if (this.shield < 0) this.shield = 0;
|
||
this.dirty_shield = true; // 标记护盾需要更新
|
||
if (this.debugMode) {
|
||
mLogger.log(this.debugMode, 'HeroAttrs', ` 护盾次数变更: ${this.hero_name}, 变化=${addValue}, ${Math.floor(oldShield)} -> ${Math.floor(this.shield)}`);
|
||
}
|
||
}
|
||
add_hp_max(value: number) {
|
||
this.hp_max += value
|
||
this.hp += value
|
||
this.dirty_hp = true; // ✅ 仅标记需要更新
|
||
return value
|
||
}
|
||
|
||
add_ap(value: number) {
|
||
this.ap += value
|
||
return value
|
||
}
|
||
|
||
/**
|
||
* 统一的特殊/固定属性数值增加方法
|
||
* @param attr_type 属性类型枚举
|
||
* @param value 增加的数值
|
||
*/
|
||
add_special_attr(attr_type: Attrs, value: number) {
|
||
// 利用枚举值(字符串)与类属性名一致的特性,动态访问并累加属性
|
||
const key = attr_type as keyof this;
|
||
|
||
// 确保目标属性存在且类型为数字,避免运行时错误
|
||
if (typeof this[key] === 'number') {
|
||
(this as any)[key] += value;
|
||
} else {
|
||
if (this.debugMode) {
|
||
mLogger.log(this.debugMode, 'HeroAttrs', `未找到对应数字属性或无法累加: attr_type=${attr_type}, value=${value}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/** 施加冰冻:委托 buffManager 写入 BuffComp,duration = FROST_TIME * time */
|
||
toFrost(time: number = 1) {
|
||
if (!this.ent) return;
|
||
buffManager.applyBuff(this.ent, BUFF_ID_FROST, 0, 0, { duration: FightSet.FROST_TIME * time });
|
||
}
|
||
|
||
/** 施加击晕:委托 buffManager 写入 BuffComp,并清零技能 CD */
|
||
toStun(time: number = 1) {
|
||
if (!this.ent) return;
|
||
buffManager.applyBuff(this.ent, BUFF_ID_STUN, 0, 0, { duration: FightSet.STUN_TIME * time });
|
||
|
||
// 击晕时 CD 清零
|
||
for (const key in this.skills) {
|
||
const skill = this.skills[key];
|
||
if (skill) {
|
||
skill.ccd = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
updateCD(dt: number) {
|
||
// 如果处于冰冻状态,则技能 CD 暂停刷新
|
||
if (this.isFrost()) return;
|
||
|
||
// 如果处于击晕状态,则技能 CD 暂停刷新(且保持清零状态)
|
||
if (this.isStun()) {
|
||
for (const key in this.skills) {
|
||
const skill = this.skills[key];
|
||
if (skill) {
|
||
skill.ccd = 0;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
for (const key in this.skills) {
|
||
const skill = this.skills[key];
|
||
if (!skill) continue;
|
||
const actualCd = this.getEffectiveSkillCd(skill.uuid);
|
||
if (actualCd <= 0) {
|
||
skill.ccd = 0;
|
||
continue;
|
||
}
|
||
if (skill.ccd >= actualCd) {
|
||
skill.ccd = actualCd;
|
||
continue;
|
||
}
|
||
skill.ccd = Math.min(actualCd, skill.ccd + dt);
|
||
}
|
||
}
|
||
/** 是否处于冰冻状态(统一走 BuffComp 判定) */
|
||
isFrost(): boolean {
|
||
return !!this.ent && buffManager.hasControl(this.ent, 'frost');
|
||
}
|
||
/** 是否处于击晕状态(统一走 BuffComp 判定) */
|
||
isStun(): boolean {
|
||
return !!this.ent && buffManager.hasControl(this.ent, 'stun');
|
||
}
|
||
getSkillLevel(skillId: number): number {
|
||
if (!skillId) return 0;
|
||
return this.skills[skillId]?.lv ?? 0;
|
||
}
|
||
|
||
getSkillIds(): number[] {
|
||
return Object.values(this.skills).map(skill => skill.uuid);
|
||
}
|
||
|
||
isSkillReady(skillId: number): boolean {
|
||
if (!skillId) return false;
|
||
const skill = this.skills[skillId];
|
||
if (!skill) return false;
|
||
const actualCd = this.getEffectiveSkillCd(skillId);
|
||
if (actualCd <= 0) return true;
|
||
return skill.ccd >= actualCd;
|
||
}
|
||
|
||
triggerSkillCD(skillId: number) {
|
||
if (!skillId) return;
|
||
const skill = this.skills[skillId];
|
||
if (!skill) return;
|
||
skill.ccd = 0;
|
||
}
|
||
|
||
getSkillCdProgress(skillId: number): number {
|
||
if (!skillId) return 1;
|
||
const skill = this.skills[skillId];
|
||
const actualCd = this.getEffectiveSkillCd(skillId);
|
||
if (!skill || actualCd <= 0) return 1;
|
||
return Math.max(0, Math.min(1, skill.ccd / actualCd));
|
||
}
|
||
|
||
getDisplaySkillCdProgress(): number {
|
||
const skillIds = this.getSkillIds();
|
||
const displaySkillId = skillIds[1] ?? skillIds[0] ?? 0;
|
||
return this.getSkillCdProgress(displaySkillId);
|
||
}
|
||
|
||
/** 将驻场配置值统一换算成百分比数值,兼容 0.2 和 20 两种写法。 */
|
||
public static getFieldPercentValue(type: FieldSkillType): number {
|
||
const rawValue = FieldSkillHelper.getFieldSkillTotalValue(type);
|
||
if (Math.abs(rawValue) <= HeroAttrsComp.percentRateThreshold) {
|
||
return rawValue * 100;
|
||
}
|
||
return rawValue;
|
||
}
|
||
|
||
/** 英雄实时暴击率 = 基础暴击率 + 驻场暴击率。 */
|
||
public getRuntimeCritical(): number {
|
||
if (this.fac !== FacSet.HERO) return this.critical;
|
||
return this.critical + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroCrit);
|
||
}
|
||
|
||
/** 英雄实时冰冻率 = 基础冰冻率 + 驻场冰冻率。 */
|
||
public getRuntimeFreezeChance(): number {
|
||
if (this.fac !== FacSet.HERO) return this.freeze_chance;
|
||
return this.freeze_chance;
|
||
}
|
||
|
||
/** 英雄实时击晕率 = 基础击晕率 + 驻场击晕率。 */
|
||
public getRuntimeStunChance(): number {
|
||
if (this.fac !== FacSet.HERO) return this.stun_chance;
|
||
return this.stun_chance + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroStun);
|
||
}
|
||
|
||
/** 英雄实时风怒概率 = 基础风怒 + 驻场风怒。 */
|
||
public getRuntimeWindFury(): number {
|
||
if (this.fac !== FacSet.HERO) return this.wfuny;
|
||
return this.wfuny + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroWindFury);
|
||
}
|
||
|
||
/** 英雄实时穿透概率 = 基础穿透 + 驻场穿透。 */
|
||
public getRuntimePunctureChance(): number {
|
||
if (this.fac !== FacSet.HERO) return this.puncture_chance;
|
||
return this.puncture_chance + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroPuncture);
|
||
}
|
||
|
||
/** 英雄实时暴击伤害 = 基础额外暴伤 + 驻场暴伤。 */
|
||
public getRuntimeCritDamageBonus(): number {
|
||
if (this.fac !== FacSet.HERO) return this.crit_damage;
|
||
return this.crit_damage + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroCritDamage);
|
||
}
|
||
|
||
/** 攻速加成通过缩短普通攻击技能 CD 生效,正值越高,攻击越快。 */
|
||
public getRuntimeAttackSpeedBonus(): number {
|
||
if (this.fac !== FacSet.HERO) return 0;
|
||
return HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroSpeed);
|
||
}
|
||
|
||
/** 英雄实时攻击力 = 基础攻击 × (1 + 驻场攻击百分比)。 */
|
||
public getRuntimeAp(baseAp: number): number {
|
||
if (this.fac !== FacSet.HERO) return baseAp;
|
||
return baseAp * (1 + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroAtk) / 100);
|
||
}
|
||
|
||
/** 英雄实时最大生命 = 基础生命 × (1 + 驻场生命百分比)。 */
|
||
public getRuntimeHp(baseHp: number): number {
|
||
if (this.fac !== FacSet.HERO) return baseHp;
|
||
return baseHp * (1 + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroHp) / 100);
|
||
}
|
||
|
||
// ==================== 计时性 Buff 最终值 ====================
|
||
//
|
||
// 以下方法在驻场属性的基础上,叠加 BuffComp 中计时性 buff/debuff 的修饰器,
|
||
// 输出"最终生效值"。设计依据:ECSComp 持有 ent 字段指向所属 Entity,
|
||
// 因此可直接通过 this.ent.get(BuffComp) 取到同实体上的 buff 组件。
|
||
|
||
/**
|
||
* 聚合目标身上所有活跃 buff 对指定属性的计时性修饰。
|
||
* 每层 buff 独立贡献其配置中的 modifiers。
|
||
*
|
||
* @param attr 目标属性枚举
|
||
* @returns flatSum=Flat 修饰总和;pctSum=百分比修饰总和(百分点)
|
||
*/
|
||
private aggregateTimedMods(attr: Attrs): { flatSum: number; pctSum: number } {
|
||
const result = { flatSum: 0, pctSum: 0 };
|
||
if (!this.ent) return result;
|
||
|
||
const buffComp = this.ent.get(BuffComp);
|
||
if (!buffComp || buffComp.buffs.size === 0) return result;
|
||
|
||
buffComp.buffs.forEach((layers, buffId) => {
|
||
const cfg = BuffList[buffId];
|
||
if (!cfg || !cfg.modifiers) return;
|
||
// 每层独立贡献,模拟叠层放大效果
|
||
for (let i = 0; i < layers.length; i++) {
|
||
const layerVal = layers[i].value_override; // 药水卡覆写值优先
|
||
for (const mod of cfg.modifiers) {
|
||
if (mod.attr !== attr) continue;
|
||
const value = layerVal !== undefined ? layerVal : mod.value;
|
||
mLogger.log(this.debugMode, "HeroAttrs", `aggregateTimedMods 命中: ${this.hero_name} buff=${cfg.name} attr=${attr} op=${mod.op} value=${value} remaining=${layers[i].remaining.toFixed(1)}`);
|
||
if (mod.op === ModOp.Flat) {
|
||
result.flatSum += value;
|
||
} else {
|
||
// PercentAdd 与 PercentMul 暂统一按加法百分比聚合;
|
||
// PercentMul 真正的独立乘区尚未启用,TODO: 启用时需改造为多乘区连乘
|
||
result.pctSum += value;
|
||
}
|
||
}
|
||
}
|
||
});
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 最终攻击力 = (基础攻击 + Flat 修饰) × (1 + 百分比修饰 / 100)。
|
||
* 融合驻场加成与计时性 buff 修饰。
|
||
*/
|
||
public getFinalAp(): number {
|
||
const runtimeAp = this.getRuntimeAp(this.ap);
|
||
const mods = this.aggregateTimedMods(Attrs.ap);
|
||
const final = (runtimeAp + mods.flatSum) * (1 + mods.pctSum / 100);
|
||
if (this.debugMode && (mods.flatSum !== 0 || mods.pctSum !== 0)) {
|
||
mLogger.log(this.debugMode, "HeroAttrs", `getFinalAp: ${this.hero_name} base=${this.ap} runtime=${runtimeAp.toFixed(1)} flat=${mods.flatSum} pct=${mods.pctSum} final=${final.toFixed(1)}`);
|
||
}
|
||
return final;
|
||
}
|
||
|
||
/**
|
||
* 最终最大生命 = (基础最大生命 + Flat 修饰) × (1 + 百分比修饰 / 100)。
|
||
* 融合驻场加成与计时性 buff 修饰。
|
||
*/
|
||
public getFinalHpMax(): number {
|
||
const runtimeHp = this.getRuntimeHp(this.hp_max);
|
||
const mods = this.aggregateTimedMods(Attrs.hp_max);
|
||
return (runtimeHp + mods.flatSum) * (1 + mods.pctSum / 100);
|
||
}
|
||
|
||
/**
|
||
* 最终暴击率 = 驻场实时暴击率 + Flat/百分比修饰之和。
|
||
* 暴击率是加法模型,Flat 和 PercentAdd 均直接累加到百分点。
|
||
*/
|
||
public getFinalCritical(): number {
|
||
const base = this.getRuntimeCritical();
|
||
const mods = this.aggregateTimedMods(Attrs.critical);
|
||
return base + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终冰冻率 = 驻场实时冰冻率 + 限时修饰(加法模型) */
|
||
public getFinalFreezeChance(): number {
|
||
const base = this.getRuntimeFreezeChance();
|
||
const mods = this.aggregateTimedMods(Attrs.freeze_chance);
|
||
return base + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终击晕率 = 驻场实时击晕率 + 限时修饰(加法模型) */
|
||
public getFinalStunChance(): number {
|
||
const base = this.getRuntimeStunChance();
|
||
const mods = this.aggregateTimedMods(Attrs.stun_chance);
|
||
return base + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终击退概率 = 基础击退 + 限时修饰(加法模型) */
|
||
public getFinalKnockbackChance(): number {
|
||
const mods = this.aggregateTimedMods(Attrs.knockback_chance);
|
||
return this.knockback_chance + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终击退距离 = 基础击退距离 + 限时修饰(加法模型) */
|
||
public getFinalKnockbackDistance(): number {
|
||
const mods = this.aggregateTimedMods(Attrs.knockback_distance);
|
||
return this.knockback_distance + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终额外暴击伤害 = 驻场实时暴伤 + 限时修饰(加法模型) */
|
||
public getFinalCritDamage(): number {
|
||
const base = this.getRuntimeCritDamageBonus();
|
||
const mods = this.aggregateTimedMods(Attrs.critical_damage);
|
||
return base + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终穿透概率 = 驻场实时穿透 + 限时修饰(加法模型) */
|
||
public getFinalPunctureChance(): number {
|
||
const base = this.getRuntimePunctureChance();
|
||
const mods = this.aggregateTimedMods(Attrs.puncture_chance);
|
||
return base + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终风怒概率 = 驻场实时风怒 + 限时修饰(加法模型) */
|
||
public getFinalWindFury(): number {
|
||
const base = this.getRuntimeWindFury();
|
||
const mods = this.aggregateTimedMods(Attrs.wfuny);
|
||
return base + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终暴击抗性 = 基础 + 限时修饰(加法模型) */
|
||
public getFinalCriticalRes(): number {
|
||
const mods = this.aggregateTimedMods(Attrs.critical_res);
|
||
return this.critical_res + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终冰冻抗性 = 基础 + 限时修饰(加法模型) */
|
||
public getFinalFreezeRes(): number {
|
||
const mods = this.aggregateTimedMods(Attrs.freeze_res);
|
||
return this.freeze_res + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终击晕抗性 = 基础 + 限时修饰(加法模型) */
|
||
public getFinalStunRes(): number {
|
||
const mods = this.aggregateTimedMods(Attrs.stun_res);
|
||
return this.stun_res + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 最终击退抗性 = 基础 + 限时修饰(加法模型) */
|
||
public getFinalKnockbackRes(): number {
|
||
const mods = this.aggregateTimedMods(Attrs.knockback_res);
|
||
return this.knockback_res + mods.flatSum + mods.pctSum;
|
||
}
|
||
|
||
/** 根据攻速加成换算实际攻击间隔,避免直接改写配置里的基础 CD。 */
|
||
public getEffectiveSkillCd(skillId: number): number {
|
||
const skill = this.skills[skillId];
|
||
if (!skill) return 0;
|
||
if (skill.cd <= 0) return 0;
|
||
// 攻速 = 驻场加成 + 计时性 buff 修饰(speed 属性的 flat/pct 均按攻速百分点处理)
|
||
const timedMods = this.aggregateTimedMods(Attrs.speed);
|
||
const speedBonus = this.getRuntimeAttackSpeedBonus() + timedMods.flatSum + timedMods.pctSum;
|
||
if (speedBonus <= 0) return skill.cd;
|
||
const speedRate = 1 + speedBonus / 100;
|
||
return Math.max(HeroAttrsComp.minAttackCd, skill.cd / speedRate);
|
||
}
|
||
|
||
|
||
|
||
// ==================== 技能距离缓存管理 ====================
|
||
/**
|
||
* 更新技能距离缓存
|
||
* 在技能初始化、新增技能、MP变化时调用
|
||
* @param skillsComp 技能组件
|
||
*/
|
||
public updateSkillDistanceCache(): void {
|
||
const maxRange = this.dis;
|
||
let minRange = 0;
|
||
this.maxSkillDistance = maxRange;
|
||
this.minSkillDistance = minRange;
|
||
}
|
||
|
||
/**
|
||
* 获取缓存的最远技能攻击距离
|
||
* @returns 最远攻击距离
|
||
*/
|
||
public getCachedMaxSkillDistance(): number {
|
||
return this.maxSkillDistance;
|
||
}
|
||
|
||
/**
|
||
* 获取缓存的最近技能攻击距离
|
||
* @returns 最近攻击距离
|
||
*/
|
||
public getCachedMinSkillDistance(): number {
|
||
return this.minSkillDistance;
|
||
}
|
||
|
||
reset() {
|
||
// 重置为初始状态
|
||
this.hero_uuid = 1001;
|
||
this.hero_name = "hero";
|
||
this.lv = 1;
|
||
this.type = 0;
|
||
this.fac = 0;
|
||
this.base_ap = 0;
|
||
this.base_hp = 0;
|
||
this.ap = 0;
|
||
this.hp = 100;
|
||
this.hp_max = 100;
|
||
this.speed = 100;
|
||
this.dis = 100;
|
||
this.shield = 0;
|
||
|
||
// 重置新增属性
|
||
this.skills = {};
|
||
this.call = undefined;
|
||
this.dead = undefined;
|
||
this.fstart = undefined;
|
||
this.fend = undefined;
|
||
this.atking = undefined;
|
||
this.atked = undefined;
|
||
this.field = undefined;
|
||
this.revive = undefined;
|
||
this.ap_bonus = undefined;
|
||
this.hp_bonus = undefined;
|
||
this.runtime_atking = undefined;
|
||
this.runtime_atked = undefined;
|
||
this.runtime_call = undefined;
|
||
this.runtime_dead = undefined;
|
||
this.runtime_fstart = undefined;
|
||
this.runtime_fend = undefined;
|
||
this.runtime_field = undefined;
|
||
this.runtime_revive = undefined;
|
||
this.critical = 0;
|
||
this.critical_res = 0;
|
||
this.freeze_chance = 0;
|
||
this.freeze_res = 0;
|
||
this.stun_chance = 0;
|
||
this.stun_res = 0;
|
||
this.knockback_chance = 0;
|
||
this.knockback_distance = 0;
|
||
this.knockback_res = 0;
|
||
this.crit_damage = 0;
|
||
this.revived_count = 0;
|
||
this.invincible_time = 0;
|
||
this.puncture_chance = 0;
|
||
this.wfuny = 0;
|
||
this.boom = false;
|
||
|
||
// 重置技能距离缓存
|
||
this.maxSkillDistance = 0;
|
||
this.minSkillDistance = 0;
|
||
|
||
this.posIndex = -1;
|
||
|
||
this.is_dead = false;
|
||
this.is_count_dead = false;
|
||
this.is_atking = false;
|
||
this.is_stop = false;
|
||
this.is_boss = false;
|
||
this.is_big_boss = false;
|
||
this.is_friend = false;
|
||
this.is_kalami = false;
|
||
this.is_reviving = false;
|
||
|
||
this.atk_count = 0;
|
||
this.atked_count = 0;
|
||
this.killed_count = 0;
|
||
this.combat_target_eid = -1;
|
||
this.enemy_in_cast_range = false;
|
||
// 重置脏标签
|
||
this.dirty_hp = false;
|
||
this.dirty_shield = false;
|
||
this.dirty_lv = false;
|
||
}
|
||
}
|
||
|
||
|
||
|