mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 11:53:14 +08:00
0e36f887d7
* feat: Shoot Animals Photography * Fix ptr name
1063 lines
34 KiB
C++
1063 lines
34 KiB
C++
#include "DailyActivities.hpp"
|
|
#include "core/commands/ListCommand.hpp"
|
|
#include "core/commands/BoolCommand.hpp"
|
|
#include "core/backend/ScriptMgr.hpp"
|
|
#include "core/frontend/Notifications.hpp"
|
|
#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"
|
|
#include "game/gta/Vehicle.hpp"
|
|
#include "game/gta/ScriptFunction.hpp"
|
|
#include "game/gta/ScriptLocal.hpp"
|
|
#include "game/gta/ScriptGlobal.hpp"
|
|
#include "game/pointers/Pointers.hpp"
|
|
#include "types/blip/BlipSprite.hpp"
|
|
#include "types/script/ScriptEvent.hpp"
|
|
#include "types/script/MPScriptData.hpp"
|
|
#include "types/script/globals/GPBD_Flow.hpp"
|
|
#include "types/script/globals/g_SavedMPGlobals.hpp"
|
|
#include "types/script/globals/DailyObjectivesState.hpp"
|
|
#include "types/script/globals/WeeklyChallenge.hpp"
|
|
#include "types/script/globals/GSBD_RandomEvents.hpp"
|
|
#include "types/script/globals/FreemodeGeneral.hpp"
|
|
//#include "types/script/globals/FreemodeDelivery.hpp"
|
|
#include "types/script/locals/StandardTimeTrialData.hpp"
|
|
#include "types/script/locals/RCBanditoTimeTrialData.hpp"
|
|
#include "types/script/locals/BikeTimeTrialData.hpp"
|
|
|
|
namespace YimMenu::Features
|
|
{
|
|
static ListCommand hiddenCacheIndex = {
|
|
"hiddencacheindex",
|
|
"Hidden Cache",
|
|
"Selected Hidden Cache",
|
|
{{0, "Hidden Cache 1"}, {1, "Hidden Cache 2"}, {2, "Hidden Cache 3"}, {3, "Hidden Cache 4"}, {4, "Hidden Cache 5"}, {5, "Hidden Cache 6"}, {6, "Hidden Cache 7"}, {7, "Hidden Cache 8"}, {8, "Hidden Cache 9"}, {9, "Hidden Cache 10"}}
|
|
};
|
|
|
|
static ListCommand treasureChestIndex = {
|
|
"treasurechestindex",
|
|
"Treasure Chest",
|
|
"Selected Treasure Chest",
|
|
{{0, "Treasure Chest 1"}, {1, "Treasure Chest 2"}}
|
|
};
|
|
|
|
static ListCommand buriedStashIndex = {
|
|
"buriedstashindex",
|
|
"Buried Stash",
|
|
"Selected Buried Stash",
|
|
{{0, "Buried Stash 1"}, {1, "Buried Stash 2"}}
|
|
};
|
|
|
|
static ListCommand skydiveIndex = {
|
|
"skydiveindex",
|
|
"Skydive",
|
|
"Selected Skydive",
|
|
{{0, "Skydive 1"}, {1, "Skydive 2"}, {2, "Skydive 3"}, {3, "Skydive 4"}, {4, "Skydive 5"}, {5, "Skydive 6"}, {6, "Skydive 7"}, {7, "Skydive 8"}, {8, "Skydive 9"}, {9, "Skydive 10"}}
|
|
};
|
|
|
|
static ListCommand timeTrialIndex = {
|
|
"timetrialindex",
|
|
"Time Trial",
|
|
"Selected Time Trial",
|
|
{{0, "Standard Time Trial"}, {1, "RC Bandito Time Trial"}, {2, "Junk Energy Bike Time Trial"}}
|
|
};
|
|
|
|
static ListCommand streetDealerIndex = {
|
|
"streetdealerindex",
|
|
"Street Dealer",
|
|
"Selected Street Dealer",
|
|
{{0, "Street Dealer 1"}, {1, "Street Dealer 2"}, {2, "Street Dealer 3"}}
|
|
};
|
|
|
|
static ListCommand lsTagIndex = {
|
|
"lstagindex",
|
|
"LS Tag",
|
|
"Selected LS Tag",
|
|
{{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
|
|
Stats::SetPackedBool(36657, completed); // Stash House
|
|
Stats::SetPackedBool(31734, completed); // Shipwreck
|
|
Stats::SetPackedBool(30297, completed); // Hidden Cache 1
|
|
Stats::SetPackedBool(30298, completed); // Hidden Cache 2
|
|
Stats::SetPackedBool(30299, completed); // Hidden Cache 3
|
|
Stats::SetPackedBool(30300, completed); // Hidden Cache 4
|
|
Stats::SetPackedBool(30301, completed); // Hidden Cache 5
|
|
Stats::SetPackedBool(30302, completed); // Hidden Cache 6
|
|
Stats::SetPackedBool(30303, completed); // Hidden Cache 7
|
|
Stats::SetPackedBool(30304, completed); // Hidden Cache 8
|
|
Stats::SetPackedBool(30305, completed); // Hidden Cache 9
|
|
Stats::SetPackedBool(30306, completed); // Hidden Cache 10
|
|
Stats::SetPackedBool(30307, completed); // Treasure Chest 1
|
|
Stats::SetPackedBool(30308, completed); // Treasure Chest 2
|
|
Stats::SetPackedBool(25522, completed); // Buried Stash 1
|
|
Stats::SetPackedBool(25523, completed); // Buried Stash 2
|
|
Stats::SetPackedBool(42252, completed); // LS Tag 1
|
|
Stats::SetPackedBool(42253, completed); // LS Tag 2
|
|
Stats::SetPackedBool(42254, completed); // LS Tag 3
|
|
Stats::SetPackedBool(42255, completed); // LS Tag 4
|
|
Stats::SetPackedBool(42256, completed); // LS Tag 5
|
|
Stats::SetPackedBool(42269, completed); // Madrazo Hit
|
|
Stats::SetPackedBool(42059, completed); // Shoot Animals Photography 1
|
|
Stats::SetPackedBool(42060, completed); // Shoot Animals Photography 2
|
|
Stats::SetPackedBool(42061, completed); // Shoot Animals Photography 3
|
|
// Street Dealers
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
static Tunable maxCoke{1238316723};
|
|
static Tunable maxMeth{658190943};
|
|
static Tunable maxWeed{803541362};
|
|
static Tunable maxAcid{3123173154};
|
|
|
|
int statIndex = 41218 + i * 5;
|
|
int premium = completed ? 0 : static_cast<int>(FreemodeGeneral::Get()->StreetDealers.Dealers[i].PremiumProduct);
|
|
int coke = completed ? 0 : (maxCoke.IsReady() ? maxCoke.Get<int>() : 1);
|
|
int meth = completed ? 0 : (maxMeth.IsReady() ? maxMeth.Get<int>() : 2);
|
|
int weed = completed ? 0 : (maxWeed.IsReady() ? maxWeed.Get<int>() : 10);
|
|
int acid = completed ? 0 : (maxAcid.IsReady() ? maxAcid.Get<int>() : 10);
|
|
Stats::SetPackedInt(statIndex + 0, premium);
|
|
Stats::SetPackedInt(statIndex + 1, coke);
|
|
Stats::SetPackedInt(statIndex + 2, meth);
|
|
Stats::SetPackedInt(statIndex + 3, weed);
|
|
Stats::SetPackedInt(statIndex + 4, acid);
|
|
}
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
// see TSE 1916113629
|
|
int location = Stats::GetInt("MPX_DAILYCOLLECT_SKYDIVES" + std::to_string(i));
|
|
Stats::SetPackedInt((34837 + i * 4), completed ? location : -1); // Junk Energy Skydives Checkpoint
|
|
Stats::SetPackedInt((34839 + i * 4), completed ? location : -1); // Junk Energy Skydives Accurate Landing
|
|
Stats::SetPackedInt((34838 + i * 4), completed ? location : -1); // Junk Energy Skydives Partime
|
|
Stats::SetPackedInt((34840 + i * 4), completed ? location : -1); // Junk Energy Skydives Gold
|
|
}
|
|
for (int i = 34252; i <= 34261; i++)
|
|
{
|
|
Stats::SetPackedBool(i, completed); // Trick or Treat
|
|
}
|
|
for (int i = 34512; i <= 34701; i++)
|
|
{
|
|
Stats::SetPackedBool(i, completed); // Trick or Treat
|
|
}
|
|
int ttLocation = 0;
|
|
int rcttLocation = 0;
|
|
int bttLocation = 0;
|
|
if (auto thread = Scripts::FindScriptThread("freemode"_J))
|
|
{
|
|
ttLocation = StandardTimeTrialData::Get(thread)->Location; // gets its value from tunable TIMETRIALVARIATION
|
|
// we can actually manually calculate these using FreemodeGeneral::Get()->DailyReset.Seed % 14
|
|
rcttLocation = RCBanditoTimeTrialData::Get(thread)->Location;
|
|
bttLocation = BikeTimeTrialData::Get(thread)->Location;
|
|
}
|
|
Stats::SetInt("MPPLY_TIMETRIAL_COMPLETED_WEEK", completed ? ttLocation : -1); // Standard Time Trial
|
|
Stats::SetInt("MPPLY_RCTTCOMPLETEDWEEK", completed ? rcttLocation : -1); // RC Bandito Time Trial
|
|
Stats::SetInt("MPPLY_BTTCOMPLETED", completed ? bttLocation : -1); // Junk Energy Bike Time Trial
|
|
Stats::SetInt("MPX_CBV_DELIVERED_BS", completed ? 1023 : 0); // Exotic Exports
|
|
Stats::SetInt("MPX_CBV_STATE", completed); // Exotic Exports
|
|
}
|
|
|
|
static void TeleportToCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables collectable, int index)
|
|
{
|
|
static ScriptFunction getCollectibleCoords("freemode"_J, ScriptPointer("GetCollectibleCoords", "5D ? ? ? 7D 2C 10").Add(1).Rip());
|
|
if (auto coords = getCollectibleCoords.Call<Vector3>(collectable, index))
|
|
{
|
|
Self::GetPed().TeleportTo(coords);
|
|
}
|
|
}
|
|
|
|
static void CollectCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables collectable, int index)
|
|
{
|
|
SCRIPT_EVENT_COLLECT_COLLECTABLE data;
|
|
data.CollectableType = collectable;
|
|
data.CollectableIndex = index;
|
|
data.SetCollected = TRUE;
|
|
data.DisplayNotification = TRUE;
|
|
data.Send();
|
|
}
|
|
|
|
static joaat_t GetNextExoticExportsVehicleHash()
|
|
{
|
|
constexpr ScriptGlobal order(1944094);
|
|
for (int i = 1; i <= 10; i++)
|
|
{
|
|
auto offset = *order.At(i).As<int*>();
|
|
if (!GPBD_Flow::Get()->Entries[Self::GetPlayer().GetId()].TunerCBVDeliveredFlags.IsSet(offset))
|
|
{
|
|
auto hash = *ScriptGlobal(1944083 + (offset + 1)).As<joaat_t*>();
|
|
return hash;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool initialized = false;
|
|
|
|
void OpenStreetDealerMenu::RunScriptImpl()
|
|
{
|
|
while (g_Running)
|
|
{
|
|
ScriptMgr::Yield();
|
|
|
|
if (!m_ShouldRunScript)
|
|
continue;
|
|
|
|
if (!m_Thread)
|
|
{
|
|
int id = Scripts::StartScript("fm_street_dealer"_J);
|
|
m_Thread = Scripts::FindScriptThreadByID(id);
|
|
if (m_Thread)
|
|
{
|
|
m_Thread->m_Context.m_State = rage::scrThread::State::PAUSED;
|
|
}
|
|
else
|
|
{
|
|
m_ShouldRunScript = false;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
auto streetDealerData = ScriptLocal(m_Thread, 253).At(12);
|
|
|
|
if (!initialized)
|
|
{
|
|
auto selected = streetDealerIndex.GetState();
|
|
FreemodeGeneral::Get()->StreetDealers.ClosestDealerLocation = FreemodeGeneral::Get()->StreetDealers.Dealers[selected].Location;
|
|
FreemodeGeneral::Get()->StreetDealers.ClosetsDealerIndex = selected;
|
|
|
|
static ScriptFunction initStreetDealerData("fm_street_dealer"_J, ScriptPointer("InitStreetDealerData", "2D 00 07 00 00 61 E9 CE 29"));
|
|
initStreetDealerData.Call<void>();
|
|
streetDealerData.At(5).As<SCR_BITSET<uint64_t>*>()->Set(0);
|
|
initialized = true;
|
|
}
|
|
|
|
static ScriptFunction runStreetDealerMenu("fm_street_dealer"_J, ScriptPointer("RunStreetDealerMenu", "2D 01 03 00 00 5D ? ? ? 2A"));
|
|
runStreetDealerMenu.Call<void>(streetDealerData.As<int*>());
|
|
|
|
if (streetDealerData.At(5).As<SCR_BITSET<uint64_t>*>()->IsSet(2) || !*Pointers.IsSessionStarted)
|
|
{
|
|
// if we don't reset these, freemode won't start the script legitimately
|
|
FreemodeGeneral::Get()->StreetDealers.ClosestDealerLocation = -1;
|
|
FreemodeGeneral::Get()->StreetDealers.ClosetsDealerIndex = -1;
|
|
|
|
m_Thread->Kill();
|
|
m_Thread->m_Context.m_State = rage::scrThread::State::KILLED;
|
|
m_Thread = nullptr;
|
|
initialized = false;
|
|
m_ShouldRunScript = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
class SetAllActivitiesCompleted : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
SetAllDailyActivitiesCompleted(true);
|
|
Notifications::Show("Daily Activities", "Completed all activities, switch session to apply the changes.");
|
|
}
|
|
};
|
|
|
|
class ResetAllActivities : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
SetAllDailyActivitiesCompleted(false);
|
|
Notifications::Show("Daily Activities", "Reset all activities, switch session to apply the changes.");
|
|
}
|
|
};
|
|
|
|
class CompleteAllChallenges : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
DailyObjectivesState::Get()->Objective[i] = g_SavedMPGlobals::Get()->Entries[0].GeneralSaved.DOObjectives[i].Objective;
|
|
}
|
|
DailyObjectivesState::Get()->Completed = TRUE;
|
|
WeeklyChallenge::Get()->Entries[0].CurrentObjective = WeeklyChallenge::Get()->Entries[0].ObjectiveCountOverride;
|
|
}
|
|
};
|
|
|
|
class TeleportToHiddenCache : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(30297 + hiddenCacheIndex.GetState()))
|
|
{
|
|
TeleportToCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::HiddenCaches, hiddenCacheIndex.GetState());
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Hidden Caches", "This Hidden Cache has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class CollectHiddenCache : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(30297 + hiddenCacheIndex.GetState()))
|
|
{
|
|
CollectCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::HiddenCaches, hiddenCacheIndex.GetState());
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Hidden Caches", "This Hidden Cache has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class TeleportToTreasureChest : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(30307 + treasureChestIndex.GetState()))
|
|
{
|
|
TeleportToCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::TresureChests, treasureChestIndex.GetState());
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Treasure Chests", "This Treasure Chest has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class CollectTreasureChest : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(30307 + treasureChestIndex.GetState()))
|
|
{
|
|
CollectCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::TresureChests, treasureChestIndex.GetState());
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Treasure Chests", "This Treasure Chest has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class EnableTreasureChestInLS : public BoolCommand
|
|
{
|
|
using BoolCommand::BoolCommand;
|
|
|
|
ScriptPatch m_EnableTreasureChestInLSPatch{};
|
|
|
|
virtual void OnEnable() override
|
|
{
|
|
if (!m_EnableTreasureChestInLSPatch)
|
|
{
|
|
m_EnableTreasureChestInLSPatch = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("EnableTreasureChestInLSPatch", "1F 56 ? ? 38 00 47 ? ? 73"), {0x72});
|
|
}
|
|
m_EnableTreasureChestInLSPatch->Enable();
|
|
}
|
|
|
|
virtual void OnDisable() override
|
|
{
|
|
if (m_EnableTreasureChestInLSPatch)
|
|
{
|
|
m_EnableTreasureChestInLSPatch->Disable();
|
|
}
|
|
}
|
|
};
|
|
|
|
class TeleportToShipwrecked : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(31734))
|
|
{
|
|
TeleportToCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::Shipwrecked, 0);
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Shipwreck", "Shipwreck has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class CollectShipwrecked : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(31734))
|
|
{
|
|
CollectCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::Shipwrecked, 0);
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Shipwreck", "Shipwreck has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class TeleportToBuriedStash : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(25522 + buriedStashIndex.GetState()))
|
|
{
|
|
TeleportToCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::BuriedStash, buriedStashIndex.GetState());
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Buried Stashes", "This Buried Stash has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class CollectBuriedStash : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(25522 + buriedStashIndex.GetState()))
|
|
{
|
|
CollectCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::BuriedStash, buriedStashIndex.GetState());
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Buried Stashes", "This Buried Stash has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class EnableBuriedStashInLS : public BoolCommand
|
|
{
|
|
using BoolCommand::BoolCommand;
|
|
|
|
ScriptPatch m_EnableBuriedStashInLSPatch{};
|
|
|
|
virtual void OnEnable() override
|
|
{
|
|
if (!m_EnableBuriedStashInLSPatch)
|
|
{
|
|
m_EnableBuriedStashInLSPatch = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("EnableBuriedStashInLSPatch", "71 39 02 38 02 06 56 ? ? 2C"), {0x72});
|
|
}
|
|
m_EnableBuriedStashInLSPatch->Enable();
|
|
}
|
|
|
|
virtual void OnDisable() override
|
|
{
|
|
if (m_EnableBuriedStashInLSPatch)
|
|
{
|
|
m_EnableBuriedStashInLSPatch->Disable();
|
|
}
|
|
}
|
|
};
|
|
|
|
class TeleportToSkydive : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
TeleportToCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::Skydives, skydiveIndex.GetState()); // no stat check as you can complete a skydive multiple times
|
|
}
|
|
};
|
|
|
|
class CompleteSkydive : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
static Tunable checkpointReward{"SKYDIVING_CHALLENGE_CASH_REWARD_ALL_CHECKPOINTS_COLLECTED"_J};
|
|
static Tunable parTimeReward{"SKYDIVING_CHALLENGE_CASH_REWARD_PAR_TIME"_J};
|
|
static Tunable landingReward{"SKYDIVING_CHALLENGE_CASH_REWARD_ACCURATE_LANDING"_J};
|
|
|
|
if (!checkpointReward.IsReady() || !parTimeReward.IsReady() || !landingReward.IsReady())
|
|
return;
|
|
|
|
int index = skydiveIndex.GetState();
|
|
int location = Stats::GetInt("MPX_DAILYCOLLECT_SKYDIVES" + std::to_string(index));
|
|
bool checkpoints = Stats::GetPackedInt(34837 + (index * 4)) == location;
|
|
bool partime = Stats::GetPackedInt(34838 + (index * 4)) == location;
|
|
bool landing = Stats::GetPackedInt(34839 + (index * 4)) == location;
|
|
|
|
*ScriptGlobal(1973628).As<int*>() = 1;
|
|
*ScriptGlobal(1973628).At(1).As<int*>() = 1;
|
|
*ScriptGlobal(1973628).At(3).As<int*>() = 5;
|
|
*ScriptGlobal(1973628).At(4).As<int*>() = location + 1;
|
|
*ScriptGlobal(1973628).At(8).At(1).As<int*>() = checkpointReward.Get<int>() / (1 + (checkpoints * 9));
|
|
*ScriptGlobal(1973628).At(8).At(2).As<int*>() = parTimeReward.Get<int>() / (1 + (partime * 9));
|
|
*ScriptGlobal(1973628).At(8).At(3).As<int*>() = landingReward.Get<int>() / (1 + (landing * 9));
|
|
|
|
SET_SKYDIVE_COMPLETED data;
|
|
data.SkydiveIndex = index;
|
|
data.SkydiveLocation = location;
|
|
data.AllCheckpointsHit = TRUE;
|
|
data.ParTimeBeaten = TRUE;
|
|
data.AccurateLanding = TRUE;
|
|
data.Send();
|
|
|
|
ScriptGlobal(1977574).At(4).As<SCR_BITSET<uint64_t>*>()->Clear(3);
|
|
}
|
|
};
|
|
|
|
class TeleportToTimeTrial : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
std::string stat = "";
|
|
switch (timeTrialIndex.GetState())
|
|
{
|
|
case 0: stat = "MPPLY_TIMETRIAL_COMPLETED_WEEK"; break;
|
|
case 1: stat = "MPPLY_RCTTCOMPLETEDWEEK"; break;
|
|
case 2: stat = "MPPLY_BTTCOMPLETED"; break;
|
|
}
|
|
|
|
if (stat.empty() || Stats::GetInt(stat) != -1)
|
|
{
|
|
Notifications::Show("Time Trials", "This Time Trial has already been beaten.", NotificationType::Error);
|
|
return;
|
|
}
|
|
|
|
int sprite = -1;
|
|
switch (timeTrialIndex.GetState())
|
|
{
|
|
case 0: sprite = static_cast<int>(BlipSprite::RADAR_TEMP_2); break;
|
|
case 1: sprite = static_cast<int>(BlipSprite::RADAR_RC_TIME_TRIALS); break;
|
|
case 2: sprite = static_cast<int>(BlipSprite::RADAR_BICYCLE_TRIAL); break;
|
|
}
|
|
|
|
auto blip = HUD::GET_FIRST_BLIP_INFO_ID(sprite);
|
|
if (HUD::DOES_BLIP_EXIST(blip))
|
|
{
|
|
if (auto coords = HUD::GET_BLIP_COORDS(blip))
|
|
{
|
|
Self::GetPed().TeleportTo(coords);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Time Trials", "Please enable the blip from the Interaction Menu.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class BeatTimeTrial : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
std::string stat = "";
|
|
switch (timeTrialIndex.GetState())
|
|
{
|
|
case 0: stat = "MPPLY_TIMETRIAL_COMPLETED_WEEK"; break;
|
|
case 1: stat = "MPPLY_RCTTCOMPLETEDWEEK"; break;
|
|
case 2: stat = "MPPLY_BTTCOMPLETED"; break;
|
|
}
|
|
|
|
if (stat.empty() || Stats::GetInt(stat) != -1)
|
|
{
|
|
Notifications::Show("Time Trials", "This Time Trial has already been beaten.", NotificationType::Error);
|
|
return;
|
|
}
|
|
|
|
rage::scrThread* thread = nullptr;
|
|
if (timeTrialIndex.GetState() == 2)
|
|
{
|
|
if (Scripts::IsScriptActive("fm_content_bicycle_time_trial"_J))
|
|
return;
|
|
|
|
MP_SCRIPT_DATA garbage{};
|
|
thread = Scripts::FindScriptThreadByID(Scripts::StartScript("fm_content_bicycle_time_trial"_J, eStackSizes::MULTIPLAYER_FREEMODE, &garbage, SCR_SIZEOF(garbage)));
|
|
}
|
|
else
|
|
{
|
|
thread = Scripts::FindScriptThread("freemode"_J);
|
|
}
|
|
|
|
if (!thread)
|
|
return;
|
|
|
|
if (timeTrialIndex.GetState() == 0)
|
|
{
|
|
if (auto ttData = StandardTimeTrialData::Get(thread))
|
|
{
|
|
ttData->Timer1.Time = *Pointers.NetworkTime;
|
|
static ScriptFunction beatStandardTimeTrial("freemode"_J, ScriptPointer("BeatStandardTimeTrial", "2D 01 19 00 00 38"));
|
|
beatStandardTimeTrial.Call<void>(ttData);
|
|
}
|
|
}
|
|
else if (timeTrialIndex.GetState() == 1)
|
|
{
|
|
if (auto rcttData = RCBanditoTimeTrialData::Get(thread))
|
|
{
|
|
rcttData->Timer1.Time = *Pointers.NetworkTime;
|
|
static ScriptFunction beatRCTimeTrial("freemode"_J, ScriptPointer("BeatRCTimeTrial", "2D 01 17 00 00 38 00 40"));
|
|
beatRCTimeTrial.Call<void>(rcttData);
|
|
}
|
|
}
|
|
else if (timeTrialIndex.GetState() == 2)
|
|
{
|
|
thread->m_Context.m_State = rage::scrThread::State::PAUSED;
|
|
|
|
*ScriptLocal(thread, 3060).At(130).At(1).As<int*>() = FreemodeGeneral::Get()->DailyReset.Seed % 14; // if we don't init this, the par time duration function will return 0 and the COMPLETED stat will be set to 0, which is bad
|
|
*ScriptLocal(thread, 142).At(4).As<int*>() = 0;
|
|
static ScriptFunction onBTTEnd("fm_content_bicycle_time_trial"_J, ScriptPointer("OnBTTEnd", "64 ? ? ? 5D ? ? ? 75 77").Add(1).Rip());
|
|
onBTTEnd.Call<void>();
|
|
|
|
thread->m_Context.m_State = rage::scrThread::State::KILLED;
|
|
}
|
|
}
|
|
};
|
|
|
|
class TeleportToExoticExportsVehicle : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (GPBD_Flow::Get()->Entries[Self::GetPlayer().GetId()].TunerCBVDeliveredFlags.Value == 1023)
|
|
{
|
|
Notifications::Show("Exotic Exports", "You have already delivered all the vehicles.", NotificationType::Error);
|
|
return;
|
|
}
|
|
|
|
if (GSBD_RandomEvents::Get()->EventData[3].State != eRandomEventState::INACTIVE)
|
|
{
|
|
if (auto coords = GSBD_RandomEvents::Get()->EventData[3].TriggerPosition)
|
|
Self::GetPed().TeleportTo(coords);
|
|
}
|
|
}
|
|
};
|
|
|
|
class DeliverNextExoticExportsVehicle : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (GPBD_Flow::Get()->Entries[Self::GetPlayer().GetId()].TunerCBVDeliveredFlags.Value == 1023)
|
|
{
|
|
Notifications::Show("Exotic Exports", "You have already delivered all the vehicles.", NotificationType::Error);
|
|
return;
|
|
}
|
|
|
|
/*if (!FreemodeDelivery::Get()->DeliveryCooldown1.HasTimePassed(30000))
|
|
{
|
|
Notifications::Show("Exotic Exports", "You have just delivered a vehicle, wait a moment.", NotificationType::Error);
|
|
return;
|
|
}*/
|
|
|
|
auto hash = GetNextExoticExportsVehicleHash();
|
|
static ScriptFunction onFreemodeDeliveryEnd("freemode"_J, ScriptPointer("OnFreemodeDeliveryEnd", "2D 0C 2A 00 00"));
|
|
onFreemodeDeliveryEnd.Call<void>(0, Self::GetPlayer().GetId(), 0, 0, 0, 0, 0, 0, 0, 0, hash, 273);
|
|
//FreemodeDelivery::Get()->DeliveryCooldown1.Reset();
|
|
}
|
|
};
|
|
|
|
class TeleportToDeadDrop : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(36628))
|
|
{
|
|
TeleportToCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::DeadDrop, 0);
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("G's Cache", "G's Cache has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class CollectDeadDrop : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(36628))
|
|
{
|
|
CollectCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::DeadDrop, 0);
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("G's Cache", "G's Cache has already been collected.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class TeleportToStashHouse : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(36657))
|
|
{
|
|
auto blip = HUD::GET_FIRST_BLIP_INFO_ID(static_cast<int>(BlipSprite::RADAR_STASH_HOUSE));
|
|
if (HUD::DOES_BLIP_EXIST(blip))
|
|
{
|
|
if (auto coords = HUD::GET_BLIP_COORDS(blip))
|
|
Self::GetPed().TeleportTo(coords);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Stash House", "Stash House has already been raided.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class EnterStashHouseSafeCode : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (auto thread = Scripts::FindScriptThread("fm_content_stash_house"_J))
|
|
{
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
int combination = *ScriptLocal(thread, 142).At(22).At(i, 2).At(1).As<int*>();
|
|
*ScriptLocal(thread, 142).At(22).At(i, 2).As<float*>() = combination;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class TeleportToStreetDealer : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
static ScriptFunction getStreetDealerCoords("freemode"_J, ScriptPointer("GetStreetDealerCoords", "5D ? ? ? 5D ? ? ? 5D ? ? ? 18").Add(1).Rip());
|
|
if (auto coords = getStreetDealerCoords.Call<Vector3>(FreemodeGeneral::Get()->StreetDealers.Dealers[streetDealerIndex.GetState()].Location))
|
|
Self::GetPed().TeleportTo(coords);
|
|
}
|
|
};
|
|
|
|
class _OpenStreetDealerMenu : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted || Scripts::IsScriptActive("fm_street_dealer"_J))
|
|
return;
|
|
|
|
OpenStreetDealerMenu::SetShouldRunScript(true);
|
|
}
|
|
};
|
|
|
|
class TeleportToLSTag : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(42252 + lsTagIndex.GetState()))
|
|
{
|
|
TeleportToCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::Tagging, lsTagIndex.GetState());
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("LS Tags", "This LS Tag has already been sprayed.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class SprayLSTag : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (!Stats::GetPackedBool(42252 + lsTagIndex.GetState()))
|
|
{
|
|
CollectCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::Tagging, lsTagIndex.GetState());
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("LS Tags", "This LS Tag has already been sprayed.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class TeleportToMadrazoHit : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (Stats::GetInt("MPX_BAIL_OFFICE_OWNED") == 0)
|
|
{
|
|
Notifications::Show("Madrazo Hits", "You must own a Bail Office.", NotificationType::Error);
|
|
return;
|
|
}
|
|
|
|
if (!Stats::GetPackedBool(42269))
|
|
{
|
|
auto blip = HUD::GET_FIRST_BLIP_INFO_ID(static_cast<int>(BlipSprite::RADAR_DAILY_BOUNTY));
|
|
if (HUD::DOES_BLIP_EXIST(blip))
|
|
{
|
|
if (auto coords = HUD::GET_BLIP_COORDS(blip))
|
|
Self::GetPed().TeleportTo(coords);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Notifications::Show("Madrazo Hits", "Madrazo Hit has already been completed.", NotificationType::Error);
|
|
}
|
|
}
|
|
};
|
|
|
|
class TeleportToMadrazoHitTarget : public Command
|
|
{
|
|
using Command::Command;
|
|
|
|
virtual void OnCall() override
|
|
{
|
|
if (!*Pointers.IsSessionStarted)
|
|
return;
|
|
|
|
if (auto thread = Scripts::FindScriptThread("fm_content_daily_bounty"_J))
|
|
{
|
|
if (auto coords = *ScriptLocal(thread, 239).At(434).At(1).At(0, 4).As<Vector3*>())
|
|
{
|
|
Self::GetPed().TeleportTo(coords);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
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<SCR_BITSET<uint64_t>*>()->Set(6);
|
|
*ScriptGlobal(2708543).At(547).As<joaat_t*>() = wildlifePhotographyAnimalHashes[index];
|
|
*ScriptGlobal(2708543).At(548).As<int*>() = *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."};
|
|
|
|
static CompleteAllChallenges _CompleteAllChallenges{"completeallchallenges", "Complete All Challenges", "Completes all the Daily Objectives and the Weekly Challenge."};
|
|
|
|
static TeleportToHiddenCache _TeleportToHiddenCache{"tptohiddencache", "Teleport to Hidden Cache", "Teleports to the selected Hidden Cache."};
|
|
static CollectHiddenCache _CollectHiddenCache{"collecthiddencache", "Collect Hidden Cache", "Collects the selected Hidden Cache."};
|
|
|
|
static TeleportToTreasureChest _TeleportToTreasureChest{"tptotreasurechest", "Teleport to Treasure Chest", "Teleports to the selected Treasure Chest."};
|
|
static CollectTreasureChest _CollectTreasureChest{"collecttreasurechest", "Collect Treasure Chest", "Collects the selected Treasure Chest."};
|
|
static EnableTreasureChestInLS _EnableTreasureChestInLS{"enabletreasurechestinls", "Enable Treasure Chests in LS", "Enables Treasure Chests in Los Santos, so you don't have to go to Cayo Perico."};
|
|
|
|
static TeleportToShipwrecked _TeleportToShipwrecked{"tptoshipwrecked", "Teleport to Shipwreck", "Teleports to Shipwreck."};
|
|
static CollectShipwrecked _CollectShipwrecked{"collectshipwrecked", "Collect Shipwreck", "Collects Shipwreck."};
|
|
|
|
static TeleportToBuriedStash _TeleportToBuriedStash{"tptoburiedstash", "Teleport to Buried Stash", "Teleports to the selected Buried Stash."};
|
|
static CollectBuriedStash _CollectBuriedStash{"collectburiedstash", "Collect Buried Stash", "Collects the selected Buried Stash."};
|
|
static EnableBuriedStashInLS _EnableBuriedStashInLS{"enableburiedstashinls", "Enable Buried Stashes in LS", "Enables Buried Stashes in Los Santos, so you don't have to go to Cayo Perico."};
|
|
|
|
static TeleportToSkydive _TeleportToSkydive{"tptoskydive", "Teleport to Skydive", "Teleports to the selected Skydive."};
|
|
static CompleteSkydive _CompleteSkydive{"completeskydive", "Complete Skydive", "Completes the selected Skydive."};
|
|
|
|
static TeleportToTimeTrial _TeleportToTimeTrial{"tptotimetrial", "Teleport to Time Trial", "Teleports to the selected Time Trial."};
|
|
static BeatTimeTrial _BeatTimeTrial{"beattimetrial", "Beat Time Trial", "Completes the selected Time Trial."};
|
|
|
|
static TeleportToExoticExportsVehicle _TeleportToExoticExportsVehicle{"tptoexoticexportsvehicle", "Teleport to Exotic Exports Vehicle", "Teleports to the next Exotic Exports vehicle."};
|
|
static DeliverNextExoticExportsVehicle _DeliverNextExoticExportsVehicle{"delivernextexoticexportsvehicle", "Deliver Next Exotic Exports Vehicle", "Directly delivers the next Exotic Exports vehicle."};
|
|
|
|
static TeleportToDeadDrop _TeleportToDeadDrop{"tptodeaddrop", "Teleport to G's Cache", "Teleports to G's Cache."};
|
|
static CollectDeadDrop _CollectDeadDrop{"collectdeaddrop", "Collect G's Cache", "Collects G's Cache."};
|
|
|
|
static TeleportToStashHouse _TeleportToStashHouse{"tptostashhouse", "Teleport to Stash House", "Teleports to Stash House."};
|
|
static EnterStashHouseSafeCode _EnterStashHouseSafeCode{"enterstashhousesafecode", "Enter Stash House Safe Code", "Enters the Stash House safe code."};
|
|
|
|
static TeleportToStreetDealer _TeleportToStreetDealer{"tptostreetdealer", "Teleport to Dealer", "Teleports to the selected Street Dealer."};
|
|
static _OpenStreetDealerMenu __OpenStreetDealerMenu{"openstreetdealermenu", "Open Street Dealer Menu", "Allows you to access the selected Street Dealer remotely."};
|
|
|
|
static TeleportToLSTag _TeleportToLSTag{"tptolstag", "Teleport to LS Tag", "Teleports to the selected LS Tag."};
|
|
static SprayLSTag _SprayLSTag{"spraylstag", "Spray LS Tag", "Sprays the selected LS Tag."};
|
|
|
|
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."};
|
|
} |