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,10 +181,176 @@ 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: | ||||
|  | @ -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
 | ||||
| } // namespace bt
 | ||||
		Loading…
	
		Reference in New Issue