diff --git a/audio/RoboZacSpeaker.wav b/audio/RoboZacSpeaker.wav new file mode 100644 index 0000000..8772203 Binary files /dev/null and b/audio/RoboZacSpeaker.wav differ diff --git a/audio/RoboZacSpeaker.wav.import b/audio/RoboZacSpeaker.wav.import new file mode 100644 index 0000000..5444170 --- /dev/null +++ b/audio/RoboZacSpeaker.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cnfmytsyo8biq" +path="res://.godot/imported/RoboZacSpeaker.wav-f05e9451bbf465ad55d22d78b38e2297.sample" + +[deps] + +source_file="res://audio/RoboZacSpeaker.wav" +dest_files=["res://.godot/imported/RoboZacSpeaker.wav-f05e9451bbf465ad55d22d78b38e2297.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=1 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/images/virtua_hand_left.png b/images/virtua_hand_left.png new file mode 100644 index 0000000..bbecfef Binary files /dev/null and b/images/virtua_hand_left.png differ diff --git a/images/virtua_hand_left.png.import b/images/virtua_hand_left.png.import new file mode 100644 index 0000000..1cde540 --- /dev/null +++ b/images/virtua_hand_left.png.import @@ -0,0 +1,41 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://hjn06y02ragw" +path.s3tc="res://.godot/imported/virtua_hand_left.png-efef97087be89c63cfe333c552ecb2f0.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://images/virtua_hand_left.png" +dest_files=["res://.godot/imported/virtua_hand_left.png-efef97087be89c63cfe333c552ecb2f0.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/scenes/button_panel.gd b/scenes/button_panel.gd new file mode 100644 index 0000000..91237d0 --- /dev/null +++ b/scenes/button_panel.gd @@ -0,0 +1,18 @@ +extends Node3D + +const BUTTON_SCENE = preload("res://scenes/floor_button.tscn") +const BUTTON_COUNT := 10 +const Y_TOP := 8.49 +const Y_BOTTOM := -4.36 + +@onready var _container: Node3D = $FloorButtons + +func _ready() -> void: + var spacing = (Y_TOP - Y_BOTTOM) / float(BUTTON_COUNT - 1) + for i in range(BUTTON_COUNT): + var btn = BUTTON_SCENE.instantiate() + btn.name = "%02d" % i + btn.position = Vector3(0, Y_BOTTOM + i * spacing, 0) + _container.add_child(btn) + var sprite: Sprite3D = btn.get_node("ButtonSprite") + sprite.frame = i diff --git a/scenes/button_panel.gd.uid b/scenes/button_panel.gd.uid new file mode 100644 index 0000000..47a28e0 --- /dev/null +++ b/scenes/button_panel.gd.uid @@ -0,0 +1 @@ +uid://cp2n258uuuou3 diff --git a/scenes/button_panel.tscn b/scenes/button_panel.tscn index fdf00c5..264decd 100644 --- a/scenes/button_panel.tscn +++ b/scenes/button_panel.tscn @@ -1,75 +1,9 @@ [gd_scene format=3 uid="uid://cnjn0vhg1phav"] [ext_resource type="PackedScene" uid="uid://bw1kbbl3n83e8" path="res://scenes/floor_button.tscn" id="1_lff67"] +[ext_resource type="Script" path="res://scenes/button_panel.gd" id="2_bpscr"] [node name="ButtonPanel" type="Node3D" unique_id=1270714626] +script = ExtResource("2_bpscr") [node name="FloorButtons" type="Node3D" parent="." unique_id=1608506954] - -[node name="00" parent="." unique_id=1628684586 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -4.360477, 0) - -[node name="01" parent="." unique_id=1324476691 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.8598118, 0) - -[node name="ButtonSprite" parent="01" index="0" unique_id=1628684586] -frame = 1 - -[node name="02" parent="." unique_id=1143590285 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1.3875825, 0) - -[node name="ButtonSprite" parent="02" index="0" unique_id=1628684586] -frame = 2 - -[node name="03" parent="." unique_id=1467372561 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.03961301, 0) - -[node name="ButtonSprite" parent="03" index="0" unique_id=1628684586] -frame = 3 - -[node name="04" parent="." unique_id=652433944 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.3832786, 0) - -[node name="ButtonSprite" parent="04" index="0" unique_id=1628684586] -frame = 4 - -[node name="05" parent="." unique_id=470439905 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.842876, 0) - -[node name="ButtonSprite" parent="05" index="0" unique_id=1628684586] -frame = 5 - -[node name="06" parent="." unique_id=1293107230 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.1931896, 0) - -[node name="ButtonSprite" parent="06" index="0" unique_id=1628684586] -frame = 6 - -[node name="07" parent="." unique_id=1309697731 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 5.632162, 0) - -[node name="ButtonSprite" parent="07" index="0" unique_id=1628684586] -frame = 7 - -[node name="08" parent="." unique_id=447931949 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 7.0681796, 0) - -[node name="ButtonSprite" parent="08" index="0" unique_id=1628684586] -frame = 8 - -[node name="09" parent="." unique_id=1200513083 instance=ExtResource("1_lff67")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 8.490524, 0) - -[node name="ButtonSprite" parent="09" index="0" unique_id=1628684586] -frame = 9 - -[editable path="00"] -[editable path="01"] -[editable path="02"] -[editable path="03"] -[editable path="04"] -[editable path="05"] -[editable path="06"] -[editable path="07"] -[editable path="08"] -[editable path="09"] diff --git a/scenes/elevator.gd b/scenes/elevator.gd index b76743d..a1db543 100644 --- a/scenes/elevator.gd +++ b/scenes/elevator.gd @@ -5,8 +5,14 @@ const DOOR_CLOSED_X := 0.7 const DOOR_DEFAULT_TIME := 3.0 const DOOR_FAST_CLOSE_TIME := 1.0 const DOOR_REOPEN_TIME := 0.4 +const PULSE_CLOSE_LAG_TOP := 1.0 +const PULSE_CLOSE_LAG_PER_FLOOR := 0.09 +const PULSE_CLOSE_LAG_MIN := 0.2 +const DOORS_NEARLY_OPEN_LEAD := 0.4 var _tween: Tween = null +var _pulse_close_pending: bool = false +var _current_floor: int = EventBus.STARTING_FLOOR func _ready(): $ElevatorDoorRight.position = Vector3(DOOR_CLOSED_X, 0, 0) @@ -16,21 +22,39 @@ func _ready(): EventBus.doors_closed.connect(_on_doors_closed) EventBus.pulse_started.connect(_on_pulse_started) EventBus.pulse_blocked.connect(_on_pulse_blocked) + EventBus.floor_changed.connect(func(f): _current_floor = f) func _on_doors_opened(): + _pulse_close_pending = false _set_door_collision(false) _tween_doors(DOOR_OPEN_X, DOOR_DEFAULT_TIME) + _tween.chain().tween_callback(func(): EventBus.doors_fully_opened.emit()) + get_tree().create_timer(DOOR_DEFAULT_TIME - DOORS_NEARLY_OPEN_LEAD, false).timeout.connect( + func(): EventBus.doors_nearly_opened.emit(), + CONNECT_ONE_SHOT + ) func _on_doors_closed(fast: bool): + _pulse_close_pending = false _set_door_collision(true) _tween_doors(DOOR_CLOSED_X, DOOR_FAST_CLOSE_TIME if fast else DOOR_DEFAULT_TIME) _tween.chain().tween_callback(func(): EventBus.doors_fully_closed.emit()) func _on_pulse_started(duration: float): _set_door_collision(true) - _tween_doors(DOOR_CLOSED_X, duration) + _pulse_close_pending = true + var floors_descended = EventBus.STARTING_FLOOR - _current_floor + var lag = max(PULSE_CLOSE_LAG_MIN, PULSE_CLOSE_LAG_TOP - floors_descended * PULSE_CLOSE_LAG_PER_FLOOR) + get_tree().create_timer(lag, false).timeout.connect( + func(): + if _pulse_close_pending: + _pulse_close_pending = false + _tween_doors(DOOR_CLOSED_X, duration), + CONNECT_ONE_SHOT + ) func _on_pulse_blocked(): + _pulse_close_pending = false _set_door_collision(false) _tween_doors(DOOR_OPEN_X, DOOR_REOPEN_TIME) diff --git a/scenes/elevator_panel.gd b/scenes/elevator_panel.gd index 9b541eb..5c38287 100644 --- a/scenes/elevator_panel.gd +++ b/scenes/elevator_panel.gd @@ -30,7 +30,7 @@ const ROBOT_DELAY_MIN := 1.0 var people_in_elevator := 0 const PERFECT_STUN_DURATION := 1.5 -const PRE_PULSE_DELAY := 3.5 +const DING_TO_DOORS_DELAY := 2.0 func _ready(): _close_button.text = "CLOSE" @@ -41,7 +41,7 @@ func _ready(): _screen.pulse_blocked_perfect.connect(_on_pulse_blocked_perfect) _screen.doors_closing.connect(func(): _on_doors_closing(true)) - EventBus.game_started.connect(_start_floor) + EventBus.game_started.connect(_on_game_started) EventBus.game_lost.connect(func(_reason): $SfxRobotIUnderstand.play()) EventBus.survivor_squeaked_in.connect(_on_survivor_squeaked_in) EventBus.robot_close_warning.connect(_on_robot_close_warning) @@ -59,6 +59,10 @@ func _unhandled_input(event): EventBus.debug_starting_floor = EventBus.STARTING_FLOOR get_tree().reload_current_scene() +func _on_game_started(): + await EventBus.intro_finished + _start_floor() + func _reset_floor_state(): doors_closing_flag = false people_in_elevator = 0 @@ -72,12 +76,18 @@ func _start_floor(): _onboarded = true EventBus.debug_starting_floor = 0 - $SfxOpen.play() $SfxBell.play() if _onboarded and randf() < 0.4: $SfxRobotDeepBreath.play() _reset_floor_state() - EventBus.doors_opened.emit() + get_tree().create_timer(DING_TO_DOORS_DELAY, false).timeout.connect( + func(): + if not is_instance_valid(self) or doors_closing_flag: + return + $SfxOpen.play() + EventBus.doors_opened.emit(), + CONNECT_ONE_SHOT + ) EventBus.people_changed.emit(people_in_elevator, THRESHOLD) EventBus.floor_changed.emit(current_floor) @@ -97,23 +107,13 @@ func _start_floor(): if not _onboarded: _onboarded = true - _screen.show_onboarding("BLOCK\nIN GREEN ZONE") - get_tree().create_timer(3.0, false).timeout.connect( - func(): - if not is_instance_valid(_screen): - return - _screen.end_onboarding() - _screen.launch_pulse(), - CONNECT_ONE_SHOT - ) - else: - get_tree().create_timer(PRE_PULSE_DELAY, false).timeout.connect( - func(): - if not is_instance_valid(self) or doors_closing_flag: - return - _screen.launch_pulse(), - CONNECT_ONE_SHOT - ) + EventBus.doors_nearly_opened.connect( + func(): + if not is_instance_valid(self) or doors_closing_flag: + return + _screen.launch_pulse(), + CONNECT_ONE_SHOT + ) func _on_survivor_squeaked_in(): if not $SfxRobotSoundsDifficult.playing: @@ -126,6 +126,7 @@ func _on_robot_close_warning(): func _on_block_pressed(): if doors_closing_flag: return + EventBus.block_pressed.emit() if _screen.pulse_active: _screen.attempt_block() else: diff --git a/scenes/game.gd b/scenes/game.gd index 62dcfd7..b760fea 100644 --- a/scenes/game.gd +++ b/scenes/game.gd @@ -2,3 +2,4 @@ extends Node3D func _ready() -> void: $ComponentSpawn.survivor_spawn = $World/SurvivorSpawn + $Ambience.finished.connect($Ambience.play) diff --git a/scenes/game.tscn b/scenes/game.tscn index 4c7222b..b2c8d5d 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -17,6 +17,8 @@ [ext_resource type="PackedScene" uid="uid://sgakdvtoblmd" path="res://scenes/title_screen.tscn" id="11_title"] [ext_resource type="AudioStream" uid="uid://b3d6rntbosvc6" path="res://audio/Ambience.wav" id="12_amb"] [ext_resource type="PackedScene" path="res://scenes/pause_menu.tscn" id="12_pause"] +[ext_resource type="AudioStream" uid="uid://cnfmytsyo8biq" path="res://audio/RoboZacSpeaker.wav" id="13_muzak"] +[ext_resource type="Script" uid="uid://deyjxii7yykjv" path="res://scenes/muzak.gd" id="14_muzak_s"] [node name="Game" type="Node3D" unique_id=1456297160] script = ExtResource("1_lbhrr") @@ -66,6 +68,11 @@ stream = ExtResource("12_amb") volume_db = -6.0 autoplay = true +[node name="Muzak" type="AudioStreamPlayer" parent="." unique_id=1100000006] +stream = ExtResource("13_muzak") +volume_db = -10.0 +script = ExtResource("14_muzak_s") + [node name="ComponentSpawn" parent="." unique_id=649225939 instance=ExtResource("9_0tnpc")] [node name="EndScreen" parent="." unique_id=993175156 instance=ExtResource("10_endsc")] diff --git a/scenes/hud.tscn b/scenes/hud.tscn index a912011..30f1905 100644 --- a/scenes/hud.tscn +++ b/scenes/hud.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=3 format=3 uid="uid://cbvi51vvpt7mu"] +[gd_scene load_steps=2 format=3 uid="uid://cbvi51vvpt7mu"] [ext_resource type="Script" uid="uid://fpaw3u5yjtbk" path="res://scenes/hud.gd" id="1_64ctp"] -[ext_resource type="PackedScene" uid="uid://dshaftstrip01" path="res://scenes/shaft_strip.tscn" id="2_shaft"] [node name="HUD" type="MarginContainer" unique_id=769131693] offset_right = 40.0 @@ -31,13 +30,3 @@ text = "FLOOR: 0" [node name="ScoreLabel" type="Label" parent="StatsColumn" unique_id=1595653166] layout_mode = 2 theme_override_font_sizes/font_size = 24 - -[node name="ShaftStrip" parent="." instance=ExtResource("2_shaft")] -top_level = true -anchors_preset = 9 -anchor_bottom = 1.0 -offset_left = 12.0 -offset_top = 12.0 -offset_right = 60.0 -offset_bottom = -12.0 -grow_vertical = 2 diff --git a/scenes/left_hand.gd b/scenes/left_hand.gd new file mode 100644 index 0000000..d89f97b --- /dev/null +++ b/scenes/left_hand.gd @@ -0,0 +1,114 @@ +extends Sprite3D + +const INTRO_DURATION := 1.5 +const ENTRY_OFFSET := 0.5 +const EXIT_OFFSET := 0.5 +const FORWARD_OFFSET := 0.05 +const FINGER_OFFSET_X := 0.0 + +@onready var _button_panel: Node3D = get_parent().get_node("ButtonPanel") + +var _buttons: Array = [] +var _intro_playing := false +var _sprite_half_h := 0.0 + +func _ready() -> void: + visible = false + _sprite_half_h = texture.get_height() * pixel_size * global_transform.basis.get_scale().y * 0.5 + + EventBus.game_started.connect(_play_intro) + EventBus.floor_changed.connect(_on_floor_changed) + +func _ensure_buttons() -> void: + if not _buttons.is_empty(): + return + _buttons = _collect_buttons() + if not _buttons.is_empty(): + _set_all_lit(false) + +func _collect_buttons() -> Array: + var arr: Array = [] + var container = _button_panel.get_node_or_null("FloorButtons") + if container == null or container.get_child_count() == 0: + container = _button_panel + for i in range(10): + var button_name := "%02d" % i + var btn = container.get_node_or_null(button_name) + if btn != null: + arr.append(btn) + return arr + +func _play_intro() -> void: + if _intro_playing: + return + _intro_playing = true + + _ensure_buttons() + if _buttons.size() < 10: + _intro_playing = false + EventBus.intro_finished.emit() + return + + _set_all_lit(false) + + # animate the hand's global_position.y from start to end over a set duration + # each button gets a sibling callback scheduled in *parallel*! + # what fraction of the way from start to end is this button's Y? + # to figure out when to fire callback on set_delay() to stagge.3.r + # this way it is easy to change the duration if we want + + var top_button: Node3D = _buttons[9] + var bottom_button: Node3D = _buttons[0] + var camera = get_viewport().get_camera_3d() + var camera_pos = camera.global_position if camera else Vector3.ZERO + var button_z = top_button.global_position.z + var hand_z = button_z - FORWARD_OFFSET + var z_ratio = (hand_z - camera_pos.z) / (button_z - camera_pos.z) + + var hand_x = camera_pos.x + (top_button.global_position.x - camera_pos.x) * z_ratio + FINGER_OFFSET_X + + var finger_start_y_world = top_button.global_position.y + ENTRY_OFFSET + var finger_end_y_world = bottom_button.global_position.y - EXIT_OFFSET + var finger_start_y = camera_pos.y + (finger_start_y_world - camera_pos.y) * z_ratio + var finger_end_y = camera_pos.y + (finger_end_y_world - camera_pos.y) * z_ratio + + var sprite_start_y = finger_start_y - _sprite_half_h + var sprite_end_y = finger_end_y - _sprite_half_h + + global_position = Vector3(hand_x, sprite_start_y, hand_z) + visible = true + + var tween := create_tween().set_parallel(true) + tween.tween_property(self, "global_position:y", sprite_end_y, INTRO_DURATION) + + for i in range(_buttons.size()): + var button_y_world = _buttons[i].global_position.y + # weird thing: button_y is projected onto the hand's Z-plane to account for perspective + # to keep the fingertip visually aligned with each button at the exact right moment + var button_y_proj = camera_pos.y + (button_y_world - camera_pos.y) * z_ratio + var t_fraction = (finger_start_y - button_y_proj) / (finger_start_y - finger_end_y) + t_fraction = clamp(t_fraction, 0.0, 1.0) + tween.tween_callback(_light_button.bind(i)).set_delay(t_fraction * INTRO_DURATION) + + await tween.finished + visible = false + _intro_playing = false + EventBus.intro_finished.emit() + +func _light_button(index: int) -> void: + var sprite: Sprite3D = _buttons[index].get_node("ButtonSprite") + sprite.frame = index + 10 + +func _set_all_lit(lit: bool) -> void: + for i in range(_buttons.size()): + var sprite: Sprite3D = _buttons[i].get_node("ButtonSprite") + sprite.frame = i + (10 if lit else 0) + +func _on_floor_changed(floor_num: int) -> void: + if floor_num >= EventBus.STARTING_FLOOR: + return + _ensure_buttons() + if floor_num < 0 or floor_num >= _buttons.size(): + return + var sprite: Sprite3D = _buttons[floor_num].get_node("ButtonSprite") + sprite.frame = floor_num diff --git a/scenes/left_hand.gd.uid b/scenes/left_hand.gd.uid new file mode 100644 index 0000000..e0dd484 --- /dev/null +++ b/scenes/left_hand.gd.uid @@ -0,0 +1 @@ +uid://cqi3wsvqno12v diff --git a/scenes/left_hand.tscn b/scenes/left_hand.tscn new file mode 100644 index 0000000..5360e2f --- /dev/null +++ b/scenes/left_hand.tscn @@ -0,0 +1,11 @@ +[gd_scene format=3 uid="uid://cl3hb1nm4xy8t"] + +[ext_resource type="Texture2D" path="res://images/virtua_hand_left.png" id="1_lhand"] +[ext_resource type="Script" path="res://scenes/left_hand.gd" id="2_lhscr"] + +[node name="LeftHand" type="Sprite3D"] +billboard = 1 +texture_filter = 0 +alpha_cut = 1 +texture = ExtResource("1_lhand") +script = ExtResource("2_lhscr") diff --git a/scenes/muzak.gd b/scenes/muzak.gd new file mode 100644 index 0000000..871f116 --- /dev/null +++ b/scenes/muzak.gd @@ -0,0 +1,64 @@ +extends AudioStreamPlayer + +const NORMAL_DB := -10.0 +const DUCKED_DB := -24.0 +const DUCK_IN := 0.15 +const DUCK_OUT := 0.4 +# these paths wwould need to be updated if renamed or moved +const BARK_PATHS := [ + "../CanvasPanel/ElevatorPanel/SfxRobotDeepBreath", + "../CanvasPanel/ElevatorPanel/SfxRobotSoundsDifficult", + "../CanvasPanel/ElevatorPanel/SfxRobotIUnderstand", + "../CanvasPanel/ElevatorPanel/SfxRobotThankYou", +] + +var _barks: Array = [] +var _ducked: bool = false +var _tween: Tween +var _started: bool = false + +func _ready() -> void: + volume_db = NORMAL_DB + finished.connect(play) + for p in BARK_PATHS: + var n := get_node_or_null(p) + if n: + _barks.append(n) + EventBus.game_started.connect(_on_game_started) + EventBus.game_won.connect(_on_game_ended) + EventBus.game_lost.connect(_on_game_lost) + +func _process(_delta: float) -> void: + if not playing: + return + var any := false + for b in _barks: + if b.playing: + any = true + break + if any and not _ducked: + _ducked = true + _to_db(DUCKED_DB, DUCK_IN) + elif not any and _ducked: + _ducked = false + _to_db(NORMAL_DB, DUCK_OUT) + +func _to_db(target: float, time: float) -> void: + if _tween: + _tween.kill() + _tween = create_tween() + _tween.tween_property(self, "volume_db", target, time) + +func _on_game_started() -> void: + if _started: + return + _started = true + _ducked = false + volume_db = NORMAL_DB + play() + +func _on_game_ended() -> void: + stop() + +func _on_game_lost(_reason) -> void: + stop() diff --git a/scenes/muzak.gd.uid b/scenes/muzak.gd.uid new file mode 100644 index 0000000..b2ae58e --- /dev/null +++ b/scenes/muzak.gd.uid @@ -0,0 +1 @@ +uid://deyjxii7yykjv diff --git a/scenes/robot.gd b/scenes/robot.gd index fadf9fe..2924016 100644 --- a/scenes/robot.gd +++ b/scenes/robot.gd @@ -6,7 +6,7 @@ const STALK_ILLUMINATE_DISTANCE := 15.0 var robot_ready: bool = false var robot_win: bool = false var speed: float = 1.0 -var doors_open: bool = false +var doors_open: bool = true var stun_remaining: float = 0.0 var _stalking_close: bool = false diff --git a/scenes/survivor.gd b/scenes/survivor.gd index 9dd2551..679d92e 100644 --- a/scenes/survivor.gd +++ b/scenes/survivor.gd @@ -7,11 +7,16 @@ const CHASE_LONG := preload("res://audio/Chase1.wav") const CHASE_TRACKS := [CHASE_SHORT_1, CHASE_SHORT_2, CHASE_MID, CHASE_LONG] static var _last_chase: AudioStream = null +const SCREAM_OPEN_DB := 0.0 +const SCREAM_CLOSED_DB := -20.0 + var speed: int = 3 var clumsiness: int = 0 var _saved: bool = false +var _in_safety_zone: bool = false @onready var safety_zone: Node = get_node_or_null("/root/Game/World/ElevatorSafeZone") +@onready var _elevator: Node = safety_zone.get_node_or_null("Elevator") if safety_zone else null #func start(xform): @@ -30,16 +35,31 @@ func _ready(): ) func _physics_process(_delta): - velocity.z = -speed + var p: float = _elevator.get_door_close_progress() if _elevator else 0.0 + if _in_safety_zone and not _saved and p > 0.49: + velocity.z = 0 + else: + velocity.z = -speed move_and_slide() + if _elevator: + $ScreamPlayer.volume_db = lerp(SCREAM_OPEN_DB, SCREAM_CLOSED_DB, p) func _on_area_3d_area_entered(area: Area3D) -> void: - if area == safety_zone and not _saved: - _saved = true + if area == safety_zone and not _in_safety_zone: + _in_safety_zone = true var elevator = safety_zone.get_node_or_null("Elevator") - if elevator and elevator.get_door_close_progress() > 0.7: - EventBus.survivor_squeaked_in.emit() + var close_progress: float = elevator.get_door_close_progress() if elevator else 0.0 + if close_progress > 0.49: + return get_tree().create_timer(0.5, false).timeout.connect( - func(): queue_free(), + func(): + if not is_instance_valid(self): + return + var elev = safety_zone.get_node_or_null("Elevator") + var final_progress: float = elev.get_door_close_progress() if elev else 0.0 + if final_progress > 0.49: + return + _saved = true + queue_free(), CONNECT_ONE_SHOT ) diff --git a/scenes/virtua_hand.gd b/scenes/virtua_hand.gd new file mode 100644 index 0000000..76cfed0 --- /dev/null +++ b/scenes/virtua_hand.gd @@ -0,0 +1,25 @@ +extends Sprite3D + +const UP_DURATION := 0.1 +const HOLD_DURATION := 0.05 +const DOWN_DURATION := 0.18 + +var _rest_position: Vector3 +var _tween: Tween + +@onready var _button: Node3D = get_parent().get_node("ElevatorButton") + +func _ready() -> void: + _rest_position = position + EventBus.block_pressed.connect(_animate_press) + +func _animate_press() -> void: + if not is_instance_valid(_button): + return + if _tween and _tween.is_valid(): + _tween.kill() + var press_pos := Vector3(_rest_position.x, _button.position.y, _rest_position.z) + _tween = create_tween() + _tween.tween_property(self, "position", press_pos, UP_DURATION).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT) + _tween.tween_interval(HOLD_DURATION) + _tween.tween_property(self, "position", _rest_position, DOWN_DURATION).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN) diff --git a/scenes/virtua_hand.gd.uid b/scenes/virtua_hand.gd.uid new file mode 100644 index 0000000..7629b6b --- /dev/null +++ b/scenes/virtua_hand.gd.uid @@ -0,0 +1 @@ +uid://by5qr8pax54b1 diff --git a/scenes/virtua_hand.tscn b/scenes/virtua_hand.tscn index b7fae54..ee5014a 100644 --- a/scenes/virtua_hand.tscn +++ b/scenes/virtua_hand.tscn @@ -1,8 +1,10 @@ [gd_scene format=3 uid="uid://bkqwi2yqa0nvg"] [ext_resource type="Texture2D" uid="uid://51p1gbyn5l02" path="res://images/virtua_hand.png" id="1_ikq7c"] +[ext_resource type="Script" path="res://scenes/virtua_hand.gd" id="2_hand_s"] [node name="VirtuaHand" type="Sprite3D" unique_id=1811028460] billboard = 1 texture_filter = 0 texture = ExtResource("1_ikq7c") +script = ExtResource("2_hand_s") diff --git a/scenes/world.tscn b/scenes/world.tscn index 3ffbb46..b44bc82 100644 --- a/scenes/world.tscn +++ b/scenes/world.tscn @@ -7,6 +7,7 @@ [ext_resource type="PackedScene" uid="uid://cwwexawpj46hk" path="res://scenes/elevator_button.tscn" id="5_qfnet"] [ext_resource type="Script" uid="uid://c6v2lhrkeup5i" path="res://scenes/world.gd" id="5_world"] [ext_resource type="PackedScene" uid="uid://cnjn0vhg1phav" path="res://scenes/button_panel.tscn" id="7_i7141"] +[ext_resource type="PackedScene" uid="uid://cl3hb1nm4xy8t" path="res://scenes/left_hand.tscn" id="8_lefth"] [sub_resource type="BoxShape3D" id="BoxShape3D_k0juu"] size = Vector3(3.8, 6.6328125, 0.8612671) @@ -63,12 +64,16 @@ unit_size = 4.0 max_distance = 30.0 [node name="CollisionShape3D" type="CollisionShape3D" parent="ElevatorSafeZone" unique_id=264489586] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.24584353, 2.8164063, -0.56969166) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.24584353, 2.8164063, -0.49238968) shape = SubResource("BoxShape3D_k0juu") [node name="ButtonPanel" parent="ElevatorSafeZone" unique_id=1270714626 instance=ExtResource("7_i7141")] transform = Transform3D(0.3, 0, 0, 0, 0.3, 0, 0, 0, 0.3, 3.4193654, 2.2598982, -0.39259052) +[node name="LeftHand" parent="ElevatorSafeZone" instance=ExtResource("8_lefth")] +transform = Transform3D(3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0) +visible = false + [node name="SurvivorSpawn" type="Marker3D" parent="." unique_id=1095768768] unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.9181392, 13.711893) diff --git a/scripts/event_bus.gd b/scripts/event_bus.gd index 168d13d..c676607 100644 --- a/scripts/event_bus.gd +++ b/scripts/event_bus.gd @@ -15,6 +15,8 @@ signal game_won signal game_lost(reason: String) signal floor_started(survivor_count: int) signal doors_opened +signal doors_nearly_opened +signal doors_fully_opened signal doors_closed(fast: bool) signal doors_fully_closed signal pulse_started(duration: float) @@ -23,5 +25,7 @@ signal robot_stun_requested(duration: float) signal robot_floor_started(delay: float, robot_speed: float) signal robot_close_warning signal survivor_squeaked_in +signal block_pressed +signal intro_finished @warning_ignore_restore("unused_signal")