Lots of refactoring

This commit is contained in:
Knaapchen 2022-10-27 19:40:41 +02:00
parent cca6f82dcb
commit 4a5b4790d4
14 changed files with 483 additions and 334 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/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)
target_include_directories(flyff-api PUBLIC "include")
target_link_libraries(flyff-api PUBLIC flyff-client fugg-api)

View File

@ -188,6 +188,10 @@ namespace flyff {
T *end() const {
return static_cast<T *>(end_);
}
size_t size() const {
return static_cast<size_t>(end_ - begin_);
}
};
static_assert(sizeof(Vector<char>) == sizeof(std::vector<char>),

View File

@ -21,9 +21,9 @@ namespace flyff {
return 0;
}
bool Mover::is_in_combat() const {
bool Mover::is_in_combat(uint32_t millis) const {
const auto &neuz = flyff::Neuz::instance();
// 10 seconds
return neuz.current_time - last_combat < 10000000;
return neuz.current_time - last_combat < (millis * 1000);
}
}; // namespace flyff

View File

@ -20,6 +20,7 @@
#include "../../item/InventoryItem.h"
#include "../../item/Part.h"
#include "JobProperty.h"
#include "ParamID.h"
namespace flyff {
namespace raw {
@ -36,9 +37,6 @@ namespace flyff {
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;
@ -98,7 +96,7 @@ namespace flyff {
return fn(kMemory.to_inside(this));
}
int get_id() const {
uint32_t get_id() const {
using Fn = FunctionPointer<u32, u32>;
const auto &fn = fugg::function_ref<Fn>(vtable->get_id);
@ -113,14 +111,15 @@ namespace flyff {
DEFINE_MEMBER(0x18, Matrix4, world_matrix);
DEFINE_MEMBER(152, Vector3, position);
DEFINE_MEMBER(164, Vector3, rotation);
DEFINE_MEMBER(152, const Vector3, position);
DEFINE_MEMBER(164, const Vector3, rotation);
DEFINE_MEMBER(188, const Pointer<const ObjectProperty>, property);
DEFINE_MEMBER(212, int, property_id);
DEFINE_MEMBER(216, bool, is_despawned);
DEFINE_MEMBER(352, MoverID, object_id);
DEFINE_MEMBER(212, const int, property_id);
DEFINE_MEMBER(216, const bool, is_despawned);
DEFINE_MEMBER(352, const MoverID, object_id);
DEFINE_MEMBER(920, Parameter, adjusted_parameters[MAX_PARAM]);
DEFINE_MEMBER(1248, Parameter, changed_parameters[MAX_PARAM]);
@ -129,11 +128,15 @@ namespace flyff {
DEFINE_MEMBER(2020, const String, name);
DEFINE_MEMBER(1616, const Vector<flyff::MoverID>, enemies);
DEFINE_MEMBER(1628, Vector3, move_target);
DEFINE_MEMBER(1652, Vector3, delta_velocity);
DEFINE_MEMBER(1688, unsigned long long, last_combat);
DEFINE_MEMBER(1728, MoverID, move_toward_target);
DEFINE_MEMBER(1736, MoverID, last_attacked_target);
DEFINE_MEMBER(1736, MoverID, last_hit_mover);
DEFINE_MEMBER(1744, MoverID, selected_target);
// The id of the target that it hit last.
DEFINE_MEMBER(1752, MoverID, attack_target);
@ -153,8 +156,6 @@ namespace flyff {
DEFINE_MEMBER(204, const ObjectType, type);
DEFINE_MEMBER(216, uint32_t, existence_check);
DEFINE_MEMBER(1804, uint32_t, object_state);
DEFINE_MEMBER(1808, uint32_t, object_state_flags);
@ -169,9 +170,9 @@ namespace flyff {
DEFINE_MEMBER(2432, const flyff::Pointer<const JobProperty>, job_property);
DEFINE_MEMBER(3224, Timestamp, last_food_time);
DEFINE_MEMBER(3248, Timestamp, last_refresher_time);
DEFINE_MEMBER(3256, Timestamp, last_vitaldrink_time);
DEFINE_MEMBER(3224, const Timestamp, last_food_time);
DEFINE_MEMBER(3248, const Timestamp, last_refresher_time);
DEFINE_MEMBER(3256, const Timestamp, last_vitaldrink_time);
DEFINE_MEMBER(3424, Vector<Skill>, skills);
@ -199,9 +200,9 @@ namespace flyff {
return max_inventory_slots - locked_inventory_slots;
}
bool is_in_combat() const;
bool is_in_combat(uint32_t millis = 10000) const;
Parameter get_param(uint32_t index, Parameter base) const {
uint32_t get_param(ParamID index, uint32_t base) const {
Parameter changeParam = get_change_param(index);
if (changeParam != 0x7FFFFFFF)
return changeParam;
@ -213,13 +214,16 @@ namespace flyff {
return base;
}
Parameter get_adjust_param(Parameter index) const {
Parameter get_adjust_param(ParamID index) const {
auto id = static_cast<uint32_t>(index);
// Find these values in CMover::Init
return adjusted_parameters[index] ^ adj_param ^ 0x0554e725 + index * 0x41c64e6d;
return adjusted_parameters[id] ^ adj_param ^ 0x0554e725 + id * 0x41c64e6d;
}
Parameter get_change_param(Parameter index) const {
return changed_parameters[index] ^ adj_param ^ index * 0x41c64e6d + 0xb5a52d1a;
Parameter get_change_param(ParamID index) const {
auto id = static_cast<uint32_t>(index);
return changed_parameters[id] ^ adj_param ^ id * 0x41c64e6d + 0xb5a52d1a;
}
u32 get_hp() const {
@ -236,8 +240,8 @@ namespace flyff {
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);
auto result = get_param(ParamID::MaxHitPoint, get_max_origin_hp());
auto percent = get_param(ParamID::MaxHitPointScaling, 0);
factor += (float) percent / (float) 100;
result = static_cast<float>(result) * factor;
@ -246,7 +250,7 @@ namespace flyff {
}
uint32_t get_hp_percent(int percent = 100) const {
int max = get_max_hp();
auto max = get_max_hp();
if (max == 0) return 0;
return get_hp() * percent / max;

View File

@ -0,0 +1,21 @@
//
// Created by main on 25-10-22.
//
#ifndef FUGG_PARAMID_H
#define FUGG_PARAMID_H
#include <cstdint>
namespace flyff {
enum class ParamID : uint32_t {
// DST_HP_MAX
MaxHitPoint = 44,
// DST_HP_MAX_RATE
MaxHitPointScaling = 53,
};
}
#endif //FUGG_PARAMID_H

View File

@ -1051403,9 +1051403,24 @@ void w2c_f2316(u32 w2c_p0) {
w2c_i0 = w2c_p0;
w2c_i0 = i32_load((&w2c_memory), (u64)(w2c_i0) + 4u);
w2c_l4 = w2c_i0;
const uintptr_t end = w2c_l4;
w2c_i1 = w2c_p0;
w2c_i1 = i32_load((&w2c_memory), (u64)(w2c_i1));
w2c_l2 = w2c_i1;
const uintptr_t begin = w2c_l2;
const size_t length = end - begin;
const unsigned char* data = &(&w2c_memory)->data[begin];
printf("Finalizing packet of length %zd: ", length);
for(int i = 0; i < length; i++) {
printf("%02X ", data[i]);
}
printf("\n");
w2c_i0 -= w2c_i1;
w2c_l7 = w2c_i0;
w2c_i0 = 9u;

View File

@ -9,7 +9,7 @@ add_executable(
"src/export/embind.cpp"
"src/export/env.cpp"
"src/export/platform.cpp"
src/Bot.cpp src/Bot.h src/Action.h src/Packet.h)
src/behaviour/Action.h src/Packet.h src/behaviour/Script.h)
# Flyff is based on c++14
add_definitions(-std=c++14 -Os)

View File

@ -1,25 +0,0 @@
//
// Created by main on 21-10-22.
//
#ifndef FUGG_ACTION_H
#define FUGG_ACTION_H
#include "Bot.h"
namespace fugg {
class Action {
public:
enum class State : unsigned char {
Running,
Failed,
Succeeded
};
virtual State evaluate(Bot &bot) = 0;
};
};
#endif //FUGG_ACTION_H

View File

@ -1,18 +0,0 @@
//
// Created by main on 21-10-22.
//
#include "Bot.h"
namespace fugg {
Bot &Bot::instance() {
// Constructor will be called.
static Bot instance;
// Whole module is initialised now.
return instance;
}
void Bot::tick() {
}
};

View File

@ -1,28 +0,0 @@
//
// Created by main on 21-10-22.
//
#ifndef FUGG_BOT_H
#define FUGG_BOT_H
#include <flyff.h>
namespace fugg {
class Bot {
Bot();
flyff::Neuz &neuz;
public:
static Bot &instance();
Bot(Bot const &) = delete;
void operator=(Bot const &) = delete;
void tick();
};
};
#endif //FUGG_BOT_H

View File

@ -115,20 +115,24 @@ namespace fugg {
send_packet(motion_packet_id, packet);
}
void Client::set_target(const unsigned long long &id) const {
void Client::send_set_target(const unsigned long long &id) const {
send_motion_packet(0x04, id);
}
void Client::clear_target() const {
void Client::send_clear_target() const {
send_motion_packet(0x04, {});
}
void Client::interact_target(const unsigned long long &index) const {
void Client::send_interact_target(const unsigned long long &index) const {
send_motion_packet(0x11, index);
}
void Client::use_skill(const flyff::SkillID &id, unsigned long long target) const {
send_motion_packet(0x5, static_cast<unsigned long long>(id), target);
void Client::send_use_skill(const flyff::SkillID &id, unsigned long long unknown) const {
send_motion_packet(0x5, static_cast<unsigned long long>(id), unknown);
}
void Client::send_follow_target(const unsigned long long &id) const {
send_motion_packet(0x10, id);
}
void Client::send_move_to(const double &x, const double &y, const double &z) const {
@ -188,16 +192,22 @@ find_skill_buff(const flyff::Pointer<flyff::Mover> &mover, const flyff::SkillID
template<size_t size>
std::vector<flyff::SkillID>
check_rebuff(const flyff::Pointer<flyff::Mover> &player, const flyff::SkillID (&buffs_to_check)[size]) {
check_rebuff(const flyff::Pointer<flyff::Mover>& buffer, const flyff::Pointer<flyff::Mover> &target,
const flyff::SkillID (&buffs_to_check)[size]) {
const auto &neuz = flyff::Neuz::instance();
auto result = std::vector<flyff::SkillID>();
for (const auto &skill_index: buffs_to_check) {
const auto &buff = find_skill_buff(player, skill_index);
const auto &buff = find_skill_buff(target, skill_index);
const auto &skill_property = flyff::Neuz::get_skill_property(skill_index);
const auto player_skill_level = buffer->get_skill_level(skill_index);
// Only check for the buff if the buffer actually has the skill.
if(player_skill_level <= 0) {
continue;
}
// If a buff is active
if (buff) {
auto player_skill_level = player->get_skill_level(skill_index);
// For some reason it's one off...
auto current_skill_level = buff->level + 1;
auto buff_property = reinterpret_cast<const flyff::SkillProperty *>(
@ -239,47 +249,52 @@ flyff::Pointer<const flyff::InventoryItem> find_first_equippable_item(const flyf
return flyff::Pointer<const flyff::InventoryItem>(highest);
}
bool is_running = false;
flyff::Vector3 start_position = {0};
// The object property id of the monster to grind.
int object_property_index = 0;
flyff::MoverID current_heal_target = 0;
enum class ScriptType {
Off,
Attacker,
Healer
};
ScriptType script = ScriptType::Off;
void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent *event, void *user_data) {
const auto &client = fugg::Client::instance();
if (event->keyCode == 220) {
const auto &player = client.player;
if (event->keyCode == 220 && script == ScriptType::Off) {
if (!client.player)
return;
auto items = player->inventory.begin();
std::cout << "Player has " << player->get_inventory_slots() << " slots" << std::endl;
std::cout << "Player has " << player->get_hp_percent() << "% HP" << std::endl;
for (auto i = 0; i < player->get_inventory_slots(); i++) {
if (items[i].property && items[i].property->is_food()) {
std::cout << client.get_text(items[i].property->name) << ": "
<< "Is food? " << (items[i].property->is_food() ? "Yes" : "No") << ", "
<< "Can use? " << (player->can_use_item(items[i].property) ? "Yes" : "No")
<< std::endl;
}
// if (items[i].property &&
// items[i].property->is_food() &&
// player->can_use_item(items[i].property)) {
// std::string str = "Eating ";
// str.append(client.get_text(items[i].property->name));
// str.append(" because HP is below 75%");
//
// client.show_message(str);
//
// client.send_use_item_in_inventory(i);
//
// break;
// }
if (client.player->selected_target == 0) {
client.show_message("No heal target selected.", {0xFF, 0, 0});
return;
}
auto target = client.get_mover(client.player->selected_target);
if (!target) {
client.show_message("No heal target selected.", {0xFF, 0xFF, 0});
return;
}
std::string str = "I'm going to heal ";
str.append(client.get_text(target->name));
str.append("<Lv. ");
str.append(std::to_string(target->get_level()));
str.append(">");
client.show_message(str);
current_heal_target = client.player->selected_target;
script = ScriptType::Healer;
client.show_message("Bot started");
// std::cout << "Velocity: " << client.player->delta_velocity << std::endl;
// if (!main_hand || main_hand->item_kind3 != flyff::ItemProperty::CheerStick) {
// const auto &stick = find_first_equippable_item(flyff::ItemProperty::CheerStick);
//
@ -303,9 +318,9 @@ void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent *event, void *u
// static_cast<flyff::ObjectProperty*>(test->property)
// );
// std::cout << "BuffSkill "
// std::cout << "BuffSkill "
// << client.get_text(skill_property->name)
// << " ReqLv. " << skill_property->required_level
// << " ReqLv. " << skill_property->required_level
// << " Lv." << skill_property->level
// << " Id: " << reinterpret_cast<void*>(skill_property->id)
// << " Remaining: " << (test->get_time_remaining() / 1000)
@ -314,7 +329,7 @@ void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent *event, void *u
// }
//
// clear_target();
// send_clear_target();
// auto items = player->inventory.begin();
// auto length = ((uintptr_t)player->inventory.end() - (uintptr_t)player->inventory.begin()) / 0x60;
@ -353,14 +368,14 @@ void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent *event, void *u
// if(stick_index != -1) {
// // Use skill Heal
// use_skill(0x2c, 10);
// send_use_skill(0x2c, 10);
// } else {
// client.show_message("Could not find a stick to heal myself.", { 0xFF, 0, 0 });
// }
}
// BracketLeft
if (event->keyCode == 219 && !is_running) {
if (event->keyCode == 219 && script == ScriptType::Off) {
if (!client.player)
return;
@ -386,14 +401,14 @@ void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent *event, void *u
start_position = {client.player->position.x, client.player->position.y, client.player->position.z};
std::cout << "Start botting at " << start_position << std::endl;
is_running = true;
script = ScriptType::Attacker;
client.show_message("Bot started");
}
if (event->keyCode == 221) {
if (is_running) {
is_running = false;
if (script != ScriptType::Off) {
script = ScriptType::Off;
client.show_message("Bot stopped");
} else {
@ -438,12 +453,12 @@ void on_keyup_hook(int event_type, const EmscriptenKeyboardEvent *event, void *u
// << "Is this food ? " << (items[i].property->is_food_item() ? "Yes" : "No")
// << std::endl;
// }
// }
// }
}
}
}
flyff::MoverID attack_next_target(flyff::Vector3 const &start_position) {
flyff::MoverID find_closest_target() {
auto &neuz = flyff::Neuz::instance();
auto &client = fugg::Client::instance();
@ -485,12 +500,6 @@ flyff::MoverID attack_next_target(flyff::Vector3 const &start_position) {
} while (current);
if (closest) {
neuz.player->selected_target = closest->object_id;
client.set_target(closest->object_id);
neuz.player->move_toward_target = closest->object_id;
client.interact_target(1);
return closest->object_id;
}
@ -539,223 +548,312 @@ flyff::MoverID find_next_item() {
return 0;
}
void interact(flyff::MoverID id) {
const auto &neuz = flyff::Neuz::instance();
void interact(flyff::MoverID id, uint32_t action) {
const auto &client = fugg::Client::instance();
if (!neuz.player)
if (!client.player)
return;
neuz.player->selected_target = id;
client.set_target(id);
client.player->selected_target = id;
client.send_set_target(id);
neuz.player->move_toward_target = id;
client.interact_target(1);
client.player->move_toward_target = id;
client.send_interact_target(action);
}
flyff::MoverID current_target = 0;
flyff::MoverID current_pickup_item = 0;
void bot_tick() {
void attacker_script() {
const auto &neuz = flyff::Neuz::instance();
const auto &client = fugg::Client::instance();
if (!neuz.player)
return;
// std::cout << "Max Max HP: "
// << neuz.player->get_max_hp()
// << ", Adjust HP:"
// << neuz.player->get_adjust_param(0x2c)
// << ", Player level: "
// << neuz.player->get_level()
// << std::endl;
if (neuz.player->is_dead()) {
neuz.show_message("Player is dead, stopping...", {0xFF, 0x00, 0x00});
script = ScriptType::Off;
return;
}
// printf("%#010x\n", neuz.player->get_state());
const auto &player = neuz.player;
if (is_running) {
if (neuz.player->is_dead()) {
neuz.show_message("Player is dead, stopping...", {0xFF, 0x00, 0x00});
is_running = false;
return;
// if is_idle:
// if item_target:
// pickup_item()
// elif attacK_target:
// attack_target()
if (neuz.player->get_hp_percent() < 50) {
// neuz.show_message("Fooding because HP is under 75%");W
auto items = player->inventory.begin();
std::cout << "Player has " << player->get_inventory_slots() << " slots" << std::endl;
for (auto i = 0; i < player->get_inventory_slots(); i++) {
if (items[i].property &&
items[i].property->is_food() &&
player->can_use_item(items[i].property)) {
std::string str = "Eating ";
str.append(neuz.get_text(items[i].property->name));
str.append(" because HP is below 75%");
neuz.show_message(str);
client.send_use_item_in_inventory(i);
break;
}
}
}
if (neuz.player->get_move_state() == 1 &&
!neuz.player->is_attacking()) {
// Check buffs.
static constexpr flyff::SkillID buffs_to_check[] = {
flyff::SkillID::HeapUp,
flyff::SkillID::Haste,
flyff::SkillID::Patience,
flyff::SkillID::BeefUp,
};
auto item = neuz.get_mover(current_pickup_item);
auto monster = neuz.get_mover(current_target);
if (neuz.player->get_job() == flyff::JobID::Assist) {
const auto &main_hand = player->get_equipped_item(flyff::Part::MainHand);
const auto &off_hand = player->get_equipped_item(flyff::Part::Shield);
auto buffs = check_rebuff(neuz.player, neuz.player, buffs_to_check);
if (!buffs.empty() && (!item || item->is_despawned) && (!monster || monster->is_dead())) {
const auto skill_index = buffs.back();
if (!main_hand || main_hand->item_kind3 != flyff::ItemKind3::CheerStick) {
const auto &stick = find_first_equippable_item(flyff::ItemKind3::CheerStick);
if (stick) {
buffs.pop_back();
client.send_use_item_in_inventory(stick->index);
client.send_use_skill(skill_index);
return;
} else {
neuz.show_message("Unable to find a stick", {0xFF, 0, 0});
}
} else {
buffs.pop_back();
client.send_use_skill(skill_index);
return;
}
}
if (buffs.empty()) {
if (!main_hand || main_hand->item_kind3 != flyff::ItemKind3::KnuckleHammer) {
const auto &knuckle = find_first_equippable_item(flyff::ItemKind3::KnuckleHammer);
if (knuckle) {
client.send_use_item_in_inventory(knuckle->index);
} else {
neuz.show_message("Unable to find a knuckle", {0xFF, 0, 0});
}
}
if (!off_hand || off_hand->item_kind3 != flyff::ItemKind3::Shield) {
const auto &shield = find_first_equippable_item(flyff::ItemKind3::Shield);
if (shield) {
client.send_use_item_in_inventory(shield->index);
} else {
neuz.show_message("Unable to find a shield", {0xFF, 0, 0});
}
}
}
}
const auto &player = neuz.player;
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) {
// Attack
interact(monster->object_id, 1);
// neuz.player->attack_target = monster->object_id;
} else if ((!item || item->is_despawned) &&
(!monster || monster->is_dead())) {
std::cout << "No item, no target. Searching..." << std::endl;
// if is_idle:
// if item_target:
// pickup_item()
// elif attacK_target:
// attack_target()
// First check all mobs if they attacked me (aggro ones).
for (const auto &id: client.player->enemies) {
const auto &mover = client.get_mover(id);
if (neuz.player->get_hp_percent() < 75) {
// neuz.show_message("Fooding because HP is under 75%");W
auto items = player->inventory.begin();
std::cout << "Player has " << player->get_inventory_slots() << " slots" << std::endl;
for (auto i = 0; i < player->get_inventory_slots(); i++) {
if (items[i].property &&
items[i].property->is_food() &&
player->can_use_item(items[i].property)) {
std::string str = "Eating ";
str.append(neuz.get_text(items[i].property->name));
str.append(" because HP is below 75%");
if (mover) {
std::string str = "Attacking ";
str.append(mover->name);
str.append(" because it is my enemy.");
current_target = id;
interact(id, 1);
neuz.show_message(str);
client.send_use_item_in_inventory(i);
break;
return;
}
}
}
// No item target and no attack target
current_pickup_item = find_next_item();
item = neuz.get_mover(current_pickup_item);
if (item) {
auto *prop = reinterpret_cast<const flyff::ItemProperty *>(
static_cast<const flyff::ObjectProperty *>(item->property)
);
if (neuz.player->get_move_state() == 1 &&
!neuz.player->is_attacking()) {
std::string str = "Picking up ";
str.append(neuz.get_text(prop->name));
interact(item->object_id, 1);
// Check buffs.
static constexpr flyff::SkillID buffs_to_check[] = {
flyff::SkillID::HeapUp,
flyff::SkillID::Haste,
flyff::SkillID::Patience,
flyff::SkillID::BeefUp,
};
neuz.show_message(str);
return;
} else {
current_target = find_closest_target();
auto item = neuz.get_mover(current_pickup_item);
auto monster = neuz.get_mover(current_target);
monster = neuz.get_mover(current_target);
if (monster) {
std::string str = "Attacking ";
str.append(monster->name);
if (neuz.player->get_job() == flyff::JobID::Assist) {
const auto &main_hand = player->get_equipped_item(flyff::Part::MainHand);
const auto &off_hand = player->get_equipped_item(flyff::Part::Shield);
auto buffs = check_rebuff(neuz.player, buffs_to_check);
if (!buffs.empty() && (!item || item->is_despawned) && (!monster || monster->is_dead())) {
const auto skill_index = buffs.back();
if (!main_hand || main_hand->item_kind3 != flyff::ItemKind3::CheerStick) {
const auto &stick = find_first_equippable_item(flyff::ItemKind3::CheerStick);
if (stick) {
buffs.pop_back();
client.send_use_item_in_inventory(stick->index);
client.use_skill(skill_index);
return;
} else {
neuz.show_message("Unable to find a stick", {0xFF, 0, 0});
}
} else {
buffs.pop_back();
client.use_skill(skill_index);
return;
}
}
if (buffs.empty()) {
if (!main_hand || main_hand->item_kind3 != flyff::ItemKind3::KnuckleHammer) {
const auto &knuckle = find_first_equippable_item(flyff::ItemKind3::KnuckleHammer);
if (knuckle) {
client.send_use_item_in_inventory(knuckle->index);
} else {
neuz.show_message("Unable to find a knuckle", {0xFF, 0, 0});
}
}
if (!off_hand || off_hand->item_kind3 != flyff::ItemKind3::Shield) {
const auto &shield = find_first_equippable_item(flyff::ItemKind3::Shield);
if (shield) {
client.send_use_item_in_inventory(shield->index);
} else {
neuz.show_message("Unable to find a shield", {0xFF, 0, 0});
}
}
}
}
if (item && !item->is_despawned && neuz.player->move_toward_target == 0) {
// Pickup
interact(item->object_id);
} else if (monster && !monster->is_dead() && neuz.player->move_toward_target == 0) {
// Attack
interact(monster->object_id);
// neuz.player->attack_target = monster->object_id;
} else if ((!item || item->is_despawned) &&
(!monster || monster->is_dead())) {
std::cout << "No item, no target. Searching..." << std::endl;
// First check all mobs if they attacked me (aggro ones).
auto current = neuz.movers.first;
do {
const auto &mover = current->mover;
if (mover->type == flyff::ObjectType::Mover &&
current->hash != neuz.player_object_id &&
!mover->is_dead() &&
// If it's a monster that has me targetted...
mover->last_attacked_target == neuz.player_object_id &&
// ...and is tracking me
mover->follow_target == neuz.player_object_id) {
current_target = attack_next_target(start_position);
monster = neuz.get_mover(current_target);
std::string str = "Attacking ";
str.append(monster->name);
str.append(" because it attacked me.");
interact(monster->object_id);
neuz.show_message(str);
return;
}
current = current->next;
} while (current);
// No item target and no attack target
current_pickup_item = find_next_item();
item = neuz.get_mover(current_pickup_item);
if (item) {
auto *prop = reinterpret_cast<const flyff::ItemProperty *>(
static_cast<const flyff::ObjectProperty *>(item->property)
);
std::string str = "Picking up ";
str.append(neuz.get_text(prop->name));
interact(item->object_id);
interact(current_target, 1);
neuz.show_message(str);
} else {
current_target = attack_next_target(start_position);
monster = neuz.get_mover(current_target);
if (monster) {
std::string str = "Attacking ";
str.append(monster->name);
std::cout << str << std::endl;
interact(monster->object_id);
// neuz.player->attack_target = monster->object_id;
neuz.show_message(str);
} else {
neuz.show_message("Unable to find target.");
}
neuz.show_message("Unable to find target.");
}
}
}
} else {
// Reset current target is not running.
current_target = 0;
current_pickup_item = 0;
}
}
void healer_script() {
const auto &client = fugg::Client::instance();
if (!client.player)
return;
if (client.player->is_dead()) {
client.show_message("Player is dead, stopping...", {0xFF, 0x00, 0x00});
script = ScriptType::Off;
return;
}
if (client.player->get_move_state() == 1 &&
!client.player->is_attacking()) {
if (client.player->selected_target != current_heal_target) {
client.send_set_target(current_heal_target);
client.player->selected_target = current_heal_target;
client.show_message("Selected target");
}
if (client.player->follow_target != current_heal_target) {
client.send_follow_target(current_heal_target);
client.player->follow_target = current_heal_target;
client.show_message("Following target");
}
const auto &target = client.get_mover(current_heal_target);
if (target) {
// If the target has been hit, heal him.
if (target->get_hp_percent() < 100) {
client.show_message("Healing target");
client.send_use_skill(flyff::SkillID::Heal);
return;
}
// If I'm hit, heal myself.
if(client.player->get_hp_percent() < 75) {
client.send_clear_target();
client.player->selected_target = 0;
client.send_use_skill(flyff::SkillID::Heal);
return;
}
// Check for rebuff on the target
{
// For target, check all buffs.
static constexpr flyff::SkillID buffs_to_check[] = {
flyff::SkillID::MentalSign,
flyff::SkillID::Patience,
flyff::SkillID::QuickStep,
flyff::SkillID::HeapUp,
flyff::SkillID::Haste,
flyff::SkillID::BeefUp,
flyff::SkillID::Accuracy,
flyff::SkillID::CatsReflex,
flyff::SkillID::CannonBall
};
auto buffs = check_rebuff(client.player, target, buffs_to_check);
if (!buffs.empty()) {
auto skill_index = buffs.back();
buffs.pop_back();
const auto& skill = fugg::Client::get_skill_property(skill_index);
std::string str = "Buffing target with";
str.append(client.get_text(skill->name));
client.show_message(str);
client.send_use_skill(skill_index);
return;
}
}
// Check for rebuff on yourself
if(!target->is_in_combat(1500)){
// For FS itself, only these selected buffs.
static constexpr flyff::SkillID buffs_to_check[] = {
flyff::SkillID::MentalSign,
flyff::SkillID::QuickStep,
flyff::SkillID::Patience,
flyff::SkillID::HeapUp,
flyff::SkillID::Haste,
flyff::SkillID::Prevention
};
auto buffs = check_rebuff(client.player, client.player, buffs_to_check);
if (!buffs.empty()) {
auto skill_index = buffs.back();
buffs.pop_back();
client.send_clear_target();
client.player->selected_target = 0;
client.send_use_skill(skill_index);
return;
}
}
} else {
client.show_message("Can't find target!", {0xFF, 0, 0});
}
}
}
void before_main_loop() {
bot_tick();
switch (script) {
case ScriptType::Attacker:
attacker_script();
break;
case ScriptType::Healer:
healer_script();
break;
default:
// Reset current target is not running.
current_target = 0;
current_pickup_item = 0;
break;
}
}
void after_main_loop() {

View File

@ -28,13 +28,15 @@ namespace fugg {
void send_motion_packet(const uint32_t &action, unsigned long long param0 = 0, unsigned long long param1 = 0,
unsigned long long param2 = 0, unsigned long long param3 = 0) const;
void set_target(const unsigned long long &id) const;
void send_set_target(const unsigned long long &id) const;
void clear_target() const;
void send_clear_target() const;
void interact_target(const unsigned long long &index) const;
void use_skill(const flyff::SkillID &id, unsigned long long target = -1) const;
void send_interact_target(const unsigned long long &index) const;
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_move_to(const double &x, const double &y, const double &z) const;
void send_chat_message(const std::string &message) const;

View File

@ -0,0 +1,57 @@
//
// Created by main on 21-10-22.
//
#ifndef FUGG_ACTION_H
#define FUGG_ACTION_H
#include "../Client.h"
namespace fugg {
class Action {
public:
enum class State : unsigned char {
Running,
Failed,
Succeeded
};
virtual State evaluate(Client &client) = 0;
};
class StatefulAction : Action {
virtual State start() = 0;
virtual State run() = 0;
virtual State stop() = 0;
};
class ActionAttack : Action {
const flyff::MoverID target_id;
public:
explicit ActionAttack(flyff::MoverID id) : target_id(id) { }
State evaluate(Client &client) override {
const auto& target = client.get_mover(target_id);
if(!target) return State::Failed;
if(client.player->selected_target != target_id) {
client.player->selected_target = target_id;
client.send_set_target(target_id);
}
if(client.player->move_toward_target != target_id) {
client.player->move_toward_target = target_id;
client.send_interact_target(1);
}
return State::Succeeded;
};
};
};
#endif //FUGG_ACTION_H

View File

@ -0,0 +1,19 @@
//
// Created by main on 25-10-22.
//
#ifndef FUGG_SCRIPT_H
#define FUGG_SCRIPT_H
#include "Action.h"
namespace fugg {
class Script {
public:
virtual Action evaluate(const Client& client) = 0;
};
};
#endif //FUGG_SCRIPT_H