#ifndef UTILS_GAME_ROOT_HPP #define UTILS_GAME_ROOT_HPP #include "game_mode.hpp" #include "game_state.hpp" #include "level.hpp" #include #include #include #include #include #include #include #include namespace gd = godot; namespace utils { class PlayerInput; class IPlayer; class SpawnPoint3D; /*! The root of a game. * * A game root node that manages levels and input devices. * Can be loaded at any point in a game's life, but suggested is setting this as the root of the boot scene. */ class GameRoot3D : public gd::Node { GDCLASS(GameRoot3D, gd::Node); static void _bind_methods(); public: //! get the current active singleton instance of GameRoot static GameRoot3D *get_singleton(); //! returns true if there is currently a singleton active for GameRoot static bool has_singleton(); virtual void _enter_tree() override; virtual void _ready() override; virtual void _exit_tree() override; /*! Instantiate a new PlayerInput. * * Does not automatically spawn a new player, but does notify game mode. */ void player_input_connected(); /*! Force-disconnect a player * * Calls queue_free on the IPlayer instance */ void remove_player(uint32_t player_id); // calls remove_player for every used player input slot void remove_all_players(); /*! Initialize and register a player instance. * * The player will be added to the tree and AFTER setup_player_input will be called. * This way the player can initialize before setting up input */ bool initialize_player(IPlayer *player, uint32_t id); /*! Un-set game mode. * Shorthand for `set_game_mode(Ref())` */ void reset_game_mode(); //! shorthand for load_level(level, Transform3D()) Level3D *load_level(gd::Ref level); /*! Load a level, only works if 'level' is a valid scene where the root Node can cast to 'Level3D'. * * \param at Sets the root node's global transform. */ Level3D *load_level_at(gd::Ref level, gd::Transform3D at); //! Unload all currently loaded levels. void unload_all_levels(); /*! Replace all currently loaded levels with a new level. * * Shorthand for * ``` * unload_all_levels(); * load_level(level); * ``` */ void replace_levels(gd::Ref level); //! Register a spawnpoint for use when spawning players void register_spawn_point(SpawnPoint3D *spawn_point); // remove a spawnpoint so it can't be used to spawn players void unregister_spawn_point(SpawnPoint3D *spawn_point); void place_player_at_spawnpoint(IPlayer *player); void player_despawned(uint32_t id); /*! Override the current gamemode. * * Replaces game mode requires destroying and respawning all players */ void set_game_mode(gd::Ref prototype); //! get the current active game mode. GameMode *get_game_mode() const; //! Get the current active game state. GameState *get_game_state() const; /*! Returns all currently active levels. * * Levels are identified by their packed scene path. */ gd::HashMap &get_levels(); //! Get the player instance associated with id. IPlayer *get_player(uint32_t id); //! Get all players in a list. gd::Vector get_players(); void set_first_boot_level(gd::Ref level); gd::Ref get_first_boot_level() const; void set_game_state_prototype(gd::Ref game_state); gd::Ref get_game_state_prototype() const; gd::RandomNumberGenerator &get_rng(); protected: //! Attempt to make 'this' the current singleton instance. void grab_singleton(); /*! Attempt to stop being the active singleton instance. * * Only works if the current singleton is 'this'. */ void release_singleton(); //! Find a Player Input device not yet associated with a player. uint32_t find_empty_player_slot() const; //! Spawn a player to be associated with id. IPlayer *spawn_player(uint32_t id); //! Callback for a level exiting the tree. void level_unloaded(gd::StringName scene_path); //! Check if a scene is a valid level. static bool is_valid_level(gd::Ref &level); private: static GameRoot3D *singleton_instance; /*! Next available player ID. * * Default is 1 because 0 is the "invalid" player id. */ uint32_t next_player_id{1}; /*! All players by id by input device. * * `get_players()` */ gd::HashMap> players{}; /*! Global random number generator. * * `&get_rng()` */ gd::RandomNumberGenerator rng{}; /*! All currently active levels. * * Each identified by their resource paths. * * `&get_levels()` */ gd::HashMap levels{}; /*! All currently available spawn points. */ gd::Vector spawn_points{}; /*! Current active gamemode. * * Replaced when a level is loaded that references a different game mode. * * `*get_game_mode()` */ GameMode *game_mode{}; /*! Active game state. * * Will be assigned loaded save data, or game_state_prototype if no save data is found. * * `*get_game_mode()` */ gd::Ref game_state{}; /*! The level to boot into on startup. * * `get_first_boot_level()` `set_first_boot_level(value)` */ gd::Ref first_boot_level{}; /*! The default game state data. * * Duplicated and assigned to game_state if no save data is available. * * `get_game_state_prototype()` `set_game_state_prototype(value)` */ gd::Ref game_state_prototype{}; }; } #endif // !UTILS_GAME_ROOT_HPP