367 lines
11 KiB
C++
367 lines
11 KiB
C++
#pragma once
|
|
|
|
#include <fugg.h>
|
|
|
|
#include <cstddef>
|
|
#include <iterator>
|
|
#include <bitset>
|
|
#include <cmath>
|
|
|
|
#include "core.h"
|
|
#include "item/ItemProperty.h"
|
|
#include "math.h"
|
|
|
|
#include "object/Object.h"
|
|
#include "object/ObjectProperty.h"
|
|
#include "object/ObjectType.h"
|
|
|
|
#include "skill/Skill.h"
|
|
|
|
#include "item/InventoryItem.h"
|
|
#include "item/Part.h"
|
|
|
|
namespace flyff {
|
|
namespace raw {
|
|
extern "C" {
|
|
// w2c_f410: CMover::GetHitPoint(PlayerObject* mover);
|
|
extern u32 w2c_f411(u32);
|
|
// w2c_f2454: CCooltimeMgr::CanUse(uint64_t* timers, ItemProperty* prop);
|
|
extern u32 w2c_f2466(u32, u32);
|
|
}
|
|
};
|
|
|
|
struct __attribute__((packed)) Mover {
|
|
static constexpr uint32_t MAX_PARAM = 0x52;
|
|
|
|
using Parameter = unsigned long;
|
|
|
|
static constexpr Parameter DST_HP_MAX = 0x2c;
|
|
static constexpr Parameter DST_HP_MAX_RATE = 0x35;
|
|
|
|
using ObjectStateFlags = Object::StateFlags;
|
|
using ObjectState = Object::State;
|
|
|
|
struct __attribute__((packed)) Vtable {
|
|
union {
|
|
DEFINE_MEMBER(52, const u32, get_level);
|
|
DEFINE_MEMBER(56, const u32, get_gender);
|
|
|
|
DEFINE_MEMBER(96, const u32, get_active_hand_item_prop);
|
|
|
|
DEFINE_MEMBER(144, const u32, get_max_origin_hp);
|
|
DEFINE_MEMBER(148, const u32, get_max_origin_mp);
|
|
|
|
const MinimumSize<220> _size;
|
|
};
|
|
};
|
|
|
|
struct __attribute__((packed)) Skill {
|
|
union {
|
|
DEFINE_MEMBER(0, const flyff::Skill, id);
|
|
DEFINE_MEMBER(4, const uintptr_t, level);
|
|
};
|
|
};
|
|
|
|
struct __attribute__((packed)) Buff {
|
|
enum class Type {
|
|
Item = 0,
|
|
Skill = 1,
|
|
};
|
|
|
|
struct __attribute__((packed)) Vtable {
|
|
union {
|
|
DEFINE_MEMBER(28, const u32, get_type);
|
|
DEFINE_MEMBER(32, const u32, get_id);
|
|
};
|
|
};
|
|
|
|
union {
|
|
DEFINE_MEMBER(0, const Pointer<Vtable>, vtable);
|
|
|
|
DEFINE_MEMBER(4, const Pointer<ObjectProperty>, property);
|
|
DEFINE_MEMBER(8, const Parameter, param_id);
|
|
|
|
DEFINE_MEMBER(16, const uint64_t, time_instantiated);
|
|
DEFINE_MEMBER(24, const uint64_t, time_total);
|
|
|
|
DEFINE_MEMBER(32, const int, level);
|
|
|
|
DEFINE_MEMBER(36, const bool, is_removed);
|
|
DEFINE_MEMBER(37, const bool, is_sfx);
|
|
};
|
|
|
|
Type get_type() const {
|
|
using Fn = FunctionPointer<Type, u32>;
|
|
const auto &fn = fugg::function_ref<Fn>(vtable->get_type);
|
|
|
|
return fn(kMemory.to_inside(this));
|
|
}
|
|
|
|
int get_id() const {
|
|
using Fn = FunctionPointer<u32, u32>;
|
|
const auto &fn = fugg::function_ref<Fn>(vtable->get_id);
|
|
|
|
return fn(kMemory.to_inside(this));
|
|
}
|
|
|
|
uint64_t get_time_remaining() const;
|
|
};
|
|
|
|
union {
|
|
DEFINE_MEMBER(0x0, const Pointer<Vtable>, vtable);
|
|
|
|
DEFINE_MEMBER(0x18, Matrix4, world_matrix);
|
|
|
|
DEFINE_MEMBER(152, Vector3, position);
|
|
DEFINE_MEMBER(164, Vector3, rotation);
|
|
|
|
DEFINE_MEMBER(188, Pointer<ObjectProperty>, property);
|
|
|
|
DEFINE_MEMBER(212, int, property_id);
|
|
DEFINE_MEMBER(216, bool, is_despawned);
|
|
DEFINE_MEMBER(352, MoverID, object_id);
|
|
|
|
DEFINE_MEMBER(920, Parameter, adjusted_parameters[MAX_PARAM]);
|
|
DEFINE_MEMBER(1248, Parameter, changed_parameters[MAX_PARAM]);
|
|
|
|
DEFINE_MEMBER(1576, Vector<Pointer<Buff>>, buffs);
|
|
|
|
DEFINE_MEMBER(2020, const String, name);
|
|
|
|
DEFINE_MEMBER(1628, Vector3, move_target);
|
|
DEFINE_MEMBER(1688, unsigned long long, last_combat);
|
|
|
|
DEFINE_MEMBER(1728, MoverID, move_toward_target);
|
|
DEFINE_MEMBER(1736, MoverID, last_attacked_target);
|
|
DEFINE_MEMBER(1744, MoverID, selected_target);
|
|
// The id of the target that it hit last.
|
|
DEFINE_MEMBER(1752, MoverID, attack_target);
|
|
DEFINE_MEMBER(1760, MoverID, follow_target);
|
|
// The id, only non-zero when the attack animation plays, of the target it is attacking.
|
|
DEFINE_MEMBER(1768, MoverID, current_attack_target);
|
|
DEFINE_MEMBER(1776, MoverID, platform_standing_on);
|
|
|
|
DEFINE_MEMBER(1816, int, adj_param);
|
|
DEFINE_MEMBER(1820, uint32_t, server_tick);
|
|
|
|
DEFINE_MEMBER(1840, int, hitpoints);
|
|
|
|
DEFINE_MEMBER(1848, int, is_grounded);
|
|
|
|
DEFINE_MEMBER(1792, int, current_animation);
|
|
|
|
DEFINE_MEMBER(204, ObjectType, type);
|
|
|
|
DEFINE_MEMBER(216, uint32_t, existence_check);
|
|
|
|
DEFINE_MEMBER(1804, uint32_t, object_state);
|
|
DEFINE_MEMBER(1808, uint32_t, object_state_flags);
|
|
|
|
DEFINE_MEMBER(904, uint32_t, if_not_null_needsattackitem);
|
|
|
|
DEFINE_MEMBER(192, uintptr_t, world);
|
|
|
|
DEFINE_MEMBER(860, Vector<InventoryItem>, inventory);
|
|
|
|
DEFINE_MEMBER(3324, Timestamp, last_food_time);
|
|
DEFINE_MEMBER(3348, Timestamp, last_refresher_time);
|
|
DEFINE_MEMBER(3356, Timestamp, last_vitaldrink_time);
|
|
|
|
DEFINE_MEMBER(3424, Vector<Skill>, skills);
|
|
|
|
const MinimumSize<4096> size;
|
|
};
|
|
|
|
uint32_t get_skill_level(const flyff::Skill &skill_index) const {
|
|
for (const auto &skill: skills) {
|
|
if (skill_index == skill.id)
|
|
return skill.level;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool can_use_item(const Pointer<ItemProperty> &property) {
|
|
return raw::w2c_f2466(kMemory.to_inside(&last_food_time), static_cast<u32>(property));
|
|
}
|
|
|
|
bool is_in_combat() const;
|
|
|
|
Parameter get_param(uint32_t index, Parameter base) const {
|
|
Parameter changeParam = get_change_param(index);
|
|
if (changeParam != 0x7FFFFFFF)
|
|
return changeParam;
|
|
|
|
Parameter adjustParam = get_adjust_param(index);
|
|
if (adjustParam)
|
|
return base + adjustParam;
|
|
else
|
|
return base;
|
|
}
|
|
|
|
Parameter get_adjust_param(Parameter index) const {
|
|
// Find these values in CMover::Init
|
|
return adjusted_parameters[index] ^ adj_param ^ 0x0554e725 + index * 0x41c64e6d;
|
|
}
|
|
|
|
Parameter get_change_param(Parameter index) const {
|
|
return changed_parameters[index] ^ adj_param ^ index * 0x41c64e6d + 0xb5a52d1a;
|
|
}
|
|
|
|
u32 get_hp() const {
|
|
return raw::w2c_f411(kMemory.to_inside(this));
|
|
}
|
|
|
|
flyff::Pointer<flyff::ItemProperty> get_equipped_item(const flyff::Part &part) {
|
|
using Fn = FunctionPointer<u32, u32, u32>;
|
|
const auto &fn = fugg::function_ref<Fn>(vtable->get_active_hand_item_prop);
|
|
|
|
const auto &ptr = fn(kMemory.to_inside(this), static_cast<uint32_t>(part));
|
|
return flyff::Pointer<flyff::ItemProperty>(ptr);
|
|
}
|
|
|
|
uint32_t get_max_hp() const {
|
|
float factor = 1.0f;
|
|
int result = get_param(DST_HP_MAX, get_max_origin_hp());
|
|
int percent = get_param(DST_HP_MAX_RATE, 0);
|
|
|
|
factor += (float) percent / (float) 100;
|
|
result = static_cast<float>(result) * factor;
|
|
|
|
return result;
|
|
}
|
|
|
|
uint32_t get_hp_percent(int percent = 100) const {
|
|
int max = get_max_hp();
|
|
if (max == 0) return 0;
|
|
|
|
return get_hp() * percent / max;
|
|
}
|
|
|
|
uint32_t get_max_origin_hp() const {
|
|
using Fn = FunctionPointer<u32, u32, u32, u32>;
|
|
const auto &fn = fugg::function_ref<Fn>(vtable->get_max_origin_hp);
|
|
|
|
return fn(kMemory.to_inside(this), 0, 0);
|
|
}
|
|
|
|
|
|
// int get_max_hp() const {
|
|
// return raw::w2c_f410(kMemory.to_inside(this));
|
|
// }
|
|
|
|
uint32_t get_state() const {
|
|
return object_state;
|
|
}
|
|
|
|
uint32_t get_move_state() const {
|
|
return (object_state & ObjectState::MovementAll);
|
|
}
|
|
|
|
uint32_t get_turn_state() const {
|
|
return (object_state & ObjectState::TurnAll);
|
|
}
|
|
|
|
uint32_t get_look_state() const {
|
|
return (object_state & ObjectState::LookAll);
|
|
}
|
|
|
|
uint32_t get_jump_state() const {
|
|
return (object_state & ObjectState::JumpAll);
|
|
}
|
|
|
|
uint32_t get_attack_state() const {
|
|
return (object_state & ObjectState::AtkAll);
|
|
}
|
|
|
|
uint32_t get_damage_state() const {
|
|
return (object_state & ObjectState::DmgAll);
|
|
}
|
|
|
|
uint32_t get_action_state() const {
|
|
return (object_state & ObjectState::ActionAll);
|
|
}
|
|
|
|
bool is_fly() const {
|
|
return (object_state_flags & ObjectStateFlags::Fly) != 0;
|
|
}
|
|
|
|
bool is_dead() const {
|
|
return (object_state & ObjectState::DieAll) != 0;
|
|
}
|
|
|
|
bool is_sit() const {
|
|
return (object_state_flags & ObjectStateFlags::Sit) != 0;
|
|
}
|
|
|
|
// Whether the movement state is running, not if the Mover
|
|
// is actually moving. See `is_moving()`
|
|
bool is_run() const {
|
|
return (object_state_flags & ObjectStateFlags::Walk) == 0;
|
|
}
|
|
|
|
// Whether the movement state is walking, not if the Mover
|
|
// is actually moving. See `is_moving()`
|
|
bool is_walk() const {
|
|
return (object_state_flags & ObjectStateFlags::Walk) != 0;
|
|
}
|
|
|
|
// Normal action - No other commands have any effect during this action.
|
|
bool is_action() const {
|
|
return (object_state & ObjectState::ActionAll) != 0;
|
|
}
|
|
|
|
bool is_moving() const {
|
|
return (get_move_state() == ObjectState::MoveForward ||
|
|
get_move_state() == ObjectState::MoveBackward ||
|
|
get_move_state() == ObjectState::Left ||
|
|
get_move_state() == ObjectState::Right);
|
|
}
|
|
|
|
bool is_jumping() const {
|
|
return (object_state & ObjectState::JumpAll) != 0;
|
|
}
|
|
|
|
bool is_attacking() const {
|
|
return (object_state & ObjectState::AtkAll) != 0;
|
|
}
|
|
|
|
bool is_damaged() const {
|
|
return (object_state & ObjectState::DmgAll) != 0;
|
|
}
|
|
|
|
bool is_turning() const {
|
|
return (object_state & ObjectState::TurnAll) != 0;
|
|
}
|
|
|
|
u32 get_level() {
|
|
const auto &fn = fugg::function_ref<FunctionPointer<u32, u32>>(vtable->get_level);
|
|
|
|
return fn(kMemory.to_inside(this));
|
|
}
|
|
|
|
// int32_t get_gender() {
|
|
// // The function itself works but I fear it returns wrong results.
|
|
// // Female player = 0
|
|
// // Male player = 0
|
|
// // Npc = 2
|
|
// const auto& fn = fugg::function_ref<FunctionPointer<int32_t, Pointer<Mover>>>(vtable->get_gender);
|
|
|
|
// return fn(Pointer<Mover>(this));
|
|
// }
|
|
};
|
|
|
|
struct MoverMap {
|
|
struct __attribute__((packed)) Node {
|
|
const Pointer<const Node> next;
|
|
unsigned int list_index;
|
|
unsigned long long hash;
|
|
Pointer<Mover> mover;
|
|
};
|
|
|
|
const Pointer<const uintptr_t> buckets;
|
|
size_t capacity;
|
|
const Pointer<const Node> first;
|
|
};
|
|
|
|
}; // namespace flyff
|