diff --git a/flyff-api/src/item/ItemKind3.h b/flyff-api/src/item/ItemKind3.h index 1c58bd8..aa3e701 100644 --- a/flyff-api/src/item/ItemKind3.h +++ b/flyff-api/src/item/ItemKind3.h @@ -84,7 +84,9 @@ namespace flyff { Necklace = 501, Ring = 502, - + Refresher = 1400, + VitalDrink = 1401, + Food = 1402, Pill = 1404 }; diff --git a/flyff-api/src/item/ItemProperty.h b/flyff-api/src/item/ItemProperty.h index 2653391..fe1daf6 100644 --- a/flyff-api/src/item/ItemProperty.h +++ b/flyff-api/src/item/ItemProperty.h @@ -21,7 +21,8 @@ namespace flyff { DEFINE_MEMBER(0, const flyff::String, model); DEFINE_MEMBER(12, const uint32_t, id); - DEFINE_MEMBER(60, const uint32_t, heal_hp); + DEFINE_MEMBER(60, const uint32_t, recovery); + DEFINE_MEMBER(96, const uint32_t, effective_up_to); // Has to be bigger that 0 to be considered for cooldown. DEFINE_MEMBER(112, const int32_t, cooldown_time); @@ -52,6 +53,32 @@ namespace flyff { return false; } + bool is_refresher() const { + static const uint32_t RECOVERY_SUB_CATEGORY_START = 1400; + + if (item_kind2 == ItemKind2::Recovery) { + const uint32_t cd = static_cast(item_kind3) - RECOVERY_SUB_CATEGORY_START; + if (cd < 5) { + return RECOVERY_COOLDOWN_INDEX[cd] == COOLDOWN_REFRESHER; + } + } + + return false; + } + + bool is_vitaldrink() const { + static const uint32_t RECOVERY_SUB_CATEGORY_START = 1400; + + if (item_kind2 == ItemKind2::Recovery) { + const uint32_t cd = static_cast(item_kind3) - RECOVERY_SUB_CATEGORY_START; + if (cd < 5) { + return RECOVERY_COOLDOWN_INDEX[cd] == COOLDOWN_VITALDRINK; + } + } + + return false; + } + bool is_quest() const { return item_kind2 == ItemKind2::Quest; } }; diff --git a/flyff-api/src/object/mover/Mover.h b/flyff-api/src/object/mover/Mover.h index c80fd5d..206c111 100644 --- a/flyff-api/src/object/mover/Mover.h +++ b/flyff-api/src/object/mover/Mover.h @@ -66,6 +66,7 @@ namespace flyff { DEFINE_MEMBER(144, const u32, get_max_origin_hp); DEFINE_MEMBER(148, const u32, get_max_origin_mp); + DEFINE_MEMBER(152, const u32, get_max_origin_fp); const MinimumSize<220> _size; }; @@ -154,6 +155,7 @@ namespace flyff { DEFINE_MEMBER(880, const uint32_t, locked_inventory_slots); DEFINE_MEMBER(904, uint32_t, if_not_null_needsattackitem); + DEFINE_MEMBER(908, uint32_t, fatiguepoints); DEFINE_MEMBER(920, ParameterValue, adjusted_parameters[MAX_PARAM]); DEFINE_MEMBER(1248, ParameterValue, changed_parameters[MAX_PARAM]); @@ -183,6 +185,7 @@ namespace flyff { DEFINE_MEMBER(1820, uint32_t, server_tick); DEFINE_MEMBER(1840, int, hitpoints); + DEFINE_MEMBER(1844, int, manapoints); DEFINE_MEMBER(1848, int, is_grounded); @@ -247,9 +250,6 @@ namespace flyff { ParameterValue get_change_param(ParamID index) const; - uint32_t get_hp() const { - return raw::get_hitpoint(kMemory.to_inside(this)); - } flyff::Pointer get_equipped_item(const flyff::Part &part) const { using Fn = FunctionPointer; @@ -273,6 +273,18 @@ namespace flyff { return src_speed <= 0 ? 0 : src_speed; } + uint32_t get_hp() const { + return hitpoints ^ adj_param; + } + + uint32_t get_mp() const { + return manapoints ^ adj_param; + } + + uint32_t get_fp() const { + return fatiguepoints; + } + uint32_t get_max_hp() const { float factor = 1.0f; auto result = get_param(ParamID::MaxHitPoint, get_max_origin_hp()); @@ -284,6 +296,28 @@ namespace flyff { return result; } + uint32_t get_max_mp() const { + float factor = 1.0f; + auto result = get_param(ParamID::MaxManaPoint, get_max_origin_mp()); + auto percent = get_param(ParamID::MaxManaPointScaling, 0); + + factor += static_cast(percent) / 100.f; + result = static_cast(result) * factor; + + return result; + } + + uint32_t get_max_fp() const { + float factor = 1.0f; + auto result = get_param(ParamID::MaxFatiguePoint, get_max_origin_fp()); + auto percent = get_param(ParamID::MaxFatiguePointScaling, 0); + + factor += static_cast(percent) / 100.f; + result = static_cast(result) * factor; + + return result; + } + uint32_t get_hp_percent(int percent = 100) const { auto max = get_max_hp(); if (max == 0) return 0; @@ -291,6 +325,20 @@ namespace flyff { return get_hp() * percent / max; } + uint32_t get_mp_percent(int percent = 100) const { + auto max = get_max_mp(); + if (max == 0) return 0; + + return get_mp() * percent / max; + } + + uint32_t get_fp_percent(int percent = 100) const { + auto max = get_max_fp(); + if (max == 0) return 0; + + return get_fp() * percent / max; + } + uint32_t get_max_origin_hp() const { using Fn = FunctionPointer; const auto &fn = fugg::function_ref(vtable->get_max_origin_hp); @@ -298,6 +346,20 @@ namespace flyff { return fn(kMemory.to_inside(this), 0, 0); } + uint32_t get_max_origin_mp() const { + using Fn = FunctionPointer; + const auto &fn = fugg::function_ref(vtable->get_max_origin_mp); + + return fn(kMemory.to_inside(this), 0, 0); + } + + uint32_t get_max_origin_fp() const { + using Fn = FunctionPointer; + const auto &fn = fugg::function_ref(vtable->get_max_origin_fp); + + 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 diff --git a/flyff-api/src/object/mover/ParamID.h b/flyff-api/src/object/mover/ParamID.h index dcb37d3..7062f17 100644 --- a/flyff-api/src/object/mover/ParamID.h +++ b/flyff-api/src/object/mover/ParamID.h @@ -22,8 +22,15 @@ namespace flyff { AdditionalDamage = 35, // DST_HP_MAX MaxHitPoint = 44, + // DST_MP_MAX + MaxManaPoint, + // DST_FP_MAX + MaxFatiguePoint, + // DST_HP_MAX_RATE MaxHitPointScaling = 53, + MaxManaPointScaling, + MaxFatiguePointScaling, // Max = 82, }; diff --git a/flyff-api/src/skill/SkillID.h b/flyff-api/src/skill/SkillID.h index cf844cf..282e983 100644 --- a/flyff-api/src/skill/SkillID.h +++ b/flyff-api/src/skill/SkillID.h @@ -4,8 +4,13 @@ namespace flyff { enum class SkillID : uint32_t { - // Assist skills - + /* Mercenary */ + SwordMastery = 7, + Protect = 9, + BlazingSword = 108, + EmpowerWeapon = 111, + HeartOfFury = 404, + /* Assist */ Haste = 20, Heal = 44, Resurrection = 45, @@ -27,7 +32,7 @@ namespace flyff { static std::ostream &operator<<(std::ostream &os, const SkillID &type) { - os << static_cast(type); + os << "SkillID(" << static_cast(type) << ")"; return os; } }; // namespace flyff \ No newline at end of file diff --git a/fugg-client/src/Client.cpp b/fugg-client/src/Client.cpp index 8c241da..3d78511 100644 --- a/fugg-client/src/Client.cpp +++ b/fugg-client/src/Client.cpp @@ -472,6 +472,15 @@ void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent *event, void *u } } + + std::cout << "Player has the following " << client.player->skills->size() << " skills" << std::endl; + for(const auto& skill : client.player->skills) { + const auto& property = fugg::Client::get_skill_property(skill.id); + if(property) { + std::cout << client.get_text(property->name) << " "; + } + std::cout << "(" << skill.id << ") at level " << skill.level << std::endl; + } } } } @@ -642,7 +651,7 @@ void attacker_script() { 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. - int32_t difference = max_hp - (hp + item_property->heal_hp); + int32_t difference = max_hp - (hp + item_property->recovery); if (difference >= 0 && std::abs(difference) < std::abs(smallest_difference)) { food_item = flyff::Pointer(&items[i]); @@ -673,7 +682,7 @@ void attacker_script() { 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. - int32_t difference = max_hp - (hp + item_property->heal_hp); + int32_t difference = max_hp - (hp + item_property->recovery); if (difference >= 0 && std::abs(difference) < std::abs(smallest_difference)) { pill_item = flyff::Pointer(&items[i]); @@ -688,12 +697,76 @@ void attacker_script() { str.append(neuz.get_text(pill_item->property->name)); str.append(" because food item was not ready."); - neuz.show_message(str); + neuz.show_message(str, {0x7F, 0, 0}); client.send_use_item_in_inventory(pill_item->index); } } + } + if (neuz.player->get_mp_percent() != 100) { + auto mana_item = flyff::Pointer(nullptr); + auto items = player->inventory.begin(); + int32_t smallest_difference = std::numeric_limits::max(); + for (auto i = 0; i < player->get_inventory_slots(); i++) { + const auto &item_property = items[i].property; + if (item_property && + player->can_use_item(item_property) && + item_property->is_refresher()) { + auto mp = client.player->get_mp(); + auto max_mp = client.player->get_max_mp(); + // How much HP is left over after healing with this item. + int32_t difference = max_mp - (mp + item_property->recovery); + + if (difference >= 0 && std::abs(difference) < std::abs(smallest_difference)) { + mana_item = flyff::Pointer(&items[i]); + smallest_difference = difference; + } + } + } + + if (mana_item) { + std::string str = "Using "; + str.append(neuz.get_text(mana_item->property->name)); + + neuz.show_message(str, {0, 0, 0x7F}); + + client.send_use_item_in_inventory(mana_item->index); + } + } + + std::cout << "Player has " << neuz.player->get_mp() << " / " << neuz.player->get_max_mp() << " MP" << std::endl; + std::cout << "Player has " << neuz.player->get_fp() << " / " << neuz.player->get_max_fp() << " FP" << std::endl; + + if (neuz.player->get_fp_percent() != 100) { + auto fatigue_item = flyff::Pointer(nullptr); + auto items = player->inventory.begin(); + int32_t smallest_difference = std::numeric_limits::max(); + for (auto i = 0; i < player->get_inventory_slots(); i++) { + const auto &item_property = items[i].property; + if (item_property && + player->can_use_item(item_property) && + item_property->is_vitaldrink()) { + auto fp = client.player->get_fp(); + auto max_fp = client.player->get_max_fp(); + // How much HP is left over after healing with this item. + int32_t difference = max_fp - (fp + item_property->recovery); + + if (difference >= 0 && std::abs(difference) < std::abs(smallest_difference)) { + fatigue_item = flyff::Pointer(&items[i]); + smallest_difference = difference; + } + } + } + + if (fatigue_item) { + std::string str = "Drinking "; + str.append(neuz.get_text(fatigue_item->property->name)); + + neuz.show_message(str, {0, 0x7F, 0}); + + client.send_use_item_in_inventory(fatigue_item->index); + } } // Check if my current target is being attacked by another player. { @@ -701,7 +774,9 @@ void attacker_script() { if (monster && !monster->enemies->empty()) { auto is_player_monster_enemy = std::find_if(monster->enemies.begin(), monster->enemies.end(), - [&client](const flyff::Mover::HitInfo &x) { return x.object_id == client.player->object_id; }) != + [&client](const flyff::Mover::HitInfo &x) { + return x.object_id == client.player->object_id; + }) != monster->enemies.end(); // If the player is not in the list of enemies, we can not attack the monster. if (!is_player_monster_enemy) { @@ -788,7 +863,16 @@ void attacker_script() { client.send_use_skill(flyff::SkillID::Asmodeus); return; } -// } + } else if(neuz.player->get_job() == flyff::JobID::Knight) { + const auto &off_hand = player->get_equipped_item(flyff::Part::Shield); + + if (off_hand && off_hand->item_kind3 == flyff::ItemKind3::Shield) { + // Only check for the buff if player is wearing a shield. + if (!check_rebuff(neuz.player, neuz.player, {flyff::SkillID::HeartOfFury}).empty()) { + client.send_use_skill(flyff::SkillID::HeartOfFury); + return; + } + } } if (item && !item->is_despawned && neuz.player->move_toward_target == 0) {