feat: merged GameRoot and GameRoot3D into GameRoot3D
This commit is contained in:
		
							parent
							
								
									d81ad91a88
								
							
						
					
					
						commit
						2344f3f2b6
					
				
							
								
								
									
										167
									
								
								game_root.cpp
									
									
									
									
									
								
							
							
						
						
									
										167
									
								
								game_root.cpp
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -16,23 +16,24 @@
 | 
			
		|||
#include <godot_cpp/variant/utility_functions.hpp>
 | 
			
		||||
 | 
			
		||||
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<uint32_t, Pair<PlayerInput*, IPlayer*>> &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<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) {
 | 
			
		||||
    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<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) {
 | 
			
		||||
    if(level.is_null() || !level.is_valid()) {
 | 
			
		||||
        this->first_boot_level.unref();
 | 
			
		||||
| 
						 | 
				
			
			@ -239,6 +200,32 @@ Ref<PackedScene> 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<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) {
 | 
			
		||||
    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<PackedScene> &level) {
 | 
			
		|||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GameRoot3D *GameRoot3D::singleton_instance{nullptr};
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<GameMode>())
 | 
			
		||||
    // unsets the gamemode
 | 
			
		||||
    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())
 | 
			
		||||
    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'
 | 
			
		||||
| 
						 | 
				
			
			@ -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<GameMode> prototype);
 | 
			
		||||
    Ref<GameMode> get_game_mode() const;
 | 
			
		||||
    Ref<GameState> get_game_state() const;
 | 
			
		||||
    void set_first_boot_level(Ref<PackedScene> level);
 | 
			
		||||
    Ref<PackedScene> 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<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:
 | 
			
		||||
    HashMap<StringName, Level3D*> levels{};
 | 
			
		||||
    HashSet<SpawnPoint3D*> spawn_points{};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue