why doesn't this work :(
Some checks failed
GDExtension CMake / build-linux (push) Has been cancelled
Some checks failed
GDExtension CMake / build-linux (push) Has been cancelled
This commit is contained in:
parent
c723c3f55b
commit
f9406aa9a9
2
extern/godot-cpp
vendored
2
extern/godot-cpp
vendored
@ -1 +1 @@
|
||||
Subproject commit 56571dc584ee7d14919996c6d58fb5b35e64af63
|
||||
Subproject commit f3a1a2fd458dfaf4de08c906f22a2fe9e924b16f
|
@ -2,7 +2,7 @@
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dw7fcik6u7kkn"
|
||||
uid="uid://dmcjlguuq8xqt"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
|
9
project/main.tscn
Normal file
9
project/main.tscn
Normal file
@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://beuvt3vhjibjt"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://dmcjlguuq8xqt" path="res://icon.svg" id="1_lodlx"]
|
||||
|
||||
[node name="Main" type="Node"]
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
texture = ExtResource("1_lodlx")
|
||||
centered = false
|
@ -11,5 +11,6 @@ config_version=5
|
||||
[application]
|
||||
|
||||
config/name="Naval Swarm"
|
||||
run/main_scene="res://main.tscn"
|
||||
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
@ -1,89 +0,0 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include <gdextension_interface.h>
|
||||
#include <godot_cpp/classes/engine.hpp>
|
||||
#include <godot_cpp/core/memory.hpp>
|
||||
#include <godot_cpp/variant/string_name.hpp>
|
||||
|
||||
#include "api/extension_interface.hpp"
|
||||
#include "entity/camera.hpp"
|
||||
#include "entity/character/character.hpp"
|
||||
#include "entity/character/enemy.hpp"
|
||||
#include "entity/character/player.hpp"
|
||||
#include "entity/controller/character_controller.hpp"
|
||||
#include "entity/controller/enemy_controller.hpp"
|
||||
#include "entity/controller/player_controller.hpp"
|
||||
#include "entity/level.hpp"
|
||||
#include "entity/projectile/projectile_spawner.hpp"
|
||||
#include "main.hpp"
|
||||
#include "singletons/console.hpp"
|
||||
#include "ui/main_dialog.hpp"
|
||||
#include "util/engine.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
static inline console* console_singleton{ nullptr };
|
||||
|
||||
void initialize_static_objects()
|
||||
{
|
||||
console_singleton = memnew(console);
|
||||
rl::engine::get()->register_singleton("Console", console::get());
|
||||
}
|
||||
|
||||
void teardown_static_objects()
|
||||
{
|
||||
rl::engine::get()->unregister_singleton("Console");
|
||||
memdelete(console_singleton);
|
||||
}
|
||||
|
||||
void initialize_extension_module(godot::ModuleInitializationLevel init_level)
|
||||
{
|
||||
if (init_level != godot::MODULE_INITIALIZATION_LEVEL_SCENE)
|
||||
return;
|
||||
|
||||
godot::ClassDB::register_class<rl::Projectile>();
|
||||
godot::ClassDB::register_class<rl::ProjectileSpawner>();
|
||||
|
||||
godot::ClassDB::register_abstract_class<rl::CharacterController>();
|
||||
godot::ClassDB::register_class<rl::PlayerController>(true);
|
||||
godot::ClassDB::register_class<rl::EnemyController>();
|
||||
|
||||
godot::ClassDB::register_class<rl::Camera>();
|
||||
godot::ClassDB::register_class<rl::Character>();
|
||||
godot::ClassDB::register_class<rl::Enemy>();
|
||||
godot::ClassDB::register_class<rl::Player>();
|
||||
|
||||
godot::ClassDB::register_class<rl::Level>();
|
||||
godot::ClassDB::register_class<rl::MainDialog>();
|
||||
godot::ClassDB::register_class<rl::Main>();
|
||||
|
||||
godot::ClassDB::register_class<console>();
|
||||
|
||||
initialize_static_objects();
|
||||
}
|
||||
|
||||
void uninitialize_extension_module(godot::ModuleInitializationLevel init_level)
|
||||
{
|
||||
if (init_level != godot::MODULE_INITIALIZATION_LEVEL_SCENE)
|
||||
return;
|
||||
|
||||
teardown_static_objects();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
GDExtensionBool GDE_EXPORT extension_library_init(GDExtensionInterfaceGetProcAddress addr,
|
||||
GDExtensionClassLibraryPtr lib,
|
||||
GDExtensionInitialization* init)
|
||||
{
|
||||
const auto init_level = godot::MODULE_INITIALIZATION_LEVEL_SCENE;
|
||||
godot::GDExtensionBinding::InitObject init_obj(addr, lib, init);
|
||||
|
||||
init_obj.register_initializer(initialize_extension_module);
|
||||
init_obj.register_terminator(uninitialize_extension_module);
|
||||
init_obj.set_minimum_library_initialization_level(init_level);
|
||||
|
||||
return init_obj.init();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
|
||||
namespace godot
|
||||
{
|
||||
void initialize_static_objects();
|
||||
void teardown_static_objects();
|
||||
void initialize_extension_module(ModuleInitializationLevel init_level);
|
||||
void uninitialize_extension_module(ModuleInitializationLevel init_level);
|
||||
|
||||
extern "C"
|
||||
{
|
||||
GDExtensionBool GDE_EXPORT extension_library_init(GDExtensionInterfaceGetProcAddress addr,
|
||||
GDExtensionClassLibraryPtr lib,
|
||||
GDExtensionInitialization* init);
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include <godot_cpp/core/error_macros.hpp>
|
||||
|
||||
#ifndef NDEBUG
|
||||
//
|
||||
// In debug mode, checks the passed in condition and outputs
|
||||
// detailed information to stederr, including a custom error
|
||||
// message when the condition evaluates to false.
|
||||
//
|
||||
#define assertion(condition, message) \
|
||||
do \
|
||||
{ \
|
||||
if (!(condition)) [[unlikely]] \
|
||||
{ \
|
||||
::godot::_err_print_error(__FUNCTION__, __FILE__, __LINE__, \
|
||||
message " => condition: (" #condition ")"); \
|
||||
::godot::_err_flush_stdout(); \
|
||||
GENERATE_TRAP(); \
|
||||
} \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define error_msg(message) \
|
||||
do \
|
||||
{ \
|
||||
::godot::_err_print_error(__FUNCTION__, __FILE__, __LINE__, message); \
|
||||
::godot::_err_flush_stdout(); \
|
||||
GENERATE_TRAP(); \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define runtime_assert(condition) assertion(condition, "validation check failed")
|
||||
|
||||
#else
|
||||
//
|
||||
// In release mode the macro does nothing ((void)0), including
|
||||
// the execution of the condition so don't define the expression
|
||||
// as anything that would be considered program logis.
|
||||
//
|
||||
#define assertion(condition, message) static_cast<void>(0)
|
||||
#define error_msg(message) static_cast<void>(0)
|
||||
#define runtime_assert(condition) static_cast<void>(0)
|
||||
#endif
|
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// MSVC doesn't have a clean way to ignore custom
|
||||
// attributes within a namespace like clang or gcc
|
||||
#define property
|
||||
#define signal_slot
|
||||
#elif defined(__GNUG__) || defined(__clang__)
|
||||
// these macros are used to define custom attributes to label godot signal callbacks
|
||||
// and node properties. they don't do anything other than making it easier to spot
|
||||
// these functions when reading the code or searching for them in an IDE.
|
||||
#define property __attribute__((rl("property")))
|
||||
#define signal_slot __attribute__((rl("signal_slot")))
|
||||
#endif
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace godot
|
||||
{
|
||||
template <typename T, typename V>
|
||||
struct GetTypeInfo;
|
||||
class Variant;
|
||||
class Object;
|
||||
}
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
template <auto S>
|
||||
concept CompileTimeStr = std::same_as<decltype(S), std::string_view> ||
|
||||
std::same_as<decltype(S), std::string> ||
|
||||
std::same_as<decltype(S), const char*>;
|
||||
|
||||
template <typename T>
|
||||
concept GDObjectDerived =
|
||||
std::derived_from<std::remove_cvref_t<std::remove_pointer_t<T>>, godot::Object> ||
|
||||
std::same_as<std::remove_cvref_t<std::remove_pointer_t<T>>, godot::Object>;
|
||||
|
||||
template <typename T>
|
||||
concept VariantConstructable = requires(const T& t) { static_cast<godot::Variant>(t); };
|
||||
|
||||
template <typename T>
|
||||
concept VariantConvertable = requires(T t) { static_cast<T>(t); };
|
||||
|
||||
template <typename T>
|
||||
concept VariantCompatible =
|
||||
VariantConstructable<std::remove_cvref_t<T>> &&
|
||||
VariantConvertable<std::remove_cvref_t<T>> && requires {
|
||||
godot::GetTypeInfo<std::type_identity_t<std::remove_cvref_t<T>>, void>::VARIANT_TYPE;
|
||||
};
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace rl::inline constants
|
||||
{
|
||||
namespace name
|
||||
{
|
||||
namespace level
|
||||
{
|
||||
constexpr inline auto level1{ "Level1" };
|
||||
constexpr inline auto physics_box{ "PhysicsBox" };
|
||||
}
|
||||
|
||||
namespace dialog
|
||||
{
|
||||
constexpr inline auto console{ "ConsolePanel" };
|
||||
constexpr inline auto canvas_layer{ "MainCanvasLayer" };
|
||||
|
||||
}
|
||||
|
||||
namespace character
|
||||
{
|
||||
constexpr inline auto player{ "Player" };
|
||||
constexpr inline auto enemy{ "Enemy" };
|
||||
constexpr inline auto firing_pt{ "FiringPoint" };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace event
|
||||
{
|
||||
constexpr inline auto position_changed{ "position_changed" };
|
||||
constexpr inline auto entered_area{ "entered_area" };
|
||||
constexpr inline auto exited_area{ "exited_area" };
|
||||
constexpr inline auto spawn_projectile{ "spawn_projectile" };
|
||||
constexpr inline auto character_move{ "character_move" };
|
||||
constexpr inline auto character_rotate{ "character_rotate" };
|
||||
constexpr inline auto character_shoot{ "character_shoot" };
|
||||
constexpr inline auto body_entered{ "body_entered" };
|
||||
constexpr inline auto body_exited{ "body_exited" };
|
||||
constexpr inline auto signal_example{ "custom_signal_example" };
|
||||
}
|
||||
|
||||
enum class LayerID : uint32_t {
|
||||
Player = 0x00000001,
|
||||
NPCs = 0x00000002,
|
||||
Projectiles = 0x00000004,
|
||||
Walls = 0x00000008,
|
||||
DamageZones = 0x00000010,
|
||||
DeathZones = 0x00000020,
|
||||
PhysicsObjects = 0x00000040,
|
||||
Layer08 = 0x00000080,
|
||||
Layer09 = 0x00000100,
|
||||
Layer10 = 0x00000200,
|
||||
Layer11 = 0x00000400,
|
||||
Layer12 = 0x00000800,
|
||||
Layer13 = 0x00001000,
|
||||
Layer14 = 0x00002000,
|
||||
Layer15 = 0x00004000,
|
||||
Layer16 = 0x00008000,
|
||||
};
|
||||
|
||||
namespace path
|
||||
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
constexpr inline auto Level1{ "res://scenes/levels/level1.tscn" };
|
||||
constexpr inline auto Player{ "res://scenes/characters/player.tscn" };
|
||||
constexpr inline auto Bullet{ "res://scenes/projectiles/bullet.tscn" };
|
||||
}
|
||||
|
||||
namespace ui
|
||||
{
|
||||
constexpr inline auto MainDialog{ "res://scenes/ui/main_dialog.tscn" };
|
||||
}
|
||||
}
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <godot_cpp/variant/string.hpp>
|
||||
|
||||
#if defined(__GNUG__)
|
||||
#define stdcall __attribute__((stdcall))
|
||||
#elif defined(__clang__)
|
||||
#define stdcall __stdcall
|
||||
#elif defined(_MSC_VER)
|
||||
#define stdcall __cdecl
|
||||
#endif
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <typename H>
|
||||
void to_arg_vec_impl(std::vector<godot::String>& s, H&& h)
|
||||
{
|
||||
s.push_back(typeid(decltype(h)).name());
|
||||
}
|
||||
|
||||
template <typename H, typename... T>
|
||||
void to_arg_vec_impl(std::vector<godot::String>& s, H&& h, T&&... t)
|
||||
{
|
||||
s.push_back(typeid(decltype(h)).name());
|
||||
to_arg_vec_impl(s, std::forward<T>(t)...);
|
||||
}
|
||||
|
||||
template <typename... TupleTypes, std::size_t... Idx>
|
||||
std::vector<godot::String> to_arg_vec(const std::tuple<TupleTypes...>& tup,
|
||||
std::integer_sequence<std::size_t, Idx...>)
|
||||
{
|
||||
std::vector<godot::String> result = {};
|
||||
to_arg_vec_impl(result, std::get<Idx>(tup)...);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... TupleTypes>
|
||||
std::vector<godot::String> to_arg_vec(const std::tuple<TupleTypes...>& tup)
|
||||
{
|
||||
static constexpr size_t arg_count = sizeof...(TupleTypes);
|
||||
return to_arg_vec(tup, std::make_index_sequence<arg_count>());
|
||||
}
|
||||
|
||||
template <std::size_t... Indexes>
|
||||
auto arg_vec_to_tuple(const std::vector<godot::String>& v, std::index_sequence<Indexes...>)
|
||||
{
|
||||
return std::make_tuple(v[Indexes]...);
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
auto arg_vec_to_tuple(const std::vector<godot::String>& v)
|
||||
{
|
||||
return arg_vec_to_tuple(v, std::make_index_sequence<N>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename>
|
||||
struct function_traits;
|
||||
|
||||
// non-const member function
|
||||
template <typename TRet, typename TClass, typename... TArgs>
|
||||
struct function_traits<TRet (TClass::*)(TArgs...)>
|
||||
{
|
||||
using return_type = TRet;
|
||||
using class_type = TClass;
|
||||
using arg_types = std::tuple<TArgs...>;
|
||||
using arg_types_nocvref = std::tuple<std::remove_cvref_t<TArgs>...>;
|
||||
inline static const std::string_view class_name{ typeid(class_type).name() };
|
||||
static constexpr size_t arg_count = sizeof...(TArgs);
|
||||
};
|
||||
|
||||
// const member function
|
||||
template <typename TRet, typename TClass, typename... TArgs>
|
||||
struct function_traits<TRet (TClass::*)(TArgs...) const>
|
||||
: public function_traits<TRet (TClass::*)(TArgs...)>
|
||||
{
|
||||
using class_type = const TClass;
|
||||
};
|
||||
|
||||
// functor / lambda
|
||||
template <typename TCallable>
|
||||
struct function_traits
|
||||
: public function_traits<decltype(&std::remove_reference_t<TCallable>::operator())>
|
||||
{
|
||||
inline static const std::string_view class_name{};
|
||||
};
|
||||
|
||||
// leaves function signature untouched if
|
||||
// __stdcall or __cdecl isn't specified
|
||||
template <typename TFunction>
|
||||
struct remove_callingconv
|
||||
{
|
||||
using type = TFunction;
|
||||
};
|
||||
|
||||
// strips __stdcall or __cdecl out of
|
||||
// lambdas function/lambda type signatures
|
||||
template <typename TRet, typename... TArgs>
|
||||
struct remove_callingconv<TRet(stdcall*)(TArgs...)>
|
||||
{
|
||||
using type = TRet(TArgs...);
|
||||
};
|
||||
|
||||
// strips __stdcall or __cdecl out of
|
||||
// noexcept function/lambda type signatures
|
||||
template <typename TRet, typename... TArgs>
|
||||
struct remove_callingconv<TRet stdcall(TArgs...) noexcept>
|
||||
{
|
||||
using type = TRet(TArgs...) noexcept;
|
||||
};
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#include <godot_cpp/classes/camera2d.hpp>
|
||||
|
||||
#include "entity/camera.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
Camera::Camera()
|
||||
{
|
||||
this->set_name("PlayerCamera");
|
||||
this->set_margin_drawing_enabled(true);
|
||||
auto anchor{ AnchorMode::ANCHOR_MODE_DRAG_CENTER };
|
||||
this->set_anchor_mode(anchor);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/camera2d.hpp>
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class Camera : public godot::Camera2D
|
||||
{
|
||||
GDCLASS(Camera, godot::Camera2D);
|
||||
|
||||
public:
|
||||
Camera();
|
||||
~Camera() = default;
|
||||
|
||||
protected:
|
||||
static void _bind_methods()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <godot_cpp/classes/input.hpp>
|
||||
#include <godot_cpp/classes/marker2d.hpp>
|
||||
#include <godot_cpp/classes/rectangle_shape2d.hpp>
|
||||
#include <godot_cpp/classes/ref.hpp>
|
||||
#include <godot_cpp/classes/resource_loader.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/core/math.hpp>
|
||||
#include <godot_cpp/variant/callable.hpp>
|
||||
#include <godot_cpp/variant/color.hpp>
|
||||
#include <godot_cpp/variant/typed_array.hpp>
|
||||
#include <godot_cpp/variant/vector2.hpp>
|
||||
|
||||
#include "core/assert.hpp"
|
||||
#include "core/concepts.hpp"
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/camera.hpp"
|
||||
#include "entity/character/character.hpp"
|
||||
#include "entity/controller/character_controller.hpp"
|
||||
#include "util/bind.hpp"
|
||||
#include "util/engine.hpp"
|
||||
#include "util/input.hpp"
|
||||
#include "util/io.hpp"
|
||||
#include "util/scene.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
Character::Character()
|
||||
{
|
||||
this->set_motion_mode(MotionMode::MOTION_MODE_FLOATING);
|
||||
}
|
||||
|
||||
void Character::_ready()
|
||||
{
|
||||
this->add_child(m_camera);
|
||||
this->add_child(m_character_controller);
|
||||
|
||||
m_firing_point = gdcast<godot::Marker2D>(
|
||||
this->find_child(name::character::firing_pt, true, false));
|
||||
|
||||
runtime_assert(m_firing_point != nullptr);
|
||||
if (!engine::editor_active())
|
||||
runtime_assert(m_character_controller != nullptr);
|
||||
|
||||
if (m_character_controller != nullptr)
|
||||
{
|
||||
signal<event::character_move>::connect<CharacterController>(m_character_controller) <=>
|
||||
signal_callback(this, on_character_movement);
|
||||
|
||||
signal<event::character_rotate>::connect<CharacterController>(m_character_controller) <=>
|
||||
signal_callback(this, on_character_rotate);
|
||||
|
||||
signal<event::character_shoot>::connect<CharacterController>(m_character_controller) <=>
|
||||
signal_callback(this, on_character_shoot);
|
||||
}
|
||||
}
|
||||
|
||||
void Character::set_controller(CharacterController* controller)
|
||||
{
|
||||
m_character_controller = controller;
|
||||
runtime_assert(m_character_controller != nullptr);
|
||||
}
|
||||
|
||||
CharacterController* Character::get_controller() const
|
||||
{
|
||||
return m_character_controller;
|
||||
}
|
||||
|
||||
[[signal_slot]]
|
||||
void Character::on_character_movement(godot::Vector2 movement_velocity, double delta_time)
|
||||
{
|
||||
double increment = m_movement_friction * delta_time;
|
||||
godot::Vector2 velocity{ this->get_velocity().lerp(movement_velocity, increment) };
|
||||
velocity = velocity.clamp({ -1.0, -1.0 }, { 1.0, 1.0 });
|
||||
this->translate(velocity * this->get_movement_speed() * delta_time);
|
||||
this->set_velocity(velocity);
|
||||
this->move_and_slide();
|
||||
}
|
||||
|
||||
[[signal_slot]]
|
||||
void Character::on_character_rotate(double rotation_angle, double delta_time)
|
||||
{
|
||||
const double smoothed_angle = godot::Math::lerp_angle(
|
||||
static_cast<double>(this->get_rotation()), rotation_angle, m_rotation_speed * delta_time);
|
||||
this->set_rotation(smoothed_angle);
|
||||
}
|
||||
|
||||
[[signal_slot]]
|
||||
void Character::on_character_shoot()
|
||||
{
|
||||
// TODO: fix this
|
||||
this->emit_signal(event::spawn_projectile, m_firing_point);
|
||||
}
|
||||
|
||||
[[property]]
|
||||
double Character::get_movement_speed() const
|
||||
{
|
||||
return m_movement_speed;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
void Character::set_movement_speed(const double move_speed)
|
||||
{
|
||||
m_movement_speed = move_speed;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
double Character::get_movement_friction() const
|
||||
{
|
||||
return m_movement_friction;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
void Character::set_movement_friction(const double move_friction)
|
||||
{
|
||||
m_movement_friction = move_friction;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
double Character::get_rotation_speed() const
|
||||
{
|
||||
return m_rotation_speed;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
void Character::set_rotation_speed(const double rotation_speed)
|
||||
{
|
||||
m_rotation_speed = rotation_speed;
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
|
||||
#include <godot_cpp/classes/character_body2d.hpp>
|
||||
|
||||
#include "core/attributes.hpp"
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/camera.hpp"
|
||||
#include "singletons/console.hpp"
|
||||
#include "util/bind.hpp"
|
||||
|
||||
namespace godot
|
||||
{
|
||||
class Marker2D;
|
||||
class Object;
|
||||
struct Vector2;
|
||||
}
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class CharacterController;
|
||||
}
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class Character : public godot::CharacterBody2D
|
||||
{
|
||||
GDCLASS(Character, godot::CharacterBody2D);
|
||||
|
||||
public:
|
||||
Character();
|
||||
virtual ~Character() = default;
|
||||
|
||||
virtual void _ready() override;
|
||||
|
||||
public:
|
||||
CharacterController* get_controller() const;
|
||||
void set_controller(CharacterController* controller);
|
||||
|
||||
protected:
|
||||
[[property]] double get_movement_speed() const;
|
||||
[[property]] double get_movement_friction() const;
|
||||
[[property]] double get_rotation_speed() const;
|
||||
|
||||
[[property]] void set_movement_speed(const double move_speed);
|
||||
[[property]] void set_movement_friction(const double move_friction);
|
||||
[[property]] void set_rotation_speed(const double rotation_speed);
|
||||
|
||||
[[signal_slot]] void on_character_shoot();
|
||||
[[signal_slot]] void on_character_rotate(double rotation_angle, double delta_time);
|
||||
[[signal_slot]] void on_character_movement(godot::Vector2 movement_velocity,
|
||||
double delta_time);
|
||||
|
||||
protected:
|
||||
static void _bind_methods()
|
||||
{
|
||||
bind_member_function(Character, on_character_movement);
|
||||
bind_member_function(Character, on_character_rotate);
|
||||
bind_member_function(Character, on_character_shoot);
|
||||
|
||||
bind_property(Character, movement_speed, double);
|
||||
bind_property(Character, movement_friction, double);
|
||||
bind_property(Character, rotation_speed, double);
|
||||
|
||||
signal_binding<Character, event::position_changed>::add<godot::Object*, godot::Vector2>();
|
||||
signal_binding<Character, event::spawn_projectile>::add<godot::Object*, godot::Vector2>();
|
||||
}
|
||||
|
||||
protected:
|
||||
// Rate of acceleration/deceleration (unit/s/s)
|
||||
double m_movement_friction{ 5.0 };
|
||||
// Rate of rotational acceleration/deceleration (unit/s/s)
|
||||
double m_rotation_speed{ 10.0 };
|
||||
// target movement speed (units/s)
|
||||
double m_movement_speed{ 500.0 };
|
||||
// target facing angle (radians)
|
||||
double m_rotation_angle{ 0.0 };
|
||||
|
||||
// the player character camera
|
||||
Camera* m_camera{ memnew(Camera) };
|
||||
// handles all input related player controls
|
||||
CharacterController* m_character_controller{ nullptr };
|
||||
// marker identifying location where to spwwn projectiles
|
||||
godot::Marker2D* m_firing_point{ nullptr };
|
||||
};
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/character/character.hpp"
|
||||
#include "entity/character/enemy.hpp"
|
||||
#include "util/scene.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
Enemy::Enemy()
|
||||
: Character()
|
||||
{
|
||||
scene::node::set_unique_name(this, name::character::enemy);
|
||||
}
|
||||
|
||||
void Enemy::_bind_methods()
|
||||
{
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/character/character.hpp"
|
||||
#include "util/bind.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class Enemy : public Character
|
||||
{
|
||||
GDCLASS(Enemy, Character);
|
||||
|
||||
public:
|
||||
Enemy();
|
||||
|
||||
using Character::_ready;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
};
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/character/character.hpp"
|
||||
#include "entity/character/player.hpp"
|
||||
#include "util/scene.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
Player::Player()
|
||||
: Character()
|
||||
{
|
||||
scene::node::set_unique_name(this, name::character::player);
|
||||
}
|
||||
|
||||
void Player::_ready()
|
||||
{
|
||||
Character::_ready();
|
||||
}
|
||||
|
||||
void Player::_bind_methods()
|
||||
{
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/character/character.hpp"
|
||||
#include "util/bind.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class Player : public Character
|
||||
{
|
||||
GDCLASS(Player, Character);
|
||||
|
||||
public:
|
||||
Player();
|
||||
~Player() = default;
|
||||
|
||||
void _ready() override;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
};
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
#include <godot_cpp/classes/input.hpp>
|
||||
#include <godot_cpp/classes/node2d.hpp>
|
||||
#include <godot_cpp/variant/variant.hpp>
|
||||
|
||||
#include "core/assert.hpp"
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/controller/character_controller.hpp"
|
||||
#include "singletons/console.hpp"
|
||||
#include "util/bind.hpp"
|
||||
#include "util/engine.hpp"
|
||||
#include "util/input.hpp"
|
||||
#include "util/io.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
void CharacterController::_process(double delta_time)
|
||||
{
|
||||
if (engine::editor_active())
|
||||
return;
|
||||
|
||||
godot::Input* input_handler{ input::get() };
|
||||
if (input_handler != nullptr)
|
||||
{
|
||||
this->process_movement_input(input_handler, delta_time);
|
||||
this->process_rotation_input(input_handler, delta_time);
|
||||
this->process_action_input(input_handler, delta_time);
|
||||
|
||||
m_elapsed_time += delta_time;
|
||||
if (m_elapsed_time > 1.0)
|
||||
{
|
||||
m_elapsed_time = 0.0;
|
||||
this->emit_signal(event::position_changed, this->get_parent(),
|
||||
this->get_global_position());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::process_action_input(godot::Input* const input, double delta_time)
|
||||
{
|
||||
error_msg("process_action_input() not implemented in derived class");
|
||||
}
|
||||
|
||||
void CharacterController::process_movement_input(godot::Input* const input, double delta_time)
|
||||
{
|
||||
error_msg("process_movement_input() not implemented in derived class");
|
||||
}
|
||||
|
||||
void CharacterController::process_rotation_input(godot::Input* const input, double delta_time)
|
||||
{
|
||||
error_msg("process_rotation_input() not implemented in derived class");
|
||||
}
|
||||
|
||||
void CharacterController::_bind_methods()
|
||||
{
|
||||
signal_binding<CharacterController, event::character_move>::add<godot::Vector2, double>();
|
||||
signal_binding<CharacterController, event::character_rotate>::add<double, double>();
|
||||
signal_binding<CharacterController, event::character_shoot>::add<godot::Object*>();
|
||||
signal_binding<CharacterController, event::position_changed>::add<godot::Object*,
|
||||
godot::Vector2>();
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <godot_cpp/classes/input.hpp>
|
||||
#include <godot_cpp/classes/node2d.hpp>
|
||||
#include <godot_cpp/classes/object.hpp>
|
||||
#include <godot_cpp/variant/vector2.hpp>
|
||||
|
||||
#include "core/concepts.hpp"
|
||||
#include "core/constants.hpp"
|
||||
#include "util/bind.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class CharacterController : public godot::Node2D
|
||||
{
|
||||
GDCLASS(CharacterController, godot::Node2D);
|
||||
|
||||
public:
|
||||
enum class InputMode {
|
||||
MouseAndKeyboard,
|
||||
Controller,
|
||||
AI
|
||||
};
|
||||
|
||||
public:
|
||||
CharacterController() = default;
|
||||
virtual ~CharacterController() = default;
|
||||
|
||||
void _process(double delta_time) override;
|
||||
|
||||
virtual void process_action_input(godot::Input* const input, double delta_time);
|
||||
virtual void process_movement_input(godot::Input* const input, double delta_time);
|
||||
virtual void process_rotation_input(godot::Input* const input, double delta_time);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
protected:
|
||||
// the active input mode for character controls
|
||||
InputMode m_input_mode{ InputMode::MouseAndKeyboard };
|
||||
// target rotation
|
||||
double m_rotation_angle{ 0.0 };
|
||||
// elapsed runtime (seconds)
|
||||
double m_elapsed_time{ 0.0 };
|
||||
};
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#include "entity/controller/enemy_controller.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
void EnemyController::process_action_input(godot::Input* const input, double delta_time)
|
||||
{
|
||||
}
|
||||
|
||||
void EnemyController::process_movement_input(godot::Input* const input, double delta_time)
|
||||
{
|
||||
}
|
||||
|
||||
void EnemyController::process_rotation_input(godot::Input* const input, double delta_time)
|
||||
{
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "entity/controller/character_controller.hpp"
|
||||
|
||||
namespace godot
|
||||
{
|
||||
class Input;
|
||||
}
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class EnemyController : public CharacterController
|
||||
{
|
||||
GDCLASS(EnemyController, CharacterController);
|
||||
|
||||
public:
|
||||
EnemyController() = default;
|
||||
~EnemyController() = default;
|
||||
|
||||
void process_action_input(godot::Input* const input, double delta_time) override;
|
||||
void process_movement_input(godot::Input* const input, double delta_time) override;
|
||||
void process_rotation_input(godot::Input* const input, double delta_time) override;
|
||||
|
||||
protected:
|
||||
static void _bind_methods()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
#include <godot_cpp/classes/input.hpp>
|
||||
#include <godot_cpp/classes/node2d.hpp>
|
||||
#include <godot_cpp/variant/variant.hpp>
|
||||
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/controller/player_controller.hpp"
|
||||
#include "singletons/console.hpp"
|
||||
#include "util/engine.hpp"
|
||||
#include "util/input.hpp"
|
||||
#include "util/io.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
void PlayerController::process_action_input(godot::Input* const input, double delta_time)
|
||||
{
|
||||
if (input->is_action_pressed("shoot"))
|
||||
this->emit_signal(event::character_shoot);
|
||||
}
|
||||
|
||||
void PlayerController::process_movement_input(godot::Input* const input, double delta_time)
|
||||
{
|
||||
auto velocity{ input->get_vector(input::action::move_left, input::action::move_right,
|
||||
input::action::move_up, input::action::move_down) };
|
||||
this->emit_signal(event::character_move, velocity, delta_time);
|
||||
}
|
||||
|
||||
// void PlayerController::_notification(int notification)
|
||||
// {
|
||||
// switch (notification)
|
||||
// {
|
||||
// case NOTIFICATION_PREDELETE:
|
||||
// [[fallthrough]];
|
||||
// case NOTIFICATION_UNPARENTED:
|
||||
// {
|
||||
// console::get()->clear_context();
|
||||
// console::get()->stop_logging();
|
||||
// break;
|
||||
// }
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
// auto console{ console::get() };
|
||||
// console->print("PlayerController: {}", notification);
|
||||
// CharacterController::_notification(notification);
|
||||
// }
|
||||
|
||||
PlayerController::InputMode PlayerController::get_input_mode(godot::Input* const input)
|
||||
{
|
||||
switch (m_input_mode)
|
||||
{
|
||||
default:
|
||||
[[fallthrough]];
|
||||
case InputMode::MouseAndKeyboard:
|
||||
{
|
||||
bool controller_input_detected{ input->is_action_pressed("controller_any") };
|
||||
if (controller_input_detected)
|
||||
m_input_mode = InputMode::Controller;
|
||||
break;
|
||||
}
|
||||
case InputMode::Controller:
|
||||
{
|
||||
godot::Vector2 mouse_velocity{ input->get_last_mouse_velocity() };
|
||||
if (!mouse_velocity.is_zero_approx())
|
||||
m_input_mode = InputMode::MouseAndKeyboard;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m_input_mode;
|
||||
}
|
||||
|
||||
void PlayerController::process_rotation_input(godot::Input* const input, double delta_time)
|
||||
{
|
||||
switch (this->get_input_mode(input))
|
||||
{
|
||||
default:
|
||||
[[fallthrough]];
|
||||
case InputMode::MouseAndKeyboard:
|
||||
{
|
||||
godot::Vector2 rotation_dir{ this->get_global_mouse_position() -
|
||||
this->get_global_position() };
|
||||
|
||||
m_rotation_angle = rotation_dir.angle() + godot::Math::deg_to_rad(90.0);
|
||||
break;
|
||||
}
|
||||
case InputMode::Controller:
|
||||
{
|
||||
godot::TypedArray<int32_t> controllers{ input->get_connected_joypads() };
|
||||
if (controllers.is_empty())
|
||||
log::warning("InputMode = Controller, but no controllers detected");
|
||||
else
|
||||
{
|
||||
godot::Vector2 target_rotation{ input->get_vector("rotate_left", "rotate_right",
|
||||
"rotate_up", "rotate_down") };
|
||||
if (!target_rotation.is_zero_approx())
|
||||
m_rotation_angle = godot::Vector2(0, 0).angle_to_point(target_rotation) +
|
||||
godot::Math::deg_to_rad(90.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->emit_signal(event::character_rotate, m_rotation_angle, delta_time);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "entity/controller/character_controller.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class PlayerController : public CharacterController
|
||||
{
|
||||
GDCLASS(PlayerController, CharacterController);
|
||||
|
||||
public:
|
||||
PlayerController() = default;
|
||||
~PlayerController() = default;
|
||||
|
||||
void process_action_input(godot::Input* const input, double delta_time) override;
|
||||
void process_movement_input(godot::Input* const input, double delta_time) override;
|
||||
void process_rotation_input(godot::Input* const input, double delta_time) override;
|
||||
InputMode get_input_mode(godot::Input* const input);
|
||||
|
||||
protected:
|
||||
static void _bind_methods()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
#include <godot_cpp/classes/collision_polygon2d.hpp>
|
||||
#include <godot_cpp/classes/marker2d.hpp>
|
||||
#include <godot_cpp/classes/rigid_body2d.hpp>
|
||||
#include <godot_cpp/variant/callable.hpp>
|
||||
#include <godot_cpp/variant/vector2.hpp>
|
||||
|
||||
#include "entity/character/character.hpp"
|
||||
#include "entity/controller/player_controller.hpp"
|
||||
#include "entity/level.hpp"
|
||||
#include "singletons/console.hpp"
|
||||
#include "util/bind.hpp"
|
||||
#include "util/conversions.hpp"
|
||||
#include "util/debug.hpp"
|
||||
#include "util/engine.hpp"
|
||||
#include "util/input.hpp"
|
||||
#include "util/io.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
Level::Level()
|
||||
{
|
||||
scene::node::set_unique_name(this, name::level::level1);
|
||||
this->activate(true);
|
||||
}
|
||||
|
||||
void Level::_ready()
|
||||
{
|
||||
godot::Node* box{ this->find_child(name::level::physics_box) };
|
||||
m_physics_box = gdcast<godot::RigidBody2D>(box);
|
||||
|
||||
resource::preload::packed_scene<Player> player_scene{ path::scene::Player };
|
||||
m_player = player_scene.instantiate();
|
||||
m_player->set_controller(memnew(PlayerController));
|
||||
|
||||
this->add_child(m_player);
|
||||
this->add_child(m_projectile_spawner);
|
||||
|
||||
PlayerController* controller{ gdcast<PlayerController>(m_player->get_controller()) };
|
||||
if (controller != nullptr)
|
||||
{
|
||||
signal<event::position_changed>::connect<CharacterController>(controller) <=>
|
||||
signal_callback(this, on_character_position_changed);
|
||||
|
||||
signal<event::spawn_projectile>::connect<Player>(m_player) <=>
|
||||
signal_callback(this, on_player_spawn_projectile);
|
||||
}
|
||||
}
|
||||
|
||||
void Level::_process(double delta_time)
|
||||
{
|
||||
if (engine::editor_active())
|
||||
return;
|
||||
|
||||
if (this->active() && input::cursor_visible()) [[unlikely]]
|
||||
input::hide_cursor();
|
||||
else if (!this->active() && !input::cursor_visible()) [[unlikely]]
|
||||
input::show_cursor();
|
||||
|
||||
this->queue_redraw();
|
||||
}
|
||||
|
||||
void Level::_draw()
|
||||
{
|
||||
if (this->active()) [[likely]]
|
||||
{
|
||||
godot::Point2 mouse_pos{ this->get_global_mouse_position() };
|
||||
this->draw_circle(mouse_pos, 5, { "DARK_CYAN" });
|
||||
}
|
||||
}
|
||||
|
||||
void Level::activate(bool active)
|
||||
{
|
||||
m_active = active;
|
||||
}
|
||||
|
||||
bool Level::active() const
|
||||
{
|
||||
return m_active;
|
||||
}
|
||||
|
||||
[[signal_slot]]
|
||||
void Level::on_physics_box_entered(godot::Node* node) const
|
||||
{
|
||||
console::get()->print("{} > {}", io::yellow("projectile"), to<std::string>(node->get_name()));
|
||||
}
|
||||
|
||||
[[signal_slot]]
|
||||
void Level::on_physics_box_exited(godot::Node* node) const
|
||||
{
|
||||
console::get()->print("{} < {}", io::red("projectile"), to<std::string>(node->get_name()));
|
||||
}
|
||||
|
||||
[[signal_slot]]
|
||||
void Level::on_player_spawn_projectile(godot::Node* obj)
|
||||
{
|
||||
Projectile* projectile{ m_projectile_spawner->spawn_projectile() };
|
||||
if (projectile != nullptr)
|
||||
{
|
||||
godot::Marker2D* firing_pt{ gdcast<godot::Marker2D>(obj) };
|
||||
if (firing_pt != nullptr)
|
||||
{
|
||||
projectile->set_position(firing_pt->get_global_position());
|
||||
projectile->set_rotation(firing_pt->get_global_rotation());
|
||||
|
||||
signal<event::body_entered>::connect<Projectile>(projectile) <=>
|
||||
signal_callback(this, on_physics_box_entered);
|
||||
|
||||
signal<event::body_exited>::connect<Projectile>(projectile) <=>
|
||||
signal_callback(this, on_physics_box_exited);
|
||||
}
|
||||
|
||||
this->add_child(projectile);
|
||||
}
|
||||
}
|
||||
|
||||
[[signal_slot]]
|
||||
void Level::on_character_position_changed(const godot::Object* const node,
|
||||
godot::Vector2 location) const
|
||||
{
|
||||
runtime_assert(node != nullptr);
|
||||
auto console{ console::get() };
|
||||
console->print("{} ({},{})", io::green(to<std::string>(node->get_class()) + " location: "),
|
||||
io::orange(location.x), io::orange(location.y));
|
||||
}
|
||||
|
||||
void Level::_bind_methods()
|
||||
{
|
||||
bind_member_function(Level, on_character_position_changed);
|
||||
bind_member_function(Level, on_player_spawn_projectile);
|
||||
bind_member_function(Level, on_physics_box_entered);
|
||||
bind_member_function(Level, on_physics_box_exited);
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include <godot_cpp/classes/node2d.hpp>
|
||||
#include <godot_cpp/classes/sprite2d.hpp>
|
||||
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/character/player.hpp"
|
||||
#include "entity/controller/player_controller.hpp"
|
||||
#include "entity/projectile/projectile_spawner.hpp"
|
||||
#include "util/bind.hpp"
|
||||
#include "util/scene.hpp"
|
||||
|
||||
namespace godot
|
||||
{
|
||||
class RigidBody2D;
|
||||
}
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class Player;
|
||||
|
||||
class Level : public godot::Node2D
|
||||
{
|
||||
GDCLASS(Level, godot::Node2D);
|
||||
|
||||
public:
|
||||
Level();
|
||||
~Level() = default;
|
||||
|
||||
virtual void _ready() override;
|
||||
void _draw() override;
|
||||
void _process(double delta_time) override;
|
||||
|
||||
void activate(bool active = true);
|
||||
bool active() const;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
[[signal_slot]] void on_physics_box_entered(godot::Node* node) const;
|
||||
[[signal_slot]] void on_physics_box_exited(godot::Node* node) const;
|
||||
[[signal_slot]] void on_player_spawn_projectile(godot::Node* obj);
|
||||
[[signal_slot]] void on_character_position_changed(const godot::Object* const obj,
|
||||
godot::Vector2 location) const;
|
||||
|
||||
private:
|
||||
std::atomic<bool> m_active{ false };
|
||||
godot::Node* m_background{ nullptr };
|
||||
ProjectileSpawner* m_projectile_spawner{ memnew(rl::ProjectileSpawner) };
|
||||
Player* m_player{ nullptr };
|
||||
godot::RigidBody2D* m_physics_box{ nullptr };
|
||||
};
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
#include <godot_cpp/variant/vector2.hpp>
|
||||
|
||||
#include "entity/projectile/projectile.hpp"
|
||||
#include "util/engine.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
void Projectile::_ready()
|
||||
{
|
||||
m_start_pos = this->get_global_position();
|
||||
auto forward{ this->get_transform()[0].normalized() };
|
||||
this->apply_impulse(forward * m_velocity);
|
||||
}
|
||||
|
||||
void Projectile::_process(double delta_time)
|
||||
{
|
||||
if (engine::editor_active())
|
||||
return;
|
||||
|
||||
m_time_to_live -= delta_time;
|
||||
if (m_time_to_live <= 0)
|
||||
{
|
||||
this->queue_free();
|
||||
return;
|
||||
}
|
||||
|
||||
godot::Vector2 curr_pos{ this->get_global_position() };
|
||||
double dist_traveled{ m_start_pos.distance_squared_to(curr_pos) };
|
||||
if (dist_traveled >= m_max_travel_dist)
|
||||
{
|
||||
this->queue_free();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[[property]]
|
||||
double Projectile::get_movement_speed() const
|
||||
{
|
||||
return m_movement_speed;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
double Projectile::get_time_to_live() const
|
||||
{
|
||||
return m_time_to_live;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
double Projectile::get_acceleration() const
|
||||
{
|
||||
return m_acceleration;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
double Projectile::get_max_travel_dist() const
|
||||
{
|
||||
return godot::Math::sqrt(m_max_travel_dist);
|
||||
}
|
||||
|
||||
[[property]]
|
||||
double Projectile::get_velocity() const
|
||||
{
|
||||
return m_velocity;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
void Projectile::set_movement_speed(double speed)
|
||||
{
|
||||
m_movement_speed = speed;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
void Projectile::set_time_to_live(double ttl)
|
||||
{
|
||||
m_time_to_live = ttl;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
void Projectile::set_acceleration(double acceleration)
|
||||
{
|
||||
m_acceleration = acceleration;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
void Projectile::set_max_travel_dist(double dist)
|
||||
{
|
||||
m_max_travel_dist = dist * dist;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
void Projectile::set_velocity(double velocity)
|
||||
{
|
||||
m_velocity = velocity;
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/rigid_body2d.hpp>
|
||||
#include <godot_cpp/variant/vector2.hpp>
|
||||
|
||||
#include "core/attributes.hpp"
|
||||
#include "singletons/console.hpp"
|
||||
#include "util/bind.hpp"
|
||||
#include "util/conversions.hpp"
|
||||
#include "util/io.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
class Projectile : public godot::RigidBody2D
|
||||
{
|
||||
GDCLASS(Projectile, godot::RigidBody2D);
|
||||
|
||||
public:
|
||||
Projectile() = default;
|
||||
virtual ~Projectile() = default;
|
||||
|
||||
void _ready() override;
|
||||
void _process(double delta_time) override;
|
||||
|
||||
[[property]] double get_movement_speed() const;
|
||||
[[property]] double get_time_to_live() const;
|
||||
[[property]] double get_max_travel_dist() const;
|
||||
[[property]] double get_acceleration() const;
|
||||
[[property]] double get_velocity() const;
|
||||
|
||||
[[property]] void set_movement_speed(double speed);
|
||||
[[property]] void set_time_to_live(double ttl);
|
||||
[[property]] void set_max_travel_dist(double dist);
|
||||
[[property]] void set_acceleration(double acceleration);
|
||||
[[property]] void set_velocity(double velocity);
|
||||
|
||||
protected:
|
||||
static void _bind_methods()
|
||||
{
|
||||
bind_member_function(Projectile, get_movement_speed);
|
||||
bind_member_function(Projectile, get_time_to_live);
|
||||
bind_member_function(Projectile, get_max_travel_dist);
|
||||
bind_member_function(Projectile, get_acceleration);
|
||||
bind_member_function(Projectile, get_velocity);
|
||||
bind_member_function(Projectile, set_movement_speed);
|
||||
bind_member_function(Projectile, set_time_to_live);
|
||||
bind_member_function(Projectile, set_max_travel_dist);
|
||||
bind_member_function(Projectile, set_acceleration);
|
||||
bind_member_function(Projectile, set_velocity);
|
||||
}
|
||||
|
||||
protected:
|
||||
godot::Vector2 m_start_pos{ 0.0, 0.0 };
|
||||
// projectile movement velocity (pixels)
|
||||
double m_velocity{ 1500 };
|
||||
// projectile movement speed (pixels/s)
|
||||
double m_movement_speed{ 1000.0 };
|
||||
// projectile acceleration (pixels/s/s)
|
||||
double m_acceleration{ 100.0 };
|
||||
// max time duration alive (seconds)
|
||||
double m_time_to_live{ 2.5 };
|
||||
// max travel distance (pixels) - uint32_t?
|
||||
double m_max_travel_dist{ 1000.0 * 1000.0 };
|
||||
};
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
#include "entity/projectile/projectile_spawner.hpp"
|
||||
#include "util/bind.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
[[nodiscard]]
|
||||
Projectile* ProjectileSpawner::spawn_projectile()
|
||||
{
|
||||
auto elapsed{ clock_t::now() - m_prev_spawn_time };
|
||||
if (elapsed < m_spawn_delay)
|
||||
return nullptr;
|
||||
else
|
||||
{
|
||||
Projectile* projectile{ m_scene.instantiate() };
|
||||
m_prev_spawn_time = clock_t::now();
|
||||
return projectile;
|
||||
}
|
||||
}
|
||||
|
||||
[[property]]
|
||||
double ProjectileSpawner::get_fire_rate() const
|
||||
{
|
||||
return m_fire_rate;
|
||||
}
|
||||
|
||||
[[property]]
|
||||
void ProjectileSpawner::set_fire_rate(double fire_rate)
|
||||
{
|
||||
m_fire_rate = fire_rate;
|
||||
m_spawn_delay = ProjectileSpawner::calculate_spawn_delay(m_fire_rate);
|
||||
}
|
||||
|
||||
ProjectileSpawner::millisec_t ProjectileSpawner::calculate_spawn_delay(double fire_rate)
|
||||
{
|
||||
// converts fire rate (shots per second) to the time delay between shots in ms.
|
||||
// the multiplication by 100 is just to offset the rounding errors by shifting
|
||||
// the decimal place to the right a few places before dividing.
|
||||
return (1000ms * 100) / static_cast<uint64_t>(fire_rate * 100);
|
||||
}
|
||||
|
||||
void ProjectileSpawner::_bind_methods()
|
||||
{
|
||||
bind_property(ProjectileSpawner, fire_rate, double);
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <godot_cpp/classes/node2d.hpp>
|
||||
#include <godot_cpp/variant/typed_array.hpp>
|
||||
|
||||
#include "core/constants.hpp"
|
||||
#include "entity/projectile/projectile.hpp"
|
||||
#include "util/bind.hpp"
|
||||
#include "util/scene.hpp"
|
||||
|
||||
namespace rl
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class ProjectileSpawner : public godot::Node2D
|
||||
{
|
||||
GDCLASS(ProjectileSpawner, godot::Node2D);
|
||||
|
||||
public:
|
||||
ProjectileSpawner() = default;
|
||||
~ProjectileSpawner() = default;
|
||||
|
||||
Projectile* spawn_projectile();
|
||||
|
||||
protected:
|
||||
[[property]] double get_fire_rate() const;
|
||||
[[property]] void set_fire_rate(double fire_rate);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
using clock_t = std::chrono::high_resolution_clock;
|
||||
using millisec_t = std::chrono::milliseconds;
|
||||
static millisec_t calculate_spawn_delay(double fire_rate);
|
||||
|
||||
private:
|
||||
// number of prjectiles per second
|
||||
double m_fire_rate{ 10.0 };
|
||||
// time delay between shots (ms). multiplication by 100 is just to offset rounding errors.
|
||||
millisec_t m_spawn_delay{ ProjectileSpawner::calculate_spawn_delay(m_fire_rate) };
|
||||
// the time point that keeps track of when the last projectile was spawned.
|
||||
clock_t::time_point m_prev_spawn_time{ clock_t::now() };
|
||||
// preloaded packed scene that will be instantiated per spawn
|
||||
resource::preload::packed_scene<Projectile> m_scene{ path::scene::Bullet };
|
||||
};
|
||||
}
|
73
src/main.cpp
73
src/main.cpp
@ -1,59 +1,22 @@
|
||||
#include "core/assert.hpp"
|
||||
#include "main.hpp"
|
||||
#include "util/conversions.hpp"
|
||||
#include "util/engine.hpp"
|
||||
#include "util/input.hpp"
|
||||
|
||||
namespace rl
|
||||
Main::Main()
|
||||
{
|
||||
Main::Main()
|
||||
{
|
||||
resource::preload::packed_scene<Level> level{ path::scene::Level1 };
|
||||
resource::preload::packed_scene<MainDialog> dialog{ path::ui::MainDialog };
|
||||
|
||||
m_active_level = level.instantiate();
|
||||
runtime_assert(m_active_level != nullptr);
|
||||
m_main_dialog = dialog.instantiate();
|
||||
runtime_assert(m_main_dialog != nullptr);
|
||||
|
||||
if (m_main_dialog != nullptr)
|
||||
{
|
||||
m_canvas_layer = gdcast<godot::CanvasLayer>(
|
||||
m_main_dialog->find_child(name::dialog::canvas_layer, true, false));
|
||||
|
||||
runtime_assert(m_canvas_layer != nullptr);
|
||||
if (m_active_level != nullptr && m_canvas_layer != nullptr)
|
||||
m_canvas_layer->add_child(m_active_level);
|
||||
|
||||
if (m_main_dialog != nullptr)
|
||||
this->add_child(m_main_dialog);
|
||||
}
|
||||
}
|
||||
|
||||
void Main::_ready()
|
||||
{
|
||||
this->apply_default_settings();
|
||||
}
|
||||
|
||||
void Main::_physics_process(double delta)
|
||||
{
|
||||
if (engine::editor_active())
|
||||
return;
|
||||
|
||||
m_signal_timer += delta;
|
||||
if (m_signal_timer > 1.0)
|
||||
{
|
||||
this->emit_signal(event::signal_example, delta);
|
||||
m_signal_timer -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void Main::apply_default_settings()
|
||||
{
|
||||
engine::set_fps(60);
|
||||
input::use_accumulated_inputs(false);
|
||||
|
||||
if (not engine::editor_active())
|
||||
engine::root_window()->set_size({ 1920, 1080 });
|
||||
}
|
||||
time_passed = 0.0;
|
||||
}
|
||||
|
||||
Main::~Main()
|
||||
{
|
||||
// Required destructor implementation
|
||||
}
|
||||
|
||||
void Main::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("_process", "delta"), &Main::_process);
|
||||
}
|
||||
|
||||
void Main::_process(double delta)
|
||||
{
|
||||
time_passed += delta;
|
||||
set_rotation(time_passed);
|
||||
}
|
||||
|
42
src/main.hpp
42
src/main.hpp
@ -1,36 +1,20 @@
|
||||
#pragma once
|
||||
#include <godot_cpp/classes/sprite2d.hpp>
|
||||
|
||||
#include <godot_cpp/classes/canvas_layer.hpp>
|
||||
#include <godot_cpp/classes/node.hpp>
|
||||
using namespace godot;
|
||||
|
||||
#include "entity/level.hpp"
|
||||
#include "ui/main_dialog.hpp"
|
||||
|
||||
namespace rl
|
||||
class Main : public Sprite2D
|
||||
{
|
||||
class Main : public godot::Node
|
||||
{
|
||||
GDCLASS(Main, godot::Node);
|
||||
GDCLASS(Main, Sprite2D) // Must be in public section
|
||||
|
||||
public:
|
||||
Main();
|
||||
~Main() = default;
|
||||
private:
|
||||
double time_passed;
|
||||
|
||||
void _ready() override;
|
||||
void _physics_process(double delta) override;
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
protected:
|
||||
void apply_default_settings();
|
||||
public:
|
||||
Main();
|
||||
virtual ~Main() override; // Required for proper destruction
|
||||
|
||||
static void _bind_methods()
|
||||
{
|
||||
signal_binding<Main, event::signal_example>::add<double>();
|
||||
}
|
||||
|
||||
private:
|
||||
double m_signal_timer{ 0.0 };
|
||||
godot::CanvasLayer* m_canvas_layer{ nullptr };
|
||||
MainDialog* m_main_dialog{ nullptr };
|
||||
Level* m_active_level{ nullptr };
|
||||
};
|
||||
}
|
||||
void _process(double delta) override;
|
||||
};
|
||||
|
@ -1,117 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/compile.h>
|
||||
#include <fmt/core.h>
|
||||
#include <spdlog/sinks/callback_sink.h>
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <godot_cpp/classes/object.hpp>
|
||||
#include <godot_cpp/classes/rich_text_label.hpp>
|
||||
|
||||
namespace rl
|
||||
{
|
||||
template <typename TContext>
|
||||
class Console : public godot::Object
|
||||
{
|
||||
GDCLASS(Console, godot::Object);
|
||||
|
||||
public:
|
||||
Console()
|
||||
{
|
||||
m_static_inst = this;
|
||||
init_loggers();
|
||||
}
|
||||
|
||||
~Console()
|
||||
{
|
||||
m_static_inst = nullptr;
|
||||
}
|
||||
|
||||
static inline rl::Console<TContext>* get()
|
||||
{
|
||||
return m_static_inst;
|
||||
}
|
||||
|
||||
void set_context(TContext* context)
|
||||
{
|
||||
m_gui_console = context;
|
||||
}
|
||||
|
||||
void clear_context()
|
||||
{
|
||||
m_gui_console = nullptr;
|
||||
}
|
||||
|
||||
void stop_logging()
|
||||
{
|
||||
m_logger->flush();
|
||||
m_stop = true;
|
||||
}
|
||||
|
||||
void init_loggers()
|
||||
requires std::same_as<TContext, godot::RichTextLabel>
|
||||
{
|
||||
auto stdout_sink{ std::make_shared<spdlog::sinks::stdout_color_sink_mt>() };
|
||||
auto stderr_sink{ std::make_shared<spdlog::sinks::stderr_color_sink_mt>() };
|
||||
auto callbk_sink{ std::make_shared<spdlog::sinks::callback_sink_mt>(
|
||||
[this](const spdlog::details::log_msg& msg) {
|
||||
if (!m_stop.load(std::memory_order_relaxed))
|
||||
{
|
||||
if (m_gui_console == nullptr)
|
||||
return;
|
||||
|
||||
using duration_t = std::chrono::duration<double>;
|
||||
const duration_t elapsed{ clock_t::now() - m_start_time };
|
||||
|
||||
m_gui_console->append_text(
|
||||
fmt::format("[color=gray]{:5} [{:>7.2}] [b]=>[/b] {}[/color]\n",
|
||||
m_line_num.fetch_add(1, std::memory_order_relaxed), elapsed,
|
||||
msg.payload)
|
||||
.c_str());
|
||||
}
|
||||
}) };
|
||||
|
||||
stderr_sink->set_level(spdlog::level::err);
|
||||
stdout_sink->set_level(spdlog::level::info);
|
||||
callbk_sink->set_level(spdlog::level::debug);
|
||||
|
||||
m_logger = std::unique_ptr<spdlog::logger>(
|
||||
new spdlog::logger{ "custom_callback_logger",
|
||||
{ stdout_sink, stderr_sink, callbk_sink } });
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
spdlog::flush_every(0.25s);
|
||||
}
|
||||
|
||||
template <typename... TArgs>
|
||||
void print(fmt::format_string<TArgs...> format_str, TArgs&&... args)
|
||||
{
|
||||
m_logger->info(format_str, std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
protected:
|
||||
static void _bind_methods()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<spdlog::logger> m_logger{ nullptr };
|
||||
std::atomic<bool> m_stop{ false };
|
||||
std::atomic<uint32_t> m_line_num{ 0 };
|
||||
TContext* m_gui_console{ nullptr };
|
||||
|
||||
using clock_t = std::chrono::high_resolution_clock;
|
||||
const clock_t::time_point m_start_time{ clock_t::now() };
|
||||
|
||||
private:
|
||||
static inline Console<TContext>* m_static_inst{ nullptr };
|
||||
};
|
||||
|
||||
using console = Console<godot::RichTextLabel>;
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
#include <godot_cpp/classes/canvas_layer.hpp>
|
||||
#include <godot_cpp/classes/control.hpp>
|
||||
|
||||
#include "core/assert.hpp"
|
||||
#include "core/constants.hpp"
|
||||
#include "singletons/console.hpp"
|
||||
#include "ui/main_dialog.hpp"
|
||||
#include "util/conversions.hpp"
|
||||
#include "util/engine.hpp"
|
||||
|
||||
namespace rl::inline ui
|
||||
{
|
||||
void MainDialog::_ready()
|
||||
{
|
||||
if (engine::editor_active())
|
||||
return;
|
||||
|
||||
Console<godot::RichTextLabel>* game_console{ console::get() };
|
||||
|
||||
godot::Node* root{ scene::tree::root_node(this) };
|
||||
godot::Node* label{ root->find_child(name::dialog::console, true, false) };
|
||||
godot::Node* level{ this->find_child(name::level::level1, true, false) };
|
||||
|
||||
m_level = gdcast<Level>(level);
|
||||
m_console_label = gdcast<godot::RichTextLabel>(label);
|
||||
|
||||
game_console->set_context(m_console_label);
|
||||
}
|
||||
|
||||
void MainDialog::_notification(int notification)
|
||||
{
|
||||
switch (notification)
|
||||
{
|
||||
case Object::NOTIFICATION_PREDELETE:
|
||||
[[fallthrough]];
|
||||
case Node::NOTIFICATION_UNPARENTED:
|
||||
{
|
||||
console::get()->clear_context();
|
||||
console::get()->stop_logging();
|
||||
break;
|
||||
}
|
||||
case Control::NOTIFICATION_MOUSE_ENTER:
|
||||
{
|
||||
m_level->activate(true);
|
||||
break;
|
||||
}
|
||||
case Control::NOTIFICATION_MOUSE_EXIT:
|
||||
{
|
||||
m_level->activate(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto console{ console::get() };
|
||||
console->print("notification: {}", notification);
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/panel.hpp>
|
||||
#include <godot_cpp/classes/rich_text_label.hpp>
|
||||
|
||||
#include "entity/level.hpp"
|
||||
|
||||
namespace rl::inline ui
|
||||
{
|
||||
class MainDialog : public godot::Panel
|
||||
{
|
||||
GDCLASS(MainDialog, godot::Panel);
|
||||
|
||||
public:
|
||||
MainDialog() = default;
|
||||
~MainDialog() = default;
|
||||
|
||||
void _ready() override;
|
||||
void _notification(int notification);
|
||||
|
||||
static void _bind_methods()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
Level* m_level{ nullptr };
|
||||
godot::RichTextLabel* m_console_label{ nullptr };
|
||||
};
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <godot_cpp/classes/object.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/variant/callable.hpp>
|
||||
#include <godot_cpp/variant/string.hpp>
|
||||
#include <godot_cpp/variant/string_name.hpp>
|
||||
#include <godot_cpp/variant/vector2.hpp>
|
||||
|
||||
#include "core/assert.hpp"
|
||||
#include "core/concepts.hpp"
|
||||
#include "core/function_traits.hpp"
|
||||
#include "util/conversions.hpp"
|
||||
#include "util/variant.hpp"
|
||||
|
||||
#define bind_member_function(class_name, func_name) method<&class_name::func_name>::bind(#func_name)
|
||||
|
||||
#define signal_callback(slot_owner, slot_callback) \
|
||||
std::forward_as_tuple(godot::Callable(slot_owner, #slot_callback), slot_owner)
|
||||
|
||||
#define bind_property(class_name, prop_name, prop_type) \
|
||||
node_property<class_name, prop_type, &class_name::get_##prop_name, \
|
||||
&class_name::set_##prop_name>::add(#prop_name)
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
template <auto Method>
|
||||
requires std::is_member_function_pointer_v<decltype(Method)>
|
||||
struct method : public function_traits<decltype(Method)>
|
||||
{
|
||||
using traits_t = function_traits<decltype(Method)>;
|
||||
|
||||
static constexpr void bind(std::string_view&& func_name)
|
||||
{
|
||||
constexpr std::size_t tup_size = std::tuple_size_v<typename traits_t::arg_types>;
|
||||
if constexpr (tup_size == 0)
|
||||
godot::ClassDB::bind_method(godot::D_METHOD(func_name.data()), Method);
|
||||
else
|
||||
{
|
||||
const typename traits_t::arg_types_nocvref func_args{};
|
||||
std::apply(
|
||||
[&](auto&&... args) {
|
||||
godot::ClassDB::bind_method(godot::D_METHOD(func_name.data()), Method,
|
||||
args...);
|
||||
},
|
||||
func_args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <GDObjectDerived TNode, VariantCompatible TProperty, auto GetterMethod, auto SetterMethod>
|
||||
struct node_property
|
||||
{
|
||||
using getter_traits = function_traits<decltype(GetterMethod)>;
|
||||
using setter_traits = function_traits<decltype(SetterMethod)>;
|
||||
|
||||
static constexpr void add(std::string&& prop_name)
|
||||
{
|
||||
const std::string getter_name{ "get_" + prop_name };
|
||||
const std::string setter_name{ "set_" + prop_name };
|
||||
|
||||
method<GetterMethod>::bind(getter_name);
|
||||
method<SetterMethod>::bind(setter_name);
|
||||
|
||||
const godot::PropertyInfo property_info(
|
||||
variant_traits<TProperty>::type_info::get_class_info().type,
|
||||
godot::String(prop_name.c_str()));
|
||||
|
||||
godot::ClassDB::add_property(TNode::get_class_static(), property_info,
|
||||
godot::String(setter_name.c_str()),
|
||||
godot::String(getter_name.c_str()));
|
||||
}
|
||||
};
|
||||
|
||||
template <auto Function>
|
||||
struct callback_func : public function_traits<decltype(Function)>
|
||||
{
|
||||
using traits_t = function_traits<decltype(Function)>;
|
||||
|
||||
static void bind(std::string_view&& func_name)
|
||||
{
|
||||
auto class_name = godot::StringName("Main");
|
||||
static constexpr std::size_t tup_size = std::tuple_size_v<typename traits_t::arg_types>;
|
||||
if constexpr (tup_size == 0)
|
||||
godot::ClassDB::bind_static_method(class_name, godot::D_METHOD(func_name.data()),
|
||||
Function);
|
||||
else
|
||||
{
|
||||
const typename traits_t::arg_types_nocvref func_args{};
|
||||
std::vector<godot::String> vec_strs = rl::detail::to_arg_vec(func_args);
|
||||
std::tuple arg_types_str{ detail::arg_vec_to_tuple<tup_size>(vec_strs) };
|
||||
|
||||
std::apply(
|
||||
[&](auto&&... args) {
|
||||
godot::ClassDB::bind_static_method(
|
||||
class_name, godot::D_METHOD(func_name.data()), Function, args...);
|
||||
},
|
||||
func_args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <GDObjectDerived TObject, auto& SignalName>
|
||||
class signal_binding
|
||||
{
|
||||
public:
|
||||
using object_t = std::type_identity_t<TObject>;
|
||||
using signal_t = std::type_identity_t<signal_binding<object_t, SignalName>>;
|
||||
static inline constexpr std::string_view signal_name{ SignalName };
|
||||
static inline std::vector<godot::PropertyInfo> signal_params{};
|
||||
|
||||
// even though we know what TObject is here (the class type adding the signal binding)
|
||||
// we can't call TObject::get_class_static() yet since this struct is instantiated before
|
||||
// the bindings library initializes the gdextension library, which will just lead to a
|
||||
// crash. this will just be set with the class name at runtime when signal_binding::add() is
|
||||
// invoked.
|
||||
static inline std::string class_name{};
|
||||
|
||||
public:
|
||||
template <typename... TArgs>
|
||||
struct add
|
||||
{
|
||||
using arg_types = std::tuple<std::type_identity_t<TArgs>...>;
|
||||
static constexpr inline size_t arg_count{ std::tuple_size_v<arg_types> };
|
||||
|
||||
add()
|
||||
{
|
||||
if (class_name.empty())
|
||||
class_name = rl::to<std::string>(object_t::get_class_static());
|
||||
else
|
||||
{
|
||||
std::string temp_name = rl::to<std::string>(object_t::get_class_static());
|
||||
runtime_assert(class_name == temp_name);
|
||||
}
|
||||
|
||||
if constexpr (arg_count == 0)
|
||||
godot::ClassDB::add_signal(class_name.data(),
|
||||
godot::MethodInfo(signal_name.data()));
|
||||
else
|
||||
{
|
||||
arg_types signal_args{};
|
||||
|
||||
std::apply(
|
||||
[&](auto&&... arg) {
|
||||
signal_params = {
|
||||
variant_traits<decltype(arg)>::type_info::get_class_info()...
|
||||
};
|
||||
},
|
||||
signal_args);
|
||||
|
||||
godot::ClassDB::add_signal(
|
||||
class_name.data(),
|
||||
godot::MethodInfo(signal_name.data(),
|
||||
std::forward<decltype(signal_params)>(signal_params)));
|
||||
}
|
||||
|
||||
runtime_assert(signal_params.size() == arg_count);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <auto&& SignalName>
|
||||
struct signal
|
||||
{
|
||||
public:
|
||||
static inline constexpr std::string_view signal_name{ SignalName };
|
||||
|
||||
public:
|
||||
template <GDObjectDerived TOwnerObj>
|
||||
struct connect
|
||||
{
|
||||
private:
|
||||
static inline TOwnerObj* m_signal_owner{ nullptr };
|
||||
|
||||
public:
|
||||
explicit connect(TOwnerObj* signal_owner)
|
||||
{
|
||||
static_assert(std::is_same_v<decltype(signal_owner), decltype(m_signal_owner)>);
|
||||
|
||||
m_signal_owner = signal_owner;
|
||||
const std::string class_name = rl::to<std::string>(m_signal_owner->get_class());
|
||||
runtime_assert(m_signal_owner->has_signal(signal_name.data()));
|
||||
}
|
||||
|
||||
template <typename TNode>
|
||||
auto operator<=>(std::tuple<godot::Callable&&, TNode&&> callback)
|
||||
{
|
||||
auto&& cb{ std::forward<godot::Callable>(std::get<0>(callback)) };
|
||||
auto&& callback_owner{ std::get<1>(callback) };
|
||||
|
||||
if (m_signal_owner == nullptr)
|
||||
{
|
||||
error_msg("Attempting to connect a signal to a null object");
|
||||
return godot::Error::ERR_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
if (!callback_owner->has_method(cb.get_method()))
|
||||
{
|
||||
error_msg("Signal connection method missing bindings");
|
||||
return godot::Error::ERR_METHOD_NOT_FOUND;
|
||||
}
|
||||
|
||||
// TODO: compare and validate the arg count, raw types, and variant conversions
|
||||
// between the matching signal binding record and the callback being connected.
|
||||
return m_signal_owner->connect(signal_name.data(), cb);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include <godot_cpp/core/object.hpp>
|
||||
#include <godot_cpp/variant/char_string.hpp>
|
||||
#include <godot_cpp/variant/string.hpp>
|
||||
#include <godot_cpp/variant/string_name.hpp>
|
||||
|
||||
#include "core/assert.hpp"
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
/** Converts std string types to godot::String at compile time */
|
||||
template <typename TStr>
|
||||
struct gd_str_conv
|
||||
{
|
||||
explicit constexpr gd_str_conv(TStr&& s)
|
||||
: m_str{ std::forward<TStr>(s) }
|
||||
{
|
||||
}
|
||||
|
||||
explicit operator godot::String()
|
||||
requires std::same_as<TStr, std::string>
|
||||
{
|
||||
return godot::String(m_str.c_str());
|
||||
}
|
||||
|
||||
explicit operator godot::String()
|
||||
requires std::same_as<TStr, std::string_view>
|
||||
{
|
||||
return godot::String(m_str.data());
|
||||
}
|
||||
|
||||
TStr m_str{};
|
||||
};
|
||||
|
||||
template <typename TOut, typename TIn>
|
||||
requires std::derived_from<std::remove_cvref_t<TIn>, godot::Object>
|
||||
constexpr inline TOut* gdcast(TIn* obj)
|
||||
{
|
||||
runtime_assert(obj != nullptr);
|
||||
auto ret{ godot::Object::cast_to<TOut>(obj) };
|
||||
runtime_assert(ret != nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename TOut>
|
||||
inline auto to(const auto& in) -> TOut
|
||||
{
|
||||
return TOut(in);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline auto to(const godot::String& in) -> std::string
|
||||
{
|
||||
static_assert(std::is_same_v<godot::String, std::remove_cvref_t<decltype(in)>>);
|
||||
return std::string(in.ascii().ptr());
|
||||
}
|
||||
|
||||
template <>
|
||||
inline auto to(const godot::StringName& in) -> std::string
|
||||
{
|
||||
static_assert(std::is_same_v<godot::StringName, std::remove_cvref_t<decltype(in)>>);
|
||||
godot::String tmp(in);
|
||||
return std::string(tmp.utf8().ptr());
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <godot_cpp/core/error_macros.hpp>
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
namespace diag
|
||||
{
|
||||
enum Option : uint_fast8_t {
|
||||
RootProcess,
|
||||
RootPhysics,
|
||||
RootViewport,
|
||||
RootInputs,
|
||||
LevelProcess,
|
||||
LevelLoad,
|
||||
LevelUnload,
|
||||
MainMenuInit,
|
||||
All
|
||||
};
|
||||
|
||||
using optval_t = std::pair<Option, bool>;
|
||||
static constexpr inline auto DebugSettings{
|
||||
std::to_array<std::type_identity_t<optval_t>>({
|
||||
{ Option::All, false },
|
||||
{ Option::RootProcess, true },
|
||||
{ Option::RootPhysics, true },
|
||||
{ Option::RootViewport, true },
|
||||
{ Option::RootInputs, true },
|
||||
{ Option::LevelProcess, true },
|
||||
{ Option::LevelLoad, true },
|
||||
{ Option::LevelUnload, true },
|
||||
{ Option::MainMenuInit, true },
|
||||
}),
|
||||
};
|
||||
|
||||
static constexpr inline bool is_enabled(const Option& diag_type)
|
||||
{
|
||||
for (auto&& [opt, val] : DebugSettings)
|
||||
{
|
||||
if (opt == Option::All && val)
|
||||
return true;
|
||||
if (opt == diag_type)
|
||||
return val;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
#include <godot_cpp/classes/engine.hpp>
|
||||
#include <godot_cpp/classes/main_loop.hpp>
|
||||
#include <godot_cpp/classes/node.hpp>
|
||||
#include <godot_cpp/classes/scene_tree.hpp>
|
||||
#include <godot_cpp/classes/window.hpp>
|
||||
|
||||
#include "util/engine.hpp"
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
godot::Engine* engine::get()
|
||||
{
|
||||
godot::Engine* engine{ godot::Engine::get_singleton() };
|
||||
return engine;
|
||||
}
|
||||
|
||||
godot::MainLoop* engine::main_loop()
|
||||
{
|
||||
auto engine{ engine::get() };
|
||||
return engine->get_main_loop();
|
||||
}
|
||||
|
||||
godot::SceneTree* engine::scene_tree()
|
||||
{
|
||||
auto loop{ engine::main_loop() };
|
||||
return ::godot::Object::cast_to<godot::SceneTree>(loop);
|
||||
}
|
||||
|
||||
godot::Window* engine::root_window()
|
||||
{
|
||||
auto tree{ engine::scene_tree() };
|
||||
return tree->get_root();
|
||||
}
|
||||
|
||||
godot::Node* engine::root_node()
|
||||
{
|
||||
return godot::Object::cast_to<godot::Node>(engine::root_window());
|
||||
}
|
||||
|
||||
void engine::set_fps(const uint32_t fps)
|
||||
{
|
||||
auto engine{ engine::get() };
|
||||
return engine->set_max_fps(fps);
|
||||
}
|
||||
|
||||
int32_t engine::max_fps()
|
||||
{
|
||||
auto engine{ engine::get() };
|
||||
return engine->get_max_fps();
|
||||
}
|
||||
|
||||
bool engine::editor_active()
|
||||
{
|
||||
auto engine{ engine::get() };
|
||||
return engine->is_editor_hint();
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace godot
|
||||
{
|
||||
class Engine;
|
||||
class MainLoop;
|
||||
class Node;
|
||||
class SceneTree;
|
||||
class Window;
|
||||
}
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
struct engine
|
||||
{
|
||||
static godot::Engine* get();
|
||||
static godot::MainLoop* main_loop();
|
||||
static godot::SceneTree* scene_tree();
|
||||
static godot::Window* root_window();
|
||||
static godot::Node* root_node();
|
||||
static void set_fps(const uint32_t fps);
|
||||
static int32_t max_fps();
|
||||
static bool editor_active();
|
||||
};
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#include <godot_cpp/classes/input.hpp>
|
||||
#include <godot_cpp/classes/input_map.hpp>
|
||||
#include <godot_cpp/core/method_ptrcall.hpp>
|
||||
#include <godot_cpp/core/type_info.hpp>
|
||||
|
||||
#include "util/input.hpp"
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
godot::InputMap* input::map::get()
|
||||
{
|
||||
godot::InputMap* mappings{ godot::InputMap::get_singleton() };
|
||||
return mappings;
|
||||
}
|
||||
|
||||
godot::Input* input::get()
|
||||
{
|
||||
godot::Input* input{ godot::Input::get_singleton() };
|
||||
return input;
|
||||
}
|
||||
|
||||
void input::hide_cursor()
|
||||
{
|
||||
godot::Input* const input{ input::get() };
|
||||
input->set_mouse_mode(godot::Input::MOUSE_MODE_HIDDEN);
|
||||
}
|
||||
|
||||
void input::show_cursor()
|
||||
{
|
||||
godot::Input* const input{ input::get() };
|
||||
input->set_mouse_mode(godot::Input::MOUSE_MODE_VISIBLE);
|
||||
}
|
||||
|
||||
bool input::cursor_visible()
|
||||
{
|
||||
godot::Input* const input{ input::get() };
|
||||
return input->get_mouse_mode() == godot::Input::MOUSE_MODE_VISIBLE;
|
||||
}
|
||||
|
||||
void input::load_project_inputs()
|
||||
{
|
||||
auto input_map{ godot::InputMap::get_singleton() };
|
||||
return input_map->load_from_project_settings();
|
||||
}
|
||||
|
||||
void input::use_accumulated_inputs(bool enable)
|
||||
{
|
||||
auto input{ godot::Input::get_singleton() };
|
||||
return input->set_use_accumulated_input(enable);
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace godot
|
||||
{
|
||||
class InputMap;
|
||||
class Input;
|
||||
}
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
namespace input
|
||||
{
|
||||
namespace action
|
||||
{
|
||||
constexpr inline auto move_left{ "move_left" };
|
||||
constexpr inline auto move_right{ "move_right" };
|
||||
constexpr inline auto move_up{ "move_up" };
|
||||
constexpr inline auto move_down{ "move_down" };
|
||||
constexpr inline auto shoot{ "shoot" };
|
||||
}
|
||||
|
||||
struct map
|
||||
{
|
||||
static godot::InputMap* get();
|
||||
};
|
||||
|
||||
godot::Input* get();
|
||||
|
||||
void hide_cursor();
|
||||
void show_cursor();
|
||||
bool cursor_visible();
|
||||
void load_project_inputs();
|
||||
void use_accumulated_inputs(bool enable);
|
||||
};
|
||||
}
|
134
src/util/io.hpp
134
src/util/io.hpp
@ -1,134 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/compile.h>
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format-inl.h>
|
||||
#include <fmt/format.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <godot_cpp/core/error_macros.hpp>
|
||||
|
||||
#include "core/concepts.hpp"
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
static inline auto white(auto&& val)
|
||||
{
|
||||
return fmt::format(FMT_COMPILE("[color=white]{}[/color]"),
|
||||
std::forward<decltype(val)>(val));
|
||||
}
|
||||
|
||||
static inline auto gray(auto&& val)
|
||||
{
|
||||
return fmt::format(FMT_COMPILE("[color=gray]{}[/color]"),
|
||||
std::forward<decltype(val)>(val));
|
||||
}
|
||||
|
||||
static inline auto black(auto&& val)
|
||||
{
|
||||
return fmt::format(FMT_COMPILE("[color=black]{}[/color]"),
|
||||
std::forward<decltype(val)>(val));
|
||||
}
|
||||
|
||||
static inline auto red(auto&& val)
|
||||
{
|
||||
return fmt::format(FMT_COMPILE("[color=red]{}[/color]"),
|
||||
std::forward<decltype(val)>(val));
|
||||
}
|
||||
|
||||
static inline auto orange(auto&& val)
|
||||
{
|
||||
return fmt::format(FMT_COMPILE("[color=orange]{}[/color]"),
|
||||
std::forward<decltype(val)>(val));
|
||||
}
|
||||
|
||||
static inline auto yellow(auto&& val)
|
||||
{
|
||||
return fmt::format(FMT_COMPILE("[color=yellow]{}[/color]"),
|
||||
std::forward<decltype(val)>(val));
|
||||
}
|
||||
|
||||
static inline auto green(auto&& val)
|
||||
{
|
||||
return fmt::format(FMT_COMPILE("[color=green]{}[/color]"),
|
||||
std::forward<decltype(val)>(val));
|
||||
}
|
||||
|
||||
static inline auto blue(auto&& val)
|
||||
{
|
||||
return fmt::format(FMT_COMPILE("[color=blue]{}[/color]"),
|
||||
std::forward<decltype(val)>(val));
|
||||
}
|
||||
|
||||
static inline auto purple(auto&& val)
|
||||
{
|
||||
return fmt::format(FMT_COMPILE("[color=purple]{}[/color]"),
|
||||
std::forward<decltype(val)>(val));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct log
|
||||
{
|
||||
enum DetailLevel : uint_fast8_t {
|
||||
None,
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
Debug,
|
||||
Trace
|
||||
};
|
||||
|
||||
static constexpr const DetailLevel DETAIL_LEVEL{ log::DetailLevel::Debug };
|
||||
|
||||
static constexpr inline bool level_active(const log::DetailLevel level)
|
||||
{
|
||||
return level <= log::DETAIL_LEVEL;
|
||||
}
|
||||
|
||||
template <typename TString>
|
||||
static inline void error(TString msg)
|
||||
{
|
||||
if (log::level_active(log::DetailLevel::Error))
|
||||
ERR_PRINT_ED(std::forward<TString>(msg));
|
||||
}
|
||||
|
||||
template <typename TString>
|
||||
static inline void warning(TString msg)
|
||||
{
|
||||
if (log::level_active(log::DetailLevel::Warning))
|
||||
WARN_PRINT_ED(std::forward<TString>(msg));
|
||||
}
|
||||
|
||||
template <typename TString>
|
||||
static inline void info(TString msg)
|
||||
{
|
||||
if (log::level_active(log::DetailLevel::Info))
|
||||
WARN_PRINT_ED(std::forward<TString>(msg));
|
||||
}
|
||||
|
||||
template <typename TString>
|
||||
static inline void trace(TString msg)
|
||||
{
|
||||
if (log::level_active(log::DetailLevel::Trace))
|
||||
WARN_PRINT_ED(std::forward<TString>(msg));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static inline void print(spdlog::format_string_t<Args...> fmt, Args&&... args)
|
||||
{
|
||||
std::string msg{ fmt::format(fmt, std::forward<Args>(args)...) };
|
||||
ERR_PRINT_ED(msg.data());
|
||||
}
|
||||
};
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/os.hpp>
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
struct os
|
||||
{
|
||||
static inline godot::OS* get()
|
||||
{
|
||||
return godot::OS::get_singleton();
|
||||
}
|
||||
};
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
|
||||
#include <godot_cpp/classes/packed_scene.hpp>
|
||||
#include <godot_cpp/classes/resource.hpp>
|
||||
#include <godot_cpp/classes/resource_loader.hpp>
|
||||
#include <godot_cpp/classes/resource_saver.hpp>
|
||||
#include <godot_cpp/classes/scene_tree.hpp>
|
||||
#include <godot_cpp/classes/window.hpp>
|
||||
|
||||
#include "core/assert.hpp"
|
||||
#include "util/conversions.hpp"
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
namespace node
|
||||
{
|
||||
template <typename TNode>
|
||||
requires std::derived_from<TNode, godot::Node>
|
||||
static inline void set_unique_name(TNode* node, const char* name)
|
||||
{
|
||||
runtime_assert(node != nullptr);
|
||||
node->set_name(name);
|
||||
node->set_unique_name_in_owner(true);
|
||||
}
|
||||
|
||||
/** Sets the owner of a node and all it's children. */
|
||||
template <typename TNodeA, typename TNodeB>
|
||||
requires std::derived_from<TNodeB, godot::Node> &&
|
||||
std::derived_from<TNodeA, godot::Node>
|
||||
static inline void set_owner(TNodeA* node, TNodeB* owner)
|
||||
{
|
||||
runtime_assert(node != nullptr && owner != nullptr);
|
||||
const int node_child_count = node->get_child_count();
|
||||
for (int i = 0; i < node_child_count; ++i)
|
||||
{
|
||||
auto child = node->get_child(i);
|
||||
child->set_owner(owner);
|
||||
set_owner(child, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace tree
|
||||
{
|
||||
template <typename TNode>
|
||||
requires std::derived_from<TNode, godot::Node>
|
||||
static inline godot::SceneTree* get(TNode* node)
|
||||
{
|
||||
godot::SceneTree* scene_tree{ node->get_tree() };
|
||||
return scene_tree;
|
||||
}
|
||||
|
||||
template <typename TNode>
|
||||
requires std::derived_from<TNode, godot::Node>
|
||||
static inline godot::Node* edited_root(TNode* node)
|
||||
{
|
||||
godot::Node* edited_root{ node->get_tree()->get_edited_scene_root() };
|
||||
return edited_root;
|
||||
}
|
||||
|
||||
template <typename TNode>
|
||||
requires std::derived_from<TNode, godot::Node>
|
||||
static inline godot::Node* root_node(TNode* node)
|
||||
{
|
||||
godot::SceneTree* scene_tree{ tree::get(node) };
|
||||
godot::Window* root_window{ scene_tree->get_root() };
|
||||
godot::Node* root_node{ gdcast<godot::Node>(root_window) };
|
||||
return root_node;
|
||||
}
|
||||
}
|
||||
|
||||
namespace packer
|
||||
{
|
||||
/**
|
||||
@return PackedScene from godot::Node parameter.
|
||||
*/
|
||||
template <typename TNode>
|
||||
requires std::derived_from<TNode, godot::Node>
|
||||
static inline godot::PackedScene* pack(TNode* node)
|
||||
{
|
||||
node::set_owner<TNode, TNode>(node, node);
|
||||
godot::PackedScene* package = memnew(godot::PackedScene);
|
||||
package->pack(node);
|
||||
return package;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace resource
|
||||
{
|
||||
namespace loader
|
||||
{
|
||||
static inline godot::ResourceLoader* get()
|
||||
{
|
||||
return godot::ResourceLoader::get_singleton();
|
||||
}
|
||||
}
|
||||
|
||||
namespace saver
|
||||
{
|
||||
static inline godot::ResourceSaver* get()
|
||||
{
|
||||
return godot::ResourceSaver::get_singleton();
|
||||
}
|
||||
}
|
||||
|
||||
namespace preload
|
||||
{
|
||||
template <typename TObj, typename TScene = godot::PackedScene>
|
||||
requires std::derived_from<TScene, godot::Resource> &&
|
||||
std::convertible_to<TObj, godot::Object>
|
||||
class packed_scene
|
||||
{
|
||||
public:
|
||||
using scene_t = TScene;
|
||||
using object_t = TObj;
|
||||
|
||||
/** Load and pack from path. */
|
||||
packed_scene(const godot::String& load_resource_path,
|
||||
const godot::String& load_type_hint = godot::String(),
|
||||
godot::ResourceLoader::CacheMode load_cache_mode =
|
||||
godot::ResourceLoader::CacheMode::CACHE_MODE_REUSE)
|
||||
{
|
||||
godot::ResourceLoader* resource_loader{ loader::get() };
|
||||
|
||||
bool resource_exists{ resource_loader->exists(load_resource_path) };
|
||||
runtime_assert(resource_exists);
|
||||
|
||||
if (resource_exists)
|
||||
{
|
||||
m_packed_resource = resource_loader->load(load_resource_path,
|
||||
load_type_hint, load_cache_mode);
|
||||
initialized = m_packed_resource.is_valid();
|
||||
}
|
||||
}
|
||||
|
||||
/* Pack from existing instance. */
|
||||
packed_scene(godot::Node* node)
|
||||
{
|
||||
m_packed_resource = scene::packer::pack(node);
|
||||
initialized = m_packed_resource.is_valid();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto instantiate() -> object_t*
|
||||
{
|
||||
assertion(initialized,
|
||||
"Resource instantiation invoked from uninitialized scene loader.");
|
||||
if (!initialized) [[unlikely]]
|
||||
return nullptr;
|
||||
|
||||
object_t* obj{ gdcast<object_t>(m_packed_resource->instantiate()) };
|
||||
runtime_assert(obj != nullptr);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Save this resource to specified path. */
|
||||
void save(godot::String& resource_save_path)
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
auto error = saver::get()->save(m_packed_resource, resource_save_path);
|
||||
assertion(error != godot::Error::OK, "Packed resource save failed.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
godot::Ref<scene_t> m_packed_resource{};
|
||||
bool initialized{ false };
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include <godot_cpp/classes/ref.hpp>
|
||||
|
||||
#include "core/concepts.hpp"
|
||||
|
||||
namespace rl::inline utils
|
||||
{
|
||||
template <VariantCompatible T>
|
||||
struct variant_traits
|
||||
{
|
||||
using raw_type = T;
|
||||
using type_info = godot::GetTypeInfo<std::remove_cvref_t<T>>;
|
||||
static constexpr inline godot::Variant::Type variant_type =
|
||||
static_cast<godot::Variant::Type>(type_info::VARIANT_TYPE);
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user