mirror of
https://github.com/EricPlayZ/EGameTools.git
synced 2025-07-18 17:37:53 +08:00
589 lines
21 KiB
C++
589 lines
21 KiB
C++
#include <Windows.h>
|
|
#include <algorithm>
|
|
#include <spdlog\spdlog.h>
|
|
#include <EGSDK\Offsets.h>
|
|
#include <EGSDK\GamePH\PlayerState.h>
|
|
#include <EGSDK\GamePH\PlayerVariables.h>
|
|
#include <EGSDK\ClassHelpers.h>
|
|
|
|
namespace EGSDK::GamePH {
|
|
static constexpr int STRING_SIZE_OFFSET = 3;
|
|
static constexpr int FLOAT_SIZE_OFFSET = 3;
|
|
static constexpr int BOOL_SIZE_OFFSET = 2;
|
|
|
|
std::unordered_map<PlayerVariable*, std::string> PlayerVariable::playerVarNames{};
|
|
std::unordered_map<PlayerVariable*, PlayerVarType> PlayerVariable::playerVarTypes{};
|
|
PlayerVariable::PlayerVariable(const std::string& name) {
|
|
playerVarNames[this] = name;
|
|
playerVarTypes[this] = PlayerVarType::NONE;
|
|
}
|
|
const char* PlayerVariable::GetName() {
|
|
auto it = playerVarNames.find(this);
|
|
if (it != playerVarNames.end()) {
|
|
return it->second.c_str();
|
|
}
|
|
return nullptr;
|
|
}
|
|
void PlayerVariable::SetName(const std::string& newName) {
|
|
playerVarNames[this] = newName;
|
|
}
|
|
PlayerVarType PlayerVariable::GetType() {
|
|
auto it = playerVarTypes.find(this);
|
|
if (it != playerVarTypes.end()) {
|
|
return it->second;
|
|
}
|
|
return PlayerVarType::NONE;
|
|
}
|
|
void PlayerVariable::SetType(PlayerVarType newType) {
|
|
playerVarTypes[this] = newType;
|
|
}
|
|
|
|
StringPlayerVariable::StringPlayerVariable(const std::string& name) : PlayerVariable(name) {
|
|
SetType(PlayerVarType::String);
|
|
}
|
|
FloatPlayerVariable::FloatPlayerVariable(const std::string& name) : PlayerVariable(name) {
|
|
SetType(PlayerVarType::Float);
|
|
}
|
|
void FloatPlayerVariable::SetValues(float value) {
|
|
this->value = value;
|
|
this->defaultValue = value;
|
|
}
|
|
BoolPlayerVariable::BoolPlayerVariable(const std::string& name) : PlayerVariable(name) {
|
|
SetType(PlayerVarType::Bool);
|
|
}
|
|
void BoolPlayerVariable::SetValues(bool value) {
|
|
this->value = value;
|
|
this->defaultValue = value;
|
|
}
|
|
|
|
std::unique_ptr<PlayerVariable>& PlayerVarVector::emplace_back(std::unique_ptr<PlayerVariable> playerVar) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
_playerVars.emplace_back(std::move(playerVar));
|
|
return _playerVars.back();
|
|
}
|
|
auto PlayerVarVector::begin() {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
return _playerVars.begin();
|
|
}
|
|
auto PlayerVarVector::end() {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
return _playerVars.end();
|
|
}
|
|
bool PlayerVarVector::none_of(const std::string& name) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
return std::none_of(_playerVars.begin(), _playerVars.end(), [&name](const auto& playerVar) {
|
|
return playerVar->GetName() == name;
|
|
});
|
|
}
|
|
|
|
auto PlayerVarVector::FindIter(const std::string& name) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
auto playerVarIt = std::find_if(_playerVars.begin(), _playerVars.end(), [&name](const auto& playerVar) {
|
|
return playerVar->GetName() == name;
|
|
});
|
|
return playerVarIt;
|
|
}
|
|
std::unique_ptr<PlayerVariable>* PlayerVarVector::FindPtr(const std::string& name) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
auto playerVarIt = FindIter(name);
|
|
return playerVarIt == _playerVars.end() ? nullptr : &*playerVarIt;
|
|
}
|
|
PlayerVariable* PlayerVarVector::Find(const std::string& name) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
auto playerVarPtr = FindPtr(name);
|
|
return !playerVarPtr ? nullptr : playerVarPtr->get();
|
|
}
|
|
auto PlayerVarVector::Erase(const std::string& name) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
auto playerVarIt = FindIter(name);
|
|
if (playerVarIt != _playerVars.end())
|
|
return _playerVars.erase(playerVarIt);
|
|
return _playerVars.end();
|
|
}
|
|
|
|
PlayerVarVector PlayerVariables::playerVars{};
|
|
PlayerVarVector PlayerVariables::customPlayerVars{};
|
|
PlayerVarVector PlayerVariables::defaultPlayerVars{};
|
|
PlayerVarVector PlayerVariables::customDefaultPlayerVars{};
|
|
bool PlayerVariables::gotPlayerVars = false;
|
|
static bool sortedPlayerVars = false;
|
|
|
|
std::unordered_map<std::string, std::any> PlayerVariables::prevPlayerVarValueMap{};
|
|
std::unordered_map<std::string, bool> PlayerVariables::prevBoolValueMap{};
|
|
|
|
template <typename T>
|
|
static void updateDefaultVar(PlayerVarVector& defaultVars, const std::string& name, T value, T defaultValue) {
|
|
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, float> || std::is_same_v<T, bool>, "Invalid type: value must be string, float or bool");
|
|
|
|
auto playerVar = defaultVars.Find(name);
|
|
if (!playerVar) {
|
|
if constexpr (std::is_same_v<T, std::string>) {
|
|
auto stringPlayerVar = std::make_unique<StringPlayerVariable>(name);
|
|
defaultVars.emplace_back(std::move(stringPlayerVar));
|
|
}
|
|
else if constexpr (std::is_same_v<T, float>) {
|
|
auto floatPlayerVar = std::make_unique<FloatPlayerVariable>(name);
|
|
floatPlayerVar->SetValues(value);
|
|
defaultVars.emplace_back(std::move(floatPlayerVar));
|
|
}
|
|
else if constexpr (std::is_same_v<T, bool>) {
|
|
auto boolPlayerVar = std::make_unique<BoolPlayerVariable>(name);
|
|
boolPlayerVar->SetValues(value);
|
|
defaultVars.emplace_back(std::move(boolPlayerVar));
|
|
}
|
|
} else {
|
|
if constexpr (std::is_same_v<T, std::string>) {
|
|
// TO IMPLEMENT
|
|
return;
|
|
} else if constexpr (std::is_same_v<T, float>) {
|
|
auto floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVar);
|
|
floatPlayerVar->SetValues(value);
|
|
} else if constexpr (std::is_same_v<T, bool>) {
|
|
auto boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVar);
|
|
boolPlayerVar->SetValues(value);
|
|
}
|
|
}
|
|
}
|
|
static void processPlayerVar(DWORD64*(*playerVarsGetter)(), std::unique_ptr<PlayerVariable>& playerVarPtr) {
|
|
static int offset = 0;
|
|
int offsetDif = 0;
|
|
while (true) {
|
|
std::string vTableName = Utils::RTTI::GetVTableName(playerVarsGetter() + offset);
|
|
if (vTableName != "StringPlayerVariable" && vTableName != "FloatPlayerVariable" && vTableName != "BoolPlayerVariable") {
|
|
if (offsetDif > 150)
|
|
return;
|
|
|
|
offset += 1;
|
|
offsetDif += 1;
|
|
continue;
|
|
}
|
|
|
|
std::string varName = playerVarPtr->GetName();
|
|
PlayerVarType varType = playerVarPtr->GetType();
|
|
|
|
switch (playerVarPtr->GetType()) {
|
|
case PlayerVarType::String:
|
|
{
|
|
if (vTableName != "StringPlayerVariable")
|
|
return;
|
|
|
|
StringPlayerVariable* stringPlayerVar = reinterpret_cast<StringPlayerVariable*>(playerVarsGetter() + offset);
|
|
playerVarPtr.reset(stringPlayerVar);
|
|
playerVarPtr->SetName(varName);
|
|
playerVarPtr->SetType(varType);
|
|
// TO IMPLEMENT
|
|
|
|
offset += STRING_SIZE_OFFSET;
|
|
return;
|
|
}
|
|
case PlayerVarType::Float:
|
|
{
|
|
if (vTableName != "FloatPlayerVariable")
|
|
return;
|
|
|
|
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVarsGetter() + offset);
|
|
playerVarPtr.reset(floatPlayerVar);
|
|
playerVarPtr->SetName(varName);
|
|
playerVarPtr->SetType(varType);
|
|
updateDefaultVar(PlayerVariables::customDefaultPlayerVars, varName, floatPlayerVar->value.data, floatPlayerVar->defaultValue.data);
|
|
|
|
offset += FLOAT_SIZE_OFFSET;
|
|
return;
|
|
}
|
|
case PlayerVarType::Bool:
|
|
{
|
|
if (vTableName != "BoolPlayerVariable")
|
|
return;
|
|
|
|
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVarsGetter() + offset);
|
|
playerVarPtr.reset(boolPlayerVar);
|
|
playerVarPtr->SetName(varName);
|
|
playerVarPtr->SetType(varType);
|
|
updateDefaultVar(PlayerVariables::customDefaultPlayerVars, varName, boolPlayerVar->value.data, boolPlayerVar->defaultValue.data);
|
|
|
|
offset += BOOL_SIZE_OFFSET;
|
|
return;
|
|
}
|
|
default:
|
|
offset += 1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
static void processPlayerVarSafe(DWORD64*(*playerVarsGetter)(), std::unique_ptr<PlayerVariable>& playerVarPtr) {
|
|
__try {
|
|
processPlayerVar(playerVarsGetter, playerVarPtr);
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
SPDLOG_ERROR("Failed to process player variable: {}", playerVarPtr->GetName());
|
|
}
|
|
}
|
|
|
|
void PlayerVariables::GetPlayerVars() {
|
|
if (gotPlayerVars)
|
|
return;
|
|
if (!sortedPlayerVars)
|
|
return;
|
|
if (!Get())
|
|
return;
|
|
|
|
for (auto& playerVarPtr : playerVars)
|
|
processPlayerVarSafe(reinterpret_cast<DWORD64*(*)()>(&Get), playerVarPtr);
|
|
|
|
gotPlayerVars = true;
|
|
}
|
|
|
|
#pragma region Player Variables Sorting
|
|
struct VarTypeFieldMeta {
|
|
PlayerVarType type;
|
|
std::string_view className;
|
|
};
|
|
const std::vector<VarTypeFieldMeta> varTypeFields = {
|
|
{ PlayerVarType::String, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<StringPlayerVariable>" },
|
|
{ PlayerVarType::Float, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<FloatPlayerVariable>" },
|
|
{ PlayerVarType::Bool, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<BoolPlayerVariable>" }
|
|
};
|
|
|
|
static bool isRetInstruction(BYTE* address) {
|
|
//return address[0] == 0xC3 && address[1] == 0xCC;
|
|
return address[0] == 0x00 && address[1] == 0x00 && address[2] == 0xC3 && address[3] == 0xCC;
|
|
}
|
|
static bool isLeaInstruction(BYTE* address, BYTE REX, BYTE ModRM) {
|
|
return address[0] == REX && address[1] == 0x8D && address[2] == ModRM;
|
|
}
|
|
static bool isCallInstruction(BYTE* address) {
|
|
return address[0] == 0xE8 && address[4] != 0xE8;
|
|
}
|
|
static bool isBelowFuncSizeLimit(BYTE* address, DWORD64 startOfFunc, size_t sizeLimit) {
|
|
return (reinterpret_cast<DWORD64>(address) - startOfFunc) < sizeLimit;
|
|
}
|
|
|
|
// to prevent infinite loops, assuming function is no longer than 500000 bytes LMAO Techland... why is your function even like 250000 bytes to begin with? bad code...
|
|
static const size_t MAX_FUNC_SIZE = 500000;
|
|
static const size_t MAX_LOAD_VAR_FUNC_SIZE = 2000;
|
|
|
|
static const char* getPlayerVarName(BYTE*& funcAddress, DWORD64 startOfFunc) {
|
|
const char* playerVarName = nullptr;
|
|
while (!playerVarName && !isRetInstruction(funcAddress) && isBelowFuncSizeLimit(funcAddress, startOfFunc, MAX_FUNC_SIZE)) {
|
|
// lea r8, varNameString
|
|
if (!isLeaInstruction(funcAddress, 0x4C, 0x05)) {
|
|
funcAddress++;
|
|
continue;
|
|
}
|
|
|
|
playerVarName = reinterpret_cast<const char*>(Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(funcAddress), 3));
|
|
if (!playerVarName) {
|
|
funcAddress++;
|
|
continue;
|
|
}
|
|
|
|
// add the size of the instruction, so we skip this instruction because this instruction is the name
|
|
funcAddress += 0x7;
|
|
}
|
|
|
|
return playerVarName;
|
|
}
|
|
static PlayerVarType getPlayerVarType(BYTE*& funcAddress, DWORD64 startOfFunc) {
|
|
PlayerVarType playerVarType = PlayerVarType::NONE;
|
|
|
|
while (!playerVarType && !isRetInstruction(funcAddress) && isBelowFuncSizeLimit(funcAddress, startOfFunc, MAX_FUNC_SIZE)) {
|
|
// call LoadPlayerXVariable
|
|
if (!isCallInstruction(funcAddress)) {
|
|
funcAddress++;
|
|
continue;
|
|
}
|
|
|
|
DWORD64 startOfLoadVarFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(funcAddress), 1);
|
|
BYTE* loadVarFuncAddress = reinterpret_cast<BYTE*>(startOfLoadVarFunc);
|
|
DWORD64 metaVTAddrFromFunc = 0;
|
|
|
|
while (!metaVTAddrFromFunc && !isRetInstruction(loadVarFuncAddress) && isBelowFuncSizeLimit(loadVarFuncAddress, startOfLoadVarFunc, MAX_LOAD_VAR_FUNC_SIZE)) {
|
|
// lea rax, typedFieldMetaVT
|
|
if (!isLeaInstruction(loadVarFuncAddress, 0x48, 0x05)) {
|
|
loadVarFuncAddress++;
|
|
continue;
|
|
}
|
|
|
|
metaVTAddrFromFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(loadVarFuncAddress), 3);
|
|
std::string vTableName = Utils::RTTI::GetVTableNameFromVTPtr(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc));
|
|
auto varTypeIt = std::find_if(varTypeFields.begin(), varTypeFields.end(), [&vTableName](const auto& varType) {
|
|
return varType.className == vTableName;
|
|
});
|
|
if (varTypeIt == varTypeFields.end()) {
|
|
metaVTAddrFromFunc = 0;
|
|
loadVarFuncAddress++;
|
|
continue;
|
|
}
|
|
|
|
playerVarType = varTypeIt->type;
|
|
break;
|
|
}
|
|
|
|
// if it's still NONE after seeing the function doesnt reference any of the variables, break so the loop stops
|
|
if (playerVarType == PlayerVarType::NONE)
|
|
break;
|
|
}
|
|
|
|
return playerVarType;
|
|
}
|
|
|
|
bool PlayerVariables::SortPlayerVars() {
|
|
DWORD64 startOfFunc = 0;
|
|
while (!startOfFunc)
|
|
startOfFunc = reinterpret_cast<DWORD64>(Offsets::Get_LoadPlayerVars());
|
|
|
|
BYTE* funcAddress = reinterpret_cast<BYTE*>(startOfFunc);
|
|
while (!isRetInstruction(funcAddress) && (reinterpret_cast<DWORD64>(funcAddress) - startOfFunc) < MAX_FUNC_SIZE) {
|
|
const char* playerVarName = getPlayerVarName(funcAddress, startOfFunc);
|
|
if (!playerVarName)
|
|
continue;
|
|
|
|
PlayerVarType playerVarType = getPlayerVarType(funcAddress, startOfFunc);
|
|
switch (playerVarType) {
|
|
case PlayerVarType::String:
|
|
playerVars.emplace_back(std::make_unique<StringPlayerVariable>(playerVarName));
|
|
break;
|
|
case PlayerVarType::Float:
|
|
playerVars.emplace_back(std::make_unique<FloatPlayerVariable>(playerVarName));
|
|
break;
|
|
case PlayerVarType::Bool:
|
|
playerVars.emplace_back(std::make_unique<BoolPlayerVariable>(playerVarName));
|
|
break;
|
|
default:
|
|
//playerVars.emplace_back(std::make_unique<PlayerVariable>(playerVarName));
|
|
break;
|
|
}
|
|
}
|
|
|
|
sortedPlayerVars = true;
|
|
return true;
|
|
}
|
|
#pragma endregion
|
|
|
|
template <typename T>
|
|
static T getDefaultValue() {
|
|
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, float> || std::is_same_v<T, bool>, "Invalid type: value must be string, float or bool");
|
|
|
|
if constexpr (std::is_same_v<T, std::string>)
|
|
return {};
|
|
else if constexpr (std::is_same_v<T, float>)
|
|
return -404.0f;
|
|
else if constexpr (std::is_same_v<T, bool>)
|
|
return false;
|
|
else
|
|
return T();
|
|
}
|
|
template <typename T>
|
|
static T GetPlayerVarValue(const std::string& name) {
|
|
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, float> || std::is_same_v<T, bool>, "Invalid type: value must be string, float or bool");
|
|
|
|
if (!gotPlayerVars)
|
|
return getDefaultValue<T>();
|
|
|
|
auto playerVar = playerVars.Find(name);
|
|
if (!playerVar)
|
|
return getDefaultValue<T>();
|
|
|
|
if constexpr (std::is_same_v<T, std::string>) {
|
|
StringPlayerVariable* stringPlayerVar = reinterpret_cast<StringPlayerVariable*>(playerVar);
|
|
return stringPlayerVar->value.data;
|
|
} else if constexpr (std::is_same_v<T, float>) {
|
|
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVar);
|
|
return floatPlayerVar->value.data;
|
|
} else if constexpr (std::is_same_v<T, bool>) {
|
|
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVar);
|
|
return boolPlayerVar->value.data;
|
|
}
|
|
}
|
|
template <typename T>
|
|
static void ChangePlayerVar(const std::string& name, const T value) {
|
|
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, float> || std::is_same_v<T, bool>, "Invalid type: value must be string, float or bool");
|
|
|
|
if (!gotPlayerVars)
|
|
return;
|
|
|
|
auto playerVar = playerVars.Find(name);
|
|
if (!playerVar)
|
|
return;
|
|
|
|
if constexpr (std::is_same_v<T, std::string>) {
|
|
switch (playerVar->GetType()) {
|
|
case PlayerVarType::String:
|
|
// TO IMPLEMENT
|
|
break;
|
|
case PlayerVarType::Float:
|
|
{
|
|
std::string valueStr = Utils::Values::to_string(value);
|
|
float actualValue = std::stof(valueStr);
|
|
|
|
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVar);
|
|
floatPlayerVar->SetValues(actualValue);
|
|
break;
|
|
}
|
|
case PlayerVarType::Bool:
|
|
{
|
|
std::string valueStr = Utils::Values::to_string(value);
|
|
bool actualValue = valueStr == "true";
|
|
|
|
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVar);
|
|
boolPlayerVar->SetValues(actualValue);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
} else if constexpr (std::is_same_v<T, float>) {
|
|
if (playerVar->GetType() != PlayerVarType::Float)
|
|
return;
|
|
|
|
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVar);
|
|
floatPlayerVar->SetValues(value);
|
|
} else if constexpr (std::is_same_v<T, bool>) {
|
|
if (playerVar->GetType() != PlayerVarType::Bool)
|
|
return;
|
|
|
|
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVar);
|
|
boolPlayerVar->SetValues(value);
|
|
}
|
|
}
|
|
template <typename T>
|
|
static void ChangePlayerVarFromList(const std::string& name, const T value, PlayerVariable* playerVar = nullptr) {
|
|
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, float> || std::is_same_v<T, bool>, "Invalid type: value must be string, float or bool");
|
|
|
|
if (!gotPlayerVars)
|
|
return;
|
|
|
|
if (!playerVar) {
|
|
playerVar = playerVars.Find(name);
|
|
if (!playerVar)
|
|
return;
|
|
}
|
|
|
|
auto customPlayerVar = customPlayerVars.Find(name);
|
|
auto defPlayerVar = defaultPlayerVars.Find(name);
|
|
|
|
if constexpr (std::is_same_v<T, std::string>) {
|
|
switch (playerVar->GetType()) {
|
|
case PlayerVarType::String:
|
|
// TO IMPLEMENT
|
|
break;
|
|
case PlayerVarType::Float:
|
|
{
|
|
if (!customPlayerVar)
|
|
customPlayerVar = customPlayerVars.emplace_back(std::make_unique<FloatPlayerVariable>(name)).get();
|
|
std::string valueStr = Utils::Values::to_string(value);
|
|
float actualValue = std::stof(valueStr);
|
|
|
|
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVar);
|
|
FloatPlayerVariable* customFloatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(customPlayerVar);
|
|
|
|
if (!defPlayerVar) {
|
|
defPlayerVar = defaultPlayerVars.emplace_back(std::make_unique<FloatPlayerVariable>(name)).get();
|
|
|
|
FloatPlayerVariable* defFloatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(defPlayerVar);
|
|
defFloatPlayerVar->SetValues(floatPlayerVar->value);
|
|
}
|
|
|
|
floatPlayerVar->SetValues(actualValue);
|
|
customFloatPlayerVar->SetValues(actualValue);
|
|
break;
|
|
}
|
|
case PlayerVarType::Bool:
|
|
{
|
|
if (!customPlayerVar)
|
|
customPlayerVar = customPlayerVars.emplace_back(std::make_unique<BoolPlayerVariable>(name)).get();
|
|
std::string valueStr = Utils::Values::to_string(value);
|
|
bool actualValue = valueStr == "true";
|
|
|
|
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVar);
|
|
BoolPlayerVariable* customBoolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(customPlayerVar);
|
|
|
|
if (!defPlayerVar) {
|
|
defPlayerVar = defaultPlayerVars.emplace_back(std::make_unique<BoolPlayerVariable>(name)).get();
|
|
|
|
BoolPlayerVariable* defBoolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(defPlayerVar);
|
|
defBoolPlayerVar->SetValues(boolPlayerVar->value);
|
|
}
|
|
|
|
boolPlayerVar->SetValues(actualValue);
|
|
customBoolPlayerVar->SetValues(actualValue);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
} else if constexpr (std::is_same_v<T, float>) {
|
|
if (playerVar->GetType() != PlayerVarType::Float)
|
|
return;
|
|
if (!customPlayerVar)
|
|
customPlayerVar = customPlayerVars.emplace_back(std::make_unique<FloatPlayerVariable>(name)).get();
|
|
|
|
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVar);
|
|
FloatPlayerVariable* customFloatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(customPlayerVar);
|
|
|
|
if (!defPlayerVar) {
|
|
defPlayerVar = defaultPlayerVars.emplace_back(std::make_unique<FloatPlayerVariable>(name)).get();
|
|
|
|
FloatPlayerVariable* defFloatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(defPlayerVar);
|
|
defFloatPlayerVar->SetValues(floatPlayerVar->value);
|
|
}
|
|
|
|
floatPlayerVar->SetValues(value);
|
|
customFloatPlayerVar->SetValues(value);
|
|
} else if constexpr (std::is_same_v<T, bool>) {
|
|
if (playerVar->GetType() != PlayerVarType::Bool)
|
|
return;
|
|
if (!customPlayerVar)
|
|
customPlayerVar = customPlayerVars.emplace_back(std::make_unique<BoolPlayerVariable>(name)).get();
|
|
|
|
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVar);
|
|
BoolPlayerVariable* customBoolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(customPlayerVar);
|
|
|
|
if (!defPlayerVar) {
|
|
defPlayerVar = defaultPlayerVars.emplace_back(std::make_unique<BoolPlayerVariable>(name)).get();
|
|
|
|
BoolPlayerVariable* defBoolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(defPlayerVar);
|
|
defBoolPlayerVar->SetValues(boolPlayerVar->value);
|
|
}
|
|
|
|
boolPlayerVar->SetValues(value);
|
|
customBoolPlayerVar->SetValues(value);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
static void ManagePlayerVarByBool(const std::string& name, const T valueIfTrue, const T valueIfFalse, bool boolVal, bool usePreviousVal = true) {
|
|
if (!gotPlayerVars)
|
|
return;
|
|
|
|
if (prevPlayerVarValueMap.find(name) == prevPlayerVarValueMap.end())
|
|
prevPlayerVarValueMap[name] = GetPlayerVarValue<T>(name);
|
|
if (prevBoolValueMap.find(name) == prevBoolValueMap.end())
|
|
prevBoolValueMap[name] = false;
|
|
|
|
if (boolVal) {
|
|
if (!prevBoolValueMap[name])
|
|
prevPlayerVarValueMap[name] = GetPlayerVarValue<T>(name);
|
|
|
|
ChangePlayerVar(name, valueIfTrue);
|
|
prevBoolValueMap[name] = true;
|
|
} else if (prevBoolValueMap[name]) {
|
|
prevBoolValueMap[name] = false;
|
|
ChangePlayerVar(name, usePreviousVal ? std::any_cast<T>(prevPlayerVarValueMap[name]) : valueIfFalse);
|
|
prevPlayerVarValueMap.erase(name);
|
|
}
|
|
}
|
|
static bool IsPlayerVarManagedByBool(const std::string& name) {
|
|
if (!gotPlayerVars)
|
|
return false;
|
|
|
|
return prevBoolValueMap.find(name) != prevBoolValueMap.end() && prevBoolValueMap[name];
|
|
}
|
|
|
|
static PlayerVariables* GetOffset_PlayerVariables() {
|
|
PlayerState* playerState = PlayerState::Get();
|
|
return playerState ? playerState->playerVariables : nullptr;
|
|
}
|
|
PlayerVariables* PlayerVariables::Get() {
|
|
return ClassHelpers::SafeGetter<PlayerVariables>(GetOffset_PlayerVariables, false, false);
|
|
}
|
|
} |