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:
parent
21a23d732d
commit
b9daa94a73
@ -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"/>
|
||||||
|
@ -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>
|
|
16
data/menu/nullifiedcat/misc/autoitem.xml
Executable file
16
data/menu/nullifiedcat/misc/autoitem.xml
Executable 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>
|
@ -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
|
|
@ -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"
|
||||||
|
@ -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);
|
||||||
|
@ -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
523
src/hacks/AutoItem.cpp
Normal 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
|
@ -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"
|
||||||
|
@ -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 }); });
|
||||||
|
Reference in New Issue
Block a user