/* * Copyright (C) 2020 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #include "resource-loader.hpp" #include "resource-manager.hpp" #include "behavior/ebt.hpp" #include "nlohmann/json.hpp" #include #include #include #include #include #include #include template void parse_argument(T& value, const std::string& string) { std::istringstream stream(string); stream >> value; } template <> void parse_argument(std::string& value, const std::string& string) { value = string; } template std::function pack_function(T (*function)(ebt::context&, Args...), std::list argv) { //if (argv.size() != sizeof...(Args)) // Parse list of strings into tuple of arguments std::tuple...> arguments; if constexpr (sizeof...(Args) > 0) { std::apply([&argv](auto& element, auto&... elements) { parse_argument(element, argv.front()); argv.pop_front(); }, arguments); } return std::bind( [function, arguments](ebt::context& context) -> ebt::status { return std::apply(function, std::tuple_cat(std::make_tuple(context), arguments)); }, std::placeholders::_1); } static ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager); static void load_node_child(ebt::decorator_node* node, const nlohmann::json& json, resource_manager* resource_manager); static void load_node_children(ebt::composite_node* node, const nlohmann::json& json, resource_manager* resource_manager); static ebt::node* load_action_node(const nlohmann::json& json, resource_manager* resource_manager) { // Get function name auto function_it = json.find("function"); if (function_it == json.end()) throw std::runtime_error("load_action_node(): Action node has no function."); std::string function_name = function_it.value().get(); // Get argument vector std::list arguments; auto arguments_it = json.find("arguments"); for (auto it = arguments_it.value().cbegin(); it != arguments_it.value().cend(); ++it) arguments.push_back(it.value().get()); ebt::action* action_node = new ebt::action(); if (function_name == "print") action_node->function = pack_function(ebt::print, arguments); else if (function_name == "print_eid") action_node->function = pack_function(ebt::print_eid, arguments); else if (function_name == "warp_to") action_node->function = pack_function(ebt::warp_to, arguments); return action_node; } static ebt::node* load_selector_node(const nlohmann::json& json, resource_manager* resource_manager) { ebt::selector* selector_node = new ebt::selector(); load_node_children(selector_node, json, resource_manager); return selector_node; } static ebt::node* load_sequence_node(const nlohmann::json& json, resource_manager* resource_manager) { ebt::sequence* sequence_node = new ebt::sequence(); load_node_children(sequence_node, json, resource_manager); return sequence_node; } static ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager) { static const std::map> node_loaders = { {"action", &load_action_node}, {"selector", &load_selector_node}, {"sequence", &load_sequence_node} }; auto node_loader = node_loaders.find(json.key()); if (node_loader == node_loaders.end()) { throw std::runtime_error("load_node(): Unknown behavior tree node type \"" + json.key() + "\""); } return node_loader->second(json.value(), resource_manager); } static void load_node_child(ebt::decorator_node* node, const nlohmann::json& json, resource_manager* resource_manager) { auto it = json.find("child"); node->child = load_node(it.value().cbegin(), resource_manager); } static void load_node_children(ebt::composite_node* node, const nlohmann::json& json, resource_manager* resource_manager) { auto children_it = json.find("children"); for (auto it = children_it.value().cbegin(); it != children_it.value().cend(); ++it) { ebt::node* child = load_node(it.value().begin(), resource_manager); node->children.push_back(child); } } template <> ebt::node* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { // Read file into buffer std::size_t size = static_cast(PHYSFS_fileLength(file)); std::string buffer; buffer.resize(size); PHYSFS_readBytes(file, &buffer[0], size); // Parse json from file buffer nlohmann::json json = nlohmann::json::parse(buffer); if (json.size() != 1) { throw std::runtime_error("resource_loader::load(): Behavior tree must have exactly one root node."); } return load_node(json.cbegin(), resource_manager); }