mirror of
https://github.com/A-Archives-and-Forks/Akebi-GC.git
synced 2026-10-12 02:28:01 +08:00
229 lines
6.3 KiB
C++
229 lines
6.3 KiB
C++
#include "pch-il2cpp.h"
|
|
#include "AutoTreeFarm.h"
|
|
|
|
#include <helpers.h>
|
|
#include <algorithm>
|
|
|
|
#include <cheat/events.h>
|
|
#include <cheat/game/util.h>
|
|
#include <cheat/game/EntityManager.h>
|
|
|
|
namespace cheat::feature
|
|
{
|
|
AutoTreeFarm::AutoTreeFarm() : Feature(),
|
|
NF(m_Enabled, "Auto tree farm", "AutoTreeFarm", false),
|
|
NF(m_AttackDelay, "Attack delay", "AutoTreeFarm", 150),
|
|
NF(m_RepeatDelay, "Repeat delay", "AutoTreeFarm", 500),
|
|
NF(m_AttackPerTree, "Attack per tree", "AutoTreeFarm", 5),
|
|
NF(m_Range, "Range", "AutoTreeFarm", 15.0f)
|
|
{
|
|
events::GameUpdateEvent += MY_METHOD_HANDLER(AutoTreeFarm::OnGameUpdate);
|
|
}
|
|
|
|
const FeatureGUIInfo& AutoTreeFarm::GetGUIInfo() const
|
|
{
|
|
static const FeatureGUIInfo info{ "Auto Tree Farm", "World", true };
|
|
return info;
|
|
}
|
|
|
|
void AutoTreeFarm::DrawMain()
|
|
{
|
|
ImGui::TextColored(ImColor(255, 165, 0, 255), "Note. This feature is not fully tested detection-wise.\n"
|
|
"Not recommended for main accounts or used with high values.");
|
|
|
|
ConfigWidget("Enabled", m_Enabled, "Automatically attack trees in range.");
|
|
ConfigWidget("Attack Delay (ms)", m_AttackDelay, 1, 0, 1000, "Delay before attacking the next tree (in ms).");
|
|
ConfigWidget("Repeat Delay (ms)", m_RepeatDelay, 1, 500, 1000, "Delay before attacking the same tree (in ms).\nValues <500ms will not work.");
|
|
|
|
ConfigWidget("Attacks per Tree", m_AttackPerTree, 1, 0, 100, "Count of attacks for one tree.\n" \
|
|
"Recommended to set to 10 or lower to avoid attacking empty trees.\n" \
|
|
"Set to 0 for unlimited attacks (even empty trees, extremely high risk).\n" \
|
|
"Note: Memorized trees' attacks are reset after game restart."
|
|
);
|
|
|
|
ConfigWidget("Range (m)", m_Range, 0.1f, 1.0f, 15.0f);
|
|
ImGui::TextColored(ImColor(255, 165, 0, 255), "Range is softly limited to ~15m for safety purposes.");
|
|
}
|
|
|
|
bool AutoTreeFarm::NeedStatusDraw() const
|
|
{
|
|
return m_Enabled;
|
|
}
|
|
|
|
void AutoTreeFarm::DrawStatus()
|
|
{
|
|
ImGui::Text("Tree Farm\n[%dms|%dms|%d|%.1fm]",
|
|
m_AttackDelay.value(),
|
|
m_RepeatDelay.value(),
|
|
m_AttackPerTree.value(),
|
|
m_Range.value());
|
|
}
|
|
|
|
AutoTreeFarm& AutoTreeFarm::GetInstance()
|
|
{
|
|
static AutoTreeFarm instance;
|
|
return instance;
|
|
}
|
|
|
|
|
|
std::unordered_set<app::SceneTreeObject*> GetTreeSet()
|
|
{
|
|
auto scenePropManager = GET_SINGLETON(MoleMole_ScenePropManager);
|
|
if (scenePropManager == nullptr)
|
|
return {};
|
|
|
|
auto scenePropDict = TO_UNI_DICT(scenePropManager->fields._scenePropDict, int32_t, app::Object*);
|
|
if (scenePropDict == nullptr)
|
|
return {};
|
|
|
|
std::unordered_set<app::SceneTreeObject*> trees;
|
|
for (auto& [id, propObject] : scenePropDict->pairs())
|
|
{
|
|
auto tree = CastTo<app::SceneTreeObject>(propObject, *app::SceneTreeObject__TypeInfo);
|
|
if (tree == nullptr)
|
|
continue;
|
|
|
|
trees.insert(tree);
|
|
}
|
|
|
|
return trees;
|
|
}
|
|
|
|
|
|
struct Vector3d
|
|
{
|
|
float x, y, z;
|
|
|
|
Vector3d(const app::Vector3& v)
|
|
{
|
|
x = v.x;
|
|
y = v.y;
|
|
z = v.z;
|
|
}
|
|
|
|
bool operator==(const Vector3d& b) const
|
|
{
|
|
return x == b.x && y == b.y && z == b.z;
|
|
}
|
|
|
|
};
|
|
|
|
struct hash_fn
|
|
{
|
|
std::size_t operator() (const Vector3d& vector) const
|
|
{
|
|
return std::hash<float>()(vector.x) ^ std::hash<float>()(vector.y) ^ std::hash<float>()(vector.z);
|
|
}
|
|
};
|
|
|
|
template <typename KeyT, typename ValT, uint32_t Size, class /*forward*/ _Hasher = std::hash<KeyT>>
|
|
class lra_map {
|
|
using key_value_pair = std::pair<KeyT, ValT>;
|
|
using list_iterator = typename std::list<key_value_pair>::iterator;
|
|
|
|
public:
|
|
void put(const KeyT& key, const ValT& val) {
|
|
auto it = elem_map.find(key);
|
|
// if element already in the map, don't modify it.
|
|
if (it != elem_map.end())
|
|
return;
|
|
|
|
items_list.push_front(key_value_pair(key, val));
|
|
elem_map[key] = items_list.begin();
|
|
|
|
if (Size < elem_map.size()) {
|
|
{
|
|
const KeyT& last_key = items_list.back().first;
|
|
elem_map.erase(last_key);
|
|
}
|
|
items_list.pop_back();
|
|
}
|
|
}
|
|
|
|
ValT& get(const KeyT& key) {
|
|
auto it = elem_map.find(key);
|
|
if (it == elem_map.end())
|
|
throw std::runtime_error("Tried to access key not present in map");
|
|
return it->second->second;
|
|
}
|
|
|
|
bool exists(const KeyT& key) const {
|
|
return elem_map.find(key) != elem_map.end();
|
|
}
|
|
protected:
|
|
std::list<key_value_pair> items_list;
|
|
std::unordered_map<KeyT, list_iterator, _Hasher> elem_map;
|
|
};
|
|
|
|
void AutoTreeFarm::OnGameUpdate()
|
|
{
|
|
static lra_map<Vector3d, uint32_t, 10, hash_fn> s_AttackCountMap;
|
|
static std::queue<app::SceneTreeObject*> s_AttackQueue;
|
|
static std::unordered_set<app::SceneTreeObject*> s_AttackQueueSet;
|
|
static uint64_t s_LastAttackTimestamp = 0;
|
|
|
|
uint64_t timestamp = app::MoleMole_TimeUtil_get_NowTimeStamp(nullptr);
|
|
if (!m_Enabled || s_LastAttackTimestamp + m_AttackDelay > timestamp)
|
|
return;
|
|
|
|
auto& manager = game::EntityManager::instance();
|
|
auto scenePropManager = GET_SINGLETON(MoleMole_ScenePropManager);
|
|
auto networkManager = GET_SINGLETON(MoleMole_NetworkManager);
|
|
if (networkManager == nullptr || scenePropManager == nullptr)
|
|
return;
|
|
|
|
auto treeSet = GetTreeSet();
|
|
for (auto& tree : treeSet)
|
|
{
|
|
if (s_AttackQueueSet.count(tree) > 0)
|
|
continue;
|
|
|
|
if (s_LastAttackTimestamp + m_RepeatDelay > timestamp)
|
|
continue;
|
|
|
|
auto position = tree->fields._.realBounds.m_Center;
|
|
if (manager.avatar()->distance(app::WorldShiftManager_GetRelativePosition(position, nullptr)) > m_Range)
|
|
continue;
|
|
|
|
s_AttackQueueSet.insert(tree);
|
|
s_AttackQueue.push(tree);
|
|
}
|
|
|
|
while (!s_AttackQueue.empty())
|
|
{
|
|
auto tree = s_AttackQueue.front();
|
|
s_AttackQueue.pop();
|
|
s_AttackQueueSet.erase(tree);
|
|
|
|
if (treeSet.count(tree) == 0)
|
|
continue;
|
|
|
|
auto position = tree->fields._.realBounds.m_Center;
|
|
if (manager.avatar()->distance(app::WorldShiftManager_GetRelativePosition(position, nullptr)) > m_Range)
|
|
continue;
|
|
|
|
app::MoleMole_Config_TreeType__Enum treeType;
|
|
auto pattern = tree->fields._config->fields._._.scenePropPatternName;
|
|
if (!app::MoleMole_ScenePropManager_GetTreeTypeByPattern(scenePropManager, pattern, &treeType, nullptr))
|
|
continue;
|
|
|
|
if (m_AttackPerTree > 0)
|
|
{
|
|
if (!s_AttackCountMap.exists(position))
|
|
s_AttackCountMap.put(position, 0);
|
|
|
|
auto& attackCount = s_AttackCountMap.get(position);
|
|
attackCount++;
|
|
|
|
if (attackCount > static_cast<uint32_t>(m_AttackPerTree))
|
|
continue;
|
|
}
|
|
|
|
s_LastAttackTimestamp = timestamp;
|
|
app::MoleMole_NetworkManager_RequestHitTreeDropNotify(networkManager, position, position, treeType, nullptr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|