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
This commit is contained in:
Knaapchen 2022-11-16 00:02:52 +01:00
parent 0899e2ed3a
commit 2fa1a801a3
12 changed files with 279 additions and 45 deletions

View File

@ -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)

View File

@ -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);
};

View File

@ -126,6 +126,28 @@ namespace flyff {
// return Pointer<T>(new T(std::forward<Args>(args)...));
// }
namespace detail {
template<typename I, typename O>
struct ArrowProxy {
I value;
O *operator->() {
return reinterpret_cast<O *>(&value);
}
O &operator*() {
return *(operator->)();
}
};
template<typename T>
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<typename T>
class __attribute__((packed)) Vector {
using RawVector = detail::RawVector<T>;
// const Pointer<T>& begin_;
// const Pointer<T>& end_;
// const Pointer<T>& 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<RawVector, std::vector<T>> operator->() const {
auto temp = RawVector{
.begin = static_cast<T *>(begin_),
.end = static_cast<T *>(end_),
.capacity = static_cast<T *>(capacity_),
};
return {temp};
}
// This has to be const until vectors can allocate...
T *begin() const {
return static_cast<T *>(begin_);
}
@ -190,7 +224,9 @@ namespace flyff {
}
size_t size() const {
return static_cast<size_t>(end_ - begin_);
return static_cast<size_t>(
static_cast<flyff::MoverID *>(end_) - static_cast<flyff::MoverID *>(begin_)
);
}
};

View File

@ -0,0 +1,61 @@
//
// Created by main on 15-11-22.
//
#ifndef FUGG_ATTACKRANGE_H
#define FUGG_ATTACKRANGE_H
#include <cstdint>
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<unsigned>(kind) << ")";
break;
}
return os;
}
};
#endif //FUGG_ATTACKRANGE_H

View File

@ -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,
};

View File

@ -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) {

View File

@ -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);

View File

@ -23,6 +23,15 @@
#include "ParamID.h"
#include "MoverProperty.h"
namespace std {
template <class T, std::size_t N>
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<flyff::ItemProperty> get_equipped_item(const flyff::Part &part) {
flyff::Pointer<flyff::ItemProperty> get_equipped_item(const flyff::Part &part) const {
using Fn = FunctionPointer<u32, u32, u32>;
const auto &fn = fugg::function_ref<Fn>(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<unsigned>(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<Mover>& other, float range) {
auto self = flyff::Pointer<Mover>(this);
return raw::w2c_f512(static_cast<u32>(self), static_cast<u32>(other), range);
}
// int get_max_hp() const {
// return raw::w2c_f410(kMemory.to_inside(this));

View File

@ -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<unsigned>(type) << ")";
}
return os;
}
}

View File

@ -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;

View File

@ -145,6 +145,10 @@ namespace fugg {
);
}
void Client::send_correction(const double &speed) const {
send_motion_packet(0x18, *reinterpret_cast<const unsigned long long *>(&speed));
}
void Client::send_chat_message(const std::string &message) const {
std::vector<uint8_t> packet;
// Message length
@ -172,6 +176,7 @@ namespace fugg {
#include <emscripten/html5.h>
#include <cstddef>
#include <cassert>
flyff::Pointer<flyff::Mover::Buff>
@ -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:

View File

@ -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;