Skip to content

Commit

Permalink
Fix SE offset
Browse files Browse the repository at this point in the history
Log errors to console
Prevent VR package build
  • Loading branch information
powerof3 committed Nov 12, 2022
1 parent e76afed commit 7c78f65
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 147 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ jobs:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
CMAKE_PROJECT_DIR: "SPID"
CMAKE_VR_CONFIG_PRESET: ''
CMAKE_VR_BUILD_PRESET: ''
AE_353_BRANCH: master-1.6.353
FOMOD_INCLUDE_PDB: true
FOMOD_MOD_NAME: "Spell Perk Item Distributor"
Expand Down
5 changes: 0 additions & 5 deletions SPID/include/Defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,6 @@ namespace INI
};

using Values = std::vector<std::pair<std::string, std::string>>;

inline bool is_mod_name(const std::string& a_str)
{
return a_str.rfind(".esp") != std::string::npos || a_str.rfind(".esl") != std::string::npos || a_str.rfind(".esm") != std::string::npos;
}
}

namespace DATA
Expand Down
2 changes: 1 addition & 1 deletion SPID/include/LookupConfigs.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,5 +197,5 @@ namespace INI
return std::make_tuple(data, std::nullopt);
}

bool Read();
std::pair<bool,bool> Read();
}
6 changes: 3 additions & 3 deletions SPID/include/LookupForms.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,12 @@ namespace Lookup
}
}
if (!form) {
logger::error(" [{}] [0x{:X}] ({}) FAIL - formID doesn't exist", path, *formID, modName.value_or(""));
logger::error("\t\t[{}] [0x{:X}] ({}) FAIL - formID doesn't exist", path, *formID, modName.value_or(""));
} else {
if constexpr (std::is_same_v<Form, RE::BGSKeyword>) {
if (string::is_empty(form->GetFormEditorID())) {
form = nullptr;
logger::error(" [{}] [0x{:X}] ({}) FAIL - keyword does not have a valid editorID", path, *formID, modName.value_or(""));
logger::error("\t\t[{}] [0x{:X}] ({}) FAIL - keyword does not have a valid editorID", path, *formID, modName.value_or(""));
}
}
}
Expand All @@ -181,7 +181,7 @@ namespace Lookup
auto& keywordArray = a_dataHandler->GetFormArray<RE::BGSKeyword>();

auto result = std::find_if(keywordArray.begin(), keywordArray.end(), [&](const auto& keyword) {
return keyword && string::iequals(keyword->formEditorID, keywordName);
return keyword && keyword->formEditorID == keywordName.c_str();
});

