Skip to content

Commit d6ce567

Browse files
Merge pull request #659 from rfortier/feat-modded-anim-compat
Feature: support for behavior mods
2 parents a6fff0c + 137e332 commit d6ce567

File tree

195 files changed

+2933
-143
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

195 files changed

+2933
-143
lines changed

Code/client/Games/ActorExtension.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ struct ActorExtension
2121
ActionEvent LatestAnimation{};
2222
size_t GraphDescriptorHash = 0;
2323

24-
private:
24+
private:
2525
uint32_t onlineFlags{0};
2626
};

Code/client/Games/References.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <Services/DebugService.h>
4040
#include <World.h>
4141

42+
4243
#if TP_SKYRIM64
4344
#include <Combat/CombatController.h>
4445
#include <AI/AITimer.h>
@@ -49,6 +50,8 @@
4950
#include <Structs/Fallout4/AnimationGraphDescriptor_Master_Behavior.h>
5051
#endif
5152

53+
extern const AnimationGraphDescriptor* BehaviorVarPatch(BSAnimationGraphManager* pManager, Actor* pActor);
54+
5255
using ScopedReferencesOverride = ScopedOverride<TESObjectREFR>;
5356
thread_local uint32_t ScopedReferencesOverride::s_refCount = 0;
5457

@@ -219,6 +222,11 @@ void TESObjectREFR::SaveAnimationVariables(AnimationVariables& aVariables) const
219222

220223
auto pDescriptor = AnimationGraphDescriptorManager::Get().GetDescriptor(pExtendedActor->GraphDescriptorHash);
221224

225+
// Modded behavior check if descriptor wasn't found
226+
extern const AnimationGraphDescriptor* BehaviorVarPatch(BSAnimationGraphManager * pManager, Actor * pActor);
227+
if (!pDescriptor)
228+
pDescriptor = BehaviorVarPatch(pManager, pActor);
229+
222230
if (!pDescriptor)
223231
return;
224232

@@ -227,10 +235,9 @@ void TESObjectREFR::SaveAnimationVariables(AnimationVariables& aVariables) const
227235
if (!pVariableSet)
228236
return;
229237

230-
aVariables.Booleans = 0;
231-
232-
aVariables.Floats.resize(pDescriptor->FloatLookupTable.size());
233-
aVariables.Integers.resize(pDescriptor->IntegerLookupTable.size());
238+
aVariables.Booleans.assign(pDescriptor->BooleanLookUpTable.size(), false);
239+
aVariables.Floats.assign(pDescriptor->FloatLookupTable.size(), 0.f);
240+
aVariables.Integers.assign(pDescriptor->IntegerLookupTable.size(), 0);
234241

235242
#if TP_FALLOUT4
236243
// TODO: maybe send a var with the variables indicating first or third person?
@@ -251,14 +258,14 @@ void TESObjectREFR::SaveAnimationVariables(AnimationVariables& aVariables) const
251258
continue;
252259

253260
if (pFirstPersonVariables->data[*firstPersonIdx] != 0)
254-
aVariables.Booleans |= (1ull << i);
261+
aVariables.Booleans[i] = true;
255262

256263
continue;
257264
}
258265
#endif
259266

260267
if (pVariableSet->data[idx] != 0)
261-
aVariables.Booleans |= (1ull << i);
268+
aVariables.Booleans[i] = true;
262269
}
263270

264271
for (size_t i = 0; i < pDescriptor->FloatLookupTable.size(); ++i)
@@ -339,6 +346,10 @@ void TESObjectREFR::LoadAnimationVariables(const AnimationVariables& aVariables)
339346

340347
auto pDescriptor = AnimationGraphDescriptorManager::Get().GetDescriptor(pExtendedActor->GraphDescriptorHash);
341348

349+
// Modded behavior check if descriptor wasn't found
350+
if (!pDescriptor)
351+
pDescriptor = BehaviorVarPatch(pManager, pActor);
352+
342353
if (!pDescriptor)
343354
return;
344355

@@ -361,7 +372,7 @@ void TESObjectREFR::LoadAnimationVariables(const AnimationVariables& aVariables)
361372

