Add PedTemplatesCheat
- Added ModMenuModule::PedTemplatesCheat
This commit is contained in:
parent
31d1f03efa
commit
392c6b38e9
@ -391,6 +391,7 @@
|
||||
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\attack-worker.h" />
|
||||
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\rotate-camera-worker.h" />
|
||||
<ClInclude Include="src\modules\modmenu\cheats\native-cheats-keeper.h" />
|
||||
<ClInclude Include="src\modules\modmenu\cheats\ped-templates.h" />
|
||||
<ClInclude Include="src\modules\modmenu\cheats\player-appearance.h" />
|
||||
<ClInclude Include="src\modules\modmenu\cheats\player-pos.h" />
|
||||
<ClInclude Include="src\modules\modmenu\cheats\position-store.h" />
|
||||
@ -678,6 +679,8 @@
|
||||
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\locked-mouse-worker.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\rotate-camera-worker.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\cheats\native-cheats-keeper.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\cheats\ped-templates-builtin.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\cheats\ped-templates.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\cheats\player-appearance.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\cheats\player-pos.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\cheats\position-store.cpp" />
|
||||
|
||||
@ -192,7 +192,9 @@
|
||||
<ClCompile Include="src\modules\ui\components\icon-sprite.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\quick-actions\toggle-mouse-control.cpp" />
|
||||
<ClCompile Include="src\game\utils\ped-utils.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\cheats\ped-templates.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\utils\spawn-ped-utils.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\cheats\ped-templates-builtin.cpp" />
|
||||
<ClCompile Include="src\modules\modmenu\utils\get-nearest-car.cpp" />
|
||||
<ClCompile Include="src\game\utils\car-utils.cpp" />
|
||||
<ClCompile Include="src\game\utils\object-utils.cpp" />
|
||||
@ -534,6 +536,7 @@
|
||||
<ClInclude Include="src\converters\ped-state.h" />
|
||||
<ClInclude Include="src\converters\ped-state-2.h" />
|
||||
<ClInclude Include="src\game\utils\ped-utils.h" />
|
||||
<ClInclude Include="src\modules\modmenu\cheats\ped-templates.h" />
|
||||
<ClInclude Include="src\modules\modmenu\utils\spawn-ped-utils.h" />
|
||||
<ClInclude Include="src\modules\modmenu\utils\get-nearest-car.h" />
|
||||
<ClInclude Include="src\game\utils\car-utils.h" />
|
||||
|
||||
@ -21,3 +21,4 @@
|
||||
#include "position-store.h"
|
||||
#include "saved-cars.h"
|
||||
#include "unstuck-keys.h"
|
||||
#include "ped-templates.h"
|
||||
|
||||
175
src/modules/modmenu/cheats/ped-templates-builtin.cpp
Normal file
175
src/modules/modmenu/cheats/ped-templates-builtin.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
#include "ped-templates.h"
|
||||
|
||||
namespace ModMenuModule {
|
||||
const std::vector<PedTemplate> builtInPedTemplatesArr = {
|
||||
{ L"Dummy", {
|
||||
Game::PED_GRAPHIC_TYPE_DUMMY,
|
||||
Game::PED_REMAP_DUMMY_27,
|
||||
static_cast<Game::WEAPON_INDEX>(-1),
|
||||
50,
|
||||
Game::PED_OCCUPATION_DUMMY,
|
||||
Game::PED_THREAT_SEARCH_AREA,
|
||||
Game::PED_THREAT_REACTION_RUN_AWAY,
|
||||
Game::PED_OBJECTIVE_NO_OBJ,
|
||||
9999,
|
||||
{},
|
||||
Game::PED_STATE_WALKING,
|
||||
Game::PED_STATE2_WALKING,
|
||||
static_cast<Game::PED_BIT_STATE>(1),
|
||||
static_cast<Game::PED_BIT_STATE2>(3),
|
||||
-1
|
||||
}, true, true},
|
||||
{ L"Following car in car", {
|
||||
Game::PED_GRAPHIC_TYPE_DUMMY,
|
||||
Game::PED_REMAP_DUMMY_37,
|
||||
static_cast<Game::WEAPON_INDEX>(-1),
|
||||
50,
|
||||
Game::PED_OCCUPATION_CRIMINAL,
|
||||
Game::PED_THREAT_SEARCH_NO_THREATS,
|
||||
Game::PED_THREAT_REACTION_NO_REACTION,
|
||||
Game::PED_OBJECTIVE_FOLLOW_CAR_IN_CAR,
|
||||
9999,
|
||||
{ -2 },
|
||||
Game::PED_STATE_WALKING,
|
||||
Game::PED_STATE2_WALKING,
|
||||
static_cast<Game::PED_BIT_STATE>(1),
|
||||
static_cast<Game::PED_BIT_STATE2>(5),
|
||||
-1
|
||||
}, true, true},
|
||||
{ L"Cop", {
|
||||
Game::PED_GRAPHIC_TYPE_EMERG,
|
||||
Game::PED_REMAP_BLUE_POLICE,
|
||||
static_cast<Game::WEAPON_INDEX>(-1),
|
||||
50,
|
||||
Game::PED_OCCUPATION_POLICE_ON_FOOT,
|
||||
Game::PED_THREAT_SEARCH_LINE_OF_SIGHT,
|
||||
Game::PED_THREAT_REACTION_REACT_AS_EMERGENCY,
|
||||
Game::PED_OBJECTIVE_NO_OBJ,
|
||||
9999,
|
||||
{},
|
||||
Game::PED_STATE_WALKING,
|
||||
Game::PED_STATE2_WALKING,
|
||||
static_cast<Game::PED_BIT_STATE>(1),
|
||||
static_cast<Game::PED_BIT_STATE2>(3),
|
||||
-1
|
||||
}, true, true},
|
||||
{ L"Mugger", {
|
||||
Game::PED_GRAPHIC_TYPE_DUMMY,
|
||||
Game::PED_REMAP_MUGGER,
|
||||
static_cast<Game::WEAPON_INDEX>(-1),
|
||||
50,
|
||||
Game::PED_OCCUPATION_MUGGER,
|
||||
Game::PED_THREAT_SEARCH_AREA,
|
||||
Game::PED_THREAT_REACTION_RUN_AWAY,
|
||||
Game::PED_OBJECTIVE_NO_OBJ,
|
||||
0,
|
||||
{},
|
||||
Game::PED_STATE_WALKING,
|
||||
Game::PED_STATE2_WALKING,
|
||||
static_cast<Game::PED_BIT_STATE>(1),
|
||||
static_cast<Game::PED_BIT_STATE2>(3),
|
||||
-1
|
||||
}, true, true},
|
||||
{ L"Car thief", {
|
||||
Game::PED_GRAPHIC_TYPE_DUMMY,
|
||||
Game::PED_REMAP_GREEN_SHORTS_PED,
|
||||
static_cast<Game::WEAPON_INDEX>(-1),
|
||||
50,
|
||||
Game::PED_OCCUPATION_CARTHIEF,
|
||||
Game::PED_THREAT_SEARCH_AREA,
|
||||
Game::PED_THREAT_REACTION_RUN_AWAY,
|
||||
Game::PED_OBJECTIVE_NO_OBJ,
|
||||
0,
|
||||
{},
|
||||
Game::PED_STATE_WALKING,
|
||||
Game::PED_STATE2_WALKING,
|
||||
static_cast<Game::PED_BIT_STATE>(1),
|
||||
static_cast<Game::PED_BIT_STATE2>(3),
|
||||
-1
|
||||
}, true, true},
|
||||
{ L"SWAT", {
|
||||
Game::PED_GRAPHIC_TYPE_EMERG,
|
||||
Game::PED_REMAP_DEFAULT,
|
||||
Game::WEAPON_PISTOL,
|
||||
400,
|
||||
Game::PED_OCCUPATION_SWAT,
|
||||
Game::PED_THREAT_SEARCH_LINE_OF_SIGHT,
|
||||
Game::PED_THREAT_REACTION_REACT_AS_EMERGENCY,
|
||||
Game::PED_OBJECTIVE_NO_OBJ,
|
||||
9999,
|
||||
{},
|
||||
Game::PED_STATE_WALKING,
|
||||
Game::PED_STATE2_WALKING,
|
||||
static_cast<Game::PED_BIT_STATE>(1),
|
||||
static_cast<Game::PED_BIT_STATE2>(4),
|
||||
-1
|
||||
}, true, true},
|
||||
{ L"FBI", {
|
||||
Game::PED_GRAPHIC_TYPE_GANG,
|
||||
Game::PED_REMAP_ZAIBATSU,
|
||||
Game::WEAPON_SILENCED_S_UZI,
|
||||
250,
|
||||
Game::PED_OCCUPATION_FBI,
|
||||
Game::PED_THREAT_SEARCH_LINE_OF_SIGHT,
|
||||
Game::PED_THREAT_REACTION_REACT_AS_EMERGENCY,
|
||||
Game::PED_OBJECTIVE_NO_OBJ,
|
||||
9999,
|
||||
{},
|
||||
Game::PED_STATE_WALKING,
|
||||
Game::PED_STATE2_WALKING,
|
||||
static_cast<Game::PED_BIT_STATE>(1),
|
||||
static_cast<Game::PED_BIT_STATE2>(4),
|
||||
-1
|
||||
}, true, true},
|
||||
{ L"Bodyguard", {
|
||||
Game::PED_GRAPHIC_TYPE_GANG,
|
||||
Game::PED_REMAP_PLAYER,
|
||||
Game::WEAPON_SILENCED_S_UZI,
|
||||
100,
|
||||
Game::PED_OCCUPATION_SPECIAL_GROUP_MEMBER,
|
||||
Game::PED_THREAT_SEARCH_LINE_OF_SIGHT,
|
||||
Game::PED_THREAT_REACTION_REACT_AS_NORMAL,
|
||||
Game::PED_OBJECTIVE_NO_OBJ,
|
||||
9999,
|
||||
{},
|
||||
Game::PED_STATE_WALKING,
|
||||
Game::PED_STATE2_WALKING,
|
||||
static_cast<Game::PED_BIT_STATE>(1),
|
||||
static_cast<Game::PED_BIT_STATE2>(4),
|
||||
-3
|
||||
}, true, true },
|
||||
{ L"Anti-player guard", {
|
||||
Game::PED_GRAPHIC_TYPE_GANG,
|
||||
Game::PED_REMAP_YAKUZA,
|
||||
Game::WEAPON_S_UZI,
|
||||
100,
|
||||
Game::PED_OCCUPATION_GUARD,
|
||||
Game::PED_THREAT_SEARCH_AREA_PLAYER_ONLY,
|
||||
Game::PED_THREAT_REACTION_REACT_AS_NORMAL,
|
||||
Game::PED_OBJECTIVE_GUARD_SPOT,
|
||||
9999,
|
||||
{},
|
||||
Game::PED_STATE_WALKING,
|
||||
Game::PED_STATE2_WALKING,
|
||||
static_cast<Game::PED_BIT_STATE>(1),
|
||||
static_cast<Game::PED_BIT_STATE2>(3),
|
||||
-1
|
||||
}, true, true },
|
||||
};
|
||||
|
||||
const std::unordered_map<std::wstring, const PedTemplate&> builtInPedTemplates = []() {
|
||||
std::unordered_map<std::wstring, const PedTemplate&> map;
|
||||
for (const auto& pedTemplate : builtInPedTemplatesArr) {
|
||||
map.emplace(pedTemplate.name, pedTemplate);
|
||||
}
|
||||
return map;
|
||||
}();
|
||||
|
||||
const std::vector<std::wstring> builtInPedTemplateNames = []() {
|
||||
std::vector<std::wstring> names;
|
||||
for (const auto& pedTemplate : builtInPedTemplatesArr) {
|
||||
names.push_back(pedTemplate.name);
|
||||
}
|
||||
return names;
|
||||
}();
|
||||
}
|
||||
440
src/modules/modmenu/cheats/ped-templates.cpp
Normal file
440
src/modules/modmenu/cheats/ped-templates.cpp
Normal file
@ -0,0 +1,440 @@
|
||||
#include "ped-templates.h"
|
||||
#include "../../../game/game.h"
|
||||
#include "ped-templates-builtin.cpp"
|
||||
#include "../cheat-registry.h"
|
||||
|
||||
static constexpr uint8_t PERSISTENCE_VERSION = 1;
|
||||
|
||||
ModMenuModule::PedTemplatesCheat* ModMenuModule::PedTemplatesCheat::m_instance = nullptr;
|
||||
|
||||
ModMenuModule::PedTemplatesCheat::PedTemplatesCheat() : ModMenuModule::CheatBase("")
|
||||
{
|
||||
assert(m_instance == nullptr && "PedTemplatesCheat instance already exists");
|
||||
m_instance = this;
|
||||
}
|
||||
|
||||
ModMenuModule::PedTemplatesCheat::~PedTemplatesCheat()
|
||||
{
|
||||
m_instance = nullptr;
|
||||
}
|
||||
|
||||
ModMenuModule::PedTemplatesCheat* ModMenuModule::PedTemplatesCheat::GetInstance()
|
||||
{
|
||||
assert(m_instance && "PedTemplatesCheat not initialized!");
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
bool ModMenuModule::PedTemplatesCheat::SaveTemplate(const std::wstring& name, const Utils::SpawnPed::SpawnPedData& data)
|
||||
{
|
||||
if (name.empty()) {
|
||||
spdlog::warn("PedTemplatesCheat::SaveTemplate: Name cannot be empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (builtInPedTemplates.find(name) != builtInPedTemplates.end()) {
|
||||
spdlog::warn("PedTemplatesCheat::SaveTemplate: Cannot overwrite built-in template");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_templates[name] = PedTemplate{
|
||||
name,
|
||||
data,
|
||||
false,
|
||||
true
|
||||
};
|
||||
m_cacheDirty = true;
|
||||
|
||||
spdlog::info("PedTemplatesCheat::SaveTemplate: Ped template saved");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModMenuModule::PedTemplatesCheat::SaveTemplate(TempPedTemplateId tempTemplateId)
|
||||
{
|
||||
auto it = m_tempTemplates.find(tempTemplateId);
|
||||
if (it == m_tempTemplates.end()) {
|
||||
spdlog::warn("PedTemplatesCheat::GetTemplate: Temporary template {} not found", tempTemplateId);
|
||||
return false;
|
||||
}
|
||||
|
||||
PedTemplate* tempTemplate = it->second.pedTemplatePtr.get();
|
||||
if (!SaveTemplate(tempTemplate->name, tempTemplate->data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tempTemplate->isBuiltin = false;
|
||||
tempTemplate->isSaved = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModMenuModule::PedTemplatesCheat::SaveTemplate(TempPedTemplateId tempTemplateId, const std::wstring& customName)
|
||||
{
|
||||
auto it = m_tempTemplates.find(tempTemplateId);
|
||||
if (it == m_tempTemplates.end()) {
|
||||
spdlog::warn("PedTemplatesCheat::GetTemplate: Temporary template {} not found", tempTemplateId);
|
||||
return false;
|
||||
}
|
||||
|
||||
PedTemplate* tempTemplate = it->second.pedTemplatePtr.get();
|
||||
if (!SaveTemplate(customName, tempTemplate->data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tempTemplate->name = customName;
|
||||
tempTemplate->isBuiltin = false;
|
||||
tempTemplate->isSaved = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
const ModMenuModule::PedTemplate* ModMenuModule::PedTemplatesCheat::GetTemplate(const std::wstring& name) const
|
||||
{
|
||||
auto builtinIt = builtInPedTemplates.find(name);
|
||||
if (builtinIt != builtInPedTemplates.end()) {
|
||||
return &builtinIt->second;
|
||||
}
|
||||
|
||||
auto it = m_templates.find(name);
|
||||
if (it == m_templates.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
bool ModMenuModule::PedTemplatesCheat::DeleteTemplate(const std::wstring& name)
|
||||
{
|
||||
auto it = m_templates.find(name);
|
||||
if (it == m_templates.end()) {
|
||||
spdlog::warn("PedTemplatesCheat::DeleteTemplate: Template not found or built-in");
|
||||
return false;
|
||||
}
|
||||
m_templates.erase(it);
|
||||
m_cacheDirty = true;
|
||||
|
||||
spdlog::info("PedTemplatesCheat::DeleteTemplate: Template deleted");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModMenuModule::PedTemplatesCheat::RenameTemplate(const std::wstring& oldName, const std::wstring& newName)
|
||||
{
|
||||
auto it = m_templates.find(oldName);
|
||||
if (it == m_templates.end()) {
|
||||
spdlog::warn("PedTemplatesCheat::UpdateTemplate: Template not found or built-in");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_templates.find(newName) != m_templates.end() || builtInPedTemplates.find(newName) != builtInPedTemplates.end()) {
|
||||
spdlog::warn("PedTemplatesCheat::RenameTemplate: Ped template with that name already exists");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_templates.emplace(newName, PedTemplate{
|
||||
newName,
|
||||
it->second.data,
|
||||
false,
|
||||
true
|
||||
});
|
||||
m_templates.erase(it);
|
||||
m_cacheDirty = true;
|
||||
|
||||
spdlog::info("PedTemplatesCheat::RenameTemplate: Template renamed");
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<std::wstring>& ModMenuModule::PedTemplatesCheat::GetTemplatesList()
|
||||
{
|
||||
RefreshSortedCache();
|
||||
return m_sortedCache;
|
||||
}
|
||||
|
||||
ModMenuModule::TempPedTemplateId ModMenuModule::PedTemplatesCheat::FindOrCreateTempTemplate(const std::string& persistenceKey, const std::wstring& baseTemplateName)
|
||||
{
|
||||
auto it = FindTempTemplateByPersistenceKey(persistenceKey);
|
||||
|
||||
if (it != m_tempTemplates.end()) {
|
||||
spdlog::debug("PedTemplatesCheat::FindOrCreateTempTemplate: Using existing temporary template with persistence key {}", persistenceKey);
|
||||
return it->second.id;
|
||||
}
|
||||
|
||||
spdlog::debug("PedTemplatesCheat::FindOrCreateTempTemplate: Creating temporary template with persistence key {}", persistenceKey);
|
||||
|
||||
const PedTemplate* baseTemplate = GetTemplate(baseTemplateName);
|
||||
if (!baseTemplate) {
|
||||
spdlog::warn(L"PedTemplatesCheat::FindOrCreateTempTemplate: Base template \"{}\" not found", baseTemplateName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
TempPedTemplateId newId = m_nextTempTemplateId++;
|
||||
m_tempTemplates.emplace(newId, TempPedTemplate{
|
||||
persistenceKey,
|
||||
newId,
|
||||
std::make_shared<PedTemplate>(*baseTemplate)
|
||||
});
|
||||
|
||||
return newId;
|
||||
}
|
||||
|
||||
ModMenuModule::TempPedTemplateId ModMenuModule::PedTemplatesCheat::FindTempTemplate(const std::string& persistenceKey)
|
||||
{
|
||||
auto it = FindTempTemplateByPersistenceKey(persistenceKey);
|
||||
|
||||
if (it == m_tempTemplates.end()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return it->second.id;
|
||||
}
|
||||
|
||||
std::shared_ptr<ModMenuModule::PedTemplate> ModMenuModule::PedTemplatesCheat::GetTempTemplate(TempPedTemplateId id) const
|
||||
{
|
||||
auto it = m_tempTemplates.find(id);
|
||||
if (it == m_tempTemplates.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second.pedTemplatePtr;
|
||||
}
|
||||
|
||||
void ModMenuModule::PedTemplatesCheat::DeleteTempTemplate(TempPedTemplateId id)
|
||||
{
|
||||
auto it = m_tempTemplates.find(id);
|
||||
if (it == m_tempTemplates.end()) {
|
||||
spdlog::warn("PedTemplatesCheat::DeleteTempTemplate: Temporary template {} not found", id);
|
||||
return;
|
||||
}
|
||||
|
||||
m_tempTemplates.erase(it);
|
||||
}
|
||||
|
||||
void ModMenuModule::PedTemplatesCheat::OnFirstEnable()
|
||||
{
|
||||
LoadFromPersistence();
|
||||
}
|
||||
|
||||
void ModMenuModule::PedTemplatesCheat::OnEnable()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ModMenuModule::PedTemplatesCheat::OnDisable()
|
||||
{
|
||||
SaveToPersistence();
|
||||
}
|
||||
|
||||
std::unordered_map<ModMenuModule::TempPedTemplateId, ModMenuModule::PedTemplatesCheat::TempPedTemplate>::iterator
|
||||
ModMenuModule::PedTemplatesCheat::FindTempTemplateByPersistenceKey(const std::string& persistenceKey)
|
||||
{
|
||||
if (persistenceKey.empty()) {
|
||||
return m_tempTemplates.end();
|
||||
}
|
||||
|
||||
return std::find_if(m_tempTemplates.begin(), m_tempTemplates.end(), [&](const auto& pair) {
|
||||
return pair.second.persistenceKey == persistenceKey;
|
||||
});
|
||||
}
|
||||
|
||||
void ModMenuModule::PedTemplatesCheat::SaveToPersistence() const
|
||||
{
|
||||
PersistenceModule::PersistenceManager* persistence = PersistenceModule::PersistenceManager::GetInstance();
|
||||
|
||||
size_t dataSize = 1 + sizeof(size_t) * 2;
|
||||
|
||||
for (const auto& pair : m_templates) {
|
||||
const PedTemplate& templateData = pair.second;
|
||||
dataSize += sizeof(size_t) + templateData.name.size() * sizeof(wchar_t);
|
||||
dataSize += sizeof(Utils::SpawnPed::SpawnPedData);
|
||||
}
|
||||
|
||||
size_t saveableTempTemplateCount = 0;
|
||||
for (const auto& pair : m_tempTemplates) {
|
||||
if (pair.second.persistenceKey.empty()) continue;
|
||||
|
||||
const TempPedTemplate& tempTemplate = pair.second;
|
||||
dataSize += sizeof(size_t) + tempTemplate.persistenceKey.size() * sizeof(char);
|
||||
dataSize += sizeof(size_t) + tempTemplate.pedTemplatePtr->name.size() * sizeof(wchar_t);
|
||||
dataSize += sizeof(Utils::SpawnPed::SpawnPedData);
|
||||
dataSize += sizeof(bool); // isBuiltin
|
||||
dataSize += sizeof(bool); // isSaved
|
||||
|
||||
saveableTempTemplateCount++;
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> dataPtr = std::make_unique<uint8_t[]>(dataSize);
|
||||
dataPtr[0] = PERSISTENCE_VERSION;
|
||||
size_t offset = 1;
|
||||
|
||||
auto writeSizeT = [&dataPtr, &offset](size_t value) {
|
||||
memcpy(dataPtr.get() + offset, &value, sizeof(size_t));
|
||||
offset += sizeof(size_t);
|
||||
};
|
||||
|
||||
auto writeWideString = [&dataPtr, &offset, writeSizeT](const std::wstring& str) {
|
||||
writeSizeT(str.size());
|
||||
memcpy(dataPtr.get() + offset, str.data(), str.size() * sizeof(wchar_t));
|
||||
offset += str.size() * sizeof(wchar_t);
|
||||
};
|
||||
|
||||
auto writeString = [&dataPtr, &offset, writeSizeT](const std::string& str) {
|
||||
writeSizeT(str.size());
|
||||
memcpy(dataPtr.get() + offset, str.data(), str.size() * sizeof(char));
|
||||
offset += str.size() * sizeof(char);
|
||||
};
|
||||
|
||||
auto writeSpawnPedData = [&dataPtr, &offset](const Utils::SpawnPed::SpawnPedData& data) {
|
||||
memcpy(dataPtr.get() + offset, &data, sizeof(Utils::SpawnPed::SpawnPedData));
|
||||
offset += sizeof(Utils::SpawnPed::SpawnPedData);
|
||||
};
|
||||
|
||||
auto writeBool = [&dataPtr, &offset](bool value) {
|
||||
memcpy(dataPtr.get() + offset, &value, sizeof(bool));
|
||||
offset += sizeof(bool);
|
||||
};
|
||||
|
||||
// templates
|
||||
writeSizeT(m_templates.size());
|
||||
for (const auto& pair : m_templates) {
|
||||
const PedTemplate& templateData = pair.second;
|
||||
writeWideString(templateData.name);
|
||||
writeSpawnPedData(templateData.data);
|
||||
}
|
||||
|
||||
// temp templates
|
||||
writeSizeT(saveableTempTemplateCount);
|
||||
for (const auto& pair : m_tempTemplates) {
|
||||
const TempPedTemplate& tempTemplate = pair.second;
|
||||
if (tempTemplate.persistenceKey.empty()) continue;
|
||||
writeString(tempTemplate.persistenceKey);
|
||||
writeWideString(tempTemplate.pedTemplatePtr->name);
|
||||
writeSpawnPedData(tempTemplate.pedTemplatePtr->data);
|
||||
writeBool(tempTemplate.pedTemplatePtr->isBuiltin);
|
||||
writeBool(tempTemplate.pedTemplatePtr->isSaved);
|
||||
}
|
||||
|
||||
persistence->SaveRaw("Cheat_PedTemplates_State", dataPtr.get(), dataSize);
|
||||
}
|
||||
|
||||
void ModMenuModule::PedTemplatesCheat::LoadFromPersistence()
|
||||
{
|
||||
m_templates.clear();
|
||||
m_tempTemplates.clear();
|
||||
m_nextTempTemplateId = 1;
|
||||
m_cacheDirty = true;
|
||||
|
||||
PersistenceModule::PersistenceManager* persistence = PersistenceModule::PersistenceManager::GetInstance();
|
||||
std::unique_ptr<uint8_t[]> dataPtr = nullptr;
|
||||
size_t dataSize = 0;
|
||||
if (!persistence->LoadRaw("Cheat_PedTemplates_State", dataPtr, dataSize)) return;
|
||||
if (dataSize < 1) {
|
||||
spdlog::error("PedTemplatesCheat::LoadFromPersistence: invalid data size");
|
||||
return;
|
||||
}
|
||||
uint8_t version = dataPtr[0];
|
||||
if (version != PERSISTENCE_VERSION && !ConvertPersistence(dataPtr, dataSize, version)) {
|
||||
return;
|
||||
}
|
||||
size_t offset = 1;
|
||||
|
||||
auto showDataSizeError = [dataSize, version]() {
|
||||
spdlog::error("PedTemplatesCheat::LoadFromPersistence: invalid data size {} for version {}", dataSize, version);
|
||||
};
|
||||
|
||||
auto readSizeT = [&dataPtr, &offset, dataSize, showDataSizeError](size_t& out) {
|
||||
if (offset + sizeof(size_t) > dataSize) {
|
||||
showDataSizeError();
|
||||
return false;
|
||||
}
|
||||
memcpy(&out, dataPtr.get() + offset, sizeof(size_t));
|
||||
offset += sizeof(size_t);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto readWideString = [&dataPtr, &offset, dataSize, readSizeT, showDataSizeError](std::wstring& out) {
|
||||
size_t strSize = 0;
|
||||
if (!readSizeT(strSize)) return false;
|
||||
if (offset + strSize * sizeof(wchar_t) > dataSize) {
|
||||
showDataSizeError();
|
||||
return false;
|
||||
}
|
||||
out.assign(reinterpret_cast<wchar_t*>(dataPtr.get() + offset), strSize);
|
||||
offset += strSize * sizeof(wchar_t);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto readString = [&dataPtr, &offset, dataSize, readSizeT, showDataSizeError](std::string& out) {
|
||||
size_t strSize = 0;
|
||||
if (!readSizeT(strSize)) return false;
|
||||
if (offset + strSize * sizeof(char) > dataSize) {
|
||||
showDataSizeError();
|
||||
return false;
|
||||
}
|
||||
out.assign(reinterpret_cast<char*>(dataPtr.get() + offset), strSize);
|
||||
offset += strSize * sizeof(char);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto readSpawnPedData = [&dataPtr, &offset, dataSize, showDataSizeError](Utils::SpawnPed::SpawnPedData& out) {
|
||||
if (offset + sizeof(Utils::SpawnPed::SpawnPedData) > dataSize) {
|
||||
showDataSizeError();
|
||||
return false;
|
||||
}
|
||||
memcpy(&out, dataPtr.get() + offset, sizeof(Utils::SpawnPed::SpawnPedData));
|
||||
offset += sizeof(Utils::SpawnPed::SpawnPedData);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto readBool = [&dataPtr, &offset, dataSize, showDataSizeError](bool& out) {
|
||||
if (offset + sizeof(bool) > dataSize) {
|
||||
showDataSizeError();
|
||||
return false;
|
||||
}
|
||||
memcpy(&out, dataPtr.get() + offset, sizeof(bool));
|
||||
offset += sizeof(bool);
|
||||
return true;
|
||||
};
|
||||
|
||||
size_t templateCount = 0;
|
||||
if (!readSizeT(templateCount)) return;
|
||||
|
||||
for (size_t i = 0; i < templateCount; ++i) {
|
||||
PedTemplate templateData;
|
||||
if (!readWideString(templateData.name)) return;
|
||||
if (!readSpawnPedData(templateData.data)) return;
|
||||
templateData.isBuiltin = false;
|
||||
templateData.isSaved = true;
|
||||
m_templates[templateData.name] = templateData;
|
||||
}
|
||||
|
||||
size_t tempTemplateCount = 0;
|
||||
if (!readSizeT(tempTemplateCount)) return;
|
||||
|
||||
for (size_t i = 0; i < tempTemplateCount; ++i) {
|
||||
TempPedTemplate tempTemplate;
|
||||
if (!readString(tempTemplate.persistenceKey)) return;
|
||||
tempTemplate.id = m_nextTempTemplateId++;
|
||||
tempTemplate.pedTemplatePtr = std::make_shared<PedTemplate>();
|
||||
if (!readWideString(tempTemplate.pedTemplatePtr->name)) return;
|
||||
if (!readSpawnPedData(tempTemplate.pedTemplatePtr->data)) return;
|
||||
if (!readBool(tempTemplate.pedTemplatePtr->isBuiltin)) return;
|
||||
if (!readBool(tempTemplate.pedTemplatePtr->isSaved)) return;
|
||||
m_tempTemplates[tempTemplate.id] = tempTemplate;
|
||||
}
|
||||
|
||||
spdlog::info("PedTemplatesCheat::LoadFromPersistence: loaded {} template(s) and {} temporary template(s)", templateCount, tempTemplateCount);
|
||||
}
|
||||
|
||||
bool ModMenuModule::PedTemplatesCheat::ConvertPersistence(std::unique_ptr<uint8_t[]>& dataPtr, size_t& dataSize, uint8_t version)
|
||||
{
|
||||
spdlog::error("PedTemplatesCheat::ConvertPersistence: unsupported version {}", version);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ModMenuModule::PedTemplatesCheat::RefreshSortedCache()
|
||||
{
|
||||
if (!m_cacheDirty) return;
|
||||
m_sortedCache = builtInPedTemplateNames;
|
||||
m_sortedCache.reserve(m_sortedCache.size() + m_templates.size());
|
||||
for (const auto& pair : m_templates) {
|
||||
m_sortedCache.push_back(pair.first);
|
||||
}
|
||||
std::sort(m_sortedCache.begin(), m_sortedCache.end());
|
||||
m_cacheDirty = false;
|
||||
}
|
||||
|
||||
REGISTER_CHEAT(PedTemplatesCheat)
|
||||
62
src/modules/modmenu/cheats/ped-templates.h
Normal file
62
src/modules/modmenu/cheats/ped-templates.h
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include "../common.h"
|
||||
#include "../cheat-base.h"
|
||||
#include "../utils/spawn-ped-utils.h"
|
||||
|
||||
namespace ModMenuModule {
|
||||
using TempPedTemplateId = size_t;
|
||||
|
||||
struct PedTemplate {
|
||||
std::wstring name = L"";
|
||||
Utils::SpawnPed::SpawnPedData data = {};
|
||||
bool isBuiltin = false;
|
||||
bool isSaved = false;
|
||||
};
|
||||
|
||||
class PedTemplatesCheat : public CheatBase {
|
||||
public:
|
||||
PedTemplatesCheat();
|
||||
virtual ~PedTemplatesCheat() override;
|
||||
static PedTemplatesCheat* GetInstance();
|
||||
|
||||
bool SaveTemplate(const std::wstring& name, const Utils::SpawnPed::SpawnPedData& data);
|
||||
bool SaveTemplate(TempPedTemplateId tempTemplateId);
|
||||
bool SaveTemplate(TempPedTemplateId tempTemplateId, const std::wstring& customName);
|
||||
// pointers may be invalidated on state change
|
||||
const PedTemplate* GetTemplate(const std::wstring& name) const;
|
||||
bool DeleteTemplate(const std::wstring& name);
|
||||
bool RenameTemplate(const std::wstring& oldName, const std::wstring& newName);
|
||||
const std::vector<std::wstring>& GetTemplatesList();
|
||||
|
||||
TempPedTemplateId FindOrCreateTempTemplate(const std::string& persistenceKey, const std::wstring& baseTemplateName);
|
||||
TempPedTemplateId FindTempTemplate(const std::string& persistenceKey);
|
||||
std::shared_ptr<PedTemplate> GetTempTemplate(TempPedTemplateId id) const;
|
||||
void DeleteTempTemplate(TempPedTemplateId id);
|
||||
|
||||
private:
|
||||
struct TempPedTemplate {
|
||||
std::string persistenceKey = "";
|
||||
TempPedTemplateId id = 0;
|
||||
std::shared_ptr<PedTemplate> pedTemplatePtr = nullptr;
|
||||
};
|
||||
|
||||
virtual void OnFirstEnable() override;
|
||||
virtual void OnEnable() override;
|
||||
virtual void OnDisable() override;
|
||||
virtual bool AutoEnableOnAttach() const override { return true; }
|
||||
|
||||
std::unordered_map<TempPedTemplateId, TempPedTemplate>::iterator FindTempTemplateByPersistenceKey(const std::string& persistenceKey);
|
||||
|
||||
void SaveToPersistence() const;
|
||||
void LoadFromPersistence();
|
||||
static bool ConvertPersistence(std::unique_ptr<uint8_t[]>& dataPtr, size_t& dataSize, uint8_t version);
|
||||
void RefreshSortedCache();
|
||||
|
||||
static PedTemplatesCheat* m_instance;
|
||||
std::unordered_map<std::wstring, PedTemplate> m_templates = {};
|
||||
std::unordered_map<TempPedTemplateId, TempPedTemplate> m_tempTemplates = {};
|
||||
TempPedTemplateId m_nextTempTemplateId = 1;
|
||||
std::vector<std::wstring> m_sortedCache = {};
|
||||
bool m_cacheDirty = true;
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user