💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
5.4 KiB

  1. /*
  2. * Copyright (C) 2020 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "resource-loader.hpp"
  20. #include "resource-manager.hpp"
  21. #include "game/behavior/ebt.hpp"
  22. #include <nlohmann/json.hpp>
  23. #include <functional>
  24. #include <map>
  25. #include <stdexcept>
  26. #include <iostream>
  27. #include <type_traits>
  28. #include <sstream>
  29. #include <physfs.h>
  30. template <class T>
  31. void parse_argument(T& value, const std::string& string)
  32. {
  33. std::istringstream stream(string);
  34. stream >> value;
  35. }
  36. template <>
  37. void parse_argument(std::string& value, const std::string& string)
  38. {
  39. value = string;
  40. }
  41. template <class T, class... Args>
  42. std::function<ebt::status(ebt::context&)> pack_function(T (*function)(ebt::context&, Args...), std::list<std::string> argv)
  43. {
  44. //if (argv.size() != sizeof...(Args))
  45. // Parse list of strings into tuple of arguments
  46. std::tuple<std::decay_t<Args>...> arguments;
  47. if constexpr (sizeof...(Args) > 0)
  48. {
  49. std::apply([&argv](auto& element, auto&... elements)
  50. {
  51. parse_argument(element, argv.front());
  52. argv.pop_front();
  53. },
  54. arguments);
  55. }
  56. return std::bind(
  57. [function, arguments](ebt::context& context) -> ebt::status
  58. {
  59. return std::apply(function, std::tuple_cat(std::make_tuple(context), arguments));
  60. },
  61. std::placeholders::_1);
  62. }
  63. static ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager);
  64. static void load_node_child(ebt::decorator_node* node, const nlohmann::json& json, resource_manager* resource_manager);
  65. static void load_node_children(ebt::composite_node* node, const nlohmann::json& json, resource_manager* resource_manager);
  66. static ebt::node* load_action_node(const nlohmann::json& json, resource_manager* resource_manager)
  67. {
  68. // Get function name
  69. auto function_it = json.find("function");
  70. if (function_it == json.end())
  71. throw std::runtime_error("load_action_node(): Action node has no function.");
  72. std::string function_name = function_it.value().get<std::string>();
  73. // Get argument vector
  74. std::list<std::string> arguments;
  75. auto arguments_it = json.find("arguments");
  76. for (auto it = arguments_it.value().cbegin(); it != arguments_it.value().cend(); ++it)
  77. arguments.push_back(it.value().get<std::string>());
  78. ebt::action* action_node = new ebt::action();
  79. if (function_name == "print") action_node->function = pack_function(ebt::print, arguments);
  80. else if (function_name == "print_eid") action_node->function = pack_function(ebt::print_eid, arguments);
  81. else if (function_name == "warp_to") action_node->function = pack_function(ebt::warp_to, arguments);
  82. return action_node;
  83. }
  84. static ebt::node* load_selector_node(const nlohmann::json& json, resource_manager* resource_manager)
  85. {
  86. ebt::selector* selector_node = new ebt::selector();
  87. load_node_children(selector_node, json, resource_manager);
  88. return selector_node;
  89. }
  90. static ebt::node* load_sequence_node(const nlohmann::json& json, resource_manager* resource_manager)
  91. {
  92. ebt::sequence* sequence_node = new ebt::sequence();
  93. load_node_children(sequence_node, json, resource_manager);
  94. return sequence_node;
  95. }
  96. static ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager)
  97. {
  98. static const std::map<std::string, std::function<ebt::node*(const nlohmann::json&, ::resource_manager*)>> node_loaders =
  99. {
  100. {"action", &load_action_node},
  101. {"selector", &load_selector_node},
  102. {"sequence", &load_sequence_node}
  103. };
  104. auto node_loader = node_loaders.find(json.key());
  105. if (node_loader == node_loaders.end())
  106. {
  107. throw std::runtime_error("load_node(): Unknown behavior tree node type \"" + json.key() + "\"");
  108. }
  109. return node_loader->second(json.value(), resource_manager);
  110. }
  111. static void load_node_child(ebt::decorator_node* node, const nlohmann::json& json, resource_manager* resource_manager)
  112. {
  113. auto it = json.find("child");
  114. node->child = load_node(it.value().cbegin(), resource_manager);
  115. }
  116. static void load_node_children(ebt::composite_node* node, const nlohmann::json& json, resource_manager* resource_manager)
  117. {
  118. auto children_it = json.find("children");
  119. for (auto it = children_it.value().cbegin(); it != children_it.value().cend(); ++it)
  120. {
  121. ebt::node* child = load_node(it.value().begin(), resource_manager);
  122. node->children.push_back(child);
  123. }
  124. }
  125. template <>
  126. ebt::node* resource_loader<ebt::node>::load(resource_manager* resource_manager, PHYSFS_File* file)
  127. {
  128. // Read file into buffer
  129. std::size_t size = static_cast<int>(PHYSFS_fileLength(file));
  130. std::string buffer;
  131. buffer.resize(size);
  132. PHYSFS_readBytes(file, &buffer[0], size);
  133. // Parse json from file buffer
  134. nlohmann::json json = nlohmann::json::parse(buffer);
  135. if (json.size() != 1)
  136. {
  137. throw std::runtime_error("resource_loader<ebt::node>::load(): Behavior tree must have exactly one root node.");
  138. }
  139. return load_node(json.cbegin(), resource_manager);
  140. }