Tie proxy locomotion/cadence to actual per-frame movement and motion-feedback instead of only replaying transmitted Speed. Changes: - docs/dev-log.md: add detailed dev log describing measured-cadence fix and cadence/animationSpeed work. - plugin/include/F4TProxyPuppet.h: added NiPoint3 include and new API: SetProxyLocomotionState, SetProxyDesiredSpeed (legacy alias), and ApplyProxyLocomotionFrame. - plugin/lib/commonlibf4/include/RE/A/ActorMotionFeedbackOutput.h: new tentative struct for motion-feedback output. - plugin/lib/commonlibf4/include/RE/Fallout.h: include new ActorMotionFeedbackOutput header. - plugin/src/F4TProxyActorController.cpp: track per-frame timing/puppet state, compute frame delta, use proxy->Move() with measured frameDelta and call ApplyProxyLocomotionFrame (instead of SetPosition/Lerp-only), and pass measured graph speed/direction to proxy. - plugin/src/F4TProxyAnimationSync.cpp: use SetProxyLocomotionState to include direction when writing proxy locomotion state. - plugin/src/F4TProxyPuppet.cpp: introduce ProxyLocomotionState map, sync controller motion fields, compute/push engine motion-feedback (via Compute/Update feedback vtable calls), use UpdateNoAI + manual motion feedback to drive cadence, implement ApplyProxyLocomotionFrame which derives velocity from world delta, clamps/chooses feedback speed (uses measured horizontal speed unless provided graph speed is valid), applies an idle threshold (<8 u/s -> idle), and writes graph speed back to the animation graph. Why: This makes foot cadence reflect on-screen translation (eliminating mild foot slide/stop-overstay) by measuring applied displacement each frame, updating controller velocity/motion-feedback, and re-asserting animation graph inputs after UpdateNoAI.
52 lines
2.1 KiB
C++
52 lines
2.1 KiB
C++
#pragma once
|
|
|
|
#include "RE/N/NiPoint3.h"
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
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<std::uint32_t> 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);
|
|
}
|