From 0e36f887d742209cd4cf83db391ea93479d5716c Mon Sep 17 00:00:00 2001 From: Arthur <121949966+ShinyWasabi@users.noreply.github.com> Date: Sat, 14 Jun 2025 21:48:41 +0300 Subject: [PATCH] feat: Shoot Animals Photography (#427) * feat: Shoot Animals Photography * Fix ptr name --- .../features/recovery/DailyActivities.cpp | 84 +++++++++++++++++++ .../submenus/Recovery/DailyActivities.cpp | 32 ++++--- src/game/frontend/submenus/Self/Weapons.cpp | 52 +++++++++++- src/game/pointers/Pointers.cpp | 5 ++ 4 files changed, 157 insertions(+), 16 deletions(-) diff --git a/src/game/features/recovery/DailyActivities.cpp b/src/game/features/recovery/DailyActivities.cpp index 61cf643..0c56d68 100644 --- a/src/game/features/recovery/DailyActivities.cpp +++ b/src/game/features/recovery/DailyActivities.cpp @@ -6,6 +6,7 @@ #include "game/backend/Self.hpp" #include "game/backend/ScriptPatches.hpp" #include "game/backend/Tunables.hpp" +#include "game/gta/Ped.hpp" #include "game/gta/Natives.hpp" #include "game/gta/Scripts.hpp" #include "game/gta/Stats.hpp" @@ -79,6 +80,36 @@ namespace YimMenu::Features {{0, "LS Tag 1"}, {1, "LS Tag 2"}, {2, "LS Tag 3"}, {3, "LS Tag 4"}, {4, "LS Tag 5"}} }; + static ListCommand animalIndex = { + "animalindex", + "Animal", + "Selected Animal", + {{0, "Animal 1"}, {1, "Animal 2"}, {2, "Animal 3"}} + }; + + static constexpr auto wildlifePhotographyAnimalHashes = std::to_array({ + "A_C_Boar"_J, + "A_C_Cat_01"_J, + "A_C_Cow"_J, + "A_C_Coyote"_J, + "A_C_Deer"_J, + "A_C_Husky"_J, + "A_C_MtLion"_J, + "A_C_Pig"_J, + "A_C_Poodle"_J, + "A_C_Pug"_J, + "A_C_Rabbit_01"_J, + "A_C_Retriever"_J, + "A_C_Rottweiler"_J, + "A_C_shepherd"_J, + "A_C_Westy"_J, + "A_C_Chickenhawk"_J, + "A_C_Cormorant"_J, + "A_C_Crow"_J, + "A_C_Hen"_J, + "A_C_Seagull"_J + }); + static void SetAllDailyActivitiesCompleted(bool completed) { Stats::SetPackedBool(36628, completed); // G's Cache @@ -934,6 +965,56 @@ namespace YimMenu::Features } }; + class SpawnAnimal : public Command + { + using Command::Command; + + virtual void OnCall() override + { + if (!*Pointers.IsSessionStarted) + return; + + if (!Stats::GetPackedBool(42059 + animalIndex.GetState())) + { + int index = Stats::GetPackedInt(28091 + animalIndex.GetState()); + if (index < 0 || index >= wildlifePhotographyAnimalHashes.size()) + return; + + Ped::Create(wildlifePhotographyAnimalHashes[index], Self::GetPed().GetPosition()); + } + else + { + Notifications::Show("Shoot Animals Photography", "This animal has already been photographed.", NotificationType::Error); + } + } + }; + + class PhotographAnimal : public Command + { + using Command::Command; + + virtual void OnCall() override + { + if (!*Pointers.IsSessionStarted) + return; + + if (!Stats::GetPackedBool(42059 + animalIndex.GetState())) + { + int index = Stats::GetPackedInt(28091 + animalIndex.GetState()); + if (index < 0 || index >= wildlifePhotographyAnimalHashes.size()) + return; + + ScriptGlobal(2708543).At(544).As*>()->Set(6); + *ScriptGlobal(2708543).At(547).As() = wildlifePhotographyAnimalHashes[index]; + *ScriptGlobal(2708543).At(548).As() = *Pointers.GameTimer - 1; // bypass 2 sec delay + } + else + { + Notifications::Show("Shoot Animals Photography", "This animal has already been photographed.", NotificationType::Error); + } + } + }; + static SetAllActivitiesCompleted _SetAllActivitiesCompleted{"setallactivitiescompleted", "Set All Activities Completed", "Switch session to apply the changes."}; static ResetAllActivities _ResetAllActivities{"resetallactivities", "Reset All Activities", "Switch session to apply the changes."}; @@ -976,4 +1057,7 @@ namespace YimMenu::Features static TeleportToMadrazoHit _TeleportToMadrazoHit{"tptomadrazohit", "Teleport to Madrazo Hit", "Teleports to Madrazo Hit."}; static TeleportToMadrazoHitTarget _TeleportToMadrazoHitTarget{"tptomadrazohittarget", "Teleport to Madrazo Hit Target", "Teleports to Madrazo Hit Target."}; + + static SpawnAnimal _SpawnAnimal{"spawnanimal", "Spawn Animal", "Spawns the selected animal."}; + static PhotographAnimal _PhotographAnimal{"photographanimal", "Photograph Animal", "Photographs the selected animal."}; } \ No newline at end of file diff --git a/src/game/frontend/submenus/Recovery/DailyActivities.cpp b/src/game/frontend/submenus/Recovery/DailyActivities.cpp index ebc36da..3261986 100644 --- a/src/game/frontend/submenus/Recovery/DailyActivities.cpp +++ b/src/game/frontend/submenus/Recovery/DailyActivities.cpp @@ -6,19 +6,20 @@ namespace YimMenu::Submenus { auto dailyActivities = std::make_shared("Daily Activities"); - auto challengesTab = std::make_shared("Challenges"); - auto hiddenCacheTab = std::make_shared("Hidden Caches"); - auto treasureChestTab = std::make_shared("Treasure Chests"); - auto shipwreckedTab = std::make_shared("Shipwreck"); - auto buriedStashTab = std::make_shared("Buried Stashes"); - auto skydiveTab = std::make_shared("Junk Energy Skydives"); - auto timeTrialsTab = std::make_shared("Time Trials"); - auto exoticExportsTab = std::make_shared("Exotic Exports"); - auto deadDropTab = std::make_shared("G's Cache"); - auto stashHouseTab = std::make_shared("Stash House"); - auto streetDealerTab = std::make_shared("Street Dealers"); - auto lsTagsTab = std::make_shared("LS Tags"); - auto madrazoHitsTab = std::make_shared("Madrazo Hits"); + auto challengesTab = std::make_shared("Challenges"); + auto hiddenCacheTab = std::make_shared("Hidden Caches"); + auto treasureChestTab = std::make_shared("Treasure Chests"); + auto shipwreckedTab = std::make_shared("Shipwreck"); + auto buriedStashTab = std::make_shared("Buried Stashes"); + auto skydiveTab = std::make_shared("Junk Energy Skydives"); + auto timeTrialsTab = std::make_shared("Time Trials"); + auto exoticExportsTab = std::make_shared("Exotic Exports"); + auto deadDropTab = std::make_shared("G's Cache"); + auto stashHouseTab = std::make_shared("Stash House"); + auto streetDealerTab = std::make_shared("Street Dealers"); + auto lsTagsTab = std::make_shared("LS Tags"); + auto madrazoHitsTab = std::make_shared("Madrazo Hits"); + auto wildlifePhotography = std::make_shared("Shoot Animals Photography"); dailyActivities->AddItem(std::make_shared("setallactivitiescompleted"_J)); dailyActivities->AddItem(std::make_shared("resetallactivities"_J)); @@ -71,6 +72,10 @@ namespace YimMenu::Submenus madrazoHitsTab->AddItem(std::make_shared("tptomadrazohit"_J)); madrazoHitsTab->AddItem(std::make_shared("tptomadrazohittarget"_J)); + wildlifePhotography->AddItem(std::make_shared("animalindex"_J)); + wildlifePhotography->AddItem(std::make_shared("spawnanimal"_J)); + wildlifePhotography->AddItem(std::make_shared("photographanimal"_J)); + dailyActivities->AddItem(std::move(challengesTab)); dailyActivities->AddItem(std::move(hiddenCacheTab)); dailyActivities->AddItem(std::move(treasureChestTab)); @@ -84,6 +89,7 @@ namespace YimMenu::Submenus dailyActivities->AddItem(std::move(streetDealerTab)); dailyActivities->AddItem(std::move(lsTagsTab)); dailyActivities->AddItem(std::move(madrazoHitsTab)); + dailyActivities->AddItem(std::move(wildlifePhotography)); return dailyActivities; } } \ No newline at end of file diff --git a/src/game/frontend/submenus/Self/Weapons.cpp b/src/game/frontend/submenus/Self/Weapons.cpp index c19af68..ef5f720 100644 --- a/src/game/frontend/submenus/Self/Weapons.cpp +++ b/src/game/frontend/submenus/Self/Weapons.cpp @@ -17,6 +17,33 @@ namespace YimMenu::Submenus joaat_t hash; }; + static void FetchWeaponStats(joaat_t weaponHash, int& kills, int& deaths, float& kd, int& headshots, int& accuracy) + { + uint64_t garbage[4]{}; + if (auto id = Scripts::StartScript("mp_weapons"_J, eStackSizes::PAUSE_MENU_SCRIPT, &garbage, 4)) + { + if (auto thread = Scripts::FindScriptThreadByID(id)) + { + thread->m_Context.m_State = rage::scrThread::State::PAUSED; + + static ScriptFunction getWeaponKills("mp_weapons"_J, ScriptPointer("GetWeaponKills", "5D ? ? ? 39 0F 38 00").Add(1).Rip()); + static ScriptFunction getWeaponDeaths("mp_weapons"_J, ScriptPointer("GetWeaponDeaths", "5D ? ? ? 39 10").Add(1).Rip()); + static ScriptFunction getWeaponKDRatio("mp_weapons"_J, ScriptPointer("GetWeaponKDRatio", "5D ? ? ? 39 12").Add(1).Rip()); + static ScriptFunction getWeaponHeadshots("mp_weapons"_J, ScriptPointer("GetWeaponHeadshots", "5D ? ? ? 39 11").Add(1).Rip()); + static ScriptFunction getWeaponAccuracy("mp_weapons"_J, ScriptPointer("GetWeaponAccuracy", "2D 01 09 00 00")); + + kills = getWeaponKills.Call(weaponHash, -1); + deaths = getWeaponDeaths.Call(weaponHash, -1); + kd = getWeaponKDRatio.Call(weaponHash, -1); + headshots = getWeaponHeadshots.Call(weaponHash, -1); + accuracy = static_cast(getWeaponAccuracy.Call(weaponHash)); + + thread->Kill(); + thread->m_Context.m_State = rage::scrThread::State::KILLED; + } + } + } + static void RenderAmmuNationMenu() { static std::vector weaponDisplays; @@ -24,12 +51,19 @@ namespace YimMenu::Submenus static joaat_t selectedWeaponHash{}; static char searchWeapon[64]; + static int kills{}; + static int deaths{}; + static float kdRatio{}; + static int headshots{}; + static int accuracy{}; + static bool init = [] { FiberPool::Push([] { while (Scripts::IsScriptActive("startup"_J)) ScriptMgr::Yield(); - if (auto id = Scripts::StartScript("mp_weapons"_J)) + uint64_t garbage[4]{}; + if (auto id = Scripts::StartScript("mp_weapons"_J, eStackSizes::PAUSE_MENU_SCRIPT, &garbage, 4)) { if (auto thread = Scripts::FindScriptThreadByID(id)) { @@ -85,8 +119,11 @@ namespace YimMenu::Submenus ImGui::PushID(weap.hash); if (ImGui::Selectable(weap.name.c_str())) { - selectedWeapon = weap.name; - selectedWeaponHash = weap.hash; + FiberPool::Push([weap] { + selectedWeapon = weap.name; + selectedWeaponHash = weap.hash; + FetchWeaponStats(selectedWeaponHash, kills, deaths, kdRatio, headshots, accuracy); + }); } ImGui::PopID(); if (ImGui::IsItemHovered() && !weap.desc.empty()) @@ -115,6 +152,15 @@ namespace YimMenu::Submenus Self::GetPed().RemoveWeapon(selectedWeaponHash); }); } + + if (*Pointers.IsSessionStarted && selectedWeaponHash != NULL) + { + ImGui::Text("Kills With: %d", kills); + ImGui::Text("Deaths By: %d", deaths); + ImGui::Text("K/D Ratio: %.2f", kdRatio); + ImGui::Text("Headshots: %d", headshots); + ImGui::Text("Accuracy: %d%%", accuracy); + } } std::shared_ptr BuildWeaponsMenu() diff --git a/src/game/pointers/Pointers.cpp b/src/game/pointers/Pointers.cpp index 30bdd31..7fd5a32 100644 --- a/src/game/pointers/Pointers.cpp +++ b/src/game/pointers/Pointers.cpp @@ -395,6 +395,11 @@ namespace YimMenu GameSkeleton = ptr.Add(0x9).Add(3).Rip().As(); }); + constexpr auto SetExplosiveAmmoOnlinePatchPtrn = Pattern<"48 83 EC 28 80 3D ? ? ? ? 00 0F 85 ? ? ? ? E9 ? ? ? ? 48 8B 45 28">("SetExplosiveAmmoOnlinePatch"); + scanner.Add(SetExplosiveAmmoOnlinePatchPtrn, [this](PointerCalculator ptr) { + BytePatches::Add(ptr.Add(0xB).As(), 0x04EB)->Apply(); + }); + if (!scanner.Scan()) { LOG(FATAL) << "Some patterns could not be found, unloading.";