#pragma once #include "RE/N/NiPoint3.h" #include #include namespace F4T::ProxyPuppet { // Installs the Actor vtable hook that stops the engine from recomputing the // locomotion graph Speed/Direction for networked proxy actors. // // Without this, Fallout 4 derives the graph "Speed" from the proxy's actual // character-controller velocity every frame. Because proxies move via // SetPosition (near-zero controller velocity), the engine overwrites whatever // Speed we drive over the network, so every proxy plays a single fixed walk // pace regardless of the remote player's real speed. This mirrors // TiltedEvolution's puppet model (HookActorProcess), but is surgically scoped // to just the Speed/Direction feedback write instead of the whole AI update. // // Safe to call repeatedly; the hook is installed at most once. bool Install(); // Publishes the set of FormIDs that currently back active proxy actors. Called // from the game-thread proxy controller each tick. Thread-safe. void SetActiveProxyFormIDs(std::vector a_formIDs); // Records locomotion state the puppet should hold. Negative speed means idle // (no override). Thread-safe. void SetProxyLocomotionState(std::uint32_t a_formID, float a_speed, float a_direction); // Legacy alias for speed-only callers. void SetProxyDesiredSpeed(std::uint32_t a_formID, float a_speed); // Syncs controller/movement-output velocity and pushes motion feedback into the // animation graph. Call once per frame after applying proxy displacement (e.g. Move). void ApplyProxyLocomotionFrame( RE::Actor& a_actor, const RE::NiPoint3& a_worldDelta, float a_deltaSeconds, float a_graphSpeed, float a_direction); // Thread-safe membership test used by the vtable hook to gate proxies from // real NPCs (which must keep their engine-driven locomotion). bool IsProxyFormID(std::uint32_t a_formID); // Diagnostics: total hook invocations (all actors) and proxy feedback skips // since process start. Used to confirm the hook actually intercepts proxies. void GetHookStats(std::uint64_t& a_outTotalCalls, std::uint64_t& a_outProxySkips); }