Add local animation graph read-only debug
Introduce a game-thread-only, read-only diagnostic helper to probe local player animation graph variables and (optionally) numeric cache IDs. Adds F4TLocalAnimationGraphDebug.h/cpp with UpdateLocalPlayerAnimationGraphDebug(...) and wires a call into plugin/src/main.cpp immediately after GetPlayerMovementState. The helper probes curated variable names via CommonLibF4 read-only APIs, logs availability/changes, and includes a disabled-by-default numeric cache ID scan (kEnableLocalPlayerAnimationGraphCacheIdReadOnlyDebug = false) due to a reported crash during testing. Update also adds extensive dev-log documentation (docs/dev-log.md) describing behavior, testing notes, and rollback instructions. Build succeeded; manual F4SE in-game verification remains pending.
This commit is contained in:
+219
@@ -2489,6 +2489,225 @@ Next investigation should focus on read-only animation graph variable discovery
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-03 - Proxy Animation Graph Read-Only Investigation
|
||||
|
||||
### Summary
|
||||
|
||||
Added a local-player-only, read-only animation graph diagnostic helper to probe
|
||||
known candidate graph variable names and compare them with existing local
|
||||
movement, sneak, jump, and weapon state signals.
|
||||
|
||||
### Files Changed
|
||||
|
||||
- `plugin/include/F4TLocalAnimationGraphDebug.h`
|
||||
- `plugin/src/F4TLocalAnimationGraphDebug.cpp`
|
||||
- `plugin/src/main.cpp`
|
||||
- `docs/dev-log.md`
|
||||
|
||||
### Details
|
||||
|
||||
- Added `UpdateLocalPlayerAnimationGraphDebug(...)` on the existing game-thread
|
||||
local polling path, immediately after `GetPlayerMovementState(...)` and
|
||||
before any transform send decision.
|
||||
- Added explicit read-only gates:
|
||||
`kEnableLocalPlayerAnimationGraphReadOnlyDebug = true` and
|
||||
`kEnableProxyAnimationGraphReadOnlyDebug = false`.
|
||||
- The helper only inspects the local player. It does not inspect runtime
|
||||
proxies by default and does not change proxy movement, lifecycle,
|
||||
neutralization, placed fallback behavior, networking, protocol, server code,
|
||||
or fake-client tooling.
|
||||
- Graph access is limited to read-only CommonLibF4 APIs:
|
||||
`GetAnimationGraphManagerImpl`, `GetGraphVariableCacheSize`,
|
||||
`GetGraphVariableImplBool`, `GetGraphVariableImplInt`,
|
||||
`GetGraphVariableImplFloat`, and
|
||||
`BGSAnimationSystemUtils::IsActiveGraphInTransition`.
|
||||
- Candidate variables are probed by curated name only. There is no raw cache
|
||||
walking, no fake enumeration, no graph variable writes, no graph events, no
|
||||
`NotifyAnimationGraphImpl`, no `PerformAction`, no `InitializeActorInstant`,
|
||||
and no `ActorState` writes.
|
||||
- Unavailable candidate reads are logged once per variable/type. Successful
|
||||
reads are logged when their value changes or when local state / graph metadata
|
||||
changes.
|
||||
|
||||
### Testing
|
||||
|
||||
- Ran `xmake build` from `plugin`; it succeeded and linked
|
||||
`Fallout4Together.dll`.
|
||||
- Manual Fallout 4 / F4SE testing is still pending:
|
||||
- Launch through F4SE.
|
||||
- `coc F4TTestCell01`.
|
||||
- Toggle local sneak on/off.
|
||||
- Move/stop and sprint if safe.
|
||||
- Jump.
|
||||
- Draw/holster weapon if safe.
|
||||
- Check `Fallout4Together.log` for `Local animation graph debug` lines.
|
||||
- Optionally add one fake client to confirm runtime proxies still spawn and
|
||||
move while proxy graph diagnostics remain disabled.
|
||||
|
||||
### Known Issues
|
||||
|
||||
- Useful graph variables have not yet been confirmed in-game; log review from
|
||||
the manual test sequence is required.
|
||||
- Candidate variable failures will only prove that those names/types were
|
||||
unavailable through the current read-by-name API, not that the underlying
|
||||
graph has no equivalent state.
|
||||
- CommonLibF4 does not expose a safe graph variable name enumeration API here,
|
||||
so this pass intentionally uses curated read-by-name probes only.
|
||||
|
||||
### Rollback Note
|
||||
|
||||
- If the helper causes a crash, severe log spam, local control issues,
|
||||
performance problems, proxy movement/lifecycle regressions, or unexpected
|
||||
network send behavior, set `kEnableLocalPlayerAnimationGraphReadOnlyDebug`
|
||||
back to `false` or remove the single helper call from `plugin/src/main.cpp`.
|
||||
|
||||
### Next Steps
|
||||
|
||||
- Run the manual F4SE test sequence and record which graph candidates are
|
||||
available, unavailable, or transition with sneak/movement/jump/weapon state.
|
||||
- If no safe graph reads produce useful values, investigate another read-only
|
||||
route such as animation graph event observation research or known Fallout 4
|
||||
graph variable references before planning any write/event milestone.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-03 - Animation Graph Cache ID Read-Only Investigation
|
||||
|
||||
### Summary
|
||||
|
||||
Added a local-player-only numeric animation graph cache ID diagnostic pass that
|
||||
uses CommonLibF4 read-only virtual getter overloads to probe cache IDs from
|
||||
`0` through `GetGraphVariableCacheSize() - 1`.
|
||||
|
||||
### Files Changed
|
||||
|
||||
- `plugin/src/F4TLocalAnimationGraphDebug.cpp`
|
||||
- `docs/dev-log.md`
|
||||
|
||||
### Details
|
||||
|
||||
- Added `kEnableLocalPlayerAnimationGraphCacheIdReadOnlyDebug = true` as a
|
||||
separate gate from the existing local-player read-only debug gate.
|
||||
- Kept the existing read-by-name candidate probing intact.
|
||||
- Numeric probing only uses:
|
||||
`GetGraphVariableImpl(std::uint32_t, bool&)`,
|
||||
`GetGraphVariableImpl(std::uint32_t, std::int32_t&)`, and
|
||||
`GetGraphVariableImpl(std::uint32_t, float&)`.
|
||||
- `GetGraphVariableCacheSize()` is the strict upper bound, so a cache size of
|
||||
`64` probes only IDs `0` through `63`.
|
||||
- Tracks per-ID/per-type availability and last value, logs initial readable
|
||||
values once, and logs later values only when they change.
|
||||
- Change logs include local movement, sprint, sneak, jump, weapon, speed,
|
||||
actor stance, and force-sneak context.
|
||||
- Added compact candidate buckets for sneak bool/int IDs, movement float IDs,
|
||||
weapon bool/int IDs, jump bool/int IDs, and sprint bool/int IDs.
|
||||
- Candidate logs intentionally use cautious wording such as candidate IDs and
|
||||
movement-speed-related hints. No permanent graph variable names are claimed.
|
||||
- This remains local-player-only. No proxy actor graph reads were added.
|
||||
- No graph writes, graph events, `NotifyAnimationGraphImpl`, `PerformAction`,
|
||||
raw memory reads, cache walking, guessed offsets, `ActorState` writes,
|
||||
networking changes, protocol changes, Python server changes, fake-client
|
||||
changes, proxy movement changes, proxy lifecycle changes, proxy
|
||||
neutralization changes, or placed fallback behavior changes were added.
|
||||
|
||||
### Testing
|
||||
|
||||
- Ran `xmake build` from `plugin`; it succeeded and linked
|
||||
`Fallout4Together.dll`.
|
||||
- Linter diagnostics for `plugin/src/F4TLocalAnimationGraphDebug.cpp` reported
|
||||
no errors.
|
||||
- Manual Fallout 4 / F4SE testing is still pending:
|
||||
- Launch through F4SE.
|
||||
- `coc F4TTestCell01`.
|
||||
- Confirm startup logs show graph manager availability, cache size, and
|
||||
readable cache ID summary.
|
||||
- Toggle local sneak on/off and check for transition logs plus related
|
||||
numeric cache ID changes.
|
||||
- Move/stop, sprint if safe, jump, and draw/holster weapon if safe.
|
||||
- Optionally add one fake client and confirm runtime proxy spawning/movement
|
||||
still works while proxy graph cache probing remains absent.
|
||||
|
||||
### Observations To Record After Manual Testing
|
||||
|
||||
- Cache size observed: not yet observed in-game for this milestone.
|
||||
- Readable bool ID count: not yet observed in-game.
|
||||
- Readable int ID count: not yet observed in-game.
|
||||
- Readable float ID count: not yet observed in-game.
|
||||
- Candidate IDs discovered: none recorded yet; requires manual log review.
|
||||
|
||||
### Known Issues
|
||||
|
||||
- Runtime usefulness is unknown until the F4SE manual test pass records actual
|
||||
cache size, readable counts, and candidate IDs.
|
||||
- If all numeric cache ID reads return unavailable, this path will only confirm
|
||||
that the safe numeric read API is not useful for the local player in that
|
||||
state; it does not justify raw memory probing or graph writes.
|
||||
|
||||
### Rollback Note
|
||||
|
||||
- If the cache ID diagnostic causes a crash, severe log spam, performance
|
||||
drops, local control issues, proxy movement regressions, network cadence
|
||||
changes, or unexpected outgoing transform JSON changes, set
|
||||
`kEnableLocalPlayerAnimationGraphCacheIdReadOnlyDebug` to `false` or revert
|
||||
this milestone.
|
||||
|
||||
### Next Steps
|
||||
|
||||
- Run the manual F4SE test sequence and record cache size, readable ID counts,
|
||||
and any candidate sneak/movement/weapon/jump/sprint IDs from
|
||||
`Fallout4Together.log`.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-03 - Animation Graph Cache ID Investigation Rollback
|
||||
|
||||
### Summary
|
||||
|
||||
Disabled numeric animation graph cache ID probing by default after a Fallout 4
|
||||
crash during read-only testing. Safer local animation graph diagnostics remain
|
||||
enabled.
|
||||
|
||||
### Files Changed
|
||||
|
||||
- `plugin/src/F4TLocalAnimationGraphDebug.cpp`
|
||||
- `docs/dev-log.md`
|
||||
|
||||
### Details
|
||||
|
||||
- Set `kEnableLocalPlayerAnimationGraphCacheIdReadOnlyDebug = false` with a
|
||||
crash-safety comment above the gate.
|
||||
- Left numeric cache ID investigation code in place behind the disabled gate for
|
||||
future manual single-ID experiments.
|
||||
- No networking, protocol, Python server, fake-client, proxy, or
|
||||
`main.cpp` changes.
|
||||
|
||||
### Animation Graph Cache ID Investigation Result
|
||||
|
||||
Numeric cache ID probing through `GetGraphVariableImpl(id, bool/int/float&)` was tested as a read-only investigation path. Fallout 4 crashed during/after the test, while the server and fake client continued running. The numeric cache ID path has been disabled by default.
|
||||
|
||||
The existing local animation graph debug helper remains active for safer diagnostics: local state transitions, graph manager availability, graph cache size, graph transition state, and read-by-name candidate probing.
|
||||
|
||||
Future graph investigation should avoid full cache scans and instead test one known/suspected variable or ID at a time behind a stricter manual gate.
|
||||
|
||||
### Testing
|
||||
|
||||
- Ran `xmake build` from `plugin`; build succeeded.
|
||||
- Manual F4SE retest pending: `coc F4TTestCell01`, toggle sneak, move/jump/weapon,
|
||||
add one fake client. Expect `Local animation graph debug:` logs without
|
||||
`Local animation graph cache debug:` lines and no crash.
|
||||
|
||||
### Known Issues
|
||||
|
||||
- Full-cache numeric ID probing is treated as unsafe until single-ID manual
|
||||
gates exist.
|
||||
|
||||
### Next Steps
|
||||
|
||||
- Run the manual F4SE test sequence to confirm no crash and that safer local
|
||||
animation debug logs still appear.
|
||||
|
||||
---
|
||||
|
||||
## Entry Template
|
||||
|
||||
Use this format for future updates:
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace RE
|
||||
{
|
||||
class PlayerCharacter;
|
||||
}
|
||||
|
||||
namespace F4T::LocalAnimationGraphDebug
|
||||
{
|
||||
// Game-thread-only, read-only diagnostics for discovering local player
|
||||
// animation graph variables before any proxy visual animation writes.
|
||||
void UpdateLocalPlayerAnimationGraphDebug(
|
||||
RE::PlayerCharacter& a_player,
|
||||
bool a_isMoving,
|
||||
bool a_isSprinting,
|
||||
bool a_isSneaking,
|
||||
bool a_isJumping,
|
||||
bool a_weaponDrawn,
|
||||
float a_movementSpeed,
|
||||
std::chrono::steady_clock::time_point a_currentTime);
|
||||
}
|
||||
@@ -0,0 +1,804 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "F4TLocalAnimationGraphDebug.h"
|
||||
|
||||
#include "F4TNetworking.h"
|
||||
|
||||
#include "RE/A/ACTOR_STANCE.h"
|
||||
#include "RE/B/BGSAnimationSystemUtils.h"
|
||||
#include "RE/B/BSFixedString.h"
|
||||
#include "RE/I/IAnimationGraphManagerHolder.h"
|
||||
#include "RE/P/PlayerCharacter.h"
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr bool kEnableLocalPlayerAnimationGraphReadOnlyDebug = true;
|
||||
// Numeric graph cache ID probing caused a Fallout 4 crash during testing. Keep disabled by default.
|
||||
// Future investigation must isolate single IDs/types manually instead of scanning the full cache.
|
||||
constexpr bool kEnableLocalPlayerAnimationGraphCacheIdReadOnlyDebug = false;
|
||||
constexpr bool kEnableProxyAnimationGraphReadOnlyDebug = false;
|
||||
|
||||
constexpr auto kCandidateGraphVariables = std::to_array<std::string_view>({
|
||||
"IsSneaking",
|
||||
"bIsSneaking",
|
||||
"IsCrouching",
|
||||
"bIsCrouching",
|
||||
"Sneaking",
|
||||
"bSneaking",
|
||||
"IsMoving",
|
||||
"bIsMoving",
|
||||
"Speed",
|
||||
"speed",
|
||||
"Direction",
|
||||
"fSpeed",
|
||||
"bSprint",
|
||||
"IsSprinting",
|
||||
"IsJumping",
|
||||
"bInJumpState",
|
||||
"WeaponDrawn",
|
||||
"bWeaponDrawn",
|
||||
});
|
||||
|
||||
struct LocalAnimationGraphState
|
||||
{
|
||||
bool isMoving{ false };
|
||||
bool isSprinting{ false };
|
||||
bool isSneaking{ false };
|
||||
bool isJumping{ false };
|
||||
bool weaponDrawn{ false };
|
||||
bool playerCharacterIsSneaking{ false };
|
||||
bool actorIsSneaking{ false };
|
||||
bool apiJumping{ false };
|
||||
bool apiSprinting{ false };
|
||||
bool apiWeaponDrawn{ false };
|
||||
bool actorStateForceSneak{ false };
|
||||
std::uint32_t actorStateStance{ 0 };
|
||||
std::int32_t weaponState{ 0 };
|
||||
std::int32_t gunState{ 0 };
|
||||
float movementSpeed{ 0.0F };
|
||||
bool graphManagerAvailable{ false };
|
||||
bool graphInTransition{ false };
|
||||
std::uint32_t graphVariableCacheSize{ 0 };
|
||||
};
|
||||
|
||||
struct CandidateReadState
|
||||
{
|
||||
bool boolUnavailableLogged{ false };
|
||||
bool intUnavailableLogged{ false };
|
||||
bool floatUnavailableLogged{ false };
|
||||
|
||||
bool hasBoolValue{ false };
|
||||
bool lastBoolValue{ false };
|
||||
|
||||
bool hasIntValue{ false };
|
||||
std::int32_t lastIntValue{ 0 };
|
||||
|
||||
bool hasFloatValue{ false };
|
||||
float lastFloatValue{ 0.0F };
|
||||
};
|
||||
|
||||
struct CacheIdReadState
|
||||
{
|
||||
bool boolUnavailableObserved{ false };
|
||||
bool hasBoolValue{ false };
|
||||
bool lastBoolValue{ false };
|
||||
|
||||
bool intUnavailableObserved{ false };
|
||||
bool hasIntValue{ false };
|
||||
std::int32_t lastIntValue{ 0 };
|
||||
|
||||
bool floatUnavailableObserved{ false };
|
||||
bool hasFloatValue{ false };
|
||||
float lastFloatValue{ 0.0F };
|
||||
};
|
||||
|
||||
struct CacheIdDebugState
|
||||
{
|
||||
bool hasCacheSize{ false };
|
||||
std::uint32_t cacheSize{ 0 };
|
||||
std::vector<CacheIdReadState> idStates{};
|
||||
std::vector<std::uint32_t> candidateSneakBoolIds{};
|
||||
std::vector<std::uint32_t> candidateSneakIntIds{};
|
||||
std::vector<std::uint32_t> candidateMovementFloatIds{};
|
||||
std::vector<std::uint32_t> candidateWeaponBoolIds{};
|
||||
std::vector<std::uint32_t> candidateWeaponIntIds{};
|
||||
std::vector<std::uint32_t> candidateJumpBoolIds{};
|
||||
std::vector<std::uint32_t> candidateJumpIntIds{};
|
||||
std::vector<std::uint32_t> candidateSprintBoolIds{};
|
||||
std::vector<std::uint32_t> candidateSprintIntIds{};
|
||||
};
|
||||
|
||||
struct CacheIdProbeUpdate
|
||||
{
|
||||
std::vector<std::uint32_t> changedBoolIds{};
|
||||
std::vector<std::uint32_t> changedIntIds{};
|
||||
std::vector<std::uint32_t> changedFloatIds{};
|
||||
std::uint32_t readableBoolIds{ 0 };
|
||||
std::uint32_t readableIntIds{ 0 };
|
||||
std::uint32_t readableFloatIds{ 0 };
|
||||
bool candidateSummaryChanged{ false };
|
||||
};
|
||||
|
||||
std::string GetLocalPlayerLogMessage(std::string_view a_message)
|
||||
{
|
||||
return F4T::Networking::GetLocalPlayerLogPrefix() + " " + std::string(a_message);
|
||||
}
|
||||
|
||||
void LogInfoWithLocalPlayerPrefix(std::string_view a_message)
|
||||
{
|
||||
const auto message = GetLocalPlayerLogMessage(a_message);
|
||||
REX::INFO(std::string_view{ message });
|
||||
}
|
||||
|
||||
bool AreFloatsDifferent(float a_lhs, float a_rhs)
|
||||
{
|
||||
if (!std::isfinite(a_lhs) || !std::isfinite(a_rhs)) {
|
||||
return a_lhs != a_rhs;
|
||||
}
|
||||
|
||||
return std::fabs(a_lhs - a_rhs) > 0.001F;
|
||||
}
|
||||
|
||||
std::string FormatIdList(const std::vector<std::uint32_t>& a_ids)
|
||||
{
|
||||
std::string result{ "[" };
|
||||
for (std::size_t index = 0; index < a_ids.size(); ++index) {
|
||||
if (index != 0) {
|
||||
result += ", ";
|
||||
}
|
||||
result += std::format("{}", a_ids[index]);
|
||||
}
|
||||
result += "]";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string FormatLocalStateContext(const LocalAnimationGraphState& a_state)
|
||||
{
|
||||
return std::format(
|
||||
"moving={}, sprinting={}, sneaking={}, jumping={}, weaponDrawn={}, movementSpeed={:.1f}, actorStateStance={}, actorStateForceSneak={}",
|
||||
a_state.isMoving,
|
||||
a_state.isSprinting,
|
||||
a_state.isSneaking,
|
||||
a_state.isJumping,
|
||||
a_state.weaponDrawn,
|
||||
a_state.movementSpeed,
|
||||
a_state.actorStateStance,
|
||||
a_state.actorStateForceSneak);
|
||||
}
|
||||
|
||||
bool AddUniqueId(std::vector<std::uint32_t>& a_ids, std::uint32_t a_id)
|
||||
{
|
||||
for (const auto existingId : a_ids) {
|
||||
if (existingId == a_id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
a_ids.push_back(a_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MatchesStateBool(bool a_value, bool a_stateValue)
|
||||
{
|
||||
return a_value == a_stateValue;
|
||||
}
|
||||
|
||||
bool MatchesStateInt(std::int32_t a_value, bool a_stateValue)
|
||||
{
|
||||
if (a_value != 0 && a_value != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (a_value != 0) == a_stateValue;
|
||||
}
|
||||
|
||||
bool HasRelevantStateChange(
|
||||
const std::optional<LocalAnimationGraphState>& a_lastState,
|
||||
const LocalAnimationGraphState& a_currentState)
|
||||
{
|
||||
if (!a_lastState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& lastState = *a_lastState;
|
||||
return lastState.isMoving != a_currentState.isMoving ||
|
||||
lastState.isSprinting != a_currentState.isSprinting ||
|
||||
lastState.isSneaking != a_currentState.isSneaking ||
|
||||
lastState.isJumping != a_currentState.isJumping ||
|
||||
lastState.weaponDrawn != a_currentState.weaponDrawn ||
|
||||
lastState.playerCharacterIsSneaking != a_currentState.playerCharacterIsSneaking ||
|
||||
lastState.actorIsSneaking != a_currentState.actorIsSneaking ||
|
||||
lastState.apiJumping != a_currentState.apiJumping ||
|
||||
lastState.apiSprinting != a_currentState.apiSprinting ||
|
||||
lastState.apiWeaponDrawn != a_currentState.apiWeaponDrawn ||
|
||||
lastState.actorStateForceSneak != a_currentState.actorStateForceSneak ||
|
||||
lastState.actorStateStance != a_currentState.actorStateStance ||
|
||||
lastState.weaponState != a_currentState.weaponState ||
|
||||
lastState.gunState != a_currentState.gunState;
|
||||
}
|
||||
|
||||
bool HasGraphMetadataChange(
|
||||
const std::optional<LocalAnimationGraphState>& a_lastState,
|
||||
const LocalAnimationGraphState& a_currentState)
|
||||
{
|
||||
if (!a_lastState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& lastState = *a_lastState;
|
||||
return lastState.graphManagerAvailable != a_currentState.graphManagerAvailable ||
|
||||
lastState.graphInTransition != a_currentState.graphInTransition ||
|
||||
lastState.graphVariableCacheSize != a_currentState.graphVariableCacheSize;
|
||||
}
|
||||
|
||||
bool HasSneakTransition(
|
||||
const std::optional<LocalAnimationGraphState>& a_lastState,
|
||||
const LocalAnimationGraphState& a_currentState)
|
||||
{
|
||||
return a_lastState && a_lastState->isSneaking != a_currentState.isSneaking;
|
||||
}
|
||||
|
||||
bool HasMovementTransition(
|
||||
const std::optional<LocalAnimationGraphState>& a_lastState,
|
||||
const LocalAnimationGraphState& a_currentState)
|
||||
{
|
||||
if (!a_lastState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return a_lastState->isMoving != a_currentState.isMoving ||
|
||||
AreFloatsDifferent(a_lastState->movementSpeed, a_currentState.movementSpeed);
|
||||
}
|
||||
|
||||
bool HasJumpTransition(
|
||||
const std::optional<LocalAnimationGraphState>& a_lastState,
|
||||
const LocalAnimationGraphState& a_currentState)
|
||||
{
|
||||
return a_lastState && a_lastState->isJumping != a_currentState.isJumping;
|
||||
}
|
||||
|
||||
bool HasWeaponTransition(
|
||||
const std::optional<LocalAnimationGraphState>& a_lastState,
|
||||
const LocalAnimationGraphState& a_currentState)
|
||||
{
|
||||
return a_lastState && a_lastState->weaponDrawn != a_currentState.weaponDrawn;
|
||||
}
|
||||
|
||||
bool HasSprintTransition(
|
||||
const std::optional<LocalAnimationGraphState>& a_lastState,
|
||||
const LocalAnimationGraphState& a_currentState)
|
||||
{
|
||||
return a_lastState && a_lastState->isSprinting != a_currentState.isSprinting;
|
||||
}
|
||||
|
||||
void LogStateSnapshot(
|
||||
std::string_view a_reason,
|
||||
const LocalAnimationGraphState& a_state)
|
||||
{
|
||||
const auto actorStateSneaking =
|
||||
a_state.actorStateStance == static_cast<std::uint32_t>(RE::ACTOR_STANCE::kSneaking);
|
||||
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph debug: {} moving={}, sprinting={}, sneaking={}, jumping={}, weaponDrawn={}, movementSpeed={:.1f}, playerCharacterIsSneaking={}, actorIsSneaking={}, actorStateSneaking={}, actorStateStance={}, actorStateForceSneak={}, apiJumping={}, apiSprinting={}, apiWeaponDrawn={}, weaponState={}, gunState={}, graphManagerAvailable={}, graphVariableCacheSize={}, graphInTransition={}.",
|
||||
a_reason,
|
||||
a_state.isMoving,
|
||||
a_state.isSprinting,
|
||||
a_state.isSneaking,
|
||||
a_state.isJumping,
|
||||
a_state.weaponDrawn,
|
||||
a_state.movementSpeed,
|
||||
a_state.playerCharacterIsSneaking,
|
||||
a_state.actorIsSneaking,
|
||||
actorStateSneaking,
|
||||
a_state.actorStateStance,
|
||||
a_state.actorStateForceSneak,
|
||||
a_state.apiJumping,
|
||||
a_state.apiSprinting,
|
||||
a_state.apiWeaponDrawn,
|
||||
a_state.weaponState,
|
||||
a_state.gunState,
|
||||
a_state.graphManagerAvailable,
|
||||
a_state.graphVariableCacheSize,
|
||||
a_state.graphInTransition));
|
||||
}
|
||||
|
||||
void ProbeBoolCandidate(
|
||||
const RE::IAnimationGraphManagerHolder& a_holder,
|
||||
std::string_view a_candidate,
|
||||
CandidateReadState& a_state,
|
||||
bool a_shouldLogSuccessfulValues)
|
||||
{
|
||||
const RE::BSFixedString variableName{ a_candidate.data() };
|
||||
bool value = false;
|
||||
if (!a_holder.GetGraphVariableImplBool(variableName, value)) {
|
||||
if (!a_state.boolUnavailableLogged) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph debug: candidate bool variable \"{}\" unavailable.",
|
||||
a_candidate));
|
||||
a_state.boolUnavailableLogged = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto changed = !a_state.hasBoolValue || a_state.lastBoolValue != value;
|
||||
if (changed || a_shouldLogSuccessfulValues) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph debug: candidate bool variable \"{}\"={}.",
|
||||
a_candidate,
|
||||
value));
|
||||
}
|
||||
|
||||
a_state.hasBoolValue = true;
|
||||
a_state.lastBoolValue = value;
|
||||
}
|
||||
|
||||
void ProbeIntCandidate(
|
||||
const RE::IAnimationGraphManagerHolder& a_holder,
|
||||
std::string_view a_candidate,
|
||||
CandidateReadState& a_state,
|
||||
bool a_shouldLogSuccessfulValues)
|
||||
{
|
||||
const RE::BSFixedString variableName{ a_candidate.data() };
|
||||
std::int32_t value = 0;
|
||||
if (!a_holder.GetGraphVariableImplInt(variableName, value)) {
|
||||
if (!a_state.intUnavailableLogged) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph debug: candidate int variable \"{}\" unavailable.",
|
||||
a_candidate));
|
||||
a_state.intUnavailableLogged = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto changed = !a_state.hasIntValue || a_state.lastIntValue != value;
|
||||
if (changed || a_shouldLogSuccessfulValues) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph debug: candidate int variable \"{}\"={}.",
|
||||
a_candidate,
|
||||
value));
|
||||
}
|
||||
|
||||
a_state.hasIntValue = true;
|
||||
a_state.lastIntValue = value;
|
||||
}
|
||||
|
||||
void ProbeFloatCandidate(
|
||||
const RE::IAnimationGraphManagerHolder& a_holder,
|
||||
std::string_view a_candidate,
|
||||
CandidateReadState& a_state,
|
||||
bool a_shouldLogSuccessfulValues)
|
||||
{
|
||||
const RE::BSFixedString variableName{ a_candidate.data() };
|
||||
float value = 0.0F;
|
||||
if (!a_holder.GetGraphVariableImplFloat(variableName, value)) {
|
||||
if (!a_state.floatUnavailableLogged) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph debug: candidate float variable \"{}\" unavailable.",
|
||||
a_candidate));
|
||||
a_state.floatUnavailableLogged = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto changed = !a_state.hasFloatValue || AreFloatsDifferent(a_state.lastFloatValue, value);
|
||||
if (changed || a_shouldLogSuccessfulValues) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph debug: candidate float variable \"{}\"={:.3f}.",
|
||||
a_candidate,
|
||||
value));
|
||||
}
|
||||
|
||||
a_state.hasFloatValue = true;
|
||||
a_state.lastFloatValue = value;
|
||||
}
|
||||
|
||||
void ProbeCandidateGraphVariables(
|
||||
const RE::IAnimationGraphManagerHolder& a_holder,
|
||||
bool a_shouldLogSuccessfulValues)
|
||||
{
|
||||
static std::array<CandidateReadState, kCandidateGraphVariables.size()> candidateStates{};
|
||||
|
||||
for (std::size_t index = 0; index < kCandidateGraphVariables.size(); ++index) {
|
||||
const auto candidate = kCandidateGraphVariables[index];
|
||||
auto& state = candidateStates[index];
|
||||
|
||||
ProbeBoolCandidate(a_holder, candidate, state, a_shouldLogSuccessfulValues);
|
||||
ProbeIntCandidate(a_holder, candidate, state, a_shouldLogSuccessfulValues);
|
||||
ProbeFloatCandidate(a_holder, candidate, state, a_shouldLogSuccessfulValues);
|
||||
}
|
||||
}
|
||||
|
||||
void ResetCacheIdDebugState(CacheIdDebugState& a_debugState, std::uint32_t a_cacheSize)
|
||||
{
|
||||
a_debugState.hasCacheSize = true;
|
||||
a_debugState.cacheSize = a_cacheSize;
|
||||
a_debugState.idStates.clear();
|
||||
a_debugState.idStates.resize(a_cacheSize);
|
||||
a_debugState.candidateSneakBoolIds.clear();
|
||||
a_debugState.candidateSneakIntIds.clear();
|
||||
a_debugState.candidateMovementFloatIds.clear();
|
||||
a_debugState.candidateWeaponBoolIds.clear();
|
||||
a_debugState.candidateWeaponIntIds.clear();
|
||||
a_debugState.candidateJumpBoolIds.clear();
|
||||
a_debugState.candidateJumpIntIds.clear();
|
||||
a_debugState.candidateSprintBoolIds.clear();
|
||||
a_debugState.candidateSprintIntIds.clear();
|
||||
}
|
||||
|
||||
void MaybeLogBoolCandidate(
|
||||
CacheIdDebugState& a_debugState,
|
||||
CacheIdProbeUpdate& a_update,
|
||||
std::uint32_t a_id,
|
||||
bool a_value,
|
||||
const LocalAnimationGraphState& a_currentState,
|
||||
bool a_sneakTransition,
|
||||
bool a_weaponTransition,
|
||||
bool a_jumpTransition,
|
||||
bool a_sprintTransition)
|
||||
{
|
||||
if (a_sneakTransition && MatchesStateBool(a_value, a_currentState.isSneaking) &&
|
||||
AddUniqueId(a_debugState.candidateSneakBoolIds, a_id)) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: candidate sneak bool id {} mirrors local sneaking transition.",
|
||||
a_id));
|
||||
a_update.candidateSummaryChanged = true;
|
||||
}
|
||||
|
||||
if (a_weaponTransition && MatchesStateBool(a_value, a_currentState.weaponDrawn) &&
|
||||
AddUniqueId(a_debugState.candidateWeaponBoolIds, a_id)) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: candidate weapon bool id {} mirrors local weapon drawn transition.",
|
||||
a_id));
|
||||
a_update.candidateSummaryChanged = true;
|
||||
}
|
||||
|
||||
if (a_jumpTransition && MatchesStateBool(a_value, a_currentState.isJumping) &&
|
||||
AddUniqueId(a_debugState.candidateJumpBoolIds, a_id)) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: candidate jump bool id {} mirrors local jump transition.",
|
||||
a_id));
|
||||
a_update.candidateSummaryChanged = true;
|
||||
}
|
||||
|
||||
if (a_sprintTransition && MatchesStateBool(a_value, a_currentState.isSprinting) &&
|
||||
AddUniqueId(a_debugState.candidateSprintBoolIds, a_id)) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: candidate sprint bool id {} mirrors local sprint transition.",
|
||||
a_id));
|
||||
a_update.candidateSummaryChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MaybeLogIntCandidate(
|
||||
CacheIdDebugState& a_debugState,
|
||||
CacheIdProbeUpdate& a_update,
|
||||
std::uint32_t a_id,
|
||||
std::int32_t a_value,
|
||||
const LocalAnimationGraphState& a_currentState,
|
||||
bool a_sneakTransition,
|
||||
bool a_weaponTransition,
|
||||
bool a_jumpTransition,
|
||||
bool a_sprintTransition)
|
||||
{
|
||||
if (a_sneakTransition && MatchesStateInt(a_value, a_currentState.isSneaking) &&
|
||||
AddUniqueId(a_debugState.candidateSneakIntIds, a_id)) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: candidate sneak int id {} mirrors local sneaking transition.",
|
||||
a_id));
|
||||
a_update.candidateSummaryChanged = true;
|
||||
}
|
||||
|
||||
if (a_weaponTransition && MatchesStateInt(a_value, a_currentState.weaponDrawn) &&
|
||||
AddUniqueId(a_debugState.candidateWeaponIntIds, a_id)) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: candidate weapon int id {} mirrors local weapon drawn transition.",
|
||||
a_id));
|
||||
a_update.candidateSummaryChanged = true;
|
||||
}
|
||||
|
||||
if (a_jumpTransition && MatchesStateInt(a_value, a_currentState.isJumping) &&
|
||||
AddUniqueId(a_debugState.candidateJumpIntIds, a_id)) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: candidate jump int id {} mirrors local jump transition.",
|
||||
a_id));
|
||||
a_update.candidateSummaryChanged = true;
|
||||
}
|
||||
|
||||
if (a_sprintTransition && MatchesStateInt(a_value, a_currentState.isSprinting) &&
|
||||
AddUniqueId(a_debugState.candidateSprintIntIds, a_id)) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: candidate sprint int id {} mirrors local sprint transition.",
|
||||
a_id));
|
||||
a_update.candidateSummaryChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MaybeLogFloatCandidate(
|
||||
CacheIdDebugState& a_debugState,
|
||||
CacheIdProbeUpdate& a_update,
|
||||
std::uint32_t a_id,
|
||||
bool a_movementTransition)
|
||||
{
|
||||
if (a_movementTransition && AddUniqueId(a_debugState.candidateMovementFloatIds, a_id)) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: float id {} appears movement-speed-related.",
|
||||
a_id));
|
||||
a_update.candidateSummaryChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void LogTransitionChangedIds(
|
||||
std::string_view a_transition,
|
||||
const CacheIdProbeUpdate& a_update,
|
||||
const LocalAnimationGraphState& a_currentState)
|
||||
{
|
||||
if (a_update.changedBoolIds.empty() && a_update.changedIntIds.empty() && a_update.changedFloatIds.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: {} transition changed boolIds={}, intIds={}, floatIds={} with {}.",
|
||||
a_transition,
|
||||
FormatIdList(a_update.changedBoolIds),
|
||||
FormatIdList(a_update.changedIntIds),
|
||||
FormatIdList(a_update.changedFloatIds),
|
||||
FormatLocalStateContext(a_currentState)));
|
||||
}
|
||||
|
||||
void LogCandidateSummary(const CacheIdDebugState& a_debugState)
|
||||
{
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug summary: candidateSneakBoolIds={}, candidateSneakIntIds={}, candidateMovementFloatIds={}, candidateWeaponBoolIds={}, candidateWeaponIntIds={}, candidateJumpBoolIds={}, candidateJumpIntIds={}, candidateSprintBoolIds={}, candidateSprintIntIds={}.",
|
||||
FormatIdList(a_debugState.candidateSneakBoolIds),
|
||||
FormatIdList(a_debugState.candidateSneakIntIds),
|
||||
FormatIdList(a_debugState.candidateMovementFloatIds),
|
||||
FormatIdList(a_debugState.candidateWeaponBoolIds),
|
||||
FormatIdList(a_debugState.candidateWeaponIntIds),
|
||||
FormatIdList(a_debugState.candidateJumpBoolIds),
|
||||
FormatIdList(a_debugState.candidateJumpIntIds),
|
||||
FormatIdList(a_debugState.candidateSprintBoolIds),
|
||||
FormatIdList(a_debugState.candidateSprintIntIds)));
|
||||
}
|
||||
|
||||
void ProbeCacheIdGraphVariables(
|
||||
const RE::IAnimationGraphManagerHolder& a_holder,
|
||||
const LocalAnimationGraphState& a_currentState,
|
||||
const std::optional<LocalAnimationGraphState>& a_lastState)
|
||||
{
|
||||
if constexpr (!kEnableLocalPlayerAnimationGraphCacheIdReadOnlyDebug) {
|
||||
return;
|
||||
}
|
||||
|
||||
static CacheIdDebugState debugState{};
|
||||
|
||||
const auto cacheSize = a_currentState.graphVariableCacheSize;
|
||||
const auto cacheSizeChanged = !debugState.hasCacheSize || debugState.cacheSize != cacheSize;
|
||||
if (cacheSizeChanged) {
|
||||
ResetCacheIdDebugState(debugState, cacheSize);
|
||||
}
|
||||
|
||||
CacheIdProbeUpdate update{};
|
||||
const auto stateContext = FormatLocalStateContext(a_currentState);
|
||||
const auto sneakTransition = HasSneakTransition(a_lastState, a_currentState);
|
||||
const auto movementTransition = HasMovementTransition(a_lastState, a_currentState);
|
||||
const auto jumpTransition = HasJumpTransition(a_lastState, a_currentState);
|
||||
const auto weaponTransition = HasWeaponTransition(a_lastState, a_currentState);
|
||||
const auto sprintTransition = HasSprintTransition(a_lastState, a_currentState);
|
||||
|
||||
for (std::uint32_t id = 0; id < cacheSize; ++id) {
|
||||
auto& idState = debugState.idStates[id];
|
||||
|
||||
bool boolValue = false;
|
||||
if (a_holder.GetGraphVariableImpl(id, boolValue)) {
|
||||
++update.readableBoolIds;
|
||||
const auto changed = !idState.hasBoolValue || idState.lastBoolValue != boolValue;
|
||||
if (changed) {
|
||||
if (idState.hasBoolValue) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: bool id {} changed {} -> {} with {}.",
|
||||
id,
|
||||
idState.lastBoolValue,
|
||||
boolValue,
|
||||
stateContext));
|
||||
} else {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: bool id {} initial {} with {}.",
|
||||
id,
|
||||
boolValue,
|
||||
stateContext));
|
||||
}
|
||||
|
||||
update.changedBoolIds.push_back(id);
|
||||
MaybeLogBoolCandidate(
|
||||
debugState,
|
||||
update,
|
||||
id,
|
||||
boolValue,
|
||||
a_currentState,
|
||||
sneakTransition,
|
||||
weaponTransition,
|
||||
jumpTransition,
|
||||
sprintTransition);
|
||||
}
|
||||
idState.hasBoolValue = true;
|
||||
idState.lastBoolValue = boolValue;
|
||||
} else {
|
||||
idState.boolUnavailableObserved = true;
|
||||
}
|
||||
|
||||
std::int32_t intValue = 0;
|
||||
if (a_holder.GetGraphVariableImpl(id, intValue)) {
|
||||
++update.readableIntIds;
|
||||
const auto changed = !idState.hasIntValue || idState.lastIntValue != intValue;
|
||||
if (changed) {
|
||||
if (idState.hasIntValue) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: int id {} changed {} -> {} with {}.",
|
||||
id,
|
||||
idState.lastIntValue,
|
||||
intValue,
|
||||
stateContext));
|
||||
} else {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: int id {} initial {} with {}.",
|
||||
id,
|
||||
intValue,
|
||||
stateContext));
|
||||
}
|
||||
|
||||
update.changedIntIds.push_back(id);
|
||||
MaybeLogIntCandidate(
|
||||
debugState,
|
||||
update,
|
||||
id,
|
||||
intValue,
|
||||
a_currentState,
|
||||
sneakTransition,
|
||||
weaponTransition,
|
||||
jumpTransition,
|
||||
sprintTransition);
|
||||
}
|
||||
idState.hasIntValue = true;
|
||||
idState.lastIntValue = intValue;
|
||||
} else {
|
||||
idState.intUnavailableObserved = true;
|
||||
}
|
||||
|
||||
float floatValue = 0.0F;
|
||||
if (a_holder.GetGraphVariableImpl(id, floatValue)) {
|
||||
++update.readableFloatIds;
|
||||
const auto changed = !idState.hasFloatValue || AreFloatsDifferent(idState.lastFloatValue, floatValue);
|
||||
if (changed) {
|
||||
if (idState.hasFloatValue) {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: float id {} changed {:.3f} -> {:.3f} with {}.",
|
||||
id,
|
||||
idState.lastFloatValue,
|
||||
floatValue,
|
||||
stateContext));
|
||||
} else {
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: float id {} initial {:.3f} with {}.",
|
||||
id,
|
||||
floatValue,
|
||||
stateContext));
|
||||
}
|
||||
|
||||
update.changedFloatIds.push_back(id);
|
||||
MaybeLogFloatCandidate(debugState, update, id, movementTransition);
|
||||
}
|
||||
idState.hasFloatValue = true;
|
||||
idState.lastFloatValue = floatValue;
|
||||
} else {
|
||||
idState.floatUnavailableObserved = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheSizeChanged) {
|
||||
const auto allUnavailable =
|
||||
update.readableBoolIds == 0 && update.readableIntIds == 0 && update.readableFloatIds == 0;
|
||||
LogInfoWithLocalPlayerPrefix(std::format(
|
||||
"Local animation graph cache debug: cacheSize={}, readableBoolIds={}, readableIntIds={}, readableFloatIds={}{}.",
|
||||
cacheSize,
|
||||
update.readableBoolIds,
|
||||
update.readableIntIds,
|
||||
update.readableFloatIds,
|
||||
allUnavailable ? ", all numeric cache ID reads unavailable" : ""));
|
||||
}
|
||||
|
||||
if (sneakTransition) {
|
||||
LogTransitionChangedIds("sneak", update, a_currentState);
|
||||
}
|
||||
if (movementTransition) {
|
||||
LogTransitionChangedIds("movement", update, a_currentState);
|
||||
}
|
||||
if (jumpTransition) {
|
||||
LogTransitionChangedIds("jump", update, a_currentState);
|
||||
}
|
||||
if (weaponTransition) {
|
||||
LogTransitionChangedIds("weapon", update, a_currentState);
|
||||
}
|
||||
if (sprintTransition) {
|
||||
LogTransitionChangedIds("sprint", update, a_currentState);
|
||||
}
|
||||
if (update.candidateSummaryChanged) {
|
||||
LogCandidateSummary(debugState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace F4T::LocalAnimationGraphDebug
|
||||
{
|
||||
void UpdateLocalPlayerAnimationGraphDebug(
|
||||
RE::PlayerCharacter& a_player,
|
||||
bool a_isMoving,
|
||||
bool a_isSprinting,
|
||||
bool a_isSneaking,
|
||||
bool a_isJumping,
|
||||
bool a_weaponDrawn,
|
||||
float a_movementSpeed,
|
||||
std::chrono::steady_clock::time_point a_currentTime)
|
||||
{
|
||||
(void)a_currentTime;
|
||||
|
||||
if constexpr (!kEnableLocalPlayerAnimationGraphReadOnlyDebug) {
|
||||
return;
|
||||
}
|
||||
|
||||
static_assert(!kEnableProxyAnimationGraphReadOnlyDebug);
|
||||
static std::optional<LocalAnimationGraphState> lastState{};
|
||||
|
||||
auto& actor = static_cast<RE::Actor&>(a_player);
|
||||
auto* graphHolder = static_cast<RE::IAnimationGraphManagerHolder*>(&a_player);
|
||||
RE::BSTSmartPointer<RE::BSAnimationGraphManager> graphManager;
|
||||
|
||||
const auto graphManagerAvailable =
|
||||
graphHolder->GetAnimationGraphManagerImpl(graphManager) && static_cast<bool>(graphManager);
|
||||
const auto graphVariableCacheSize = graphHolder->GetGraphVariableCacheSize();
|
||||
const auto graphInTransition =
|
||||
RE::BGSAnimationSystemUtils::IsActiveGraphInTransition(static_cast<RE::TESObjectREFR*>(&a_player));
|
||||
|
||||
const LocalAnimationGraphState currentState{
|
||||
a_isMoving,
|
||||
a_isSprinting,
|
||||
a_isSneaking,
|
||||
a_isJumping,
|
||||
a_weaponDrawn,
|
||||
a_player.IsSneaking(),
|
||||
actor.IsSneaking(),
|
||||
a_player.IsJumping(),
|
||||
a_player.DoGetSprinting(),
|
||||
a_player.GetWeaponMagicDrawn(),
|
||||
a_player.forceSneak != 0,
|
||||
static_cast<std::uint32_t>(a_player.stance),
|
||||
static_cast<std::int32_t>(a_player.weaponState),
|
||||
static_cast<std::int32_t>(a_player.gunState),
|
||||
a_movementSpeed,
|
||||
graphManagerAvailable,
|
||||
graphInTransition,
|
||||
graphVariableCacheSize
|
||||
};
|
||||
|
||||
const auto relevantStateChanged = HasRelevantStateChange(lastState, currentState);
|
||||
const auto graphMetadataChanged = HasGraphMetadataChange(lastState, currentState);
|
||||
if (relevantStateChanged || graphMetadataChanged) {
|
||||
LogStateSnapshot(
|
||||
relevantStateChanged ? "state transition" : "graph metadata changed",
|
||||
currentState);
|
||||
}
|
||||
|
||||
ProbeCandidateGraphVariables(*graphHolder, relevantStateChanged || graphMetadataChanged);
|
||||
ProbeCacheIdGraphVariables(*graphHolder, currentState, lastState);
|
||||
|
||||
lastState = currentState;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "F4TNetworking.h"
|
||||
#include "F4TLocalAnimationGraphDebug.h"
|
||||
#include "F4TProxyActorController.h"
|
||||
|
||||
#include "RE/A/ACTOR_STANCE.h"
|
||||
@@ -399,6 +400,15 @@ namespace
|
||||
const auto currentTime = std::chrono::steady_clock::now();
|
||||
const auto currentMovementState =
|
||||
GetPlayerMovementState(*player, hasLastTransform ? &lastSampleTransform : nullptr, lastSampleTime, currentTransform, currentTime);
|
||||
F4T::LocalAnimationGraphDebug::UpdateLocalPlayerAnimationGraphDebug(
|
||||
*player,
|
||||
currentMovementState.isMoving,
|
||||
currentMovementState.isSprinting,
|
||||
currentMovementState.isSneaking,
|
||||
currentMovementState.isJumping,
|
||||
currentMovementState.weaponDrawn,
|
||||
currentMovementState.movementSpeed,
|
||||
currentTime);
|
||||
if (!hasLastTransform) {
|
||||
lastSentTransform = currentTransform;
|
||||
lastSampleTransform = currentTransform;
|
||||
|
||||
Reference in New Issue
Block a user