diff --git a/src/entrance.cpp b/src/entrance.cpp index 63e50bb..51b9c1f 100644 --- a/src/entrance.cpp +++ b/src/entrance.cpp @@ -1,27 +1,29 @@ #include "entrance.hpp" +#include "godot_cpp/variant/utility_functions.hpp" #include "level.hpp" namespace godot { void Entrance::_bind_methods() {} void Entrance::_enter_tree() { - this->seek_parent_level(); - if(parentLevel != nullptr) { + parentLevel = this->seek_parent_level(); + if (parentLevel != nullptr) { parentLevel->add_entrance(this); } } void Entrance::_exit_tree() { - if(parentLevel != nullptr) + if (parentLevel != nullptr) parentLevel->remove_entrance(this); } -Level *Entrance::seek_parent_level() { - Node *current = this; +Level* Entrance::seek_parent_level() { + Node* current = this; do { current = current->get_parent(); - if(Level *level = Object::cast_to(current)) + if (Level* level = Object::cast_to(current)) return level; - } while(current != nullptr); + } while (current != nullptr); + UtilityFunctions::push_error("Failed to find level"); return nullptr; } -} +} // namespace godot diff --git a/src/entrance.hpp b/src/entrance.hpp index 0b22a67..35b9676 100644 --- a/src/entrance.hpp +++ b/src/entrance.hpp @@ -9,14 +9,17 @@ class Level; class Entrance : public Node3D { GDCLASS(Entrance, Node3D) static void _bind_methods(); + protected: - Level *parentLevel; + Level* parentLevel; + public: virtual void _enter_tree() override; virtual void _exit_tree() override; -protected: - Level *seek_parent_level(); -}; -} -#endif // !ENTRANCE_HPP +protected: + Level* seek_parent_level(); +}; +} // namespace godot + +#endif // !ENTRANCE_HPP diff --git a/src/game_mode.cpp b/src/game_mode.cpp new file mode 100644 index 0000000..a5efdfe --- /dev/null +++ b/src/game_mode.cpp @@ -0,0 +1,109 @@ +#include "game_mode.hpp" +#include +#include "godot_cpp/variant/utility_functions.hpp" +#include "godot_macros.h" +#include "level.hpp" +#include "player.hpp" + +namespace godot { +GameMode* GameMode::static_instance{nullptr}; + +void GameMode::_bind_methods() { +#define CLASSNAME GameMode + GDPROPERTY_HINTED(first_level, Variant::OBJECT, + PropertyHint::PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); + GDPROPERTY_HINTED(player_scene, Variant::OBJECT, + PropertyHint::PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); +} + +void GameMode::_enter_tree() { + GDGAMEONLY(); + if (GameMode::static_instance != nullptr) { + this->queue_free(); + } else { + GameMode::static_instance = this; + } +} + +void GameMode::_exit_tree() { + GDGAMEONLY(); + if (GameMode::static_instance == this) { + GameMode::static_instance = nullptr; + } +} + +void GameMode::_ready() { + GDGAMEONLY(); + this->load_level(firstLevel, std::nullopt); +} + +void GameMode::spawn_player(String const& entrance) { + if (playerInstance == nullptr) { + Node* node = playerScene->instantiate(); + if (node == nullptr) { + UtilityFunctions::push_error( + "Failed to instantiate player subscene"); + return; + } + playerInstance = Object::cast_to(node); + if (playerInstance == nullptr) { + UtilityFunctions::push_error( + "Player scene root is not of type Player"); + node->queue_free(); + return; + } + this->add_child(playerInstance); + } + if (levelInstance != nullptr) + playerInstance->set_global_transform( + levelInstance->get_entrance(entrance)); + else + UtilityFunctions::push_error("Cannot spawn player without a level"); +} + +void GameMode::load_level(Ref& level, + std::optional entrance) { + if (levelInstance != nullptr) + levelInstance->queue_free(); + if (firstLevel.is_null() || !firstLevel.is_valid()) { + UtilityFunctions::push_error("No initial level configured"); + return; + } + Node* inst = firstLevel->instantiate(); + if (inst == nullptr) { + UtilityFunctions::push_error("Failed to instantiate level"); + return; + } + levelInstance = Object::cast_to(inst); + if (levelInstance == nullptr) { + UtilityFunctions::push_error("Level scene root is not of type Level"); + inst->queue_free(); + return; + } + this->add_child(inst); + if (entrance.has_value()) + this->spawn_player(entrance.value()); + else + this->spawn_player(levelInstance->get_default_entrance()); +} + +Player* GameMode::get_player_instance() const { + return playerInstance; +} + +void GameMode::set_first_level(Ref level) { + firstLevel = level; +} + +Ref GameMode::get_first_level() const { + return firstLevel; +} + +void GameMode::set_player_scene(Ref scene) { + playerScene = scene; +} + +Ref GameMode::get_player_scene() const { + return playerScene; +} +} // namespace godot diff --git a/src/game_mode.hpp b/src/game_mode.hpp index 1bf2190..84c8320 100644 --- a/src/game_mode.hpp +++ b/src/game_mode.hpp @@ -1,6 +1,8 @@ #ifndef GAME_STATE_HPP #define GAME_STATE_HPP +#include "optional" + #include "godot_cpp/classes/node.hpp" #include "godot_cpp/classes/packed_scene.hpp" @@ -11,14 +13,31 @@ class Player; class GameMode : public Node { GDCLASS(GameMode, Node) static void _bind_methods(); + static GameMode* static_instance; + protected: - Level *currentLevel{nullptr}; + Level* levelInstance{nullptr}; Ref firstLevel{}; - Player *playerInstance{nullptr}; -public: - void load_level(Ref levelScene); -}; -} + Player* playerInstance{nullptr}; + Ref playerScene{}; -#endif // !GAME_STATE_HPP +public: + virtual void _enter_tree() override; + virtual void _exit_tree() override; + virtual void _ready() override; + + void spawn_player(String const& entrance); + void load_level(Ref& levelScene, + std::optional entrance); + + Player* get_player_instance() const; + + void set_first_level(Ref level); + Ref get_first_level() const; + void set_player_scene(Ref scene); + Ref get_player_scene() const; +}; +} // namespace godot + +#endif // !GAME_STATE_HPP