diff --git a/asset_dev/audio/464839__breviceps__anime-sound-effect-piercing-impact-stabbing.wav b/asset_dev/audio/464839__breviceps__anime-sound-effect-piercing-impact-stabbing.wav new file mode 100644 index 0000000..317a3f8 --- /dev/null +++ b/asset_dev/audio/464839__breviceps__anime-sound-effect-piercing-impact-stabbing.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04d3162ad017f14f69abfeb5f8ec7b23a654cefcd94dd497c294d249170ed108 +size 440656 diff --git a/assets/sound/sfx/player/hit1.wav b/assets/sound/sfx/player/hit1.wav new file mode 100644 index 0000000..5220033 --- /dev/null +++ b/assets/sound/sfx/player/hit1.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7332500bd671ae86db5a23ac930cab00552124604edebfd051426b4b6a88e146 +size 259492 diff --git a/assets/sound/sfx/player/hit1.wav.import b/assets/sound/sfx/player/hit1.wav.import new file mode 100644 index 0000000..4eb30f0 --- /dev/null +++ b/assets/sound/sfx/player/hit1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://gocwud8dxgrj" +path="res://.godot/imported/hit1.wav-53061910ec313d77eab7d39e11bb6d2e.sample" + +[deps] + +source_file="res://assets/sound/sfx/player/hit1.wav" +dest_files=["res://.godot/imported/hit1.wav-53061910ec313d77eab7d39e11bb6d2e.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=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/assets/sound/sfx/player/hit2.wav b/assets/sound/sfx/player/hit2.wav new file mode 100644 index 0000000..a8b2309 --- /dev/null +++ b/assets/sound/sfx/player/hit2.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92e85f962e5611efcda07cfaa62fc29c97b41e510067ad7a292afa156f8b93dc +size 178534 diff --git a/assets/sound/sfx/player/hit2.wav.import b/assets/sound/sfx/player/hit2.wav.import new file mode 100644 index 0000000..c0449f9 --- /dev/null +++ b/assets/sound/sfx/player/hit2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://ixxphtt1fp88" +path="res://.godot/imported/hit2.wav-4dd8ae86a458841903e431693113cba2.sample" + +[deps] + +source_file="res://assets/sound/sfx/player/hit2.wav" +dest_files=["res://.godot/imported/hit2.wav-4dd8ae86a458841903e431693113cba2.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=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/assets/sound/sfx/player/hit3.wav b/assets/sound/sfx/player/hit3.wav new file mode 100644 index 0000000..bdc4bda --- /dev/null +++ b/assets/sound/sfx/player/hit3.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30345841fd4786656b75bb81d0026b22bfae3ec1869938b9883a336ef6a2c127 +size 166750 diff --git a/assets/sound/sfx/player/hit3.wav.import b/assets/sound/sfx/player/hit3.wav.import new file mode 100644 index 0000000..c39edc2 --- /dev/null +++ b/assets/sound/sfx/player/hit3.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://wa0uejal81b" +path="res://.godot/imported/hit3.wav-c5ea520c5ed0d4243944981f59bae603.sample" + +[deps] + +source_file="res://assets/sound/sfx/player/hit3.wav" +dest_files=["res://.godot/imported/hit3.wav-c5ea520c5ed0d4243944981f59bae603.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=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/assets/sprites/dope_explosion/dope_explosion.tres b/assets/sprites/dope_explosion/dope_explosion.tres index 3b48847..79178fc 100644 --- a/assets/sprites/dope_explosion/dope_explosion.tres +++ b/assets/sprites/dope_explosion/dope_explosion.tres @@ -21,38 +21,38 @@ [resource] resource_local_to_scene = true frames = 17 -pause = true -frame_0/texture = ExtResource("1_d0hbp") +one_shot = true +frame_0/texture = ExtResource("16_kbc1f") frame_0/duration = 0.1 -frame_1/texture = ExtResource("2_bjmaj") +frame_1/texture = ExtResource("17_ip4qp") frame_1/duration = 0.1 -frame_2/texture = ExtResource("10_e4v3n") +frame_2/texture = ExtResource("3_21mo1") frame_2/duration = 0.1 -frame_3/texture = ExtResource("11_7y6m6") +frame_3/texture = ExtResource("4_qthhk") frame_3/duration = 0.1 -frame_4/texture = ExtResource("12_702rw") +frame_4/texture = ExtResource("5_0684l") frame_4/duration = 0.1 -frame_5/texture = ExtResource("13_orlo6") +frame_5/texture = ExtResource("6_y6mm3") frame_5/duration = 0.1 -frame_6/texture = ExtResource("14_doi0c") +frame_6/texture = ExtResource("7_4d5db") frame_6/duration = 0.1 -frame_7/texture = ExtResource("15_8snwx") +frame_7/texture = ExtResource("8_icpm3") frame_7/duration = 0.1 -frame_8/texture = ExtResource("16_kbc1f") +frame_8/texture = ExtResource("9_mv3mc") frame_8/duration = 0.1 -frame_9/texture = ExtResource("17_ip4qp") +frame_9/texture = ExtResource("1_d0hbp") frame_9/duration = 0.1 -frame_10/texture = ExtResource("3_21mo1") +frame_10/texture = ExtResource("2_bjmaj") frame_10/duration = 0.1 -frame_11/texture = ExtResource("4_qthhk") +frame_11/texture = ExtResource("10_e4v3n") frame_11/duration = 0.1 -frame_12/texture = ExtResource("5_0684l") +frame_12/texture = ExtResource("11_7y6m6") frame_12/duration = 0.1 -frame_13/texture = ExtResource("6_y6mm3") +frame_13/texture = ExtResource("12_702rw") frame_13/duration = 0.1 -frame_14/texture = ExtResource("7_4d5db") +frame_14/texture = ExtResource("13_orlo6") frame_14/duration = 0.1 -frame_15/texture = ExtResource("8_icpm3") +frame_15/texture = ExtResource("14_doi0c") frame_15/duration = 0.1 -frame_16/texture = ExtResource("9_mv3mc") +frame_16/texture = ExtResource("15_8snwx") frame_16/duration = 0.1 diff --git a/levels/debug_level/debug_level.tscn b/levels/debug_level/debug_level.tscn index e740d14..695585e 100644 --- a/levels/debug_level/debug_level.tscn +++ b/levels/debug_level/debug_level.tscn @@ -620,7 +620,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 16, 0) shape = SubResource("CylinderShape3D_f1rqf") [node name="GravityHalo" type="Node3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 73.1897, 78.0923, 345.794) +transform = Transform3D(0.688355, -0.285755, 0.666717, 0, 0.919135, 0.393942, -0.725374, -0.271172, 0.632691, 73.19, 90.937, 345.794) [node name="CSGCombiner3D" type="CSGCombiner3D" parent="GravityHalo"] use_collision = true diff --git a/src/game/game.tscn b/src/game/game.tscn index 5425d05..5340ea8 100644 --- a/src/game/game.tscn +++ b/src/game/game.tscn @@ -163,7 +163,6 @@ libraries = { [node name="Rumbler" type="Control" parent="RootControl"] unique_name_in_owner = true -process_mode = 1 layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -174,7 +173,6 @@ script = ExtResource("3_3vfdb") [node name="ViewportContainer" type="SubViewportContainer" parent="RootControl/Rumbler"] unique_name_in_owner = true -process_mode = 3 layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 diff --git a/src/player/shot_setup/death_sequence.gd b/src/player/shot_setup/death_sequence.gd new file mode 100644 index 0000000..41b3f36 --- /dev/null +++ b/src/player/shot_setup/death_sequence.gd @@ -0,0 +1,10 @@ +extends Node3D + +@export var screen_shake_intensity := 10.0 +@export var screen_shake_length := 0.5 + +@onready var game: Game = get_tree().get_first_node_in_group(Game.group) + + +func screen_shake() -> void: + game.viewport.screen_shake(screen_shake_intensity, screen_shake_length) diff --git a/src/player/shot_setup/explosion.gd b/src/player/shot_setup/explosion.gd new file mode 100644 index 0000000..5b0dc05 --- /dev/null +++ b/src/player/shot_setup/explosion.gd @@ -0,0 +1,14 @@ +extends MeshInstance3D + +var texture: AnimatedTexture + + +func _ready() -> void: + var quad: QuadMesh = mesh + var mat: StandardMaterial3D = quad.material + texture = mat.albedo_texture + + +func play() -> void: + texture.current_frame = 0 + texture.pause = false diff --git a/src/player/shot_setup/shot_setup.gd b/src/player/shot_setup/shot_setup.gd index 89b2bdb..30ec3ac 100644 --- a/src/player/shot_setup/shot_setup.gd +++ b/src/player/shot_setup/shot_setup.gd @@ -10,6 +10,7 @@ enum Phase { SHOT, SHOT_RESET, FINISHED, + DEAD, } const PITCH_MIN := deg_to_rad(-60.0) @@ -140,6 +141,8 @@ var _tracking_camera: OrbitalCamera @onready var shot_animation: AnimationPlayer = %ShotAnimation @onready var shot_sfx: ShotSFX = %ShotSFX +@onready var death_animation: AnimationPlayer = %DeathAnimation + @onready var arrow: Node3D = %Arrow @onready var arrow_pivot: Node3D = %ArrowPivot @onready var arrow_animation: AnimationPlayer = %ArrowAnimation @@ -171,21 +174,32 @@ static var scene := preload("res://src/player/shot_setup/shot_setup.tscn") func _ready() -> void: - # Create & set up HUD - hud = ShotHUD.create(player) - world.ui.add_player_hud(hud) - ball_type = initial_ball - club_type = initial_club_type - character.set_color(player.color) + if player: + player.on_life_changed.connect(_on_life_changed) - # Set up player 3D label - player_label.text = player.name - player_label.modulate = player.color - player_label.outline_modulate = ColorTools.get_bg_color(player.color) + # Create & set up HUD + hud = ShotHUD.create(player) + world.ui.add_player_hud(hud) + ball_type = initial_ball + club_type = initial_club_type + character.set_color(player.color) + + # Set up player 3D label + player_label.text = player.name + player_label.modulate = player.color + player_label.outline_modulate = ColorTools.get_bg_color(player.color) _on_phase_change(phase) +func _on_tree_exiting() -> void: + if is_instance_valid(_tracking_camera): + _tracking_camera.queue_free() + if is_instance_valid(_free_camera): + _free_camera.queue_free() + hud.queue_free() + + func _set_camera_distance(value: float) -> void: var tween := get_tree().create_tween() tween.tween_property(zoom, "position:z", value, ZOOM_LENGTH).set_trans(Tween.TRANS_SINE) @@ -390,6 +404,20 @@ func end_shot_track() -> void: phase = Phase.FINISHED +func start_death() -> void: + print_debug("starting death sequence") + world.ui.play_death_sequence() + death_animation.play("death") + get_tree().paused = true + + +func finish_death() -> void: + print_debug("finishing death sequence") + get_tree().paused = false + player.die() + queue_free() + + func _set_club_type(new_club_type: Club.Type) -> void: if new_club_type == club_type: return @@ -597,6 +625,8 @@ func _process(delta: float) -> void: if _restart_queued: _restart_queued = false phase = Phase.AIM + Phase.DEAD: + start_death() func _on_ball_sleeping_state_changed() -> void: @@ -639,6 +669,13 @@ func _on_hitbox_ball_collision(ball: GameBall) -> void: ball.apply_central_impulse(explosion_impulse) +func _on_life_changed(new_value: float) -> void: + if new_value <= 0: + # No action is taken till the next process phase + # This is because we need to be in the main thread to run the death sequence! + phase = Phase.DEAD + + func _on_reset_prompt_timer_timeout() -> void: reset_enabled = true diff --git a/src/player/shot_setup/shot_setup.tscn b/src/player/shot_setup/shot_setup.tscn index 69aa110..d8e9c68 100644 --- a/src/player/shot_setup/shot_setup.tscn +++ b/src/player/shot_setup/shot_setup.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=34 format=3 uid="uid://cy7t2tc4y3b4"] +[gd_scene load_steps=42 format=3 uid="uid://cy7t2tc4y3b4"] [ext_resource type="Script" path="res://src/player/shot_setup/shot_setup.gd" id="1_r6ei4"] [ext_resource type="Script" path="res://src/player/shot_setup/ball_point.gd" id="2_e6i3g"] @@ -15,7 +15,11 @@ [ext_resource type="Script" path="res://src/player/shot_setup/hitbox.gd" id="7_uh8kn"] [ext_resource type="Texture2D" uid="uid://2yoipvd107t1" path="res://assets/sprites/dope_explosion/dope_explosion.tres" id="8_5ghmo"] [ext_resource type="FontFile" uid="uid://dsa0oh7c0h4pu" path="res://assets/fonts/Racing_Sans_One/RacingSansOne-Regular.ttf" id="9_y5iv1"] +[ext_resource type="Script" path="res://src/player/shot_setup/death_sequence.gd" id="10_vakjm"] +[ext_resource type="AudioStream" uid="uid://ixxphtt1fp88" path="res://assets/sound/sfx/player/hit2.wav" id="11_i5g55"] +[ext_resource type="AudioStream" uid="uid://wa0uejal81b" path="res://assets/sound/sfx/player/hit3.wav" id="12_0losp"] [ext_resource type="AudioStream" uid="uid://cvsv02inxvsyw" path="res://assets/sound/sfx/player/explosion.mp3" id="15_fn6g1"] +[ext_resource type="Script" path="res://src/player/shot_setup/explosion.gd" id="15_ytrxd"] [sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_gh0gi"] random_pitch = 1.1 @@ -34,6 +38,129 @@ random_pitch = 1.1 streams_count = 1 stream_0/stream = ExtResource("7_niyj5") +[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_7seum"] +playback_mode = 1 +random_pitch = 1.1 +streams_count = 2 +stream_0/stream = ExtResource("11_i5g55") +stream_1/stream = ExtResource("12_0losp") + +[sub_resource type="Animation" id="Animation_dlkgq"] +resource_name = "death" +length = 8.0 +tracks/0/type = "method" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Camera3D1") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(1.1), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [], +"method": &"make_current" +}] +} +tracks/1/type = "method" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Camera3D2") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(2.2), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [], +"method": &"make_current" +}] +} +tracks/2/type = "method" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Camera3D3") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(3.3), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [], +"method": &"make_current" +}] +} +tracks/3/type = "method" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("%PlayerPivot/..") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(8), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [], +"method": &"finish_death" +}] +} +tracks/4/type = "method" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath(".") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0, 1.1, 2.2, 3.3), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"values": [{ +"args": [], +"method": &"screen_shake" +}, { +"args": [], +"method": &"screen_shake" +}, { +"args": [], +"method": &"screen_shake" +}, { +"args": [], +"method": &"screen_shake" +}] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("AudioStreamPlayer3D:playing") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} + +[sub_resource type="Animation" id="Animation_ll5da"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("AudioStreamPlayer3D:playing") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_kc803"] +_data = { +"RESET": SubResource("Animation_ll5da"), +"death": SubResource("Animation_dlkgq") +} + [sub_resource type="Animation" id="Animation_ug2a7"] length = 0.001 tracks/0/type = "value" @@ -222,37 +349,25 @@ length = 0.001 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("ExplosionMesh:mesh:material:albedo_texture:pause") +tracks/0/path = NodePath("ExplosionMesh:visible") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [true] +"values": [false] } tracks/1/type = "value" tracks/1/imported = false -tracks/1/enabled = true -tracks/1/path = NodePath("ExplosionMesh:visible") +tracks/1/enabled = false +tracks/1/path = NodePath("ExplosionMesh/ExplosionSFXPlayer:playing") tracks/1/interp = 1 tracks/1/loop_wrap = true tracks/1/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, -"values": [false] -} -tracks/2/type = "value" -tracks/2/imported = false -tracks/2/enabled = false -tracks/2/path = NodePath("ExplosionMesh/ExplosionSFXPlayer:playing") -tracks/2/interp = 1 -tracks/2/loop_wrap = true -tracks/2/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 1, "values": [true] } @@ -262,38 +377,40 @@ length = 1.618 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("ExplosionMesh:mesh:material:albedo_texture:pause") +tracks/0/path = NodePath("ExplosionMesh:visible") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0, 1.618), "transitions": PackedFloat32Array(1, 1), "update": 1, -"values": [false, true] +"values": [true, false] } tracks/1/type = "value" tracks/1/imported = false tracks/1/enabled = true -tracks/1/path = NodePath("ExplosionMesh:visible") +tracks/1/path = NodePath("ExplosionMesh/ExplosionSFXPlayer:playing") tracks/1/interp = 1 tracks/1/loop_wrap = true tracks/1/keys = { -"times": PackedFloat32Array(0, 1.618), -"transitions": PackedFloat32Array(1, 1), +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), "update": 1, -"values": [true, false] +"values": [true] } -tracks/2/type = "value" +tracks/2/type = "method" tracks/2/imported = false tracks/2/enabled = true -tracks/2/path = NodePath("ExplosionMesh/ExplosionSFXPlayer:playing") +tracks/2/path = NodePath("ExplosionMesh") tracks/2/interp = 1 tracks/2/loop_wrap = true tracks/2/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), -"update": 1, -"values": [true] +"values": [{ +"args": [], +"method": &"play" +}] } [sub_resource type="AnimationLibrary" id="AnimationLibrary_dg262"] @@ -345,6 +462,33 @@ transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -0.555 [node name="DemoCamera" type="Camera3D" parent="PlayerPivot" groups=["DemoCamera"]] transform = Transform3D(-0.311543, -0.0687373, 0.947743, 0, 0.99738, 0.0723374, -0.950232, 0.0225362, -0.310727, 0.845792, 0.706621, -0.383459) +[node name="DeathSequence" type="Node3D" parent="PlayerPivot"] +process_mode = 3 +script = ExtResource("10_vakjm") + +[node name="AudioStreamPlayer3D" type="AudioStreamPlayer" parent="PlayerPivot/DeathSequence"] +stream = SubResource("AudioStreamRandomizer_7seum") +volume_db = -4.0 +bus = &"SFX" + +[node name="DeathAnimation" type="AnimationPlayer" parent="PlayerPivot/DeathSequence"] +unique_name_in_owner = true +libraries = { +"": SubResource("AnimationLibrary_kc803") +} + +[node name="Camera3D1" type="Camera3D" parent="PlayerPivot/DeathSequence"] +transform = Transform3D(-0.00257592, 0.0226116, 0.999741, 0, 0.999744, -0.0226116, -0.999997, -5.82457e-05, -0.00257526, 1.6905, 0.831287, 0.0668699) +far = 8192.0 + +[node name="Camera3D2" type="Camera3D" parent="PlayerPivot/DeathSequence"] +transform = Transform3D(0.864734, -0.175509, 0.470564, 0, 0.936951, 0.349461, -0.502229, -0.302191, 0.810214, 0.386249, 1.16999, 0.906541) +far = 8192.0 + +[node name="Camera3D3" type="Camera3D" parent="PlayerPivot/DeathSequence"] +transform = Transform3D(0.00178714, 0.559853, 0.82859, 0, 0.828592, -0.559853, -0.999998, 0.00100054, 0.00148081, 0.334412, 0.629604, 0.136928) +far = 8192.0 + [node name="Direction" type="Node3D" parent="."] unique_name_in_owner = true @@ -466,6 +610,7 @@ shape = SubResource("SphereShape3D_xvvdi") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) visible = false mesh = SubResource("QuadMesh_t34ji") +script = ExtResource("15_ytrxd") [node name="ExplosionAnimation" type="AnimationPlayer" parent="ExplosionMesh"] unique_name_in_owner = true @@ -500,6 +645,7 @@ line_spacing = -16.0 autowrap_mode = 2 width = 120.0 +[connection signal="tree_exiting" from="." to="." method="_on_tree_exiting"] [connection signal="ball_changed" from="BallPoint" to="." method="_on_game_ball_changed"] [connection signal="timeout" from="DownswingTimer" to="." method="finish_downswing"] [connection signal="timeout" from="BallReturnTimer" to="." method="_on_ball_return_timer_timeout"] diff --git a/src/player/world_player.gd b/src/player/world_player.gd index 0b6df77..e1047a7 100644 --- a/src/player/world_player.gd +++ b/src/player/world_player.gd @@ -3,6 +3,7 @@ class_name WorldPlayer extends Resource signal on_life_changed(new_value: float) signal on_balls_changed(type: GameBall.Type, new_value: int) +signal on_death(player: WorldPlayer) @export_range(0, 100) var life: float = 100.0: set(value): @@ -89,6 +90,10 @@ func prev_ball(type: GameBall.Type) -> GameBall.Type: return keys[j] +func die() -> void: + on_death.emit(self) + + ## Create a debug player instance static func create_debug() -> WorldPlayer: var instance := WorldPlayer.new() diff --git a/src/shaders/desaturate.gdshader b/src/shaders/desaturate.gdshader new file mode 100644 index 0000000..73f08f4 --- /dev/null +++ b/src/shaders/desaturate.gdshader @@ -0,0 +1,14 @@ +shader_type canvas_item; + +// Regular saturation is between 0 and 1 +// But I'm gonna leave this unbounded for fun because you can also supersaturate & anti-saturate o_O +uniform float saturation; + +uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest; + +void fragment() { + vec4 source = texture(screen_texture, SCREEN_UV); + + COLOR.rgb = mix(vec3(dot(source.rgb, vec3(0.299, 0.587, 0.114))), source.rgb, saturation); + COLOR.a = source.a; +} \ No newline at end of file diff --git a/src/ui/camera/orbital_camera/orbital_camera.gd b/src/ui/camera/orbital_camera/orbital_camera.gd index c942572..193c3a4 100644 --- a/src/ui/camera/orbital_camera/orbital_camera.gd +++ b/src/ui/camera/orbital_camera/orbital_camera.gd @@ -21,7 +21,7 @@ static var scene := preload("res://src/ui/camera/orbital_camera/orbital_camera.t func _physics_process(delta: float) -> void: var up := Vector3.UP # If target is a game ball, realign to gravity - if target is GameBall: + if is_instance_valid(target) and target is GameBall: up = -(target as GameBall).current_gravity.normalized() var right := up.cross(global_basis.z).normalized() var forward := right.cross(up).normalized() diff --git a/src/ui/game_viewport_container.gd b/src/ui/game_viewport_container.gd index c242a8a..ef79160 100644 --- a/src/ui/game_viewport_container.gd +++ b/src/ui/game_viewport_container.gd @@ -16,7 +16,7 @@ func screen_shake(intensity: float, duration: float = 0.2) -> void: if not Game.settings.enable_screen_shake: return - var tween := get_tree().create_tween() + var tween := create_tween() rumbler.intensity = intensity tween.tween_property(rumbler, "intensity", 0.0, duration).set_trans(Tween.TRANS_CUBIC) tween.tween_callback(_reset_position) diff --git a/src/ui/main_theme.tres b/src/ui/main_theme.tres index 86a173b..5be5e9f 100644 --- a/src/ui/main_theme.tres +++ b/src/ui/main_theme.tres @@ -81,6 +81,10 @@ ClubSelectLabelDisabled/colors/font_outline_color = Color(0.2, 0.2, 0.2, 1) ClubSelectLabelDisabled/colors/font_shadow_color = Color(0.2, 0.2, 0.2, 1) ClubSelectLabelDisabled/font_sizes/font_size = 84 ClubSelectLabelDisabled/fonts/font = ExtResource("1_3rv2b") +DeathScreenLabel/base_type = &"RichTextLabel" +DeathScreenLabel/colors/default_color = Color(0.611765, 0.133333, 0.133333, 1) +DeathScreenLabel/colors/font_outline_color = Color(0, 0, 0, 1) +DeathScreenLabel/fonts/normal_font = ExtResource("1_eha6a") HeaderLarge/constants/outline_size = 8 HeaderLarge/font_sizes/font_size = 28 HeaderMedium/constants/outline_size = 6 diff --git a/src/ui/shot_hud/death_alert.tscn b/src/ui/shot_hud/death_alert.tscn new file mode 100644 index 0000000..3654cc2 --- /dev/null +++ b/src/ui/shot_hud/death_alert.tscn @@ -0,0 +1,266 @@ +[gd_scene load_steps=12 format=3 uid="uid://biokiug3e0ipk"] + +[ext_resource type="Shader" path="res://src/shaders/desaturate.gdshader" id="1_0nlhb"] +[ext_resource type="Script" path="res://src/ui/decorations/text_effects/typewriter/typewriter_label.gd" id="2_w3oh2"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_m6b5h"] +shader = ExtResource("1_0nlhb") +shader_parameter/saturation = 1.0 + +[sub_resource type="Gradient" id="Gradient_d8y1q"] +offsets = PackedFloat32Array(0.248473, 1) +colors = PackedColorArray(0, 0, 0, 0.8, 0, 0, 0, 0) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_7xfnx"] +gradient = SubResource("Gradient_d8y1q") +width = 256 +fill = 1 +fill_from = Vector2(0.5, 0.5) +fill_to = Vector2(1, 0.5) +metadata/_snap_enabled = true + +[sub_resource type="Curve" id="Curve_ndj60"] +min_value = -16.0 +max_value = 16.0 +_data = [Vector2(0, 0), 0.0, 44.2396, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="Curve" id="Curve_nld14"] +min_value = -100.0 +max_value = 0.0 +_data = [Vector2(0, -100), 0.0, 451.099, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="CurveXYZTexture" id="CurveXYZTexture_bixq7"] +curve_x = SubResource("Curve_ndj60") +curve_y = SubResource("Curve_nld14") + +[sub_resource type="Animation" id="Animation_l6yhv"] +resource_name = "display" +length = 8.0 +loop_mode = 1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("AlertContainer/GradientBG:modulate") +tracks/0/interp = 2 +tracks/0/loop_wrap = false +tracks/0/keys = { +"times": PackedFloat32Array(0, 3), +"transitions": PackedFloat32Array(1.618, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("AlertContainer/Text/Subheading:modulate") +tracks/1/interp = 2 +tracks/1/loop_wrap = false +tracks/1/keys = { +"times": PackedFloat32Array(3, 8), +"transitions": PackedFloat32Array(1.618, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath(".:modulate") +tracks/2/interp = 2 +tracks/2/loop_wrap = false +tracks/2/keys = { +"times": PackedFloat32Array(7.5, 8), +"transitions": PackedFloat32Array(1.618, 1), +"update": 0, +"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)] +} +tracks/3/type = "method" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("AlertContainer/Text/TypewriterLabel") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [], +"method": &"restart" +}] +} +tracks/4/type = "method" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath(".") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(8), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [], +"method": &"queue_free" +}] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("Desaturator:material:shader_parameter/saturation") +tracks/5/interp = 1 +tracks/5/loop_wrap = false +tracks/5/keys = { +"times": PackedFloat32Array(0, 6, 7.5, 8), +"transitions": PackedFloat32Array(1.618, 1, 1, 1), +"update": 0, +"values": [1.0, 0.0, 0.0, 1.0] +} + +[sub_resource type="Animation" id="Animation_4aged"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("AlertContainer/GradientBG:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("AlertContainer/Text/Subheading:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath(".:modulate") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("Desaturator:material:shader_parameter/saturation") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [1.0] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_em7mg"] +_data = { +"RESET": SubResource("Animation_4aged"), +"display": SubResource("Animation_l6yhv") +} + +[node name="DeathAlert" type="Control"] +custom_minimum_size = Vector2(0, 300) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Desaturator" type="ColorRect" parent="."] +material = SubResource("ShaderMaterial_m6b5h") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="AlertContainer" type="Control" parent="."] +custom_minimum_size = Vector2(0, 300) +layout_mode = 1 +anchors_preset = 14 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="GradientBG" type="TextureRect" parent="AlertContainer"] +texture_filter = 6 +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -128.0 +offset_right = 128.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = SubResource("GradientTexture2D_7xfnx") + +[node name="Text" type="Control" parent="AlertContainer"] +texture_filter = 6 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="TypewriterLabel" type="RichTextLabel" parent="AlertContainer/Text"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_type_variation = &"DeathScreenLabel" +theme_override_constants/outline_size = 16 +theme_override_font_sizes/normal_font_size = 256 +bbcode_enabled = true +text = "[center][wave][type speed=10 delay=2.7 factor=4.0]got em[/type][/wave][/center]" +scroll_active = false +autowrap_mode = 0 +script = ExtResource("2_w3oh2") +translation_curve = SubResource("CurveXYZTexture_bixq7") + +[node name="Subheading" type="RichTextLabel" parent="AlertContainer/Text"] +custom_minimum_size = Vector2(220, 40) +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -110.0 +offset_top = -40.0 +offset_right = 110.0 +offset_bottom = -50.0 +grow_horizontal = 2 +grow_vertical = 0 +theme_type_variation = &"DeathScreenLabel" +theme_override_constants/outline_size = 4 +theme_override_font_sizes/normal_font_size = 36 +bbcode_enabled = true +text = "[center][wave]lmao[/wave][/center]" + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_em7mg") +} +autoplay = "display" diff --git a/src/ui/shot_hud/nice_alert.tscn b/src/ui/shot_hud/nice_alert.tscn index 9bb3b07..cf427d0 100644 --- a/src/ui/shot_hud/nice_alert.tscn +++ b/src/ui/shot_hud/nice_alert.tscn @@ -445,8 +445,10 @@ _data = { } [node name="NiceAlert" type="Control"] +texture_filter = 5 custom_minimum_size = Vector2(0, 300) layout_mode = 3 +anchors_preset = 14 anchor_top = 0.5 anchor_right = 1.0 anchor_bottom = 0.5 diff --git a/src/ui/shot_hud/shot_hud.gd b/src/ui/shot_hud/shot_hud.gd index 27ceb20..77a044b 100644 --- a/src/ui/shot_hud/shot_hud.gd +++ b/src/ui/shot_hud/shot_hud.gd @@ -20,7 +20,7 @@ var player: WorldPlayer @onready var _curve_animation: AnimationPlayer = %CurveAnimation @onready var _power_animation: AnimationPlayer = %PowerAnimation -@onready var _nice_container: Control = %NiceContainer +@onready var _alert_container: Control = %AlertContainer @onready var _wasted_animation: AnimationPlayer = %WastedAnimation @onready var _club_selector_animation: AnimationPlayer = %ClubSelectorAnimation @@ -90,7 +90,7 @@ func gauge_flourish() -> void: func play_nice_animation() -> void: - _nice_container.add_child(NICE_ALERT_SCENE.instantiate()) + _alert_container.add_child(NICE_ALERT_SCENE.instantiate()) func play_wasted_animation() -> void: diff --git a/src/ui/shot_hud/shot_hud.tscn b/src/ui/shot_hud/shot_hud.tscn index 9158803..8ae512f 100644 --- a/src/ui/shot_hud/shot_hud.tscn +++ b/src/ui/shot_hud/shot_hud.tscn @@ -547,15 +547,6 @@ grow_vertical = 2 mouse_filter = 2 script = ExtResource("1_x5b4c") -[node name="NiceContainer" type="Control" parent="."] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - [node name="WastedFeedback" type="RichTextLabel" parent="."] visible = false custom_minimum_size = Vector2(1400, 0) @@ -565,10 +556,10 @@ anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -offset_left = -350.0 -offset_top = -66.0 -offset_right = 350.0 -offset_bottom = 66.0 +offset_left = -700.0 +offset_top = -136.5 +offset_right = 700.0 +offset_bottom = 136.5 grow_horizontal = 2 grow_vertical = 2 pivot_offset = Vector2(100, 115) @@ -818,3 +809,12 @@ unique_name_in_owner = true libraries = { "": SubResource("AnimationLibrary_o70c6") } + +[node name="AlertContainer" type="Control" parent="."] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 diff --git a/src/ui/world_ui.gd b/src/ui/world_ui.gd index aa48f13..d3ce947 100644 --- a/src/ui/world_ui.gd +++ b/src/ui/world_ui.gd @@ -1,13 +1,17 @@ class_name WorldUI extends Control ## Container & accessor for the world UI. +const DEATH_ALERT_SCENE := preload("res://src/ui/shot_hud/death_alert.tscn") + const DEMO_CAMERA_GROUP := "DemoCamera" @export var pause_scene := preload("res://src/ui/menus/pause_menu/pause_menu.tscn") var _prev_camera: Camera3D +var _pre_death_camera: Camera3D @onready var hud_container: Control = %HUDContainer +@onready var effect_container: Control = %EffectContainer @onready var pause_container: Control = %PauseContainer @@ -21,6 +25,19 @@ func add_player_hud(hud: ShotHUD) -> void: hud_container.add_child(hud) +func play_death_sequence() -> void: + _pre_death_camera = get_viewport().get_camera_3d() + hud_container.hide() + var alert := DEATH_ALERT_SCENE.instantiate() + alert.tree_exiting.connect(_finish_death_sequence) + effect_container.add_child(alert) + + +func _finish_death_sequence() -> void: + hud_container.show() + _pre_death_camera.make_current() + + func pause() -> void: # Switch to demo cam, if there is one. var democams: Array[Node] = get_tree().get_nodes_in_group(DEMO_CAMERA_GROUP) diff --git a/src/world/play_manager/play_manager.gd b/src/world/play_manager/play_manager.gd index d6ee4f4..d1e33c5 100644 --- a/src/world/play_manager/play_manager.gd +++ b/src/world/play_manager/play_manager.gd @@ -8,6 +8,7 @@ class_name PlayManager extends Resource func initialize() -> void: for player: WorldPlayer in players: player.shot_setup.finished.connect(_on_turn_finished) + player.on_death.connect(_on_player_death) on_initialization() @@ -21,3 +22,11 @@ func on_turn_finished(_shot_setup: ShotSetup) -> void: func _on_turn_finished(shot_setup: ShotSetup) -> void: on_turn_finished(shot_setup) + + +func on_player_death(_player: WorldPlayer) -> void: + pass # Implemented in derived type + + +func _on_player_death(player: WorldPlayer) -> void: + on_player_death(player) diff --git a/src/world/play_manager/round_robin_manager.gd b/src/world/play_manager/round_robin_manager.gd index 7a224b4..c27f2bd 100644 --- a/src/world/play_manager/round_robin_manager.gd +++ b/src/world/play_manager/round_robin_manager.gd @@ -11,3 +11,18 @@ func on_turn_finished(source: ShotSetup) -> void: print_debug("Shot finished for ", source.player.name) players.push_back(players.pop_front()) players[0].shot_setup.queue_restart() + + +func on_player_death(player: WorldPlayer) -> void: + # If the dying player was active, we need to finish it and activate the next player's turn + if player.shot_setup.is_active(): + on_turn_finished(player.shot_setup) + + players.erase(player) + print("Player 0 phase: ", ShotSetup.Phase.keys()[players[0].shot_setup.phase]) + if len(players) == 1: + on_win_condition() + + +func on_win_condition() -> void: + pass # TODO diff --git a/src/world/world.tscn b/src/world/world.tscn index cedd33b..3b9ba64 100644 --- a/src/world/world.tscn +++ b/src/world/world.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://cwnwcd8kushl3"] +[gd_scene load_steps=10 format=3 uid="uid://cwnwcd8kushl3"] [ext_resource type="Script" path="res://src/world/world.gd" id="1_ybjyx"] [ext_resource type="PackedScene" uid="uid://bm2o3mex10v11" path="res://levels/debug_level/debug_level.tscn" id="2_0xu5a"] @@ -6,10 +6,22 @@ [ext_resource type="Script" path="res://src/ui/world_ui.gd" id="2_imewa"] [ext_resource type="Resource" uid="uid://crock3revdn73" path="res://src/player/debug_player.tres" id="3_pyw81"] [ext_resource type="Script" path="res://src/world/play_manager/round_robin_manager.gd" id="5_h6mje"] +[ext_resource type="Resource" uid="uid://c1pnqsddvey3m" path="res://src/equipment/clubs/drivers/debug_driver.tres" id="5_u5ok3"] + +[sub_resource type="Resource" id="Resource_njyo4"] +script = ExtResource("2_e743i") +life = 4.0 +name = "Gfolfer2" +color = Color(1, 0.439216, 0.439216, 1) +driver = ExtResource("5_u5ok3") +_balls = { +1: -1, +2: -1 +} [sub_resource type="Resource" id="Resource_rdjhi"] script = ExtResource("5_h6mje") -players = Array[ExtResource("2_e743i")]([ExtResource("3_pyw81")]) +players = Array[ExtResource("2_e743i")]([ExtResource("3_pyw81"), SubResource("Resource_njyo4")]) [node name="World" type="Node" groups=["WorldGroup"]] script = ExtResource("1_ybjyx") @@ -39,6 +51,17 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +[node name="EffectContainer" type="Control" parent="UI"] +unique_name_in_owner = true +process_mode = 3 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + [node name="PauseContainer" type="Control" parent="UI"] unique_name_in_owner = true layout_mode = 1