seperated node implementations from single file.
parent
2c1a4c597e
commit
c84c81b71d
|
@ -0,0 +1,130 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bt/node_task.hpp>
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
using yield = std::suspend_always;
|
||||||
|
|
||||||
|
template <typename Context> class behavior_node {
|
||||||
|
public:
|
||||||
|
virtual node_task tick(Context &ctx) noexcept = 0;
|
||||||
|
|
||||||
|
virtual ~behavior_node() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
class composite_node : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
void add_child(std::unique_ptr<behavior_node<Context>> &&child) {
|
||||||
|
_children.push_back(std::move(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<std::unique_ptr<behavior_node<Context>>> _children;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
class decorator_node : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
decorator_node(std::unique_ptr<behavior_node<Context>> child)
|
||||||
|
: _child(std::move(child)) {}
|
||||||
|
|
||||||
|
void set_child(std::unique_ptr<behavior_node<Context>> &&child) {
|
||||||
|
_child = std::move(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<behavior_node<Context>> _child;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
class sequence_node : public composite_node<Context> {
|
||||||
|
public:
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
std::size_t idx = 0;
|
||||||
|
while (idx < this->_children.size()) {
|
||||||
|
auto task = this->_children[idx]->tick(ctx);
|
||||||
|
while (!task.done()) {
|
||||||
|
task.resume();
|
||||||
|
|
||||||
|
if (idx < this->_children.size() - 1)
|
||||||
|
co_await yield{};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.result() == task_result::failure) {
|
||||||
|
co_return task_result::failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
class selector_node : public composite_node<Context> {
|
||||||
|
public:
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
for (std::size_t idx = 0; idx < this->_children.size(); ++idx) {
|
||||||
|
auto task = this->_children[idx]->tick(ctx);
|
||||||
|
while (!task.done()) {
|
||||||
|
task.resume();
|
||||||
|
co_await yield{};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.result() == task_result::success) {
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
co_return task_result::failure;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context, typename TickFn>
|
||||||
|
class action_node : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
action_node(TickFn tick) noexcept : _tick(tick) {}
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override { return _tick(ctx); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
TickFn _tick;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context, typename ConditionFn>
|
||||||
|
class conditional_node : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
conditional_node(ConditionFn condition) noexcept : _condition(condition) {}
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
if (_condition(ctx)) {
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
co_return task_result::failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConditionFn _condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
class inverter_node : public decorator_node<Context> {
|
||||||
|
public:
|
||||||
|
inverter_node(std::unique_ptr<behavior_node<Context>> child)
|
||||||
|
: decorator_node<Context>(std::move(child)) {}
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
auto task = this->_child->tick(ctx);
|
||||||
|
while (!task.done()) {
|
||||||
|
task.resume();
|
||||||
|
co_await yield{};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.result() == task_result::success) {
|
||||||
|
co_return task_result::failure;
|
||||||
|
}
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -10,348 +10,18 @@
|
||||||
|
|
||||||
#include <Game/Core.hpp>
|
#include <Game/Core.hpp>
|
||||||
|
|
||||||
|
#include <bt/behavior_node.hpp>
|
||||||
|
#include <bt/interact.hpp>
|
||||||
|
#include <bt/inventory.hpp>
|
||||||
|
#include <bt/login.hpp>
|
||||||
|
#include <bt/magic.hpp>
|
||||||
|
#include <bt/mainscreen.hpp>
|
||||||
|
#include <bt/navigation.hpp>
|
||||||
|
#include <bt/node_task.hpp>
|
||||||
|
#include <bt/player.hpp>
|
||||||
|
#include <bt/script.hpp>
|
||||||
|
|
||||||
namespace bt {
|
namespace bt {
|
||||||
enum class task_result { failure, success };
|
|
||||||
|
|
||||||
class node_task {
|
|
||||||
public:
|
|
||||||
class promise_type;
|
|
||||||
|
|
||||||
using handle_type = std::coroutine_handle<promise_type>;
|
|
||||||
|
|
||||||
class promise_type {
|
|
||||||
public:
|
|
||||||
std::optional<task_result> result;
|
|
||||||
handle_type get_return_object() noexcept {
|
|
||||||
return handle_type::from_promise(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::suspend_always initial_suspend() noexcept { return {}; }
|
|
||||||
|
|
||||||
std::suspend_always final_suspend() noexcept { return {}; }
|
|
||||||
|
|
||||||
void return_value(task_result r) noexcept { result = r; }
|
|
||||||
|
|
||||||
void unhandled_exception() noexcept {
|
|
||||||
std::terminate(); // TODO: handle exception
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class awaiter {
|
|
||||||
public:
|
|
||||||
awaiter(handle_type handle) noexcept : _handle(handle) {}
|
|
||||||
|
|
||||||
bool await_ready() const noexcept { return false; }
|
|
||||||
|
|
||||||
auto await_suspend(std::coroutine_handle<>) noexcept { return _handle; }
|
|
||||||
|
|
||||||
task_result await_resume() noexcept { return *_handle.promise().result; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
handle_type _handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
node_task(handle_type handle) noexcept : _handle(handle) {}
|
|
||||||
|
|
||||||
node_task(node_task &&other) noexcept : _handle(other._handle) {
|
|
||||||
other._handle = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
~node_task() {
|
|
||||||
if (_handle) {
|
|
||||||
_handle.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node_task &operator=(node_task &&other) noexcept {
|
|
||||||
if (&other != this) {
|
|
||||||
if (_handle) {
|
|
||||||
_handle.destroy();
|
|
||||||
}
|
|
||||||
_handle = other._handle;
|
|
||||||
other._handle = nullptr;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
awaiter operator co_await() noexcept { return awaiter{_handle}; }
|
|
||||||
|
|
||||||
void resume() noexcept { _handle.resume(); }
|
|
||||||
|
|
||||||
bool done() const noexcept { return _handle.done(); }
|
|
||||||
|
|
||||||
task_result result() noexcept { return *_handle.promise().result; }
|
|
||||||
|
|
||||||
std::optional<task_result> try_result() noexcept {
|
|
||||||
return _handle.promise().result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
handle_type _handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
using yield = std::suspend_always;
|
|
||||||
|
|
||||||
template <typename Context> class behavior_node {
|
|
||||||
public:
|
|
||||||
virtual node_task tick(Context &ctx) noexcept = 0;
|
|
||||||
|
|
||||||
virtual ~behavior_node() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Context>
|
|
||||||
class composite_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
void add_child(std::unique_ptr<behavior_node<Context>> &&child) {
|
|
||||||
_children.push_back(std::move(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::vector<std::unique_ptr<behavior_node<Context>>> _children;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Context>
|
|
||||||
class sequence_node : public composite_node<Context> {
|
|
||||||
public:
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
std::size_t idx = 0;
|
|
||||||
while (idx < this->_children.size()) {
|
|
||||||
auto task = this->_children[idx]->tick(ctx);
|
|
||||||
while (!task.done()) {
|
|
||||||
task.resume();
|
|
||||||
|
|
||||||
if (idx < this->_children.size() - 1)
|
|
||||||
co_await yield{};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task.result() == task_result::failure) {
|
|
||||||
co_return task_result::failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
++idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Context>
|
|
||||||
class selector_node : public composite_node<Context> {
|
|
||||||
public:
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
for (std::size_t idx = 0; idx < this->_children.size(); ++idx) {
|
|
||||||
auto task = this->_children[idx]->tick(ctx);
|
|
||||||
while (!task.done()) {
|
|
||||||
task.resume();
|
|
||||||
co_await yield{};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task.result() == task_result::success) {
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
co_return task_result::failure;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Context, typename TickFn>
|
|
||||||
class action_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
action_node(TickFn tick) noexcept : _tick(tick) {}
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override { return _tick(ctx); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
TickFn _tick;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Context, typename ConditionFn>
|
|
||||||
class conditional_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
conditional_node(ConditionFn condition) noexcept : _condition(condition) {}
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
if (_condition(ctx)) {
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
co_return task_result::failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ConditionFn _condition;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Context>
|
|
||||||
class inverter_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
inverter_node(std::unique_ptr<behavior_node<Context>> child)
|
|
||||||
: _child(std::move(child)) {}
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
auto task = _child->tick(ctx);
|
|
||||||
while (!task.done()) {
|
|
||||||
task.resume();
|
|
||||||
co_await yield{};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task.result() == task_result::success) {
|
|
||||||
co_return task_result::failure;
|
|
||||||
}
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<behavior_node<Context>> _child;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Script
|
|
||||||
template <typename Context>
|
|
||||||
class terminate_script_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
Terminate = true;
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Login
|
|
||||||
template<typename Context>
|
|
||||||
class login_player_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
if (!Login::LoginPlayer())
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mainscreen
|
|
||||||
template <typename Context>
|
|
||||||
class is_logged_in_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
constexpr is_logged_in_node() noexcept = default;
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
if (!Mainscreen::IsLoggedIn())
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Player
|
|
||||||
template <typename Context>
|
|
||||||
class is_animating_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
std::int32_t anim_id;
|
|
||||||
|
|
||||||
is_animating_node(std::int32_t anim_id) : anim_id(anim_id) {}
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
const auto player = Players::GetLocal();
|
|
||||||
if (!player)
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
if (player.GetAnimationID() != anim_id)
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inventory
|
|
||||||
template <typename Context, typename ItemID>
|
|
||||||
requires std::same_as<std::int32_t, ItemID> ||
|
|
||||||
std::same_as<std::string, ItemID> ||
|
|
||||||
std::same_as<std::regex, ItemID> ||
|
|
||||||
std::same_as<std::vector<std::int32_t>, ItemID> ||
|
|
||||||
std::same_as<std::vector<std::string>, ItemID>
|
|
||||||
class inventory_contains_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
ItemID item_id;
|
|
||||||
|
|
||||||
inventory_contains_node(ItemID item_id) : item_id(item_id) {}
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
if (!Inventory::Contains(item_id))
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Magic
|
|
||||||
template <typename Context>
|
|
||||||
class select_spell_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
Magic::SPELL spell;
|
|
||||||
|
|
||||||
select_spell_node(Magic::SPELL spell) : spell(spell) {}
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
if (!Magic::SelectSpell(spell))
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Context>
|
|
||||||
class spell_selected_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
Magic::SPELL spell;
|
|
||||||
|
|
||||||
spell_selected_node(Magic::SPELL spell) : spell(spell) {}
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
if (!Magic::IsSpellSelected(spell))
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Context>
|
|
||||||
class cast_spell_node : public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
Magic::SPELL spell;
|
|
||||||
|
|
||||||
cast_spell_node(Magic::SPELL spell) : spell(spell) {}
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
if (!Magic::CastSpell(spell))
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Interact
|
|
||||||
template <typename Context, typename Interactable> class interact_node;
|
|
||||||
|
|
||||||
template <typename Context>
|
|
||||||
class interact_node<Context, Interactable::Item>
|
|
||||||
: public behavior_node<Context> {
|
|
||||||
public:
|
|
||||||
std::string name;
|
|
||||||
std::string action;
|
|
||||||
|
|
||||||
interact_node(const std::string &name, const std::string &action)
|
|
||||||
: name(name), action(action) {}
|
|
||||||
|
|
||||||
node_task tick(Context &ctx) noexcept override {
|
|
||||||
const auto item = Inventory::GetItem(name);
|
|
||||||
if (!item)
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
if (!item.Interact(action))
|
|
||||||
co_return task_result::failure;
|
|
||||||
|
|
||||||
co_return task_result::success;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Context> class tree_builder {
|
template <typename Context> class tree_builder {
|
||||||
public:
|
public:
|
||||||
tree_builder() noexcept : _root(nullptr) {}
|
tree_builder() noexcept : _root(nullptr) {}
|
||||||
|
@ -416,6 +86,16 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <template<typename...> class Node, typename... Args>
|
||||||
|
tree_builder &leaf(Args&&... args) {
|
||||||
|
if (_node_stack.empty()) {
|
||||||
|
throw std::runtime_error("Leaf must be within a sequence or selector");
|
||||||
|
}
|
||||||
|
|
||||||
|
_children.back().push_back(std::make_unique<Node<Context, Args...>>(std::forward<Args>(args)...));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename TickFn> tree_builder &action(TickFn tick) {
|
template <typename TickFn> tree_builder &action(TickFn tick) {
|
||||||
if (_node_stack.empty()) {
|
if (_node_stack.empty()) {
|
||||||
throw std::runtime_error("Action must be within a sequence or selector");
|
throw std::runtime_error("Action must be within a sequence or selector");
|
||||||
|
@ -455,100 +135,6 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_builder& terminate_script() {
|
|
||||||
if (_node_stack.empty()) {
|
|
||||||
throw std::runtime_error("Terminate must be within a sequence or selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
_children.back().push_back(std::make_unique<terminate_script_node<Context>>());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
tree_builder &login_player() {
|
|
||||||
if (_node_stack.empty()) {
|
|
||||||
throw std::runtime_error("Leaf must be within a sequence or selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
_children.back().push_back(std::make_unique<login_player_node<Context>>());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
tree_builder &is_animating(std::int32_t anim_id) {
|
|
||||||
if (_node_stack.empty()) {
|
|
||||||
throw std::runtime_error("Leaf must be within a sequence or selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
_children.back().push_back(
|
|
||||||
std::make_unique<is_animating_node<Context>>(anim_id));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
tree_builder &is_logged_in() {
|
|
||||||
if (_node_stack.empty()) {
|
|
||||||
throw std::runtime_error("Leaf must be within a sequence or selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
_children.back().push_back(std::make_unique<is_logged_in_node<Context>>());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ItemID>
|
|
||||||
requires std::same_as<std::int32_t, ItemID> ||
|
|
||||||
std::same_as<std::string, ItemID> ||
|
|
||||||
std::same_as<std::regex, ItemID> ||
|
|
||||||
std::same_as<std::vector<std::int32_t>, ItemID> ||
|
|
||||||
std::same_as<std::vector<std::string>, ItemID>
|
|
||||||
tree_builder &inventory_contains(ItemID item_id) {
|
|
||||||
if (_node_stack.empty()) {
|
|
||||||
throw std::runtime_error("Leaf must be within a sequence or selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
_children.back().push_back(
|
|
||||||
std::make_unique<inventory_contains_node<Context, ItemID>>(item_id));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
tree_builder &select_spell(Magic::SPELL spell) {
|
|
||||||
if (_node_stack.empty()) {
|
|
||||||
throw std::runtime_error("Leaf must be within a sequence or selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
_children.back().push_back(
|
|
||||||
std::make_unique<select_spell_node<Context>>(spell));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
tree_builder &spell_selected(Magic::SPELL spell) {
|
|
||||||
if (_node_stack.empty()) {
|
|
||||||
throw std::runtime_error("Leaf must be within a sequence or selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
_children.back().push_back(
|
|
||||||
std::make_unique<spell_selected_node<Context>>(spell));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
tree_builder &cast_spell(Magic::SPELL spell) {
|
|
||||||
if (_node_stack.empty()) {
|
|
||||||
throw std::runtime_error("Leaf must be within a sequence or selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
_children.back().push_back(
|
|
||||||
std::make_unique<cast_spell_node<Context>>(spell));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Interactable>
|
|
||||||
tree_builder &interact(const std::string &name, const std::string &action) {
|
|
||||||
if (_node_stack.empty()) {
|
|
||||||
throw std::runtime_error("Leaf must be within a sequence or selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
_children.back().push_back(
|
|
||||||
std::make_unique<interact_node<Context, Interactable>>(name, action));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<behavior_node<Context>> build() {
|
std::unique_ptr<behavior_node<Context>> build() {
|
||||||
if (!_node_stack.empty()) {
|
if (!_node_stack.empty()) {
|
||||||
throw std::runtime_error("Mismatched begin/end");
|
throw std::runtime_error("Mismatched begin/end");
|
||||||
|
@ -556,11 +142,10 @@ public:
|
||||||
|
|
||||||
return std::move(_root);
|
return std::move(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<behavior_node<Context>> _root;
|
std::unique_ptr<behavior_node<Context>> _root;
|
||||||
std::vector<std::unique_ptr<behavior_node<Context>>> _node_stack;
|
std::vector<std::unique_ptr<behavior_node<Context>>> _node_stack;
|
||||||
std::vector<std::vector<std::unique_ptr<behavior_node<Context>>>> _children;
|
std::vector<std::vector<std::unique_ptr<behavior_node<Context>>>> _children;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace bt
|
} // namespace bt
|
|
@ -0,0 +1,60 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Game/Core.hpp>
|
||||||
|
#include <bt/behavior_node.hpp>
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
namespace meta {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept Interactable =
|
||||||
|
std::same_as<T, Interactable::Item> || std::same_as<T, Interactable::NPC> ||
|
||||||
|
std::same_as<T, Interactable::Player> ||
|
||||||
|
std::same_as<T, Interactable::Projectile> ||
|
||||||
|
std::same_as<T, Interactable::Widget> ||
|
||||||
|
std::same_as<T, Interactable::Item>;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Context, typename ActionSource, typename InteractableSource>
|
||||||
|
requires std::is_same_v<std::string, ActionSource> ||
|
||||||
|
std::is_same_v<std::string, std::invoke_result_t<ActionSource>> ||
|
||||||
|
std::is_same_v<std::string, std::invoke_result_t<ActionSource, Context &>> &&
|
||||||
|
meta::Interactable<std::invoke_result_t<InteractableSource>> ||
|
||||||
|
meta::Interactable<std::invoke_result_t<InteractableSource, Context &>>
|
||||||
|
class interact_node : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
ActionSource action_source;
|
||||||
|
InteractableSource interactable_source;
|
||||||
|
|
||||||
|
interact_node(ActionSource action_source,
|
||||||
|
InteractableSource interactable_source)
|
||||||
|
: action_source(action_source), interactable_source(interactable_source) {}
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
const auto interactable = [&]() {
|
||||||
|
if constexpr (std::is_invocable_v<InteractableSource>)
|
||||||
|
return interactable_source();
|
||||||
|
else if constexpr (std::is_invocable_v<InteractableSource, Context &>)
|
||||||
|
return interactable_source(ctx);
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (!interactable)
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
const auto action = [&]() {
|
||||||
|
if constexpr (std::is_invocable_v<ActionSource>)
|
||||||
|
return action_source();
|
||||||
|
else if constexpr (std::is_invocable_v<ActionSource, Context &>)
|
||||||
|
return action_source(ctx);
|
||||||
|
else if constexpr (std::is_same_v<std::string, ActionSource>)
|
||||||
|
return action_source;
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (!interactable.Interact(action))
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bt
|
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Game/Core.hpp>
|
||||||
|
#include <bt/behavior_node.hpp>
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
|
||||||
|
template <typename Context, typename ItemID>
|
||||||
|
requires std::same_as<std::int32_t, ItemID> ||
|
||||||
|
std::same_as<std::string, ItemID> ||
|
||||||
|
std::same_as<std::regex, ItemID> ||
|
||||||
|
std::same_as<std::vector<std::int32_t>, ItemID> ||
|
||||||
|
std::same_as<std::initializer_list<std::int32_t>, ItemID> ||
|
||||||
|
std::same_as<std::vector<std::string>, ItemID> ||
|
||||||
|
std::same_as<std::initializer_list<std::string>, ItemID>
|
||||||
|
class inventory_contains_node : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
ItemID item_id;
|
||||||
|
|
||||||
|
inventory_contains_node(ItemID item_id) : item_id(item_id) {}
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
if (!Inventory::Contains(item_id))
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bt
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Game/Core.hpp>
|
||||||
|
#include <bt/behavior_node.hpp>
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
template <typename Context>
|
||||||
|
class login_player : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
if (!Login::LoginPlayer())
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bt
|
|
@ -0,0 +1,128 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Game/Core.hpp>
|
||||||
|
|
||||||
|
#include <bt/behavior_node.hpp>
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
template <typename Context, typename SpellSource> class select_spell_node;
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
class select_spell_node<Context, Magic::SPELL> : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
Magic::SPELL spell;
|
||||||
|
|
||||||
|
select_spell_node(Magic::SPELL spell) : spell(spell) {}
|
||||||
|
|
||||||
|
node_task tick(Context &) noexcept override {
|
||||||
|
if (!Magic::SelectSpell(spell))
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context, typename SpellGetter>
|
||||||
|
requires std::is_same_v<Magic::SPELL,
|
||||||
|
std::result_of_t<SpellGetter(Context &)>>
|
||||||
|
class select_spell_node<Context, SpellGetter> : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
SpellGetter spell_getter;
|
||||||
|
|
||||||
|
select_spell_node(SpellGetter spell_getter) : spell_getter(spell_getter) {}
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
if (!Magic::SelectSpell(spell_getter(ctx)))
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context, typename SpellSource> class spell_selected_node;
|
||||||
|
|
||||||
|
template<typename Context>
|
||||||
|
class spell_selected_node<Context, Magic::SPELL>
|
||||||
|
: public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
Magic::SPELL spell;
|
||||||
|
|
||||||
|
spell_selected_node(Magic::SPELL spell) : spell(spell) {}
|
||||||
|
|
||||||
|
node_task tick(Context &) noexcept override {
|
||||||
|
if (!Magic::IsSpellSelected(spell))
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context, typename SpellGetter>
|
||||||
|
requires std::is_same_v<Magic::SPELL,
|
||||||
|
std::result_of_t<SpellGetter(Context &)>>
|
||||||
|
class spell_selected_node<Context, SpellGetter>
|
||||||
|
: public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
SpellGetter spell_getter;
|
||||||
|
|
||||||
|
spell_selected_node(SpellGetter spell_getter) : spell_getter(spell_getter) {}
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
if (!Magic::IsSpellSelected(spell_getter(ctx)))
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// template <typename Context>
|
||||||
|
// class cast_spell_node : public behavior_node<Context> {
|
||||||
|
// public:
|
||||||
|
// Magic::SPELL spell;
|
||||||
|
|
||||||
|
// cast_spell_node(Magic::SPELL spell) : spell(spell) {}
|
||||||
|
|
||||||
|
// node_task tick(Context &ctx) noexcept override {
|
||||||
|
// if (!Magic::CastSpell(spell))
|
||||||
|
// co_return task_result::failure;
|
||||||
|
|
||||||
|
// co_return task_result::success;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
template <typename Context, typename SpellSource>
|
||||||
|
class cast_spell_node;
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
class cast_spell_node<Context, Magic::SPELL> : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
Magic::SPELL spell;
|
||||||
|
|
||||||
|
cast_spell_node(Magic::SPELL spell) : spell(spell) {}
|
||||||
|
|
||||||
|
node_task tick(Context &) noexcept override {
|
||||||
|
if (!Magic::CastSpell(spell))
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context, typename SpellGetter>
|
||||||
|
requires std::is_same_v<Magic::SPELL,
|
||||||
|
std::result_of_t<SpellGetter(Context &)>>
|
||||||
|
class cast_spell_node<Context, SpellGetter> : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
SpellGetter spell_getter;
|
||||||
|
|
||||||
|
cast_spell_node(SpellGetter spell_getter) : spell_getter(spell_getter) {}
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
if (!Magic::CastSpell(spell_getter(ctx)))
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bt
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Game/Core.hpp>
|
||||||
|
#include <bt/behavior_node.hpp>
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
template <typename Context>
|
||||||
|
class is_logged_in_node : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
constexpr is_logged_in_node() noexcept = default;
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
if (!Mainscreen::IsLoggedIn())
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bt
|
|
@ -0,0 +1,193 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Game/Core.hpp>
|
||||||
|
|
||||||
|
#include <bt/behavior_node.hpp>
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#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
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
namespace pathfinding {
|
||||||
|
class obstacle {
|
||||||
|
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) {}
|
||||||
|
|
||||||
|
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);
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace bt
|
|
@ -0,0 +1,85 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <coroutine>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
enum class task_result { failure, success };
|
||||||
|
|
||||||
|
class node_task {
|
||||||
|
public:
|
||||||
|
class promise_type;
|
||||||
|
|
||||||
|
using handle_type = std::coroutine_handle<promise_type>;
|
||||||
|
|
||||||
|
class promise_type {
|
||||||
|
public:
|
||||||
|
std::optional<task_result> result;
|
||||||
|
handle_type get_return_object() noexcept {
|
||||||
|
return handle_type::from_promise(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::suspend_always initial_suspend() noexcept { return {}; }
|
||||||
|
|
||||||
|
std::suspend_always final_suspend() noexcept { return {}; }
|
||||||
|
|
||||||
|
void return_value(task_result r) noexcept { result = r; }
|
||||||
|
|
||||||
|
void unhandled_exception() noexcept {
|
||||||
|
// std::terminate(); // TODO: handle exception
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class awaiter {
|
||||||
|
public:
|
||||||
|
awaiter(handle_type handle) noexcept : _handle(handle) {}
|
||||||
|
|
||||||
|
bool await_ready() const noexcept { return false; }
|
||||||
|
|
||||||
|
auto await_suspend(std::coroutine_handle<>) noexcept { return _handle; }
|
||||||
|
|
||||||
|
task_result await_resume() noexcept { return *_handle.promise().result; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
handle_type _handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
node_task(handle_type handle) noexcept : _handle(handle) {}
|
||||||
|
|
||||||
|
node_task(node_task &&other) noexcept : _handle(other._handle) {
|
||||||
|
other._handle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
~node_task() {
|
||||||
|
if (_handle) {
|
||||||
|
_handle.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node_task &operator=(node_task &&other) noexcept {
|
||||||
|
if (&other != this) {
|
||||||
|
if (_handle) {
|
||||||
|
_handle.destroy();
|
||||||
|
}
|
||||||
|
_handle = other._handle;
|
||||||
|
other._handle = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
awaiter operator co_await() noexcept { return awaiter{_handle}; }
|
||||||
|
|
||||||
|
void resume() noexcept { _handle.resume(); }
|
||||||
|
|
||||||
|
bool done() const noexcept { return _handle.done(); }
|
||||||
|
|
||||||
|
task_result result() noexcept { return *_handle.promise().result; }
|
||||||
|
|
||||||
|
std::optional<task_result> try_result() noexcept {
|
||||||
|
return _handle.promise().result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
handle_type _handle;
|
||||||
|
};
|
||||||
|
} // namespace bt
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Game/Core.hpp>
|
||||||
|
#include <bt/behavior_node.hpp>
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
template <typename Context, typename Animation>
|
||||||
|
class is_animating_node : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
Animation anim_id;
|
||||||
|
|
||||||
|
is_animating_node(std::int32_t anim_id) : anim_id(anim_id) {}
|
||||||
|
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
const auto player = Players::GetLocal();
|
||||||
|
if (!player)
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
if (player.GetAnimationID() != anim_id)
|
||||||
|
co_return task_result::failure;
|
||||||
|
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bt
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Game/Core.hpp>
|
||||||
|
#include <bt/behavior_node.hpp>
|
||||||
|
|
||||||
|
namespace bt {
|
||||||
|
template <typename Context>
|
||||||
|
class terminate_script_node : public behavior_node<Context> {
|
||||||
|
public:
|
||||||
|
node_task tick(Context &ctx) noexcept override {
|
||||||
|
Terminate = true;
|
||||||
|
co_return task_result::success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue