From 229b3f8f7ef3784f90abaf11909fa004c79d2f50 Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Thu, 13 Mar 2025 13:34:35 -0600 Subject: [PATCH] Grunk alarm! --- assets/materials/gunk_lowrez.material | Bin 783 -> 824 bytes levels/mechanic_test/mechanic_test.gd | 30 +++++-- levels/mechanic_test/mechanic_test.tscn | 62 +++++++++++++- src/game/game_manager.gd | 24 ++++++ src/world/mechanics/alarm/gunk_alarm.gd | 36 +++++++++ src/world/mechanics/alarm/gunk_alarm.gd.uid | 1 + src/world/mechanics/alarm/gunk_alarm.tscn | 85 ++++++++++++++++++++ 7 files changed, 227 insertions(+), 11 deletions(-) create mode 100644 src/world/mechanics/alarm/gunk_alarm.gd create mode 100644 src/world/mechanics/alarm/gunk_alarm.gd.uid create mode 100644 src/world/mechanics/alarm/gunk_alarm.tscn diff --git a/assets/materials/gunk_lowrez.material b/assets/materials/gunk_lowrez.material index 21aa1d5ce1c88aa37f64bcba8606f947e0d80157..a350d28f39fde5a46a88e47dfa5196d41f1573be 100644 GIT binary patch literal 824 zcmV-81IPSQQ$s@n000005C8yh2LJ#d0{{RhwJ-f(a0ay)07l|-LIBYu9dw0|2&#JY zI1~YP$N!6&vWyCV1l>kNq%xDe$=cW@NJ751)QIA2^PsdPQs6?Z(@ilb#u=-<8<+F_ zU&E$X09gQH0NGCYpTK*eiQOeN-Z%zl)|frz{{!BuITC7mbqRx7`v37#Urs@)A6)I{VfA_?oBs&@3-I^@IopGm8k4%s z61E$-EvM!7#m!QUS7Zs>=0b3q!Il&Di&0NdGr5x!GQ&(!K4PI(TeG)Mb0lZxr@20< ziH1AjYHszCA#V+?#maC>Eqm*B#%cX8aNU9N1!vtBW!i9trulLB4?^RD#f20)q!GuY zCNfzs-ca1i{2ZJ#(%t%`A!b^{8ZRaY^)F1sl(kGzF0A{pf+L|jX|Lv}M9^z%D6yBh z)ow^adzniy@ynb^ZrG)YU0QOD&texbH8wRcC@Cj5DS6=`sb-99_+PEqu_5y#a-u;c z)Zc3dzX-^I{{}{?{~RbD9RCks z=%O86of#{jxOHMN;RHDOsKnfnBehVJ%YTy(7&Z_cBtS5Ll&FC!LHIGTc%!o{wftji7u^Z(GxrX`G`3eB&zNcD`^G<&|#vCNvfq4RvRWO4M|3r@`i?HtCJE7s!sJa+YlUh(h5L^L+f-h+92%jMG1cupStNXR|e5h zdlE4~A~m_q(=${eTB3hsoSPGIf~Dc( zF?%5-#=wV#0OKlii#1q4u(O*9j9xwyF0+AGAxsFv#~I`F{L3n((ab-&@4W+3Q$s^) C$bm@! literal 783 zcmV+q1MvJ(Q$s@n000005C8zw1^@u}0ssIgwJ-f()CFx90G6R~Lh#Zg9W--kaRcY{ zy1bc4RdX-Tlsf-`=M$erh)5+Qdy`wgEKw$X!5AyinYQGSx*28T6Bls^M7z zw=Zs$YD__5*EZLI(+swR%L)6%s3oYa+{p=wM?t6+1sbNlr!_wT$fZu!<}$7 zw`xg}w+6RjWjLjly>&a|to|3c>cIH24JkkHtlObX8_v))zYqUMXlB63&?0AK;+NDy zr0T_+iCdW;gO5bITbGo?OpK_5Munihb%l7UmZ#~3bwwUrt*xQFUglQ2AqwqfE&_>P z=16k8EY-_Wm1}$zJDjb-u?{gcHZ>n#QGwY>p>Uvd_)opi*&*X&vSI-h#M49FaQSb+ z0}ll;CDfDgf!Rp_!^6`s7i-&Y{b7s5{ zpyXAlg>)iB#ziUSirmO$nqK~|h|sW&011GEs4+1^%!tg)gegq298iKGWOUsUKt(mq zNy3bXNSJ|{sqC>E!Ki%+|7xsYkYEQ-6Q@cjY`(E&3Nigc&D{Q6W^GlQG69*I?kcWe z{GF*Q@RC;HyR&}(!@6yF>YVnM;E=f@rGELU7Qs8Qm22jv&&q;q*q3C9pqBabD+HDR z)npai6?7(+BI@xA3`-*18YoI_#x9X%JE2^&}}02O2>2KZfIPz1V@ N&)}0xze-Y5Lqiazi1Ppd diff --git a/levels/mechanic_test/mechanic_test.gd b/levels/mechanic_test/mechanic_test.gd index dcca6de..17b00a5 100644 --- a/levels/mechanic_test/mechanic_test.gd +++ b/levels/mechanic_test/mechanic_test.gd @@ -4,12 +4,14 @@ extends Node3D @onready var gunk_hall: GunkBody = %GunkHall @onready var bulkhead: Node3D = $Bulkhead -@onready var open_switch: Node3D = $Bulkhead/Podium/OpenSwitch -@onready var close_switch: Node3D = $Bulkhead/Podium2/CloseSwitch +@onready var open_switch: Node3D = %OpenSwitch +@onready var close_switch: Node3D = %CloseSwitch -@onready var nodule_spawn_point: Marker3D = $NoduleSpawner/NoduleSpawnPoint +@onready var nodule_spawn_point: Marker3D = %NoduleSpawnPoint +@onready var alarm_spawn_point: Marker3D = %AlarmSpawnPoint static var nodule_scene: PackedScene = load("res://src/world/gunk_node/grunk_nodule.tscn") +static var alarm_scene: PackedScene = load("res://src/world/mechanics/alarm/gunk_alarm.tscn") func reset() -> void: @@ -20,8 +22,22 @@ func reset() -> void: Callable(close_switch, "disable").call() -func spawn_nodule() -> void: - for c: Node in nodule_spawn_point.get_children(): +func _do_spawn(spawn_point: Node3D, scene: PackedScene) -> void: + for c: Node in spawn_point.get_children(): c.queue_free() - var instance := nodule_scene.instantiate() - nodule_spawn_point.add_child(instance) + var instance := scene.instantiate() + spawn_point.add_child(instance) + + +func spawn_nodule() -> void: + _do_spawn(nodule_spawn_point, nodule_scene) + + +func spawn_alarm() -> void: + _do_spawn(alarm_spawn_point, alarm_scene) + + +func trigger_spawned_alarm() -> void: + var alarm: GunkAlarm = alarm_spawn_point.get_child(0) + if alarm: + alarm.trigger() diff --git a/levels/mechanic_test/mechanic_test.tscn b/levels/mechanic_test/mechanic_test.tscn index a08a65c..2170ef2 100644 --- a/levels/mechanic_test/mechanic_test.tscn +++ b/levels/mechanic_test/mechanic_test.tscn @@ -173,17 +173,58 @@ mesh = SubResource("BoxMesh_goufh") [node name="CollisionShape3D" type="CollisionShape3D" parent="NoduleSpawner"] shape = SubResource("ConcavePolygonShape3D_bg05n") -[node name="SpawnSwitch" parent="NoduleSpawner" instance=ExtResource("2_pka60")] +[node name="WallSwitch" parent="NoduleSpawner" instance=ExtResource("2_pka60")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35) enabled = true [node name="Label3D" type="Label3D" parent="NoduleSpawner"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0.3) -text = "Spawn" +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0.3) +text = "Spawn +Nodule" [node name="NoduleSpawnPoint" type="Marker3D" parent="NoduleSpawner"] +unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -3) +[node name="AlarmSpawner" type="StaticBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.5, 1, -6) + +[node name="MeshInstance3D" type="MeshInstance3D" parent="AlarmSpawner"] +mesh = SubResource("BoxMesh_goufh") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="AlarmSpawner"] +shape = SubResource("ConcavePolygonShape3D_bg05n") + +[node name="WallSwitch" parent="AlarmSpawner" instance=ExtResource("2_pka60")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35) +enabled = true + +[node name="Label3D" type="Label3D" parent="AlarmSpawner"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0.3) +text = "Spawn +Alarm" + +[node name="AlarmSpawnPoint" type="Marker3D" parent="AlarmSpawner"] +unique_name_in_owner = true +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.7, -4) + +[node name="AlarmTrigger" type="StaticBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.5, 1, -6) + +[node name="MeshInstance3D" type="MeshInstance3D" parent="AlarmTrigger"] +mesh = SubResource("BoxMesh_goufh") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="AlarmTrigger"] +shape = SubResource("ConcavePolygonShape3D_bg05n") + +[node name="AlarmTriggerSwitch" parent="AlarmTrigger" instance=ExtResource("2_pka60")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35) +enabled = true + +[node name="Label3D" type="Label3D" parent="AlarmTrigger"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0.3) +text = "Trigger" + [node name="GunkHall" parent="." instance=ExtResource("4_2uiim")] unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.5, 0, -31) @@ -197,6 +238,7 @@ mesh = SubResource("ArrayMesh_x2vho") shape = SubResource("ConcavePolygonShape3D_qjnj2") [node name="Bulkhead" parent="." instance=ExtResource("8_0j1ke")] +unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.5, 0, -2) [node name="Podium" type="StaticBody3D" parent="Bulkhead"] @@ -209,9 +251,14 @@ mesh = SubResource("BoxMesh_goufh") shape = SubResource("ConcavePolygonShape3D_bg05n") [node name="OpenSwitch" parent="Bulkhead/Podium" instance=ExtResource("2_pka60")] +unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35) enabled = true +[node name="Label3D" type="Label3D" parent="Bulkhead/Podium"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0.3) +text = "Open" + [node name="Podium2" type="StaticBody3D" parent="Bulkhead"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 1, 0) @@ -222,10 +269,17 @@ mesh = SubResource("BoxMesh_goufh") shape = SubResource("ConcavePolygonShape3D_bg05n") [node name="CloseSwitch" parent="Bulkhead/Podium2" instance=ExtResource("2_pka60")] +unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0.35) +[node name="Label3D" type="Label3D" parent="Bulkhead/Podium2"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0.3) +text = "Close" + [connection signal="activated" from="ResetPodium/ResetSwitch" to="." method="reset"] -[connection signal="activated" from="NoduleSpawner/SpawnSwitch" to="." method="spawn_nodule"] +[connection signal="activated" from="NoduleSpawner/WallSwitch" to="." method="spawn_nodule"] +[connection signal="activated" from="AlarmSpawner/WallSwitch" to="." method="spawn_alarm"] +[connection signal="activated" from="AlarmTrigger/AlarmTriggerSwitch" to="." method="trigger_spawned_alarm"] [connection signal="activated" from="Bulkhead/Podium/OpenSwitch" to="Bulkhead" method="open"] [connection signal="activated" from="Bulkhead/Podium/OpenSwitch" to="Bulkhead/Podium/OpenSwitch" method="disable"] [connection signal="activated" from="Bulkhead/Podium/OpenSwitch" to="Bulkhead/Podium2/CloseSwitch" method="enable"] diff --git a/src/game/game_manager.gd b/src/game/game_manager.gd index f8c84bb..8a4c84b 100644 --- a/src/game/game_manager.gd +++ b/src/game/game_manager.gd @@ -1,13 +1,37 @@ class_name GameManagerType extends Node ## Autoloaded singleton encapsulating game state. +## Emitted just after `delta` is added to the player's grunk tank. signal grunk_collected(delta: float) +## Emitted just before the alert level is raised to `new_value`. +signal alert_raised(new_value: int) + +## Emitted just before the alert level is reset to zero. +signal alert_cleared + +const MAX_ALERT := 6 + var grunk_tank := 0.0 var alert_level := 0 +## Add to the player's grunk tank. func collect_grunk(delta: float) -> void: grunk_tank += delta grunk_collected.emit(delta) + + +## Raise the alert level, if possible. +func raise_alert(delta: int) -> void: + var new_value := clampi(alert_level + delta, 0, MAX_ALERT) + if new_value != alert_level: + alert_raised.emit(new_value) + alert_level = new_value + + +## Reset the alert level to zero. +func clear_alert() -> void: + alert_cleared.emit() + alert_level = 0 diff --git a/src/world/mechanics/alarm/gunk_alarm.gd b/src/world/mechanics/alarm/gunk_alarm.gd new file mode 100644 index 0000000..1a0c822 --- /dev/null +++ b/src/world/mechanics/alarm/gunk_alarm.gd @@ -0,0 +1,36 @@ +class_name GunkAlarm extends GunkNode +## Raises the grunk alert when triggered. + +## Emitted when this alarm is triggered, just after the alert level is raised. +signal triggered + +const ALERT_DELTA := 1 + +var _busy := false + +@onready var mesh_instance_3d: MeshInstance3D = %MeshInstance3D +@onready var animation_player: AnimationPlayer = %AnimationPlayer + + +## Trigger this alarm. +## +## Will not trigger if this alarm is busy (e.g. on cooldown) +func trigger() -> void: + if not _busy: + Game.manager.raise_alert(ALERT_DELTA) + _busy = true + animation_player.play("trigger") + triggered.emit() + + +func _process(delta: float) -> void: + super._process(delta) + # TODO actual model & animation + var material: StandardMaterial3D = mesh_instance_3d.mesh.surface_get_material(0) + var value := 1.0 - _sustained_damage / durability + material.albedo_color = Color(value, value, value) + + +func _on_animation_finished(anim_name: StringName) -> void: + if anim_name == "trigger": + _busy = false diff --git a/src/world/mechanics/alarm/gunk_alarm.gd.uid b/src/world/mechanics/alarm/gunk_alarm.gd.uid new file mode 100644 index 0000000..45103f9 --- /dev/null +++ b/src/world/mechanics/alarm/gunk_alarm.gd.uid @@ -0,0 +1 @@ +uid://djb5tabjcvwkt diff --git a/src/world/mechanics/alarm/gunk_alarm.tscn b/src/world/mechanics/alarm/gunk_alarm.tscn new file mode 100644 index 0000000..f9f304b --- /dev/null +++ b/src/world/mechanics/alarm/gunk_alarm.tscn @@ -0,0 +1,85 @@ +[gd_scene load_steps=8 format=3 uid="uid://dgeg3kkogm71m"] + +[ext_resource type="Script" uid="uid://djb5tabjcvwkt" path="res://src/world/mechanics/alarm/gunk_alarm.gd" id="1_piaxx"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_piaxx"] +resource_local_to_scene = true +emission_enabled = true +emission = Color(1, 0, 0, 1) +emission_energy_multiplier = 0.0 + +[sub_resource type="CylinderMesh" id="CylinderMesh_1uge1"] +resource_local_to_scene = true +material = SubResource("StandardMaterial3D_piaxx") +top_radius = 0.3 +bottom_radius = 0.3 +height = 0.3 + +[sub_resource type="CylinderShape3D" id="CylinderShape3D_b4qsp"] +height = 0.3 +radius = 0.3 + +[sub_resource type="Animation" id="Animation_piaxx"] +resource_name = "trigger" +length = 4.0 +step = 0.1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("MeshInstance3D:mesh:material:emission_energy_multiplier") +tracks/0/interp = 2 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [0.0, 4.0, 0.0, 4.0, 0.0, 4.0, 0.0, 4.0, 0.0] +} + +[sub_resource type="Animation" id="Animation_b6er8"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("MeshInstance3D:mesh:material:emission_energy_multiplier") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [0.0] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_b6er8"] +_data = { +&"RESET": SubResource("Animation_b6er8"), +&"trigger": SubResource("Animation_piaxx") +} + +[node name="GunkAlarm" type="StaticBody3D"] +collision_layer = 5 +collision_mask = 0 +script = ExtResource("1_piaxx") +durability = 3.0 +value = 10000.0 +metadata/_custom_type_script = "uid://bypgxi0gy56yk" + +[node name="Label3D" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.2, 0) +text = "ALARM" + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +unique_name_in_owner = true +mesh = SubResource("CylinderMesh_1uge1") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("CylinderShape3D_b4qsp") + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +unique_name_in_owner = true +libraries = { +&"": SubResource("AnimationLibrary_b6er8") +} + +[connection signal="animation_finished" from="AnimationPlayer" to="." method="_on_animation_finished"]