From d549dd2d9495738cc3b58b3db8213fbee03a2311 Mon Sep 17 00:00:00 2001 From: Daniil Zakharov Date: Tue, 18 Nov 2025 16:21:05 +0300 Subject: [PATCH 1/2] Fix beast form transform animation bug Replay cache needs to be cleared when a character respawns --- Code/client/Games/Skyrim/Actor.cpp | 12 ++++++------ Code/server/Game/Animation/ActionReplayCache.cpp | 5 +++++ Code/server/Game/Animation/ActionReplayCache.h | 2 ++ Code/server/Game/Animation/AnimationEventLists.cpp | 2 ++ Code/server/Services/CharacterService.cpp | 4 ++++ 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Code/client/Games/Skyrim/Actor.cpp b/Code/client/Games/Skyrim/Actor.cpp index fd9315f01..764e71b9e 100644 --- a/Code/client/Games/Skyrim/Actor.cpp +++ b/Code/client/Games/Skyrim/Actor.cpp @@ -875,16 +875,16 @@ extern thread_local bool g_forceAnimation; void Actor::FixVampireLordModel() noexcept { - TESBoundObject* pObject = Cast(TESForm::GetById(0x2011a84)); - if (!pObject) + TESBoundObject* pLordArmor = Cast(TESForm::GetById(0x2011a84)); + if (!pLordArmor) return; { ScopedInventoryOverride _; - AddObjectToContainer(pObject, nullptr, 1, nullptr); + AddObjectToContainer(pLordArmor, nullptr, 1, nullptr); } - EquipManager::Get()->Equip(this, pObject, nullptr, 1, nullptr, false, true, false, false); + EquipManager::Get()->Equip(this, pLordArmor, nullptr, 1, nullptr, false, true, false, false); g_forceAnimation = true; @@ -892,8 +892,8 @@ void Actor::FixVampireLordModel() noexcept uint32_t isLevitating = GetAnimationVariableInt(&str); spdlog::critical("isLevitating {}", isLevitating); - // By default, a loaded vampire lord is not levitating. - if (isLevitating) + // Enforce levitation via SAE + if (!isLevitating) { BSFixedString levitation("LevitationToggle"); SendAnimationEvent(&levitation); diff --git a/Code/server/Game/Animation/ActionReplayCache.cpp b/Code/server/Game/Animation/ActionReplayCache.cpp index 091f88be9..85b8a4cb9 100644 --- a/Code/server/Game/Animation/ActionReplayCache.cpp +++ b/Code/server/Game/Animation/ActionReplayCache.cpp @@ -94,3 +94,8 @@ std::optional ActionReplayCache::FindInstantCounterpartForActi } return std::nullopt; } + +void ActionReplayCache::Clear() noexcept +{ + m_actions.clear(); +} diff --git a/Code/server/Game/Animation/ActionReplayCache.h b/Code/server/Game/Animation/ActionReplayCache.h index d12bd5f6d..53e01dc93 100644 --- a/Code/server/Game/Animation/ActionReplayCache.h +++ b/Code/server/Game/Animation/ActionReplayCache.h @@ -25,6 +25,8 @@ class ActionReplayCache const Vector& GetActions() const noexcept { return m_actions; }; + void Clear() noexcept; + private: // Returns true if clients should reset the animation graph of the Actor before replaying bool RefineReplayCache() noexcept; diff --git a/Code/server/Game/Animation/AnimationEventLists.cpp b/Code/server/Game/Animation/AnimationEventLists.cpp index c16b3dc10..57e367058 100644 --- a/Code/server/Game/Animation/AnimationEventLists.cpp +++ b/Code/server/Game/Animation/AnimationEventLists.cpp @@ -177,6 +177,8 @@ const Set AnimationEventLists::kIgnore = { {"idleChairVar2"}, {"idleChairArmsCrossedVar1"}, {"idleChairArmsCrossedVar2"}, + // See `Actor::FixVampireLordModel` + {"LevitationToggle"}, // Weapon drawing is handled separately in CharacterService {"WeapEquip"}, {"WeapSoloEquip"}, diff --git a/Code/server/Services/CharacterService.cpp b/Code/server/Services/CharacterService.cpp index 80d098b2c..9ec33d9ad 100644 --- a/Code/server/Services/CharacterService.cpp +++ b/Code/server/Services/CharacterService.cpp @@ -497,6 +497,10 @@ void CharacterService::OnRequestRespawn(const PacketEvent& acMes } auto& ownerComponent = view.get(*it); + + // Replay cache needs to be cleared when a character respawns + m_world.try_get(*it)->ActionsReplayCache.Clear(); + if (ownerComponent.GetOwner() == acMessage.pPlayer) { if (!acMessage.Packet.AppearanceBuffer.empty()) From 71b319d747de533d588ff66214269b703509e678 Mon Sep 17 00:00:00 2001 From: Daniil Zakharov Date: Tue, 18 Nov 2025 20:35:13 +0300 Subject: [PATCH 2/2] Leave `if (isLevitating)` as it was --- Code/client/Games/Skyrim/Actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/client/Games/Skyrim/Actor.cpp b/Code/client/Games/Skyrim/Actor.cpp index 764e71b9e..931c2bdf1 100644 --- a/Code/client/Games/Skyrim/Actor.cpp +++ b/Code/client/Games/Skyrim/Actor.cpp @@ -892,8 +892,8 @@ void Actor::FixVampireLordModel() noexcept uint32_t isLevitating = GetAnimationVariableInt(&str); spdlog::critical("isLevitating {}", isLevitating); - // Enforce levitation via SAE - if (!isLevitating) + // By default, a loaded vampire lord is not levitating. + if (isLevitating) { BSFixedString levitation("LevitationToggle"); SendAnimationEvent(&levitation);