Files
pixelheros/assets/script/game/hero/HeroAttrsComp.ts
walkpan e194132731 feat: 新增暴击抗性和冰冻抗性属性并完善暴击冰冻判定
1.  在HeroAttrs枚举中新增critical_res和freeze_res属性
2.  在HeroAttrsComp中添加对应抗性属性并在重置方法中初始化
3.  修改暴击和冰冻判定逻辑,加入抗性减免计算
2026-05-14 22:53:15 +08:00

398 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroDisVal, HeroInfo, HSkillInfo, HType } from "../common/config/heroSet";
import { mLogger } from "../common/Logger";
import { Timer } from "db://oops-framework/core/common/timer/Timer";
import { FacSet, FightSet } from "../common/config/GameSet";
import { FieldSkillSet, FieldSkillType } from "../common/config/SkillSet";
import { smc } from "../common/SingletonModuleComp";
import { TalentConfig, TalentType } from "../common/config/TalentSet";
@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;
pool_lv: number = 1;
type: number = 0; // 0近战 1远程 2辅助
fac: number = 0; // 0:hero 1:monster
// ==================== 基础属性(有初始值) ====================
ap: number = 0; // 基础攻击
hp: number = 100; // 基础血量
hp_max: number = 100; // 最大血量
speed: number = 100; // 基础移动速度
dis: number = 100; // 基础距离
shield: number = 0; // 当前护盾
// ==================== 攻击属性 (补充) ====================
skills: Record<number, HSkillInfo> = {};
// ==================== 触发类技能 ====================
call?: number[];
dead?: number[];
fstart?: number[];
fend?: number[];
atking?: {s_uuid: number, t_num: number}[];
atked?: {s_uuid: number, t_num: number}[];
revive?: {s_uuid: number, r_num: number, upr: number};
// ==================== 特殊属性 ====================
critical: number = 0; // 暴击率
critical_res: number = 0; // 暴击抗性
freeze_chance: number = 0; // 冰冻概率
freeze_res: number = 0; // 冰冻抗性
knockback_chance: number = 0; // 击退概率
knockback_distance: number = 0; // 击退距离强化
knockback_res: number = 0; // 击退抗性
crit_damage: number = 0; // 额外暴击伤害
puncture: number = 0; // 穿刺次数
wfuny: number = 0; // 风怒
revived_count: number = 0; // 已复活次数
invincible_time: number = 0;// 无敌时间
frost_end_time: number = 0;
boom: boolean = false; // 自爆怪
// ==================== 脏标签标记 ====================
dirty_hp: boolean = false; // 血量变更标记
dirty_shield: boolean = false; // 护盾变更标记
// ==================== 技能距离缓存 ====================
maxSkillDistance: number = 0; // 最远技能攻击距离缓存受MP影响
minSkillDistance: number = 0; // 最近技能攻击距离缓存不受MP影响用于停止位置判断
// ==================== 阵型位置 ====================
// ==================== 标记状态 ====================
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() {
this.frost_end_time = 0;
}
/*******************基础属性管理********************/
add_hp(value:number){
const oldHp = this.hp;
let addValue = value;
this.hp += addValue;
this.hp = Math.max(0, Math.min(this.hp, this.hp_max));
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
}
toFrost(time: number=1) {
const frostTime = FightSet.FROST_TIME * time;
this.frost_end_time = Math.max(this.frost_end_time, frostTime);
}
updateCD(dt: number){
// 如果处于冰冻状态,则技能 CD 暂停刷新
if (this.isFrost()) 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);
}
}
isFrost(): boolean {
return this.frost_end_time > 0
}
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 两种写法。 */
private getFieldPercentValue(type: FieldSkillType): number {
const rawValue = HeroAttrsComp.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 + this.getFieldPercentValue(FieldSkillType.HeroCrit);
}
/** 英雄实时冰冻率 = 基础冰冻率 + 驻场冰冻率。 */
public getRuntimeFreezeChance(): number {
if (this.fac !== FacSet.HERO) return this.freeze_chance;
return this.freeze_chance + this.getFieldPercentValue(FieldSkillType.HeroFrost);
}
/** 英雄实时暴击伤害 = 基础额外暴伤 + 驻场暴伤。 */
public getRuntimeCritDamageBonus(): number {
if (this.fac !== FacSet.HERO) return this.crit_damage;
return this.crit_damage + this.getFieldPercentValue(FieldSkillType.HeroCritDamage);
}
/** 攻速加成通过缩短普通攻击技能 CD 生效,正值越高,攻击越快。 */
public getRuntimeAttackSpeedBonus(): number {
if (this.fac !== FacSet.HERO) return 0;
return this.getFieldPercentValue(FieldSkillType.HeroSpeed);
}
/** 根据攻速加成换算实际攻击间隔,避免直接改写配置里的基础 CD。 */
public getEffectiveSkillCd(skillId: number): number {
const skill = this.skills[skillId];
if (!skill) return 0;
if (skill.cd <= 0) return 0;
const speedBonus = this.getRuntimeAttackSpeedBonus();
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 rangeType = this.type as HType.Melee | HType.Mid | HType.Long;
const maxRange = HeroDisVal[rangeType];
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.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.revive = undefined;
this.critical = 0;
this.critical_res = 0;
this.freeze_chance = 0;
this.freeze_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 = 0;
this.wfuny = 0;
this.boom = false;
this.frost_end_time = 0;
// 重置技能距离缓存
this.maxSkillDistance = 0;
this.minSkillDistance = 0;
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;
}
/** 获取指定驻场技能类型的总加成值(只计算存活的英雄) */
public static getFieldSkillTotalValue(type: FieldSkillType): number {
let total = 0;
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
const model = entity.get(HeroAttrsComp);
if (!model || model.is_dead || model.fac !== FacSet.HERO) return;
const heroConfig = HeroInfo[model.hero_uuid];
if (heroConfig && heroConfig.field) {
for (const skillUuid of heroConfig.field) {
const skillConfig = FieldSkillSet[skillUuid];
if (skillConfig && skillConfig.type === type) {
total += skillConfig.value;
}
}
}
});
return total;
}
/** 获取指定天赋的加成数值 */
public static getTalentValue(talentId: TalentType): number {
if (!smc || !smc.collection || !smc.collection.talents) return 0;
let level = smc.collection.talents[talentId] || 0;
if (level <= 0) return 0;
let talentInfo = TalentConfig.talents.find(t => t.id === talentId);
if (!talentInfo || !talentInfo.values || level > talentInfo.values.length) return 0;
return talentInfo.values[level - 1];
}
}
@ecs.register('HeroBuffSystem')
export class HeroBuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
private timer =new Timer(0.1)
filter(): ecs.IMatcher {
return ecs.allOf(HeroAttrsComp);
}
update(e: ecs.Entity): void {
if(this.timer.update(this.dt)){
const attrsComp = e.get(HeroAttrsComp);
if(attrsComp.frost_end_time > 0){
attrsComp.frost_end_time -= 0.1;
if(attrsComp.frost_end_time <= 0){
attrsComp.frost_end_time = 0;
}
}
}
void e;
}
}