feat(world): add weather options

This commit is contained in:
maybegreat48
2025-04-07 00:43:09 -04:00
parent 143bde0eb8
commit 171bb62d76
4 changed files with 200 additions and 9 deletions
+135
View File
@@ -0,0 +1,135 @@
#include "core/commands/Command.hpp"
#include "core/commands/ListCommand.hpp"
#include "core/commands/LoopedCommand.hpp"
#include "game/backend/ScriptPatches.hpp"
#include "game/gta/Natives.hpp"
namespace YimMenu::Features
{
static std::vector<std::pair<int, const char*>> g_WeatherTypes = {
{0, "Clear"},
{1, "Extra Sunny"},
{2, "Clouds"},
{3, "Overcast"},
{4, "Rain"},
{5, "Clearing"},
{6, "Thunder"},
{7, "Smog"},
{8, "Foggy"},
{9, "Xmas"},
{10, "Snow"},
{11, "Snow Light"},
{12, "Blizzard"},
{13, "Halloween"},
{14, "Neutral"},
{15, "Rain Halloween"},
{16, "Snow Halloween"},
};
constexpr std::array g_WeatherCodes = {
"CLEAR",
"EXTRASUNNY",
"CLOUDS",
"OVERCAST",
"RAIN",
"CLEARING",
"THUNDER",
"SMOG",
"FOGGY",
"XMAS",
"SNOW",
"SNOWLIGHT",
"BLIZZARD",
"HALLOWEEN",
"NEUTRAL",
"RAIN_HALLOWEEN",
"SNOW_HALLOWEEN",
};
static ListCommand _Weather{"weather", "Weather", "Weather to set or force. Note that this is local and cannot be seen by other players", g_WeatherTypes, 0};
class SetWeather : public Command
{
using Command::Command;
virtual void OnCall() override
{
MISC::SET_WEATHER_TYPE_PERSIST(g_WeatherCodes[_Weather.GetState()]);
}
};
static SetWeather _SetWeather{"setweather", "Set Weather", "Sets the specifed weather. Note that this effect is local and cannot be seen by other players"};
class ForceWeather : public LoopedCommand
{
using LoopedCommand::LoopedCommand;
ScriptPatch m_ShopControllerWeatherPatch{};
bool m_SnowEnabled{};
void EnableSnow()
{
if (!m_SnowEnabled)
{
STREAMING::REQUEST_NAMED_PTFX_ASSET("core_snow");
if (AUDIO::REQUEST_SCRIPT_AUDIO_BANK("SNOW_FOOTSTEPS", false, -1) && STREAMING::HAS_NAMED_PTFX_ASSET_LOADED("core_snow"))
{
GRAPHICS::USE_SNOW_FOOT_VFX_WHEN_UNSHELTERED(true);
GRAPHICS::USE_SNOW_WHEEL_VFX_WHEN_UNSHELTERED(true);
GRAPHICS::_FORCE_ALLOW_SNOW_FOOT_VFX_ON_ICE(true);
GRAPHICS::_FORCE_GROUND_SNOW_PASS(true);
m_SnowEnabled = true;
}
}
}
void DisableSnow()
{
if (m_SnowEnabled)
{
GRAPHICS::USE_SNOW_FOOT_VFX_WHEN_UNSHELTERED(false);
GRAPHICS::USE_SNOW_WHEEL_VFX_WHEN_UNSHELTERED(false);
GRAPHICS::_FORCE_ALLOW_SNOW_FOOT_VFX_ON_ICE(false);
GRAPHICS::_FORCE_GROUND_SNOW_PASS(false);
STREAMING::REMOVE_NAMED_PTFX_ASSET("core_snow");
AUDIO::RELEASE_NAMED_SCRIPT_AUDIO_BANK("SNOW_FOOTSTEPS");
m_SnowEnabled = false;
}
}
virtual void OnEnable() override
{
// ensure shop controller can't control the weather
if (!m_ShopControllerWeatherPatch)
{
m_ShopControllerWeatherPatch = ScriptPatches::AddPatch("shop_controller"_J, "2D 00 07 00 00 71 39 02 71", 5, {0x2E, 0x00, 0x00});
}
m_ShopControllerWeatherPatch->Enable();
}
virtual void OnTick() override
{
auto weather = _Weather.GetState();
MISC::SET_OVERRIDE_WEATHER(g_WeatherCodes[weather]);
if (weather == 9 || weather == 16)
EnableSnow();
else
DisableSnow();
}
virtual void OnDisable() override
{
if (m_ShopControllerWeatherPatch)
{
m_ShopControllerWeatherPatch->Disable();
}
DisableSnow();
MISC::CLEAR_OVERRIDE_WEATHER();
}
};
static ForceWeather _ForceWeather{"forceweather", "Force Weather", "Forces the specified weather. Note that this effect is local and cannot be seen by other players"};
}
@@ -214,6 +214,21 @@ namespace YimMenu::Submenus
STATS::SET_PACKED_STAT_INT_CODE(info.m_Index, value.m_AsInt, -1);
}
static void WritePackedStatRange(int start, int end, int value)
{
for (int i = start; i <= end; i++)
{
auto info = GetPackedStatInfo(i);
if (!info.m_IsValid)
break; // the rest are probably not valid, either
if (info.m_IsBoolStat)
STATS::SET_PACKED_STAT_BOOL_CODE(info.m_Index, static_cast<bool>(value), -1);
else
STATS::SET_PACKED_STAT_INT_CODE(info.m_Index, value, -1);
}
}
static bool RenderPackedStatEditor(StatValue& value, const PackedStatInfo& info)
{
ImGui::SetNextItemWidth(150.f);
@@ -228,6 +243,7 @@ namespace YimMenu::Submenus
auto menu = std::make_shared<Category>("Stat Editor");
auto normal = std::make_shared<Group>("Regular");
auto packed = std::make_shared<Group>("Packed");
auto packed_range = std::make_shared<Group>("Packed Range");
normal->AddItem(std::make_unique<ImGuiItem>([] {
if (!NativeInvoker::AreHandlersCached())
@@ -303,9 +319,29 @@ namespace YimMenu::Submenus
});
}));
packed_range->AddItem(std::make_unique<ImGuiItem>([] {
if (!NativeInvoker::AreHandlersCached())
return ImGui::TextDisabled("Natives not cached yet");
static int start{}, end{}, value{};
ImGui::SetNextItemWidth(150.f);
ImGui::InputInt("Start", &start);
ImGui::SameLine();
ImGui::SetNextItemWidth(150.f);
ImGui::InputInt("End", &end);
ImGui::SetNextItemWidth(150.f);
ImGui::InputScalar("Value##packed_range", ImGuiDataType_U8, &value);
ImGui::SameLine();
if (ImGui::Button("Write##packed_range"))
FiberPool::Push([] {
WritePackedStatRange(start, end, value);
});
}));
menu->AddItem(std::move(normal));
menu->AddItem(std::move(packed));
menu->AddItem(std::move(packed_range));
return menu;
}
}
@@ -359,7 +359,7 @@ namespace YimMenu::Submenus
}
}
static void PrintWarningMessage(std::string_view message)
static void SetTransactionError(std::string_view message)
{
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::Colors::Red.Value);
@@ -367,6 +367,14 @@ namespace YimMenu::Submenus
ImGui::PopStyleColor();
}
static void SetTransactionWarning(std::string_view message)
{
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::Colors::Yellow.Value);
ImGui::Text("%s", message.data());
ImGui::PopStyleColor();
}
static bool EditTransactionItem(std::string_view label, TransactionInfo& info, TransactionItemHash& item, bool& is_valid, bool required = true, bool validate_category = true)
{
bool modified = false;
@@ -396,7 +404,7 @@ namespace YimMenu::Submenus
if (!item.m_IsValid && !empty)
{
PrintWarningMessage("Item not found!");
SetTransactionError("Item not found!");
is_valid = false;
return false;
}
@@ -404,17 +412,24 @@ namespace YimMenu::Submenus
// TODO: maybe not check this every tick?
if (BANNED_ITEM_HASHES.contains(item.m_Hash))
{
PrintWarningMessage("This item has been blocked for your safety");
SetTransactionError("This item has been blocked for your safety");
is_valid = false;
return false;
}
// TODO: is this really needed? useful? could we abuse more without this check?
if (validate_category && !IsPriceModifier(item.m_IntendedCategory) && item.m_IntendedCategory != info.m_Category.m_Hash && !empty)
{
PrintWarningMessage(std::format("Item category {} does not match txn category {}", CategoryNameFromHash(item.m_IntendedCategory), info.m_Category.m_Name));
is_valid = false;
return false;
if (info.m_Type == TransactionInfo::Type::SERVICE)
{
SetTransactionError(std::format("Item category {} does not match txn category {}", CategoryNameFromHash(item.m_IntendedCategory), info.m_Category.m_Name));
is_valid = false;
return false;
}
else
{
SetTransactionWarning(std::format("Item category {} does not match txn category {}", CategoryNameFromHash(item.m_IntendedCategory), info.m_Category.m_Name));
}
}
return modified;
@@ -481,7 +496,7 @@ namespace YimMenu::Submenus
ImGui::InputInt("Price", &info.m_Service.m_Price);
if (info.m_Service.m_Price > info.m_Service.m_Item.m_IntendedPrice && info.m_Action.m_Hash == "NET_SHOP_ACTION_EARN"_J)
{
PrintWarningMessage(std::format("Item price exceeds maximum allowed ({})", info.m_Service.m_Item.m_IntendedPrice));
SetTransactionError(std::format("Item price exceeds maximum allowed ({})", info.m_Service.m_Item.m_IntendedPrice));
txn_valid = false;
}
}
+6 -1
View File
@@ -9,7 +9,6 @@ namespace YimMenu::Submenus
auto main = std::make_shared<Category>("Main");
auto spawnersGroup = std::make_shared<Category>("Spawners");
auto iplsGroup = std::make_shared<Category>("IPLs");
auto killPeds = std::make_shared<Group>("Kill", 1);
killPeds->AddItem(std::make_shared<CommandItem>("killallpeds"_J));
@@ -23,9 +22,15 @@ namespace YimMenu::Submenus
bringOpts->AddItem(std::make_shared<CommandItem>("bringvehs"_J));
bringOpts->AddItem(std::make_shared<CommandItem>("bringobjs"_J));
auto weatherOpts = std::make_shared<Group>("Weather", 1);
weatherOpts->AddItem(std::make_shared<ListCommandItem>("weather"_J));
weatherOpts->AddItem(std::make_shared<ConditionalItem>("forceweather"_J, std::make_shared<CommandItem>("setweather"_J), true));
weatherOpts->AddItem(std::make_shared<BoolCommandItem>("forceweather"_J));
main->AddItem(std::move(killPeds));
main->AddItem(std::move(deleteOpts));
main->AddItem(std::move(bringOpts));
main->AddItem(std::move(weatherOpts));
auto spawnGroup = std::make_shared<Group>("Vehicle");
auto modsGroup = std::make_shared<Group>("Modifications");