From 2344f3f2b653dbf6a6c57c2e18c43a1f2c813024 Mon Sep 17 00:00:00 2001 From: Sara Date: Mon, 6 May 2024 11:03:00 +0200 Subject: [PATCH] feat: merged GameRoot and GameRoot3D into GameRoot3D --- game_root.cpp | 167 +++++++++++++++++++++++--------------------------- game_root.hpp | 51 +++++++-------- player.hpp | 2 +- 3 files changed, 101 insertions(+), 119 deletions(-) diff --git a/game_root.cpp b/game_root.cpp index e4522d5..bc86795 100644 --- a/game_root.cpp +++ b/game_root.cpp @@ -16,23 +16,24 @@ #include namespace godot { -void GameRoot::_bind_methods() { -#define CLASSNAME GameRoot +void GameRoot3D::_bind_methods() { +#define CLASSNAME GameRoot3D GDFUNCTION(reset_game_mode); + GDPROPERTY_HINTED(first_boot_level, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); GDSIGNAL("player_connected", PropertyInfo(Variant::OBJECT, "player_input", PROPERTY_HINT_NODE_TYPE, "PlayerInput")); GDSIGNAL("player_disconnected", PropertyInfo(Variant::OBJECT, "player_input", PROPERTY_HINT_NODE_TYPE, "PlayerInput")); GDSIGNAL("player_spawned", PropertyInfo(Variant::OBJECT, "player_info", PROPERTY_HINT_NODE_TYPE, "Node")); } -GameRoot *GameRoot::get_singleton() { - return GameRoot::singleton_instance; +GameRoot3D *GameRoot3D::get_singleton() { + return GameRoot3D::singleton_instance; } -bool GameRoot::has_singleton() { - return GameRoot::singleton_instance != nullptr; +bool GameRoot3D::has_singleton() { + return GameRoot3D::singleton_instance != nullptr; } -void GameRoot::_enter_tree() { GDGAMEONLY(); +void GameRoot3D::_enter_tree() { GDGAMEONLY(); // TODO: Replace this with detecting input devices if(this->players.is_empty()) { this->player_input_connected(); @@ -40,18 +41,22 @@ void GameRoot::_enter_tree() { GDGAMEONLY(); this->grab_singleton(); } -void GameRoot::_exit_tree() { GDGAMEONLY(); +void GameRoot3D::_ready() { GDGAMEONLY(); + this->load_level(this->first_boot_level); +} + +void GameRoot3D::_exit_tree() { GDGAMEONLY(); this->release_singleton(); } -void GameRoot::player_input_connected() { +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(StringName("player_connected"), input); } -void GameRoot::remove_player(uint32_t player_id) { +void GameRoot3D::remove_player(uint32_t player_id) { if(!this->players.has(player_id)) return; // convert player object to node @@ -64,7 +69,7 @@ void GameRoot::remove_player(uint32_t player_id) { this->players.get(player_id).second = nullptr; } -void GameRoot::remove_all_players() { +void GameRoot3D::remove_all_players() { // free all player instances in use for(KeyValue> &pair : this->players) { // skip unused player slots @@ -75,7 +80,7 @@ void GameRoot::remove_all_players() { } } -bool GameRoot::initialize_player(IPlayer *player, uint32_t id) { +bool GameRoot3D::initialize_player(IPlayer *player, uint32_t id) { if(!this->players.has(id)) return false; // register the player @@ -89,86 +94,10 @@ bool GameRoot::initialize_player(IPlayer *player, uint32_t id) { return true; } -void GameRoot::reset_game_mode() { +void GameRoot3D::reset_game_mode() { this->set_game_mode(Ref()); } -void GameRoot::grab_singleton() { - if(GameRoot::has_singleton()) { - this->set_process_mode(PROCESS_MODE_DISABLED); - UtilityFunctions::push_error("More than one GameRoot instance active"); - } else { - GameRoot::singleton_instance = this; - } -} - -void GameRoot::release_singleton() { - if(GameRoot::singleton_instance == this) { - GameRoot::singleton_instance = nullptr; - } else { - UtilityFunctions::push_error("GameRoot instance attempted to release singleton while it is not the singleton instance"); - } -} - -uint32_t GameRoot::find_empty_player_slot() const { - for(KeyValue> const &kvp : this->players) { - if(kvp.value.second == nullptr) { - return kvp.key; - } - } - return 0; -} - -void GameRoot::set_game_mode(Ref prototype) { - this->remove_all_players(); - // allow "unsetting" the gamemode by passing an invalid gamemode - // shorthand for this behaviour is reset_game_mode - if(prototype.is_null() || !prototype.is_valid()) { - if(!this->game_mode.is_null() && this->game_mode.is_valid()) - this->game_mode->_end(); - this->game_mode.unref(); - return; - } - // 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(); - 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); -} - -Ref GameRoot::get_game_mode() const { - return this->game_mode; -} - -Ref GameRoot::get_game_state() const { - return this->game_mode->get_game_state(); -} - -IPlayer *GameRoot::spawn_player(uint32_t id) { - UtilityFunctions::push_error("GameRoot::spawn_player not implemented"); - return nullptr; -} - -GameRoot *GameRoot::singleton_instance{nullptr}; - -#undef CLASSNAME - -void GameRoot3D::_bind_methods() { -#define CLASSNAME GameRoot3D - GDPROPERTY_HINTED(first_boot_level, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); -} - -void GameRoot3D::_ready() { GDGAMEONLY(); - this->load_level(this->first_boot_level); -} - Level3D *GameRoot3D::load_level(Ref level) { return this->load_level_at(level, Transform3D()); } @@ -221,6 +150,38 @@ void GameRoot3D::place_player_at_spawnpoint(IPlayer *player) { player->spawn_at_position(spawn_point->get_global_transform()); } +void GameRoot3D::set_game_mode(Ref prototype) { + this->remove_all_players(); + // allow "unsetting" the gamemode by passing an invalid gamemode + // shorthand for this behaviour is reset_game_mode + if(prototype.is_null() || !prototype.is_valid()) { + if(!this->game_mode.is_null() && this->game_mode.is_valid()) + this->game_mode->_end(); + this->game_mode.unref(); + return; + } + // 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(); + 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); +} + +Ref GameRoot3D::get_game_mode() const { + return this->game_mode; +} + +Ref GameRoot3D::get_game_state() const { + return this->game_mode->get_game_state(); +} + void GameRoot3D::set_first_boot_level(Ref level) { if(level.is_null() || !level.is_valid()) { this->first_boot_level.unref(); @@ -239,6 +200,32 @@ Ref GameRoot3D::get_first_boot_level() const { return this->first_boot_level; } +void GameRoot3D::grab_singleton() { + if(GameRoot3D::has_singleton()) { + this->set_process_mode(PROCESS_MODE_DISABLED); + 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 { + 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(KeyValue> const &kvp : this->players) { + if(kvp.value.second == nullptr) { + return kvp.key; + } + } + return 0; +} + IPlayer *GameRoot3D::spawn_player(uint32_t id) { if(id == 0) { UtilityFunctions::push_error("Failed to find any valid player slot when spawning player"); @@ -270,4 +257,6 @@ bool GameRoot3D::is_valid_level(Ref &level) { } return true; } + +GameRoot3D *GameRoot3D::singleton_instance{nullptr}; } diff --git a/game_root.hpp b/game_root.hpp index 1ae142c..06f644f 100644 --- a/game_root.hpp +++ b/game_root.hpp @@ -16,16 +16,17 @@ class PlayerInput; class IPlayer; class SpawnPoint3D; -class GameRoot : public Node { - GDCLASS(GameRoot, Node); +class GameRoot3D : public Node { + GDCLASS(GameRoot3D, Node); static void _bind_methods(); public: // get the current active singleton instance of GameRoot - static GameRoot *get_singleton(); + 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(); @@ -42,32 +43,7 @@ public: // shorthand for set_game_mode(Ref()) // unsets the gamemode void reset_game_mode(); - // override the current gamemode - // force-respawns all players - void set_game_mode(Ref prototype); - Ref get_game_mode() const; - Ref get_game_state() const; -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; - virtual IPlayer *spawn_player(uint32_t id); -protected: - static GameRoot *singleton_instance; - uint32_t next_player_id{1}; // 0 is the "invalid" player id - HashMap> players{}; - Ref game_mode{}; -}; - -class GameRoot3D : public GameRoot { - GDCLASS(GameRoot3D, GameRoot); - static void _bind_methods(); -public: - virtual void _ready() override; // 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' @@ -81,11 +57,28 @@ public: void place_player_at_spawnpoint(IPlayer *player); // ----- 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; protected: - virtual IPlayer *spawn_player(uint32_t id) override; + // 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); 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: HashMap levels{}; HashSet spawn_points{}; diff --git a/player.hpp b/player.hpp index 5a22ea2..3a3fe7f 100644 --- a/player.hpp +++ b/player.hpp @@ -10,7 +10,7 @@ class PlayerInput; class Node; class IPlayer { -friend class GameRoot; +friend class GameRoot3D; public: virtual void setup_player_input(PlayerInput *input) = 0; virtual Node *to_node() = 0;