mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
feat(world): add weather options
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user