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