diff --git a/game_root.cpp b/game_root.cpp index da1952c..805f4eb 100644 --- a/game_root.cpp +++ b/game_root.cpp @@ -1,19 +1,19 @@ #include "game_root.hpp" +#include "game_mode.hpp" +#include "godot_macros.h" +#include "level.hpp" +#include "player.hpp" +#include "player_input.hpp" +#include "spawn_point.hpp" #include #include +#include #include #include +#include +#include #include #include -#include -#include -#include "godot_cpp/templates/pair.hpp" -#include "utils/godot_macros.h" -#include "utils/player_input.hpp" -#include "utils/spawn_point.hpp" -#include "utils/player.hpp" -#include "game_mode.hpp" -#include "level.hpp" namespace godot { void GameRoot::_bind_methods() { @@ -33,8 +33,9 @@ bool GameRoot::has_singleton() { void GameRoot::_enter_tree() { GDGAMEONLY(); // TODO: Replace this with detecting input devices - if(this->players.is_empty()) + if(this->players.is_empty()) { this->player_connected(); + } this->grab_singleton(); } @@ -42,18 +43,6 @@ void GameRoot::_exit_tree() { GDGAMEONLY(); this->release_singleton(); } -void GameRoot::set_game_mode(Ref mode) { - if(mode.is_null() || !mode.is_valid()) { - this->game_mode = Ref(); - return; - } - this->game_mode = mode; -} - -Ref GameRoot::get_game_mode() const { - return this->game_mode; -} - void GameRoot::player_connected() { PlayerInput *input = memnew(PlayerInput); this->add_child(input); @@ -61,26 +50,17 @@ void GameRoot::player_connected() { this->emit_signal(StringName("player_connected"), input); } -bool GameRoot::initialize_player(IPlayer *player) { - KeyValue> *found{nullptr}; - // find an unassigned player input instance - for(KeyValue> &pair : this->players) { - if(pair.value.second == nullptr) { - found = &pair; - break; - } - } - // no player slots available, notify caller - if(!found) - return false; - player->player_id = found->key; - found->value.second = player; - player->setup_player_input(found->value.first); +bool GameRoot::initialize_player(IPlayer *player, uint32_t id) { + Pair &found{this->players.get(id)}; + this->add_child(player->to_node()); + player->player_id = id; + found.second = player; + player->setup_player_input(found.first); return true; } void GameRoot::reset_game_mode() { - this->game_mode.unref(); + this->set_game_mode(Ref()); } void GameRoot::grab_singleton() { @@ -100,6 +80,48 @@ void GameRoot::release_singleton() { } } +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) { + // free all player instances in use + for(KeyValue> &pair : this->players) { + if(pair.value.second == nullptr) continue; + Node *node = dynamic_cast(pair.value.second); + if(node == nullptr) { + UtilityFunctions::push_error("Attempt to cast player '", pair.key, "' to node failed"); + } else { + node->queue_free(); + } + } + if(prototype.is_null() || !prototype.is_valid()) { + this->game_mode.unref(); + return; + } + // create new gamemode instance + this->game_mode = prototype->duplicate(false); + // copy the game state from the prototype + this->game_mode->set_game_state(prototype->get_game_state()->duplicate(false)); + uint32_t new_player_id = 0; + do { + new_player_id = this->find_empty_player_slot(); + IPlayer *player = this->spawn_player(new_player_id); + if(player != nullptr) + this->initialize_player(player, new_player_id); + } while(new_player_id != 0); +} + +IPlayer *GameRoot::spawn_player(uint32_t id) { + UtilityFunctions::push_error("GameRoot::spawn_player not implemented"); + return nullptr; +} + GameRoot *GameRoot::singleton_instance{nullptr}; #undef CLASSNAME @@ -132,7 +154,7 @@ Level3D *GameRoot3D::load_level_at(Ref level, Transform3D at) { this->levels.insert(level->get_path(), instance); // if this is the first level containing a game mode currently active use it's gamemode as a prototype if(this->game_mode.is_null()) { - this->change_game_mode(instance->get_game_mode_prototype()); + this->set_game_mode(instance->get_game_mode_prototype()); instance->connect("tree_exited", Callable(this, "reset_game_mode")); } return instance; @@ -172,6 +194,25 @@ Ref GameRoot3D::get_first_boot_level() const { return this->first_boot_level; } +IPlayer *GameRoot3D::spawn_player(uint32_t id) { + if(id == 0) { + 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) { + UtilityFunctions::push_error("Failed to instantiate player scene '", this->game_mode->get_player_scene()->get_path(), "'"); + return nullptr; + } + IPlayer *player = dynamic_cast(player_node); + if(player == nullptr) { + UtilityFunctions::push_error("Player scene does not implement required IPlayer interface"); + player_node->queue_free(); + return nullptr; + } + return player; +} + bool GameRoot3D::is_valid_level(Ref &level) { if(level.is_null() || !level.is_valid() || !level->can_instantiate()) { UtilityFunctions::push_error("Can't load level from invalid packed scene"); @@ -184,25 +225,4 @@ bool GameRoot3D::is_valid_level(Ref &level) { } return true; } - -void GameRoot3D::change_game_mode(Ref prototype) { - // free all player instances in use - for(KeyValue> &pair : this->players) { - if(pair.value.second == nullptr) continue; - Node *node = dynamic_cast(pair.value.second); - if(node == nullptr) { - UtilityFunctions::push_error("Attempt to cast player '", pair.key, "' to node failed"); - } else { - node->queue_free(); - } - } - // create new gamemode instance - this->game_mode = prototype->duplicate(true); - Node *player_node = this->game_mode->get_player_scene()->instantiate(); - IPlayer *player = dynamic_cast(player_node); - if(player != nullptr) { - this->initialize_player(player); - } else { - } -} } diff --git a/game_root.hpp b/game_root.hpp index 388575d..76012fa 100644 --- a/game_root.hpp +++ b/game_root.hpp @@ -26,21 +26,21 @@ public: virtual void _enter_tree() override; virtual void _exit_tree() override; - void set_game_mode(Ref mode); - Ref get_game_mode() const; - void player_connected(); void player_disconnected(); - bool initialize_player(IPlayer *player); + bool initialize_player(IPlayer *player, uint32_t id); void reset_game_mode(); protected: void grab_singleton(); void release_singleton(); + uint32_t find_empty_player_slot() const; + void set_game_mode(Ref prototype); + virtual IPlayer *spawn_player(uint32_t id); protected: static GameRoot *singleton_instance; - uint32_t next_player_id{0}; + uint32_t next_player_id{1}; // 0 is the "invalid" player id HashMap> players{}; Ref game_mode{}; }; @@ -58,9 +58,10 @@ public: void set_first_boot_level(Ref level); Ref get_first_boot_level() const; +protected: + virtual IPlayer *spawn_player(uint32_t id) override; private: static bool is_valid_level(Ref &level); - void change_game_mode(Ref prototype); private: HashMap levels{}; HashSet spawn_points{};