From 2fa1a801a37882544100b0298b378865240bca83 Mon Sep 17 00:00:00 2001 From: Knaapchen Date: Wed, 16 Nov 2022 00:02:52 +0100 Subject: [PATCH] Added AttackRange enum Added CObj::IsRangeObj to check if an object is in range Identified more ItemKind2 and ItemKind3's Added get_attack_range to Mover --- flyff-api/CMakeLists.txt | 2 +- flyff-api/src/Neuz.cpp | 12 ++-- flyff-api/src/core.h | 40 ++++++++++- flyff-api/src/item/AttackRange.h | 61 ++++++++++++++++ flyff-api/src/item/ItemKind2.h | 9 ++- flyff-api/src/item/ItemKind3.h | 24 ++++--- flyff-api/src/item/ItemProperty.h | 2 + flyff-api/src/object/mover/Mover.h | 43 +++++++++++- flyff-api/src/object/mover/ParamID.h | 24 +++++++ flyff-client/src/client.c | 4 +- fugg-client/src/Client.cpp | 101 ++++++++++++++++++++++----- fugg-client/src/Client.h | 2 + 12 files changed, 279 insertions(+), 45 deletions(-) create mode 100644 flyff-api/src/item/AttackRange.h diff --git a/flyff-api/CMakeLists.txt b/flyff-api/CMakeLists.txt index 994c35b..0d9e60d 100644 --- a/flyff-api/CMakeLists.txt +++ b/flyff-api/CMakeLists.txt @@ -8,7 +8,7 @@ add_library( "include/flyff.h" src/core.h src/skill/SkillProperty.h - src/object/ObjectProperty.h src/item/ItemKind2.h src/item/ItemKind3.h src/item/Part.h src/item/InventoryItem.h src/object/mover/JobProperty.h src/object/mover/ParamID.h src/object/mover/MoverProperty.h src/object/mover/Rank.h src/object/mover/Element.h) + src/object/ObjectProperty.h src/item/ItemKind2.h src/item/ItemKind3.h src/item/Part.h src/item/InventoryItem.h src/object/mover/JobProperty.h src/object/mover/ParamID.h src/object/mover/MoverProperty.h src/object/mover/Rank.h src/object/mover/Element.h src/item/AttackRange.h) target_include_directories(flyff-api PUBLIC "include") target_link_libraries(flyff-api PUBLIC flyff-client fugg-api) \ No newline at end of file diff --git a/flyff-api/src/Neuz.cpp b/flyff-api/src/Neuz.cpp index a3f1a1f..502aeec 100644 --- a/flyff-api/src/Neuz.cpp +++ b/flyff-api/src/Neuz.cpp @@ -22,17 +22,17 @@ namespace flyff { static constexpr uintptr_t MAYBE_LOGIN_TIME = START_TIME + 8; extern "C" { -// get_mover + // get_mover extern u32 w2c_f208(u64); -// get_text + // get_text extern void w2c_f167(u32, u32); -// show_message + // show_message extern void w2c_f341(u32, u32, u32); -// show_announcement + // show_announcement extern void w2c_f607(u32, u32, u32); -// can_attack_target + // can_attack_target extern u32 w2c_f515(u32, u32); -// CProject::GetSkillProp() + // CProject::GetSkillProp() extern u32 w2c_f440(u32, u32); }; diff --git a/flyff-api/src/core.h b/flyff-api/src/core.h index ebcfa68..28003da 100644 --- a/flyff-api/src/core.h +++ b/flyff-api/src/core.h @@ -126,6 +126,28 @@ namespace flyff { // return Pointer(new T(std::forward(args)...)); // } + namespace detail { + template + struct ArrowProxy { + I value; + + O *operator->() { + return reinterpret_cast(&value); + } + + O &operator*() { + return *(operator->)(); + } + }; + + template + struct RawVector { + T *begin; + T *end; + T *capacity; + }; + }; + class __attribute__((packed)) String { union { DEFINE_MEMBER(0x0, char*, text); @@ -167,6 +189,8 @@ namespace flyff { template class __attribute__((packed)) Vector { + using RawVector = detail::RawVector; + // const Pointer& begin_; // const Pointer& end_; // const Pointer& capacity_; @@ -180,7 +204,17 @@ namespace flyff { typedef T *iterator; typedef const T *const_iterator; - // This has to be const until strings can allocate... + detail::ArrowProxy> operator->() const { + auto temp = RawVector{ + .begin = static_cast(begin_), + .end = static_cast(end_), + .capacity = static_cast(capacity_), + }; + + return {temp}; + } + + // This has to be const until vectors can allocate... T *begin() const { return static_cast(begin_); } @@ -190,7 +224,9 @@ namespace flyff { } size_t size() const { - return static_cast(end_ - begin_); + return static_cast( + static_cast(end_) - static_cast(begin_) + ); } }; diff --git a/flyff-api/src/item/AttackRange.h b/flyff-api/src/item/AttackRange.h new file mode 100644 index 0000000..b059564 --- /dev/null +++ b/flyff-api/src/item/AttackRange.h @@ -0,0 +1,61 @@ +// +// Created by main on 15-11-22. +// + +#ifndef FUGG_ATTACKRANGE_H +#define FUGG_ATTACKRANGE_H + +#include + +namespace flyff { + enum class AttackRange : uint32_t { + // one-handed melee weapon + Short = 1, + // two-handed melee weapon + Long, + // weapons such as whips and spears + Far, + // Shooting Weapon + Range, + // wand distance + Wand, + //shooting weapon + HRange, + // long wand distance + HWand, + }; + + static std::ostream &operator<<(std::ostream &os, const AttackRange &kind) { + switch (kind) { + case AttackRange::Short: + os << "Short"; + break; + case AttackRange::Long: + os << "Long"; + break; + case AttackRange::Far: + os << "Far"; + break; + case AttackRange::Range: + os << "Range"; + break; + case AttackRange::Wand: + os << "Wand"; + break; + case AttackRange::HRange: + os << "HRange"; + break; + case AttackRange::HWand: + os << "HWand"; + break; + default: + os << "AttackRange(" << static_cast(kind) << ")"; + break; + } + + return os; + } +}; + + +#endif //FUGG_ATTACKRANGE_H diff --git a/flyff-api/src/item/ItemKind2.h b/flyff-api/src/item/ItemKind2.h index 9c1369b..558095d 100644 --- a/flyff-api/src/item/ItemKind2.h +++ b/flyff-api/src/item/ItemKind2.h @@ -10,20 +10,16 @@ namespace flyff { enum class ItemKind2 : uint32_t { // from https://api.flyff.com/#tag/item + // Remove those which are identified. // "weapon" // "armor" // "fashion" - // "jewelry" // "flying" // "collector" - // "quest" // "trans" // "fuel" // "booty" - // "arrow" // "charm" - // "recovery" - // "blinkwing" // "firework" // "pickuppet" // "teleportring" @@ -34,7 +30,10 @@ namespace flyff { // "scroll" // "vendorskin" // "raisedpet" + Jewelry = 5, + Arrow = 12, Recovery = 14, + Blinkwing = 15, Quest = 25, }; diff --git a/flyff-api/src/item/ItemKind3.h b/flyff-api/src/item/ItemKind3.h index 5335566..263660e 100644 --- a/flyff-api/src/item/ItemKind3.h +++ b/flyff-api/src/item/ItemKind3.h @@ -19,13 +19,11 @@ namespace flyff { // "board" // "book" // "boots" -// "bow" // "broom" // "car" // "cloak" // "cloth" // "drink" -// "earring" // "elementcard" // "event" // "food" @@ -38,27 +36,19 @@ namespace flyff { // "letter" // "mask" // "mineral" -// "necklace" // "petfeed" // "piercingcard" // "piercingdice" -// "pill" // "protectscroll" // "randomscroll" // "refresher" -// "ring" -// "shield" // "shoes" // "specialstone" -// "staff" // "suit" -// "sword" // "townblinkwing" // "trans" // "upgradedice" -// "wand" // "wings" -// "yoyo" // "gacha" // "globalgacha" // "giftbox" @@ -76,12 +66,24 @@ namespace flyff { // "raisedpet" // "raisedpettransmute" // "harvestglove" + Sword = 200, + Stick = 202, Knuckle = 203, + Wand = 204, + Staff = 205, + Yoyo = 206, + Bow = 207, Shield = 300, - HealthPill = 1404 + + Earring = 500, + Necklace = 501, + Ring = 502, + + + Pill = 1404 }; static std::ostream &operator<<(std::ostream &os, const ItemKind3 &kind) { diff --git a/flyff-api/src/item/ItemProperty.h b/flyff-api/src/item/ItemProperty.h index b5975e5..2653391 100644 --- a/flyff-api/src/item/ItemProperty.h +++ b/flyff-api/src/item/ItemProperty.h @@ -6,6 +6,7 @@ #include "ItemKind2.h" #include "ItemKind3.h" +#include "AttackRange.h" namespace flyff { struct __attribute__((packed)) ItemProperty { @@ -30,6 +31,7 @@ namespace flyff { DEFINE_MEMBER(180, const flyff::String, description); DEFINE_MEMBER(244, const uint32_t, required_level); + DEFINE_MEMBER(256, const AttackRange, attack_range); DEFINE_MEMBER(348, const ItemKind2, item_kind2); DEFINE_MEMBER(352, const ItemKind3, item_kind3); diff --git a/flyff-api/src/object/mover/Mover.h b/flyff-api/src/object/mover/Mover.h index 34bd75d..62aac29 100644 --- a/flyff-api/src/object/mover/Mover.h +++ b/flyff-api/src/object/mover/Mover.h @@ -23,6 +23,15 @@ #include "ParamID.h" #include "MoverProperty.h" + +namespace std { + template + constexpr std::size_t size(const T (&array)[N]) noexcept + { + return N; + } +}; + namespace flyff { namespace raw { extern "C" { @@ -30,6 +39,8 @@ namespace flyff { extern u32 w2c_f411(u32); // w2c_f2454: CCooltimeMgr::CanUse(uint64_t* timers, ItemProperty* prop); extern u32 w2c_f2466(u32, u32); + // CObj::IsRangeObj() + extern u32 w2c_f512(u32 w2c_p0, u32 w2c_p1, f32 w2c_p2); } }; @@ -163,6 +174,8 @@ namespace flyff { DEFINE_MEMBER(1840, int, hitpoints); DEFINE_MEMBER(1848, int, is_grounded); + + DEFINE_MEMBER(1884, float, arrival_range); DEFINE_MEMBER(1888, const float, speed_factor); DEFINE_MEMBER(1804, uint32_t, object_state); @@ -206,7 +219,7 @@ namespace flyff { bool is_in_combat(uint32_t millis = 10000) const; - uint32_t get_param(ParamID index, uint32_t base) const { + uint32_t get_param(ParamID index, uint32_t base = 0) const { Parameter changeParam = get_change_param(index); if (changeParam != 0x7FFFFFFF) return changeParam; @@ -234,7 +247,7 @@ namespace flyff { return raw::w2c_f411(kMemory.to_inside(this)); } - flyff::Pointer get_equipped_item(const flyff::Part &part) { + flyff::Pointer get_equipped_item(const flyff::Part &part) const { using Fn = FunctionPointer; const auto &fn = fugg::function_ref(vtable->get_active_hand_item_prop); @@ -281,6 +294,32 @@ namespace flyff { return fn(kMemory.to_inside(this), 0, 0); } + float get_attack_range(const AttackRange& range) const { + static const float attack_ranges[] = { + 2, 3, 4, 10, 15, 6, 18 + }; + + float attack_range = 0.0f; + + auto attack_range_index = static_cast(range) - 1; + if(attack_range_index < std::size(attack_ranges)) { + attack_range = attack_ranges[attack_range_index]; + } + + if(is_fly() && attack_range <= 3.0) { + attack_range = 3.0; + } + + auto nTmpATR = get_param(ParamID::DstHawkEye); + + return attack_range * (nTmpATR / 100.0 + 1.0); + } + + uint32_t is_object_in_range(const flyff::Pointer& other, float range) { + auto self = flyff::Pointer(this); + + return raw::w2c_f512(static_cast(self), static_cast(other), range); + } // int get_max_hp() const { // return raw::w2c_f410(kMemory.to_inside(this)); diff --git a/flyff-api/src/object/mover/ParamID.h b/flyff-api/src/object/mover/ParamID.h index 69f3901..dcb37d3 100644 --- a/flyff-api/src/object/mover/ParamID.h +++ b/flyff-api/src/object/mover/ParamID.h @@ -15,6 +15,8 @@ namespace flyff { Stamina, Speed = 4, + // DST_HAWKEYE_RATE: Increased attack range modifier. + DstHawkEye = 8, // UnknownSpeed = 9, AdditionalDamage = 35, @@ -22,8 +24,30 @@ namespace flyff { MaxHitPoint = 44, // DST_HP_MAX_RATE MaxHitPointScaling = 53, + +// Max = 82, }; + + static std::ostream &operator<<(std::ostream &os, const ParamID &type) { + switch (type) { + case ParamID::Strength: os << "Strength"; break; + case ParamID::Dexterity: os << "Dexterity"; break; + case ParamID::Intelligence: os << "Intelligence"; break; + case ParamID::Stamina: os << "Stamina"; break; + + case ParamID::Speed: os << "Speed"; break; + + case ParamID::AdditionalDamage: os << "AdditionalDamage"; break; + + case ParamID::MaxHitPoint: os << "MaxHitPoint"; break; + case ParamID::MaxHitPointScaling: os << "MaxHitPointScaling"; break; + + default: os << "ParamID(" << static_cast(type) << ")"; + } + return os; + } + } diff --git a/flyff-client/src/client.c b/flyff-client/src/client.c index 943fbd3..6b9f2eb 100644 --- a/flyff-client/src/client.c +++ b/flyff-client/src/client.c @@ -753,7 +753,7 @@ static void w2c_f508(u32); static u32 w2c_f509(u32); static void w2c_f510(u32, u32); static u32 w2c_f511(u32); -static u32 w2c_f512(u32, u32, f32); +u32 w2c_f512(u32, u32, f32); static u32 w2c_f513(u32); static void w2c_f514(u32); u32 w2c_f515(u32, u32); @@ -142263,7 +142263,7 @@ static u32 w2c_f511(u32 w2c_p0) { return w2c_i0; } -static u32 w2c_f512(u32 w2c_p0, u32 w2c_p1, f32 w2c_p2) { +u32 w2c_f512(u32 w2c_p0, u32 w2c_p1, f32 w2c_p2) { u32 w2c_l3 = 0, w2c_l4 = 0; f32 w2c_l5 = 0; FUNC_PROLOGUE; diff --git a/fugg-client/src/Client.cpp b/fugg-client/src/Client.cpp index 7aa07f6..00c2f52 100644 --- a/fugg-client/src/Client.cpp +++ b/fugg-client/src/Client.cpp @@ -145,6 +145,10 @@ namespace fugg { ); } + void Client::send_correction(const double &speed) const { + send_motion_packet(0x18, *reinterpret_cast(&speed)); + } + void Client::send_chat_message(const std::string &message) const { std::vector packet; // Message length @@ -172,6 +176,7 @@ namespace fugg { #include #include +#include flyff::Pointer @@ -477,7 +482,10 @@ flyff::MoverID find_closest_target() { if (mover->type == flyff::ObjectType::Mover && current->hash != neuz.player_object_id && + // Should not be in dead state. !mover->is_dead() && + // Only attack enemies that are not attacking someone else. + mover->enemies->empty() && // Only attack ground mobs. mover->is_grounded && // Only attack same mobs. @@ -542,19 +550,58 @@ flyff::MoverID find_next_item() { return 0; } -void interact(flyff::MoverID id, uint32_t action) { +void pickup_target(flyff::MoverID id, uint32_t action) { const auto &client = fugg::Client::instance(); if (!client.player) return; - client.player->selected_target = id; - client.send_set_target(id); + if (client.player->selected_target != id) { + client.player->selected_target = id; + client.send_set_target(id); + } - client.player->move_toward_target = id; client.send_interact_target(action); } +void attack_target(flyff::MoverID id) { + const auto &client = fugg::Client::instance(); + assert(client.player); + + const auto& target = client.get_mover(id); + assert(target); + + if (client.player->selected_target != id) { + client.player->selected_target = id; + client.send_set_target(id); + } + + if (client.player->attack_target != id) { + auto item = client.player->get_equipped_item(flyff::Part::MainHand); + assert(item); + + auto range = client.player->get_attack_range(item->attack_range); + auto result = client.player->is_object_in_range(target, range); + + client.show_message("Range=" + std::to_string(range) + ", result=" + std::to_string(result)); + + if(result == 0) { + client.player->move_toward_target = id; + client.player->arrival_range = range; + } + + client.player->attack_target = id; + client.send_interact_target(1); + + // Maybe Wand too... + if(item->item_kind3 == flyff::ItemKind3::Bow) { + // Release the 'charged' shot immediatly. + client.send_motion_packet(0x15); + } + } + +} + flyff::MoverID current_target = 0; flyff::MoverID current_pickup_item = 0; @@ -619,7 +666,7 @@ void attacker_script() { const auto &item_property = items[i].property; if (item_property && player->can_use_item(item_property) && - item_property->item_kind3 == flyff::ItemKind3::HealthPill) { + item_property->item_kind3 == flyff::ItemKind3::Pill) { auto hp = client.player->get_hp(); auto max_hp = client.player->get_max_hp(); // How much HP is left over after healing with this item. @@ -633,7 +680,7 @@ void attacker_script() { } } - if(pill_item) { + if (pill_item) { std::string str = "Eating "; str.append(neuz.get_text(pill_item->property->name)); str.append(" because food item was not ready."); @@ -645,11 +692,27 @@ void attacker_script() { } } + // Check if my current target is being attacked by another player. + { + auto monster = neuz.get_mover(current_target); + if (monster && !monster->enemies->empty()) { + auto is_player_monster_enemy = + std::find(monster->enemies.begin(), monster->enemies.end(), client.player->object_id) != nullptr; + // If the player is not in the list of enemies, we can not attack the monster. + if (!is_player_monster_enemy) { + client.show_message("Uh oh, you are being killstealed.", {0xFF, 0, 0}); - if (neuz.player->get_move_state() == flyff::Object::MoveState::Stand && + current_target = 0; + neuz.player->selected_target = 0; + client.send_clear_target(); + return; + } + } + } + + if ((neuz.player->get_move_state() == flyff::Object::MoveState::Stand || + neuz.player->get_move_state() == flyff::Object::MoveState::PickUp) && !neuz.player->is_attacking()) { - - // Check buffs. static constexpr flyff::SkillID buffs_to_check[] = { // flyff::SkillID::MentalSign, @@ -722,10 +785,10 @@ void attacker_script() { if (item && !item->is_despawned && neuz.player->move_toward_target == 0) { // Pickup - interact(item->object_id, 1); - } else if (monster && !monster->is_dead() && neuz.player->move_toward_target == 0) { + pickup_target(item->object_id, 1); + } else if (monster && !monster->is_dead() && neuz.player->attack_target == 0) { // Attack - interact(monster->object_id, 1); + attack_target(monster->object_id); // neuz.player->attack_target = monster->object_id; } else if ((!item || item->is_despawned) && (!monster || monster->is_dead())) { @@ -741,14 +804,14 @@ void attacker_script() { str.append(" because it is my enemy."); current_target = id; - interact(id, 1); + attack_target(id); neuz.show_message(str); return; } } // Only pickup if player has no pickup pet spawned. - if(client.player->pickup_pet_inventory_index == -1) { + if (client.player->pickup_pet_inventory_index == -1) { // No item target and no attack target current_pickup_item = find_next_item(); item = neuz.get_mover(current_pickup_item); @@ -760,7 +823,7 @@ void attacker_script() { std::string str = "Picking up "; str.append(neuz.get_text(prop->name)); - interact(item->object_id, 1); + pickup_target(item->object_id, 1); neuz.show_message(str); return; @@ -773,7 +836,7 @@ void attacker_script() { std::string str = "Attacking "; str.append(monster->name); - interact(current_target, 1); + attack_target(current_target); neuz.show_message(str); } else { @@ -919,7 +982,13 @@ void after_main_loop() { // std::cout << "Speed: " << client.player->get_speed() << std::endl; // } + if (client.player) { + auto monster = client.get_mover(client.player->selected_target); + if (monster) { + + } + } switch (script) { case ScriptType::Attacker: diff --git a/fugg-client/src/Client.h b/fugg-client/src/Client.h index b761bc8..7e5c95b 100644 --- a/fugg-client/src/Client.h +++ b/fugg-client/src/Client.h @@ -38,6 +38,8 @@ namespace fugg { void send_use_skill(const flyff::SkillID &id, unsigned long long unknown = -1) const; void send_follow_target(const unsigned long long &id) const; + void send_correction(const double& speed) const; + void send_move_to(const double &x, const double &y, const double &z) const; void send_chat_message(const std::string &message) const;