AutoItem: replaces Achievement.cpp with autocraft and autorent system. (#1119)

AutoItem.cpp: Add - auto craft and auto tent for items
This commit is contained in:
aUniqueUser 2020-10-10 14:24:16 -04:00 committed by GitHub
parent 21a23d732d
commit b9daa94a73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 697 additions and 584 deletions

View File

@ -1,7 +1,7 @@
<Tab name="Misc"> <Tab name="Misc">
<TabContainer width="fill" height="fill"> <TabContainer width="fill" height="fill">
<Include path="nullifiedcat/misc/anticheat.xml"/> <Include path="nullifiedcat/misc/anticheat.xml"/>
<Include path="nullifiedcat/misc/autoequip.xml"/> <Include path="nullifiedcat/misc/autoitem.xml"/>
<Include path="nullifiedcat/misc/buildings.xml"/> <Include path="nullifiedcat/misc/buildings.xml"/>
<Include path="nullifiedcat/misc/spyalert.xml"/> <Include path="nullifiedcat/misc/spyalert.xml"/>
<Include path="nullifiedcat/misc/collective.xml"/> <Include path="nullifiedcat/misc/collective.xml"/>

View File

@ -1,57 +0,0 @@
<Tab name="Auto Equip" padding="4 4 4 4">
<Box name="Auto equip" width="content" height="content" padding="12 6 6 6">
<List width="200">
<LabeledObject width="fill" label="Autoequip primary">
<Select target="achievement.equip-primary">
<Option name="None" value="0"/>
<Option name="Force-A-Nature" value="1"/>
<Option name="Backburner" value="2"/>
<Option name="Natascha" value="3"/>
<Option name="Frontier Justice" value="4"/>
<Option name="Blutsauger" value="5"/>
<Option name="Huntsman" value="6"/>
<Option name="Ambassador" value="7"/>
<Option name="Direct Hit" value="8"/>
<Option name="Stock" value="100"/>
</Select>
</LabeledObject>
<LabeledObject width="fill" label="Autoequip secondary">
<Select target="achievement.equip-secondary">
<Option name="None" value="0"/>
<Option name="Bonk! Atomic punch" value="1"/>
<Option name="Buff Banner" value="2"/>
<Option name="Flare Gun" value="3"/>
<Option name="Chargin' Targe" value="4"/>
<Option name="Scottish Resistance" value="5"/>
<Option name="Sandvich" value="6"/>
<Option name="Wrangler" value="7"/>
<Option name="Kritzkrieg" value="8"/>
<Option name="Jarate" value="9"/>
<Option name="Razorback" value="10"/>
<Option name="Stock" value="100"/>
</Select>
</LabeledObject>
<LabeledObject width="fill" label="Autoequip melee">
<Select target="achievement.equip-melee">
<Option name="None" value="0"/>
<Option name="Sandman" value="1"/>
<Option name="Equalizer" value="2"/>
<Option name="Axtinguisher" value="3"/>
<Option name="Eyelander" value="4"/>
<Option name="Killing Gloves Of Boxing" value="5"/>
<Option name="Gunslinger" value="6"/>
<Option name="Ubersaw" value="7"/>
<Option name="Stock" value="100"/>
</Select>
</LabeledObject>
<LabeledObject width="fill" label="Autoequip PDA2">
<Select target="achievement.equip-pda2">
<Option name="None" value="0"/>
<Option name="Cloak and Dagger" value="1"/>
<Option name="Deadringer" value="2"/>
<Option name="Stock" value="100"/>
</Select>
</LabeledObject>
</List>
</Box>
</Tab>

View File

@ -0,0 +1,16 @@
<Tab name="Auto Item" padding="4 4 4 4">
<Box name="Auto item" width="content" height="content" padding="12 6 6 6">
<List width="200">
<AutoVariable width="fill" target="auto-item.enable" label="Enable auto item"/>
<AutoVariable width="fill" target="auto-item.time" label="Equip timer"/>
<AutoVariable width="fill" target="auto-item.weapons" label="Weapons"/>
<AutoVariable width="fill" target="auto-item.weapons.primary" label="Primary"/>
<AutoVariable width="fill" target="auto-item.weapons.secondary" label="Secondary/Cloak"/>
<AutoVariable width="fill" target="auto-item.weapons.melee" label="Melee"/>
<AutoVariable width="fill" target="auto-item.hats" label="Hats"/>
<AutoVariable width="fill" target="auto-item.hats.1" label="Hat 1"/>
<AutoVariable width="fill" target="auto-item.hats.2" label="Hat 2"/>
<AutoVariable width="fill" target="auto-item.hats.3" label="Hat 3"/>
</List>
</Box>
</Tab>

View File

@ -1,15 +0,0 @@
/*
* Achievement.h
*
* Created on: Jan 20, 2017
* Author: nullifiedcat
*/
#pragma once
namespace hacks::tf2::achievement
{
void Lock();
void Unlock();
} // namespace hacks::tf2::achievement

View File

@ -24,7 +24,6 @@
#include "FollowBot.hpp" #include "FollowBot.hpp"
#include "Trigger.hpp" #include "Trigger.hpp"
#include "UberSpam.hpp" #include "UberSpam.hpp"
#include "Achievement.hpp"
#include "Spam.hpp" #include "Spam.hpp"
#include "NavBot.hpp" #include "NavBot.hpp"
#include "FollowBot.hpp" #include "FollowBot.hpp"

View File

@ -1,5 +1,5 @@
/* /*
* CTFInventoryManager.cpp * CTFInventoryManager.hpp
* *
* Created on: Apr 26, 2018 * Created on: Apr 26, 2018
* Author: bencat07 * Author: bencat07
@ -19,6 +19,7 @@ public:
class CEconItemView class CEconItemView
{ {
public: public:
int GetDefinitionIndex();
unsigned long long UUID(); unsigned long long UUID();
}; };
@ -28,6 +29,9 @@ public:
CTFPlayerInventory() = delete; CTFPlayerInventory() = delete;
public: public:
int GetItemCount();
CEconItemView *GetItem(int idx);
std::vector<unsigned long long> GetItemsOfItemDef(int id);
CEconItemView *GetFirstItemOfItemDef(int id); CEconItemView *GetFirstItemOfItemDef(int id);
}; };
@ -42,3 +46,6 @@ public:
bool EquipItemInLoadout(int, int, unsigned long long); bool EquipItemInLoadout(int, int, unsigned long long);
}; };
} // namespace re } // namespace re
extern bool Craft(std::vector<int> item_ids);
extern bool Rent(int item_id);

View File

@ -1,507 +0,0 @@
/*
* Achievement.cpp
*
* Created on: Jan 20, 2017
* Author: nullifiedcat
*/
#include <settings/Bool.hpp>
#include "Misc.hpp"
#include "common.hpp"
namespace hacks::tf2::achievement
{
static settings::Boolean safety{ "achievement.safety", "true" };
struct Autoequip_unlock_list
{
std::string name;
int achievement_id;
int item_id;
tf_class player_class;
int slot;
Autoequip_unlock_list(std::string _name, int _ach_id, int _item_id, tf_class _player_class, int _slot)
{
name = _name;
achievement_id = _ach_id;
item_id = _item_id;
player_class = _player_class;
slot = _slot;
}
};
// Keep track of possible achievement items
static std::vector<Autoequip_unlock_list> primary, secondary, melee, pda2;
// Keep track of stock items
static std::array<bool, 4> equip_stock;
static settings::Int equip_primary{ "achievement.equip-primary", "0" };
static settings::Int equip_secondary{ "achievement.equip-secondary", "0" };
static settings::Int equip_melee{ "achievement.equip-melee", "0" };
static settings::Int equip_pda2{ "achievement.equip-pda2", "0" };
#if ENABLE_TEXTMODE
static settings::Boolean auto_noisemaker{ "misc.auto-noisemaker", "true" };
#else
static settings::Boolean auto_noisemaker{ "misc.auto-noisemaker", "false" };
#endif
bool checkachmngr()
{
if (!g_IAchievementMgr)
{
g_IAchievementMgr = g_IEngine->GetAchievementMgr();
if (!g_IAchievementMgr)
return false;
}
return true;
}
void Lock()
{
if (!checkachmngr())
return;
if (safety)
{
ConColorMsg({ 255, 0, 0, 255 }, "Switch `cat set achievement.safety` to false before using any achievement commands!\n");
return;
}
g_ISteamUserStats->RequestCurrentStats();
for (int i = 0; i < g_IAchievementMgr->GetAchievementCount(); i++)
{
g_ISteamUserStats->ClearAchievement(g_IAchievementMgr->GetAchievementByIndex(i)->GetName());
}
g_ISteamUserStats->StoreStats();
g_ISteamUserStats->RequestCurrentStats();
}
void Unlock()
{
if (!checkachmngr())
return;
/*auto Invmng = re::CTFInventoryManager::GTFInventoryManager();
auto Inv = re::CTFPlayerInventory::GTFPlayerInventory();
auto Item = Inv->GetFirstItemOfItemDef(59);
Invmng->EquipItemInLoadout(0, 0, (unsigned long long
int)Item->uniqueid());*/
if (safety)
{
ConColorMsg({ 255, 0, 0, 255 }, "Switch `cat set achievement.safety` to false before using any achievement commands!\n");
return;
}
for (int i = 0; i < g_IAchievementMgr->GetAchievementCount(); i++)
{
g_IAchievementMgr->AwardAchievement(g_IAchievementMgr->GetAchievementByIndex(i)->GetAchievementID());
}
}
CatCommand dump_achievement("achievement_dump", "Dump achievements to file (development)", []() {
if (!checkachmngr())
return;
std::ofstream out("/tmp/cathook_achievements.txt", std::ios::out);
if (out.bad())
return;
for (int i = 0; i < g_IAchievementMgr->GetAchievementCount(); i++)
{
out << '[' << i << "] " << g_IAchievementMgr->GetAchievementByIndex(i)->GetName() << ' ' << g_IAchievementMgr->GetAchievementByIndex(i)->GetAchievementID() << "\n";
}
out.close();
});
CatCommand unlock_single("achievement_unlock_single", "Unlocks single achievement by ID", [](const CCommand &args) {
if (!checkachmngr())
return;
char *out = nullptr;
int id = strtol(args.Arg(1), &out, 10);
if (out == args.Arg(1))
{
logging::Info("NaN achievement ID!");
return;
}
IAchievement *ach = reinterpret_cast<IAchievement *>(g_IAchievementMgr->GetAchievementByID(id));
if (ach)
{
g_IAchievementMgr->AwardAchievement(id);
}
});
// For some reason it SEGV's when I try to GetAchievementByID();
CatCommand lock_single("achievement_lock_single", "Locks single achievement by INDEX!", [](const CCommand &args) {
if (!checkachmngr())
return;
if (args.ArgC() < 2)
{
logging::Info("Actually provide an index");
return;
}
int id;
try
{
id = std::stoi(args.Arg(1));
}
catch (std::invalid_argument)
{
logging::Info("NaN achievement ID!");
return;
}
IAchievement *ach = reinterpret_cast<IAchievement *>(g_IAchievementMgr->GetAchievementByID(id));
int index = -1;
if (ach)
for (int i = 0; i < g_IAchievementMgr->GetAchievementCount(); i++)
{
auto ach2 = g_IAchievementMgr->GetAchievementByIndex(i);
if (ach2->GetAchievementID() == id)
{
index = i;
break;
}
}
if (ach && index != -1)
{
g_ISteamUserStats->RequestCurrentStats();
auto ach = g_IAchievementMgr->GetAchievementByIndex(index);
g_ISteamUserStats->ClearAchievement(ach->GetName());
g_ISteamUserStats->StoreStats();
g_ISteamUserStats->RequestCurrentStats();
}
});
CatCommand lock("achievement_lock", "Lock all achievements", Lock);
CatCommand unlock("achievement_unlock", "Unlock all achievements", Unlock);
static bool accept_notifs;
static bool equip_hats;
static bool equip;
std::vector<Autoequip_unlock_list> equip_queue;
void unlock_achievements_and_accept(std::vector<int> items)
{
if (!checkachmngr())
return;
for (auto id : items)
{
IAchievement *ach = reinterpret_cast<IAchievement *>(g_IAchievementMgr->GetAchievementByID(id));
if (ach)
{
g_IAchievementMgr->AwardAchievement(id);
}
}
accept_notifs = true;
}
static CatCommand get_sniper_items("achievement_sniper", "Get all sniper achievement items", []() {
static std::vector<int> sniper_items = { 1136, 1137, 1138 };
unlock_achievements_and_accept(sniper_items);
});
static Timer accept_time{};
static Timer equip_noisemaker{};
static Timer cooldowm{};
static Timer cooldown_2{};
static CatCommand get_best_hats("achievement_cathats", "Get and equip the bencat hats", []() {
static std::vector<int> bencat_hats = { 1902, 1912, 2006 };
unlock_achievements_and_accept(bencat_hats);
hacks::shared::misc::generate_schema();
hacks::shared::misc::Schema_Reload();
equip_hats = true;
});
struct queue_struct
{
int clazz;
int slot;
unsigned long long uuid;
};
static std::deque<queue_struct> gc_queue;
bool equip_item(int clazz, int slot, int id)
{
auto invmng = re::CTFInventoryManager::GTFInventoryManager();
auto inv = invmng->GTFPlayerInventory();
// Request to equip a stock item
if (id == -1)
{
// -1 in Unsigned long long would be the maximum value
gc_queue.push_front({ clazz, slot, ULLONG_MAX });
return true;
}
else
{
auto item_view = inv->GetFirstItemOfItemDef(id);
if (item_view)
{
gc_queue.push_front({ clazz, slot, item_view->UUID() });
return true;
}
}
return false;
}
bool equip_action_item(int action_item, std::pair<int, int> classes)
{
for (int i = classes.first; i <= classes.second; i++)
if (!equip_item(i, 9, action_item))
return false;
return true;
}
bool equip_hats_fn(std::vector<int> hats, std::pair<int, int> classes)
{
for (int i = classes.first; i <= classes.second; i++)
// 7...8...10. Wait why is it 10 tf2? you make no sense
if (!(equip_item(i, 7, hats[0]) && equip_item(i, 8, hats[1]) && equip_item(i, 10, hats[2])))
return false;
return true;
}
static std::vector<int> hat_list = { 302, 940, 941 };
// TNE didn't want me to put "epic_" infront of it :(
static Timer hat_steal_timer{};
static Timer gc_timer{};
// We need this incase a bot joins one class due to being stuck
// and then switches to the other while the stock equip is on
static std::array<Timer, 4> stock_equip_reset{};
// We also need a delay between each attempt to not spam gc
static Timer stock_equip_wait{};
void CreateMove()
{
if (gc_queue.size() && gc_timer.test_and_set(3000))
{
for (int i = 0; i < 10 && gc_queue.size(); i++)
{
queue_struct item = gc_queue.at(gc_queue.size() - 1);
auto invmng = re::CTFInventoryManager::GTFInventoryManager();
invmng->EquipItemInLoadout(item.clazz, item.slot, item.uuid);
gc_queue.pop_back();
}
}
if (CE_BAD(LOCAL_E))
return;
// Wait 500ms between each call
if (LOCAL_E->m_bAlivePlayer() && stock_equip_wait.test_and_set(500))
for (int i = 0; i < 4; i++)
{
// We should equip our stock weapons
if (equip_stock[i])
{
tf_class player_class = (tf_class) g_pLocalPlayer->clazz;
// This is pretty much always true, however, there are exceptions.
int slot = i;
// Non spy can't change PDA (and engineer doesn't need to)
if (i == 3 && player_class != tf_spy)
{
equip_stock[i] = false;
continue;
}
// For spy the primary is in reality a secondary. No idea why they did that
if (player_class == tf_spy && slot == 0)
slot = 1;
// PDA slot ids are way different, adjust
if (slot == 3)
slot = 6;
// If the timer is running for longer than 10s this is the first call after update
stock_equip_reset[i].test_and_set(10000);
// After 3 seconds we should stop equipping
if (stock_equip_reset[i].check(3000))
equip_stock[i] = false;
// Equip the item (-1 for stock)
equip_item(player_class, slot, -1);
}
}
}
void Callback(int after, int type)
{
if (!after)
return;
std::vector<Autoequip_unlock_list> equip_from;
// Equip stock weapons
if (after == 100 && type <= 3)
equip_stock[type] = true;
// Store the needed array
switch (type)
{
case 0:
if (after > primary.size())
return;
equip_from = primary;
// primary
break;
case 1:
if (after > secondary.size())
return;
equip_from = secondary;
// secondary
break;
case 2:
if (after > melee.size())
return;
equip_from = melee;
// melee
break;
case 3:
if (after > pda2.size())
return;
equip_from = pda2;
// PDA 2
break;
}
if (equip_from.size())
{
// Needs to get passed std vector of ints
std::vector<int> pass = { equip_from.at(after - 1).achievement_id };
// Unlock achievements and start accepting
unlock_achievements_and_accept(pass);
// equip queue
equip_queue.push_back(equip_from.at(after - 1));
// Start equip process
equip = true;
}
}
void Paint()
{
// Start accepting
if (accept_notifs)
{
accept_time.update();
accept_notifs = false;
}
// "Trigger/Accept first notification" aka Achievement items
if (!accept_time.check(5000) && cooldowm.test_and_set(500))
g_IEngine->ClientCmd_Unrestricted("cl_trigger_first_notification");
// Noisemaker code
if (auto_noisemaker && equip_noisemaker.test_and_set(60000))
{
// Inventory Manager
auto invmng = re::CTFInventoryManager::GTFInventoryManager();
// Inventory
auto inv = invmng->GTFPlayerInventory();
// No Noisemaker in inventory?
if (!inv->GetFirstItemOfItemDef(673) && !inv->GetFirstItemOfItemDef(536))
accept_time.update();
else
{
bool success = equip_action_item(673, { tf_scout, tf_engineer });
if (!success)
{
success = equip_action_item(536, { tf_scout, tf_engineer });
if (!success)
logging::Info("Bruh moment while equipping noisemaker");
}
}
}
// Hat equip code
if (equip_hats)
{
// If done start accepting notifications, also time out after a while
if (accept_time.check(5000) && !accept_time.check(10000) && cooldowm.test_and_set(500))
{
// Inventory Manager
auto invmng = re::CTFInventoryManager::GTFInventoryManager();
// Inventory
auto inv = invmng->GTFPlayerInventory();
// Frontline field recorder
auto item_view1 = inv->GetFirstItemOfItemDef(302);
// Gibus
auto item_view2 = inv->GetFirstItemOfItemDef(940);
// Skull Island Tropper
auto item_view3 = inv->GetFirstItemOfItemDef(941);
if (item_view1 && item_view2 && item_view3)
{
if (!accept_time.check(7500))
{
// Equip these hats on all classes
bool success = equip_hats_fn({ 302, 940, 941 }, { tf_scout, tf_engineer });
if (success)
{
logging::Info("Equipping hats!");
equip_hats = false;
}
}
}
}
else if (accept_time.check(10000))
equip_hats = false;
}
// Equip weapons
if (equip)
{
// After 5 seconds of accept time, start
if (accept_time.check(5000) && !accept_time.check(10000) && cooldown_2.test_and_set(500))
{
// Watch for each item and equip it
for (int i = 0; i < equip_queue.size(); i++)
{
auto equip = equip_queue.at(i);
bool success = equip_item(equip.player_class, equip.slot, equip.item_id);
if (success)
{
logging::Info("Equipped Item!");
equip_queue.erase(equip_queue.begin() + i);
}
}
// We did it
if (!equip_queue.size())
equip = false;
}
else if (accept_time.check(10000) && cooldown_2.test_and_set(500))
{
if (equip_queue.size())
{
logging::Info("Equipping failed!");
equip_queue.clear();
}
}
}
};
static InitRoutine init([]() {
// Primary list
primary.push_back(Autoequip_unlock_list("Force-A-Nature", 1036, 45, tf_scout, 0));
primary.push_back(Autoequip_unlock_list("Backburner", 1638, 40, tf_pyro, 0));
primary.push_back(Autoequip_unlock_list("Natascha", 1538, 41, tf_heavy, 0));
primary.push_back(Autoequip_unlock_list("Frontier Justice", 1801, 141, tf_engineer, 0));
primary.push_back(Autoequip_unlock_list("Blutsauger", 1437, 36, tf_medic, 0));
primary.push_back(Autoequip_unlock_list("Huntsman", 1136, 56, tf_sniper, 0));
primary.push_back(Autoequip_unlock_list("Ambassador", 1735, 61, tf_spy, 1));
primary.push_back(Autoequip_unlock_list("Direct hit", 1237, 127, tf_soldier, 0));
// Secondary list
secondary.push_back(Autoequip_unlock_list("Bonk!", 1038, 46, tf_scout, 1));
secondary.push_back(Autoequip_unlock_list("Buff Banner", 1238, 129, tf_soldier, 1));
secondary.push_back(Autoequip_unlock_list("Flare gun", 1637, 39, tf_pyro, 1));
secondary.push_back(Autoequip_unlock_list("Chargin' Targe", 1336, 131, tf_demoman, 1));
secondary.push_back(Autoequip_unlock_list("Scottish Resistance", 1638, 130, tf_demoman, 1));
secondary.push_back(Autoequip_unlock_list("Sandvich", 1537, 42, tf_heavy, 1));
secondary.push_back(Autoequip_unlock_list("Wrangler", 1803, 140, tf_engineer, 1));
secondary.push_back(Autoequip_unlock_list("Kritzkrieg", 1438, 35, tf_medic, 1));
secondary.push_back(Autoequip_unlock_list("Jarate", 1137, 58, tf_sniper, 1));
secondary.push_back(Autoequip_unlock_list("Razorback", 1138, 57, tf_sniper, 1));
// Melee list
melee.push_back(Autoequip_unlock_list("Sandman", 1037, 44, tf_scout, 2));
melee.push_back(Autoequip_unlock_list("Equalizer", 1236, 128, tf_soldier, 2));
melee.push_back(Autoequip_unlock_list("Axtinguisher", 1639, 38, tf_pyro, 2));
melee.push_back(Autoequip_unlock_list("Eyelander", 1337, 132, tf_demoman, 2));
melee.push_back(Autoequip_unlock_list("Killing Gloves of Boxing", 1539, 43, tf_heavy, 2));
melee.push_back(Autoequip_unlock_list("Gunslinger", 1802, 142, tf_engineer, 2));
melee.push_back(Autoequip_unlock_list("Ubersaw", 1439, 37, tf_medic, 2));
// PDA 2
pda2.push_back(Autoequip_unlock_list("Cloak and dagger", 1736, 60, tf_spy, 6));
pda2.push_back(Autoequip_unlock_list("Deadringer", 1737, 59, tf_spy, 6));
// Callbacks
equip_primary.installChangeCallback([](settings::VariableBase<int> &, int after) { Callback(after, 0); });
equip_secondary.installChangeCallback([](settings::VariableBase<int> &, int after) { Callback(after, 1); });
equip_melee.installChangeCallback([](settings::VariableBase<int> &, int after) { Callback(after, 2); });
equip_pda2.installChangeCallback([](settings::VariableBase<int> &, int after) { Callback(after, 3); });
EC::Register(EC::CreateMove, CreateMove, "cm_achivement");
EC::Register(EC::Paint, Paint, "paint_achivement");
});
} // namespace hacks::tf2::achievement

523
src/hacks/AutoItem.cpp Normal file
View File

@ -0,0 +1,523 @@
/*
This file is part of Cathook.
Cathook is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Cathook is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Cathook. If not, see <https://www.gnu.org/licenses/>.
*/
// Codeowners: aUniqueUser
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include "common.hpp"
namespace hacks::tf2::autoitem
{
static settings::Boolean enable{ "auto-item.enable", "false" };
static settings::Int interval{ "auto-item.time", "30000" };
// stock by default
static settings::Boolean weapons{ "auto-item.weapons", "false" };
static settings::String primary{ "auto-item.weapons.primary", "-1" };
static settings::String secondary{ "auto-item.weapons.secondary", "-1" };
static settings::String melee{ "auto-item.weapons.melee", "-1" };
// ben cat hats by default
static settings::Boolean hats{ "auto-item.hats", "false" };
static settings::Int hat1{ "auto-item.hats.1", "940" };
static settings::Int hat2{ "auto-item.hats.2", "941" };
static settings::Int hat3{ "auto-item.hats.3", "302" };
static settings::Boolean debug{ "auto-item.debug", "false" };
#define Debug(...) \
if (*debug) \
logging::Info("AutoItem.cpp: " __VA_ARGS__)
#if ENABLE_TEXTMODE
static settings::Boolean autoNoisemaker{ "misc.auto-noisemaker", "true" };
#else
static settings::Boolean autoNoisemaker{ "misc.auto-noisemaker", "false" };
#endif
// 536 is Birthday noisemaker
// 673 is Christmas noisemaker
static int noisemaker_id = 536;
std::vector<std::tuple<int, int, std::string>> ach_items;
std::vector<std::vector<std::string>> craft_groups;
bool checkAchMgr()
{
if (!g_IAchievementMgr)
{
g_IAchievementMgr = g_IEngine->GetAchievementMgr();
if (!g_IAchievementMgr)
return false;
}
return true;
}
void Lock()
{
if (!checkAchMgr())
return;
g_ISteamUserStats->RequestCurrentStats();
for (int i = 0; i < g_IAchievementMgr->GetAchievementCount(); i++)
{
g_ISteamUserStats->ClearAchievement(g_IAchievementMgr->GetAchievementByIndex(i)->GetName());
}
g_ISteamUserStats->StoreStats();
g_ISteamUserStats->RequestCurrentStats();
}
void Unlock()
{
if (!checkAchMgr())
return;
for (int i = 0; i < g_IAchievementMgr->GetAchievementCount(); i++)
{
g_IAchievementMgr->AwardAchievement(g_IAchievementMgr->GetAchievementByIndex(i)->GetAchievementID());
}
}
void unlockSingle(int achID)
{
if (!checkAchMgr())
return;
auto *ach = reinterpret_cast<IAchievement *>(g_IAchievementMgr->GetAchievementByID(achID));
if (ach)
{
g_IAchievementMgr->AwardAchievement(achID);
}
}
std::pair<int, std::string> isAchItem(int id)
{
for (auto &i : ach_items)
{
if (std::get<0>(i) == id)
return std::make_pair(std::get<1>(i), std::get<2>(i));
}
return std::make_pair(0, "");
}
void getItem(int id, bool rent = true)
{
Debug("Trying to get item, %i", id);
auto index = isAchItem(id);
if (index.first != 0)
{
unlockSingle(index.first);
g_IEngine->ClientCmd_Unrestricted("cl_trigger_first_notification");
}
else if (rent)
Rent(id);
else
Debug("Failed to get item %i", id);
}
static bool equipItem(int clazz, int slot, int id, bool get = true, bool allowRent = false)
{
auto invmng = re::CTFInventoryManager::GTFInventoryManager();
auto inv = invmng->GTFPlayerInventory();
// Slot correction for spy
if (g_pLocalPlayer->clazz == tf_spy)
{
// Secondary -> Cloak
if (slot == 1)
slot = 6;
}
if (get)
{
auto item_view = inv->GetFirstItemOfItemDef(id);
if (!item_view)
{
getItem(id, allowRent);
return false;
}
}
if (id == -1)
return invmng->EquipItemInLoadout(clazz, slot, -1);
auto item_view = inv->GetFirstItemOfItemDef(id);
if (item_view)
return invmng->EquipItemInLoadout(clazz, slot, item_view->UUID());
return false;
}
void parseRvars()
{
craft_groups.clear();
std::vector<std::string> tmp_vec;
boost::split(tmp_vec, *primary, boost::is_any_of(";-"));
craft_groups.emplace_back(tmp_vec);
boost::split(tmp_vec, *secondary, boost::is_any_of(";-"));
craft_groups.emplace_back(tmp_vec);
boost::split(tmp_vec, *melee, boost::is_any_of(";-"));
craft_groups.emplace_back(tmp_vec);
}
bool use_fallback = false;
int first_item_attempts = 0;
void getAndEquipWeapon(std::string str, int clazz, int slot)
{
auto invmng = re::CTFInventoryManager::GTFInventoryManager();
auto inv = invmng->GTFPlayerInventory();
if (str == "-1")
{
equipItem(clazz, slot, -1, false, false);
return;
}
// Single item, rent or simply get via achievement, use fallback if needed and specified.
if (str.find(',') == std::string::npos && str.find(';') == std::string::npos)
{
std::vector<std::string> ids_split_str;
std::vector<int> ids_split;
try
{
if (str.find('/') != std::string::npos)
{
boost::split(ids_split_str, str, boost::is_any_of("/"));
for (auto &id : ids_split_str)
{
ids_split.emplace_back(std::stoi(id));
}
use_fallback = true;
}
else
ids_split.emplace_back(std::stoi(str));
}
catch (std::invalid_argument &e)
{
Debug("invalid_argument error making ids_split vector.");
return;
}
auto item_view = inv->GetFirstItemOfItemDef(ids_split.at(0));
// Try the first item 3 times before moving to fallback
if (use_fallback)
{
if (!item_view && first_item_attempts >= 3)
equipItem(clazz, slot, ids_split.at(1), true, true);
else
{
equipItem(clazz, slot, ids_split.at(0), true, true);
first_item_attempts++;
}
}
else
equipItem(clazz, slot, ids_split.at(0), true, true);
}
else
{
// Using auto-craft
int result;
int loc = str.find('-');
if (loc != std::string::npos)
{
try
{
result = std::stoi(str.substr(loc + 1, str.length()));
}
catch (std::invalid_argument &e)
{
Debug("invalid_argument error making result integer.");
return;
}
auto item_view = inv->GetFirstItemOfItemDef(result);
if (item_view)
{
equipItem(clazz, slot, result, false, false);
return;
}
std::vector<std::string> ids_rec_str;
std::vector<int> ids_rec;
for (auto &group_str : craft_groups[slot])
{
Debug("Crafting group: %s", group_str.c_str());
ids_rec.clear();
ids_rec_str.clear();
// Last group should be the just the result, skip it
if (group_str == std::to_string(result))
continue;
// Split this crafting group into IDs
boost::split(ids_rec_str, group_str, boost::is_any_of(","));
try
{
// Convert to ints
for (auto &id : ids_rec_str)
{
ids_rec.emplace_back(std::stoi(id));
}
}
catch (std::invalid_argument &e)
{
Debug("invalid_argument error making rec ids vector.");
return;
}
// See if we have the requirements to perform this craft, if not try to get them.
int rec_req_amount_have = 0;
for (auto &id : ids_rec)
{
// For now we should just assume the user has
// provided a way for getting needed scrap
if (id == 5000 || id == 5001 || id == 5002)
{
rec_req_amount_have++;
continue;
}
// In this loop id is a item id of 1 part of the crafting group
auto item_view = inv->GetFirstItemOfItemDef(id);
if (item_view)
rec_req_amount_have++;
else
{
auto index = isAchItem(id);
if (index.first != 0)
{
if (!checkAchMgr())
return;
if (g_IAchievementMgr->HasAchieved(index.second.c_str()))
{
Debug("Cant get specified ach item %i because it has already been unlocked! moving to next item.", id);
// User's fault if they put a ach item they unlocked and have used for crafting already/deleted.
rec_req_amount_have++;
continue;
}
Debug("Getting ach item %i, required for this craft.", id);
getItem(id, false);
return;
}
else
{
Debug("Missing required crafting materials! %i is NOT a ach item!", id);
return;
}
}
}
// Only attempt crafting if we have the correct amount of items.
if (rec_req_amount_have == ids_rec.size())
{
Craft(ids_rec);
g_IEngine->ClientCmd_Unrestricted("cl_trigger_first_notification");
}
}
}
else
{
Debug("No result item, can't auto-craft.");
return;
}
}
}
static Timer t{};
void CreateMove()
{
if (!enable || CE_BAD(LOCAL_E) || !t.test_and_set(*interval))
return;
int clazz = g_pLocalPlayer->clazz;
auto invmng = re::CTFInventoryManager::GTFInventoryManager();
auto inv = invmng->GTFPlayerInventory();
// Only run if we are playing a valid class
if (clazz != 0)
{
if (weapons)
{
getAndEquipWeapon(*primary, clazz, 0);
getAndEquipWeapon(*secondary, clazz, 1);
getAndEquipWeapon(*melee, clazz, 2);
}
if (hats)
{
static int offset = 0;
const static int slots[3] = { 7, 8, 10 };
equipItem(clazz, slots[offset], *hat1);
equipItem(clazz, slots[(offset + 1) % 3], *hat2);
equipItem(clazz, slots[(offset + 2) % 3], *hat3);
offset = (offset + 1) % 3;
}
if (autoNoisemaker && inv->GetFirstItemOfItemDef(noisemaker_id))
{
equipItem(clazz, 9, noisemaker_id, false, false);
}
}
}
CatCommand dump_achievement("achievement_dump", "Dump achievements to file (development)", []() {
if (!checkAchMgr())
return;
std::ofstream out("/tmp/cathook_achievements.txt", std::ios::out);
if (out.bad())
return;
for (int i = 0; i < g_IAchievementMgr->GetAchievementCount(); i++)
{
out << '[' << i << "] " << g_IAchievementMgr->GetAchievementByIndex(i)->GetName() << ' ' << g_IAchievementMgr->GetAchievementByIndex(i)->GetAchievementID() << "\n";
}
out.close();
});
CatCommand unlock_single("achievement_unlock_single", "Unlocks single achievement by ID", [](const CCommand &args) {
if (!checkAchMgr())
return;
char *out = nullptr;
int id = strtol(args.Arg(1), &out, 10);
if (out == args.Arg(1))
{
logging::Info("NaN achievement ID!");
return;
}
unlockSingle(id);
});
// For some reason it SEGV's when I try to GetAchievementByID();
CatCommand lock_single("achievement_lock_single", "Locks single achievement by INDEX!", [](const CCommand &args) {
if (!checkAchMgr())
return;
if (args.ArgC() < 2)
{
logging::Info("Actually provide an index");
return;
}
int id;
try
{
id = std::stoi(args.Arg(1));
}
catch (std::invalid_argument)
{
logging::Info("Bad achievement ID!");
return;
}
auto *ach = reinterpret_cast<IAchievement *>(g_IAchievementMgr->GetAchievementByID(id));
int index = -1;
if (ach)
for (int i = 0; i < g_IAchievementMgr->GetAchievementCount(); i++)
{
auto ach2 = g_IAchievementMgr->GetAchievementByIndex(i);
if (ach2->GetAchievementID() == id)
{
index = i;
break;
}
}
if (ach && index != -1)
{
g_ISteamUserStats->RequestCurrentStats();
auto ach = g_IAchievementMgr->GetAchievementByIndex(index);
g_ISteamUserStats->ClearAchievement(ach->GetName());
g_ISteamUserStats->StoreStats();
g_ISteamUserStats->RequestCurrentStats();
}
});
CatCommand rent_item("rent_item", "testrun a item by ID", [](const CCommand &args) {
char *out = nullptr;
int id = strtol(args.Arg(1), &out, 10);
if (out == args.Arg(1))
{
logging::Info("Bad item ID!");
return;
}
Rent(id);
});
CatCommand lock("achievement_lock", "Lock all achievements", Lock);
CatCommand unlock("achievement_unlock", "Unlock all achievements", Unlock);
static InitRoutine init([]() {
primary.installChangeCallback([](settings::VariableBase<std::string> &var, const std::string &after) { parseRvars(); });
secondary.installChangeCallback([](settings::VariableBase<std::string> &var, const std::string &after) { parseRvars(); });
melee.installChangeCallback([](settings::VariableBase<std::string> &var, const std::string &after) { parseRvars(); });
EC::Register(EC::CreateMove, CreateMove, "autoitem_cm");
std::time_t theTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm *aTime = std::localtime(&theTime);
int day = aTime->tm_mday;
int month = aTime->tm_mon + 1; // Month is 0 - 11, add 1 to get a jan-dec 1-12 concept
// We only want to Use the christmas noisemaker around christmas time, let's use 12th of december+ til 12th of january
if ((month == 12 && day >= 12) || (month == 1 && day <= 12))
noisemaker_id = 673;
/* haha emplace_back go brrrrrr */
ach_items.emplace_back(45, 1036, "TF_SCOUT_ACHIEVE_PROGRESS1"); // Force-A-Nature - Scout Milestone 1
ach_items.emplace_back(44, 1037, "TF_SCOUT_ACHIEVE_PROGRESS2"); // Sandman - Scout Milestone 2
ach_items.emplace_back(46, 1038, "TF_SCOUT_ACHIEVE_PROGRESS3"); // Bonk! Atomic Punch - Scout Milestone 3
ach_items.emplace_back(128, 1236, "TF_SOLDIER_ACHIEVE_PROGRESS1"); // Equalizer - Soldier Milestone 1
ach_items.emplace_back(127, 1237, "TF_SOLDIER_ACHIEVE_PROGRESS2"); // Direct Hit - Soldier Milestone 2
ach_items.emplace_back(129, 1238, "TF_SOLDIER_ACHIEVE_PROGRESS3"); // Buff Banner - Soldier Milestone 3
ach_items.emplace_back(39, 1637, "TF_PYRO_ACHIEVE_PROGRESS1"); // Flare Gun - Pyro Milestone 1
ach_items.emplace_back(40, 1638, "TF_PYRO_ACHIEVE_PROGRESS2"); // Backburner - Pyro Milestone 2
ach_items.emplace_back(38, 1639, "TF_PYRO_ACHIEVE_PROGRESS3"); // Axtinguisher - Pyro Milestone 3
ach_items.emplace_back(131, 1336, "TF_DEMOMAN_ACHIEVE_PROGRESS1"); // Chargin' Targe - Demoman Milestone 1
ach_items.emplace_back(132, 1337, "TF_DEMOMAN_ACHIEVE_PROGRESS2"); // Eyelander - Demoman Milestone 2
ach_items.emplace_back(130, 1338, "TF_DEMOMAN_ACHIEVE_PROGRESS3"); // Scottish Resistance - Demoman Milestone 3
ach_items.emplace_back(42, 1537, "TF_HEAVY_ACHIEVE_PROGRESS1"); // Sandvich - Heavy Milestone 1
ach_items.emplace_back(41, 1538, "TF_HEAVY_ACHIEVE_PROGRESS2"); // Natascha - Heavy Milestone 2
ach_items.emplace_back(43, 1539, "TF_HEAVY_ACHIEVE_PROGRESS3"); // Killing Gloves of Boxing - Heavy Milestone 3
ach_items.emplace_back(141, 1801, "TF_ENGINEER_ACHIEVE_PROGRESS1"); // Frontier Justice - Engineer Milestone 1
ach_items.emplace_back(142, 1802, "TF_ENGINEER_ACHIEVE_PROGRESS2"); // Gunslinger - Engineer Milestone 2
ach_items.emplace_back(140, 1803, "TF_ENGINEER_ACHIEVE_PROGRESS3"); // Wrangler - Engineer Milestone 3
ach_items.emplace_back(36, 1437, "TF_MEDIC_ACHIEVE_PROGRESS1"); // Blutsauger - Medic Milestone 1
ach_items.emplace_back(35, 1438, "TF_MEDIC_ACHIEVE_PROGRESS2"); // Kritzkrieg - Medic Milestone 2
ach_items.emplace_back(37, 1439, "TF_MEDIC_ACHIEVE_PROGRESS3"); // Ubersaw - Medic Milestone 3
ach_items.emplace_back(56, 1136, "TF_SNIPER_ACHIEVE_PROGRESS1"); // Huntsman - Sniper Milestone 1
ach_items.emplace_back(58, 1137, "TF_SNIPER_ACHIEVE_PROGRESS2"); // Jarate - Sniper Milestone 2
ach_items.emplace_back(57, 1138, "TF_SNIPER_ACHIEVE_PROGRESS3"); // Razorback - Sniper Milestone 3
ach_items.emplace_back(61, 1735, "TF_SPY_ACHIEVE_PROGRESS1"); // Ambassador - Spy Milestone 1
ach_items.emplace_back(60, 1736, "TF_SPY_ACHIEVE_PROGRESS2"); // Cloak and Dagger - Spy Milestone 2
ach_items.emplace_back(59, 1737, "TF_SPY_ACHIEVE_PROGRESS3"); // Dead Ringer - Spy Milestone 3
ach_items.emplace_back(1123, 1928, "TF_HALLOWEEN_DOOMSDAY_MILESTONE"); // Necro Smasher - Carnival of Carnage: Step Right Up
ach_items.emplace_back(940, 1902, "TF_HALLOWEEN_DOMINATE_FOR_HAT"); // Ghostly Gibus - Ghastly Gibus Grab
ach_items.emplace_back(115, 1901, "TF_HALLOWEEN_COLLECT_PUMPKINS"); // Mildly Disturbing Halloween Mask - Candy Coroner
ach_items.emplace_back(278, 1906, "TF_HALLOWEEN_BOSS_KILL"); // Horseless Headless Horsemann's Head - Sleepy Holl0WND
ach_items.emplace_back(302, 2006, "TF_REPLAY_YOUTUBE_VIEWS_TIER2"); // Frontline Field Recorder - Local Cinema Star
ach_items.emplace_back(581, 1910, "TF_HALLOWEEN_EYEBOSS_KILL"); // MONOCULUS! - Optical Defusion
ach_items.emplace_back(668, 2212, "TF_MAPS_FOUNDRY_ACHIEVE_PROGRESS1"); // Full Head Of Steam - Foundry Milestone
ach_items.emplace_back(756, 2412, "TF_MAPS_DOOMSDAY_ACHIEVE_PROGRESS1"); // Gentle Munitionne of Leisure - Doomsday Milestone
ach_items.emplace_back(941, 1912, "TF_HALLOWEEN_MERASMUS_COLLECT_LOOT"); // Skull Island Topper - A Lovely Vacation Spot
ach_items.emplace_back(581, 1911, "TF_HALLOWEEN_LOOT_ISLAND"); // Bombinomicon - Dive Into a Good Book
ach_items.emplace_back(744, 156, "TF_DOMINATE_FOR_GOGGLES"); // Pyrovision Goggles - A Fresh Pair of Eyes
ach_items.emplace_back(1164, 167, "TF_PASS_TIME_GRIND"); // Civilian Grade JACK Hat - Jackpot!
ach_items.emplace_back(1169, 166, "TF_PASS_TIME_HAT"); // Military Grade JACK Hat - Tune Merasmus's Multi-Dimensional Television
ach_items.emplace_back(1170, 166, "TF_PASS_TIME_HAT"); // PASS Time Miniature Half JACK - Tune Merasmus's Multi-Dimensional Television
ach_items.emplace_back(267, 1909, "TF_HALLOWEEN_BOSS_KILL_MELEE"); // Haunted Metal Scrap - Gored!
});
} // namespace hacks::tf2::autoitem

View File

@ -1,7 +1,7 @@
set(files "${CMAKE_CURRENT_LIST_DIR}/AutoJoin.cpp" set(files "${CMAKE_CURRENT_LIST_DIR}/AutoJoin.cpp"
"${CMAKE_CURRENT_LIST_DIR}/CatBot.cpp" "${CMAKE_CURRENT_LIST_DIR}/CatBot.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Spam.cpp" "${CMAKE_CURRENT_LIST_DIR}/Spam.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Achievement.cpp" "${CMAKE_CURRENT_LIST_DIR}/AutoItem.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Aimbot.cpp" "${CMAKE_CURRENT_LIST_DIR}/Aimbot.cpp"
"${CMAKE_CURRENT_LIST_DIR}/AutoViewmodel.cpp" "${CMAKE_CURRENT_LIST_DIR}/AutoViewmodel.cpp"
"${CMAKE_CURRENT_LIST_DIR}/MiscAimbot.cpp" "${CMAKE_CURRENT_LIST_DIR}/MiscAimbot.cpp"

View File

@ -6,6 +6,7 @@
*/ */
#include "common.hpp" #include "common.hpp"
#include "e8call.hpp" #include "e8call.hpp"
#include "DetourHook.hpp"
using namespace re; using namespace re;
CTFInventoryManager *CTFInventoryManager::GTFInventoryManager() CTFInventoryManager *CTFInventoryManager::GTFInventoryManager()
@ -31,6 +32,31 @@ CTFPlayerInventory *CTFInventoryManager::GTFPlayerInventory()
return vfunc<fn_t>(this, offsets::PlatformOffset(21, offsets::undefined, 22), 0)(this);*/ return vfunc<fn_t>(this, offsets::PlatformOffset(21, offsets::undefined, 22), 0)(this);*/
} }
int CTFPlayerInventory::GetItemCount()
{
return *(int *) ((char *) this + 80);
}
#define SIZE_OF_ITEMVIEW 228
CEconItemView *CTFPlayerInventory::GetItem(int idx)
{
uintptr_t item_start = *(uintptr_t *) ((char *) this + 68);
uintptr_t item_offset = idx * SIZE_OF_ITEMVIEW;
return (CEconItemView *) (item_start + item_offset);
}
std::vector<unsigned long long> CTFPlayerInventory::GetItemsOfItemDef(int id)
{
std::vector<unsigned long long> uuid_vec;
for (int i = 0; i < this->GetItemCount(); i++)
{
auto item = this->GetItem(i);
if (item->GetDefinitionIndex() == id)
uuid_vec.push_back(item->UUID());
}
return uuid_vec;
}
CEconItemView *CTFPlayerInventory::GetFirstItemOfItemDef(int id) CEconItemView *CTFPlayerInventory::GetFirstItemOfItemDef(int id)
{ {
typedef CEconItemView *(*GetFirstItemOfItemDef_t)(int16_t, void *); typedef CEconItemView *(*GetFirstItemOfItemDef_t)(int16_t, void *);
@ -39,6 +65,11 @@ CEconItemView *CTFPlayerInventory::GetFirstItemOfItemDef(int id)
return GetFirstItemOfItemDef_fn(id, this); return GetFirstItemOfItemDef_fn(id, this);
} }
int CEconItemView::GetDefinitionIndex()
{
return *(ushort *) ((uintptr_t) this + 0x24);
}
unsigned long long CEconItemView::UUID() unsigned long long CEconItemView::UUID()
{ {
unsigned long long value = *(unsigned long long *) ((char *) this + 56); unsigned long long value = *(unsigned long long *) ((char *) this + 56);
@ -53,7 +84,123 @@ static CatCommand equip_debug("equip_debug", "Debug auto equip stuff", []() {
auto item_view = inv->GetFirstItemOfItemDef(56); auto item_view = inv->GetFirstItemOfItemDef(56);
if (item_view) if (item_view)
{ {
logging::Info("%llu %llu", item_view->UUID()); logging::Info("%llu", item_view->UUID());
logging::Info("Equip item: %d", invmng->EquipItemInLoadout(tf_sniper, 0, item_view->UUID())); logging::Info("Equip item: %d", invmng->EquipItemInLoadout(tf_sniper, 0, item_view->UUID()));
} }
}); });
static CatCommand list_debug("equip_list_debug", "Debug item def listing", []() {
auto invmng = CTFInventoryManager::GTFInventoryManager();
auto inv = invmng->GTFPlayerInventory();
auto items = inv->GetItemsOfItemDef(5000);
for (auto item : items)
logging::Info("%llu", item);
});
// Crafting slots on crafting page
#define CRAFTING_SLOTS_INPUT_ROWS 3
#define CRAFTING_SLOTS_INPUT_COLUMNS 4
#define CRAFTING_SLOTS_INPUTPANELS (CRAFTING_SLOTS_INPUT_ROWS * CRAFTING_SLOTS_INPUT_COLUMNS)
class CCraftingPanel_reclass2
{
public:
char unknown_data[560];
// Items in the input model panels
unsigned long long m_InputItems[CRAFTING_SLOTS_INPUTPANELS];
const int *m_ItemPanelCriteria[CRAFTING_SLOTS_INPUTPANELS];
char unknown_data2[72];
int m_iCurrentlySelectedRecipe;
char unknown_data3[50];
int m_iCraftingAttempts;
};
// Pass the Definition Indexes of the items you want to craft with (https://wiki.alliedmods.net/Team_Fortress_2_Item_Definition_Indexes)
bool Craft(std::vector<int> item_ids)
{
static auto craft_func = gSignatures.GetClientSignature("55 89 E5 57 56 53 83 EC 6C C7 45 ? 00 00 00 00 "
"8B 45 ? C7 45 ? 00 00 00 00 C7 45 ? 00 00 00 "
"00 C7 45 ? 00 00 00 00 C7 45 ? 00 00 00 00 83 80 ? ? ? ? 01");
// Return early to avoid a crash
static auto patch_addr = craft_func + 0x22e;
static BytePatch patch(patch_addr, { 0x83, 0xc4, 0x6c, 0x5b, 0x5e, 0x5f, 0x5d, 0xc3 });
patch.Patch();
CCraftingPanel_reclass2 panel{};
auto invmng = CTFInventoryManager::GTFInventoryManager();
auto inv = invmng->GTFPlayerInventory();
std::vector<unsigned long long> item_UUIDs;
// Iterate all desired items
for (auto &id : item_ids)
{
auto items = inv->GetItemsOfItemDef(id);
// Iterate all the items matching the ID
for (auto &item : items)
{
// Only add entries which are not added yet
bool failed = false;
for (auto &uuid : item_UUIDs)
if (uuid == item)
failed = true;
// Continue to next entry on failure
if (failed)
continue;
else
{
item_UUIDs.push_back(item);
break;
}
}
}
// No items found
if (item_UUIDs.empty())
return false;
// Add items
for (int i = 0; i < item_UUIDs.size(); i++)
{
panel.m_InputItems[i] = item_UUIDs[i];
}
// Select "Custom" Recipe
panel.m_iCurrentlySelectedRecipe = -2;
// Call the Craft function
typedef void (*Craft_t)(CCraftingPanel_reclass2 *);
Craft_t Craft_fn = (Craft_t) craft_func;
Craft_fn(&panel);
// Undo our patches to the game
patch.Shutdown();
return true;
}
bool Rent(int item_id)
{
typedef void (*DoPreviewItem_t)(void *, int);
static auto DoPreviewItem_addr = gSignatures.GetClientSignature("55 89 E5 56 53 81 EC 50 01 00 00 65 A1 ? ? ? ? 89 45 ? 31 C0 0F B7 75");
static auto id = (DoPreviewItem_addr + 0xf3);
// Request the item instead of testing if we can request it
static BytePatch id_patch(id, { 0xa7 });
id_patch.Patch();
auto DoPreviewItem_fn = (DoPreviewItem_t) DoPreviewItem_addr;
// The nullptr is "this" but it's unused in the function
DoPreviewItem_fn(nullptr, item_id);
// Reset the id
id_patch.Shutdown();
return true;
}
static CatCommand debug_panel("equip_debug_panel", "Debug the Crafting panel that gets used", []() { Craft({ 5000, 5000, 5000 }); });