if (result != keywordArray.end()) {
Expand Down
258 changes: 129 additions & 129 deletions SPID/src/DistributePCLevelMult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,140 +4,140 @@

namespace Distribute::PlayerLeveledActor
{
struct HandleUpdatePlayerLevel
{
static void thunk(RE::Actor* a_actor)
{
if (const auto npc = a_actor->GetActorBase(); npc && npc->HasPCLevelMult()) {
const auto input = npc->IsDynamicForm() ? PCLevelMult::Input{ a_actor, npc, true, false } : // use character formID for permanent storage
PCLevelMult::Input{ npc, true, false };
Distribute(npc, input);
}
struct HandleUpdatePlayerLevel
{
static void thunk(RE::Actor* a_actor)
{
if (const auto npc = a_actor->GetActorBase()) {
const auto input = npc->IsDynamicForm() ? PCLevelMult::Input{ a_actor, npc, true, false } : // use character formID for permanent storage
PCLevelMult::Input{ npc, true, false };
Distribute(npc, input);
}

func(a_actor);
}
static inline REL::Relocation<decltype(thunk)> func;
};
func(a_actor);
}
static inline REL::Relocation<decltype(thunk)> func;
};

struct LoadGame
{
static void thunk(RE::Character* a_this, std::uintptr_t a_buf)
{
if (const auto actorbase = a_this->GetActorBase(); actorbase && actorbase->HasPCLevelMult()) {
const auto input = actorbase->IsDynamicForm() ? PCLevelMult::Input{ a_this, actorbase, true, false } : // use character formID for permanent storage
PCLevelMult::Input{ actorbase, true, false };
struct LoadGame
{
static void thunk(RE::Character* a_this, std::uintptr_t a_buf)
{
if (const auto actorbase = a_this->GetActorBase(); actorbase && actorbase->HasPCLevelMult()) {
const auto input = actorbase->IsDynamicForm() ? PCLevelMult::Input{ a_this, actorbase, true, false } : // use character formID for permanent storage
PCLevelMult::Input{ actorbase, true, false };

if (const auto pcLevelMultManager = PCLevelMult::Manager::GetSingleton(); !pcLevelMultManager->FindDistributedEntry(input)) {
//start distribution for first time
Distribute(actorbase, input);
} else {
//handle redistribution and removal
pcLevelMultManager->ForEachDistributedEntry(input, [&](RE::TESForm& a_form, [[maybe_unused]] IdxOrCount a_count, bool a_isBelowLevel) {
switch (a_form.GetFormType()) {
case RE::FormType::Keyword:
{
auto keyword = a_form.As<RE::BGSKeyword>();
if (a_isBelowLevel) {
actorbase->RemoveKeyword(keyword);
} else {
actorbase->AddKeyword(keyword);
}
}
break;
case RE::FormType::Faction:
{
auto faction = a_form.As<RE::TESFaction>();
auto it = std::ranges::find_if(actorbase->factions, [&](const auto& factionRank) {
return factionRank.faction == faction;
});
if (it != actorbase->factions.end()) {
if (a_isBelowLevel) {
(*it).rank = -1;
} else {
(*it).rank = 1;
}
}
}
break;
case RE::FormType::Perk:
{
auto perk = a_form.As<RE::BGSPerk>();
if (a_isBelowLevel) {
actorbase->RemovePerk(perk);
} else {
actorbase->AddPerk(perk, 1);
}
}
break;
case RE::FormType::Spell:
{
auto spell = a_form.As<RE::SpellItem>();
if (auto actorEffects = actorbase->GetSpellList()) {
if (a_isBelowLevel) {
actorEffects->RemoveSpell(spell);
} else if (!actorEffects->GetIndex(spell)) {
actorEffects->AddSpell(spell);
}
}
}
break;
case RE::FormType::LeveledSpell:
{
auto spell = a_form.As<RE::TESLevSpell>();
if (auto actorEffects = actorbase->GetSpellList()) {
if (a_isBelowLevel) {
actorEffects->RemoveLevSpell(spell);
} else {
actorEffects->AddLevSpell(spell);
}
}
}
break;
case RE::FormType::Shout:
{
auto shout = a_form.As<RE::TESShout>();
if (auto actorEffects = actorbase->GetSpellList()) {
if (a_isBelowLevel) {
actorEffects->RemoveShout(shout);
} else {
actorEffects->AddShout(shout);
}
}
}
break;
default:
{
if (a_form.IsInventoryObject()) {
auto boundObject = static_cast<RE::TESBoundObject*>(&a_form);
if (a_isBelowLevel) {
actorbase->RemoveObjectFromContainer(boundObject, a_count);
} else if (actorbase->CountObjectsInContainer(boundObject) < a_count) {
actorbase->AddObjectToContainer(boundObject, a_count, a_this);
}
}
}
break;
}
});
}
}
if (const auto pcLevelMultManager = PCLevelMult::Manager::GetSingleton(); !pcLevelMultManager->FindDistributedEntry(input)) {
//start distribution for first time
Distribute(actorbase, input);
} else {
//handle redistribution and removal
pcLevelMultManager->ForEachDistributedEntry(input, [&](RE::TESForm& a_form, [[maybe_unused]] IdxOrCount a_count, bool a_isBelowLevel) {
switch (a_form.GetFormType()) {
case RE::FormType::Keyword:
{
const auto keyword = a_form.As<RE::BGSKeyword>();
if (a_isBelowLevel) {
actorbase->RemoveKeyword(keyword);
} else {
actorbase->AddKeyword(keyword);
}
}
break;
case RE::FormType::Faction:
{
auto faction = a_form.As<RE::TESFaction>();
const auto it = std::ranges::find_if(actorbase->factions, [&](const auto& factionRank) {
return factionRank.faction == faction;
});
if (it != actorbase->factions.end()) {
if (a_isBelowLevel) {
(*it).rank = -1;
} else {
(*it).rank = 1;
}
}
}
break;
case RE::FormType::Perk:
{
const auto perk = a_form.As<RE::BGSPerk>();
if (a_isBelowLevel) {
actorbase->RemovePerk(perk);
} else {
actorbase->AddPerk(perk, 1);
}
}
break;
case RE::FormType::Spell:
{
const auto spell = a_form.As<RE::SpellItem>();
if (const auto actorEffects = actorbase->GetSpellList()) {
if (a_isBelowLevel) {
actorEffects->RemoveSpell(spell);
} else if (!actorEffects->GetIndex(spell)) {
actorEffects->AddSpell(spell);
}
}
}
break;
case RE::FormType::LeveledSpell:
{
const auto spell = a_form.As<RE::TESLevSpell>();
if (const auto actorEffects = actorbase->GetSpellList()) {
if (a_isBelowLevel) {
actorEffects->RemoveLevSpell(spell);
} else {
actorEffects->AddLevSpell(spell);
}
}
}
break;
case RE::FormType::Shout:
{
const auto shout = a_form.As<RE::TESShout>();
if (const auto actorEffects = actorbase->GetSpellList()) {
if (a_isBelowLevel) {
actorEffects->RemoveShout(shout);
} else {
actorEffects->AddShout(shout);
}
}
}
break;
default:
{
if (a_form.IsInventoryObject()) {
const auto boundObject = static_cast<RE::TESBoundObject*>(&a_form);
if (a_isBelowLevel) {
actorbase->RemoveObjectFromContainer(boundObject, a_count);
} else if (actorbase->CountObjectsInContainer(boundObject) < a_count) {
actorbase->AddObjectToContainer(boundObject, a_count, a_this);
}
}
}
break;
}
});
}
}

func(a_this, a_buf);
}
static inline REL::Relocation<decltype(thunk)> func;
func(a_this, a_buf);
}
static inline REL::Relocation<decltype(thunk)> func;

static inline size_t index{ 0 };
static inline size_t size{ 0xF };
};
static inline size_t index{ 0 };
static inline size_t size{ 0xF };
};

void Install()
{
// ProcessLists::HandleUpdate
// inlined into SetLevel in AE
REL::Relocation<std::uintptr_t> target{ RELOCATION_ID(40575, 41567), OFFSET(0x86, 0x137) };
stl::write_thunk_call<HandleUpdatePlayerLevel>(target.address());
void Install()
{
// ProcessLists::HandlePlayerLevelUpdate
// inlined into SetLevel in AE
REL::Relocation<std::uintptr_t> target{ RELOCATION_ID(40575, 41567), OFFSET(0x97, 0x137) };
stl::write_thunk_call<HandleUpdatePlayerLevel>(target.address());

stl::write_vfunc<RE::Character, LoadGame>();
logger::info(" Hooked npc load save");
}
stl::write_vfunc<RE::Character, LoadGame>();
logger::info(" Hooked npc load save");
}
}
11 changes: 8 additions & 3 deletions SPID/src/LookupConfigs.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#include "LookupConfigs.h"

bool INI::Read()
std::pair<bool, bool> INI::Read()
{
logger::info("{:*^50}", "INI");

std::vector<std::string> files = distribution::get_configs(R"(Data\)", "_DISTR"sv);

if (files.empty()) {
logger::warn(" No .ini files with _DISTR suffix were found within the Data folder, aborting...");
return false;
return { false, false };
}

logger::info("\t{} matching inis found", files.size());
Expand All @@ -18,6 +18,8 @@ bool INI::Read()
configs[RECORD::add[i]] = INIDataVec{};
}

bool shouldLogErrors{ false };

for (auto& path : files) {
logger::info("\tINI : {}", path);

Expand All @@ -30,6 +32,8 @@ bool INI::Read()
continue;
}

string::replace_first_instance(path, "Data\\", "");

if (auto values = ini.GetSection(""); values && !values->empty()) {
std::multimap<CSimpleIniA::Entry, std::pair<std::string, std::string>, CSimpleIniA::Entry::LoadOrder> oldFormatMap;
for (auto& [key, entry] : *values) {
Expand All @@ -42,6 +46,7 @@ bool INI::Read()
}
} catch (...) {
logger::warn("\t\tFailed to parse entry [{} = {}]", key.pItem, entry);
shouldLogErrors = true;
}
}

Expand All @@ -59,5 +64,5 @@ bool INI::Read()
}
}

return true;
return { true, shouldLogErrors };
}
4 changes: 2 additions & 2 deletions SPID/src/PCLevelMultManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ namespace PCLevelMult
for (const auto& [level, cachedData] : levelMap) {
const bool is_below_level = a_input.npcLevel < level;
for (auto& [formid, idx] : cachedData.distributedEntries) {
if (auto form = RE::TESForm::LookupByID(formid)) {
if (const auto form = RE::TESForm::LookupByID(formid)) {
a_fn(*form, idx, is_below_level);
}
}
Expand All @@ -171,7 +171,7 @@ namespace PCLevelMult
}

// For spawned actors with FF reference IDs
void Manager::DeleteNPC(RE::FormID a_characterID)
void Manager::DeleteNPC(RE::FormID a_characterID)
{
auto& currentCache = cache[GetSingleton()->GetCurrentPlayerID()];
if (const auto it = currentCache.find(a_characterID); it != currentCache.end()) {
Expand Down
Loading

0 comments on commit 7c78f65

Please sign in to comment.