diff --git a/flyff-api/include/flyff.h b/flyff-api/include/flyff.h index 05016b6..63fe922 100644 --- a/flyff-api/include/flyff.h +++ b/flyff-api/include/flyff.h @@ -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); diff --git a/flyff-api/src/Mover.h b/flyff-api/src/Mover.h index 5275e83..188865f 100644 --- a/flyff-api/src/Mover.h +++ b/flyff-api/src/Mover.h @@ -3,22 +3,52 @@ #include #include +#include +#include +#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(static_cast(x) & + static_cast(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, 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; + // } }; -}; \ No newline at end of file +struct MoverMap { + struct __attribute__((packed)) Node { + Pointer next; + unsigned int list_index; + unsigned long long hash; + Pointer mover; + }; + + Pointer buckets; + size_t capacity; + Pointer first; +}; + +}; // namespace flyff \ No newline at end of file diff --git a/flyff-api/src/Neuz.cpp b/flyff-api/src/Neuz.cpp index 094e181..adf2e32 100644 --- a/flyff-api/src/Neuz.cpp +++ b/flyff-api/src/Neuz.cpp @@ -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(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 Neuz::get_mover(const flyff::MoverID& id) const { + const auto& node = Pointer(raw::w2c_f208(id)); + + return node ? node->mover : nullptr; +} + } // namespace flyff \ No newline at end of file diff --git a/flyff-api/src/Neuz.h b/flyff-api/src/Neuz.h index 408d503..30054b0 100644 --- a/flyff-api/src/Neuz.h +++ b/flyff-api/src/Neuz.h @@ -3,6 +3,8 @@ #include #include +#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 get_mover(const flyff::MoverID& id) const; + // Easy to implement: Just send a EmscriptenKeyboardEvent* to the registered function. // void send_keydown(); // void send_keyup(); diff --git a/flyff-api/src/core.h b/flyff-api/src/core.h index cbf4cae..ddaa30b 100644 --- a/flyff-api/src/core.h +++ b/flyff-api/src/core.h @@ -1,8 +1,9 @@ #pragma once #include -#include #include +#include +#include template 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 -struct ArrowProxy { - I value; - - O* operator->() { - return reinterpret_cast(&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((*memory)->data); + } + + uintptr_t to_inside(const uintptr_t offset) { return offset - start(); } + + uintptr_t to_outside(const uintptr_t offset) { return reinterpret_cast(&(*memory)->data[offset]); } - template - T* to_outside(T* offset) const { + template + T* to_outside(const T* offset) { return reinterpret_cast( - to_outside( - reinterpret_cast(offset) - ) - ); + to_outside(reinterpret_cast(offset))); } }; @@ -56,15 +50,76 @@ class Table {}; static const auto& kTable = &Z_clientZ_table; + +template +T& make_ref(uintptr_t address) { + return *reinterpret_cast(kMemory.to_outside(address)); +} + template -class Pointer { +class __attribute__((packed)) Pointer { + // Always located INSIDE the flyff module. uintptr_t offset; - public: - T* operator->() { return reinterpret_cast(kMemory.to_outside(offset)); } + T* as_outside() const { + return reinterpret_cast(kMemory.to_outside(offset)); + } + + public: + Pointer() : offset(0) { } + Pointer(T* ptr) : offset(kMemory.to_inside(reinterpret_cast(ptr))) { } + + explicit Pointer(uintptr_t ptr) : offset(ptr) { } + + Pointer(const Pointer& other) : offset(other.offset) { } + Pointer& operator=(Pointer 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 const& rhs) const { return offset == rhs.offset; } + + friend std::ostream& operator<<(std::ostream& os, const Pointer& ptr) { + os << "0x" << reinterpret_cast(ptr.offset); + return os; + } }; -class String { +// Allocates an unmanaged pointer inside the flyff module. +// template +// Pointer make_pointer(Args&&... args) { +// return Pointer(new T(std::forward(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(©); } @@ -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 +class __attribute__((packed)) Vector { + Pointer begin; + Pointer end; + Pointer 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) == sizeof(std::vector), + "Vector needs to be the same size as std::vector"); + + +class HashMap { + +}; + }; // namespace flyff \ No newline at end of file diff --git a/flyff-api/src/item.h b/flyff-api/src/item.h new file mode 100644 index 0000000..ee3b7d3 --- /dev/null +++ b/flyff-api/src/item.h @@ -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, item); + + DEFINE_MEMBER(60, uint32_t, index); + DEFINE_MEMBER(64, uint32_t, quantity); + }; +}; + +} // namespace flyff \ No newline at end of file diff --git a/flyff-api/src/math.h b/flyff-api/src/math.h index db28c0e..72cf533 100644 --- a/flyff-api/src/math.h +++ b/flyff-api/src/math.h @@ -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 { diff --git a/fugg-client/CMakeLists.txt b/fugg-client/CMakeLists.txt index 6ec7ca8..6738c72 100644 --- a/fugg-client/CMakeLists.txt +++ b/fugg-client/CMakeLists.txt @@ -21,6 +21,7 @@ set_target_properties( fugg-client PROPERTIES LINK_FLAGS "\ -lembind \ + -g \ -sINITIAL_MEMORY=2048MB \ -fsanitize=undefined \ -sASSERTIONS=1 \ diff --git a/fugg-client/src/client.cpp b/fugg-client/src/client.cpp index eaf80ff..d1b80d3 100644 --- a/fugg-client/src/client.cpp +++ b/fugg-client/src/client.cpp @@ -45,7 +45,7 @@ const flyff::String get_text(const std::string& id) { return *reinterpret_cast(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(message); auto color_ = std::make_unique(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(0x0003546c); + if(player_pointer == 0) + return; + + const auto& player = fugg::module_ref(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(0x0003546c); - if(player_pointer == 0) - return; - const auto& player = fugg::module_ref(player_pointer); - const auto& player_object_id = fugg::module_ref(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(0x0003546c); + if(player_pointer == 0) + return; + + // const auto& player = fugg::module_ref(player_pointer); + + // for(flyff::InventoryItem* item : player.inventory) { + // std::cout << get_text(item.item->name) << std::endl; + // } + } + } +} + +flyff::Pointer attack_next_target(flyff::Vector3 const& start_position) { + const auto& neuz = flyff::Neuz::instance(); + + const auto& player_pointer = fugg::module_ref(0x0003546c); + if(player_pointer == 0) + return nullptr; + + auto& player = fugg::module_ref(player_pointer); + const auto& player_object_id = fugg::module_ref(0x00034598); + + float lowest_distance = std::numeric_limits::max(); + flyff::Pointer 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(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(current); - const auto& mover = fugg::module_ref(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(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(0x0003546c); + if(player_pointer == 0) + return nullptr; + + auto& player = fugg::module_ref(player_pointer); + const auto& player_object_id = fugg::module_ref(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(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(current); + const auto& mover = fugg::module_ref(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(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(0x00032968); + + auto current = map.first; + do { + const auto& current_ = fugg::module_ref(current); + const auto& mover = fugg::module_ref(current_.mover); + + if(&mover == ptr) { + return true; + } + + current = current_.next; + } while(current != 0); + + return false; +} + +flyff::Pointer current_target; +flyff::Mover const* current_pickup_item = nullptr; + +void bot_tick() { + const auto& player_pointer = fugg::module_ref(0x0003546c); + if(player_pointer == 0) + return; + + const auto& player = fugg::module_ref(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(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(&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");