Pathfinding supports links WIP, navigation_node, antiban nodes, bt builder changes.
parent
c84c81b71d
commit
96a09ddf9d
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <bt/behavior_node.hpp>
|
||||
|
||||
#include <Game/Core.hpp>
|
||||
|
||||
namespace bt::antiban {
|
||||
template<typename Context>
|
||||
class lose_focus_node : public behavior_node<Context> {
|
||||
public:
|
||||
bool move_mouse;
|
||||
|
||||
lose_focus_node() noexcept : move_mouse(true) {}
|
||||
|
||||
lose_focus_node(bool move_mouse) noexcept : move_mouse(move_mouse) {}
|
||||
|
||||
node_task tick(Context &ctx) noexcept override {
|
||||
move_mouse ? Antiban::MouseOffClient(true) : Antiban::LoseClientFocus();
|
||||
co_return task_result::success;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -81,6 +81,32 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
class parallel_node : public composite_node<Context> {
|
||||
public:
|
||||
node_task tick(Context &ctx) noexcept override {
|
||||
std::vector<node_task> tasks;
|
||||
for (auto& child : this->_children) {
|
||||
tasks.push_back(child->tick(ctx));
|
||||
}
|
||||
|
||||
while (!tasks.empty()) {
|
||||
for (auto& task : tasks) {
|
||||
if (task.done())
|
||||
continue;
|
||||
|
||||
task.resume();
|
||||
co_await yield{};
|
||||
}
|
||||
|
||||
std::erase_if(tasks, [](auto& task) { return task.done(); });
|
||||
}
|
||||
|
||||
co_return task_result::success;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Context, typename TickFn>
|
||||
class action_node : public behavior_node<Context> {
|
||||
public:
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <Game/Core.hpp>
|
||||
|
||||
#include <bt/antiban.hpp>
|
||||
#include <bt/behavior_node.hpp>
|
||||
#include <bt/interact.hpp>
|
||||
#include <bt/inventory.hpp>
|
||||
|
@ -18,6 +19,8 @@
|
|||
#include <bt/mainscreen.hpp>
|
||||
#include <bt/navigation.hpp>
|
||||
#include <bt/node_task.hpp>
|
||||
#include <bt/pathfinding.hpp>
|
||||
#include <bt/pathfinding_obstacles.hpp>
|
||||
#include <bt/player.hpp>
|
||||
#include <bt/script.hpp>
|
||||
|
||||
|
@ -26,64 +29,59 @@ template <typename Context> class tree_builder {
|
|||
public:
|
||||
tree_builder() noexcept : _root(nullptr) {}
|
||||
|
||||
tree_builder &begin_sequence() {
|
||||
_node_stack.push_back(std::make_unique<sequence_node<Context>>());
|
||||
template<template<typename...> class Compositor>
|
||||
tree_builder &begin_compositor() {
|
||||
_node_stack.push_back(std::make_unique<Compositor<Context>>());
|
||||
_children.emplace_back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
tree_builder &end_compositor() {
|
||||
if (_node_stack.empty()) {
|
||||
throw std::runtime_error("Mismatched begin_compositor/end_compositor");
|
||||
}
|
||||
|
||||
auto node = std::move(_node_stack.back());
|
||||
_node_stack.pop_back();
|
||||
auto children = std::move(_children.back());
|
||||
_children.pop_back();
|
||||
|
||||
for (auto &&child : children) {
|
||||
static_cast<composite_node<Context> *>(node.get())
|
||||
->add_child(std::move(child));
|
||||
}
|
||||
|
||||
if (_node_stack.empty()) {
|
||||
_root = std::move(node);
|
||||
} else {
|
||||
_children.back().push_back(std::move(node));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
tree_builder &begin_sequence() {
|
||||
return begin_compositor<sequence_node>();
|
||||
}
|
||||
|
||||
tree_builder &end_sequence() {
|
||||
if (_node_stack.empty()) {
|
||||
throw std::runtime_error("Mismatched begin_sequence/end_sequence");
|
||||
}
|
||||
|
||||
auto node = std::move(_node_stack.back());
|
||||
_node_stack.pop_back();
|
||||
auto children = std::move(_children.back());
|
||||
_children.pop_back();
|
||||
|
||||
for (auto &&child : children) {
|
||||
static_cast<composite_node<Context> *>(node.get())
|
||||
->add_child(std::move(child));
|
||||
}
|
||||
|
||||
if (_node_stack.empty()) {
|
||||
_root = std::move(node);
|
||||
} else {
|
||||
_children.back().push_back(std::move(node));
|
||||
}
|
||||
|
||||
return *this;
|
||||
return end_compositor();
|
||||
}
|
||||
|
||||
tree_builder &begin_selector() {
|
||||
_node_stack.push_back(std::make_unique<selector_node<Context>>());
|
||||
_children.emplace_back();
|
||||
return *this;
|
||||
return begin_compositor<selector_node>();
|
||||
}
|
||||
|
||||
tree_builder &end_selector() {
|
||||
if (_node_stack.empty()) {
|
||||
throw std::runtime_error("Mismatched begin_selector/end_selector");
|
||||
}
|
||||
return end_compositor();
|
||||
}
|
||||
|
||||
auto node = std::move(_node_stack.back());
|
||||
_node_stack.pop_back();
|
||||
auto children = std::move(_children.back());
|
||||
_children.pop_back();
|
||||
tree_builder &begin_parallel() {
|
||||
return begin_compositor<parallel_node>();
|
||||
}
|
||||
|
||||
for (auto &&child : children) {
|
||||
static_cast<composite_node<Context> *>(node.get())
|
||||
->add_child(std::move(child));
|
||||
}
|
||||
|
||||
if (_node_stack.empty()) {
|
||||
_root = std::move(node);
|
||||
} else {
|
||||
_children.back().push_back(std::move(node));
|
||||
}
|
||||
|
||||
return *this;
|
||||
tree_builder &end_parallel() {
|
||||
return end_compositor();
|
||||
}
|
||||
|
||||
template <template<typename...> class Node, typename... Args>
|
||||
|
@ -118,7 +116,8 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
tree_builder &invert() {
|
||||
template<template<typename...> class Decorator>
|
||||
tree_builder &decorate() {
|
||||
if (_node_stack.empty()) {
|
||||
throw std::runtime_error("Invert must be within a sequence or selector");
|
||||
}
|
||||
|
@ -130,11 +129,15 @@ public:
|
|||
auto child = std::move(_children.back().back());
|
||||
_children.back().pop_back();
|
||||
_children.back().push_back(
|
||||
std::make_unique<inverter_node<Context>>(std::move(child)));
|
||||
std::make_unique<Decorator<Context>>(std::move(child)));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
tree_builder &invert() {
|
||||
return decorate<inverter_node>();
|
||||
}
|
||||
|
||||
std::unique_ptr<behavior_node<Context>> build() {
|
||||
if (!_node_stack.empty()) {
|
||||
throw std::runtime_error("Mismatched begin/end");
|
||||
|
|
|
@ -3,191 +3,43 @@
|
|||
#include <Game/Core.hpp>
|
||||
|
||||
#include <bt/behavior_node.hpp>
|
||||
#include <bt/pathfinding.hpp>
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<Tile> {
|
||||
std::size_t operator()(const Tile &tile) const {
|
||||
std::size_t h1 = std::hash<std::int32_t>()(tile.X);
|
||||
std::size_t h2 = std::hash<std::int32_t>()(tile.Y);
|
||||
std::size_t h3 = std::hash<std::int32_t>()(tile.Plane);
|
||||
return h1 ^ (h2 << 1) ^ (h3 << 2);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
|
||||
namespace bt {
|
||||
namespace pathfinding {
|
||||
class obstacle {
|
||||
template <typename Context, typename DestinationSource, typename PFSSource>
|
||||
requires std::is_same_v<Tile, DestinationSource> ||
|
||||
std::is_same_v<Tile,
|
||||
std::result_of_t<DestinationSource(Context &)>> ||
|
||||
std::is_same_v<pathfinding::settings, PFSSource> ||
|
||||
std::is_same_v<const pathfinding::settings &,
|
||||
std::result_of_t<PFSSource(Context &)>>
|
||||
class navigation_node : public behavior_node<Context> {
|
||||
public:
|
||||
Tile tile;
|
||||
Tile destination;
|
||||
|
||||
obstacle(Tile tile, Tile destination) noexcept
|
||||
: tile(tile), destination(destination) {}
|
||||
|
||||
obstacle(Tile tile) noexcept : tile(tile), destination(tile) {}
|
||||
|
||||
bool operator==(const obstacle &other) const noexcept {
|
||||
return tile == other.tile;
|
||||
}
|
||||
|
||||
bool operator!=(const obstacle &other) const noexcept {
|
||||
return tile != other.tile;
|
||||
}
|
||||
|
||||
virtual bool can_handle() const noexcept = 0;
|
||||
|
||||
virtual bool handle() noexcept = 0;
|
||||
};
|
||||
|
||||
std::unordered_set<std::int32_t> mapped_regions;
|
||||
std::unordered_map<Tile, Pathfinding::COLLISION_FLAG> collision_data;
|
||||
std::unordered_map<Tile, std::shared_ptr<obstacle>> obstacles;
|
||||
|
||||
void load_collision_data(std::ifstream &file) {
|
||||
std::string line;
|
||||
std::getline(file, line); // skip csv header
|
||||
while (std::getline(file, line)) {
|
||||
std::stringstream ss(line);
|
||||
std::string token;
|
||||
std::vector<std::string> tokens;
|
||||
while (std::getline(ss, token, ',')) {
|
||||
tokens.emplace_back(token);
|
||||
}
|
||||
|
||||
const auto region = std::stoi(tokens[0]);
|
||||
const auto plane = std::stoi(tokens[1]);
|
||||
const auto x = std::stoi(tokens[2]);
|
||||
const auto y = std::stoi(tokens[3]);
|
||||
const auto flag =
|
||||
static_cast<Pathfinding::COLLISION_FLAG>(std::stoi(tokens[4]));
|
||||
|
||||
mapped_regions.insert(region);
|
||||
collision_data.emplace(Tile(x, y, plane), flag);
|
||||
}
|
||||
}
|
||||
|
||||
void visit_neighbors(const Tile &tile, const auto &func) {
|
||||
Pathfinding::COLLISION_FLAG flags = Pathfinding::COLLISION_FLAG::OPEN;
|
||||
auto it = collision_data.find(tile);
|
||||
if (it != collision_data.end()) {
|
||||
flags = it->second;
|
||||
}
|
||||
|
||||
const auto blocked = [](Pathfinding::COLLISION_FLAG flags) {
|
||||
return (flags & Pathfinding::BLOCKED) || (flags & Pathfinding::OCCUPIED);
|
||||
};
|
||||
|
||||
constexpr std::array<std::tuple<int, int, Pathfinding::COLLISION_FLAG>, 4>
|
||||
directions = {{{0, 1, Pathfinding::COLLISION_FLAG::NORTH},
|
||||
{1, 0, Pathfinding::COLLISION_FLAG::EAST},
|
||||
{0, -1, Pathfinding::COLLISION_FLAG::SOUTH},
|
||||
{-1, 0, Pathfinding::COLLISION_FLAG::WEST}}};
|
||||
|
||||
for (const auto &[dx, dy, dirFlag] : directions) {
|
||||
if ((flags & dirFlag) == Pathfinding::COLLISION_FLAG::OPEN) {
|
||||
int newX = tile.X + dx;
|
||||
int newY = tile.Y + dy;
|
||||
|
||||
auto neighbor = Tile(newX, newY, tile.Plane);
|
||||
|
||||
auto neighborIt = collision_data.find(neighbor);
|
||||
if (neighborIt != collision_data.end()) {
|
||||
if (!blocked(neighborIt->second)) {
|
||||
func(neighbor);
|
||||
}
|
||||
} else {
|
||||
func(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Tile> find_path(const Tile &start, const Tile &end) {
|
||||
class path_node {
|
||||
public:
|
||||
Tile tile;
|
||||
std::shared_ptr<path_node> parent;
|
||||
double cost;
|
||||
|
||||
path_node(const Tile &tile, std::shared_ptr<path_node> parent, double cost)
|
||||
: tile(tile), parent(parent), cost(cost) {}
|
||||
};
|
||||
|
||||
const auto compare = [](const std::shared_ptr<path_node> &a,
|
||||
const std::shared_ptr<path_node> &b) {
|
||||
return a->cost > b->cost;
|
||||
};
|
||||
|
||||
const auto heuristic = [](const Tile &a, const Tile &b) {
|
||||
return std::abs(a.X - b.X) + std::abs(a.Y - b.Y);
|
||||
};
|
||||
|
||||
std::priority_queue<std::shared_ptr<path_node>,
|
||||
std::vector<std::shared_ptr<path_node>>,
|
||||
decltype(compare)>
|
||||
queue(compare);
|
||||
|
||||
std::unordered_map<Tile, double, std::hash<Tile>> costs;
|
||||
std::unordered_set<Tile, std::hash<Tile>> visited;
|
||||
|
||||
queue.emplace(std::make_shared<path_node>(start, nullptr, 0.0));
|
||||
costs[start] = 0.0;
|
||||
|
||||
while (!queue.empty()) {
|
||||
auto current = queue.top();
|
||||
queue.pop();
|
||||
|
||||
if (current->tile == end) {
|
||||
std::vector<Tile> path;
|
||||
while (current != nullptr) {
|
||||
path.push_back(current->tile);
|
||||
current = current->parent;
|
||||
}
|
||||
std::reverse(path.begin(), path.end());
|
||||
return path;
|
||||
}
|
||||
|
||||
visit_neighbors(current->tile, [&](const Tile &neighbor) {
|
||||
double new_cost =
|
||||
costs[current->tile] + 1; // Assuming constant cost of 1 for each step
|
||||
|
||||
if (visited.find(neighbor) == visited.end() ||
|
||||
new_cost < costs[neighbor]) {
|
||||
costs[neighbor] = new_cost;
|
||||
double priority = new_cost + heuristic(neighbor, end);
|
||||
queue.emplace(std::make_shared<path_node>(neighbor, current, priority));
|
||||
visited.insert(neighbor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return std::vector<Tile>(); // no path found
|
||||
}
|
||||
} // namespace pathfinding
|
||||
|
||||
template <typename Context, typename DestinationSource> class navigation_node;
|
||||
|
||||
template <typename Context>
|
||||
class navigation_node<Context, Tile> : public behavior_node<Context> {
|
||||
public:
|
||||
Tile destination;
|
||||
|
||||
navigation_node(const Tile &destination) : destination(destination) {}
|
||||
navigation_node(const DestinationSource &destination_source,
|
||||
const PFSSource &pfs_source)
|
||||
: _destination_source(destination_source), _pfs_source(pfs_source) {}
|
||||
|
||||
node_task tick(Context &ctx) noexcept override {
|
||||
const auto player = Players::GetLocal();
|
||||
if (!player)
|
||||
co_return task_result::failure;
|
||||
|
||||
const auto position = player.GetTile();
|
||||
const auto path = pathfinding::find_path(position, destination);
|
||||
auto position = player.GetTile();
|
||||
if (!position)
|
||||
co_return task_result::failure;
|
||||
|
||||
co_return task_result::success;
|
||||
}
|
||||
|
||||
private:
|
||||
DestinationSource _destination_source;
|
||||
PFSSource _pfs_source;
|
||||
};
|
||||
} // namespace bt
|
|
@ -0,0 +1,235 @@
|
|||
#pragma once
|
||||
|
||||
#include <Game/Core.hpp>
|
||||
|
||||
#include <bt/behavior_node.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<Tile> {
|
||||
std::size_t operator()(const Tile &tile) const {
|
||||
std::size_t h1 = std::hash<std::int32_t>()(tile.X);
|
||||
std::size_t h2 = std::hash<std::int32_t>()(tile.Y);
|
||||
std::size_t h3 = std::hash<std::int32_t>()(tile.Plane);
|
||||
return h1 ^ (h2 << 1) ^ (h3 << 2);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
namespace bt::pathfinding {
|
||||
class link {
|
||||
public:
|
||||
Tile start;
|
||||
Tile destination;
|
||||
|
||||
link(Tile start, Tile destination) noexcept
|
||||
: start(start), destination(destination) {}
|
||||
|
||||
bool operator==(const link &other) const noexcept {
|
||||
return start == other.start;
|
||||
}
|
||||
|
||||
bool operator!=(const link &other) const noexcept {
|
||||
return start != other.start;
|
||||
}
|
||||
|
||||
virtual bool can_handle() const noexcept = 0;
|
||||
|
||||
virtual bool handle() noexcept = 0;
|
||||
};
|
||||
|
||||
using path_step = std::variant<Tile, std::shared_ptr<link>>;
|
||||
|
||||
using path = std::vector<path_step>;
|
||||
|
||||
class path_node {
|
||||
public:
|
||||
path_step step;
|
||||
std::shared_ptr<path_node> parent;
|
||||
double cost;
|
||||
|
||||
path_node(const path_step &step, std::shared_ptr<path_node> parent,
|
||||
double cost)
|
||||
: step(step), parent(parent), cost(cost) {}
|
||||
};
|
||||
|
||||
class settings {
|
||||
public:
|
||||
std::int32_t agility_level;
|
||||
|
||||
settings() noexcept = default;
|
||||
};
|
||||
|
||||
std::unordered_set<std::int32_t> mapped_regions;
|
||||
std::unordered_map<Tile, Pathfinding::COLLISION_FLAG> collision_data;
|
||||
std::unordered_map<Tile, std::shared_ptr<link>> links;
|
||||
|
||||
void add_link(std::shared_ptr<link> link) noexcept {
|
||||
links.emplace(link->start, link);
|
||||
}
|
||||
|
||||
void load_collision_data(std::ifstream &file) {
|
||||
std::string line;
|
||||
std::getline(file, line); // skip csv header
|
||||
while (std::getline(file, line)) {
|
||||
std::stringstream ss(line);
|
||||
std::string token;
|
||||
std::vector<std::string> tokens;
|
||||
while (std::getline(ss, token, ',')) {
|
||||
tokens.emplace_back(token);
|
||||
}
|
||||
|
||||
const auto region = std::stoi(tokens[0]);
|
||||
const auto plane = std::stoi(tokens[1]);
|
||||
const auto x = std::stoi(tokens[2]);
|
||||
const auto y = std::stoi(tokens[3]);
|
||||
const auto flag =
|
||||
static_cast<Pathfinding::COLLISION_FLAG>(std::stoi(tokens[4]));
|
||||
|
||||
mapped_regions.insert(region);
|
||||
collision_data.emplace(Tile(x, y, plane), flag);
|
||||
}
|
||||
}
|
||||
|
||||
void visit_neighbors(const path_step &step, const settings& settings_, const auto &func) {
|
||||
Tile tile;
|
||||
if (std::holds_alternative<Tile>(step)) {
|
||||
tile = std::get<Tile>(step);
|
||||
} else {
|
||||
const auto &lnk = std::get<std::shared_ptr<link>>(step);
|
||||
tile = lnk->destination;
|
||||
}
|
||||
|
||||
Pathfinding::COLLISION_FLAG flags = Pathfinding::COLLISION_FLAG::OPEN;
|
||||
auto it = collision_data.find(tile);
|
||||
if (it != collision_data.end()) {
|
||||
flags = it->second;
|
||||
}
|
||||
|
||||
const auto blocked = [](Pathfinding::COLLISION_FLAG flags) {
|
||||
return (flags & Pathfinding::BLOCKED) || (flags & Pathfinding::OCCUPIED);
|
||||
};
|
||||
|
||||
constexpr std::array<std::tuple<int, int, Pathfinding::COLLISION_FLAG>, 4>
|
||||
directions = {{{0, 1, Pathfinding::COLLISION_FLAG::NORTH},
|
||||
{1, 0, Pathfinding::COLLISION_FLAG::EAST},
|
||||
{0, -1, Pathfinding::COLLISION_FLAG::SOUTH},
|
||||
{-1, 0, Pathfinding::COLLISION_FLAG::WEST}}};
|
||||
|
||||
for (const auto &[dx, dy, dirFlag] : directions) {
|
||||
if ((flags & dirFlag) == Pathfinding::COLLISION_FLAG::OPEN) {
|
||||
int newX = tile.X + dx;
|
||||
int newY = tile.Y + dy;
|
||||
|
||||
const auto neighbor = Tile(newX, newY, tile.Plane);
|
||||
|
||||
const auto link_it = links.find(neighbor);
|
||||
if (link_it != links.end()) {
|
||||
func(link_it->second);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto neighbor_it = collision_data.find(neighbor);
|
||||
if (neighbor_it != collision_data.end()) {
|
||||
if (!blocked(neighbor_it->second)) {
|
||||
func(neighbor);
|
||||
}
|
||||
} else {
|
||||
func(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path find_path(const Tile &start, const Tile &end, const settings &settings_) {
|
||||
const auto compare = [](const std::shared_ptr<path_node> &a,
|
||||
const std::shared_ptr<path_node> &b) {
|
||||
return a->cost > b->cost;
|
||||
};
|
||||
|
||||
const auto heuristic = [](const path_step &a, const path_step &b) {
|
||||
const Tile &tileA =
|
||||
std::holds_alternative<Tile>(a)
|
||||
? std::get<Tile>(a)
|
||||
: std::get<std::shared_ptr<link>>(a)->destination;
|
||||
const Tile &tileB =
|
||||
std::holds_alternative<Tile>(b)
|
||||
? std::get<Tile>(b)
|
||||
: std::get<std::shared_ptr<link>>(b)->destination;
|
||||
|
||||
return std::abs(tileA.X - tileB.X) + std::abs(tileA.Y - tileB.Y) +
|
||||
std::abs(tileA.Plane - tileB.Plane);
|
||||
};
|
||||
|
||||
std::priority_queue<std::shared_ptr<path_node>,
|
||||
std::vector<std::shared_ptr<path_node>>,
|
||||
decltype(compare)>
|
||||
queue(compare);
|
||||
|
||||
std::unordered_map<path_step, double> costs;
|
||||
std::unordered_set<path_step> visited;
|
||||
|
||||
queue.emplace(std::make_shared<path_node>(start, nullptr, 0.0));
|
||||
costs[start] = 0.0;
|
||||
|
||||
while (!queue.empty()) {
|
||||
auto current = queue.top();
|
||||
queue.pop();
|
||||
|
||||
if (heuristic(current->step, end) == 0) { // Reached destination
|
||||
path p;
|
||||
while (current != nullptr) {
|
||||
p.emplace_back(current->step);
|
||||
current = current->parent;
|
||||
}
|
||||
|
||||
std::reverse(p.begin(), p.end());
|
||||
return p;
|
||||
}
|
||||
|
||||
visit_neighbors(current->step, settings_, [&](const auto &neighbor) {
|
||||
using neighbor_t = std::decay_t<decltype(neighbor)>;
|
||||
if constexpr (std::is_same_v<Tile, neighbor_t>) {
|
||||
double new_cost = costs[current->step] +
|
||||
1; // Assuming constant cost of 1 for each step
|
||||
|
||||
if (visited.find(neighbor) == visited.end() ||
|
||||
new_cost < costs[neighbor]) {
|
||||
costs[neighbor] = new_cost;
|
||||
double priority = new_cost + heuristic(neighbor, end);
|
||||
queue.emplace(
|
||||
std::make_shared<path_node>(neighbor, current, priority));
|
||||
visited.insert(neighbor);
|
||||
}
|
||||
} else if constexpr (std::is_same_v<std::shared_ptr<link>, neighbor_t>) {
|
||||
auto new_cost = costs[current->step] + 1;
|
||||
if (visited.find(neighbor->destination) == visited.end() ||
|
||||
new_cost < costs[neighbor->destination]) {
|
||||
costs[neighbor->destination] = new_cost;
|
||||
double priority = new_cost + heuristic(neighbor->destination, end);
|
||||
queue.emplace(
|
||||
std::make_shared<path_node>(neighbor, current, priority));
|
||||
visited.insert(neighbor);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return path();
|
||||
}
|
||||
|
||||
void paint_path(Tile start, Tile end, const settings &settings_) {
|
||||
auto path_ = find_path(start, end, settings_);
|
||||
for (const auto &step : path_) {
|
||||
if (const auto tile = std::get_if<Tile>(&step)) {
|
||||
Paint::DrawTile(*tile, 255, 0, 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace bt::pathfinding
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <bt/pathfinding.hpp>
|
||||
|
||||
namespace bt::pathfinding {
|
||||
class game_object : public link {
|
||||
public:
|
||||
Tile tile;
|
||||
std::string action;
|
||||
|
||||
game_object(Tile tile, std::string action, Tile start,
|
||||
Tile destination) noexcept
|
||||
: link(start, destination), tile(tile), action(action) {}
|
||||
|
||||
bool can_handle() const noexcept override { return true; }
|
||||
|
||||
bool handle() noexcept override {
|
||||
auto player = Players::GetLocal();
|
||||
if (!player)
|
||||
return false;
|
||||
|
||||
auto position = player.GetTile();
|
||||
if (!position)
|
||||
return false;
|
||||
|
||||
if (position.DistanceFrom(tile) >= 5)
|
||||
return false;
|
||||
|
||||
const auto obj = GameObjects::Get(tile);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
if (!obj.Interact(action))
|
||||
return false;
|
||||
|
||||
if (!WaitFunc(2000, 50, [&]() {
|
||||
position = player.GetTile();
|
||||
if (!position)
|
||||
return false;
|
||||
|
||||
return position == destination;
|
||||
}))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void load_obstacles() {
|
||||
add_link(std::make_shared<game_object>(Tile(3205, 3208, 0), "Climb-up",
|
||||
Tile(3205, 3208, 0),
|
||||
Tile(3205, 3209, 1)));
|
||||
}
|
||||
} // namespace bt::pathfinding
|
Loading…
Reference in New Issue