feat: stripped away over complicated bits
This commit is contained in:
		
							parent
							
								
									79c37a3ccc
								
							
						
					
					
						commit
						efaeb16fe5
					
				|  | @ -1,20 +0,0 @@ | |||
| #include "game_mode.hpp" | ||||
| #include <godot_cpp/classes/global_constants.hpp> | ||||
| #include <godot_cpp/classes/packed_scene.hpp> | ||||
| #include <godot_cpp/classes/scene_state.hpp> | ||||
| #include "utils/godot_macros.hpp" | ||||
| 
 | ||||
| namespace utils { | ||||
| void GameMode::_bind_methods() { | ||||
| #define CLASSNAME GameMode | ||||
|     GDPROPERTY_HINTED(player_scene, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); | ||||
| } | ||||
| 
 | ||||
| void GameMode::set_player_scene(gd::Ref<gd::PackedScene> scene) { | ||||
|     this->player_scene = scene; | ||||
| } | ||||
| 
 | ||||
| gd::Ref<gd::PackedScene> GameMode::get_player_scene() const { | ||||
|     return this->player_scene; | ||||
| } | ||||
| } | ||||
|  | @ -1,27 +0,0 @@ | |||
| #ifndef UTILS_GAME_MODE_HPP | ||||
| #define UTILS_GAME_MODE_HPP | ||||
| 
 | ||||
| #include <godot_cpp/classes/node.hpp> | ||||
| #include <godot_cpp/classes/packed_scene.hpp> | ||||
| 
 | ||||
| namespace gd = godot; | ||||
| 
 | ||||
