Program Listing for File Actor.hpp

Return to documentation for file (engine/include/Cacao/Actor.hpp)

#pragma once

#include "DllHelper.hpp"
#include "Transform.hpp"
#include "Exceptions.hpp"

#include <cstdint>
#include <memory>
#include <string>
#include <typeindex>

#include "crossguid/guid.hpp"

namespace Cacao {
    class Actor;
    class World;

    class ActorRef {
      public:
        ActorRef() {}

        Actor* operator->();

        const Actor* operator->() const;

        operator bool() const noexcept;

        bool operator==(const ActorRef& rhs) const noexcept;

      private:
        friend class Actor;
        friend class World;

        //Non-owning World pointer
        std::weak_ptr<World> world;

        //Actor slot access information
        uint64_t slotID;
        uint64_t generation;

        //Null state
        bool null = true;

        //Hidden valid handle constructor
        ActorRef(std::weak_ptr<World> world, uint64_t slot, uint64_t generation) : world(world), slotID(slot), generation(generation), null(false) {}

        //Hidden resolver function
        void* Resolve() const noexcept;
    };

    class CACAO_API Component {
      public:
        bool IsEnabled() const;

        void SetEnabled(bool state) {
            enabled = state;
            if(IsEnabled()) {
                OnEnable();
            }
        }

        ActorRef GetOwner() const {
            Check<NonexistentValueException>((bool)owner, "Cannot access the invalid owner of a component!");
            return owner;
        }

        virtual ~Component() {}

      protected:
        Component() {}

        virtual void OnMount() {};

        virtual void OnDelete() {};

        virtual void OnEnable() {};

        virtual void OnDisable() {};

        friend class Actor;

      private:
        bool enabled;
        ActorRef owner;
    };

    class CACAO_API Actor {
      public:
        std::string name;
        const xg::Guid guid;
        Transform transform;

        glm::mat4 GetWorldTransformMatrix() const;

        ActorRef GetParent() const {
            return parent;
        }

        bool IsActive() const;

        void SetActive(bool state);

        void Reparent(ActorRef newParent);

        template<typename T>
            requires std::is_base_of_v<Component, T>
        bool HasComponent() const {
            return components.contains(std::type_index(typeid(T))) && components.at(std::type_index(typeid(T))).component;
        }

        template<typename T, typename... Args>
            requires std::is_base_of_v<Component, T> && std::is_constructible_v<T, Args&&...>
        void MountComponent(Args&&... args) {
            Check<ExistingValueException>(!HasComponent<T>(), "A component of the type specified already exists on the actor!");

            //Prepare objects
            std::type_index type(typeid(T));
            std::unique_ptr<T> component = std::make_unique<T>(std::forward<Args...>(args...));

            //Call-down to internal function
            _ComponentSetup(type, std::move(component));
        }

        void MountComponent(const std::string& factoryID);

        template<typename T>
            requires std::is_base_of_v<Component, T>
        T& GetComponent() const {
            Check<NonexistentValueException>(HasComponent<T>(), "A component of the type specified does not exist on the actor!");
            return dynamic_cast<T&>(*components.at(typeid(T)).component);
        }

        template<typename T>
            requires std::is_base_of_v<Component, T>
        void DeleteComponent() {
            Check<NonexistentValueException>(HasComponent<T>(), "A component of the type specified does not exist on the actor!");
            components.erase(std::type_index(typeid(T)));
        }

        std::unordered_map<std::type_index, Component*> GetAllComponents() const {
            return _ComponentGet([](const std::unique_ptr<Component>&) { return true; });
        }

        template<typename F>
            requires std::is_invocable_r_v<bool, F, const std::unique_ptr<Component>&>
        std::unordered_map<std::type_index, Component*> GetComponentsFiltered(F filter) const {
            return _ComponentGet([&filter](const std::unique_ptr<Component>& c) { return filter(c); });
        }

        std::vector<ActorRef> GetAllChildren() const {
            return children;
        }

      private:
        Actor(const std::string& name, ActorRef parent, xg::Guid);
        friend class World;
        friend class Component;

        struct ComponentHandle {
            std::unique_ptr<Component> component;
            uint64_t generation = 0;

            ComponentHandle() = default;
            ComponentHandle(ComponentHandle&&) = default;
            ComponentHandle& operator=(ComponentHandle&&) = default;
            ComponentHandle(const ComponentHandle& o)
              : component(std::make_unique<Component>(*o.component)), generation(0) {}
            ComponentHandle& operator=(const ComponentHandle& o) {
                if(this != &o) {
                    component = std::make_unique<Component>(*o.component);
                    generation = 0;
                }
                return *this;
            }
        };

        ActorRef parent;
        ActorRef self;
        std::vector<ActorRef> children;
        std::unordered_map<std::type_index, ComponentHandle> components;
        World* world;//NON-OWNING --- DO NOT FREE THIS

        void _ComponentSetup(std::type_index type, std::unique_ptr<Component>&& ptr);
        std::unordered_map<std::type_index, Component*> _ComponentGet(std::function<bool(const std::unique_ptr<Component>&)> filter) const;

        bool active;
    };
}