feat: GameMode is now a Node rather than Resource

stripped
Sara 2024-05-30 15:07:17 +02:00
parent ec2c3fd835
commit 2ac9e8399f
6 changed files with 39 additions and 34 deletions

View File

@ -10,9 +10,6 @@ void GameMode::_bind_methods() {
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");
} }
void GameMode::_begin() {}
void GameMode::_end() {}
void GameMode::set_player_scene(gd::Ref<gd::PackedScene> scene) { void GameMode::set_player_scene(gd::Ref<gd::PackedScene> scene) {
this->player_scene = scene; this->player_scene = scene;
} }

View File

@ -1,24 +1,21 @@
#ifndef UTILS_GAME_MODE_HPP #ifndef UTILS_GAME_MODE_HPP
#define UTILS_GAME_MODE_HPP #define UTILS_GAME_MODE_HPP
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/packed_scene.hpp> #include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/resource.hpp>
namespace gd = godot; namespace gd = godot;
namespace utils { namespace utils {
/*! Stores session-relevant data. /*! 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. * Inheriting classes are intended to keep only data that is relevant 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. * 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::Node {
GDCLASS(GameMode, gd::Resource); GDCLASS(GameMode, gd::Node);
static void _bind_methods(); static void _bind_methods();
public: public:
virtual void _begin(); //!< Called when the match begins.
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;
private: private:

View File

@ -96,7 +96,7 @@ bool GameRoot3D::initialize_player(IPlayer *player, uint32_t id) {
} }
void GameRoot3D::reset_game_mode() { void GameRoot3D::reset_game_mode() {
this->set_game_mode(gd::Ref<GameMode>()); this->set_game_mode(nullptr);
} }
Level3D *GameRoot3D::load_level(gd::Ref<gd::PackedScene> level) { Level3D *GameRoot3D::load_level(gd::Ref<gd::PackedScene> level) {
@ -116,13 +116,15 @@ Level3D *GameRoot3D::load_level_at(gd::Ref<gd::PackedScene> level, gd::Transform
instance->connect("tree_exited", callable_mp(this, &GameRoot3D::level_unloaded).bind(level->get_path())); instance->connect("tree_exited", callable_mp(this, &GameRoot3D::level_unloaded).bind(level->get_path()));
// store and add to tree at desired transform // store and add to tree at desired transform
// if this is the first level containing a game mode currently active use it's gamemode as a prototype // if this is the first level containing a game mode currently active use it's gamemode as a prototype
bool const switch_game_mode{this->game_mode.is_null()}; gd::Ref<gd::PackedScene> game_mode_prototype{instance->get_game_mode_prototype()};
bool const switch_game_mode{this->game_mode->get_scene_file_path() != game_mode_prototype->get_path()};
if(switch_game_mode) { if(switch_game_mode) {
this->set_game_mode(instance->get_game_mode_prototype()); this->set_game_mode(instance->get_game_mode_prototype());
} }
this->add_child(instance); this->add_child(instance);
instance->set_global_transform(at); instance->set_global_transform(at);
if(switch_game_mode && this->game_mode.is_valid()) { // set initial player positions if new player were spawned due to game mode switch
if(switch_game_mode && this->game_mode != nullptr) {
for(gd::KeyValue<uint32_t, gd::Pair<PlayerInput *, IPlayer *>> const &kvp : this->players) { for(gd::KeyValue<uint32_t, gd::Pair<PlayerInput *, IPlayer *>> const &kvp : this->players) {
this->place_player_at_spawnpoint(kvp.value.second); this->place_player_at_spawnpoint(kvp.value.second);
} }
@ -172,19 +174,21 @@ void GameRoot3D::player_despawned(uint32_t id) {
pair.first->clear_listeners(); pair.first->clear_listeners();
} }
void GameRoot3D::set_game_mode(gd::Ref<GameMode> prototype) { void GameRoot3D::set_game_mode(gd::Ref<gd::PackedScene> prototype) {
this->remove_all_players(); this->remove_all_players();
// allow "unsetting" the gamemode by passing an invalid gamemode if(this->game_mode != nullptr)
// shorthand for this behaviour is reset_game_mode this->game_mode->queue_free();
if(prototype.is_null() || !prototype.is_valid()) { if(prototype.is_null() || !prototype.is_valid())
if(!this->game_mode.is_null() && this->game_mode.is_valid()) return; // allow "unsetting" the gamemode by passing an invalid gamemode
this->game_mode->_end(); // Detect passing of valid scene that is an invalid game mode
this->game_mode.unref(); if(!gd::ClassDB::is_parent_class(prototype->get_state()->get_node_type(0), "GameMode")) {
gd::UtilityFunctions::push_error("Attempted to load non-gamemode scene as gamemode");
return; return;
} }
// shallow clone the game mode prototype .. // instantiate the game mode as a child
this->game_mode = prototype->duplicate(false); this->game_mode = Object::cast_to<GameMode>(prototype->instantiate());
this->game_mode->_begin(); this->add_child(game_mode);
// instantiate players
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();
do { do {
@ -197,7 +201,7 @@ void GameRoot3D::set_game_mode(gd::Ref<GameMode> prototype) {
} }
GameMode *GameRoot3D::get_game_mode() const { GameMode *GameRoot3D::get_game_mode() const {
return this->game_mode.ptr(); return this->game_mode;
} }
GameState *GameRoot3D::get_game_state() const { GameState *GameRoot3D::get_game_state() const {

View File

@ -58,7 +58,7 @@ public:
bool initialize_player(IPlayer *player, uint32_t id); bool initialize_player(IPlayer *player, uint32_t id);
/*! Un-set game mode. /*! Un-set game mode.
* Shorthand for set_game_mode(Ref<GameMode>()) * Shorthand for `set_game_mode(Ref<PackedScene>())`
*/ */
void reset_game_mode(); void reset_game_mode();
@ -91,7 +91,7 @@ public:
* *
* Replaces game mode requires destroying and respawning 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<gd::PackedScene> prototype);
//! get the current active game mode. //! get the current active game mode.
GameMode *get_game_mode() const; GameMode *get_game_mode() const;
//! Get the current active game state. //! Get the current active game state.
@ -134,7 +134,7 @@ private:
gd::RandomNumberGenerator rng{}; //!< Global random number generator. gd::RandomNumberGenerator rng{}; //!< Global random number generator.
gd::HashMap<gd::StringName, Level3D*> levels{}; //!< all currently active levels identified by their resource paths. 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::Vector<SpawnPoint3D*> spawn_points{}; //!< all currently available spawn points.
gd::Ref<GameMode> game_mode{}; //!< current active gamemode. GameMode *game_mode{}; //!< current active gamemode.
/*! Active game state. /*! Active game state.
* *
* Will be assigned loaded save data, or game_state_prototype if no save data is found. * Will be assigned loaded save data, or game_state_prototype if no save data is found.

View File

@ -1,17 +1,24 @@
#include "level.hpp" #include "level.hpp"
#include "godot_cpp/core/class_db.hpp"
#include "utils/godot_macros.hpp" #include "utils/godot_macros.hpp"
#include <godot_cpp/classes/scene_state.hpp>
namespace utils { namespace utils {
void Level3D::_bind_methods() { void Level3D::_bind_methods() {
#define CLASSNAME Level3D #define CLASSNAME Level3D
GDPROPERTY_HINTED(game_mode_prototype, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "GameMode"); GDPROPERTY_HINTED(game_mode_prototype, gd::Variant::OBJECT, gd::PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
} }
void Level3D::set_game_mode_prototype(gd::Ref<GameMode> prototype) { void Level3D::set_game_mode_prototype(gd::Ref<gd::PackedScene> prototype) {
this->game_mode_prototype = prototype; if(prototype.is_null() || !prototype.is_valid())
this->game_mode_prototype = gd::Ref<gd::PackedScene>(nullptr);
else if(!gd::ClassDB::is_parent_class(prototype->get_state()->get_node_type(0), "GameMode"))
return;
else
this->game_mode_prototype = prototype;
} }
gd::Ref<GameMode> Level3D::get_game_mode_prototype() const { gd::Ref<gd::PackedScene> Level3D::get_game_mode_prototype() const {
return this->game_mode_prototype; return this->game_mode_prototype;
} }
} }

View File

@ -15,10 +15,10 @@ class Level3D : public gd::Node3D {
GDCLASS(Level3D, gd::Node3D); GDCLASS(Level3D, gd::Node3D);
static void _bind_methods(); static void _bind_methods();
public: public:
void set_game_mode_prototype(gd::Ref<GameMode> prototype); void set_game_mode_prototype(gd::Ref<gd::PackedScene> prototype);
gd::Ref<GameMode> get_game_mode_prototype() const; gd::Ref<gd::PackedScene> get_game_mode_prototype() const;
private: private:
gd::Ref<GameMode> game_mode_prototype{}; //!< The starting state of the game mode to instantiate if this is the "leading" level. gd::Ref<gd::PackedScene> game_mode_prototype{}; //!< The starting state of the game mode to instantiate if this is the "leading" level.
}; };
} }