#ifndef ENTT_ENTITY_PROTOTYPE_HPP #define ENTT_ENTITY_PROTOTYPE_HPP #include #include #include #include #include #include "../config/config.h" #include "registry.hpp" #include "entity.hpp" #include "fwd.hpp" namespace entt { /** * @brief Prototype container for _concepts_. * * A prototype is used to define a _concept_ in terms of components.
* Prototypes act as templates for those specific types of an application which * users would otherwise define through a series of component assignments to * entities. In other words, prototypes can be used to assign components to * entities of a registry at once. * * @note * Components used along with prototypes must be copy constructible. Prototypes * wrap component types with custom types, so they do not interfere with other * users of the registry they were built with. * * @warning * Prototypes directly use their underlying registries to store entities and * components for their purposes. Users must ensure that the lifetime of a * registry and its contents exceed that of the prototypes that use it. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_prototype { using basic_fn_type = void(const basic_prototype &, basic_registry &, const Entity); using component_type = typename basic_registry::component_type; template struct component_wrapper { Component component; }; struct component_handler { basic_fn_type *assign_or_replace; basic_fn_type *assign; }; void release() { if(reg->valid(entity)) { reg->destroy(entity); } } public: /*! @brief Registry type. */ using registry_type = basic_registry; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /** * @brief Constructs a prototype that is bound to a given registry. * @param ref A valid reference to a registry. */ basic_prototype(registry_type &ref) : reg{&ref}, entity{ref.create()} {} /** * @brief Releases all its resources. */ ~basic_prototype() { release(); } /** * @brief Move constructor. * * After prototype move construction, instances that have been moved from * are placed in a valid but unspecified state. It's highly discouraged to * continue using them. * * @param other The instance to move from. */ basic_prototype(basic_prototype &&other) : handlers{std::move(other.handlers)}, reg{other.reg}, entity{other.entity} { other.entity = null; } /** * @brief Move assignment operator. * * After prototype move assignment, instances that have been moved from are * placed in a valid but unspecified state. It's highly discouraged to * continue using them. * * @param other The instance to move from. * @return This prototype. */ basic_prototype & operator=(basic_prototype &&other) { if(this != &other) { auto tmp{std::move(other)}; handlers.swap(tmp.handlers); std::swap(reg, tmp.reg); std::swap(entity, tmp.entity); } return *this; } /** * @brief Assigns to or replaces the given component of a prototype. * @tparam Component Type of component to assign or replace. * @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 & set(Args &&... args) { component_handler handler; handler.assign_or_replace = [](const basic_prototype &proto, registry_type &other, const Entity dst) { const auto &wrapper = proto.reg->template get>(proto.entity); other.template assign_or_replace(dst, wrapper.component); }; handler.assign = [](const basic_prototype &proto, registry_type &other, const Entity dst) { if(!other.template has(dst)) { const auto &wrapper = proto.reg->template get>(proto.entity); other.template assign(dst, wrapper.component); } }; handlers[reg->template type()] = handler; auto &wrapper = reg->template assign_or_replace>(entity, Component{std::forward(args)...}); return wrapper.component; } /** * @brief Removes the given component from a prototype. * @tparam Component Type of component to remove. */ template void unset() ENTT_NOEXCEPT { reg->template reset>(entity); handlers.erase(reg->template type()); } /** * @brief Checks if a prototype owns all the given components. * @tparam Component Components for which to perform the check. * @return True if the prototype owns all the components, false otherwise. */ template bool has() const ENTT_NOEXCEPT { return reg->template has...>(entity); } /** * @brief Returns references to the given components. * * @warning * Attempting to get a component from a prototype that doesn't own it * results in undefined behavior.
* An assertion will abort the execution at runtime in debug mode if the * prototype doesn't own an instance of the given component. * * @tparam Component Types of components to get. * @return References to the components owned by the prototype. */ template decltype(auto) get() const ENTT_NOEXCEPT { if constexpr(sizeof...(Component) == 1) { return (std::as_const(*reg).template get>(entity).component); } else { return std::tuple &...>{get()...}; } } /*! @copydoc get */ template inline decltype(auto) get() ENTT_NOEXCEPT { if constexpr(sizeof...(Component) == 1) { return (const_cast(std::as_const(*this).template get()), ...); } else { return std::tuple{get()...}; } } /** * @brief Returns pointers to the given components. * @tparam Component Types of components to get. * @return Pointers to the components owned by the prototype. */ template auto try_get() const ENTT_NOEXCEPT { if constexpr(sizeof...(Component) == 1) { const auto *wrapper = reg->template try_get>(entity); return wrapper ? &wrapper->component : nullptr; } else { return std::tuple *...>{try_get()...}; } } /*! @copydoc try_get */ template inline auto try_get() ENTT_NOEXCEPT { if constexpr(sizeof...(Component) == 1) { return (const_cast(std::as_const(*this).template try_get()), ...); } else { return std::tuple{try_get()...}; } } /** * @brief Creates a new entity using a given prototype. * * Utility shortcut, equivalent to the following snippet: * * @code{.cpp} * const auto entity = registry.create(); * prototype(registry, entity); * @endcode * * @note * The registry may or may not be different from the one already used by * the prototype. There is also an overload that directly uses the * underlying registry. * * @param other A valid reference to a registry. * @return A valid entity identifier. */ entity_type create(registry_type &other) const { const auto entt = other.create(); assign(other, entt); return entt; } /** * @brief Creates a new entity using a given prototype. * * Utility shortcut, equivalent to the following snippet: * * @code{.cpp} * const auto entity = registry.create(); * prototype(entity); * @endcode * * @note * This overload directly uses the underlying registry as a working space. * Therefore, the components of the prototype and of the entity will share * the same registry. * * @return A valid entity identifier. */ inline entity_type create() const { return create(*reg); } /** * @brief Assigns the components of a prototype to a given entity. * * Assigning a prototype to an entity won't overwrite existing components * under any circumstances.
* In other words, only those components that the entity doesn't own yet are * copied over. All the other components remain unchanged. * * @note * The registry may or may not be different from the one already used by * the prototype. There is also an overload that directly uses the * underlying registry. * * @warning * Attempting to use an invalid entity results in undefined behavior.
* An assertion will abort the execution at runtime in debug mode in case of * invalid entity. * * @param other A valid reference to a registry. * @param dst A valid entity identifier. */ void assign(registry_type &other, const entity_type dst) const { for(auto &handler: handlers) { handler.second.assign(*this, other, dst); } } /** * @brief Assigns the components of a prototype to a given entity. * * Assigning a prototype to an entity won't overwrite existing components * under any circumstances.
* In other words, only those components that the entity doesn't own yet are * copied over. All the other components remain unchanged. * * @note * This overload directly uses the underlying registry as a working space. * Therefore, the components of the prototype and of the entity will share * the same registry. * * @warning * Attempting to use an invalid entity results in undefined behavior.
* An assertion will abort the execution at runtime in debug mode in case of * invalid entity. * * @param dst A valid entity identifier. */ inline void assign(const entity_type dst) const { assign(*reg, dst); } /** * @brief Assigns or replaces the components of a prototype for an entity. * * Existing components are overwritten, if any. All the other components * will be copied over to the target entity. * * @note * The registry may or may not be different from the one already used by * the prototype. There is also an overload that directly uses the * underlying registry. * * @warning * Attempting to use an invalid entity results in undefined behavior.
* An assertion will abort the execution at runtime in debug mode in case of * invalid entity. * * @param other A valid reference to a registry. * @param dst A valid entity identifier. */ void assign_or_replace(registry_type &other, const entity_type dst) const { for(auto &handler: handlers) { handler.second.assign_or_replace(*this, other, dst); } } /** * @brief Assigns or replaces the components of a prototype for an entity. * * Existing components are overwritten, if any. All the other components * will be copied over to the target entity. * * @note * This overload directly uses the underlying registry as a working space. * Therefore, the components of the prototype and of the entity will share * the same registry. * * @warning * Attempting to use an invalid entity results in undefined behavior.
* An assertion will abort the execution at runtime in debug mode in case of * invalid entity. * * @param dst A valid entity identifier. */ inline void assign_or_replace(const entity_type dst) const { assign_or_replace(*reg, dst); } /** * @brief Assigns the components of a prototype to an entity. * * Assigning a prototype to an entity won't overwrite existing components * under any circumstances.
* In other words, only the components that the entity doesn't own yet are * copied over. All the other components remain unchanged. * * @note * The registry may or may not be different from the one already used by * the prototype. There is also an overload that directly uses the * underlying registry. * * @warning * Attempting to use an invalid entity results in undefined behavior.
* An assertion will abort the execution at runtime in debug mode in case of * invalid entity. * * @param other A valid reference to a registry. * @param dst A valid entity identifier. */ inline void operator()(registry_type &other, const entity_type dst) const ENTT_NOEXCEPT { assign(other, dst); } /** * @brief Assigns the components of a prototype to an entity. * * Assigning a prototype to an entity won't overwrite existing components * under any circumstances.
* In other words, only the components that the entity doesn't own yet are * copied over. All the other components remain unchanged. * * @note * This overload directly uses the underlying registry as a working space. * Therefore, the components of the prototype and of the entity will share * the same registry. * * @warning * Attempting to use an invalid entity results in undefined behavior.
* An assertion will abort the execution at runtime in debug mode in case of * invalid entity. * * @param dst A valid entity identifier. */ inline void operator()(const entity_type dst) const ENTT_NOEXCEPT { assign(*reg, dst); } /** * @brief Creates a new entity using a given prototype. * * Utility shortcut, equivalent to the following snippet: * * @code{.cpp} * const auto entity = registry.create(); * prototype(registry, entity); * @endcode * * @note * The registry may or may not be different from the one already used by * the prototype. There is also an overload that directly uses the * underlying registry. * * @param other A valid reference to a registry. * @return A valid entity identifier. */ inline entity_type operator()(registry_type &other) const ENTT_NOEXCEPT { return create(other); } /** * @brief Creates a new entity using a given prototype. * * Utility shortcut, equivalent to the following snippet: * * @code{.cpp} * const auto entity = registry.create(); * prototype(entity); * @endcode * * @note * This overload directly uses the underlying registry as a working space. * Therefore, the components of the prototype and of the entity will share * the same registry. * * @return A valid entity identifier. */ inline entity_type operator()() const ENTT_NOEXCEPT { return create(*reg); } /** * @brief Returns a reference to the underlying registry. * @return A reference to the underlying registry. */ inline const registry_type & backend() const ENTT_NOEXCEPT { return *reg; } /*! @copydoc backend */ inline registry_type & backend() ENTT_NOEXCEPT { return const_cast(std::as_const(*this).backend()); } private: std::unordered_map handlers; registry_type *reg; entity_type entity; }; } #endif // ENTT_ENTITY_PROTOTYPE_HPP