diff --git a/levels/debug_level/debug_level.tscn b/levels/debug_level/debug_level.tscn index 425e67e..449efd2 100644 --- a/levels/debug_level/debug_level.tscn +++ b/levels/debug_level/debug_level.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=60 format=3 uid="uid://bm2o3mex10v11"] +[gd_scene load_steps=61 format=3 uid="uid://bm2o3mex10v11"] [ext_resource type="Terrain3DAssets" uid="uid://cwl34gstabgrx" path="res://levels/debug_level/terrain_assets.res" id="1_5smdk"] [ext_resource type="Shader" path="res://src/shaders/psx_water.gdshader" id="6_0efu4"] @@ -286,6 +286,10 @@ albedo_texture = ExtResource("18_scall") uv1_scale = Vector3(4, 2, 1) texture_filter = 4 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_f1rqf"] +height = 32.0 +radius = 32.0 + [sub_resource type="CylinderShape3D" id="CylinderShape3D_sqfj0"] height = 55.0 radius = 100.0 @@ -604,7 +608,7 @@ gravity = -9.8 [node name="CollisionShape3D" type="CollisionShape3D" parent="GravityBowl/Area3D"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 16, 0) -shape = SubResource("CylinderShape3D_sqfj0") +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) diff --git a/project.godot b/project.godot index 25e860c..d92c19b 100644 --- a/project.godot +++ b/project.godot @@ -132,6 +132,11 @@ shot_cancel={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) ] } +shot_reset={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":114,"location":0,"echo":false,"script":null) +] +} select_driver={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":49,"location":0,"echo":false,"script":null) diff --git a/src/player/shot_setup/shot_setup.gd b/src/player/shot_setup/shot_setup.gd index 007451a..3414ffb 100644 --- a/src/player/shot_setup/shot_setup.gd +++ b/src/player/shot_setup/shot_setup.gd @@ -8,6 +8,7 @@ enum Phase { CURVE_ADJUST, DOWNSWING, SHOT, + SHOT_RESET, FINISHED, } @@ -87,6 +88,15 @@ var invert_pitch: bool = ProjectSettings.get_setting("game/config/controls/camer var control_disabled := false +var reset_enabled := false: + set(value): + if value != reset_enabled: + if value: + hud.show_reset_prompt() + else: + hud.hide_reset_prompt() + reset_enabled = value + var phase: Phase = Phase.FINISHED: set(value): if value != phase: @@ -158,6 +168,7 @@ var _tracking_camera: OrbitalCamera @onready var downswing_timer: Timer = %DownswingTimer @onready var ball_return_timer: Timer = %BallReturnTimer +@onready var reset_prompt_timer: Timer = %ResetPromptTimer @onready var explosion_animation: AnimationPlayer = %ExplosionAnimation @onready var player_label: Label3D = %PlayerLabel @@ -273,6 +284,8 @@ func take_shot() -> void: game_ball.freeze = false game_ball.apply_impulse(impulse, offset) + reset_prompt_timer.start() + # Play SFX shot_sfx.play_shot_sfx(club_type, is_shot_good(), shot_power) @@ -375,7 +388,7 @@ func end_shot_track() -> void: camera.make_current() if is_instance_valid(_tracking_camera): _tracking_camera.queue_free() - if phase == Phase.SHOT: + if phase in [Phase.SHOT, Phase.SHOT_RESET]: phase = Phase.FINISHED @@ -418,6 +431,9 @@ func _set_club_type(new_club_type: Club.Type) -> void: ## Called immediately before `phase` is mutated. func _on_phase_change(new_phase: Phase) -> void: + reset_prompt_timer.stop() + reset_enabled = false + match new_phase: Phase.AIM: hud.show_hud() @@ -569,6 +585,11 @@ func _process(delta: float) -> void: hud.stop_curve_bar() phase = Phase.DOWNSWING Phase.SHOT: + if reset_enabled and Input.is_action_just_pressed("shot_reset"): + phase = Phase.SHOT_RESET + reset_enabled = false + return_ball() + if driving_range and Input.is_action_just_pressed("shot_accept"): phase = Phase.AIM return_ball() @@ -587,6 +608,7 @@ func _on_ball_sleeping_state_changed() -> void: func _on_ball_entered_water() -> void: # Should only be possible during SHOT phase, but let's check just to be sure... if phase == Phase.SHOT: + phase = Phase.SHOT_RESET game_ball.freeze = true hud.play_wasted_animation() player.life -= WATER_DAMAGE @@ -617,6 +639,10 @@ func _on_hitbox_ball_collision(ball: GameBall) -> void: ball.apply_central_impulse(explosion_impulse) +func _on_reset_prompt_timer_timeout() -> void: + reset_enabled = true + + ## Create a new instance for the given player. static func create(_player: WorldPlayer) -> ShotSetup: var instance: ShotSetup = ShotSetup.scene.instantiate() diff --git a/src/player/shot_setup/shot_setup.tscn b/src/player/shot_setup/shot_setup.tscn index 682250d..69aa110 100644 --- a/src/player/shot_setup/shot_setup.tscn +++ b/src/player/shot_setup/shot_setup.tscn @@ -451,6 +451,11 @@ one_shot = true unique_name_in_owner = true one_shot = true +[node name="ResetPromptTimer" type="Timer" parent="."] +unique_name_in_owner = true +wait_time = 45.0 +one_shot = true + [node name="Hitbox" type="Area3D" parent="."] script = ExtResource("7_uh8kn") @@ -498,5 +503,6 @@ width = 120.0 [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"] +[connection signal="timeout" from="ResetPromptTimer" to="." method="_on_reset_prompt_timer_timeout"] [connection signal="ball_collision" from="Hitbox" to="." method="_on_hitbox_ball_collision"] [connection signal="body_entered" from="Hitbox" to="Hitbox" method="_on_body_entered"] diff --git a/src/ui/shot_hud/shot_hud.gd b/src/ui/shot_hud/shot_hud.gd index ebb338f..4ecf3f5 100644 --- a/src/ui/shot_hud/shot_hud.gd +++ b/src/ui/shot_hud/shot_hud.gd @@ -26,6 +26,8 @@ var player: WorldPlayer @onready var _player_name: Label = %PlayerName +@onready var _reset_prompt_animation: AnimationPlayer = %ResetPromptAnimation + @onready var _life_bar_rumbler: Rumbler = %LifeBarRumbler static var scene: PackedScene = preload("res://src/ui/shot_hud/shot_hud.tscn") @@ -93,6 +95,13 @@ func play_wasted_animation() -> void: _wasted_animation.play("display") +func show_reset_prompt() -> void: + _reset_prompt_animation.play("show") + + +func hide_reset_prompt() -> void: + _reset_prompt_animation.play_backwards("show") + ## Set the value of the life bar, potentially playing some kind of effect in response. ## ## To set the life bar without triggering an effect, set it directly with `life_bar.value` diff --git a/src/ui/shot_hud/shot_hud.tscn b/src/ui/shot_hud/shot_hud.tscn index cfde0b9..f7289e0 100644 --- a/src/ui/shot_hud/shot_hud.tscn +++ b/src/ui/shot_hud/shot_hud.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=38 format=3 uid="uid://c4ifdiohng830"] +[gd_scene load_steps=47 format=3 uid="uid://c4ifdiohng830"] [ext_resource type="Script" path="res://src/ui/shot_hud/shot_hud.gd" id="1_x5b4c"] [ext_resource type="Shader" path="res://src/shaders/canvas_retro.gdshader" id="1_ybxxp"] @@ -13,6 +13,7 @@ [ext_resource type="FontFile" uid="uid://dsa0oh7c0h4pu" path="res://assets/fonts/Racing_Sans_One/RacingSansOne-Regular.ttf" id="8_bejx4"] [ext_resource type="Texture2D" uid="uid://tancoet1lih5" path="res://assets/ui/ball_icons/basic_icon.png" id="8_tt8i3"] [ext_resource type="PackedScene" uid="uid://dmciuk3pbjsae" path="res://src/ui/shot_hud/life_bar/life_bar.tscn" id="9_w1fiw"] +[ext_resource type="PackedScene" uid="uid://b47goj32i6sdh" path="res://src/ui/input_prompt/input_prompt.tscn" id="14_ik4gg"] [sub_resource type="Animation" id="Animation_3xds6"] resource_name = "RESET" @@ -642,6 +643,114 @@ _data = { "show": SubResource("Animation_nicro") } +[sub_resource type="Gradient" id="Gradient_jevda"] +interpolation_mode = 2 +offsets = PackedFloat32Array(0, 0.5, 1) +colors = PackedColorArray(0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_q43a6"] +gradient = SubResource("Gradient_jevda") +fill_from = Vector2(0, 0.4) +fill_to = Vector2(1, 0.6) +metadata/_snap_enabled = true + +[sub_resource type="Animation" id="Animation_ornnh"] +resource_name = "idle" +length = 6.0 +loop_mode = 1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Glint:anchor_left") +tracks/0/interp = 2 +tracks/0/loop_wrap = false +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.233333), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [-1.0, 1.0] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Glint:anchor_right") +tracks/1/interp = 2 +tracks/1/loop_wrap = false +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.233333), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.0, 2.0] +} + +[sub_resource type="Animation" id="Animation_3dab1"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Glint:anchor_left") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [-1.0] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Glint:anchor_right") +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_bdkm3"] +_data = { +"RESET": SubResource("Animation_3dab1"), +"idle": SubResource("Animation_ornnh") +} + +[sub_resource type="Animation" id="Animation_ll1u8"] +resource_name = "show" +length = 0.4 +tracks/0/type = "bezier" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:position:y") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(-55, -0.25, 0, 0.0709939, 50.4391, 0, -0.251647, 15.8692, 0.2, 0), +"times": PackedFloat32Array(0, 0.4) +} + +[sub_resource type="Animation" id="Animation_qjs4v"] +length = 0.001 +tracks/0/type = "bezier" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:position:y") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(-55, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_o70c6"] +_data = { +"RESET": SubResource("Animation_qjs4v"), +"show": SubResource("Animation_ll1u8") +} + [node name="ShotHUD" type="Control"] layout_mode = 3 anchors_preset = 15 @@ -920,3 +1029,42 @@ root_node = NodePath("../..") libraries = { "": SubResource("AnimationLibrary_c3i4w") } + +[node name="ResetPrompt" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -95.5 +offset_top = -55.0 +offset_right = 95.5 +grow_horizontal = 2 +theme_override_constants/margin_top = 16 + +[node name="ResetInputPrompt" parent="ResetPrompt" instance=ExtResource("14_ik4gg")] +clip_children = 2 +layout_mode = 2 +text = "❓ - RESET" +action = &"shot_reset" +label = "RESET" + +[node name="Glint" type="TextureRect" parent="ResetPrompt/ResetInputPrompt"] +layout_mode = 1 +anchors_preset = -1 +anchor_left = -1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = SubResource("GradientTexture2D_q43a6") + +[node name="GlintAnimation" type="AnimationPlayer" parent="ResetPrompt/ResetInputPrompt"] +libraries = { +"": SubResource("AnimationLibrary_bdkm3") +} +autoplay = "idle" + +[node name="ResetPromptAnimation" type="AnimationPlayer" parent="ResetPrompt"] +unique_name_in_owner = true +libraries = { +"": SubResource("AnimationLibrary_o70c6") +}