Skip to content

Commit

Permalink
finished chance based distribution
Browse files Browse the repository at this point in the history
  • Loading branch information
ponzipyramid authored and ThirdEyeSqueegee committed Jan 18, 2024
1 parent 8fa6be0 commit 2894f26
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 46 deletions.
2 changes: 2 additions & 0 deletions include/Distributor.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ class Distributor : public Singleton<Distributor>
static void RemoveDistribute(const Maps::TDistrVec& distr_vec) noexcept;

static void ReplaceDistribute(const Maps::TDistrVec& distr_vec) noexcept;

static void RuntimeDistribute(RE::TESObjectREFR* a_ref);
};
21 changes: 4 additions & 17 deletions include/Hooks.h
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
#pragma once
#include "Distributor.h"

namespace Hooks {

struct InitItemImpl
{
static void Thunk(RE::TESObjectREFR* a_ref)
{
if (a_ref->HasContainer()) {
func(a_ref);

logger::info("processing container: {}", a_ref->GetFormID());

auto inv = a_ref->GetInventory();
for (const auto& [form, data] : inv) {
logger::info("contains: {}", form->GetFormEditorID());
}

auto gold = RE::TESDataHandler::GetSingleton()->LookupForm(0xF, "Skyrim.esm");

a_ref->RemoveItem(gold->As<RE::TESBoundObject>(), 1000000, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr);
a_ref->AddObjectToContainer(gold->As<RE::TESBoundObject>(), nullptr, 100, nullptr);
func(a_ref);

return;
if (a_ref->HasContainer()) {
Distributor::RuntimeDistribute(a_ref);
}

func(a_ref);
}
static inline REL::Relocation<decltype(Thunk)> func;
static inline constexpr std::size_t idx{ 0x13 };
Expand Down
32 changes: 26 additions & 6 deletions include/Maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class DistrToken
std::optional<int> count{};
std::optional<std::string> rhs{};
std::optional<int> rhs_count{};
std::optional<int> chance;
};

class Container
Expand All @@ -49,6 +50,16 @@ class DistrObject
std::optional<int> count{};
std::optional<int> replace_with_count{};
std::optional<Container> container{};
std::optional<int> chance{};
};

class RuntimeDistrMap {
public:
std::vector<DistrObject> to_add;
std::vector<DistrObject> to_remove;
std::vector<DistrObject> to_remove_all;
std::vector<DistrObject> to_replace;
std::vector<DistrObject> to_replace_all;
};

struct FormIDAndPluginName
Expand All @@ -71,6 +82,13 @@ class Maps : public Singleton<Maps>
return ptr ? static_cast<std::size_t>(ptr - s.data()) : ULLONG_MAX;
}

static auto HasPos(const std::string_view s, const char c)
{
const auto ptr{ std::strrchr(s.data(), c) };

return ptr != nullptr;
}

using TDistrTokenVec = std::vector<DistrToken>;
using TConflictTestMap = phmap::parallel_flat_hash_map<std::string, TDistrTokenVec>;

Expand All @@ -82,6 +100,8 @@ class Maps : public Singleton<Maps>

using TDistrVec = std::vector<DistrObject>;
inline static TDistrVec distr_object_vec;

inline static std::unordered_map<RE::FormID, RuntimeDistrMap> runtime_map;
};

// fmt helpers
Expand All @@ -105,20 +125,20 @@ inline auto format_as(const DistrType& type)

inline auto format_as(const DistrToken& token)
{
const auto& [type, filename, to_identifier, identifier, count, rhs, rhs_count]{ token };
return fmt::format("[Type: {} / Filename: {} / To: {} / Identifier: {} / Count: {} / RHS: {} / RHS Count: {}]", type, filename, to_identifier, identifier, count.value_or(-1),
rhs.value_or("null"), rhs_count.value_or(-1));
const auto& [type, filename, to_identifier, identifier, count, rhs, rhs_count, chance]{ token };
return fmt::format("[Type: {} / Filename: {} / To: {} / Identifier: {} / Count: {} / RHS: {} / RHS Count: {} / Chance: {}]", type, filename, to_identifier, identifier, count.value_or(-1),
rhs.value_or("null"), rhs_count.value_or(-1), chance.value_or(100));
}

