Skip to content

Commit fca4be5

Browse files
committed
bunch of fixes
1 parent 1c35ab1 commit fca4be5

File tree

8 files changed

+451
-231
lines changed

8 files changed

+451
-231
lines changed

cmake/headerlist.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ set(headers ${headers}
44
include/Graphics.h
55
include/Hooks.h
66
include/PCH.h
7+
include/Serialization.h
78
include/Settings.h
89
)

cmake/sourcelist.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set(sources ${sources}
33
src/Graphics.cpp
44
src/Hooks.cpp
55
src/PCH.cpp
6+
src/Serialization.cpp
67
src/Settings.cpp
78
src/main.cpp
89
)

include/Graphics.h

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,26 @@ namespace Graphics
112112

113113
namespace Slot
114114
{
115-
void set_toggle_data(RE::NiAVObject* a_object, const Biped a_slot, bool a_hide);
115+
void set_toggle_data(const RE::Actor* a_actor, const Biped a_slot, bool a_hide);
116116

117-
bool get_toggle_data(RE::NiAVObject* a_object, const Biped a_slot, bool a_default);
117+
bool get_toggle_data(const RE::Actor* a_actor, const Biped a_slot, bool a_default);
118+
119+
static SlotData get_head_slots()
120+
{
121+
for (auto& data : armorSlots | std::views::values) {
122+
auto& [autoToggle, slots] = data;
123+
for (auto& slot : slots) {
124+
if (headSlots.count(slot)) {
125+
return data;
126+
}
127+
}
128+
}
129+
return { false, std::set<Biped>{} };
130+
}
118131

119132
struct detail
120133
{
121-
static void toggle_decal(RE::NiAVObject* a_root, RE::NiAVObject* a_node, bool a_hide)
134+
static void toggle_decal(RE::NiAVObject* a_root, RE::NiAVObject* a_node, bool a_hide)
122135
{
123136
if (const auto decalNode = netimmerse_cast<RE::BGSDecalNode*>(a_root->GetObjectByName(RE::FixedStrings::GetSingleton()->skinnedDecalNode))) {
124137
RE::BSVisit::TraverseScenegraphGeometries(a_node, [&](RE::BSGeometry* a_geometry) -> RE::BSVisit::BSVisitControl {
@@ -150,7 +163,7 @@ namespace Graphics
150163
continue;
151164
}
152165
if (const auto node = object.partClone.get(); node) {
153-
set_toggle_data(a_root, slot, a_hide);
166+
set_toggle_data(a_actor, slot, a_hide);
154167

155168
node->CullNode(a_hide);
156169
if (slot < Biped::kEditorTotal) {
@@ -192,8 +205,8 @@ namespace Graphics
192205
continue;
193206
}
194207
if (const auto node = object.partClone.get(); node) {
195-
const auto hiddenState = get_toggle_data(a_root, slot, autoToggle);
196-
set_toggle_data(a_root, slot, !hiddenState);
208+
const auto hiddenState = get_toggle_data(a_actor, slot, autoToggle);
209+
set_toggle_data(a_actor, slot, !hiddenState);
197210

198211
node->CullNode(!hiddenState);
199212
if (slot < Biped::kEditorTotal) {
@@ -221,23 +234,23 @@ namespace Graphics
221234
});
222235
}
223236

224-
static void toggle_slots_synced(RE::Actor* a_actor, const RE::BSTSmartPointer<RE::BipedAnim>& a_biped, RE::NiAVObject* a_root, RE::NiAVObject* a_playerRoot, const SlotData& a_slotData)
237+
static void toggle_slots_synced(RE::Actor* a_actor, const RE::BSTSmartPointer<RE::BipedAnim>& a_biped, RE::NiAVObject* a_root, const SlotData& a_slotData)
225238
{
226-
if (!a_actor || !a_biped || !a_root || !a_playerRoot) {
239+
if (!a_actor || !a_biped || !a_root) {
227240
return;
228241
}
229242

230243
const auto task = SKSE::GetTaskInterface();
231-
task->AddTask([a_biped, a_slotData, a_actor, a_root, a_playerRoot]() {
244+
task->AddTask([a_biped, a_slotData, a_actor, a_root]() {
232245
auto& [autoToggle, slots] = a_slotData;
233246
for (auto& slot : slots) {
234247
auto& object = a_biped->objects[slot];
235248
if (const auto item = object.item; !item) {
236249
continue;
237250
}
238251
if (const auto node = object.partClone.get(); node) {
239-
const auto hiddenState = !get_toggle_data(a_playerRoot, slot, autoToggle);
240-
set_toggle_data(a_root, slot, !hiddenState);
252+
const auto hiddenState = !get_toggle_data(RE::PlayerCharacter::GetSingleton(), slot, autoToggle);
253+
set_toggle_data(RE::PlayerCharacter::GetSingleton(), slot, !hiddenState);
241254

242255
node->SetAppCulled(!hiddenState);
243256
if (slot < Biped::kEditorTotal) {
@@ -268,6 +281,7 @@ namespace Graphics
268281

269282
void ToggleActorEquipment(RE::Actor* a_actor, bool a_hide);
270283
void ToggleActorEquipment(RE::Actor* a_actor, const SlotData& a_slotData);
284+
void ToggleActorHeadParts(RE::Actor* a_actor, bool a_hide);
271285

272286
void ToggleFollowerEquipment(bool a_hide);
273287
void ToggleFollowerEquipment(const SlotData& a_slotData, bool a_playerSync = false);

include/Hooks.h

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Hooks
1010
static std::tuple<bool, bool, Biped> get_slot_info(const SlotKeyVec& a_vec, std::function<bool(Biped a_slot)> a_func)
1111
{
1212
for (const auto& data : a_vec | std::views::values) {
13-
const auto& [autoToggle, slots] = data;
13+
const auto& [autoToggle, slots] = data;
1414
for (auto& slot : slots) {
1515
if (a_func(slot)) {
1616
return { true, autoToggle, slot };
@@ -20,17 +20,6 @@ namespace Hooks
2020
return { false, true, Biped::kNone };
2121
}
2222

23-
static std::pair<bool, bool> get_head_slots()
24-
{
25-
for (auto& data : armorSlots | std::views::values) {
26-
const auto& [autoToggle, slots] = data;
27-
if (std::ranges::includes(slots, headSlots)) {
28-
return { true, autoToggle };
29-
}
30-
}
31-
return { false, false };
32-
}
33-
3423
static void hide_armor(const RE::BipedAnim* a_biped, RE::NiAVObject* a_object, std::int32_t a_slot)
3524
{
3625
if (const auto ref = a_biped->actorRef.get(); ref) {
@@ -41,7 +30,7 @@ namespace Hooks
4130
auto [contains, autoToggle, matchingSlot] = get_slot_info(armorSlots, [&](const auto& b_slot) {
4231
return b_slot == slot;
4332
});
44-
if (contains && Graphics::Slot::get_toggle_data(root, slot, autoToggle)) {
33+
if (contains && Graphics::Slot::get_toggle_data(actor, slot, autoToggle)) {
4534
a_object->CullNode(true);
4635
}
4736
}
@@ -101,9 +90,9 @@ namespace Hooks
10190
if (actor && Settings::GetSingleton()->CanToggleEquipment(actor)) {
10291
auto slot = static_cast<Biped>(a_slot);
10392
auto [contains, autoToggle, matchingSlot] = detail::get_slot_info(weaponSlots, [&](const auto& b_slot) {
104-
return b_slot == slot;
93+
return b_slot == slot;
10594
});
106-
if (contains && Graphics::Slot::get_toggle_data(a_root3D, slot, autoToggle)) {
95+
if (contains && Graphics::Slot::get_toggle_data(actor, slot, autoToggle)) {
10796
object->CullNode(true);
10897
}
10998
}
@@ -122,7 +111,7 @@ namespace Hooks
122111

123112
switch (Settings::GetSingleton()->autoToggleType) {
124113
case Type::kPlayerOnly:
125-
stl::write_thunk_call<AttachWeaponModelToActor>(target.address() + OFFSET(0x17F, 0x2B1));
114+
stl::write_thunk_call<AttachWeaponModelToActor>(target.address() + OFFSET(0x17F, 0x2B1));
126115
break;
127116
case Type::kFollowerOnly:
128117
stl::write_thunk_call<AttachWeaponModelToActor>(target.address() + OFFSET(0x1D0, 0x2FA));
@@ -142,17 +131,17 @@ namespace Hooks
142131

143132
namespace Head
144133
{
145-
struct GetRootNode
134+
struct GetRootNode // HEAD
146135
{
147136
static RE::NiAVObject* thunk(RE::Actor* a_actor)
148137
{
149138
const auto root = func(a_actor);
150139

151140
if (a_actor && root && Settings::GetSingleton()->CanToggleEquipment(a_actor)) {
152-
auto [contains, autoToggle] = detail::get_head_slots();
153-
if (contains && Graphics::Slot::get_toggle_data(root, Biped::kHair, autoToggle)) {
154-
Graphics::Head::UpdateHair(a_actor, root, true);
155-
return nullptr;
141+
auto [autoToggle, slots] = Graphics::Slot::get_head_slots();
142+
if (!slots.empty() && Graphics::Slot::get_toggle_data(a_actor, Biped::kHead, autoToggle)) {
143+
Graphics::Slot::ToggleActorHeadParts(a_actor, true);
144+
return nullptr;
156145
}
157146
}
158147

@@ -165,20 +154,17 @@ namespace Hooks
165154
{
166155
static void thunk(RE::BipedAnim* a_biped, RE::NiAVObject* a_geometry, std::uint32_t a_slot)
167156
{
168-
if (a_biped) {
169-
if (const auto ref = a_biped->actorRef.get(); ref) {
170-
const auto actor = ref->As<RE::Actor>();
171-
if (actor && Settings::GetSingleton()->CanToggleEquipment(actor)) {
172-
if (const auto root = a_biped->root; root) {
173-
auto slot = static_cast<Biped>(a_slot);
174-
auto [contains, autoToggle, matchingSlot] = detail::get_slot_info(armorSlots, [&slot](const auto& b_slot) {
175-
return slot = b_slot;
176-
});
177-
if (contains && Graphics::Slot::get_toggle_data(root, slot, autoToggle)) {
178-
Graphics::Head::UpdateFacePartitions(actor, a_geometry, slot, true);
179-
return;
180-
}
181-
}
157+
const auto ref = a_biped ? a_biped->actorRef.get() : RE::NiPointer<RE::TESObjectREFR>();
158+
const auto actor = ref ? ref->As<RE::Actor>() : nullptr;
159+
160+
if (a_biped && actor && Settings::GetSingleton()->CanToggleEquipment(actor)) {
161+
if (const auto root = a_biped->root; root) {
162+
auto slot = static_cast<Biped>(a_slot);
163+
auto [contains, autoToggle, matchingSlot] = detail::get_slot_info(armorSlots, [&slot](const auto& b_slot) {
164+
return slot = b_slot;
165+
});
166+
if (contains && Graphics::Slot::get_toggle_data(actor, slot, autoToggle)) {
167+
return;
182168
}
183169
}
184170
}
@@ -193,7 +179,7 @@ namespace Hooks
193179
REL::Relocation<std::uintptr_t> UpdateHairAndHead{ RELOCATION_ID(24220, 24724), OFFSET(0x1A, 0x19) };
194180
stl::write_thunk_call<GetRootNode>(UpdateHairAndHead.address());
195181

196-
REL::Relocation<std::uintptr_t> ProcessArmorDismemberment{ RELOCATION_ID(15539, 15715), OFFSET(0x70, 0x2C2) }; //everything got inlined in AE
182+
REL::Relocation<std::uintptr_t> ProcessArmorDismemberment{ RELOCATION_ID(15539, 15715), OFFSET(0x70, 0x2C2) }; //everything got inlined in AE
197183
stl::write_thunk_call<UpdateDismemberPartition>(ProcessArmorDismemberment.address());
198184
}
199185
}

include/Serialization.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
3+
namespace Serialization
4+
{
5+
constexpr std::uint32_t kSerializationVersion = 1;
6+
constexpr std::uint32_t kEquipmentToggle = 'ETOG';
7+
constexpr std::uint32_t kAutoToggle = 'TOGG';
8+
9+
class AutoToggleMap
10+
{
11+
public:
12+
static AutoToggleMap* GetSingleton()
13+
{
14+
static AutoToggleMap singleton;
15+
return &singleton;
16+
}
17+
18+
bool Add(const RE::Actor* a_actor, Biped a_slot, bool a_toggleState);
19+
bool Remove(const RE::Actor* a_actor);
20+
std::int32_t GetToggleState(const RE::Actor* a_actor, Biped a_slot);
21+
22+
void Clear();
23+
bool Save(SKSE::SerializationInterface* a_intfc, std::uint32_t a_type, std::uint32_t a_version);
24+
bool Save(SKSE::SerializationInterface* a_intfc);
25+
bool Load(SKSE::SerializationInterface* a_intfc);
26+
27+
protected:
28+
AutoToggleMap() = default;
29+
AutoToggleMap(const AutoToggleMap&) = delete;
30+
AutoToggleMap(AutoToggleMap&&) = delete;
31+
~AutoToggleMap() = default;
32+
33+
AutoToggleMap& operator=(const AutoToggleMap&) = delete;
34+
AutoToggleMap& operator=(AutoToggleMap&&) = delete;
35+
36+
using Lock = std::recursive_mutex;
37+
using Locker = std::lock_guard<Lock>;
38+
39+
mutable Lock _lock{};
40+
std::map<RE::FormID, std::map<Biped, bool>> _map{};
41+
};
42+
43+
std::string DecodeTypeCode(std::uint32_t a_typeCode);
44+
45+
void SaveCallback(SKSE::SerializationInterface* a_intfc);
46+
void LoadCallback(SKSE::SerializationInterface* a_intfc);
47+
void RevertCallback(SKSE::SerializationInterface* a_intfc);
48+
}

0 commit comments

Comments
 (0)