various AB behavior nodes added
parent
c19dfa7c06
commit
2c1a4c597e
|
@ -5,6 +5,13 @@ project(bt)
|
|||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED true)
|
||||
|
||||
if (NOT DEFINED ALPACABOT_DIR)
|
||||
set(ALPACABOT_DIR $ENV{USERPROFILE}\\AlpacaBot)
|
||||
endif()
|
||||
|
||||
add_library(bt INTERFACE)
|
||||
|
||||
target_include_directories(bt INTERFACE include)
|
||||
target_include_directories(bt INTERFACE
|
||||
${ALPACABOT_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
|
@ -8,6 +8,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <Game/Core.hpp>
|
||||
|
||||
namespace bt {
|
||||
enum class task_result { failure, success };
|
||||
|
||||
|
@ -133,10 +135,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <typename Context> auto sequence() {
|
||||
return std::make_unique<sequence_node<Context>>();
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
class selector_node : public composite_node<Context> {
|
||||
public:
|
||||
|
@ -156,10 +154,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <typename Context> auto selector() {
|
||||
return std::make_unique<selector_node<Context>>();
|
||||
}
|
||||
|
||||
template <typename Context, typename TickFn>
|
||||
class action_node : public behavior_node<Context> {
|
||||
public:
|
||||
|
@ -171,10 +165,6 @@ private:
|
|||
TickFn _tick;
|
||||
};
|
||||
|
||||
template <typename Context, typename TickFn> auto action(TickFn tick) {
|
||||
return std::make_unique<action_node<Context, TickFn>>(tick);
|
||||
}
|
||||
|
||||
template <typename Context, typename ConditionFn>
|
||||
class conditional_node : public behavior_node<Context> {
|
||||
public:
|
||||
|
@ -191,11 +181,177 @@ private:
|
|||
ConditionFn _condition;
|
||||
};
|
||||
|
||||
template <typename Context, typename ConditionFn>
|
||||
auto conditional(ConditionFn condition) {
|
||||
return std::make_unique<conditional_node<Context, 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 {
|
||||
public:
|
||||
tree_builder() noexcept : _root(nullptr) {}
|
||||
|
@ -217,7 +373,8 @@ public:
|
|||
_children.pop_back();
|
||||
|
||||
for (auto &&child : children) {
|
||||
static_cast<composite_node<Context> *>(node.get())->add_child(std::move(child));
|
||||
static_cast<composite_node<Context> *>(node.get())
|
||||
->add_child(std::move(child));
|
||||
}
|
||||
|
||||
if (_node_stack.empty()) {
|
||||
|
@ -246,7 +403,8 @@ public:
|
|||
_children.pop_back();
|
||||
|
||||
for (auto &&child : children) {
|
||||
static_cast<composite_node<Context> *>(node.get())->add_child(std::move(child));
|
||||
static_cast<composite_node<Context> *>(node.get())
|
||||
->add_child(std::move(child));
|
||||
}
|
||||
|
||||
if (_node_stack.empty()) {
|
||||
|
@ -263,17 +421,131 @@ public:
|
|||
throw std::runtime_error("Action must be within a sequence or selector");
|
||||
}
|
||||
|
||||
_children.back().push_back(std::make_unique<action_node<Context, TickFn>>(tick));
|
||||
_children.back().push_back(
|
||||
std::make_unique<action_node<Context, TickFn>>(tick));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename ConditionFn>
|
||||
tree_builder &conditional(ConditionFn condition) {
|
||||
if (_node_stack.empty()) {
|
||||
throw std::runtime_error("Condition must be within a sequence or selector");
|
||||
throw std::runtime_error(
|
||||
"Condition must be within a sequence or selector");
|
||||
}
|
||||
|
||||
_children.back().push_back(std::make_unique<conditional_node<Context, ConditionFn>>(condition));
|
||||
_children.back().push_back(
|
||||
std::make_unique<conditional_node<Context, ConditionFn>>(condition));
|
||||
return *this;
|
||||
}
|
||||
|
||||
tree_builder &invert() {
|
||||
if (_node_stack.empty()) {
|
||||
throw std::runtime_error("Invert must be within a sequence or selector");
|
||||
}
|
||||
|
||||
if (_children.back().empty()) {
|
||||
throw std::runtime_error("Invert requires a preceding node");
|
||||
}
|
||||
|
||||
auto child = std::move(_children.back().back());
|
||||
_children.back().pop_back();
|
||||
_children.back().push_back(
|
||||
std::make_unique<inverter_node<Context>>(std::move(child)));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -281,6 +553,7 @@ public:
|
|||
if (!_node_stack.empty()) {
|
||||
throw std::runtime_error("Mismatched begin/end");
|
||||
}
|
||||
|
||||
return std::move(_root);
|
||||
}
|
||||
|
||||
|
@ -290,5 +563,4 @@ private:
|
|||
std::vector<std::vector<std::unique_ptr<behavior_node<Context>>>> _children;
|
||||
};
|
||||
|
||||
|
||||
} // namespace bt
|
Loading…
Reference in New Issue