From c98f1df8810290039c5f684858bd17fe25598c7f Mon Sep 17 00:00:00 2001 From: Sara Date: Tue, 23 Jun 2026 11:21:04 +0200 Subject: [PATCH] feat: checkpoints,deathboxes&respawning --- modules/viscosity/checkpoint.cpp | 25 +++++ modules/viscosity/checkpoint.h | 13 +++ modules/viscosity/death_box.cpp | 26 +++++ modules/viscosity/death_box.h | 12 ++ modules/viscosity/level.cpp | 66 +++++++++++ modules/viscosity/level.h | 33 ++++++ modules/viscosity/player_body.cpp | 15 ++- modules/viscosity/player_body.h | 3 +- modules/viscosity/register_types.cpp | 6 + project/levels/appartments.tscn | 13 ++- project/levels/appartments_under_ground.tscn | 109 +++---------------- project/levels/world.tscn | 34 +++++- 12 files changed, 254 insertions(+), 101 deletions(-) create mode 100644 modules/viscosity/checkpoint.cpp create mode 100644 modules/viscosity/checkpoint.h create mode 100644 modules/viscosity/death_box.cpp create mode 100644 modules/viscosity/death_box.h create mode 100644 modules/viscosity/level.cpp create mode 100644 modules/viscosity/level.h diff --git a/modules/viscosity/checkpoint.cpp b/modules/viscosity/checkpoint.cpp new file mode 100644 index 00000000..36f05622 --- /dev/null +++ b/modules/viscosity/checkpoint.cpp @@ -0,0 +1,25 @@ +#include "checkpoint.h" + +#include "core/config/engine.h" +#include "core/object/callable_mp.h" +#include "viscosity/level.h" +#include "viscosity/player_body.h" + +void Checkpoint::body_entered(Node3D *body) { + if (cast_to(body)) { + Level::get_singleton()->checkpoint(); + } +} + +void Checkpoint::_notification(int what) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + switch (what) { + default: + return; + case NOTIFICATION_READY: + connect("body_entered", callable_mp(this, &self_type::body_entered)); + return; + } +} diff --git a/modules/viscosity/checkpoint.h b/modules/viscosity/checkpoint.h new file mode 100644 index 00000000..369ca8b3 --- /dev/null +++ b/modules/viscosity/checkpoint.h @@ -0,0 +1,13 @@ +#pragma once + +#include "core/math/transform_3d.h" +#include "scene/3d/physics/area_3d.h" + +class Checkpoint : public Area3D { + GDCLASS(Checkpoint, Area3D); + static void _bind_methods() {} + void body_entered(Node3D *node); + +protected: + void _notification(int what); +}; diff --git a/modules/viscosity/death_box.cpp b/modules/viscosity/death_box.cpp new file mode 100644 index 00000000..87950211 --- /dev/null +++ b/modules/viscosity/death_box.cpp @@ -0,0 +1,26 @@ +#include "death_box.h" + +#include "core/config/engine.h" +#include "core/object/callable_mp.h" +#include "viscosity/level.h" +#include "viscosity/player_body.h" + +void Deathbox::body_entered(Node3D *body) { + if (PlayerBody * player{ cast_to(body) }) { + player->damage(); + callable_mp(Level::get_singleton(), &Level::rescue).call_deferred(); + } +} + +void Deathbox::_notification(int what) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + switch (what) { + default: + return; + case NOTIFICATION_READY: + connect("body_entered", callable_mp(this, &self_type::body_entered)); + return; + } +} diff --git a/modules/viscosity/death_box.h b/modules/viscosity/death_box.h new file mode 100644 index 00000000..01996940 --- /dev/null +++ b/modules/viscosity/death_box.h @@ -0,0 +1,12 @@ +#pragma once + +#include "scene/3d/physics/area_3d.h" +class Deathbox : public Area3D { + GDCLASS(Deathbox, Area3D); + +public: + void body_entered(Node3D *body); + +protected: + void _notification(int what); +}; diff --git a/modules/viscosity/level.cpp b/modules/viscosity/level.cpp new file mode 100644 index 00000000..036b290a --- /dev/null +++ b/modules/viscosity/level.cpp @@ -0,0 +1,66 @@ +#include "level.h" +#include "core/config/engine.h" +#include "core/error/error_macros.h" +#include "core/object/callable_mp.h" +#include "viscosity/player_body.h" + +Level *Level::singleton_instance{ nullptr }; + +Level *Level::get_singleton() { + return singleton_instance; +} + +void Level::_bind_methods() { + BIND_HPROPERTY(Variant::OBJECT, player_scene, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); + BIND_HPROPERTY(Variant::OBJECT, default_spawnpoint, PROPERTY_HINT_NODE_TYPE, "Node3D"); +} + +void Level::respawn() { + if (this->player) { + this->player->queue_free(); + } + this->player = cast_to(this->player_scene->instantiate()); + ERR_FAIL_COND_EDMSG(!this->player, "Invalid player scene set on level config"); + this->add_child(this->player); + this->player->set_transform(this->last_checkpoint); + this->player->force_update_transform(); +} + +void Level::rescue() { + if (!this->player) { + respawn(); + } else { + double health{ this->player->get_health() }; + respawn(); + this->player->set_health(health); + } +} + +void Level::checkpoint() { + this->last_checkpoint = this->player->get_transform(); +} + +void Level::_notification(int what) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + switch (what) { + default: + return; + case NOTIFICATION_ENTER_TREE: + ERR_FAIL_COND_EDMSG(singleton_instance, "Cannot instantiate second level while another level is loaded"); + singleton_instance = this; + return; + case NOTIFICATION_READY: + if (this->default_spawnpoint) { + this->last_checkpoint = this->default_spawnpoint->get_global_transform(); + } + respawn(); + return; + case NOTIFICATION_EXIT_TREE: + if (singleton_instance == this) { + singleton_instance = nullptr; + } + return; + } +} diff --git a/modules/viscosity/level.h b/modules/viscosity/level.h new file mode 100644 index 00000000..8daf8a6b --- /dev/null +++ b/modules/viscosity/level.h @@ -0,0 +1,33 @@ +#pragma once + +#include "macros.h" +#include "scene/3d/node_3d.h" +#include "scene/resources/packed_scene.h" +class Checkpoint; +class PlayerBody; + +class Level : public Node3D { + GDCLASS(Level, Node3D); + static Level *singleton_instance; + +public: + static Level *get_singleton(); + static void _bind_methods(); + void respawn(); + void rescue(); + void checkpoint(); + +protected: + void _notification(int what); + +private: + Ref player_scene{}; + Node3D *default_spawnpoint{ nullptr }; + + PlayerBody *player{ nullptr }; + Transform3D last_checkpoint{}; + +public: + GET_SET_FNS(Ref, player_scene); + GET_SET_FNS(Node3D *, default_spawnpoint); +}; diff --git a/modules/viscosity/player_body.cpp b/modules/viscosity/player_body.cpp index 31fb1edc..66fafcca 100644 --- a/modules/viscosity/player_body.cpp +++ b/modules/viscosity/player_body.cpp @@ -1,7 +1,9 @@ #include "player_body.h" #include "core/config/engine.h" +#include "core/math/transform_3d.h" #include "core/object/class_db.h" #include "player_behaviour_tree.h" +#include "viscosity/level.h" void PlayerBody::_bind_methods() { BIND_PROPERTY(Variant::FLOAT, health); @@ -32,8 +34,19 @@ void PlayerBody::_notification(int what) { } void PlayerBody::damage() { - this->health -= 0.1; + set_health(this->health - 0.1); +} + +void PlayerBody::set_health(double value) { + this->health = value; if (this->skin) { this->skin->set_instance_shader_parameter("health", this->health); } + if (this->health < 0) { + Level::get_singleton()->respawn(); + } +} + +double PlayerBody::get_health() const { + return this->health; } diff --git a/modules/viscosity/player_body.h b/modules/viscosity/player_body.h index 6c546e67..0f1010f7 100644 --- a/modules/viscosity/player_body.h +++ b/modules/viscosity/player_body.h @@ -20,5 +20,6 @@ private: GeometryInstance3D *skin{ nullptr }; public: - GET_SET_FNS(double, health); + void set_health(double value); + double get_health() const; }; diff --git a/modules/viscosity/register_types.cpp b/modules/viscosity/register_types.cpp index a80dd2e0..97d5e586 100644 --- a/modules/viscosity/register_types.cpp +++ b/modules/viscosity/register_types.cpp @@ -2,7 +2,10 @@ #include "core/object/class_db.h" #include "viscosity/area_loader.h" +#include "viscosity/checkpoint.h" +#include "viscosity/death_box.h" #include "viscosity/enemy_orb.h" +#include "viscosity/level.h" #include "viscosity/player_action.h" #include "viscosity/player_animation_action.h" #include "viscosity/player_behaviour_tree.h" @@ -22,6 +25,9 @@ void initialize_viscosity_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); } void uninitialize_viscosity_module(ModuleInitializationLevel p_level) { diff --git a/project/levels/appartments.tscn b/project/levels/appartments.tscn index f9ea0f33..520bed7c 100644 --- a/project/levels/appartments.tscn +++ b/project/levels/appartments.tscn @@ -87,6 +87,9 @@ size = Vector3(34.032814, 12.55675, 5.36638) [sub_resource type="BoxOccluder3D" id="BoxOccluder3D_4x0nu"] size = Vector3(9.69065, 16.597681, 5.36638) +[sub_resource type="BoxShape3D" id="BoxShape3D_sewij"] +size = Vector3(167, 26, 143) + [node name="Appartments" type="Node3D" unique_id=2084435541] [node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=251730206] @@ -7540,10 +7543,18 @@ transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, -43, transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -46, 0, 61) [node name="CSGBox3D3" type="CSGBox3D" parent="Area" unique_id=1411597639] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -21.813, -28.664265, 70) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -21.813, -24.664265, 70) size = Vector3(92, 39, 139) material = ExtResource("18_vsqsw") +[node name="Deathbox" type="Deathbox" parent="Area" unique_id=838339085] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -12, -7, 51) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Area/Deathbox" unique_id=1141727205] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -11.5, 0) +shape = SubResource("BoxShape3D_sewij") +debug_color = Color(0.7232896, 0, 0.18208742, 0.41960785) + [node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=129268097] use_collision = true diff --git a/project/levels/appartments_under_ground.tscn b/project/levels/appartments_under_ground.tscn index eea3cc0c..89003367 100644 --- a/project/levels/appartments_under_ground.tscn +++ b/project/levels/appartments_under_ground.tscn @@ -1,10 +1,6 @@ [gd_scene format=3 uid="uid://dn2b7abjjel6j"] -[ext_resource type="Material" uid="uid://q6ixu2q8qaw1" path="res://assets/environment/triplanar_materials/bricks.tres" id="1_hyss3"] -[ext_resource type="Material" uid="uid://c6uf0ckexrkxk" path="res://assets/environment/triplanar_materials/gray_concrete.tres" id="2_2c4k1"] [ext_resource type="Material" uid="uid://diprpexdcxpja" path="res://assets/environment/triplanar_materials/tiles.tres" id="3_sxlls"] -[ext_resource type="PackedScene" uid="uid://do0q3dvf2y8if" path="res://assets/environment/walls/window_frame.blend" id="4_grooo"] -[ext_resource type="PackedScene" uid="uid://4418x3hwthdc" path="res://assets/environment/walls/city_wall_base.blend" id="5_dqeqk"] [ext_resource type="Material" uid="uid://cirdfdncgft7o" path="res://assets/effects/floor.tres" id="6_sxlls"] [sub_resource type="BoxShape3D" id="BoxShape3D_exgcw"] @@ -17,103 +13,28 @@ size = Vector3(35, 24, 91) [node name="Area" type="Node3D" parent="." unique_id=821786742] +[node name="CSGCombiner3D2" type="CSGCombiner3D" parent="Area" unique_id=711485935] + +[node name="CSGBox3D4" type="CSGBox3D" parent="Area/CSGCombiner3D2" unique_id=356389791] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8646579, 0) +flip_faces = true +size = Vector3(11.776367, 26, 11.896484) +material = ExtResource("6_sxlls") + +[node name="CSGBox3D2" type="CSGBox3D" parent="Area/CSGCombiner3D2" unique_id=351025264] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.6870003, -29.664265, -38) +size = Vector3(51, 37, 101) +material = ExtResource("6_sxlls") + [node name="CSGCombiner3D" type="CSGCombiner3D" parent="Area" unique_id=356944886] layers = 5 use_collision = true -[node name="CSGBox3D" type="CSGBox3D" parent="Area/CSGCombiner3D" unique_id=2147070688] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -3.389523, 0) -size = Vector3(11.779541, 35.621643, 11.972656) -material = ExtResource("1_hyss3") - -[node name="CSGBox3D2" type="CSGBox3D" parent="Area/CSGCombiner3D" unique_id=356389791] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -5.146725, 0) -operation = 2 -size = Vector3(11, 38.022766, 10) -material = ExtResource("2_2c4k1") - [node name="CSGBox3D3" type="CSGBox3D" parent="Area/CSGCombiner3D" unique_id=198262996] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -29.658108, 0) -size = Vector3(12, 1, 12) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -20.658108, 1) +size = Vector3(12, 1, 10) material = ExtResource("3_sxlls") -[node name="window_frame" parent="Area" unique_id=226621753 instance=ExtResource("4_grooo")] -transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 6, -24, -5) - -[node name="window_frame2" parent="Area" unique_id=198104588 instance=ExtResource("4_grooo")] -transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 6, -24, -3) - -[node name="window_frame3" parent="Area" unique_id=1778086349 instance=ExtResource("4_grooo")] -transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 6, -24, 3) - -[node name="window_frame4" parent="Area" unique_id=991193163 instance=ExtResource("4_grooo")] -transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 6, -24, 5) - -[node name="city_wall_base" parent="Area" unique_id=1823372432 instance=ExtResource("5_dqeqk")] -transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 6, -24, -1) - -[node name="city_wall_base2" parent="Area" unique_id=1356203604 instance=ExtResource("5_dqeqk")] -transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 6, -24, 1) - -[node name="window_frame5" parent="Area" unique_id=372451463 instance=ExtResource("4_grooo")] -transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -6, -24, -5.0000005) - -[node name="window_frame6" parent="Area" unique_id=1396379676 instance=ExtResource("4_grooo")] -transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -6, -24, -3.0000005) - -[node name="window_frame7" parent="Area" unique_id=1087244952 instance=ExtResource("4_grooo")] -transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -6, -24, 2.9999995) - -[node name="window_frame8" parent="Area" unique_id=663305857 instance=ExtResource("4_grooo")] -transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -6, -24, 4.9999995) - -[node name="city_wall_base3" parent="Area" unique_id=1945979472 instance=ExtResource("5_dqeqk")] -transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -6, -24, -1.0000005) - -[node name="city_wall_base4" parent="Area" unique_id=1257259467 instance=ExtResource("5_dqeqk")] -transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -6, -24, 0.99999946) - -[node name="window_frame9" parent="Area" unique_id=1201347784 instance=ExtResource("4_grooo")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, -24, -6.0000005) - -[node name="window_frame10" parent="Area" unique_id=1541010099 instance=ExtResource("4_grooo")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, -24, -6.0000005) - -[node name="window_frame11" parent="Area" unique_id=1595142513 instance=ExtResource("4_grooo")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3, -24, -6.0000005) - -[node name="window_frame12" parent="Area" unique_id=360625100 instance=ExtResource("4_grooo")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, -24, -6.0000005) - -[node name="city_wall_base5" parent="Area" unique_id=42182012 instance=ExtResource("5_dqeqk")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, -24, -6.0000005) - -[node name="city_wall_base6" parent="Area" unique_id=393602731 instance=ExtResource("5_dqeqk")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, -24, -6.0000005) - -[node name="window_frame13" parent="Area" unique_id=1498057755 instance=ExtResource("4_grooo")] -transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -5, -24, 6) - -[node name="window_frame14" parent="Area" unique_id=1378481903 instance=ExtResource("4_grooo")] -transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -3, -24, 6) - -[node name="window_frame15" parent="Area" unique_id=910105726 instance=ExtResource("4_grooo")] -transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 3, -24, 5.999999) - -[node name="window_frame16" parent="Area" unique_id=837678815 instance=ExtResource("4_grooo")] -transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 5, -24, 5.999999) - -[node name="city_wall_base7" parent="Area" unique_id=1538275479 instance=ExtResource("5_dqeqk")] -transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, -1, -24, 5.9999995) - -[node name="city_wall_base8" parent="Area" unique_id=1026692077 instance=ExtResource("5_dqeqk")] -transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 1, -24, 5.9999995) - -[node name="CSGBox3D2" type="CSGBox3D" parent="Area" unique_id=351025264] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.6870003, -29.664265, -38) -size = Vector3(51, 37, 101) -material = ExtResource("6_sxlls") - [node name="AreaLoader" type="AreaLoader" parent="." unique_id=1144416817 node_paths=PackedStringArray("area")] area = NodePath("../Area") diff --git a/project/levels/world.tscn b/project/levels/world.tscn index 3c683dd8..8678b8e9 100644 --- a/project/levels/world.tscn +++ b/project/levels/world.tscn @@ -34,7 +34,15 @@ height = 109.2168 radial_segments = 8 rings = 1 -[node name="World" type="Node3D" unique_id=1490306849] +[sub_resource type="BoxShape3D" id="BoxShape3D_exgcw"] +size = Vector3(1, 4.220703, 5.626709) + +[sub_resource type="BoxShape3D" id="BoxShape3D_lmpr0"] +size = Vector3(2.8554688, 4.220703, 1.0498047) + +[node name="World" type="Level" unique_id=601795347 node_paths=PackedStringArray("default_spawnpoint")] +player_scene = ExtResource("4_w32yo") +default_spawnpoint = NodePath("StartSpawn") [node name="WorldEnvironment" parent="." unique_id=1373429783 instance=ExtResource("1_pmfem")] @@ -43,9 +51,6 @@ transform = Transform3D(0.5606569, -0.6936318, -0.4522596, 6.7005715e-08, -0.546 light_color = Color(0.88157237, 0.8014759, 0.84016156, 1) light_energy = 0.528 -[node name="Player" parent="." unique_id=591139793 instance=ExtResource("4_w32yo")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -47.99643, -6.5538864, 10.622761) - [node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=2045560215] transform = Transform3D(1, 0, 0, 0, -1, 8.742278e-08, 0, -8.742278e-08, -1, 0, 34.67711, -2.725292e-06) material_override = SubResource("StandardMaterial3D_wqmfy") @@ -71,3 +76,24 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 42.893333, -0.017716527, 8.96 [node name="AppartmentsUnderGround" parent="." unique_id=821786742 instance=ExtResource("10_lmpr0")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.0700092, -6.020659, 123.80248) + +[node name="StartSpawn" type="Marker3D" parent="." unique_id=819454161] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -47.964764, -6.561678, 10.5630455) + +[node name="Checkpoint" type="Checkpoint" parent="." unique_id=342819047] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -27.667583, 1.9508343, 0.41703868) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Checkpoint" unique_id=1645299362] +shape = SubResource("BoxShape3D_exgcw") + +[node name="Checkpoint2" type="Checkpoint" parent="." unique_id=579407448] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 31.389427, 1.9508343, 0.41703868) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Checkpoint2" unique_id=1827833114] +shape = SubResource("BoxShape3D_exgcw") + +[node name="Checkpoint3" type="Checkpoint" parent="." unique_id=1700913428] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 36.84496, 1.9508343, 15.171164) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Checkpoint3" unique_id=1155112272] +shape = SubResource("BoxShape3D_lmpr0")