#ifndef GAME_ROOT_HPP #define GAME_ROOT_HPP #include "game_mode.hpp" #include "godot_cpp/classes/random_number_generator.hpp" #include "level.hpp" #include #include #include #include #include #include #include namespace godot { class PlayerInput; class IPlayer; class SpawnPoint3D; class GameRoot3D : public Node { GDCLASS(GameRoot3D, 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(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(Ref level, Transform3D at); void unload_all_levels(); void replace_levels(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(Ref prototype); Ref get_game_mode() const; Ref get_game_state() const; void set_first_boot_level(Ref level); Ref get_first_boot_level() const; HashMap &get_levels(); IPlayer *get_player(uint32_t id); 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(StringName scene_path); static bool is_valid_level(Ref &level); protected: static GameRoot3D *singleton_instance; uint32_t next_player_id{1}; // 0 is the "invalid" player id HashMap> players{}; Ref game_mode{}; private: RandomNumberGenerator rng{}; HashMap levels{}; Vector spawn_points{}; Ref first_boot_level{}; }; } #endif // !GAME_ROOT_HPP