From 816987c436ce9890b28415081e7be32260947c11 Mon Sep 17 00:00:00 2001 From: Leonid Pospelov Date: Sun, 24 Mar 2024 15:49:07 +0500 Subject: [PATCH] fix(skymp5-client): handle actors that ignore Disable & more (#1886) --- .../services/services/worldCleanerService.ts | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/skymp5-client/src/services/services/worldCleanerService.ts b/skymp5-client/src/services/services/worldCleanerService.ts index 4bddca8157..0436430fa8 100644 --- a/skymp5-client/src/services/services/worldCleanerService.ts +++ b/skymp5-client/src/services/services/worldCleanerService.ts @@ -1,30 +1,41 @@ -import { Game, Actor } from "skyrimPlatform"; import { ClientListener, CombinedController, Sp } from "./clientListener"; +import { NiPoint3 } from "../../sync/movement"; +import { ObjectReferenceEx } from "../../extensions/objectReferenceEx"; +import { Actor } from "skyrimPlatform"; export class WorldCleanerService extends ClientListener { constructor(private sp: Sp, private controller: CombinedController) { super(); this.controller.on("update", () => this.onUpdate()); + this.controller.emitter.on("gameLoad", () => this.onGameLoad()); } modWcProtection(actorId: number, mod: number): void { const currentProtection = this.protection.get(actorId); this.protection.set(actorId, currentProtection ? currentProtection + mod : mod); } - + getWcProtection(actorId: number): number { return this.protection.get(actorId) || 0; } + private onGameLoad() { + let player = this.sp.Game.getPlayer(); + if (!player) return; + + this.initialPos = ObjectReferenceEx.getPos(player); + this.initialCellOrWorld = ObjectReferenceEx.getWorldOrCell(player); + } + private onUpdate() { this.processOneActor(); } private processOneActor() { - const pc = Game.getPlayer(); + const pc = this.sp.Game.getPlayer(); if (pc === null) return; - const actor = Game.findRandomActor( + const actor = this.sp.Game.findRandomActor( pc.getPositionX(), pc.getPositionY(), pc.getPositionZ(), @@ -47,13 +58,35 @@ export class WorldCleanerService extends ClientListener { return; } - if (actor.isDead()) { + // Keep vanila pre-placed bodies, but delete player bodies + if (actor.isDead() && actorId < 0xff000000) { actor.blockActivation(true); return; } + const pos = ObjectReferenceEx.getPos(actor); + const cellOrWorld = ObjectReferenceEx.getWorldOrCell(actor); + + const chickenRace = 0xa919d; + + // We discovered anomaly chickens that fail to Disable if we load game near to them + // Refs: 106C22, 106C23 + if (actorId < 0xff000000 && actor.getRace()?.getFormID() === chickenRace) { + if (this.initialPos && ObjectReferenceEx.getDistanceNoZ(pos, this.initialPos) < 4096) { + if (cellOrWorld === this.initialCellOrWorld) { + if (this.isActorInDialogue(actor)) return; + this.logTrace(`Deleting chicken anomaly ${actorId.toString(16)}`); + actor.killSilent(null); + actor.blockActivation(true); + actor.disableNoWait(false); + actor.setAlpha(0, false); + return; + } + } + } + actor.disable(false).then(() => { - const ac = Actor.from(Game.getFormEx(actorId)); + const ac = this.sp.Actor.from(this.sp.Game.getFormEx(actorId)); if (!ac || this.isActorInDialogue(ac)) return; ac.delete(); }); @@ -64,4 +97,6 @@ export class WorldCleanerService extends ClientListener { } private protection = new Map(); + private initialPos?: NiPoint3; + private initialCellOrWorld?: number; }