#ifndef GAME_ROOT_HPP #define GAME_ROOT_HPP #include "game_mode.hpp" #include "level.hpp" #include #include #include #include #include #include #include #include namespace gd = godot; namespace utils { class PlayerInput; class IPlayer; class SpawnPoint3D; 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; 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); // shorthand for set_game_mode(Ref()) // unsets the gamemode 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' // sets the level's root node's global transform Level3D *load_level_at(gd::Ref level, gd::Transform3D at); void unload_all_levels(); 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); // ----- getter / setters ----- // override the current gamemode // force-respawns all players void set_game_mode(gd::Ref prototype); gd::Ref get_game_mode() const; gd::Ref get_game_state() const; void set_first_boot_level(gd::Ref level); gd::Ref get_first_boot_level() const; gd::HashMap &get_levels(); IPlayer *get_player(uint32_t id); gd::Vector get_players(); 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(); uint32_t find_empty_player_slot() const; IPlayer *spawn_player(uint32_t id); void level_unloaded(gd::StringName scene_path); static bool is_valid_level(gd::Ref &level); protected: static GameRoot3D *singleton_instance; uint32_t next_player_id{1}; // 0 is the "invalid" player id gd::HashMap> players{}; gd::Ref game_mode{}; private: gd::RandomNumberGenerator rng{}; gd::HashMap levels{}; gd::Vector spawn_points{}; gd::Ref first_boot_level{}; }; } #endif // !GAME_ROOT_HPP