Pre FilemapVersion 70

This commit is contained in:
Knaapchen 2022-10-14 10:07:08 +02:00
parent dce2ca08f3
commit be0fafb249
9 changed files with 585 additions and 104 deletions

View File

@ -24,8 +24,6 @@ namespace flyff {
// extern void w2c_f167(fugg::String*, std::string*);
extern void w2c_f167(u32, u32);
extern u32 w2c_f208(u64);
extern void w2c_f337(u32, u32, u32);
// f271_called_on_attack
extern u32 w2c_f271(u32 w2c_p0, u32 w2c_p1, u64 w2c_p2, u64 w2c_p3, u64 w2c_p4, u64 w2c_p5);

View File

@ -3,22 +3,52 @@
#include <fugg.h>
#include <cstddef>
#include <iterator>
#include <bitset>
#include "ObjectType.h"
#include "core.h"
#include "math.h"
#include "ObjectType.h"
#include "item.h"
namespace flyff {
enum class MovementFlags : uint32_t {
None = 0 << 0,
CombatStance = 1 << 0,
Walking = 1 << 1,
Sitting = 1 << 2,
Flying = 1 << 3,
Unknown16 = 1 << 4,
Unknown32 = 1 << 5,
// Not sure, maybe for flying.
TurboActivated = 1 << 6,
};
inline constexpr MovementFlags operator&(MovementFlags x, MovementFlags y) {
return static_cast<MovementFlags>(static_cast<uint32_t>(x) &
static_cast<uint32_t>(y));
}
struct __attribute__((packed)) Mover {
union {
DEFINE_MEMBER(0x18, flyff::Matrix4, world_matrix);
DEFINE_MEMBER(0x98, flyff::Vector3, position);
DEFINE_MEMBER(152, flyff::Vector3, position);
DEFINE_MEMBER(164, flyff::Vector3, rotation);
DEFINE_MEMBER(216, bool, set_in_constructor);
DEFINE_MEMBER(2020, const flyff::String, name);
DEFINE_MEMBER(1628, flyff::Vector3, move_target);
DEFINE_MEMBER(1728, flyff::MoverID, move_toward_target);
DEFINE_MEMBER(1736, flyff::MoverID, last_attacked_target);
DEFINE_MEMBER(1744, flyff::MoverID, selected_target);
// The id of the target that it hit last.
DEFINE_MEMBER(1752, flyff::MoverID, attack_target);
DEFINE_MEMBER(1760, flyff::MoverID, follow_target);
// The id, only non-zero when the attack animation plays, of the target it is attacking.
DEFINE_MEMBER(1768, flyff::MoverID, current_attack_target);
DEFINE_MEMBER(1776, flyff::MoverID, platform_standing_on);
@ -29,16 +59,59 @@ struct __attribute__((packed)) Mover {
DEFINE_MEMBER(204, ObjectType, type);
DEFINE_MEMBER(216, uint32_t, existence_check);
DEFINE_MEMBER(916, uint32_t, if_not_null_needsattackitem);
DEFINE_MEMBER(1804, uint32_t, pickup_things);
DEFINE_MEMBER(1806, uint16_t, pickup_flags);
// Maybe status flags
DEFINE_MEMBER(1807, uint8_t, is_dead_flags);
DEFINE_MEMBER(1808, MovementFlags, movement_flags);
DEFINE_MEMBER(904, uint32_t, if_not_null_needsattackitem);
DEFINE_MEMBER(192, uintptr_t, world);
DEFINE_MEMBER(2264, flyff::MoverID, maybe_hash);
// Only works on players.
DEFINE_MEMBER(2472, uint32_t, level);
DEFINE_MEMBER(860, flyff::Vector<InventoryItem>, inventory);
const MinimumSize<4096> __size;
};
bool is_dead() const {
return (is_dead_flags & 8) != 0;
static constexpr uint8_t const& IsDeadMask = 8;
return (is_dead_flags & IsDeadMask) != 0;
}
bool is_walking() const {
return (movement_flags & MovementFlags::Walking) != MovementFlags::None;
}
bool is_flying() const {
return (movement_flags & MovementFlags::Flying) != MovementFlags::None;
}
// bool prevents_pickup_emote() const {
// DEFINE_MEMBER(1806, uint16_t, pickup_flags);
// // Returns true if in attack animation
// return (pickup_flags & 0x8ff) != 0;
// }
};
};
struct MoverMap {
struct __attribute__((packed)) Node {
Pointer<Node> next;
unsigned int list_index;
unsigned long long hash;
Pointer<Mover> mover;
};
Pointer<uintptr_t> buckets;
size_t capacity;
Pointer<Node> first;
};
}; // namespace flyff

View File

@ -6,6 +6,12 @@
// static_assert(offsetof(TestMover, name) == 0x7f4, "Mover.name needs to be at 2020");
namespace flyff {
namespace raw {
extern "C" {
// get_mover
extern u32 w2c_f208(u64);
};
};
// Emscripten runtime:
// 1. Pre-run
@ -14,10 +20,8 @@ namespace flyff {
// 4. Call main
// 5. Post-run
Neuz::Neuz() {
// 1. Pre-run: There are no pre-runs in this binary.
// 2. Init runtime: `___wasm_call_ctors` is added to on init runtime.
(*flyff::raw::_wasm_call_ctors)();
Neuz::Neuz() : movers(make_ref<MoverMap>(0x00032968)) {
}
Neuz& Neuz::instance() {
@ -27,6 +31,9 @@ Neuz& Neuz::instance() {
wasm_rt_init();
// Initialise the client.
flyff::raw::init();
// 1. Pre-run: There are no pre-runs in this binary.
// 2. Init runtime: `___wasm_call_ctors` is added to on init runtime.
(*flyff::raw::_wasm_call_ctors)();
// Only init the module once.
initialised = true;
}
@ -42,4 +49,11 @@ int Neuz::main() const {
// TODO: Fix arguments.
return (*flyff::raw::main)(0, 0);
}
Pointer<Mover> Neuz::get_mover(const flyff::MoverID& id) const {
const auto& node = Pointer<MoverMap::Node>(raw::w2c_f208(id));
return node ? node->mover : nullptr;
}
} // namespace flyff

View File

@ -3,6 +3,8 @@
#include <cstdint>
#include <client.h>
#include "Mover.h"
namespace flyff {
namespace raw {
static const auto& init = &Z_client_init;
@ -24,9 +26,13 @@ namespace flyff {
Neuz(Neuz const&) = delete;
void operator=(Neuz const&) = delete;
MoverMap const& movers;
// Execute the `main` function of the flyff-client.
int main() const;
Pointer<Mover> get_mover(const flyff::MoverID& id) const;
// Easy to implement: Just send a EmscriptenKeyboardEvent* to the registered function.
// void send_keydown();
// void send_keyup();

View File

@ -1,8 +1,9 @@
#pragma once
#include <cstddef>
#include <string>
#include <iostream>
#include <string>
#include <vector>
template <const size_t kSize>
class MinimumSize {
@ -12,23 +13,13 @@ class MinimumSize {
#define STR_MERGE_IMPL(a, b) a##b
#define STR_MERGE(a, b) STR_MERGE_IMPL(a, b)
#define MAKE_PAD(size) STR_MERGE(_pad, __COUNTER__)[size]
#define DEFINE_MEMBER(offset, type, name) struct {unsigned char MAKE_PAD(offset); type name;}
#define DEFINE_MEMBER(offset, type, name) \
struct { \
unsigned char MAKE_PAD(offset); \
type name; \
}
namespace flyff {
namespace detail {
template<typename I, typename O>
struct ArrowProxy {
I value;
O* operator->() {
return reinterpret_cast<O*>(&value);
}
O& operator*() {
return *(operator->)();
}
};
}
class Memory {
wasm_rt_memory_t** memory;
@ -36,17 +27,20 @@ class Memory {
public:
explicit Memory(wasm_rt_memory_t** mem) : memory(mem) {}
const uintptr_t to_outside(const uintptr_t& offset) const {
uintptr_t start() const {
return reinterpret_cast<uintptr_t>((*memory)->data);
}
uintptr_t to_inside(const uintptr_t offset) { return offset - start(); }
uintptr_t to_outside(const uintptr_t offset) {
return reinterpret_cast<uintptr_t>(&(*memory)->data[offset]);
}
template<typename T>
T* to_outside(T* offset) const {
template <typename T>
T* to_outside(const T* offset) {
return reinterpret_cast<T*>(
to_outside(
reinterpret_cast<uintptr_t>(offset)
)
);
to_outside(reinterpret_cast<uintptr_t>(offset)));
}
};
@ -56,15 +50,76 @@ class Table {};
static const auto& kTable = &Z_clientZ_table;
template<typename T>
T& make_ref(uintptr_t address) {
return *reinterpret_cast<T*>(kMemory.to_outside(address));
}
template <typename T>
class Pointer {
class __attribute__((packed)) Pointer {
// Always located INSIDE the flyff module.
uintptr_t offset;
public:
T* operator->() { return reinterpret_cast<T*>(kMemory.to_outside(offset)); }
T* as_outside() const {
return reinterpret_cast<T*>(kMemory.to_outside(offset));
}
public:
Pointer() : offset(0) { }
Pointer(T* ptr) : offset(kMemory.to_inside(reinterpret_cast<uintptr_t>(ptr))) { }
explicit Pointer(uintptr_t ptr) : offset(ptr) { }
Pointer(const Pointer<T>& other) : offset(other.offset) { }
Pointer<T>& operator=(Pointer<T> other) {
offset = other.offset;
return *this;
}
T* operator->() {
return as_outside();
}
T const* operator->() const {
return as_outside();
}
T& operator*() {
return *as_outside();
}
T const& operator*() const {
return *as_outside();
}
explicit operator T*() {
return as_outside();
}
explicit operator T const*() {
return as_outside();
}
explicit operator bool() const {
return offset != NULL;
}
bool operator==(Pointer<T> const& rhs) const { return offset == rhs.offset; }
friend std::ostream& operator<<(std::ostream& os, const Pointer<T>& ptr) {
os << "0x" << reinterpret_cast<void*>(ptr.offset);
return os;
}
};
class String {
// Allocates an unmanaged pointer inside the flyff module.
// template <class T, class... Args>
// Pointer<T> make_pointer(Args&&... args) {
// return Pointer<T>(new T(std::forward<Args>(args)...));
// }
class __attribute__((packed)) String {
union {
DEFINE_MEMBER(0x0, char*, text);
DEFINE_MEMBER(0x4, size_t, length);
@ -76,7 +131,7 @@ class String {
const MinimumSize<12> __size;
};
public:
public:
// This has to be const until strings can allocate...
operator const std::string() const {
auto copy = *this;
@ -86,7 +141,7 @@ public:
copy.text = kMemory.to_outside(text);
// TODO: Probably offset capacity too.
}
return *reinterpret_cast<std::string*>(&copy);
}
@ -96,9 +151,44 @@ public:
return os;
}
};
typedef unsigned long long MoverID;
static_assert(sizeof(String) == sizeof(std::string),
"String needs to be the same size as std::string");
template<typename T>
class __attribute__((packed)) Vector {
Pointer<T> begin;
Pointer<T> end;
Pointer<T> capacity;
public:
typedef T* iterator;
typedef const T* const_iterator
// This has to be const until strings can allocate...
T* begin() {
return begin;
}
T* end() {
return end;
}
// friend std::ostream& operator<<(std::ostream& os, const String& str) {
// const std::string conv = str;
// os << conv;
// return os;
// }
};
static_assert(sizeof(Vector<char>) == sizeof(std::vector<char>),
"Vector needs to be the same size as std::vector");
class HashMap {
};
}; // namespace flyff

26
flyff-api/src/item.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "core.h"
namespace flyff {
struct __attribute__((packed)) ItemProperty {
union {
DEFINE_MEMBER(0, const flyff::String, model);
DEFINE_MEMBER(12, uint32_t, id);
DEFINE_MEMBER(156, const flyff::String, name);
DEFINE_MEMBER(168, const flyff::String, icon);
DEFINE_MEMBER(180, const flyff::String, description);
};
};
struct __attribute__((packed)) InventoryItem {
union {
DEFINE_MEMBER(36, Pointer<ItemProperty>, item);
DEFINE_MEMBER(60, uint32_t, index);
DEFINE_MEMBER(64, uint32_t, quantity);
};
};
} // namespace flyff

View File

@ -7,6 +7,11 @@ namespace flyff {
struct Vector3 {
float x, y, z;
friend std::ostream& operator<<(std::ostream& os, const Vector3& pos) {
os << pos.x << ", " << pos.y << ", " << pos.z;
return os;
}
};
struct Matrix4 {

View File

@ -21,6 +21,7 @@ set_target_properties(
fugg-client
PROPERTIES LINK_FLAGS "\
-lembind \
-g \
-sINITIAL_MEMORY=2048MB \
-fsanitize=undefined \
-sASSERTIONS=1 \

View File

@ -45,7 +45,7 @@ const flyff::String get_text(const std::string& id) {
return *reinterpret_cast<flyff::String*>(result.get());
}
void show_message(const std::string& message, const flyff::Color& color) {
void show_message(const std::string& message, const flyff::Color& color = { 0xFF, 0xFF, 0xFF }) {
// Copy the message into a new auto ptr
auto message_ = std::make_unique<fugg::String>(message);
auto color_ = std::make_unique<flyff::Color>(color);
@ -208,9 +208,26 @@ void write_chat_message(const std::string& message) {
send_packet(0x2000, packet);
}
bool is_running = false;
flyff::Vector3 start_position = { 0 };
void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent* event, void* user_data) {
// [
if(event->keyCode == 219) {
// BracketLeft
if(event->keyCode == 219 && is_running == false) {
const auto& player_pointer = fugg::module_ref<uintptr_t>(0x0003546c);
if(player_pointer == 0)
return;
const auto& player = fugg::module_ref<flyff::Mover>(player_pointer);
start_position = { player.position.x, player.position.y, player.position.z };
std::cout << "Start botting at " << start_position << std::endl;
is_running = true;
show_message("Bot started", { 0xFF, 0xFF, 0xFF });
// write_chat_message("Hi, how are you doing?");
// send_logout();
// show_message(get_text("ids_textclient_fly_noskill"), { 0xFF, 0xFF, 0xFF });
@ -220,14 +237,7 @@ void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent* event, void* u
// Some aibatt NW of Flaris
// set_target(0x069f7df5);
// interact_target(1);
const auto& player_pointer = fugg::module_ref<uintptr_t>(0x0003546c);
if(player_pointer == 0)
return;
const auto& player = fugg::module_ref<flyff::Mover>(player_pointer);
const auto& player_object_id = fugg::module_ref<flyff::MoverID>(0x00034598);
// std::stringstream str;
// str << "Position: "
@ -248,69 +258,327 @@ void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent* event, void* u
// move_to(6968.38, 100.011, 3328.86);
// send_motion_packet(0x404, 0x04, 535);
// send_motion_packet(0x404, 0x11, 1);
}
if(event->keyCode == 221) {
if(is_running == true) {
is_running = false;
show_message("Bot stopped", { 0xFF, 0xFF, 0xFF });
} else {
const auto& player_pointer = fugg::module_ref<uintptr_t>(0x0003546c);
if(player_pointer == 0)
return;
// const auto& player = fugg::module_ref<flyff::Mover>(player_pointer);
// for(flyff::InventoryItem* item : player.inventory) {
// std::cout << get_text(item.item->name) << std::endl;
// }
}
}
}
flyff::Pointer<flyff::Mover> attack_next_target(flyff::Vector3 const& start_position) {
const auto& neuz = flyff::Neuz::instance();
const auto& player_pointer = fugg::module_ref<uintptr_t>(0x0003546c);
if(player_pointer == 0)
return nullptr;
auto& player = fugg::module_ref<flyff::Mover>(player_pointer);
const auto& player_object_id = fugg::module_ref<flyff::MoverID>(0x00034598);
float lowest_distance = std::numeric_limits<float>::max();
flyff::Pointer<flyff::Mover> closest = nullptr;
flyff::MoverID closest_hash;
auto current = neuz.movers.first;
do {
const auto& mover = current->mover;
auto dx = std::abs(mover->position.x - start_position.x);
auto dy = std::abs(mover->position.y - start_position.y) * 10;
auto dz = std::abs(mover->position.z - start_position.z);
auto squared = (dx * dx) + (dy * dy) + (dz * dz);
auto d = std::sqrt(squared);
struct __attribute__((packed)) MoverHolder {
uintptr_t next;
unsigned int list_index;
unsigned long long hash;
uintptr_t mover;
};
struct __attribute__((packed)) MoverHolderMap {
uintptr_t buckets;
size_t capacity;
uintptr_t first;
};
const auto& map = fugg::module_ref<MoverHolderMap>(0x00032968);
std::cout << "Got mover map with " << map.capacity << " movers." << std::endl;
float lowest_distance = 999999.f;
uintptr_t closest = 0;
flyff::MoverID closest_hash = 0;
auto current = map.first;
do {
const auto& current_ = fugg::module_ref<MoverHolder>(current);
const auto& mover = fugg::module_ref<flyff::Mover>(current_.mover);
auto dx = mover.position.x - player.position.x;
auto dz = mover.position.z - player.position.z;
auto squared = (dx * dx) + (dz * dz);
auto d = std::sqrt(squared);
if(mover.type == ObjectType::Mover &&
current_.hash != player_object_id &&
!mover.is_dead() &&
d < lowest_distance) {
closest = current_.mover;
closest_hash = current_.hash;
if(mover->type == ObjectType::Mover &&
current->hash != player_object_id &&
!mover->is_dead()) {
// If it's a monster that has me targetted
if(mover->last_attacked_target == player_object_id) {
closest = mover;
closest_hash = current->hash;
std::cout << mover->name << " has attacked me, so I'm attacking" << std::endl;
break;
} else if(d < lowest_distance) {
closest = mover;
closest_hash = current->hash;
lowest_distance = d;
}
current = current_.next;
} while(current != 0);
if(closest != 0) {
const auto& closest_ = fugg::module_ref<flyff::Mover>(closest);
std::cout << closest_.name << " (of type " << closest_.type << ") is closest to the player." << std::endl;
set_target(closest_hash);
interact_target(1);
} else {
std::cout << "There is no one close" << std::endl;
}
current = current->next;
} while(current);
if(closest) {
player.selected_target = closest_hash;
player.move_toward_target = closest_hash;
player.attack_target = closest_hash;
player.follow_target = closest_hash;
set_target(closest_hash);
interact_target(1);
return closest;
}
return nullptr;
}
const flyff::Mover* pickup_next_item() {
const auto& player_pointer = fugg::module_ref<uintptr_t>(0x0003546c);
if(player_pointer == 0)
return nullptr;
auto& player = fugg::module_ref<flyff::Mover>(player_pointer);
const auto& player_object_id = fugg::module_ref<flyff::MoverID>(0x00034598);
struct __attribute__((packed)) MoverHolder {
uintptr_t next;
unsigned int list_index;
unsigned long long hash;
uintptr_t mover;
};
struct __attribute__((packed)) MoverHolderMap {
uintptr_t buckets;
size_t capacity;
uintptr_t first;
};
const auto& map = fugg::module_ref<MoverHolderMap>(0x00032968);
float lowest_distance = 999999.f;
uintptr_t closest = 0;
flyff::MoverID closest_hash = 0;
auto current = map.first;
do {
const auto& current_ = fugg::module_ref<MoverHolder>(current);
const auto& mover = fugg::module_ref<flyff::Mover>(current_.mover);
auto dx = std::abs(player.position.x - mover.position.x);
auto dz = std::abs(player.position.z - mover.position.z);
auto squared = (dx * dx) + (dz * dz);
auto d = std::sqrt(squared);
if(mover.world != 0 &&
mover.set_in_constructor == false &&
mover.type == ObjectType::Item &&
d < lowest_distance) {
closest = current_.mover;
closest_hash = current_.hash;
lowest_distance = d;
}
current = current_.next;
} while(current != 0);
if(closest != 0) {
const auto& closest_ = fugg::module_ref<flyff::Mover>(closest);
player.selected_target = closest_hash;
player.move_toward_target = closest_hash;
player.move_target = closest_.position;
set_target(closest_hash);
interact_target(1);
return &closest_;
}
return nullptr;
}
bool is_in_moverlist(const void* ptr) {
struct __attribute__((packed)) MoverHolder {
uintptr_t next;
unsigned int list_index;
unsigned long long hash;
uintptr_t mover;
};
struct __attribute__((packed)) MoverHolderMap {
uintptr_t buckets;
size_t capacity;
uintptr_t first;
};
const auto& map = fugg::module_ref<MoverHolderMap>(0x00032968);
auto current = map.first;
do {
const auto& current_ = fugg::module_ref<MoverHolder>(current);
const auto& mover = fugg::module_ref<flyff::Mover>(current_.mover);
if(&mover == ptr) {
return true;
}
current = current_.next;
} while(current != 0);
return false;
}
flyff::Pointer<flyff::Mover> current_target;
flyff::Mover const* current_pickup_item = nullptr;
void bot_tick() {
const auto& player_pointer = fugg::module_ref<uintptr_t>(0x0003546c);
if(player_pointer == 0)
return;
const auto& player = fugg::module_ref<flyff::Mover>(player_pointer);
const auto& neuz = flyff::Neuz::instance();
// if(player.selected_target) {
// const auto target = neuz.get_mover(player.selected_target);
// std::cout << "Target: "
// << target->name << " "
// << "(" << target << ")"
// << " has "
// << target->move_toward_target << ", "
// << target->selected_target << ", "
// << target->attack_target << ", "
// << target->follow_target << ", "
// // << target->
// << std::endl;
// }
// if(player.selected_target != 0) {
// auto selected_target = neuz.get_mover(player.selected_target);
// std::cout << "Target: "
// << selected_target->name << " at "
// << selected_target->position.x << ", "
// << selected_target->position.y << ", "
// << selected_target->position.z << std::endl;
// } else {
// std::cout << "Got no target." << std::endl;
// }
// std::cout << get_text("ids_textclient_needsattackitem") << std::endl;
// const auto& neuz = flyff::Neuz::instance();
// auto first = neuz.movers.first;
// do {
// auto& mover = *first->mover;
// std::cout << mover.position << std::endl;
// first = first->next;
// } while(first);
// std::cout << neuz.movers.capacity << std::endl;
// std::cout << reinterpret_cast<void*>(player.if_not_null_needsattackitem) << std::endl;
// std::cout << "Some flags & 4 = " << ((player.movement_flags & 4) != 0) << std::endl;
// std::cout << player.prevents_pickup_emote() << std::endl;
// std::cout << player.pickup_things << std::endl;
// std::cout << get_text("ids_textclient_party_changeitem") << std::endl;
// Party Distribute Item changed to %s.
// std::cout << get_text("ids_textclient_invalid_target_item") << std::endl;
// std::cout << get_text("ids_textclient_sbeve_notuseitem") << std::endl;
// std::cout << get_text("ids_textclient_lackspace") << std::endl;
// Cannot be done.
// You cannot use that item.
// Inventory is full. Please make room and try again.
// std::cout << get_text("ids_textclient_notdrop") << std::endl;
// You cannot drop a premium item on the ground.
// std::cout << get_text("ids_textclient_return_useitem") << std::endl;
// The item loses its potency 4 hours after being equipped if not returned to the inventory . Do you want to use the item?
// std::cout << get_text("ids_textclient_attentioncooltime") << std::endl;
// Please wait a while before using the item again.
// std::cout << get_text("ids_textclient_remove_error") << std::endl;
// That weapon is not possible to remove the element upgrading. Try again after checking.
// std::cout << get_text("ids_textclient_warningccls") << std::endl;
// Your stats will reset after you change your job. Do you want to change your job?
if(is_running) {
if(current_pickup_item != nullptr) {
// std::cout << "Item's World = " << current_pickup_item->world << std::endl;
// There is an item to pick up.
if(is_in_moverlist(current_pickup_item)) {
// Wait
show_message("Waiting for pickup");
} else {
show_message("Picked up, waiting for ready");
if((player.pickup_flags & 0x8ff) == 0 && player.current_animation == 0) {
show_message("Picking up done.");
current_pickup_item = pickup_next_item();
if(current_pickup_item != nullptr) {
show_message("Picking up");
}
}
// Maybe some confirmation...
// send_motion_packet(0x01, *reinterpret_cast<const MotionParam*>(&player.rotation.y));
}
} else if(current_target == nullptr) {
if(player.pickup_things != 1)
return;
current_pickup_item = nullptr;
current_target = attack_next_target(start_position);
if(current_target) {
std::string str = "Attacking ";
str.append(current_target->name);
show_message(str, { 0xFF, 0xFF, 0xFF });
} else {
show_message("Unable to find target.", { 0xFF, 0x00, 0x00 });
}
} else if(current_target->is_dead()) {
current_target = nullptr;
current_pickup_item = pickup_next_item();
if(current_pickup_item != nullptr) {
show_message("Picking up");
}
}
} else {
// Reset current target is not running.
current_target = nullptr;
current_pickup_item = nullptr;
}
}
void before_main_loop() {
bot_tick();
// auto& client = fugg::Client::instance();
// const auto& test = translate("ids_textclient_cannot_dropmoney");