| namespace utils { | ||||
| /*! Stores session-relevant data.
 | ||||
|  * | ||||
|  * Inheriting classes are intended to keep only data that is relevant for the duration of the current session/match. Use GameState instead if you want data to be saved between sessions. | ||||
|  * Will be destroyed when a level is loaded that does not match the same game mode class. | ||||
|  * The current active game mode can be gotten from the GameRoot3D singleton instance. | ||||
|  */ | ||||
| class GameMode : public gd::Node { | ||||
|     GDCLASS(GameMode, gd::Node); | ||||
|     static void _bind_methods(); | ||||
| public: | ||||
|     void set_player_scene(gd::Ref<gd::PackedScene> scene); | ||||
|     gd::Ref<gd::PackedScene> get_player_scene() const; | ||||
| private: | ||||
|     gd::Ref<gd::PackedScene> player_scene{}; //!< The scene to instantiate when spawning a player.
 | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| #endif // !UTILS_GAME_MODE_HPP
 | ||||
							
								
								
									
										323
									
								
								game_root.cpp
									
									
									
									
									
								
							
							
						
						
									
										323
									
								
								game_root.cpp
									
									
									
									
									
								
							|  | @ -1,323 +0,0 @@ | |||
| #include "game_root.hpp" | ||||
| #include "game_mode.hpp" | ||||
| #include "godot_macros.hpp" | ||||
| #include "level.hpp" | ||||
| #include "player.hpp" | ||||
| #include "player_input.hpp" | ||||
| #include "player.hpp" | ||||
| #include "spawn_point.hpp" | ||||
| #include "utils/game_state.hpp" | ||||
| #include <cstdint> | ||||
| #include <godot_cpp/classes/global_constants.hpp> | ||||
| #include <godot_cpp/classes/input.hpp> | ||||
| #include <godot_cpp/classes/packed_scene.hpp> | ||||
| #include <godot_cpp/classes/scene_state.hpp> | ||||
| #include <godot_cpp/core/class_db.hpp> | ||||
| #include <godot_cpp/templates/pair.hpp> | ||||
| #include <godot_cpp/variant/string_name.hpp> | ||||
| #include <godot_cpp/variant/utility_functions.hpp> | ||||
| 
 | ||||
| namespace utils { | ||||
| void GameRoot3D::_bind_methods() { | ||||
| #define CLASSNAME GameRoot3D | ||||
|     GDFUNCTION(reset_game_mode); | ||||
|     GDPROPERTY_HINTED(first_boot_level, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); | ||||
|     GDPROPERTY_HINTED(game_state_prototype, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "GameState"); | ||||
|     GDSIGNAL("player_connected", gd::PropertyInfo(gd::Variant::OBJECT, "player_input", gd::PROPERTY_HINT_NODE_TYPE, "PlayerInput")); | ||||
|     GDSIGNAL("player_disconnected", gd::PropertyInfo(gd::Variant::OBJECT, "player_input", gd::PROPERTY_HINT_NODE_TYPE, "PlayerInput")); | ||||
|     GDSIGNAL("player_spawned", gd::PropertyInfo(gd::Variant::OBJECT, "player_info", gd::PROPERTY_HINT_NODE_TYPE, "Node")); | ||||
| } | ||||
| 
 | ||||
| GameRoot3D *GameRoot3D::get_singleton() { | ||||
|     return GameRoot3D::singleton_instance; | ||||
| } | ||||
| 
 | ||||
| bool GameRoot3D::has_singleton() { | ||||
|     return GameRoot3D::singleton_instance != nullptr; | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::_enter_tree() { GDGAMEONLY(); | ||||
|     // TODO: Replace this with detecting input devices
 | ||||
|     this->player_input_connected(); | ||||
|     this->grab_singleton(); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::_ready() { GDGAMEONLY(); | ||||
|     this->load_level(this->first_boot_level); | ||||
|     // TODO: try load save data from file.
 | ||||
|     this->game_state = this->game_state_prototype->duplicate(true); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::_exit_tree() { GDGAMEONLY(); | ||||
|     this->release_singleton(); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::player_input_connected() { | ||||
|     PlayerInput *input = memnew(PlayerInput); | ||||
|     this->add_child(input); | ||||
|     this->players.insert(this->next_player_id++, {input, nullptr}); | ||||
|     this->emit_signal(gd::StringName("player_connected"), input); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::remove_player(uint32_t player_id) { | ||||
|     if(!this->players.has(player_id)) | ||||
|         return; | ||||
|     // convert player object to node
 | ||||
|     gd::Node *node = this->players.get(player_id).second->to_node(); | ||||
|     if(node == nullptr) { | ||||
|         gd::UtilityFunctions::push_error("IPlayer::to_node failed for player with id '", player_id, "'"); | ||||
|         return; | ||||
|     } | ||||
|     node->queue_free(); | ||||
|     this->players.get(player_id).second = nullptr; | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::remove_all_players() { | ||||
|     // free all player instances in use
 | ||||
|     for(gd::KeyValue<uint32_t, gd::Pair<PlayerInput*, IPlayer*>> &pair : this->players) { | ||||
|         // skip unused player slots
 | ||||
|         if(pair.value.second == nullptr) | ||||
|             continue; | ||||
|         else | ||||
|             this->remove_player(pair.key); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool GameRoot3D::initialize_player(IPlayer *player, uint32_t id) { | ||||
|     if(!this->players.has(id)) | ||||
|         return false; | ||||
|     // register the player
 | ||||
|     gd::Pair<PlayerInput*, IPlayer*> &found{this->players.get(id)}; | ||||
|     found.second = player; | ||||
|     // set player id
 | ||||
|     player->player_id = id; | ||||
|     this->emit_signal("player_spawned", player->to_node()); | ||||
|     this->add_child(player->to_node()); | ||||
|     player->setup_player_input(found.first); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::reset_game_mode() { | ||||
|     this->set_game_mode(nullptr); | ||||
| } | ||||
| 
 | ||||
| Level3D *GameRoot3D::load_level(gd::Ref<gd::PackedScene> level) { | ||||
|     return this->load_level_at(level, gd::Transform3D()); | ||||
| } | ||||
| 
 | ||||
| Level3D *GameRoot3D::load_level_at(gd::Ref<gd::PackedScene> level, gd::Transform3D at) { | ||||
|     if(!GameRoot3D::is_valid_level(level)) { | ||||
|         return nullptr; | ||||
|     } | ||||
|     Level3D *instance = Object::cast_to<Level3D>(level->instantiate()); | ||||
|     if(instance == nullptr) { | ||||
|         gd::UtilityFunctions::push_error("Unexpected failure to instantiate level scene '", level->get_path(), "'."); | ||||
|         return nullptr; | ||||
|     } | ||||
|     this->levels.insert(level->get_path(), instance); | ||||
|     instance->connect("tree_exited", callable_mp(this, &GameRoot3D::level_unloaded).bind(level->get_path())); | ||||
|     // store and add to tree at desired transform
 | ||||
|     // if this is the first level containing a game mode currently active use it's gamemode as a prototype
 | ||||
|     gd::Ref<gd::PackedScene> game_mode_prototype{instance->get_game_mode_prototype()}; | ||||
|     bool const switch_game_mode{!this->game_mode || this->game_mode->get_scene_file_path() != game_mode_prototype->get_path()}; | ||||
|     if(switch_game_mode) { | ||||
|         this->set_game_mode(instance->get_game_mode_prototype()); | ||||
|     } | ||||
|     this->add_child(instance); | ||||
|     instance->set_global_transform(at); | ||||
|     // set initial player positions if new player were spawned due to game mode switch
 | ||||
|     if(switch_game_mode && this->game_mode != nullptr) { | ||||
|         for(gd::KeyValue<uint32_t, gd::Pair<PlayerInput *, IPlayer *>> const &kvp : this->players) { | ||||
|             this->place_player_at_spawnpoint(kvp.value.second); | ||||
|         } | ||||
|     } | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::unload_all_levels() { | ||||
|     gd::HashMap<gd::StringName, Level3D*> levels = this->get_levels(); | ||||
|     for(gd::KeyValue<gd::StringName, Level3D*> &kvp : levels) | ||||
|         kvp.value->call_deferred("queue_free"); | ||||
|     this->get_levels().clear(); | ||||
|     this->reset_game_mode(); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::replace_levels(gd::Ref<gd::PackedScene> scene) { | ||||
|     this->unload_all_levels(); | ||||
|     this->load_level(scene); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::register_spawn_point(SpawnPoint3D *spawn_point) { | ||||
|     if(this->spawn_points.has(spawn_point)) { | ||||
|         gd::UtilityFunctions::push_error("Duplicate attempt to register spawnpoint '", spawn_point->get_path(), "'"); | ||||
|         return; | ||||
|     } | ||||
|     if(!this->spawn_points.has(spawn_point)) | ||||
|         this->spawn_points.push_back(spawn_point); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::unregister_spawn_point(SpawnPoint3D *spawn_point) { | ||||
|     if(!this->spawn_points.has(spawn_point)) { | ||||
|         gd::UtilityFunctions::push_error("Attempt to unregister spawnpoint '", spawn_point->get_path(), "', which is not registered."); | ||||
|         return; | ||||
|     } | ||||
|     this->spawn_points.erase(spawn_point); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::place_player_at_spawnpoint(IPlayer *player) { | ||||
|     if(this->spawn_points.is_empty()) return; | ||||
|     SpawnPoint3D *spawn_point = this->spawn_points[rng->randi() % this->spawn_points.size()]; | ||||
|     player->spawn_at_position(spawn_point->get_global_transform()); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::player_despawned(uint32_t id) { | ||||
|     gd::Pair<PlayerInput*, IPlayer*> &pair = this->players.get(id); | ||||
|     pair.second = nullptr; | ||||
|     pair.first->clear_listeners(); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::set_game_mode(gd::Ref<gd::PackedScene> prototype) { | ||||
|     this->remove_all_players(); | ||||
|     if(this->game_mode != nullptr) | ||||
|         this->game_mode->queue_free(); | ||||
|     if(prototype.is_null() || !prototype.is_valid()) | ||||
|         return; // allow "unsetting" the gamemode by passing an invalid gamemode
 | ||||
|     // Detect passing of valid scene that is an invalid game mode
 | ||||
|     if(!gd::ClassDB::is_parent_class(prototype->get_state()->get_node_type(0), "GameMode")) { | ||||
|         gd::UtilityFunctions::push_error("Attempted to load non-gamemode scene as gamemode"); | ||||
|         return; | ||||
|     } | ||||
|     // instantiate the game mode as a child
 | ||||
|     this->game_mode = Object::cast_to<GameMode>(prototype->instantiate()); | ||||
|     this->add_child(game_mode); | ||||
|     // instantiate players
 | ||||
|     if(this->game_mode->get_player_scene().is_valid()) { | ||||
|         uint32_t new_player_id = this->find_empty_player_slot(); | ||||
|         do { | ||||
|             IPlayer *player = this->spawn_player(new_player_id); | ||||
|             if(player != nullptr) | ||||
|                 this->initialize_player(player, new_player_id); | ||||
|             new_player_id = this->find_empty_player_slot(); | ||||
|         } while(new_player_id != 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| GameMode *GameRoot3D::get_game_mode() const { | ||||
|     return this->game_mode; | ||||
| } | ||||
| 
 | ||||
| GameState *GameRoot3D::get_game_state() const { | ||||
|     return this->game_state.ptr(); | ||||
| } | ||||
| 
 | ||||
| gd::HashMap<gd::StringName, Level3D *> &GameRoot3D::get_levels() { | ||||
|     return this->levels; | ||||
| } | ||||
| 
 | ||||
| IPlayer *GameRoot3D::get_player(uint32_t id) { | ||||
|     return this->players[id].second; | ||||
| } | ||||
| 
 | ||||
| gd::Vector<IPlayer*> GameRoot3D::get_players() { | ||||
|     gd::Vector<IPlayer*> players{}; | ||||
|     for(gd::KeyValue<uint32_t, gd::Pair<PlayerInput*, IPlayer*>> pair : this->players) { | ||||
|         players.push_back(pair.value.second); | ||||
|     } | ||||
|     return players; | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::set_first_boot_level(gd::Ref<gd::PackedScene> level) { | ||||
|     if(level.is_null() || !level.is_valid()) { | ||||
|         this->first_boot_level.unref(); | ||||
|         return; | ||||
|     } | ||||
|     gd::StringName const root_type = level->get_state()->get_node_type(0); | ||||
|     if(!gd::ClassDB::is_parent_class(root_type, "Level3D")) { | ||||
|         gd::UtilityFunctions::push_error("First boot level cannot be of type '", root_type, "'. First boot level has to inherit from Level3D"); | ||||
|         this->first_boot_level.unref(); | ||||
|         return; | ||||
|     } | ||||
|     this->first_boot_level = level; | ||||
| } | ||||
| 
 | ||||
| gd::Ref<gd::PackedScene> GameRoot3D::get_first_boot_level() const { | ||||
|     return this->first_boot_level; | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::set_game_state_prototype(gd::Ref<GameState> game_state) { | ||||
|     this->game_state_prototype = game_state; | ||||
| } | ||||
| 
 | ||||
| gd::Ref<GameState> GameRoot3D::get_game_state_prototype() const { | ||||
|     return this->game_state_prototype; | ||||
| } | ||||
| 
 | ||||
| gd::RandomNumberGenerator &GameRoot3D::get_rng() { | ||||
|     return *this->rng.ptr(); | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::grab_singleton() { | ||||
|     if(GameRoot3D::has_singleton()) { | ||||
|         this->set_process_mode(PROCESS_MODE_DISABLED); | ||||
|         gd::UtilityFunctions::push_error("More than one GameRoot instance active"); | ||||
|     } else { | ||||
|         GameRoot3D::singleton_instance = this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::release_singleton() { | ||||
|     if(GameRoot3D::singleton_instance == this) { | ||||
|         GameRoot3D::singleton_instance = nullptr; | ||||
|     } else { | ||||
|         gd::UtilityFunctions::push_error("GameRoot instance attempted to release singleton while it is not the singleton instance"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| uint32_t GameRoot3D::find_empty_player_slot() const { | ||||
|     for(gd::KeyValue<uint32_t, gd::Pair<PlayerInput*, IPlayer*>> const &kvp : this->players) { | ||||
|         if(kvp.value.second == nullptr) { | ||||
|             return kvp.key; | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| IPlayer *GameRoot3D::spawn_player(uint32_t id) { | ||||
|     if(id == 0) { | ||||
|         gd::UtilityFunctions::push_error("Failed to find any valid player slot when spawning player"); | ||||
|         return nullptr; | ||||
|     } | ||||
|     Node *player_node = this->game_mode->get_player_scene()->instantiate(); | ||||
|     if(player_node == nullptr) { | ||||
|         gd::UtilityFunctions::push_error("Failed to instantiate player scene '", this->game_mode->get_player_scene()->get_path(), "'"); | ||||
|         return nullptr; | ||||
|     } | ||||
|     IPlayer *player = dynamic_cast<IPlayer*>(player_node); | ||||
|     if(player == nullptr) { | ||||
|         gd::UtilityFunctions::push_error("Player scene does not implement required IPlayer interface"); | ||||
|         player_node->queue_free(); | ||||
|         return nullptr; | ||||
|     } | ||||
|     player_node->connect("tree_exited", callable_mp(this, &GameRoot3D::player_despawned).bind(id)); | ||||
|     return player; | ||||
| } | ||||
| 
 | ||||
| void GameRoot3D::level_unloaded(gd::StringName scene_path) { | ||||
|     this->levels.erase(scene_path); | ||||
| } | ||||
| 
 | ||||
| bool GameRoot3D::is_valid_level(gd::Ref<gd::PackedScene> &level) { | ||||
|     if(level.is_null() || !level.is_valid() || !level->can_instantiate()) { | ||||
|         gd::UtilityFunctions::push_error("Can't load level from invalid packed scene"); | ||||
|         return false; | ||||
|     } | ||||
|     gd::StringName const root_type = level->get_state()->get_node_type(0); | ||||
|     if(!gd::ClassDB::is_parent_class(root_type, "Level3D")) { | ||||
|         gd::UtilityFunctions::push_error("Can't load level with root type '", root_type, "'. Root node has to be of type Level3D"); | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| GameRoot3D *GameRoot3D::singleton_instance{nullptr}; | ||||
| } | ||||
							
								
								
									
										185
									
								
								game_root.hpp
									
									
									
									
									
								
							
							
						
						
									
										185
									
								
								game_root.hpp
									
									
									
									
									
								
							|  | @ -1,185 +0,0 @@ | |||
| #ifndef UTILS_GAME_ROOT_HPP | ||||
| #define UTILS_GAME_ROOT_HPP | ||||
| 
 | ||||
| #include "game_mode.hpp" | ||||
| #include "game_state.hpp" | ||||
| #include "level.hpp" | ||||
| #include <godot_cpp/classes/node.hpp> | ||||
| #include <godot_cpp/classes/packed_scene.hpp> | ||||
| #include <godot_cpp/classes/random_number_generator.hpp> | ||||
| #include <godot_cpp/templates/hash_map.hpp> | ||||
| #include <godot_cpp/templates/hash_set.hpp> | ||||
| #include <godot_cpp/templates/pair.hpp> | ||||
| #include <godot_cpp/templates/pair.hpp> | ||||
| #include <godot_cpp/templates/vector.hpp> | ||||
| 
 | ||||
| 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<PackedScene>())` | ||||
|      */ | ||||
|     void reset_game_mode(); | ||||
| 
 | ||||
|     //! shorthand for load_level(level, Transform3D())
 | ||||
|     Level3D *load_level(gd::Ref<gd::PackedScene> 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<gd::PackedScene> 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<gd::PackedScene> 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<gd::PackedScene> 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<gd::StringName, Level3D *> &get_levels(); | ||||
|     //! Get the player instance associated with id.
 | ||||
|     IPlayer *get_player(uint32_t id); | ||||
|     //! Get all players in a list.
 | ||||
|     gd::Vector<IPlayer*> get_players(); | ||||
|     void set_first_boot_level(gd::Ref<gd::PackedScene> level); | ||||
|     gd::Ref<gd::PackedScene> get_first_boot_level() const; | ||||
|     void set_game_state_prototype(gd::Ref<GameState> game_state); | ||||
|     gd::Ref<GameState> 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<gd::PackedScene> &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<uint32_t, gd::Pair<PlayerInput*, IPlayer*>> players{}; | ||||
|     /*! Global random number generator.
 | ||||
|      * | ||||
|      * `&get_rng()` | ||||
|      */ | ||||
|     gd::Ref<gd::RandomNumberGenerator> rng{}; | ||||
|     /*! All currently active levels.
 | ||||
|      * | ||||
|      * Each identified by their resource paths. | ||||
|      * | ||||
|      * `&get_levels()` | ||||
|      */ | ||||
|     gd::HashMap<gd::StringName, Level3D*> levels{}; | ||||
|     /*! All currently available spawn points.
 | ||||
|      */ | ||||
|     gd::Vector<SpawnPoint3D*> 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<GameState> game_state{}; | ||||
|     /*! The level to boot into on startup.
 | ||||
|      * | ||||
|      * `get_first_boot_level()` `set_first_boot_level(value)` | ||||
|      */ | ||||
|     gd::Ref<gd::PackedScene> 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<GameState> game_state_prototype{};  | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| #endif // !UTILS_GAME_ROOT_HPP
 | ||||
|  | @ -1,9 +0,0 @@ | |||
| #include "game_state.hpp" | ||||
| 
 | ||||
| using namespace godot; | ||||
| 
 | ||||
| namespace utils { | ||||
| void GameState::_bind_methods() { | ||||
| #define CLASSNAME GameState | ||||
| } | ||||
| } | ||||
|  | @ -1,18 +0,0 @@ | |||
| #ifndef UTILS_GAME_STATE_HPP | ||||
| #define UTILS_GAME_STATE_HPP | ||||
| 
 | ||||
| #include <godot_cpp/classes/resource.hpp> | ||||
| 
 | ||||
| namespace utils { | ||||
| /*! Parent class for saved game state.
 | ||||
|  * | ||||
|  * Inherit and add godot properties to save persistently. | ||||
|  */ | ||||
| class GameState : public godot::Resource { | ||||
|     GDCLASS(GameState, godot::Resource); | ||||
|     static void _bind_methods(); | ||||
| public: | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| #endif // !UTILS_GAME_STATE_HPP
 | ||||
							
								
								
									
										24
									
								
								level.cpp
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								level.cpp
									
									
									
									
									
								
							|  | @ -1,24 +0,0 @@ | |||
| #include "level.hpp" | ||||
| #include <godot_cpp/core/class_db.hpp> | ||||
| #include "utils/godot_macros.hpp" | ||||
| #include <godot_cpp/classes/scene_state.hpp> | ||||
| 
 | ||||
| namespace utils { | ||||
| void Level3D::_bind_methods() { | ||||
| #define CLASSNAME Level3D | ||||
|     GDPROPERTY_HINTED(game_mode_prototype, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); | ||||
| } | ||||
| 
 | ||||
| void Level3D::set_game_mode_prototype(gd::Ref<gd::PackedScene> prototype) { | ||||
|     if(prototype.is_null() || !prototype.is_valid()) | ||||
|         this->game_mode_prototype = gd::Ref<gd::PackedScene>(nullptr); | ||||
|     else if(!gd::ClassDB::is_parent_class(prototype->get_state()->get_node_type(0), "GameMode")) | ||||
|         return; | ||||
|     else | ||||
|         this->game_mode_prototype = prototype; | ||||
| } | ||||
| 
 | ||||
| gd::Ref<gd::PackedScene> Level3D::get_game_mode_prototype() const { | ||||
|     return this->game_mode_prototype; | ||||
| } | ||||
| } | ||||
							
								
								
									
										25
									
								
								level.hpp
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								level.hpp
									
									
									
									
									
								
							|  | @ -1,25 +0,0 @@ | |||
| #ifndef UTILS_LEVEL_HPP | ||||
| #define UTILS_LEVEL_HPP | ||||
| 
 | ||||
| #include "game_mode.hpp" | ||||
| #include <godot_cpp/classes/node3d.hpp> | ||||
| 
 | ||||
| namespace gd = godot; | ||||
| 
 | ||||
| namespace utils { | ||||
| /*! 3D level root to be used with GameRoot3D.
 | ||||
|  * | ||||
|  * The configured game mode will become the active GameMode in GameRoot3D if one does not exist yet. | ||||
|  */ | ||||
| class Level3D : public gd::Node3D { | ||||
|     GDCLASS(Level3D, gd::Node3D); | ||||
|     static void _bind_methods(); | ||||
| public: | ||||
|     void set_game_mode_prototype(gd::Ref<gd::PackedScene> prototype); | ||||
|     gd::Ref<gd::PackedScene> get_game_mode_prototype() const; | ||||
| private: | ||||
|     gd::Ref<gd::PackedScene> game_mode_prototype{}; //!< The starting state of the game mode to instantiate if this is the "leading" level.
 | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| #endif // !UTILS_LEVEL_HPP
 | ||||
|  | @ -1,8 +0,0 @@ | |||
| #include "player.hpp" | ||||
| 
 | ||||
| namespace utils { | ||||
| uint32_t IPlayer::get_player_id() { | ||||
|     return this->player_id.value_or(0); | ||||
| } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										39
									
								
								player.hpp
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								player.hpp
									
									
									
									
									
								
							|  | @ -1,39 +0,0 @@ | |||
| #ifndef UTILS_PLAYER_HPP | ||||
| #define UTILS_PLAYER_HPP | ||||
| 
 | ||||
| #include <cstdint> | ||||
| #include <optional> | ||||
| #include <godot_cpp/variant/transform3d.hpp> | ||||
| 
 | ||||
| namespace gd = godot; | ||||
| 
 | ||||
| namespace godot { class Node; } | ||||
| 
 | ||||
| namespace utils { | ||||
| class PlayerInput; | ||||
| 
 | ||||
| /*! Interface required for player nodes.
 | ||||
|  * | ||||
|  * Use multiple inheritance and implement IPlayer to make a regular node usable as a player with GameRoot3D. | ||||
|  */ | ||||
| class IPlayer { | ||||
| friend class GameRoot3D; | ||||
| public: | ||||
|     /*! Called by GameRoot3D when this player is instantiated or assigned a new PlayerInput.
 | ||||
|      * | ||||
|      * Use PlayerInput::listen_to to register input callbacks. There's no need to keep the input pointer around. As the instance is managed by the GameRoot3D. | ||||
|      */ | ||||
|     virtual void setup_player_input(PlayerInput *input) = 0; | ||||
|     //! Convert IPlayer instance to node.
 | ||||
|     virtual gd::Node *to_node() = 0; | ||||
|     //! Spawn the player at a given transform, usually the global transform of a SpawnPoint3D.
 | ||||
|     virtual void spawn_at_position(gd::Transform3D const &at) = 0; | ||||
| 
 | ||||
|     uint32_t get_player_id(); //!< Returns the player id assigned to this instance.
 | ||||
| 
 | ||||
| private: | ||||
|     std::optional<uint32_t> player_id{std::nullopt}; | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| #endif // !UTILS_PLAYER_HPP
 | ||||
|  | @ -1,19 +1,9 @@ | |||
| #include "register_types.hpp" | ||||
| #include "game_mode.hpp" | ||||
| #include "game_root.hpp" | ||||
| #include "game_state.hpp" | ||||
| #include "level.hpp" | ||||
| #include "player_input.hpp" | ||||
| #include "spawn_point.hpp" | ||||
| #include <godot_cpp/core/class_db.hpp> | ||||
| 
 | ||||
| namespace utils { | ||||
| void godot_cpp_utils_register_types() { | ||||
|     GDREGISTER_CLASS(utils::GameMode); | ||||
|     GDREGISTER_CLASS(utils::GameRoot3D); | ||||
|     GDREGISTER_CLASS(utils::GameState); | ||||
|     GDREGISTER_CLASS(utils::Level3D); | ||||
|     GDREGISTER_CLASS(utils::PlayerInput); | ||||
|     GDREGISTER_CLASS(utils::SpawnPoint3D); | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -1,23 +0,0 @@ | |||
| #include "spawn_point.hpp" | ||||
| #include "utils/game_root.hpp" | ||||
| 
 | ||||
| namespace utils { | ||||
| void SpawnPoint3D::_bind_methods() { | ||||
| } | ||||
| 
 | ||||
| void SpawnPoint3D::_enter_tree() { | ||||
|     GameRoot3D *root = gd::Object::cast_to<GameRoot3D>(GameRoot3D::get_singleton()); | ||||
|     if(root == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     root->register_spawn_point(this); | ||||
| } | ||||
| 
 | ||||
| void SpawnPoint3D::_exit_tree() { | ||||
|     GameRoot3D *root = Object::cast_to<GameRoot3D>(GameRoot3D::get_singleton()); | ||||
|     if(root == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     root->unregister_spawn_point(this); | ||||
| } | ||||
| } | ||||
|  | @ -1,22 +0,0 @@ | |||
| #ifndef UTILS_SPAWN_POINT_HPP | ||||
| #define UTILS_SPAWN_POINT_HPP | ||||
| 
 | ||||
| #include <godot_cpp/classes/node3d.hpp> | ||||
| 
 | ||||
| namespace gd = godot; | ||||
| 
 | ||||
| namespace utils { | ||||
| /*! A location in the game world that the player can spawn at.
 | ||||
|  * | ||||
|  * Registers and de-registers itself with the GameRoot3D to enable/disable this spawnpoint. | ||||
|  */ | ||||
| class SpawnPoint3D : public gd::Node3D { | ||||
|     GDCLASS(SpawnPoint3D, gd::Node3D); | ||||
|     static void _bind_methods(); | ||||
| public: | ||||
|     virtual void _enter_tree() override; | ||||
|     virtual void _exit_tree() override; | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| #endif // !UTILS_SPAWN_POINT_HPP
 | ||||
		Loading…
	
		Reference in a new issue
	
	 Sara
						Sara