From 88bd934d289e67753852e9a7849cbb09e7a9320d Mon Sep 17 00:00:00 2001 From: Sara Date: Wed, 29 May 2024 17:36:17 +0200 Subject: [PATCH] feat: game mode no longer owns game state --- game_mode.cpp | 14 ------ game_mode.hpp | 15 ++++--- game_root.cpp | 44 +++++++++++-------- game_root.hpp | 116 +++++++++++++++++++++++++++++++++++--------------- 4 files changed, 116 insertions(+), 73 deletions(-) diff --git a/game_mode.cpp b/game_mode.cpp index bbbb107..b087312 100644 --- a/game_mode.cpp +++ b/game_mode.cpp @@ -3,12 +3,10 @@ #include #include #include "utils/godot_macros.h" -#include "game_state.hpp" namespace utils { void GameMode::_bind_methods() { #define CLASSNAME GameMode - GDPROPERTY_HINTED(game_state, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "GameState"); GDPROPERTY_HINTED(player_scene, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); } @@ -22,16 +20,4 @@ void GameMode::set_player_scene(gd::Ref scene) { gd::Ref GameMode::get_player_scene() const { return this->player_scene; } - -void GameMode::set_game_state(gd::Ref state) { - if(state.is_null() || !state.is_valid()) { - this->game_state.unref(); - return; - } - this->game_state = state; -} - -gd::Ref GameMode::get_game_state() { - return this->game_state; -} } diff --git a/game_mode.hpp b/game_mode.hpp index cf2f7d3..ab6a350 100644 --- a/game_mode.hpp +++ b/game_mode.hpp @@ -3,25 +3,26 @@ #include #include -#include "game_state.hpp" namespace gd = godot; namespace utils { +/*! Stores session-relevant data. + * + * Contains any data that is only needed 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. + */ class GameMode : public gd::Resource { GDCLASS(GameMode, gd::Resource); static void _bind_methods(); public: - virtual void _begin(); - virtual void _end(); + virtual void _begin(); //!< Called when the match begins. + virtual void _end(); //!< Called when the match is ending. void set_player_scene(gd::Ref scene); gd::Ref get_player_scene() const; - void set_game_state(gd::Ref state); - gd::Ref get_game_state(); private: - gd::Ref player_scene{}; - gd::Ref game_state{}; + gd::Ref player_scene{}; //!< The scene to instantiate when spawning a player. }; } diff --git a/game_root.cpp b/game_root.cpp index a997475..51d21f7 100644 --- a/game_root.cpp +++ b/game_root.cpp @@ -6,6 +6,7 @@ #include "player_input.hpp" #include "player.hpp" #include "spawn_point.hpp" +#include "utils/game_state.hpp" #include #include #include @@ -21,6 +22,7 @@ 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")); @@ -182,8 +184,6 @@ void GameRoot3D::set_game_mode(gd::Ref prototype) { } // shallow clone the game mode prototype .. this->game_mode = prototype->duplicate(false); - // .. except for the game state, which should be cloned as well - this->game_mode->set_game_state(prototype->get_game_state()->duplicate(false)); this->game_mode->_begin(); if(this->game_mode->get_player_scene().is_valid()) { uint32_t new_player_id = this->find_empty_player_slot(); @@ -196,12 +196,28 @@ void GameRoot3D::set_game_mode(gd::Ref prototype) { } } -gd::Ref GameRoot3D::get_game_mode() const { - return this->game_mode; +GameMode *GameRoot3D::get_game_mode() const { + return this->game_mode.ptr(); } -gd::Ref GameRoot3D::get_game_state() const { - return this->game_mode->get_game_state(); +GameState *GameRoot3D::get_game_state() const { + return this->game_state.ptr(); +} + +gd::HashMap &GameRoot3D::get_levels() { + return this->levels; +} + +IPlayer *GameRoot3D::get_player(uint32_t id) { + return this->players[id].second; +} + +gd::Vector GameRoot3D::get_players() { + gd::Vector players{}; + for(gd::KeyValue> pair : this->players) { + players.push_back(pair.value.second); + } + return players; } void GameRoot3D::set_first_boot_level(gd::Ref level) { @@ -222,20 +238,12 @@ gd::Ref GameRoot3D::get_first_boot_level() const { return this->first_boot_level; } -gd::HashMap &GameRoot3D::get_levels() { - return this->levels; +void GameRoot3D::set_game_state_prototype(gd::Ref game_state) { + this->game_state_prototype = game_state; } -IPlayer *GameRoot3D::get_player(uint32_t id) { - return this->players[id].second; -} - -gd::Vector GameRoot3D::get_players() { - gd::Vector players{}; - for(gd::KeyValue> pair : this->players) { - players.push_back(pair.value.second); - } - return players; +gd::Ref GameRoot3D::get_game_state_prototype() const { + return this->game_state_prototype; } void GameRoot3D::grab_singleton() { diff --git a/game_root.hpp b/game_root.hpp index cb9f6a3..5161823 100644 --- a/game_root.hpp +++ b/game_root.hpp @@ -2,6 +2,7 @@ #define GAME_ROOT_HPP #include "game_mode.hpp" +#include "game_state.hpp" #include "level.hpp" #include #include @@ -19,82 +20,129 @@ 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 + //! get the current active singleton instance of GameRoot static GameRoot3D *get_singleton(); - // returns true if there is currently a singleton active for GameRoot + //! 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 + /*! 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 + /*! 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 + /*! Un-set game mode. + * Shorthand for set_game_mode(Ref()) + */ void reset_game_mode(); - // shorthand for load_level(level, Transform3D()) + //! 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 + /*! 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 + //! 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 + /*! Override the current gamemode. + * + * Replaces game mode requires destroying and respawning all players + */ void set_game_mode(gd::Ref prototype); - gd::Ref get_game_mode() const; - gd::Ref get_game_state() const; + //! 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; - gd::HashMap &get_levels(); - IPlayer *get_player(uint32_t id); - gd::Vector get_players(); + void set_game_state_prototype(gd::Ref game_state); + gd::Ref get_game_state_prototype() const; protected: - // attempt to make 'this' the current singleton instance + //! 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' + /*! 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); -protected: +private: 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{}; + uint32_t next_player_id{1}; //!< Next available player ID. Default is 1 because 0 is the "invalid" player id. + gd::HashMap> players{}; //!< all players by id by input device. - gd::Ref first_boot_level{}; + gd::RandomNumberGenerator rng{}; //!< Global random number generator. + gd::HashMap levels{}; //!< all currently active levels identified by their resource paths. + gd::Vector spawn_points{}; //!< all currently available spawn points. + gd::Ref game_mode{}; //!< current active gamemode. + /*! Active game state. + * + * Will be assigned loaded save data, or game_state_prototype if no save data is found. + */ + gd::Ref game_state{}; + + gd::Ref first_boot_level{}; //!< The level to boot into on startup. + gd::Ref game_state_prototype{}; //!< The default game state data used for game_state if no save data is available. }; }