feat: game mode no longer owns game state
parent
0c7ab811cf
commit
88bd934d28
|
@ -3,12 +3,10 @@
|
||||||
#include <godot_cpp/classes/packed_scene.hpp>
|
#include <godot_cpp/classes/packed_scene.hpp>
|
||||||
#include <godot_cpp/classes/scene_state.hpp>
|
#include <godot_cpp/classes/scene_state.hpp>
|
||||||
#include "utils/godot_macros.h"
|
#include "utils/godot_macros.h"
|
||||||
#include "game_state.hpp"
|
|
||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
void GameMode::_bind_methods() {
|
void GameMode::_bind_methods() {
|
||||||
#define CLASSNAME GameMode
|
#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");
|
GDPROPERTY_HINTED(player_scene, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,16 +20,4 @@ void GameMode::set_player_scene(gd::Ref<gd::PackedScene> scene) {
|
||||||
gd::Ref<gd::PackedScene> GameMode::get_player_scene() const {
|
gd::Ref<gd::PackedScene> GameMode::get_player_scene() const {
|
||||||
return this->player_scene;
|
return this->player_scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameMode::set_game_state(gd::Ref<GameState> state) {
|
|
||||||
if(state.is_null() || !state.is_valid()) {
|
|
||||||
this->game_state.unref();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->game_state = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
gd::Ref<GameState> GameMode::get_game_state() {
|
|
||||||
return this->game_state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,25 +3,26 @@
|
||||||
|
|
||||||
#include <godot_cpp/classes/packed_scene.hpp>
|
#include <godot_cpp/classes/packed_scene.hpp>
|
||||||
#include <godot_cpp/classes/resource.hpp>
|
#include <godot_cpp/classes/resource.hpp>
|
||||||
#include "game_state.hpp"
|
|
||||||
|
|
||||||
namespace gd = godot;
|
namespace gd = godot;
|
||||||
|
|
||||||
namespace utils {
|
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 {
|
class GameMode : public gd::Resource {
|
||||||
GDCLASS(GameMode, gd::Resource);
|
GDCLASS(GameMode, gd::Resource);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
public:
|
public:
|
||||||
virtual void _begin();
|
virtual void _begin(); //!< Called when the match begins.
|
||||||
virtual void _end();
|
virtual void _end(); //!< Called when the match is ending.
|
||||||
|
|
||||||
void set_player_scene(gd::Ref<gd::PackedScene> scene);
|
void set_player_scene(gd::Ref<gd::PackedScene> scene);
|
||||||
gd::Ref<gd::PackedScene> get_player_scene() const;
|
gd::Ref<gd::PackedScene> get_player_scene() const;
|
||||||
void set_game_state(gd::Ref<GameState> state);
|
|
||||||
gd::Ref<GameState> get_game_state();
|
|
||||||
private:
|
private:
|
||||||
gd::Ref<gd::PackedScene> player_scene{};
|
gd::Ref<gd::PackedScene> player_scene{}; //!< The scene to instantiate when spawning a player.
|
||||||
gd::Ref<GameState> game_state{};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "player_input.hpp"
|
#include "player_input.hpp"
|
||||||
#include "player.hpp"
|
#include "player.hpp"
|
||||||
#include "spawn_point.hpp"
|
#include "spawn_point.hpp"
|
||||||
|
#include "utils/game_state.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <godot_cpp/classes/global_constants.hpp>
|
#include <godot_cpp/classes/global_constants.hpp>
|
||||||
#include <godot_cpp/classes/input.hpp>
|
#include <godot_cpp/classes/input.hpp>
|
||||||
|
@ -21,6 +22,7 @@ void GameRoot3D::_bind_methods() {
|
||||||
#define CLASSNAME GameRoot3D
|
#define CLASSNAME GameRoot3D
|
||||||
GDFUNCTION(reset_game_mode);
|
GDFUNCTION(reset_game_mode);
|
||||||
GDPROPERTY_HINTED(first_boot_level, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
|
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_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_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"));
|
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<GameMode> prototype) {
|
||||||
}
|
}
|
||||||
// shallow clone the game mode prototype ..
|
// shallow clone the game mode prototype ..
|
||||||
this->game_mode = prototype->duplicate(false);
|
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();
|
this->game_mode->_begin();
|
||||||
if(this->game_mode->get_player_scene().is_valid()) {
|
if(this->game_mode->get_player_scene().is_valid()) {
|
||||||
uint32_t new_player_id = this->find_empty_player_slot();
|
uint32_t new_player_id = this->find_empty_player_slot();
|
||||||
|
@ -196,12 +196,28 @@ void GameRoot3D::set_game_mode(gd::Ref<GameMode> prototype) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gd::Ref<GameMode> GameRoot3D::get_game_mode() const {
|
GameMode *GameRoot3D::get_game_mode() const {
|
||||||
return this->game_mode;
|
return this->game_mode.ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
gd::Ref<GameState> GameRoot3D::get_game_state() const {
|
GameState *GameRoot3D::get_game_state() const {
|
||||||
return this->game_mode->get_game_state();
|
return this->game_state.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
gd::HashMap<gd::StringName, Level3D *> &GameRoot3D::get_levels() {
|
||||||
|
return this->levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPlayer *GameRoot3D::get_player(uint32_t id) {
|
||||||
|
return this->players[id].second;
|
||||||
|
}
|
||||||
|
|
||||||
|
gd::Vector<IPlayer*> GameRoot3D::get_players() {
|
||||||
|
gd::Vector<IPlayer*> players{};
|
||||||
|
for(gd::KeyValue<uint32_t, gd::Pair<PlayerInput*, IPlayer*>> pair : this->players) {
|
||||||
|
players.push_back(pair.value.second);
|
||||||
|
}
|
||||||
|
return players;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameRoot3D::set_first_boot_level(gd::Ref<gd::PackedScene> level) {
|
void GameRoot3D::set_first_boot_level(gd::Ref<gd::PackedScene> level) {
|
||||||
|
@ -222,20 +238,12 @@ gd::Ref<gd::PackedScene> GameRoot3D::get_first_boot_level() const {
|
||||||
return this->first_boot_level;
|
return this->first_boot_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
gd::HashMap<gd::StringName, Level3D *> &GameRoot3D::get_levels() {
|
void GameRoot3D::set_game_state_prototype(gd::Ref<GameState> game_state) {
|
||||||
return this->levels;
|
this->game_state_prototype = game_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPlayer *GameRoot3D::get_player(uint32_t id) {
|
gd::Ref<GameState> GameRoot3D::get_game_state_prototype() const {
|
||||||
return this->players[id].second;
|
return this->game_state_prototype;
|
||||||
}
|
|
||||||
|
|
||||||
gd::Vector<IPlayer*> GameRoot3D::get_players() {
|
|
||||||
gd::Vector<IPlayer*> players{};
|
|
||||||
for(gd::KeyValue<uint32_t, gd::Pair<PlayerInput*, IPlayer*>> pair : this->players) {
|
|
||||||
players.push_back(pair.value.second);
|
|
||||||
}
|
|
||||||
return players;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameRoot3D::grab_singleton() {
|
void GameRoot3D::grab_singleton() {
|
||||||
|
|
116
game_root.hpp
116
game_root.hpp
|
@ -2,6 +2,7 @@
|
||||||
#define GAME_ROOT_HPP
|
#define GAME_ROOT_HPP
|
||||||
|
|
||||||
#include "game_mode.hpp"
|
#include "game_mode.hpp"
|
||||||
|
#include "game_state.hpp"
|
||||||
#include "level.hpp"
|
#include "level.hpp"
|
||||||
#include <godot_cpp/classes/node.hpp>
|
#include <godot_cpp/classes/node.hpp>
|
||||||
#include <godot_cpp/classes/packed_scene.hpp>
|
#include <godot_cpp/classes/packed_scene.hpp>
|
||||||
|
@ -19,82 +20,129 @@ class PlayerInput;
|
||||||
class IPlayer;
|
class IPlayer;
|
||||||
class SpawnPoint3D;
|
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 {
|
class GameRoot3D : public gd::Node {
|
||||||
GDCLASS(GameRoot3D, gd::Node);
|
GDCLASS(GameRoot3D, gd::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 GameRoot3D *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 _ready() override;
|
||||||
virtual void _exit_tree() 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();
|
void player_input_connected();
|
||||||
// force-disconnect a player
|
/*! Force-disconnect a player
|
||||||
// calls queue_free on the IPlayer instance
|
*
|
||||||
|
* Calls queue_free on the IPlayer instance
|
||||||
|
*/
|
||||||
void remove_player(uint32_t player_id);
|
void remove_player(uint32_t player_id);
|
||||||
// calls remove_player for every used player input slot
|
// calls remove_player for every used player input slot
|
||||||
void remove_all_players();
|
void remove_all_players();
|
||||||
// initialize and register a player instance
|
/*! 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
|
* 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);
|
bool initialize_player(IPlayer *player, uint32_t id);
|
||||||
|
|
||||||
// shorthand for set_game_mode(Ref<GameMode>())
|
/*! Un-set game mode.
|
||||||
// unsets the gamemode
|
* Shorthand for set_game_mode(Ref<GameMode>())
|
||||||
|
*/
|
||||||
void reset_game_mode();
|
void reset_game_mode();
|
||||||
|
|
||||||
// shorthand for load_level(level, Transform3D())
|
//! shorthand for load_level(level, Transform3D())
|
||||||
Level3D *load_level(gd::Ref<gd::PackedScene> level);
|
Level3D *load_level(gd::Ref<gd::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'.
|
||||||
// sets the level's root node's global transform
|
*
|
||||||
|
* \param at Sets the root node's global transform.
|
||||||
|
*/
|
||||||
Level3D *load_level_at(gd::Ref<gd::PackedScene> level, gd::Transform3D at);
|
Level3D *load_level_at(gd::Ref<gd::PackedScene> level, gd::Transform3D at);
|
||||||
|
//! Unload all currently loaded levels.
|
||||||
void unload_all_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<gd::PackedScene> level);
|
void replace_levels(gd::Ref<gd::PackedScene> level);
|
||||||
|
//! Register a spawnpoint for use when spawning players
|
||||||
// register a spawnpoint for use when spawning players
|
|
||||||
void register_spawn_point(SpawnPoint3D *spawn_point);
|
void register_spawn_point(SpawnPoint3D *spawn_point);
|
||||||
// remove a spawnpoint so it can't be used to spawn players
|
// remove a spawnpoint so it can't be used to spawn players
|
||||||
void unregister_spawn_point(SpawnPoint3D *spawn_point);
|
void unregister_spawn_point(SpawnPoint3D *spawn_point);
|
||||||
void place_player_at_spawnpoint(IPlayer *player);
|
void place_player_at_spawnpoint(IPlayer *player);
|
||||||
void player_despawned(uint32_t id);
|
void player_despawned(uint32_t id);
|
||||||
|
|
||||||
// ----- getter / setters -----
|
/*! Override the current gamemode.
|
||||||
// override the current gamemode
|
*
|
||||||
// force-respawns all players
|
* Replaces game mode requires destroying and respawning all players
|
||||||
|
*/
|
||||||
void set_game_mode(gd::Ref<GameMode> prototype);
|
void set_game_mode(gd::Ref<GameMode> prototype);
|
||||||
gd::Ref<GameMode> get_game_mode() const;
|
//! get the current active game mode.
|
||||||
gd::Ref<GameState> get_game_state() const;
|
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<gd::StringName, Level3D *> &get_levels();
|
||||||
|
//! Get the player instance associated with id.
|
||||||
|
IPlayer *get_player(uint32_t id);
|
||||||
|
//! Get all players in a list.
|
||||||
|
gd::Vector<IPlayer*> get_players();
|
||||||
void set_first_boot_level(gd::Ref<gd::PackedScene> level);
|
void set_first_boot_level(gd::Ref<gd::PackedScene> level);
|
||||||
gd::Ref<gd::PackedScene> get_first_boot_level() const;
|
gd::Ref<gd::PackedScene> get_first_boot_level() const;
|
||||||
gd::HashMap<gd::StringName, Level3D *> &get_levels();
|
void set_game_state_prototype(gd::Ref<GameState> game_state);
|
||||||
IPlayer *get_player(uint32_t id);
|
gd::Ref<GameState> get_game_state_prototype() const;
|
||||||
gd::Vector<IPlayer*> get_players();
|
|
||||||
protected:
|
protected:
|
||||||
// attempt to make 'this' the current singleton instance
|
//! Attempt to make 'this' the current singleton instance.
|
||||||
void grab_singleton();
|
void grab_singleton();
|
||||||
// attempt to stop being the active singleton instance
|
/*! Attempt to stop being the active singleton instance.
|
||||||
// only works if the current singleton is 'this'
|
*
|
||||||
|
* Only works if the current singleton is 'this'.
|
||||||
|
*/
|
||||||
void release_singleton();
|
void release_singleton();
|
||||||
|
//! Find a Player Input device not yet associated with a player.
|
||||||
uint32_t find_empty_player_slot() const;
|
uint32_t find_empty_player_slot() const;
|
||||||
|
//! Spawn a player to be associated with id.
|
||||||
IPlayer *spawn_player(uint32_t id);
|
IPlayer *spawn_player(uint32_t id);
|
||||||
|
//! Callback for a level exiting the tree.
|
||||||
void level_unloaded(gd::StringName scene_path);
|
void level_unloaded(gd::StringName scene_path);
|
||||||
|
//! Check if a scene is a valid level.
|
||||||
static bool is_valid_level(gd::Ref<gd::PackedScene> &level);
|
static bool is_valid_level(gd::Ref<gd::PackedScene> &level);
|
||||||
protected:
|
private:
|
||||||
static GameRoot3D *singleton_instance;
|
static GameRoot3D *singleton_instance;
|
||||||
|
|
||||||
uint32_t next_player_id{1}; // 0 is the "invalid" player id
|
uint32_t next_player_id{1}; //!< Next available player ID. Default is 1 because 0 is the "invalid" player id.
|
||||||
gd::HashMap<uint32_t, gd::Pair<PlayerInput*, IPlayer*>> players{};
|
gd::HashMap<uint32_t, gd::Pair<PlayerInput*, IPlayer*>> players{}; //!< all players by id by input device.
|
||||||
gd::Ref<GameMode> game_mode{};
|
|
||||||
private:
|
|
||||||
gd::RandomNumberGenerator rng{};
|
|
||||||
gd::HashMap<gd::StringName, Level3D*> levels{};
|
|
||||||
gd::Vector<SpawnPoint3D*> spawn_points{};
|
|
||||||
|
|
||||||
gd::Ref<gd::PackedScene> first_boot_level{};
|
gd::RandomNumberGenerator rng{}; //!< Global random number generator.
|
||||||
|
gd::HashMap<gd::StringName, Level3D*> levels{}; //!< all currently active levels identified by their resource paths.
|
||||||
|
gd::Vector<SpawnPoint3D*> spawn_points{}; //!< all currently available spawn points.
|
||||||
|
gd::Ref<GameMode> 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<GameState> game_state{};
|
||||||
|
|
||||||
|
gd::Ref<gd::PackedScene> first_boot_level{}; //!< The level to boot into on startup.
|
||||||
|
gd::Ref<GameState> game_state_prototype{}; //!< The default game state data used for game_state if no save data is available.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue