From f34325739fffa2c869e9d55b40a2fc0418f8a0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taylan=20Utku=20Y=C4=B1lmaz?= <86522690+tuyilmaz@users.noreply.github.com> Date: Thu, 25 Jul 2024 00:27:01 +0300 Subject: [PATCH] TP to Madam Nazar and Moonshine Shack, Recovery options, and ScriptFunction Static Call (#174) --- src/game/features/self/TpToMadamNazar.cpp | 22 +++++ src/game/features/self/TpToMoonshineShack.cpp | 34 ++++++++ src/game/frontend/submenus/Recovery.cpp | 26 +++++- src/game/frontend/submenus/Teleport.cpp | 2 + src/game/rdr/ScriptFunction.cpp | 61 ++++++++++---- src/game/rdr/ScriptFunction.hpp | 29 ++++++- src/util/Rewards.cpp | 84 +++++++++++++++++++ src/util/Rewards.hpp | 34 ++++++++ 8 files changed, 274 insertions(+), 18 deletions(-) create mode 100644 src/game/features/self/TpToMadamNazar.cpp create mode 100644 src/game/features/self/TpToMoonshineShack.cpp diff --git a/src/game/features/self/TpToMadamNazar.cpp b/src/game/features/self/TpToMadamNazar.cpp new file mode 100644 index 00000000..074f25af --- /dev/null +++ b/src/game/features/self/TpToMadamNazar.cpp @@ -0,0 +1,22 @@ +#include "core/commands/Command.hpp" +#include "game/backend/Self.hpp" +#include "game/rdr/ScriptGlobal.hpp" +#include "util/teleport.hpp" + +namespace YimMenu::Features +{ + + class TpToNazar : public Command + { + using Command::Command; + static constexpr auto NazarLocation = ScriptGlobal(1051832).At(4681); + + virtual void OnCall() override + { + if (NazarLocation.CanAccess()) + YimMenu::Teleport::TeleportEntity(Self::GetPed().GetHandle(), *NazarLocation.As(), true); + } + }; + + static TpToNazar _TpToNazar{"tptonazar", "Teleport To Madam Nazar", "Teleport to Madam Nazar's current location"}; +} \ No newline at end of file diff --git a/src/game/features/self/TpToMoonshineShack.cpp b/src/game/features/self/TpToMoonshineShack.cpp new file mode 100644 index 00000000..0d863bb6 --- /dev/null +++ b/src/game/features/self/TpToMoonshineShack.cpp @@ -0,0 +1,34 @@ +#include "core/commands/Command.hpp" +#include "core/frontend/Notifications.hpp" +#include "game/backend/Self.hpp" +#include "game/rdr/ScriptGlobal.hpp" +#include "util/teleport.hpp" + +namespace YimMenu::Features +{ + + class TpToMoonshineShack : public Command + { + using Command::Command; + static constexpr auto ShackBlip = ScriptGlobal(1297441).At(128); + + virtual void OnCall() override + { + if (ShackBlip.CanAccess()) + { + Blip Shack = *ShackBlip.As(); + if (MAP::DOES_BLIP_EXIST(Shack)) + { + Vector3 ShackCoords = MAP::GET_BLIP_COORDS(Shack); + YimMenu::Teleport::TeleportEntity(Self::GetPed().GetHandle(), ShackCoords, true); + } + else + { + Notifications::Show("Moonshine Shack", "Unable to find moonshine shack blip", NotificationType::Error); + } + } + } + }; + + static TpToMoonshineShack _TpToMoonshineShack{"tptomoonshineshack", "Teleport To Moonshine Shack", "Teleport to your moonshine shack"}; +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Recovery.cpp b/src/game/frontend/submenus/Recovery.cpp index 021a45ac..ec8fadee 100644 --- a/src/game/frontend/submenus/Recovery.cpp +++ b/src/game/frontend/submenus/Recovery.cpp @@ -20,7 +20,31 @@ namespace YimMenu::Submenus if (recoveryCommand->GetState()) { static Rewards::eRewardType selected{}; - std::map reward_translations = {{Rewards::eRewardType::HEIRLOOMS, "Heirlooms"}, {Rewards::eRewardType::COINS, "Coins"}, {Rewards::eRewardType::ALCBOTTLES, "Alcohol Bottles"}, {Rewards::eRewardType::ARROWHEADS, "Arrowheads"}, {Rewards::eRewardType::BRACELETS, "Bracelets"}, {Rewards::eRewardType::EARRINGS, "Earrings"}, {Rewards::eRewardType::NECKLACES, "Necklaces"}, {Rewards::eRewardType::RINGS, "Rings"}, {Rewards::eRewardType::TAROTCARDS_CUPS, "Tarot Cards - Cups"}, {Rewards::eRewardType::TAROTCARDS_PENTACLES, "Tarot Cards - Pentacles"}, {Rewards::eRewardType::TAROTCARDS_SWORDS, "Tarot Cards - Swords"}, {Rewards::eRewardType::TAROTCARDS_WANDS, "Tarot Cards - Wands"}}; + std::map reward_translations = { + {Rewards::eRewardType::HEIRLOOMS, "Heirlooms"}, + {Rewards::eRewardType::COINS, "Coins"}, + {Rewards::eRewardType::ALCBOTTLES, "Alcohol Bottles"}, + {Rewards::eRewardType::ARROWHEADS, "Arrowheads"}, + {Rewards::eRewardType::BRACELETS, "Bracelets"}, + {Rewards::eRewardType::EARRINGS, "Earrings"}, + {Rewards::eRewardType::NECKLACES, "Necklaces"}, + {Rewards::eRewardType::RINGS, "Rings"}, + {Rewards::eRewardType::TAROTCARDS_CUPS, "Tarot Cards - Cups"}, + {Rewards::eRewardType::TAROTCARDS_PENTACLES, "Tarot Cards - Pentacles"}, + {Rewards::eRewardType::TAROTCARDS_SWORDS, "Tarot Cards - Swords"}, + {Rewards::eRewardType::TAROTCARDS_WANDS, "Tarot Cards - Wands"}, + {Rewards::eRewardType::FOSSILS, "Fossils"}, + {Rewards::eRewardType::EGGS, "Eggs"}, + {Rewards::eRewardType::HERBS, "Herbs"}, + {Rewards::eRewardType::TREASURE, "Treasure Reward"}, + {Rewards::eRewardType::CAPITALE, "Capitale"}, + {Rewards::eRewardType::XP, "25K XP"}, + {Rewards::eRewardType::MOONSHINERXP, "200 Moonshiner XP"}, + {Rewards::eRewardType::TRADERXP, "200 Trader XP"}, + {Rewards::eRewardType::COLLECTORXP, "200 Collector XP"}, + {Rewards::eRewardType::NATURALISTXP, "300 Naturalist XP"}, + {Rewards::eRewardType::BOUNTYHUNTERXP, "200 Bounty Hunter XP"}, + }; if (ImGui::BeginCombo("Rewards", reward_translations[selected].c_str())) { diff --git a/src/game/frontend/submenus/Teleport.cpp b/src/game/frontend/submenus/Teleport.cpp index 5dae41cc..ad20df7e 100644 --- a/src/game/frontend/submenus/Teleport.cpp +++ b/src/game/frontend/submenus/Teleport.cpp @@ -192,6 +192,8 @@ namespace YimMenu::Submenus miscGroup->AddItem(std::make_shared("tptowaypoint"_J)); miscGroup->AddItem(std::make_shared("tptomount"_J)); miscGroup->AddItem(std::make_shared("tptotraintrack"_J)); + miscGroup->AddItem(std::make_shared("tptomoonshineshack"_J)); + miscGroup->AddItem(std::make_shared("tptonazar"_J)); main->AddItem(miscGroup); diff --git a/src/game/rdr/ScriptFunction.cpp b/src/game/rdr/ScriptFunction.cpp index 3da044a8..4db13f04 100644 --- a/src/game/rdr/ScriptFunction.cpp +++ b/src/game/rdr/ScriptFunction.cpp @@ -61,32 +61,63 @@ namespace YimMenu { } - void ScriptFunction::Call(const std::vector& args) + void ScriptFunction::RunScript(rage::scrThread* thread, rage::scrProgram* program, const std::vector& args) { - auto pc = GetPC(); - auto thread = Scripts::FindScriptThread(m_Hash); - auto program = Scripts::FindScriptProgram(m_Hash); - const auto globals_initialized = std::vector(50, true); // std::vector does weird stuff so we aren't using that - if (!pc || !thread || !program) - return; - - auto old_thread = *Pointers.CurrentScriptThread; + auto old_thread = *Pointers.CurrentScriptThread; auto old_thread_running = rage::tlsContext::Get()->m_RunningScript; - auto stack = reinterpret_cast(thread->m_Stack); - auto context = thread->m_Context; + auto stack = reinterpret_cast(thread->m_Stack); + auto context = thread->m_Context; for (auto& arg : args) stack[context.m_StackPointer++] = arg; stack[context.m_StackPointer++] = 0; // return address - context.m_ProgramCounter = pc.value(); - context.m_State = rage::eThreadState::idle; + context.m_ProgramCounter = m_Pc.value(); + context.m_State = rage::eThreadState::idle; - Pointers.ScriptVM(stack, Pointers.ScriptGlobals, reinterpret_cast(const_cast(globals_initialized.data())), program, &context); + Pointers.ScriptVM(stack, + Pointers.ScriptGlobals, + reinterpret_cast(const_cast(globals_initialized.data())), + program, + &context); rage::tlsContext::Get()->m_RunningScript = old_thread_running; - *Pointers.CurrentScriptThread = old_thread; + *Pointers.CurrentScriptThread = old_thread; + } + + void ScriptFunction::StaticCall(const std::vector& args) + { + auto pc = GetPC(); + auto program = Scripts::FindScriptProgram(m_Hash); + + if (!pc || !program) + return; + + rage::scrThread* thread = (rage::scrThread*)new uint8_t[sizeof(rage::scrThread)]; + memcpy(thread, *Pointers.CurrentScriptThread, sizeof(rage::scrThread)); + + void* stack = new uint64_t[25000]; + thread->m_Stack = stack; + thread->m_Context.m_StackSize = 25000; + thread->m_Context.m_StackPointer = 1; + + RunScript(thread, program, args); + + delete[] stack; + delete[] (uint8_t*)thread; + } + + void ScriptFunction::Call(const std::vector& args) + { + auto pc = GetPC(); + auto thread = Scripts::FindScriptThread(m_Hash); + auto program = Scripts::FindScriptProgram(m_Hash); + + if (!pc || !thread || !program) + return; + + RunScript(thread, program, args); } } \ No newline at end of file diff --git a/src/game/rdr/ScriptFunction.hpp b/src/game/rdr/ScriptFunction.hpp index 29f5c585..34af46ee 100644 --- a/src/game/rdr/ScriptFunction.hpp +++ b/src/game/rdr/ScriptFunction.hpp @@ -2,21 +2,40 @@ #include "core/memory/Pattern.hpp" #include "util/Joaat.hpp" +namespace rage +{ + class scrThread; + class scrProgram; +} + namespace YimMenu { class ScriptFunction { + private: joaat_t m_Hash; SimplePattern m_Pattern; std::optional m_Pc; std::optional GetPC(); + + void RunScript(rage::scrThread* thread, rage::scrProgram* program, const std::vector& args); + + void Call(const std::vector& args); + + void StaticCall(const std::vector& args); public: ScriptFunction(joaat_t hash, SimplePattern pattern); - // TODO: add static_call and return value support + // TODO: return value support - void Call(const std::vector& args); + template + void StaticCall(Args... args) + { + std::vector params; + (params.push_back(static_cast(args)), ...); + StaticCall(params); + } template void operator()(Args... args) @@ -26,4 +45,10 @@ namespace YimMenu Call(params); } }; + + namespace ScriptFunctions + { + static inline ScriptFunction GiveItemDatabaseAward("interactive_campfire"_J, "22 05 24"); + static inline ScriptFunction GiveLootTableAward("interactive_campfire"_J, "22 02 14 00 00 2F"); + } } \ No newline at end of file diff --git a/src/util/Rewards.cpp b/src/util/Rewards.cpp index b9bc7f00..e827f3df 100644 --- a/src/util/Rewards.cpp +++ b/src/util/Rewards.cpp @@ -1,5 +1,8 @@ #include "Rewards.hpp" +#include "game/backend/ScriptMgr.hpp" #include "game/pointers/Pointers.hpp" +#include "game/rdr/Natives.hpp" +#include "game/rdr/ScriptFunction.hpp" #include #include @@ -12,6 +15,21 @@ namespace YimMenu::Rewards (*Pointers.EventGroupNetwork)->AddEvent(&event, false); }; + void GiveScriptReward(const RewardInfo& info, bool loottable) + { + if (!SCRIPTS::HAS_SCRIPT_WITH_NAME_HASH_LOADED("interactive_campfire"_J)) + { + SCRIPTS::REQUEST_SCRIPT_WITH_NAME_HASH("interactive_campfire"_J); + for (int i = 0; i < 150 && !SCRIPTS::HAS_SCRIPT_WITH_NAME_HASH_LOADED("interactive_campfire"_J); i++) + ScriptMgr::Yield(10ms); + } + + if (loottable) + ScriptFunctions::GiveLootTableAward.StaticCall(info.reward_hash, 0); + else + ScriptFunctions::GiveItemDatabaseAward.StaticCall(info.reward_hash, false, 255, 0, false); + } + void GiveRequestedRewards(std::vector rewards) { for (auto& reward : rewards) @@ -97,6 +115,72 @@ namespace YimMenu::Rewards GiveReward(card); } break; + case eRewardType::FOSSILS: + for (const auto& fossil : Fossils) + { + GiveScriptReward(fossil); + } + break; + case eRewardType::EGGS: + for (const auto& egg : Eggs) + { + GiveScriptReward(egg); + } + break; + case eRewardType::HERBS: + for (const auto& herb : Herbs) + { + GiveScriptReward(herb); + } + break; + case eRewardType::TREASURE: + for (const auto& treasure : TreasureReward) + { + GiveScriptReward(treasure); + } + break; + case eRewardType::CAPITALE: + for (const auto& capitale : CapitaleReward) + { + GiveScriptReward(capitale); + } + break; + case eRewardType::XP: + for (const auto& xp : RegularXP) + { + GiveScriptReward(xp, false); + } + break; + case eRewardType::MOONSHINERXP: + for (const auto& xp : MoonshinerXP) + { + GiveScriptReward(xp, false); + } + break; + case eRewardType::TRADERXP: + for (const auto& xp : TraderXP) + { + GiveScriptReward(xp, false); + } + break; + case eRewardType::COLLECTORXP: + for (const auto& xp : CollectorXP) + { + GiveScriptReward(xp, false); + } + break; + case eRewardType::NATURALISTXP: + for (const auto& xp : NaturalistXP) + { + GiveScriptReward(xp, false); + } + break; + case eRewardType::BOUNTYHUNTERXP: + for (const auto& xp : BountyHunterXP) + { + GiveScriptReward(xp, false); + } + break; } } }; diff --git a/src/util/Rewards.hpp b/src/util/Rewards.hpp index ddc4adfb..b2c3153f 100644 --- a/src/util/Rewards.hpp +++ b/src/util/Rewards.hpp @@ -38,6 +38,28 @@ namespace YimMenu::Rewards inline const RewardInfo TarotCards_Wands[]{{"MP005_S_CARDT_2W"_J, "DOCUMENT_CARD_TWO_WANDS"_J, "2 of Wands"}, {"MP005_S_CARDT_3W"_J, "DOCUMENT_CARD_THREE_WANDS"_J, "3 of Wands"}, {"MP005_S_CARDT_4W"_J, "DOCUMENT_CARD_FOUR_WANDS"_J, "4 of Wands"}, {"MP005_S_CARDT_5W"_J, "DOCUMENT_CARD_FIVE_WANDS"_J, "5 of Wands"}, {"MP005_S_CARDT_6W"_J, "DOCUMENT_CARD_SIX_WANDS"_J, "6 of Wands"}, {"MP005_S_CARDT_7W"_J, "DOCUMENT_CARD_SEVEN_WANDS"_J, "7 of Wands"}, {"MP005_S_CARDT_8W"_J, "DOCUMENT_CARD_EIGHT_WANDS"_J, "8 of Wands"}, {"MP005_S_CARDT_9W"_J, "DOCUMENT_CARD_NINE_WANDS"_J, "9 of Wands"}, {"MP005_S_CARDT_10W"_J, "DOCUMENT_CARD_TEN_WANDS"_J, "10 of Wands"}, {"MP005_S_CARDT_ACW"_J, "DOCUMENT_CARD_ACE_WANDS"_J, "Ace of Wands"}, {"MP005_S_CARDT_KIW"_J, "DOCUMENT_CARD_KING_WANDS"_J, "King of Wands"}, {"MP005_S_CARDT_KNW"_J, "DOCUMENT_CARD_KNIGHT_WANDS"_J, "Knight of Wands"}, {"MP005_S_CARDT_PAW"_J, "DOCUMENT_CARD_PAGE_WANDS"_J, "Page of Wands"}, {"MP005_S_CARDT_QUW"_J, "DOCUMENT_CARD_QUEEN_WANDS"_J, "Queen of Wands"}}; + inline const RewardInfo Fossils[]{{0, "COLLECTIBLE_FOSSIL_CORAL"_J, ""}, {0, "COLLECTIBLE_FOSSIL_BIVALVE"_J, ""}, {0, "COLLECTIBLE_FOSSIL_BRACHIOPOD"_J, ""}, {0, "COLLECTIBLE_FOSSIL_BROW_HORN"_J, ""}, {0, "COLLECTIBLE_FOSSIL_CEPHALOPOD"_J, ""}, {0, "COLLECTIBLE_FOSSIL_TRILOBITE_CRYPTO"_J, ""}, {0, "COLLECTIBLE_FOSSIL_TRILOBITE_ISO"_J, ""}, {0, "COLLECTIBLE_FOSSIL_TOOTH_MEGA"_J, ""}, {0, "COLLECTIBLE_FOSSIL_NEO"_J, ""}, {0, "COLLECTIBLE_FOSSIL_STONE"_J, ""}, {0, "COLLECTIBLE_FOSSIL_PET_WOOD"_J, ""}, {0, "COLLECTIBLE_FOSSIL_PET_WOOD_RAINBOW"_J, ""}, {0, "COLLECTIBLE_FOSSIL_SEA_LILY"_J, ""}, {0, "COLLECTIBLE_FOSSIL_SEA_SCORPION"_J, ""}, {0, "COLLECTIBLE_FOSSIL_TOOTH_SERRATED"_J, ""}, {0, "COLLECTIBLE_FOSSIL_TAIL_SPIKE"_J, ""}, {0, "COLLECTIBLE_FOSSIL_TOE_CLAW"_J, ""}, {0, "COLLECTIBLE_FOSSIL_TOOTH_TREX"_J, ""}, {0, "COLLECTIBLE_FOSSIL_TULLY_MONSTER"_J, ""}, {0, "COLLECTIBLE_FOSSIL_PET_WOOD_YELLOW"_J, ""}}; + + inline const RewardInfo Eggs[]{{0, "COLLECTIBLE_EGG_VULTURE"_J, ""}, {0, "COLLECTIBLE_EGG_LOON"_J, ""}, {0, "COLLECTIBLE_EGG_DUCK"_J, ""}, {0, "COLLECTIBLE_EGG_GOOSE"_J, ""}, {0, "COLLECTIBLE_EGG_HERON"_J, ""}, {0, "COLLECTIBLE_EGG_EGRET"_J, ""}, {0, "COLLECTIBLE_EGG_EAGLE"_J, ""}, {0, "COLLECTIBLE_EGG_HAWK"_J, ""}, {0, "COLLECTIBLE_EGG_SPOONBILL"_J, ""}, {0, "COLLECTIBLE_EGG_CONDOR"_J, ""}}; + + inline const RewardInfo Herbs[]{{0, "HERB_LOOT_ALASKAN_GINSENG"_J, "Alaskan Ginseng"}, {0, "HERB_LOOT_AMERICAN_GINSENG"_J, "American Ginseng"}, {0, "HERB_LOOT_BAY_BOLETE"_J, "Bay Bolete"}, {0, "HERB_LOOT_BLACK_BERRY"_J, "Black Berry"}, {0, "HERB_LOOT_BLACK_CURRANT"_J, "Black Currant"}, {0, "HERB_LOOT_BURDOCK_ROOT"_J, "Burdock Root"}, {0, "HERB_LOOT_CHANTERELLES"_J, "Chanterelles"}, {0, "HERB_LOOT_COMMON_BULRUSH"_J, "Common Bulrush"}, {0, "HERB_LOOT_CREEPING_THYME"_J, "Creeping Thyme"}, {0, "HERB_LOOT_DESERT_SAGE"_J, "Desert Sage"}, {0, "HERB_LOOT_ENGLISH_MACE"_J, "English Mace"}, {0, "HERB_LOOT_EVERGREEN_HUCKLEBERRY"_J, "Evergreen Huckleberry"}, {0, "HERB_LOOT_GOLDEN_CURRANT"_J, "Golden Currant"}, {0, "HERB_LOOT_HUMMINGBIRD_SAGE"_J, "Hummingbird Sage"}, {0, "HERB_LOOT_INDIAN_TOBACCO"_J, "Indian Tobacco"}, {0, "HERB_LOOT_MILKWEED"_J, "Milkweed"}, {0, "HERB_LOOT_OLEANDER_SAGE"_J, "Oleander Sage"}, {0, "HERB_LOOT_OREGANO"_J, "Oregano"}, {0, "HERB_LOOT_PARASOL_MUSHROOM"_J, "Parasol Mushroom"}, {0, "HERB_LOOT_PRAIRIE_POPPY"_J, "Prairie Poppy"}, {0, "HERB_LOOT_RAMS_HEAD"_J, "Rams Head"}, {0, "HERB_LOOT_RED_RASPBERRY"_J, "Red Raspberry"}, {0, "HERB_LOOT_RED_SAGE"_J, "Red Sage"}, {0, "HERB_LOOT_VANILLA_FLOWER"_J, "Vanilla Flower"}, {0, "HERB_LOOT_VIOLET_SNOWDROP"_J, "Violet Snowdrop"}, {0, "HERB_LOOT_WILD_CARROTS"_J, "Wild Carrots"}, {0, "HERB_LOOT_WILD_FEVERFEW"_J, "Wild Feverfew"}, {0, "HERB_LOOT_WILD_MINT"_J, "Wild Mint"}, {0, "HERB_LOOT_WINTERGREEN_BERRY"_J, "Wintergreen Berry"}, {0, "HERB_LOOT_YARROW"_J, "Yarrow"}, {0, "HERB_LOOT_AGARITA"_J, "Agarita"}, {0, "HERB_LOOT_BITTERWEED"_J, "Bitterweed"}, {0, "HERB_LOOT_BLUE_BONNET"_J, "Blue Bonnet"}, {0, "HERB_LOOT_BLOOD_FLOWER"_J, "Blood Flower"}, {0, "HERB_LOOT_CARDINAL_FLOWER"_J, "Cardinal Flower"}, {0, "HERB_LOOT_CHOCOLATE_DAISY"_J, "Chocolate Daisy"}, {0, "HERB_LOOT_CREEK_PLUM"_J, "Creek Plum"}, {0, "HERB_LOOT_RHUBARB"_J, "Rhubarb"}, {0, "HERB_LOOT_WISTERIA"_J, "Wisteria"}, {0, "HERB_LOOT_HARRIETUM"_J, "Harrietum"}}; + + inline const RewardInfo TreasureReward[]{{0, "REWARD_ABANDONED_LOOT_MALE"_J, "Treasure Reward"}}; + + inline const RewardInfo CapitaleReward[]{{0, "REWARD_CAPITALE_CHEST_LARGE"_J, "Capitale Reward"}}; + + inline const RewardInfo RegularXP[]{{0, "AWARD_XP_MISSION_COMPLETE_REWARD_XXL"_J, "25K XP Reward"}}; + + inline const RewardInfo MoonshinerXP[]{{0, 0xCBA92A55, "Moonshine XP Reward"}}; + + inline const RewardInfo TraderXP[]{{0, 0xB11109AB, "Trader XP Reward"}}; + + inline const RewardInfo CollectorXP[]{{0, 0xA64F15F2, "Collector XP Reward"}}; + + inline const RewardInfo NaturalistXP[]{{0, 0x05839847, "Naturalist XP Reward"}}; + + inline const RewardInfo BountyHunterXP[]{{0, 0x5424CC1A, "Bounty Hunter XP Reward"}}; + enum class eRewardType : int { // GOLD_REWARDS, @@ -53,8 +75,20 @@ namespace YimMenu::Rewards TAROTCARDS_PENTACLES, TAROTCARDS_SWORDS, TAROTCARDS_WANDS, + FOSSILS, + EGGS, + HERBS, + TREASURE, + CAPITALE, + XP, + MOONSHINERXP, + TRADERXP, + COLLECTORXP, + NATURALISTXP, + BOUNTYHUNTERXP, }; void GiveRequestedRewards(std::vector rewards); void GiveReward(const RewardInfo& info); + void GiveScriptReward(const RewardInfo& info, bool loottable = true); } \ No newline at end of file