Files
heros/assets/script/game/hero/MonAttrsComp.ts
2025-10-30 10:39:46 +08:00

204 lines
7.3 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.

/*
* @Author: dgflash
* @Date: 2021-11-18 15:56:01
* @LastEditors: dgflash
* @LastEditTime: 2022-08-17 13:43:25
*/
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs";
import { BuffConf } from "../common/config/SkillSet";
import { HeroInfo, AttrSet } from "../common/config/heroSet";
/**
* 怪物属性数据模型 - 存储纯数据,不含表现逻辑
* 简化版本只保留临时buff系统移除持久型buff
*
* 注意HeroAttrsComp 中有详细的 ECS 架构分析,迁移方案见那个文件
*/
@ecs.register('MonAttrsComp')
export class MonAttrsComp extends ecs.Comp {
// ==================== 角色基础信息 ====================
hero_uuid: number = 1001;
hero_name: string = "monster";
lv: number = 1;
type: number = 0; // 0近战 1远程 2辅助
fac: number = 1; // 0:hero 1:monster默认为怪物
// ==================== 动态属性值 ====================
hp: number = 100; // 当前血量
mp: number = 100; // 当前魔法值
shield: number = 0; // 当前护盾
Attrs: any = []; // 最终属性数组经过Buff计算后
NeAttrs: any = []; // 负面状态数组
// ==================== Buff/Debuff 系统 ====================
/** 临时型buff数组 - 按时间自动过期怪物只使用临时buff */
BUFFS_TEMP: Record<number, Array<{value: number, BType: BType, remainTime: number}>> = {};
// ==================== 标记状态 ====================
is_dead: boolean = false;
is_count_dead: boolean = false;
is_boss: boolean = false;
is_big_boss: boolean = false;
is_master: boolean = false;
is_friend: boolean = false;
is_kalami: boolean = false;
// ==================== 计数统计 ====================
atk_count: number = 0; // 攻击次数
atked_count: number = 0; // 被攻击次数
// ==================== 技能配置 ====================
skills: any = [];
addBuff(buffConf: BuffConf) {
// 怪物只使用临时buff
const attrIndex = buffConf.buff;
if (!this.BUFFS_TEMP[attrIndex]) this.BUFFS_TEMP[attrIndex] = [];
this.BUFFS_TEMP[attrIndex].push({ value: buffConf.value, BType: buffConf.BType, remainTime: buffConf.time });
this.recalculateSingleAttr(attrIndex);
}
recalculateSingleAttr(attrIndex: number) {
const baseValues: Record<number, number> = {
[Attrs.HP_MAX]: this.hp, [Attrs.MP_MAX]: this.mp, [Attrs.DEF]: this.def,
[Attrs.AP]: this.base_ap, [Attrs.MAP]: this.base_map, [Attrs.SPEED]: this.base_speed, [Attrs.SHIELD_MAX]: 0
};
const baseVal = baseValues[attrIndex] !== undefined ? baseValues[attrIndex] : 0;
let totalValue = baseVal, totalRatio = 0;
// 怪物只计算临时buff
if (this.BUFFS_TEMP[attrIndex]) {
for (const buff of this.BUFFS_TEMP[attrIndex]) {
if (buff.BType === BType.VALUE) totalValue += buff.value;
else totalRatio += buff.value;
}
}
const attrType = AttrsType[attrIndex];
this.Attrs[attrIndex] = attrType === BType.RATIO ? totalValue + totalRatio : Math.floor(totalValue * (1 + totalRatio / 100));
this.clampSingleAttr(attrIndex);
}
private clampSingleAttr(attrIndex: number) {
switch(attrIndex) {
case Attrs.HP_MAX: case Attrs.MP_MAX: case Attrs.DEF: case Attrs.AP: case Attrs.MAP:
this.Attrs[attrIndex] = Math.max(1, this.Attrs[attrIndex]); break;
case Attrs.CRITICAL: case Attrs.DODGE: case Attrs.HIT:
this.Attrs[attrIndex] = Math.max(0, Math.min(AttrSet.ATTR_MAX, this.Attrs[attrIndex])); break;
}
}
updateTemporaryBuffsDebuffs(dt: number) {
const affectedAttrs = new Set<number>();
for (const attrIndex in this.BUFFS_TEMP) {
const buffs = this.BUFFS_TEMP[attrIndex];
buffs.forEach(buff => {
buff.remainTime -= dt;
if (buff.remainTime <= 0) buffs.splice(buffs.indexOf(buff), 1);
});
if (buffs.length === 0) {
delete this.BUFFS_TEMP[attrIndex];
affectedAttrs.add(parseInt(attrIndex));
}
}
for (const key in this.NeAttrs) {
this.NeAttrs[key].remainTime -= dt;
if (this.NeAttrs[key].remainTime <= 0) this.NeAttrs[key].remainTime = 0;
}
affectedAttrs.forEach(attrIndex => this.recalculateSingleAttr(attrIndex));
}
clearBuffs(attrIndex?: number, isBuff: boolean = true): void {
if (attrIndex === undefined) {
for (const idx in this.BUFFS_TEMP) this.clearBuffsForAttr(parseInt(idx), isBuff);
} else {
this.clearBuffsForAttr(attrIndex, isBuff);
}
}
private clearBuffsForAttr(attrIndex: number, isBuff: boolean): void {
if (!this.BUFFS_TEMP[attrIndex]) return;
this.BUFFS_TEMP[attrIndex] = this.BUFFS_TEMP[attrIndex].filter(buff => {
const shouldClear = isBuff ? buff.value > 0 : buff.value < 0;
return !shouldClear;
});
if (this.BUFFS_TEMP[attrIndex].length === 0) delete this.BUFFS_TEMP[attrIndex];
this.recalculateSingleAttr(attrIndex);
}
clearNeAttr(neAttrIndex: number): void {
if (this.NeAttrs[neAttrIndex]) {
this.NeAttrs[neAttrIndex].value = 0;
this.NeAttrs[neAttrIndex].time = 0;
}
}
clearAllNeAttrs(): void {
for (const key in this.NeAttrs) {
this.NeAttrs[key].value = 0;
this.NeAttrs[key].time = 0;
}
}
public isStun(): boolean {
return this.NeAttrs[NeAttrs.IN_STUN]?.time > 0;
}
public isFrost(): boolean {
return this.NeAttrs[NeAttrs.IN_FROST]?.time > 0;
}
reset() {
this.hero_uuid = 1001;
this.hero_name = "monster";
this.lv = 1;
this.type = 0;
this.fac = 1;
this.hp = 100;
this.mp = 100;
this.shield = 0;
this.Attrs = [];
this.NeAttrs = [];
this.BUFFS_TEMP = {}; // 只重置临时buff
this.is_dead = false;
this.is_count_dead = false;
this.is_boss = false;
this.is_big_boss = false;
this.is_master = false;
this.is_friend = false;
this.is_kalami = false;
this.atk_count = 0;
this.atked_count = 0;
this.skills = [];
}
}
/**
* ==================== 怪物属性更新系统 ====================
*
* 与 HeroAttrSystem 类似,但针对怪物
* 可以复用相同逻辑,也可以定制不同规则
*/
export class MonAttrSystem extends ecs.ComblockSystem
implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(MonAttrsComp);
}
update(e: ecs.Entity): void {
const model = e.get(MonAttrsComp);
if (!model || model.is_dead) return;
// 怪物的属性更新逻辑(可以与英雄不同)
model.updateTemporaryBuffsDebuffs(this.dt);
// 怪物可能没有自然回复,或者回复速度不同
// model.mp += MonUpSet.MP * this.dt;
// model.hp += MonUpSet.HP * this.dt;
}
}