inline auto format_as(const DistrObject& obj)
{
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container]{ obj };
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container, chance]{ obj };
return fmt::format("[Type: {} / Filename: {} / Bound object: {} (0x{:x}) / Leveled list: {} (0x{:x} / Replace with obj: {} (0x{:x}) / Replace with list: {} (0x{:x}) "
"/ Count: {} / Replace count: {} / Container: {} (0x{:x}) ({})]",
"/ Count: {} / Replace count: {} / Container: {} (0x{:x}) ({})] / Chance: {}",
type, filename, bound_object ? bound_object->GetName() : "null", bound_object ? bound_object->GetFormID() : 0,
leveled_list ? leveled_list->GetFormEditorID() : "null", leveled_list ? leveled_list->GetFormID() : 0,
replace_with_obj ? replace_with_obj->GetName() : "null", replace_with_obj ? replace_with_obj->GetFormID() : 0,
replace_with_list ? replace_with_list->GetName() : "null", replace_with_list ? replace_with_list->GetFormID() : 0, count.value_or(-1),
replace_with_count.value_or(-1), container.has_value() ? container.value().container_name : "null",
container.has_value() ? container.value().container_form_id : 0, container.has_value() ? container->container_type : RE::FormType::Container);
container.has_value() ? container.value().container_form_id : 0, container.has_value() ? container->container_type : RE::FormType::Container, chance.value_or(100));
}
42 changes: 38 additions & 4 deletions include/Utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class Utility : public Singleton<Utility>
{
static auto IsEditorID(const std::string_view identifier) { return std::strchr(identifier.data(), '~') == nullptr; }



static FormIDAndPluginName GetFormIDAndPluginName(const std::string_view identifier)
{
if (const auto tilde{ std::strchr(identifier.data(), '~') }) {
Expand Down Expand Up @@ -81,6 +83,38 @@ class Utility : public Singleton<Utility>
public:
static auto CachePlayerLevel() { player_level = RE::PlayerCharacter::GetSingleton()->GetLevel(); }

static int GetRandomChance() {
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> distr(0, 100);

return distr(rng);
}

static int GetRandomCount(int count, unsigned int chance) {
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> distr(0, 100);

int actual_count = 0;
for (int i = 0; i < count; i++) {
if (distr(rng) < chance) {
actual_count++;
}
}

return actual_count;
}

static int GetChance(const std::string& str) {
const auto quest_pos{ Maps::GetPos(str, '?') };

logger::info("Has Chance: {}", quest_pos != ULLONG_MAX);
const auto chance = quest_pos == ULLONG_MAX ? 100 : Maps::ToInt(str.substr(quest_pos + 1));

return chance;
}

static auto ResolveLeveledList(RE::TESLevItem* list)
{
RE::BSScrapArray<RE::CALCED_OBJECT> calced_objects{};
Expand All @@ -106,19 +140,19 @@ class Utility : public Singleton<Utility>
if (const auto replace_with_obj{ GetBoundObject(distr_token.rhs.value()) })
return { distr_token.type, nullptr, leveled_list, distr_token.filename, replace_with_obj, nullptr, distr_token.count, distr_token.rhs_count, cont };
}
return { distr_token.type, nullptr, leveled_list, distr_token.filename, nullptr, nullptr, distr_token.count, std::nullopt, cont };
return { distr_token.type, nullptr, leveled_list, distr_token.filename, nullptr, nullptr, distr_token.count, std::nullopt, cont, std::nullopt };
}
}
else if (const auto bound_obj{ GetBoundObject(distr_token.identifier) }) {
if (const auto cont{ GetContainer(distr_token.to_identifier) }; cont.container) {
if (distr_token.type <=> DistrType::Replace == 0 || distr_token.type <=> DistrType::ReplaceAll == 0) {
if (const auto replace_with_list{ GetLevItem(distr_token.rhs.value()) })
return { distr_token.type, bound_obj, nullptr, distr_token.filename, nullptr, replace_with_list, distr_token.count, distr_token.rhs_count, cont };
return { distr_token.type, bound_obj, nullptr, distr_token.filename, nullptr, replace_with_list, distr_token.count, distr_token.rhs_count, cont, distr_token.chance };

if (const auto replace_with_obj{ GetBoundObject(distr_token.rhs.value()) })
return { distr_token.type, bound_obj, nullptr, distr_token.filename, replace_with_obj, nullptr, distr_token.count, distr_token.rhs_count, cont };
return { distr_token.type, bound_obj, nullptr, distr_token.filename, replace_with_obj, nullptr, distr_token.count, distr_token.rhs_count, cont, distr_token.chance };
}
return { distr_token.type, bound_obj, nullptr, distr_token.filename, nullptr, nullptr, distr_token.count, std::nullopt, cont };
return { distr_token.type, bound_obj, nullptr, distr_token.filename, nullptr, nullptr, distr_token.count, std::nullopt, cont, distr_token.chance };
}
}
logger::error("ERROR: Failed to build DistrObject for {}", distr_token);
Expand Down
2 changes: 1 addition & 1 deletion src/Conflicts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void Conflicts::PrepareDistributionImpl(const Maps::TConflictTestMap& test_map)
{
for (auto distr_token_vec : test_map | std::views::values) {
for (const auto& distr_token : distr_token_vec) {
const auto& [type, filename, to_identifier, identifier, count, rhs, rhs_count]{ distr_token };
const auto& [type, filename, to_identifier, identifier, count, rhs, rhs_count, chance]{ distr_token };

DistrObject result;

Expand Down
110 changes: 107 additions & 3 deletions src/Distributor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@ void Distributor::AddDistribute(const Maps::TDistrVec& distr_vec) noexcept
const auto start_time{ std::chrono::system_clock::now() };

for (const auto& distr_obj : distr_vec) {
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container]{ distr_obj };
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container, chance]{ distr_obj };
if (type <=> DistrType::Add != 0)
continue;

if (container.has_value()) {
const auto& [cont, cont_form_id, cont_type, cont_name]{ container.value() };

if (chance.value_or(100) < 100) {
Maps::runtime_map[cont_form_id].to_add.push_back(distr_obj);
continue;
}

if (bound_object)
cont->AddObjectToContainer(bound_object, count.value(), nullptr);
else if (leveled_list) {
Expand All @@ -51,20 +57,31 @@ void Distributor::RemoveDistribute(const Maps::TDistrVec& distr_vec) noexcept
const auto start_time{ std::chrono::system_clock::now() };

for (const auto& distr_obj : distr_vec) {
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container]{ distr_obj };
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container, chance]{ distr_obj };
if (type <=> DistrType::Remove != 0 && type <=> DistrType::RemoveAll != 0)
continue;

if (container.has_value()) {
const auto& [cont, cont_form_id, cont_type, cont_name]{ container.value() };

if (count.has_value()) {
if (chance.value_or(100) < 100) {
Maps::runtime_map[cont_form_id].to_remove.push_back(distr_obj);
continue;
}

if (bound_object)
cont->RemoveObjectFromContainer(bound_object, count.value());
else if (leveled_list)
cont->RemoveObjectFromContainer(leveled_list, count.value());
logger::info("\t- {}", distr_obj);
}
else {
if (chance.value_or(100) < 100) {
Maps::runtime_map[cont_form_id].to_remove_all.push_back(distr_obj);
continue;
}

const auto& num_to_remove{ cont->CountObjectsInContainer(bound_object) };
if (bound_object)
cont->RemoveObjectFromContainer(bound_object, num_to_remove);
Expand All @@ -87,13 +104,19 @@ void Distributor::ReplaceDistribute(const Maps::TDistrVec& distr_vec) noexcept
const auto start_time{ std::chrono::system_clock::now() };

for (const auto& distr_obj : distr_vec) {
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container]{ distr_obj };
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container, chance]{ distr_obj };
if (type <=> DistrType::Replace != 0 && type <=> DistrType::ReplaceAll != 0)
continue;

if (container.has_value()) {
const auto& [cont, cont_form_id, cont_type, cont_name]{ container.value() };

if (count.has_value()) {
if (chance.value_or(100) < 100) {
Maps::runtime_map[cont_form_id].to_replace.push_back(distr_obj);
continue;
}

if (bound_object) {
if (replace_with_obj) {
cont->RemoveObjectFromContainer(bound_object, count.value());
Expand All @@ -119,6 +142,11 @@ void Distributor::ReplaceDistribute(const Maps::TDistrVec& distr_vec) noexcept
logger::info("\t^ {}", distr_obj);
}
else {
if (chance.value_or(100) < 100) {
Maps::runtime_map[cont_form_id].to_replace_all.push_back(distr_obj);
continue;
}

const auto& num_to_remove{ cont->CountObjectsInContainer(bound_object) };
if (bound_object) {
if (replace_with_obj) {
Expand Down Expand Up @@ -150,3 +178,79 @@ void Distributor::ReplaceDistribute(const Maps::TDistrVec& distr_vec) noexcept
const auto elapsed{ std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - start_time) };
logger::info("^-------Finished REPLACE distribution in {} us-------^", elapsed.count());
}



void Distributor::RuntimeDistribute(RE::TESObjectREFR* a_ref) {
if (!a_ref)
return;

RuntimeDistrMap* to_modify = nullptr;

if (Maps::runtime_map.count(a_ref->GetFormID())) {
to_modify = &Maps::runtime_map[a_ref->GetFormID()];
}
else if (Maps::runtime_map.count(a_ref->GetBaseObject()->GetFormID())) {
to_modify = &Maps::runtime_map[a_ref->GetBaseObject()->GetFormID()];
}

if (to_modify) {
auto counts = a_ref->GetInventoryCounts();

for (auto& distr_obj : to_modify->to_add) {
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container, chance] { distr_obj };

const auto num_to_add = Utility::GetRandomCount(count.value(), chance.value());

logger::info("Adding {} {} to {}", num_to_add, bound_object->GetFormID(), a_ref->GetFormID());

a_ref->AddObjectToContainer(bound_object, nullptr, num_to_add, nullptr);

}

for (auto& distr_obj : to_modify->to_remove) {
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container, chance] { distr_obj };

const auto num_to_remove = Utility::GetRandomCount(count.value(), chance.value());

logger::info("Removing {} {} from {}", num_to_remove, bound_object->GetFormID(), a_ref->GetFormID());

a_ref->RemoveItem(bound_object, num_to_remove, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr);

}

for (auto& distr_obj : to_modify->to_remove_all) {
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container, chance] { distr_obj };


if (Utility::GetRandomChance() < chance.value()) {
const auto num_to_remove = counts[bound_object];
logger::info("Removing {} {} from {}", num_to_remove, bound_object->GetFormID(), a_ref->GetFormID());
a_ref->RemoveItem(bound_object, num_to_remove, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr);
}
}

for (auto& distr_obj : to_modify->to_replace) {
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container, chance] { distr_obj };

const auto num_to_swap = Utility::GetRandomCount(count.value(), chance.value());

logger::info("Swapping {} {} for {} from {}", num_to_swap, bound_object->GetFormID(), replace_with_obj->GetFormID(), a_ref->GetFormID());

a_ref->RemoveItem(bound_object, num_to_swap, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr);
a_ref->AddObjectToContainer(replace_with_obj, nullptr, replace_with_count.value(), nullptr);
}

for (auto& distr_obj : to_modify->to_replace_all) {
const auto& [type, bound_object, leveled_list, filename, replace_with_obj, replace_with_list, count, replace_with_count, container, chance] { distr_obj };

if (Utility::GetRandomChance() < chance.value()) {
const auto num_to_swap = counts[bound_object];
logger::info("Swapping {} {} for {} from {}", num_to_swap, bound_object->GetFormID(), replace_with_obj->GetFormID(), a_ref->GetFormID());

a_ref->RemoveItem(bound_object, num_to_swap, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr);
a_ref->AddObjectToContainer(replace_with_obj, nullptr, num_to_swap, nullptr);
}
}
}
}
Loading

0 comments on commit 2894f26

Please sign in to comment.