💿🐜 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.5 KiB

/*
* Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
*/
#include "resource-loader.hpp"
#include "resource-manager.hpp"
#include "ecs/ebt.hpp"
#include <nlohmann/json.hpp>
#include <functional>
#include <map>
#include <stdexcept>
#include <iostream>
#include <type_traits>
#include <sstream>
#include <physfs.h>
template <class T>
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 <class T, class... Args>
std::function<ecs::ebt::status(ecs::ebt::context&)> pack_function(T (*function)(ecs::ebt::context&, Args...), std::list<std::string> argv)
{
//if (argv.size() != sizeof...(Args))
// Parse list of strings into tuple of arguments
std::tuple<std::decay_t<Args>...> 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](ecs::ebt::context& context) -> ecs::ebt::status
{
return std::apply(function, std::tuple_cat(std::make_tuple(context), arguments));
},
std::placeholders::_1);
}
static ecs::ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager);
static void load_node_child(ecs::ebt::decorator_node* node, const nlohmann::json& json, resource_manager* resource_manager);
static void load_node_children(ecs::ebt::composite_node* node, const nlohmann::json& json, resource_manager* resource_manager);
static ecs::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<std::string>();
// Get argument vector
std::list<std::string> 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<std::string>());
ecs::ebt::action* action_node = new ecs::ebt::action();
if (function_name == "print") action_node->function = pack_function(ecs::ebt::print, arguments);
else if (function_name == "print_eid") action_node->function = pack_function(ecs::ebt::print_eid, arguments);
else if (function_name == "warp_to") action_node->function = pack_function(ecs::ebt::warp_to, arguments);
return action_node;
}
static ecs::ebt::node* load_selector_node(const nlohmann::json& json, resource_manager* resource_manager)
{
ecs::ebt::selector* selector_node = new ecs::ebt::selector();
load_node_children(selector_node, json, resource_manager);
return selector_node;
}
static ecs::ebt::node* load_sequence_node(const nlohmann::json& json, resource_manager* resource_manager)
{
ecs::ebt::sequence* sequence_node = new ecs::ebt::sequence();
load_node_children(sequence_node, json, resource_manager);
return sequence_node;
}
static ecs::ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager)
{
static const std::map<std::string, std::function<ecs::ebt::node*(const nlohmann::json&, ::resource_manager*)>> 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(ecs::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(ecs::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)
{
ecs::ebt::node* child = load_node(it.value().begin(), resource_manager);
node->children.push_back(child);
}
}
template <>
ecs::ebt::node* resource_loader<ecs::ebt::node>::load(resource_manager* resource_manager, PHYSFS_File* file)
{
// Read file into buffer
std::size_t size = static_cast<int>(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<ecs::ebt::node>::load(): Behavior tree must have exactly one root node.");
}
return load_node(json.cbegin(), resource_manager);
}