|
|
- #ifndef ENTT_ENTITY_PROTOTYPE_HPP
- #define ENTT_ENTITY_PROTOTYPE_HPP
-
-
- #include <tuple>
- #include <utility>
- #include <cstddef>
- #include <type_traits>
- #include <unordered_map>
- #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.<br/>
- * 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<typename Entity>
- class basic_prototype {
- using basic_fn_type = void(const basic_prototype &, basic_registry<Entity> &, const Entity);
- using component_type = typename basic_registry<Entity>::component_type;
-
- template<typename Component>
- 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<Entity>;
- /*! @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<typename Component, typename... Args>
- 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<component_wrapper<Component>>(proto.entity);
- other.template assign_or_replace<Component>(dst, wrapper.component);
- };
-
- handler.assign = [](const basic_prototype &proto, registry_type &other, const Entity dst) {
- if(!other.template has<Component>(dst)) {
- const auto &wrapper = proto.reg->template get<component_wrapper<Component>>(proto.entity);
- other.template assign<Component>(dst, wrapper.component);
- }
- };
-
- handlers[reg->template type<Component>()] = handler;
- auto &wrapper = reg->template assign_or_replace<component_wrapper<Component>>(entity, Component{std::forward<Args>(args)...});
- return wrapper.component;
- }
-
- /**
- * @brief Removes the given component from a prototype.
- * @tparam Component Type of component to remove.
- */
- template<typename Component>
- void unset() ENTT_NOEXCEPT {
- reg->template reset<component_wrapper<Component>>(entity);
- handlers.erase(reg->template type<Component>());
- }
-
- /**
- * @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<typename... Component>
- bool has() const ENTT_NOEXCEPT {
- return reg->template has<component_wrapper<Component>...>(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.<br/>
- * 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<typename... Component>
- decltype(auto) get() const ENTT_NOEXCEPT {
- if constexpr(sizeof...(Component) == 1) {
- return (std::as_const(*reg).template get<component_wrapper<Component...>>(entity).component);
- } else {
- return std::tuple<std::add_const_t<Component> &...>{get<Component>()...};
- }
- }
-
- /*! @copydoc get */
- template<typename... Component>
- inline decltype(auto) get() ENTT_NOEXCEPT {
- if constexpr(sizeof...(Component) == 1) {
- return (const_cast<Component &>(std::as_const(*this).template get<Component>()), ...);
- } else {
- return std::tuple<Component &...>{get<Component>()...};
- }
- }
-
- /**
- * @brief Returns pointers to the given components.
- * @tparam Component Types of components to get.
- * @return Pointers to the components owned by the prototype.
- */
- template<typename... Component>
- auto try_get() const ENTT_NOEXCEPT {
- if constexpr(sizeof...(Component) == 1) {
- const auto *wrapper = reg->template try_get<component_wrapper<Component...>>(entity);
- return wrapper ? &wrapper->component : nullptr;
- } else {
- return std::tuple<std::add_const_t<Component> *...>{try_get<Component>()...};
- }
- }
-
- /*! @copydoc try_get */
- template<typename... Component>
- inline auto try_get() ENTT_NOEXCEPT {
- if constexpr(sizeof...(Component) == 1) {
- return (const_cast<Component *>(std::as_const(*this).template try_get<Component>()), ...);
- } else {
- return std::tuple<Component *...>{try_get<Component>()...};
- }
- }
-
- /**
- * @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.<br/>
- * 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.<br/>
- * 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.<br/>
- * 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.<br/>
- * 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.<br/>
- * 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.<br/>
- * 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.<br/>
- * 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.<br/>
- * 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.<br/>
- * 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.<br/>
- * 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<registry_type &>(std::as_const(*this).backend());
- }
-
- private:
- std::unordered_map<component_type, component_handler> handlers;
- registry_type *reg;
- entity_type entity;
- };
-
-
- }
-
-
- #endif // ENTT_ENTITY_PROTOTYPE_HPP
|