114 lines
3.7 KiB
TypeScript
114 lines
3.7 KiB
TypeScript
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);
|
||
}
|
||
}
|