diff --git a/src/entt/entity/prototype.hpp b/src/entt/entity/prototype.hpp new file mode 100644 index 000000000..367d52e37 --- /dev/null +++ b/src/entt/entity/prototype.hpp @@ -0,0 +1,226 @@ +#ifndef ENTT_ENTITY_PROTOTYPE_HPP +#define ENTT_ENTITY_PROTOTYPE_HPP + +#include "registry.hpp" + +namespace entt { + +/** + * @brief A prototype entity for creating new entities + * + * Prototype provides a similar interface to the registry except that Prototype + * stores a single entity. This entity is not apart of the registry so it is not + * seen by views. The Prototype can be used to accommodate components to an + * entity on the registry. + * + * Note that components stored in the Prototype must have copy constructors to + * initialize an entity. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class Prototype { +public: + using entity_t = Entity; + using registry_t = entt::Registry; + using family_t = entt::Family; + +private: + struct StorageBase { + virtual ~StorageBase() = default; + virtual void accommodate(registry_t &, entity_t) const ENTT_NOEXCEPT = 0; + }; + + template + struct Storage : StorageBase { + void accommodate(registry_t ®, const entity_t entity) const ENTT_NOEXCEPT override { + reg.template accommodate(entity, comp); + } + + Component comp; + }; + +public: + Prototype() = default; + Prototype(Prototype &&) = default; + Prototype &operator=(Prototype &&) = default; + + /** + * @brief Checks if there exists at least one component assigned + * + * @return True if no components are assigned + */ + bool empty() const ENTT_NOEXCEPT { + return std::all_of(comps.cbegin(), comps.cend(), [] (auto comp) { + return comp == nullptr; + }); + } + + /** + * @brief Accommodate copies of the components to the given entity + */ + void operator()(registry_t ®, const entity_t entity) const ENTT_NOEXCEPT { + for (const std::unique_ptr &component : comps) { + if (component) { + component->accommodate(reg, entity); + } + } + } + /** + * @brief Create an new entity assign copies of the components to it + * + * @return Newly created entity + */ + entity_t operator()(registry_t ®) const ENTT_NOEXCEPT { + const entity_t entity = reg.create(); + (*this)(reg, entity); + return entity; + } + + /** + * @brief Returns the numeric identifier of a type of component at runtime + * + * The component doesn't need to be assigned to the prototype for this + * function to return a valid ID. The IDs are not synced with the registry. + * + * @tparam Component Type of component to query. + * @return Runtime numeric identifier of the given type of component + */ + template + size_t type() const ENTT_NOEXCEPT { + return family_t::template type(); + } + + /** + * @brief Assigns the given component to the prototype + * + * @warning + * An assertion will abort the execution at runtime in debug mode in case + * the prototype already owns the given component + * + * @tparam Component Type of component to create + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + Component &assign(Args &&... args) { + assert(!has()); + auto component = std::make_unique>(); + component->comp = Component {std::forward(args)...}; + const size_t index = type(); + while (comps.size() <= index) { + comps.emplace_back(); + } + Component &comp = component->comp; + comps[index] = std::move(component); + return comp; + } + + /** + * @brief Removes the given component from the prototype + * + * @warning + * An assertion will abort the execution at runtime in debug mode in case + * the prototype doesn't own the given component + * + * @tparam Component Type of component to remove + */ + template + void remove() ENTT_NOEXCEPT { + assert(has()); + comps[type()] = nullptr; + } + + /** + * @brief Checks if the prototype has all the given components + * + * @tparam Components Components that the prototype must own + * @return True if the prototype owns all of the components, false otherwise. + */ + template + bool has() const ENTT_NOEXCEPT { + bool all = true; + [[maybe_unused]] + bool acc[] = {(all = all && type() < comps.size() && comps[type()] != nullptr)...}; + return all; + } + + /** + * @brief Returns a const reference to the given component + * + * @warning + * An assertion will abort the execution at runtime in debug mode in case + * the prototype doesn't own the given component + * + * @tparam Component Type of component to get + * @return A reference to the component + */ + template + const Component &get() const ENTT_NOEXCEPT { + assert(has()); + return static_cast *>(comps[type()].get())->comp; + } + + /** + * @brief Returns a reference to the given component + * + * @warning + * An assertion will abort the execution at runtime in debug mode in case + * the prototype doesn't own the given component + * + * @tparam Component Type of component to get + * @return A reference to the component + */ + template + Component &get() ENTT_NOEXCEPT { + assert(has()); + return static_cast *>(comps[type()].get())->comp; + } + + /** + * @brief Replaces the given component + * + * @warning + * An assertion will abort the execution at runtime in debug mode in case + * the prototype doesn't own the given component + * + * @tparam Component Type of component to replace + * @tparam Args Types of arguments to use to construct the component + * @param args Parameters to use to initialize the component + * @return A refernce to the newly created component + */ + template + Component &replace(Args &&... args) ENTT_NOEXCEPT { + return (get() = Component{std::forward(args)...}); + } + + /** + * @brief Assigns of replaces the given component + * + * @tparam Component Type of component to replace + * @tparam Args Types of arguments to use to construct the component + * @param args Parameters to use to initialize the component + * @return A refernce to the newly created component + */ + template + Component &accommodate(Args &&... args) ENTT_NOEXCEPT { + if (has()) { + return replace(std::forward(args)...); + } else { + return assign(std::forward(args)...); + } + } + +private: + std::vector> comps; +}; + +/** + * @brief Default prototype + */ +using DefaultPrototype = Prototype; + +} + +#endif