feat: merged GameRoot and GameRoot3D into GameRoot3D

stripped
Sara 2024-05-06 11:03:00 +02:00
parent d81ad91a88
commit 2344f3f2b6
3 changed files with 101 additions and 119 deletions

View File

@ -16,23 +16,24 @@
#include <godot_cpp/variant/utility_functions.hpp> #include <godot_cpp/variant/utility_functions.hpp>
namespace godot { namespace godot {
void GameRoot::_bind_methods() { void GameRoot3D::_bind_methods() {
#define CLASSNAME GameRoot #define CLASSNAME GameRoot3D
GDFUNCTION(reset_game_mode); 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_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_disconnected", PropertyInfo(Variant::OBJECT, "player_input", PROPERTY_HINT_NODE_TYPE, "PlayerInput"));
GDSIGNAL("player_spawned", PropertyInfo(Variant::OBJECT, "player_info", PROPERTY_HINT_NODE_TYPE, "Node")); GDSIGNAL("player_spawned", PropertyInfo(Variant::OBJECT, "player_info", PROPERTY_HINT_NODE_TYPE, "Node"));
} }
GameRoot *GameRoot::get_singleton() { GameRoot3D *GameRoot3D::get_singleton() {
return GameRoot::singleton_instance; return GameRoot3D::singleton_instance;
} }
bool GameRoot::has_singleton() { bool GameRoot3D::has_singleton() {
return GameRoot::singleton_instance != nullptr; return GameRoot3D::singleton_instance != nullptr;
} }
void GameRoot::_enter_tree() { GDGAMEONLY(); void GameRoot3D::_enter_tree() { GDGAMEONLY();
// TODO: Replace this with detecting input devices // TODO: Replace this with detecting input devices
if(this->players.is_empty()) { if(this->players.is_empty()) {
this->player_input_connected(); this->player_input_connected();
@ -40,18 +41,22 @@ void GameRoot::_enter_tree() { GDGAMEONLY();
this->grab_singleton(); 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(); this->release_singleton();
} }
void GameRoot::player_input_connected() { void GameRoot3D::player_input_connected() {
PlayerInput *input = memnew(PlayerInput); PlayerInput *input = memnew(PlayerInput);
this->add_child(input); this->add_child(input);
this->players.insert(this->next_player_id++, {input, nullptr}); this->players.insert(this->next_player_id++, {input, nullptr});
this->emit_signal(StringName("player_connected"), input); 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)) if(!this->players.has(player_id))
return; return;
// convert player object to node // convert player object to node
@ -64,7 +69,7 @@ void GameRoot::remove_player(uint32_t player_id) {
this->players.get(player_id).second = nullptr; this->players.get(player_id).second = nullptr;
} }
void GameRoot::remove_all_players() { void GameRoot3D::remove_all_players() {
// free all player instances in use // free all player instances in use
for(KeyValue<uint32_t, Pair<PlayerInput*, IPlayer*>> &pair : this->players) { for(KeyValue<uint32_t, Pair<PlayerInput*, IPlayer*>> &pair : this->players) {
// skip unused player slots // 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)) if(!this->players.has(id))
return false; return false;
// register the player // register the player
@ -89,86 +94,10 @@ bool GameRoot::initialize_player(IPlayer *player, uint32_t id) {
return true; return true;
} }
void GameRoot::reset_game_mode() { void GameRoot3D::reset_game_mode() {
this->set_game_mode(Ref<GameMode>()); this->set_game_mode(Ref<GameMode>());
} }
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<uint32_t, Pair<PlayerInput*, IPlayer*>> const &kvp : this->players) {
if(kvp.value.second == nullptr) {
return kvp.key;
}
}
return 0;
}
void GameRoot::set_game_mode(Ref<GameMode> 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<GameMode> GameRoot::get_game_mode() const {
return this->game_mode;
}
Ref<GameState> 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<PackedScene> level) { Level3D *GameRoot3D::load_level(Ref<PackedScene> level) {
return this->load_level_at(level, Transform3D()); 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()); player->spawn_at_position(spawn_point->get_global_transform());
} }
void GameRoot3D::set_game_mode(Ref<GameMode> 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<GameMode> GameRoot3D::get_game_mode() const {
return this->game_mode;
}
Ref<GameState> GameRoot3D::get_game_state() const {
return this->game_mode->get_game_state();
}
void GameRoot3D::set_first_boot_level(Ref<PackedScene> level) { void GameRoot3D::set_first_boot_level(Ref<PackedScene> level) {
if(level.is_null() || !level.is_valid()) { if(level.is_null() || !level.is_valid()) {
this->first_boot_level.unref(); this->first_boot_level.unref();
@ -239,6 +200,32 @@ Ref<PackedScene> GameRoot3D::get_first_boot_level() const {
return this->first_boot_level; 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<uint32_t, Pair<PlayerInput*, IPlayer*>> const &kvp : this->players) {
if(kvp.value.second == nullptr) {
return kvp.key;
}
}
return 0;
}
IPlayer *GameRoot3D::spawn_player(uint32_t id) { IPlayer *GameRoot3D::spawn_player(uint32_t id) {
if(id == 0) { if(id == 0) {
UtilityFunctions::push_error("Failed to find any valid player slot when spawning player"); UtilityFunctions::push_error("Failed to find any valid player slot when spawning player");
@ -270,4 +257,6 @@ bool GameRoot3D::is_valid_level(Ref<PackedScene> &level) {
} }
return true; return true;
} }
GameRoot3D *GameRoot3D::singleton_instance{nullptr};
} }

View File

@ -16,16 +16,17 @@ class PlayerInput;
class IPlayer; class IPlayer;
class SpawnPoint3D; class SpawnPoint3D;
class GameRoot : public Node { class GameRoot3D : public Node {
GDCLASS(GameRoot, Node); GDCLASS(GameRoot3D, Node);
static void _bind_methods(); static void _bind_methods();
public: public:
// get the current active singleton instance of GameRoot // 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 // returns true if there is currently a singleton active for GameRoot
static bool has_singleton(); static bool has_singleton();
virtual void _enter_tree() override; virtual void _enter_tree() override;
virtual void _ready() override;
virtual void _exit_tree() override; virtual void _exit_tree() override;
void player_input_connected(); void player_input_connected();
@ -42,32 +43,7 @@ public:
// shorthand for set_game_mode(Ref<GameMode>()) // shorthand for set_game_mode(Ref<GameMode>())
// unsets the gamemode // unsets the gamemode
void reset_game_mode(); void reset_game_mode();
// override the current gamemode
// force-respawns all players
void set_game_mode(Ref<GameMode> prototype);
Ref<GameMode> get_game_mode() const;
Ref<GameState> 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<uint32_t, Pair<PlayerInput*, IPlayer*>> players{};
Ref<GameMode> game_mode{};
};
class GameRoot3D : public GameRoot {
GDCLASS(GameRoot3D, GameRoot);
static void _bind_methods();
public:
virtual void _ready() override;
// shorthand for load_level(level, Transform3D()) // shorthand for load_level(level, Transform3D())
Level3D *load_level(Ref<PackedScene> level); Level3D *load_level(Ref<PackedScene> level);
// load a level, only works if 'level' is a valid scene where the root Node can cast to 'Level3D' // 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); void place_player_at_spawnpoint(IPlayer *player);
// ----- getter / setters ----- // ----- getter / setters -----
// override the current gamemode
// force-respawns all players
void set_game_mode(Ref<GameMode> prototype);
Ref<GameMode> get_game_mode() const;
Ref<GameState> get_game_state() const;
void set_first_boot_level(Ref<PackedScene> level); void set_first_boot_level(Ref<PackedScene> level);
Ref<PackedScene> get_first_boot_level() const; Ref<PackedScene> get_first_boot_level() const;
protected: 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<PackedScene> &level); static bool is_valid_level(Ref<PackedScene> &level);
protected:
static GameRoot3D *singleton_instance;
uint32_t next_player_id{1}; // 0 is the "invalid" player id
HashMap<uint32_t, Pair<PlayerInput*, IPlayer*>> players{};
Ref<GameMode> game_mode{};
private: private:
HashMap<StringName, Level3D*> levels{}; HashMap<StringName, Level3D*> levels{};
HashSet<SpawnPoint3D*> spawn_points{}; HashSet<SpawnPoint3D*> spawn_points{};

View File

@ -10,7 +10,7 @@ class PlayerInput;
class Node; class Node;
class IPlayer { class IPlayer {
friend class GameRoot; friend class GameRoot3D;
public: public:
virtual void setup_player_input(PlayerInput *input) = 0; virtual void setup_player_input(PlayerInput *input) = 0;
virtual Node *to_node() = 0; virtual Node *to_node() = 0;