Skip to content

Commit

Permalink
Simplify (and correct) tracking of modified Master behavior.
Browse files Browse the repository at this point in the history
New to remember the modified MasterBehavior hash, since it is used to return from Beast mode. Previous implementation stored in ActorExtension, but would only get set on the first modified humanoid which might not be the PlayerCharacter.

Similar issue for dragons, remember the modified hash so Actor::IsDragon() test will work for all modded dragons, not just the first one.
  • Loading branch information
rfortier committed Oct 28, 2024
1 parent b5ab271 commit e751157
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 37 deletions.
2 changes: 0 additions & 2 deletions Code/client/Games/ActorExtension.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ struct ActorExtension

ActionEvent LatestAnimation{};
size_t GraphDescriptorHash = 0;
size_t UnmoddedGraphDescriptorHash = 0; // Hash before any mods change it.
size_t HumanoidGraphDescriptorHash = 0; // Copy of hash before overwritten by beast form.

private:
uint32_t onlineFlags{0};
Expand Down
23 changes: 3 additions & 20 deletions Code/client/Games/Skyrim/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
#include <Forms/BGSOutfit.h>
#include <Forms/TESObjectARMO.h>

#include <ModCompat/BehaviorVar.h>

#ifdef SAVE_STUFF

#include <Games/Skyrim/SaveLoad.h>
Expand Down Expand Up @@ -543,26 +545,7 @@ bool Actor::IsDead() const noexcept
bool Actor::IsDragon() const noexcept
{
const ActorExtension* pExtension = const_cast<Actor*>(this)->GetExtension();
auto hash = pExtension->UnmoddedGraphDescriptorHash;

if (hash)
return AnimationGraphDescriptor_BHR_Master::m_key == hash;

// Still want to continue with the original code, because if Nemesis/Pandora
// isn't on the client at all, BehaviorVar::Patch doesn't run on the Dragons.

// TODO: if anyone has a better way of doing this, please do tell.
BSAnimationGraphManager* pManager = nullptr;
animationGraphHolder.GetBSAnimationGraph(&pManager);

if (!pManager)
return false;

const auto* pGraph = pManager->animationGraphs.Get(pManager->animationGraphIndex);
if (!pGraph)
return false;

return AnimationGraphDescriptor_BHR_Master::m_key == pManager->GetDescriptorKey();
return BehaviorVar::IsDragon(pExtension->GraphDescriptorHash);
}

void Actor::Kill() noexcept
Expand Down
9 changes: 3 additions & 6 deletions Code/client/Games/Skyrim/PlayerCharacter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#include <Forms/TESObjectCELL.h>

#include <ModCompat/BehaviorVar.h>

int32_t PlayerCharacter::LastUsedCombatSkill = -1;

