From dbd559e1df2f3acebd1136f5e2cd091cde2979c9 Mon Sep 17 00:00:00 2001 From: Nick Stefan Date: Sun, 16 Jun 2024 16:46:28 -0600 Subject: [PATCH] multiple base forms iterator auto FormIDSet pointer? input build multi-swap random select swapFormId fix identifier pointer? hash name build not const no const? FormID handle optional random based on player name seed randomness from playerId singleton for saveload manager add some logs ordered set no reserve no const const no dereference node handle value no dereference more logs logs clean up logs return after error early return if size doesnt match typo pass chance object instead of chance.chanceType swap definition order fix identifier move seed to chance part of ini fix list init initializer type logs regex before split str member fix remove logs build --- src/ObjectProperties.cpp | 10 ++++---- src/ObjectProperties.h | 6 ++--- src/PCH.h | 3 +++ src/RNG.cpp | 19 +++++++++++---- src/RNG.h | 51 ++++++++++++++++++++-------------------- src/SwapData.cpp | 31 ++++++++++++++++++++++-- src/Util.cpp | 17 ++++++++++++++ src/Util.h | 1 + 8 files changed, 99 insertions(+), 39 deletions(-) diff --git a/src/ObjectProperties.cpp b/src/ObjectProperties.cpp index 8ba9744..fd38f81 100644 --- a/src/ObjectProperties.cpp +++ b/src/ObjectProperties.cpp @@ -1,7 +1,7 @@ #include "ObjectProperties.h" -RandValueParams::RandValueParams(CHANCE_TYPE a_type, const RE::TESObjectREFR* a_ref) : - rng(a_type, a_ref) +RandValueParams::RandValueParams(Chance a_chance, const RE::TESObjectREFR* a_ref) : + rng(a_chance, a_ref) {} FloatRange::FloatRange(const std::string& a_str) @@ -139,15 +139,15 @@ bool ObjectProperties::IsValid() const return location || rotation || refScale || recordFlagsSet != 0 || recordFlagsUnset != 0; } -void ObjectProperties::SetChanceType(CHANCE_TYPE a_type) +void ObjectProperties::SetChance(Chance a_chance) { - chanceType = a_type; + chance = a_chance; } void ObjectProperties::SetTransform(RE::TESObjectREFR* a_refr) const { if (location || rotation || refScale) { - RandValueParams params(chanceType, a_refr); + RandValueParams params(chance, a_refr); if (location) { location->SetTransform(a_refr->data.location, params); } diff --git a/src/ObjectProperties.h b/src/ObjectProperties.h index d2ddae5..1bb9370 100644 --- a/src/ObjectProperties.h +++ b/src/ObjectProperties.h @@ -4,7 +4,7 @@ struct RandValueParams { - RandValueParams(CHANCE_TYPE a_type, const RE::TESObjectREFR* a_ref); + RandValueParams(Chance a_chance, const RE::TESObjectREFR* a_ref); BOS_RNG rng{}; bool clamp{ false }; @@ -72,7 +72,7 @@ class ObjectProperties bool IsValid() const; - void SetChanceType(CHANCE_TYPE a_type); + void SetChance(Chance a_chance); void SetTransform(RE::TESObjectREFR* a_refr) const; void SetRecordFlags(RE::TESObjectREFR* a_refr) const; @@ -80,7 +80,7 @@ class ObjectProperties void assign_record_flags(const std::string& a_str, bool a_unsetFlag); // members - CHANCE_TYPE chanceType{ CHANCE_TYPE::kRefHash }; + Chance chance{}; std::optional location{ std::nullopt }; std::optional rotation{ std::nullopt }; diff --git a/src/PCH.h b/src/PCH.h index 01e6a62..f16b1d2 100644 --- a/src/PCH.h +++ b/src/PCH.h @@ -45,9 +45,12 @@ template using Map = ankerl::unordered_dense::map; template using Set = ankerl::unordered_dense::set; +template +using OrderedSet = std::set; using FormIDSet = Set; using FormIDOrSet = std::variant; +using FormIDOrderedSet = OrderedSet; template using FormIDMap = Map; diff --git a/src/RNG.cpp b/src/RNG.cpp index 2c3b332..d3f05bc 100644 --- a/src/RNG.cpp +++ b/src/RNG.cpp @@ -1,7 +1,7 @@ #include "RNG.h" -BOS_RNG::BOS_RNG(CHANCE_TYPE a_type, const RE::TESObjectREFR* a_ref) : - type(a_type) +BOS_RNG::BOS_RNG(Chance a_chance, const RE::TESObjectREFR* a_ref) : + type(a_chance.chanceType) { switch (type) { case CHANCE_TYPE::kRefHash: @@ -27,11 +27,19 @@ BOS_RNG::BOS_RNG(CHANCE_TYPE a_type, const RE::TESObjectREFR* a_ref) : } } break; + case CHANCE_TYPE::kRandom: + seed = a_chance.seed; + break; default: break; } } +BOS_RNG::BOS_RNG(Chance a_chance) : + type(a_chance.chanceType), + seed(a_chance.seed) + {} + Chance::Chance(const std::string& a_str) { if (distribution::is_valid_entry(a_str)) { @@ -43,8 +51,11 @@ Chance::Chance(const std::string& a_str) } else { chanceType = CHANCE_TYPE::kRefHash; } + if (srell::cmatch match; srell::regex_search(a_str.c_str(), match, regex::generic)) { - chanceValue = string::to_num(match[1].str()); + const auto chanceOptions = string::split(match[1].str(), ","); + chanceValue = string::to_num(chanceOptions[0]); + seed = chanceOptions.size() > 1 ? string::to_num(chanceOptions[1]) : 0; } } } @@ -53,7 +64,7 @@ Chance::Chance(const std::string& a_str) bool Chance::PassedChance(const RE::TESObjectREFR* a_ref) const { if (chanceValue < 100.0f) { - BOS_RNG rng(chanceType, a_ref); + BOS_RNG rng(*this, a_ref); if (const auto rngValue = rng.generate(0.0f, 100.0f); rngValue > chanceValue) { return false; } diff --git a/src/RNG.h b/src/RNG.h index f019091..1793f86 100644 --- a/src/RNG.h +++ b/src/RNG.h @@ -1,22 +1,38 @@ #pragma once -struct BOS_RNG +enum class CHANCE_TYPE +{ + kRandom, + kRefHash, + kLocationHash +}; + +struct Chance { public: - enum class CHANCE_TYPE - { - kRandom, - kRefHash, - kLocationHash - }; + Chance() = default; + explicit Chance(const std::string& a_str); + bool PassedChance(const RE::TESObjectREFR* a_ref) const; + + // members + CHANCE_TYPE chanceType{ CHANCE_TYPE::kRefHash }; + float chanceValue{ 100.0f }; + std::uint64_t seed{ 0 }; + +}; + +struct BOS_RNG +{ +public: BOS_RNG() = default; - BOS_RNG(CHANCE_TYPE a_type, const RE::TESObjectREFR* a_ref); + BOS_RNG(Chance a_chance, const RE::TESObjectREFR* a_ref); + BOS_RNG(Chance a_chance); template T generate(T a_min, T a_max) const { - if (type == CHANCE_TYPE::kRandom) { + if (type == CHANCE_TYPE::kRandom && seed == 0) { return SeedRNG().generate(a_min, a_max); } return SeedRNG(seed).generate(a_min, a_max); @@ -24,20 +40,5 @@ struct BOS_RNG // members CHANCE_TYPE type; - std::uint64_t seed; -}; - -using CHANCE_TYPE = BOS_RNG::CHANCE_TYPE; - -struct Chance -{ -public: - Chance() = default; - explicit Chance(const std::string& a_str); - - bool PassedChance(const RE::TESObjectREFR* a_ref) const; - - // members - CHANCE_TYPE chanceType{ CHANCE_TYPE::kRefHash }; - float chanceValue{ 100.0f }; + std::uint64_t seed { 0 }; }; diff --git a/src/SwapData.cpp b/src/SwapData.cpp index d0b99c5..3871562 100644 --- a/src/SwapData.cpp +++ b/src/SwapData.cpp @@ -8,7 +8,7 @@ namespace FormSwap record(a_input.record), path(a_input.path) { - properties.SetChanceType(chance.chanceType); + properties.SetChance(chance); } bool ObjectData::HasValidProperties(const RE::TESObjectREFR* a_ref) const @@ -49,7 +49,7 @@ namespace FormSwap auto& set = std::get(formIDSet); const auto setEnd = std::distance(set.begin(), set.end()) - 1; - const auto randIt = BOS_RNG(chance.chanceType, a_ref).generate(0, setEnd); + const auto randIt = BOS_RNG(chance, a_ref).generate(0, setEnd); return RE::TESForm::LookupByID(*std::next(set.begin(), randIt)); } @@ -92,6 +92,33 @@ namespace FormSwap } else { logger::error("\t\t\t\tfail : [{}] (SWAP formID not found)", a_str); } + } else if (const auto baseFormIDs = util::GetFormIDOrderedSet(formPair[0]); !baseFormIDs.empty()) { + if (auto swapFormIDs = util::GetFormIDOrderedSet(formPair[1]); !swapFormIDs.empty()) { + if (baseFormIDs.size() > swapFormIDs.size()) { + logger::error("\t\t\t\tfail : [{}] (SWAP formID set must be equal or larger than BASE formID set)", a_str); + return; + } + auto properties = formPair.size() > 2 ? formPair[2] : std::string{}; + auto chance = formPair.size() > 3 ? formPair[3] : std::string{}; + + auto a_chance = Chance(chance); + auto a_rng = BOS_RNG(a_chance); + + // randomly assign each baseFormID to a unique swapFormID + for (auto itBaseFormID : baseFormIDs) { + const auto setEnd = std::distance(swapFormIDs.begin(), swapFormIDs.end()) - 1; + const auto randIt = a_rng.generate(0, setEnd); + auto swapFormID = swapFormIDs.extract(*std::next(swapFormIDs.begin(), randIt)); + if (swapFormID) { + const Input input(properties, std::string{}, a_str, a_path); + SwapFormData swapFormData(swapFormID.value(), input); + + a_func(itBaseFormID, swapFormData); + } + } + } else { + logger::error("\t\t\t\tfail : [{}] (SWAP formID set not found)", a_str); + } } else { logger::error("\t\t\t\tfail : [{}] (BASE formID not found)", a_str); } diff --git a/src/Util.cpp b/src/Util.cpp index d3c357f..d54039c 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -55,4 +55,21 @@ namespace util return GetFormID(a_str); } } + + FormIDOrderedSet GetFormIDOrderedSet(const std::string& a_str) + { + FormIDOrderedSet set; + if (a_str.contains(",")) { + const auto IDStrs = string::split(a_str, ","); + for (auto& IDStr : IDStrs) { + if (auto formID = GetFormID(IDStr); formID != 0) { + set.emplace(formID); + } else { + logger::error("\t\t\tfailed to process {} (formID not found)", IDStr); + } + } + return set; + } + return set; + } } diff --git a/src/Util.h b/src/Util.h index 06a8912..dd84f1e 100644 --- a/src/Util.h +++ b/src/Util.h @@ -13,4 +13,5 @@ namespace util RE::FormID GetFormID(const std::string& a_str); FormIDOrSet GetSwapFormID(const std::string& a_str); + FormIDOrderedSet GetFormIDOrderedSet(const std::string& a_str); }