362373
if (pVariableSet->size > idx)
363374
{
364-
pVariableSet->data[idx] = (aVariables.Booleans & (1ull << i)) != 0;
375+
pVariableSet->data[idx] = aVariables.Booleans.size() > i ? aVariables.Booleans[i] : false;
365376
}
366377
}
367378

Code/client/Games/Skyrim/Actor.cpp

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
#include <Forms/BGSOutfit.h>
5050
#include <Forms/TESObjectARMO.h>
5151

52+
#include <ModCompat/BehaviorVar.h>
53+
5254
#ifdef SAVE_STUFF
5355

5456
#include <Games/Skyrim/SaveLoad.h>
@@ -542,18 +544,8 @@ bool Actor::IsDead() const noexcept
542544

543545
bool Actor::IsDragon() const noexcept
544546
{
545-
// TODO: if anyone has a better way of doing this, please do tell.
546-
BSAnimationGraphManager* pManager = nullptr;
547-
animationGraphHolder.GetBSAnimationGraph(&pManager);
548-
549-
if (!pManager)
550-
return false;
551-
552-
const auto* pGraph = pManager->animationGraphs.Get(pManager->animationGraphIndex);
553-
if (!pGraph)
554-
return false;
555-
556-
return AnimationGraphDescriptor_BHR_Master::m_key == pManager->GetDescriptorKey();
547+
const ActorExtension* pExtension = const_cast<Actor*>(this)->GetExtension();
548+
return BehaviorVar::IsDragon(pExtension->GraphDescriptorHash);
557549
}
558550

559551
void Actor::Kill() noexcept

Code/client/Games/Skyrim/PlayerCharacter.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
#include <Forms/TESObjectCELL.h>
2424

25+
#include <ModCompat/BehaviorVar.h>
26+
2527
int32_t PlayerCharacter::LastUsedCombatSkill = -1;
2628