TP_THIS_FUNCTION(TPickUpObject, char, PlayerCharacter, TESObjectREFR* apObject, int32_t aCount, bool aUnk1, bool aUnk2);
Expand Down Expand Up @@ -162,12 +164,7 @@ void TP_MAKE_THISCALL(HookSetBeastForm, void, void* apUnk1, void* apUnk2, bool a
{
if (!aEntering)
{
PlayerCharacter::Get()->GetExtension()->GraphDescriptorHash = AnimationGraphDescriptor_Master_Behavior::m_key;
// Restore to saved, modified behavior hash, IFF Nemesis/Pandora is installed, otherwise BehaviorVar::Patch() doesn't run.
auto hash = PlayerCharacter::Get()->GetExtension()->HumanoidGraphDescriptorHash;
if (hash)
PlayerCharacter::Get()->GetExtension()->GraphDescriptorHash = hash;

PlayerCharacter::Get()->GetExtension()->GraphDescriptorHash = BehaviorVar::GetHumanoidHash();
World::Get().GetRunner().Trigger(BeastFormChangeEvent());
}

Expand Down
26 changes: 17 additions & 9 deletions Code/client/ModCompat/BehaviorVar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
#include "BehaviorVarsMap.h"

#if TP_SKYRIM64
#include <Structs/Skyrim/AnimationGraphDescriptor_Master_Behavior.h>
#include <Camera/TESCamera.h> // Camera 1st person is only in Skyrim?
#include <Camera/PlayerCamera.h>
#endif
Expand Down Expand Up @@ -361,8 +360,8 @@ const AnimationGraphDescriptor* BehaviorVar::Patch(BSAnimationGraphManager* apMa
// if the only humanoid Actor is the Dragonborn/player in 1st person.
// With that case filtered out, keep a counter to see if we need to defend against other
// cases (like a modded behavior where we CAN't find the signature var).
if (invocations++ == 100)
spdlog::warn(__FUNCTION__ ": warning, more than 100 invocations, investigate why");
if (invocations++ == 1000)
spdlog::warn(__FUNCTION__ ": warning, more than 1000 invocations, investigate why");

spdlog::info(__FUNCTION__ ": actor with formID {:x} with hash of {} has modded (or not synced) behavior", hexFormID, hash);

Expand Down Expand Up @@ -434,13 +433,22 @@ const AnimationGraphDescriptor* BehaviorVar::Patch(BSAnimationGraphManager* apMa
foundRep.creatureName,
foundRep.signatureVar);

// Save the new hash for the actor. Save a copy to restore to if overwritten; this currently
// only happens when humanoids enter beast mode, Humanoid copy is used to change back.
// We also save the original (unmodded) STR hash, because there are some hardwired tests
// that need it (like the IsDragon() test).
// Save the new hash for the actor. Save copies of the new hash when humanoids (Master_Behavior)
// or dragons (BHR_Master) are modified. The humanoid hash is used to return from beast mode
// (werewolf or vampire), and the dragon hash is used to implement an IsDragon() primitive.
pExtendedActor->GraphDescriptorHash = hash;
pExtendedActor->HumanoidGraphDescriptorHash = hash;
pExtendedActor->UnmoddedGraphDescriptorHash = foundRep.origHash;
switch (foundRep.origHash)
{
case AnimationGraphDescriptor_Master_Behavior::m_key:
m_humanoidGraphDescriptorHash = hash;
spdlog::info(__FUNCTION__ ": captured modified Master_Behavior hash {:X}", hash);
break;

case AnimationGraphDescriptor_BHR_Master::m_key:
m_dragonGraphDescriptorHash = hash;
spdlog::info(__FUNCTION__ ": captured modified BHR_Master (dragon) hash {:X}", hash);
break;
}

return ConstructModdedDescriptor(hash, foundRep, reverseMap);
}
Expand Down
10 changes: 10 additions & 0 deletions Code/client/ModCompat/BehaviorVar.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include <Structs/AnimationGraphDescriptorManager.h>
#include <Structs/Skyrim/AnimationGraphDescriptor_Master_Behavior.h>
#include <Structs/Skyrim/AnimationGraphDescriptor_BHR_Master.h>

struct BehaviorVar
{
Expand All @@ -20,6 +22,8 @@ struct BehaviorVar
const AnimationGraphDescriptor* Patch(BSAnimationGraphManager* apManager, Actor* apActor);
bool FailListed(const uint64_t acHash);
void FailList(const uint64_t acHash);
static const bool IsDragon(const uint64_t acHash) { return acHash == Get()->m_dragonGraphDescriptorHash; }
static const uint64_t GetHumanoidHash() { return Get()->m_humanoidGraphDescriptorHash; }

void Init();
void Debug();
Expand All @@ -28,6 +32,12 @@ struct BehaviorVar
static BehaviorVar* single;
uint64_t invocations = 0;

// These start out with the unmodded hashes and save the modded
// hash if behavior is modified. Humanoid is used to return from
// beast mode, and dragon is used for IsDragon() test.
uint64_t m_humanoidGraphDescriptorHash {AnimationGraphDescriptor_Master_Behavior::m_key};
uint64_t m_dragonGraphDescriptorHash {AnimationGraphDescriptor_BHR_Master::m_key};

void SeedAnimationVariables(
const uint64_t acHash,
const AnimationGraphDescriptor* acpDescriptor,
Expand Down

0 comments on commit e751157

Please sign in to comment.