From 42f59962f523e5e0d885861e3435e5ec465614a0 Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Wed, 5 Mar 2025 15:35:49 -0700 Subject: [PATCH] Interaction sequence for wall switch --- src/player/player.gd | 7 +- src/props/interactive.gd | 10 +- src/props/wall_switch/wall_switch.gd | 44 ++++++ src/props/wall_switch/wall_switch.tscn | 196 +++++++++++++++---------- src/ui/hud/player_hud.gd | 8 +- src/world/gunk_body/gunk_body.gd | 12 ++ 6 files changed, 190 insertions(+), 87 deletions(-) diff --git a/src/player/player.gd b/src/player/player.gd index 4274331..2a4b79c 100644 --- a/src/player/player.gd +++ b/src/player/player.gd @@ -40,7 +40,12 @@ func get_spray() -> Spray: func _physics_process(delta: float) -> void: - player_hud.select_interactive(interact_ray.get_collider() as Interactive) + # Will be null if no valid interactor is selected. + var interactive: Interactive = interact_ray.get_collider() as Interactive + player_hud.select_interactive(interactive) + + if interactive and Input.is_action_just_pressed("interact"): + interactive.activate() if Input.is_action_pressed("fire"): get_spray().fire() diff --git a/src/props/interactive.gd b/src/props/interactive.gd index b8c55e7..30343aa 100644 --- a/src/props/interactive.gd +++ b/src/props/interactive.gd @@ -1,6 +1,12 @@ class_name Interactive extends StaticBody3D ## Props the player can interact with. -signal activate +signal activated -@export var disabled := false +@export var enabled := false + + +func activate() -> void: + if enabled: + activated.emit() + # TODO: bonk diff --git a/src/props/wall_switch/wall_switch.gd b/src/props/wall_switch/wall_switch.gd index 67e954f..57819e7 100644 --- a/src/props/wall_switch/wall_switch.gd +++ b/src/props/wall_switch/wall_switch.gd @@ -1 +1,45 @@ extends Node3D + +signal cleared +signal activated + +const CLEAR_THRESHOLD := 2750 + +@export var enabled := false + +@onready var animation_player: AnimationPlayer = %AnimationPlayer +@onready var light_animation: AnimationPlayer = %LightAnimation +@onready var gunk_body: GunkBody = %GunkBody +@onready var interactive: Interactive = %Interactive + + +func _ready() -> void: + if enabled: + enable() + + +func enable() -> void: + enabled = true + + # Clear off the rest of the gunk + gunk_body.clear_all() + light_animation.play("success") + interactive.enabled = true + + +func _on_gunk_body_painted() -> void: + if not enabled: + var clear_total := gunk_body.get_clear_total() + if clear_total >= CLEAR_THRESHOLD: + enable() + + +func _activate() -> void: + animation_player.play("activate") + activated.emit() + # Disable while animation is playing + interactive.enabled = false + + +func _animation_finished(_anim_name: StringName) -> void: + interactive.enabled = true diff --git a/src/props/wall_switch/wall_switch.tscn b/src/props/wall_switch/wall_switch.tscn index 828046f..2d48d82 100644 --- a/src/props/wall_switch/wall_switch.tscn +++ b/src/props/wall_switch/wall_switch.tscn @@ -61,74 +61,6 @@ texture_filter = 2 [sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_1xqlp"] data = PackedVector3Array(-0.24, -0.32, 0.1, -0.3, 0.4, -0.1, -0.24, 0.32, 0.1, -0.24, -0.32, 0.1, -0.3, -0.4, -0.1, -0.3, 0.4, -0.1, -0.3, -0.4, -0.1, 0.3, 0.4, -0.1, -0.3, 0.4, -0.1, -0.3, -0.4, -0.1, 0.3, -0.4, -0.1, 0.3, 0.4, -0.1, 0.3, -0.4, -0.1, 0.24, 0.32, 0.1, 0.3, 0.4, -0.1, 0.3, -0.4, -0.1, 0.24, -0.32, 0.1, 0.24, 0.32, 0.1, -0.24, -0.32, 0.1, 0.3, -0.4, -0.1, -0.3, -0.4, -0.1, 0.3, -0.4, -0.1, 0.1, -0.32, 0.1, 0.24, -0.32, 0.1, 0.3, -0.4, -0.1, -0.24, -0.32, 0.1, 0.1, -0.32, 0.1, 0.24, 0.32, 0.1, -0.3, 0.4, -0.1, 0.3, 0.4, -0.1, -0.3, 0.4, -0.1, 0.1, 0.32, 0.1, -0.24, 0.32, 0.1, -0.3, 0.4, -0.1, 0.24, 0.32, 0.1, 0.1, 0.32, 0.1, 0.24, -0.32, 0.1, 0.1, 0.32, 0.1, 0.24, 0.32, 0.1, 0.24, -0.32, 0.1, 0.1, -0.32, 0.1, 0.1, 0.32, 0.1, -0.19, 0.27, 0.1, -0.19, -0.27, 0.05, -0.19, -0.27, 0.1, -0.19, 0.27, 0.1, -0.19, 0.27, 0.05, -0.19, -0.27, 0.05, -0.19, 0.27, 0.1, 0.1, 0.32, 0.1, 0.05, 0.27, 0.1, -0.19, 0.27, 0.1, -0.24, 0.32, 0.1, 0.1, 0.32, 0.1, -0.19, -0.27, 0.1, -0.24, 0.32, 0.1, -0.19, 0.27, 0.1, -0.19, -0.27, 0.1, -0.24, -0.32, 0.1, -0.24, 0.32, 0.1, 0.05, -0.27, 0.1, -0.24, -0.32, 0.1, -0.19, -0.27, 0.1, 0.05, -0.27, 0.1, 0.1, -0.32, 0.1, -0.24, -0.32, 0.1, 0.05, 0.27, 0.1, 0.1, -0.32, 0.1, 0.05, -0.27, 0.1, 0.05, 0.27, 0.1, 0.1, 0.32, 0.1, 0.1, -0.32, 0.1, 0.05, -0.27, 0.05, -0.19, 0.27, 0.05, 0.05, 0.27, 0.05, 0.05, -0.27, 0.05, -0.19, -0.27, 0.05, -0.19, 0.27, 0.05, -0.19, -0.27, 0.1, 0.05, -0.27, 0.05, 0.05, -0.27, 0.1, -0.19, -0.27, 0.1, -0.19, -0.27, 0.05, 0.05, -0.27, 0.05, 0.05, 0.27, 0.1, -0.19, 0.27, 0.05, -0.19, 0.27, 0.1, 0.05, 0.27, 0.1, 0.05, 0.27, 0.05, -0.19, 0.27, 0.05, 0.05, -0.27, 0.1, 0.05, 0.27, 0.05, 0.05, 0.27, 0.1, 0.05, -0.27, 0.1, 0.05, -0.27, 0.05, 0.05, 0.27, 0.05, -0.175, 0.195, 0.075, -0.175, 0.245, 0.025, -0.175, 0.245, 0.075, -0.175, 0.195, 0.075, -0.175, 0.195, 0.025, -0.175, 0.245, 0.025, 0.0175, 0.195, 0.025, 0.035, 0.245, 0.025, 0.0175, 0.245, 0.025, 0.0175, 0.195, 0.025, 0.035, 0.195, 0.025, 0.035, 0.245, 0.025, 0.035, 0.195, 0.025, 0.035, 0.245, 0.075, 0.035, 0.245, 0.025, 0.035, 0.195, 0.025, 0.035, 0.195, 0.075, 0.035, 0.245, 0.075, -0.1575, 0.195, 0.075, -0.175, 0.245, 0.075, -0.1575, 0.245, 0.075, -0.1575, 0.195, 0.075, -0.175, 0.195, 0.075, -0.175, 0.245, 0.075, 0.0175, -0.225, 0.075, 0.0175, -0.25, 0.05, 0.0175, -0.225, 0.025, -0.1575, 0.245, 0.025, -0.175, 0.245, 0.075, -0.175, 0.245, 0.025, -0.1575, 0.245, 0.025, -0.1575, 0.245, 0.075, -0.175, 0.245, 0.075, 0.035, 0.245, 0.025, 0.0175, 0.245, 0.075, 0.0175, 0.245, 0.025, 0.035, 0.245, 0.025, 0.035, 0.245, 0.075, 0.0175, 0.245, 0.075, 0.0175, 0.245, 0.025, -0.1575, 0.245, 0.075, -0.1575, 0.245, 0.025, 0.0175, 0.245, 0.025, 0.0175, 0.245, 0.075, -0.1575, 0.245, 0.075, -0.175, 0.195, 0.075, -0.175, -0.225, 0.025, -0.175, 0.195, 0.025, -0.175, 0.195, 0.075, -0.175, -0.225, 0.075, -0.175, -0.225, 0.025, -0.1575, 0.195, 0.025, 0.0175, 0.195, 0.075, 0.0175, 0.195, 0.025, -0.1575, 0.195, 0.025, -0.1575, 0.195, 0.075, 0.0175, 0.195, 0.075, 0.035, 0.195, 0.075, 0.0175, 0.245, 0.075, 0.035, 0.245, 0.075, 0.035, 0.195, 0.075, 0.0175, 0.195, 0.075, 0.0175, 0.245, 0.075, 0.0175, 0.195, 0.075, -0.1575, 0.245, 0.075, 0.0175, 0.245, 0.075, 0.0175, 0.195, 0.075, -0.1575, 0.195, 0.075, -0.1575, 0.245, 0.075, -0.175, 0.195, 0.025, -0.1575, 0.245, 0.025, -0.175, 0.245, 0.025, -0.175, 0.195, 0.025, -0.1575, 0.195, 0.025, -0.1575, 0.245, 0.025, -0.1575, 0.195, 0.025, 0.0175, 0.245, 0.025, -0.1575, 0.245, 0.025, -0.1575, 0.195, 0.025, 0.0175, 0.195, 0.025, 0.0175, 0.245, 0.025, 0.035, 0.195, 0.075, 0.0175, -0.225, 0.075, 0.0175, 0.195, 0.075, 0.035, 0.195, 0.075, 0.035, -0.225, 0.075, 0.0175, -0.225, 0.075, -0.175, 0.195, 0.025, -0.1575, -0.225, 0.025, -0.1575, 0.195, 0.025, -0.175, 0.195, 0.025, -0.175, -0.225, 0.025, -0.1575, -0.225, 0.025, 0.0175, 0.195, 0.025, 0.035, -0.225, 0.025, 0.035, 0.195, 0.025, 0.0175, 0.195, 0.025, 0.0175, -0.225, 0.025, 0.035, -0.225, 0.025, -0.1575, 0.195, 0.075, -0.175, -0.225, 0.075, -0.175, 0.195, 0.075, -0.1575, 0.195, 0.075, -0.1575, -0.225, 0.075, -0.175, -0.225, 0.075, 0.035, -0.225, 0.025, 0.035, -0.25, 0.05, 0.035, -0.225, 0.075, -0.1575, -0.225, 0.025, -0.1575, -0.25, 0.05, -0.1575, -0.225, 0.075, -0.1575, -0.25, 0.05, -0.175, -0.225, 0.025, -0.175, -0.25, 0.05, -0.1575, -0.25, 0.05, -0.1575, -0.225, 0.025, -0.175, -0.225, 0.025, 0.0175, -0.25, 0.05, 0.035, -0.225, 0.075, 0.035, -0.25, 0.05, 0.0175, -0.25, 0.05, 0.0175, -0.225, 0.075, 0.035, -0.225, 0.075, 0.035, -0.25, 0.05, 0.0175, -0.225, 0.025, 0.0175, -0.25, 0.05, 0.035, -0.25, 0.05, 0.035, -0.225, 0.025, 0.0175, -0.225, 0.025, -0.175, -0.25, 0.05, -0.1575, -0.225, 0.075, -0.1575, -0.25, 0.05, -0.175, -0.25, 0.05, -0.175, -0.225, 0.075, -0.1575, -0.225, 0.075, -0.175, -0.225, 0.075, -0.175, -0.25, 0.05, -0.175, -0.225, 0.025, -0.1575, 0.195, 0.025, -0.1575, -0.225, 0.075, -0.1575, 0.195, 0.075, -0.1575, 0.195, 0.025, -0.1575, -0.225, 0.025, -0.1575, -0.225, 0.075, 0.0175, 0.195, 0.075, 0.0175, -0.225, 0.025, 0.0175, 0.195, 0.025, 0.0175, 0.195, 0.075, 0.0175, -0.225, 0.075, 0.0175, -0.225, 0.025, 0.035, 0.195, 0.025, 0.035, -0.225, 0.075, 0.035, 0.195, 0.075, 0.035, 0.195, 0.025, 0.035, -0.225, 0.025, 0.035, -0.225, 0.075) -[sub_resource type="Animation" id="Animation_pea1n"] -resource_name = "error_blink" -length = 4.0 -loop_mode = 1 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("LightAnimation/DangerLight:mesh:material:emission_energy_multiplier") -tracks/0/interp = 2 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 1, 3, 4), -"transitions": PackedFloat32Array(0.618, 1, 1.618, 1), -"update": 0, -"values": [0.0, 4.0, 4.0, 0.0] -} - -[sub_resource type="Animation" id="Animation_ugm6b"] -length = 0.001 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("LightAnimation/DangerLight: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] -} -tracks/1/type = "value" -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/path = NodePath("LightAnimation/SuccessLight:mesh:material:emission_energy_multiplier") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [0.0] -} - -[sub_resource type="Animation" id="Animation_njbf4"] -resource_name = "success" -length = 0.5 -step = 0.05 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("LightAnimation/SuccessLight:mesh:material:emission_energy_multiplier") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45), -"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), -"update": 1, -"values": [0.0, 4.0, 0.0, 4.0, 0.0, 4.0, 0.0, 4.0, 0.0, 4.0] -} - -[sub_resource type="AnimationLibrary" id="AnimationLibrary_6ueib"] -_data = { -"RESET": SubResource("Animation_ugm6b"), -"error_blink": SubResource("Animation_pea1n"), -"success": SubResource("Animation_njbf4") -} - [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_21svm"] albedo_color = Color(0, 0, 0, 1) emission_enabled = true @@ -149,6 +81,98 @@ emission_energy_multiplier = 0.0 material = SubResource("StandardMaterial3D_0cc87") size = Vector3(0.08, 0.05, 0.025) +[sub_resource type="Animation" id="Animation_pea1n"] +resource_name = "error_blink" +length = 4.0 +loop_mode = 1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Lights/DangerLight:mesh:material:emission_energy_multiplier") +tracks/0/interp = 2 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1, 3, 4), +"transitions": PackedFloat32Array(0.618, 1, 1.618, 1), +"update": 0, +"values": [0.0, 4.0, 4.0, 0.0] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Lights/SuccessLight:mesh:material:emission_energy_multiplier") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [0.0] +} + +[sub_resource type="Animation" id="Animation_ugm6b"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Lights/DangerLight: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] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Lights/SuccessLight:mesh:material:emission_energy_multiplier") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [0.0] +} + +[sub_resource type="Animation" id="Animation_njbf4"] +resource_name = "success" +length = 0.5 +step = 0.05 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Lights/SuccessLight:mesh:material:emission_energy_multiplier") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 1, +"values": [0.0, 2.0, 0.0, 2.0, 0.0, 2.0, 0.0, 2.0, 0.0, 2.0] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Lights/DangerLight:mesh:material:emission_energy_multiplier") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [0.0] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_6ueib"] +_data = { +"RESET": SubResource("Animation_ugm6b"), +"error_blink": SubResource("Animation_pea1n"), +"success": SubResource("Animation_njbf4") +} + [sub_resource type="BoxShape3D" id="BoxShape3D_6maql"] size = Vector3(0.475, 0.65, 0.2) @@ -161,34 +185,46 @@ bones/1/rotation = Quaternion(-2.98023e-08, 1.19209e-07, -2.11758e-22, 1) [node name="WallSwitch_2" parent="Armature/Skeleton3D" index="0"] surface_material_override/0 = SubResource("StandardMaterial3D_o1wte") +[node name="AnimationPlayer" parent="." index="1"] +unique_name_in_owner = true + [node name="GunkBody" parent="." index="2" node_paths=PackedStringArray("mesh_instance") instance=ExtResource("7_3ib1l")] +unique_name_in_owner = true mask_dim = 64 mesh_instance = NodePath("../Armature/Skeleton3D/WallSwitch_2") [node name="CollisionShape3D" type="CollisionShape3D" parent="GunkBody" index="1"] shape = SubResource("ConcavePolygonShape3D_1xqlp") -[node name="LightAnimation" type="AnimationPlayer" parent="." index="3"] +[node name="Lights" type="Node3D" parent="." index="3"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.17, 0.24, 0.1) + +[node name="DangerLight" type="MeshInstance3D" parent="Lights" index="0"] +mesh = SubResource("BoxMesh_g24yf") +skeleton = NodePath("../..") + +[node name="SuccessLight" type="MeshInstance3D" parent="Lights" index="1"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.06, 0) +mesh = SubResource("BoxMesh_a6hjm") +skeleton = NodePath("../..") + +[node name="LightAnimation" type="AnimationPlayer" parent="Lights" index="2"] +unique_name_in_owner = true +root_node = NodePath("../..") libraries = { "": SubResource("AnimationLibrary_6ueib") } autoplay = "error_blink" -[node name="DangerLight" type="MeshInstance3D" parent="LightAnimation" index="0"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.17, 0.24, 0.1) -mesh = SubResource("BoxMesh_g24yf") -skeleton = NodePath("../..") - -[node name="SuccessLight" type="MeshInstance3D" parent="LightAnimation" index="1"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.17, 0.18, 0.1) -mesh = SubResource("BoxMesh_a6hjm") -skeleton = NodePath("../..") - [node name="Interactive" type="StaticBody3D" parent="." index="4"] +unique_name_in_owner = true collision_layer = 2 collision_mask = 0 script = ExtResource("10_qw6jt") -disabled = true [node name="CollisionShape3D" type="CollisionShape3D" parent="Interactive" index="0"] shape = SubResource("BoxShape3D_6maql") + +[connection signal="animation_finished" from="AnimationPlayer" to="." method="_animation_finished"] +[connection signal="painted" from="GunkBody" to="." method="_on_gunk_body_painted"] +[connection signal="activated" from="Interactive" to="." method="_activate"] diff --git a/src/ui/hud/player_hud.gd b/src/ui/hud/player_hud.gd index 2ed164d..b01ac27 100644 --- a/src/ui/hud/player_hud.gd +++ b/src/ui/hud/player_hud.gd @@ -1,6 +1,6 @@ class_name PlayerHUD extends Control -const TRANSITION_TIME := 0.05 +const TRANSITION_TIME := 0.06 const COLOR_VISIBLE := Color("#ffffffee") const COLOR_DISABLED := Color("#cccccc44") @@ -29,9 +29,9 @@ func _to_invisible(element: Control) -> void: func select_interactive(prop: Interactive) -> void: if prop: - if prop.disabled: - _to_disabled(interact_hud) - else: + if prop.enabled: _to_visible(interact_hud) + else: + _to_disabled(interact_hud) else: _to_invisible(interact_hud) diff --git a/src/world/gunk_body/gunk_body.gd b/src/world/gunk_body/gunk_body.gd index 4fa34f2..e0f785b 100644 --- a/src/world/gunk_body/gunk_body.gd +++ b/src/world/gunk_body/gunk_body.gd @@ -1,6 +1,8 @@ class_name GunkBody extends StaticBody3D ## StaticBody3D with an associated "gunkable" mesh. +signal painted + const CONTINUITY_LIMIT := 128 const BUFFER_LIMIT := 3 const FACE_EPSILON := 0.01 @@ -35,6 +37,15 @@ func _ready() -> void: meshtool.create_from_surface(mesh, 0) +func clear_all() -> void: + mask_control.queue_draw( + func() -> void: + mask_control.draw_rect( + Rect2(0, 0, mask_control.size.x, mask_control.size.y), MASK_COLOR + ) + ) + + ## Get the precise number of gunk pixels cleared from this image. ## ## This will use a cached result unless the mask has been painted since the last calculation. @@ -184,3 +195,4 @@ func _process(_delta: float) -> void: func _on_mask_painted() -> void: _clear_total_dirty = true + painted.emit()