Files
heros/assets/script/game/damage/DamageSystem.ts
2025-02-03 22:02:26 +08:00

114 lines
3.7 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 { oops } from "db://oops-framework/core/Oops";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroViewComp } from "../hero/HeroViewComp";
import { DamageRequest, DamageResult } from "./DamageComp";
@ecs.register('DamageSystem')
export class DamageSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
private _timers: { [key: string]: number } = {};
filter(): ecs.IMatcher {
return ecs.allOf(DamageRequest);
}
update(e: ecs.Entity) {
const req = e.get(DamageRequest);
const result = this.calculateDamage(req);
this.applyDamage(req.target, result);
e.remove(DamageRequest);
}
private calculateDamage(req: DamageRequest): DamageResult {
const targetView = req.target.get(HeroViewComp);
const sourceView = req.source.get(HeroViewComp);
let final = req.baseValue;
// 伤害浮动±10%
const damageFloat = 0.9 + Math.random() * 0.2; // 0.9~1.1
final *= damageFloat;
final = Math.round(final);
// 闪避判定
if (Math.random()*100 < targetView.dodge) {
const result = new DamageResult();
result.isDodged = true;
return result;
}
// 护甲减伤
if (!req.ignoreDefense) {
const effectiveArmor = Math.min(targetView.def, 300); // 最大减伤75%
const damageReduction = effectiveArmor / (effectiveArmor + 100);
final *= (1 - damageReduction);
final = Math.round(final); // 四舍五入取整
}
// 暴击判定
let isCrit = false;
if (req.canCrit) {
const critRate = sourceView.crit;
if (Math.random() * 100 < critRate) {
final *= 1.5;
isCrit = true;
}
}
const result = new DamageResult();
result.value = Math.max(1, final); // 确保最小伤害为1
result.isCrit = isCrit;
result.position = req.target.get(HeroViewComp).node.position;
result.delay = req.delay;
return result;
}
private applyDamage(target: ecs.Entity, result: DamageResult) {
const view = target.get(HeroViewComp);
if (!view.ent.has(HeroViewComp)) return;
this.scheduleOnce(()=>{
// 护盾优先吸收伤害
let remainingDamage = result.value;
if(result.isDodged){
view.BUFFCOMP.tooltip(5,"*闪避*");
return;
}
if (view.shield > 0) {
const shieldAbsorb = Math.min(view.shield, remainingDamage);
view.shield -= shieldAbsorb;
remainingDamage -= shieldAbsorb;
if (view.shield <= 0) {
view.BUFFCOMP.show_shield(false);
}
}
// 剩余伤害扣除血量
if (remainingDamage > 0) {
view.hp -= remainingDamage;
view.showDamage(result.value, result.isCrit);
}else{
view.BUFFCOMP.tooltip(5,"*吸收*");
}
}, result.delay)
// 直接触发事件
oops.message.dispatchEvent("OnDamage", {
target,
damage: result.value,
isCrit: result.isCrit
});
}
private scheduleOnce(callback: () => void, delay: number) {
const timer = setTimeout(() => {
callback();
delete this._timers[timer];
}, delay * 1000);
this._timers[timer] = timer;
}
onDestroy() {
Object.values(this._timers).forEach(clearTimeout);
}
}