feat: checkpoints,deathboxes&respawning

This commit is contained in:
Sara Gerretsen 2026-06-23 11:21:04 +02:00
parent ec70e69d48
commit c98f1df881
12 changed files with 254 additions and 101 deletions

View file

@ -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<PlayerBody>(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;
}
}

View file

@ -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);
};

View file

@ -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<PlayerBody>(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;
}
}

View file

@ -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);
};

View file

@ -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<PlayerBody>(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;
}
}

33
modules/viscosity/level.h Normal file
View file

@ -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<PackedScene> player_scene{};
Node3D *default_spawnpoint{ nullptr };
PlayerBody *player{ nullptr };
Transform3D last_checkpoint{};
public:
GET_SET_FNS(Ref<PackedScene>, player_scene);
GET_SET_FNS(Node3D *, default_spawnpoint);
};

View file

@ -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;
}

View file

@ -20,5 +20,6 @@ private:
GeometryInstance3D *skin{ nullptr };
public:
GET_SET_FNS(double, health);
void set_health(double value);
double get_health() const;
};

View file

@ -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<PlayerMovementAction>();
ClassDB::register_class<EnemyOrb>();
ClassDB::register_class<AreaLoader>();
ClassDB::register_class<Level>();
ClassDB::register_class<Checkpoint>();
ClassDB::register_class<Deathbox>();
}
void uninitialize_viscosity_module(ModuleInitializationLevel p_level) {

View file

@ -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

View file

@ -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")

View file

@ -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")