2729
TP_THIS_FUNCTION(TPickUpObject, char, PlayerCharacter, TESObjectREFR* apObject, int32_t aCount, bool aUnk1, bool aUnk2);
@@ -162,7 +164,7 @@ void TP_MAKE_THISCALL(HookSetBeastForm, void, void* apUnk1, void* apUnk2, bool a
162164
{
163165
if (!aEntering)
164166
{
165-
PlayerCharacter::Get()->GetExtension()->GraphDescriptorHash = AnimationGraphDescriptor_Master_Behavior::m_key;
167+
PlayerCharacter::Get()->GetExtension()->GraphDescriptorHash = BehaviorVar::GetHumanoidHash();
166168
World::Get().GetRunner().Trigger(BeastFormChangeEvent());
167169
}
168170

184 KB
Binary file not shown.
221 KB
Binary file not shown.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include "BehaviorVarsMap.h"
2+
#define MAGIC_ENUM_RANGE_MAX 400
3+
#include "magic_enum.hpp"
4+
5+
namespace BehaviorOrig
6+
{
7+
template <typename V> void GenerateFromEnum(uint64_t aKey)
8+
{
9+
using namespace magic_enum;
10+
constexpr size_t count = enum_count<V>();
11+
constexpr auto variables_values = enum_values<V>();
12+
constexpr auto variables_names = enum_names<V>();
13+
BehaviorVars sig;
14+
15+
sig.m_key = aKey;
16+
for (size_t index = 0; index < count; index++)
17+
{
18+
TiltedPhoques::String name(variables_names[index]);
19+
uint32_t value = enum_integer(variables_values[index]);
20+
21+
// Remove the leading 'k'
22+
name = name.substr(1, name.size() - 1);
23+
24+
sig.m_nameMap[name] = value;
25+
sig.m_valueMap[value] = name;
26+
}
27+
28+
BehaviorVarsMap::getInstance().Register(sig);
29+
};
30+
31+
#ifdef TP_SKYRIM
32+
#include "BehaviorOrig.Skyrim"
33+
#else
34+
#include "BehaviorOrig.Fallout4"
35+
#endif
36+
37+
} // namespace BehaviorOrig
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
param (
2+
[string]$name = "Skyrim"
3+
)
4+
5+
Set-PSDebug -Trace 1
6+
7+
$output = "BehaviorOrig$name.obs"
8+
$path = "..\..\encoding\Structs\$name"
9+
$files = Get-ChildItem $path -Filter *_*.cpp
10+
11+
# Output everything to the target file.
12+
$(
13+
14+
15+
"// DO NOT EDIT, MACHINE-GENERATED CODE
16+
//
17+
// This is an admittedly dirty approach, but done with the best of intentions.
18+
//
19+
// The idea is to minimize intrusion into the STR Devs's base code, but we
20+
// need some information that isn't exposed (the behavior variables' strings,
21+
// not just their enum values). This is needed so we can translate the numeric
22+
// original behavior vars back to their string names, so we can then look up the
23+
// NEW numeric value when behaviors are modified.
24+
//
25+
// That would require a simple code change, but one needed so ubiquitously
26+
// it is intrusive. If the STR team gives permission for the change, this
27+
// can all be cleaned up. Unless and until they do, we make some edited copies
28+
// of the relevant behavior structs in their own namespace to provide the needed info.
29+
//
30+
// Would be so much easier if Nemesis simply added new vars to the end of the
31+
// list, rather than randomly rearranging it. Rumor is Pandora does it correctly.
32+
//
33+
"
34+
35+
36+
$creatures = "`r`nvoid BehaviorOrigInit()`r`n{`r`n"
37+
38+
foreach ($f in $files)
39+
{
40+
$path = $f.FullName
41+
$pathparts = Get-Item $path
42+
$file = $pathparts.Name
43+
$creature = $pathparts.BaseName
44+
$creatures = $creatures + " (void) $creature::getInstance();`r`n"
45+
46+
$extract = "`r`nclass $creature`r`n{`r`n private:"
47+
$extract
48+
49+
# For the enum declaration, match non-greedy to stop at first close brace.
50+
# Master_Behavior special case since they include more stuff.
51+
# Also even more special for Fallout 3rd-person, since they inject namespaces
52+
# we need a look-behind to get rid of it.
53+
$pattern = "namespace TP"
54+
$extract = [Regex]::Match((Get-Content $path -Raw), $pattern).Value
55+
if ([string]::IsNullOrEmpty($extract)) {
56+
$pattern = "(?s) *enum Variables(.*?)};"
57+
} else {
58+
$pattern = "(?s)(?<=namespace TP`r`n{`r`n) *enum Variables(.*?)};"
59+
}
60+
61+
$extract = [Regex]::Match((Get-Content $path -Raw), $pattern).Value
62+
$extract
63+
64+
$pattern = "key =.*;"
65+
$extract = [Regex]::Match((Get-Content $path -Raw), $pattern).Value
66+
67+
# If no key was found in the .cpp file it is because for some creatures it is in .h file
68+
if ([string]::IsNullOrEmpty($extract))
69+
{
70+
$hpath = Get-Item $path
71+
$hpath = Join-Path $hpath.DirectoryName ($hpath.BaseName + ".h")
72+
$extract = [Regex]::Match((Get-Content $hpath -Raw), $pattern).Value
73+
}
74+
75+
$extract = ' static const uint64_t m_' + $extract + "`r`n"
76+
$extract
77+
78+
# Constructor
79+
$extract = " // Constructor
80+
$creature()
81+
{
82+
BehaviorOrig::GenerateFromEnum<Variables>(m_key);
83+
}
84+
85+
// Singleton
86+
$creature($creature const&) = delete;
87+
void operator=($creature const&) = delete;
88+
89+
public:
90+
static $creature& getInstance()
91+
{
92+
// The only instance. Guaranteed lazy initiated
93+
// Guaranteed that it will be destroyed correctly
94+
static $creature instance;
95+
96+
return instance;
97+
}`r`n};"
98+
99+
$extract
100+
}
101+
102+
$creatures = $creatures + "};`r`n"
103+
$creatures
104+
105+
106+
) *>&1 > $output
107+
# Close redirection

0 commit comments

Comments
 (0)