diff --git a/assets/media/images/car_sprite.png b/assets/media/images/car_sprite.png new file mode 100644 index 0000000..ad690d5 Binary files /dev/null and b/assets/media/images/car_sprite.png differ diff --git a/assets/media/images/car_sprite.png.import b/assets/media/images/car_sprite.png.import new file mode 100644 index 0000000..801f339 --- /dev/null +++ b/assets/media/images/car_sprite.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cys6q5fgkhf8v" +path="res://.godot/imported/car_sprite.png-6749c796738ab73a3c043ac1e444c50b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/media/images/car_sprite.png" +dest_files=["res://.godot/imported/car_sprite.png-6749c796738ab73a3c043ac1e444c50b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +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=1 diff --git a/assets/media/images/sound-loud.svg b/assets/media/images/sound-loud.svg new file mode 100644 index 0000000..c3fb03c --- /dev/null +++ b/assets/media/images/sound-loud.svg @@ -0,0 +1,7 @@ + + + + + + sound-loud-filled + \ No newline at end of file diff --git a/assets/media/images/sound-loud.svg.import b/assets/media/images/sound-loud.svg.import new file mode 100644 index 0000000..22aee7b --- /dev/null +++ b/assets/media/images/sound-loud.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://du5pfobp663lk" +path="res://.godot/imported/sound-loud.svg-333e34fba7f1fc42a8539247f3d18be7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/media/images/sound-loud.svg" +dest_files=["res://.godot/imported/sound-loud.svg-333e34fba7f1fc42a8539247f3d18be7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +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=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/media/images/sound-off.svg b/assets/media/images/sound-off.svg new file mode 100644 index 0000000..3c9b09b --- /dev/null +++ b/assets/media/images/sound-off.svg @@ -0,0 +1,7 @@ + + + + + + sound-off-filled + \ No newline at end of file diff --git a/assets/media/images/sound-off.svg.import b/assets/media/images/sound-off.svg.import new file mode 100644 index 0000000..1c506d5 --- /dev/null +++ b/assets/media/images/sound-off.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://15k2jvk1jxr2" +path="res://.godot/imported/sound-off.svg-285eff611e322220e8bf9e813ec81196.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/media/images/sound-off.svg" +dest_files=["res://.godot/imported/sound-off.svg-285eff611e322220e8bf9e813ec81196.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +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=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/fps.gd b/fps.gd new file mode 100644 index 0000000..e8a0b69 --- /dev/null +++ b/fps.gd @@ -0,0 +1,10 @@ +extends CanvasLayer + + +func _process(_delta: float) -> void: + $Label.text = "FPS: " + str(Engine.get_frames_per_second()) + + +func _input(event: InputEvent) -> void: + if event.is_action_pressed("toggle_fps"): + visible = not visible diff --git a/fps.gd.uid b/fps.gd.uid new file mode 100644 index 0000000..3424f8c --- /dev/null +++ b/fps.gd.uid @@ -0,0 +1 @@ +uid://dby7rbf2klgm0 diff --git a/levels/level_01.tscn b/levels/level_01.tscn index 6b3db9d..5a073a6 100644 --- a/levels/level_01.tscn +++ b/levels/level_01.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=6 format=4 uid="uid://dsf218g5whvei"] +[gd_scene load_steps=7 format=4 uid="uid://dsf218g5whvei"] [ext_resource type="Script" uid="uid://cq6hvd85q87qa" path="res://scripts/level.gd" id="1_mckh2"] [ext_resource type="Texture2D" uid="uid://c8so6h2ldkd2c" path="res://assets/media/images/tileset.jpeg" id="1_ty3hu"] +[ext_resource type="PackedScene" uid="uid://1jwlt8wyr0ea" path="res://scenes/car.tscn" id="3_81a5f"] [sub_resource type="NavigationPolygon" id="NavigationPolygon_mckh2"] vertices = PackedVector2Array(422, 810.227, 410, 810.297, 410, 810, 422, 810, 74, 326, -55.9375, 326, -55.4375, 266, 86, 266, 86, 746, 74, 758, 422, 758, 410, 746, 422, 314, 410, 326, 262, 314, 186, 326, 186, -21.0781, 262, -20.7969) @@ -904,5 +905,8 @@ position = Vector2(296, 320) [node name="SAida3" type="Node2D" parent="SaidasLabirinto"] position = Vector2(418, 416) +[node name="car" parent="." instance=ExtResource("3_81a5f")] +position = Vector2(97, 117) + [connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] [connection signal="timeout" from="Spawn" to="." method="_on_spawn_timeout"] diff --git a/levels/level_02.tscn b/levels/level_02.tscn index d870f27..cd47c1b 100644 --- a/levels/level_02.tscn +++ b/levels/level_02.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=6 format=4 uid="uid://d3w1e0xaquuqx"] +[gd_scene load_steps=7 format=4 uid="uid://d3w1e0xaquuqx"] [ext_resource type="Script" uid="uid://cq6hvd85q87qa" path="res://scripts/level.gd" id="1_g685p"] [ext_resource type="Texture2D" uid="uid://c8so6h2ldkd2c" path="res://assets/media/images/tileset.jpeg" id="1_nnn84"] +[ext_resource type="PackedScene" uid="uid://1jwlt8wyr0ea" path="res://scenes/car.tscn" id="3_8yqd8"] [sub_resource type="NavigationPolygon" id="NavigationPolygon_g685p"] vertices = PackedVector2Array(525.039, 877.82, -222.969, 865.18, -127.953, -90, 528.961, -90) @@ -533,5 +534,8 @@ position = Vector2(247, 599) [node name="SAida3" type="Node2D" parent="SaidasLabirinto"] position = Vector2(404, 765) +[node name="car" parent="." instance=ExtResource("3_8yqd8")] +position = Vector2(220, 275) + [connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] [connection signal="timeout" from="Spawn" to="." method="_on_spawn_timeout"] diff --git a/levels/level_03.tscn b/levels/level_03.tscn index a1515e2..83e93c2 100644 --- a/levels/level_03.tscn +++ b/levels/level_03.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=6 format=4 uid="uid://mux8il3lbk2v"] +[gd_scene load_steps=7 format=4 uid="uid://mux8il3lbk2v"] [ext_resource type="Script" uid="uid://cq6hvd85q87qa" path="res://scripts/level.gd" id="1_lear0"] [ext_resource type="Texture2D" uid="uid://c8so6h2ldkd2c" path="res://assets/media/images/tileset.jpeg" id="2_85vqe"] +[ext_resource type="PackedScene" uid="uid://1jwlt8wyr0ea" path="res://scenes/car.tscn" id="3_85vqe"] [sub_resource type="NavigationPolygon" id="NavigationPolygon_mckh2"] vertices = PackedVector2Array(262, 1323.37, 186, 1323.66, 186, 326, 262, 326, 22, 1324.25, -54, 1324.54, -54, 326, 22, 326, 518, 1322.43, 442, 1322.71, 442, 326, 518, 326, 186, 266, 186, -20.0938, 262, -19.9297, 262, 266, -54, 266, -54, -20.6016, 22, -20.4375, 22, 266, 582, 266, 582, 326, -358, 326, -358, 266) @@ -904,5 +905,8 @@ position = Vector2(213, 1299) [node name="SAida3" type="Node2D" parent="SaidasLabirinto"] position = Vector2(472, 1298) +[node name="car" parent="." instance=ExtResource("3_85vqe")] +position = Vector2(-12, 179) + [connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] [connection signal="timeout" from="Spawn" to="." method="_on_spawn_timeout"] diff --git a/project.godot b/project.godot index 87060e7..4caa497 100644 --- a/project.godot +++ b/project.godot @@ -18,6 +18,7 @@ config/icon="res://icon.svg" [autoload] Controller="*res://scripts/controller.gd" +Fps="*res://scenes/fps.tscn" [display] @@ -74,6 +75,11 @@ pause={ "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":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } +toggle_fps={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null) +] +} [rendering] diff --git a/scenes/achievement_popup.tscn b/scenes/achievement_popup.tscn new file mode 100644 index 0000000..691ca19 --- /dev/null +++ b/scenes/achievement_popup.tscn @@ -0,0 +1,47 @@ +[gd_scene load_steps=2 format=3 uid="uid://wuftlm7jlgu1"] + +[ext_resource type="Script" uid="uid://cq5wwed4giwid" path="res://scripts/achievement_popup.gd" id="1_nkkgm"] + +[node name="achievement_popup" type="CanvasLayer"] +process_mode = 3 +script = ExtResource("1_nkkgm") + +[node name="ColorRect" type="ColorRect" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0.131126, 0.155787, 0.131127, 0.603922) + +[node name="Panel" type="Panel" parent="."] +modulate = Color(0.443137, 0.823529, 0, 1) +z_index = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 57.0 +grow_horizontal = 2 + +[node name="AchievementTitleLabel" type="Label" parent="Panel/VBoxContainer"] +layout_mode = 2 +text = "Nível desbloqueado!" +horizontal_alignment = 1 + +[node name="AchievementOkButton" type="Button" parent="Panel/VBoxContainer"] +layout_mode = 2 +text = "ok +" + +[connection signal="pressed" from="Panel/VBoxContainer/AchievementOkButton" to="." method="_on_achievement_ok_button_pressed"] diff --git a/scenes/car.tscn b/scenes/car.tscn new file mode 100644 index 0000000..993bebd --- /dev/null +++ b/scenes/car.tscn @@ -0,0 +1,41 @@ +[gd_scene load_steps=5 format=3 uid="uid://1jwlt8wyr0ea"] + +[ext_resource type="Texture2D" uid="uid://cys6q5fgkhf8v" path="res://assets/media/images/car_sprite.png" id="1_c35m8"] +[ext_resource type="Script" uid="uid://cm0b1p0yaiy77" path="res://scripts/car.gd" id="1_qt2eu"] + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_qt2eu"] +radius = 7.10637 +height = 22.7064 + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_50v30"] +radius = 7.0 +height = 26.0 + +[node name="car" type="CharacterBody2D"] +z_index = 10 +script = ExtResource("1_qt2eu") + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture = ExtResource("1_c35m8") +hframes = 5 +vframes = 5 +frame = 10 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(5, 0) +rotation = -0.690893 +scale = Vector2(5.04, 5.04) +shape = SubResource("CapsuleShape2D_qt2eu") + +[node name="ClickArea" type="Area2D" parent="."] +position = Vector2(19, 17) +rotation = -0.733553 +scale = Vector2(4.4, 4.89964) + +[node name="CollisionShape2D" type="CollisionShape2D" parent="ClickArea"] +position = Vector2(0, -5) +shape = SubResource("CapsuleShape2D_50v30") + +[node name="DragTimer" type="Timer" parent="."] +wait_time = 15.0 +autostart = true diff --git a/scenes/fps.tscn b/scenes/fps.tscn new file mode 100644 index 0000000..78f104c --- /dev/null +++ b/scenes/fps.tscn @@ -0,0 +1,22 @@ +[gd_scene load_steps=2 format=3 uid="uid://umm2j5dxjro3"] + +[ext_resource type="Script" uid="uid://dby7rbf2klgm0" path="res://fps.gd" id="1_jtrh3"] + +[node name="FPS" type="CanvasLayer"] +visible = false +script = ExtResource("1_jtrh3") + +[node name="Label" type="Label" parent="."] +modulate = Color(0.956863, 0.333333, 0, 1) +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -21.0 +offset_top = -33.5 +offset_right = 21.0 +grow_horizontal = 2 +grow_vertical = 0 +theme_override_font_sizes/font_size = 24 +text = "FPS" diff --git a/scenes/ui.tscn b/scenes/ui.tscn index 65e28b4..75602e3 100644 --- a/scenes/ui.tscn +++ b/scenes/ui.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=10 format=3 uid="uid://42oeppt28feq"] +[gd_scene load_steps=13 format=3 uid="uid://42oeppt28feq"] [ext_resource type="Script" uid="uid://bcsw0mt21tpsw" path="res://scripts/ui.gd" id="1_nt7q6"] [ext_resource type="VideoStream" uid="uid://bbup00575hgcg" path="res://assets/media/videos/patriotas_final_1.ogv" id="2_p7vwb"] @@ -7,6 +7,7 @@ [ext_resource type="Theme" uid="uid://dmhlpo4s3t401" path="res://assets/themes/default_menu_theme.tres" id="3_yev5y"] [ext_resource type="Texture2D" uid="uid://d1ar24frbwesc" path="res://assets/media/images/logo-converter.png" id="4_wm3ai"] [ext_resource type="FontFile" uid="uid://cul3qtiyor1u1" path="res://assets/fonts/Early GameBoy.ttf" id="5_8dubc"] +[ext_resource type="Texture2D" uid="uid://du5pfobp663lk" path="res://assets/media/images/sound-loud.svg" id="8_ktti3"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_p7vwb"] content_margin_left = 3.0 @@ -24,6 +25,29 @@ corner_radius_top_right = 5 corner_radius_bottom_right = 5 corner_radius_bottom_left = 5 +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7dvkv"] +bg_color = Color(0.854902, 0, 0.137255, 0.862745) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_vdcm2"] +bg_color = Color(0, 1, 0.443137, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.8, 0.270588, 0, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + [sub_resource type="LabelSettings" id="LabelSettings_p7vwb"] font = ExtResource("5_8dubc") font_size = 32 @@ -94,7 +118,7 @@ layout_mode = 2 size_flags_horizontal = 2 size_flags_vertical = 4 -[node name="Label" type="Label" parent="header/HBoxContainer/MarginContainer"] +[node name="HighScoreLabel" type="Label" parent="header/HBoxContainer/MarginContainer"] layout_mode = 2 size_flags_horizontal = 4 theme_override_colors/font_color = Color(0.960784, 0.960784, 0.960784, 1) @@ -114,7 +138,43 @@ theme_override_fonts/font = ExtResource("5_8dubc") theme_override_font_sizes/font_size = 14 theme_override_styles/normal = SubResource("StyleBoxFlat_p7vwb") +[node name="VBoxContainer" type="VBoxContainer" parent="header"] +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = -113.0 +offset_top = -310.0 +offset_right = 85.0 +offset_bottom = -251.0 +grow_horizontal = 0 +grow_vertical = 2 +scale = Vector2(0.56, 0.56) +alignment = 1 + +[node name="MarginContainer" type="MarginContainer" parent="header/VBoxContainer"] +layout_mode = 2 +theme = ExtResource("3_yev5y") +theme_override_constants/margin_bottom = 0 + +[node name="DragTimerLabel" type="Label" parent="header/VBoxContainer/MarginContainer"] +modulate = Color(0, 0, 0, 1) +layout_mode = 2 +size_flags_vertical = 1 +text = "Drag: --" + +[node name="ShotProgressBar" type="ProgressBar" parent="header/VBoxContainer"] +custom_minimum_size = Vector2(0, 18) +layout_mode = 2 +theme_override_styles/background = SubResource("StyleBoxFlat_7dvkv") +theme_override_styles/fill = SubResource("StyleBoxFlat_vdcm2") +max_value = 1.0 +value = 1.0 +show_percentage = false + [node name="main_menu" type="CanvasLayer" parent="."] +visible = false [node name="ColorRect" type="ColorRect" parent="main_menu"] anchors_preset = 15 @@ -333,6 +393,27 @@ text = "Back to main menu" layout_mode = 2 text = "Quit" +[node name="sound_icons" type="CanvasLayer" parent="."] + +[node name="VBoxContainer" type="VBoxContainer" parent="sound_icons"] +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = -30.0 +offset_top = -346.0 +offset_right = 34.0 +offset_bottom = -282.0 +grow_horizontal = 0 +grow_vertical = 2 +scale = Vector2(0.439999, 0.439999) + +[node name="sound_toggle_button" type="TextureButton" parent="sound_icons/VBoxContainer"] +layout_mode = 2 +texture_normal = ExtResource("8_ktti3") +stretch_mode = 0 + [connection signal="pressed" from="intro_container/VBoxContainer/SetupMarginContainer/SkipIntroButton" to="." method="_on_skip_intro_button_pressed"] [connection signal="pressed" from="main_menu/VBoxContainer/ChooseLevelButton" to="." method="_on_choose_level_button_pressed"] [connection signal="pressed" from="main_menu/VBoxContainer/SetupMarginContainer/VBoxContainer/Button_Quit_From_Main_Menu" to="." method="_on_button_quit_from_main_menu_pressed"] @@ -344,3 +425,4 @@ text = "Quit" [connection signal="pressed" from="game_over_menu/VBoxContainer/Button_Retry" to="." method="_on_button_retry_pressed"] [connection signal="pressed" from="game_over_menu/VBoxContainer/Button_Back_To_Main_menu" to="." method="_on_button_back_to_main_menu_pressed"] [connection signal="pressed" from="game_over_menu/VBoxContainer/Button_Quit_From_Game_Over" to="." method="_on_button_quit_from_game_over_pressed"] +[connection signal="pressed" from="sound_icons/VBoxContainer/sound_toggle_button" to="." method="_on_sound_toggle_button_pressed"] diff --git a/scripts/achievement_popup.gd b/scripts/achievement_popup.gd new file mode 100644 index 0000000..8c25f37 --- /dev/null +++ b/scripts/achievement_popup.gd @@ -0,0 +1,20 @@ +extends CanvasLayer + +@onready var achievement_title_label: Label = $Panel/VBoxContainer/AchievementTitleLabel + + +func _ready(): + visible = false + + +func show_achievement(level_name: String): + print("Exibindo popup de conquista:", level_name) + achievement_title_label.text = "Novo nível desbloqueado:\n" + level_name + visible = true + get_tree().paused = true + + +func _on_achievement_ok_button_pressed() -> void: + visible = false + get_tree().paused = false + queue_free() diff --git a/scripts/achievement_popup.gd.uid b/scripts/achievement_popup.gd.uid new file mode 100644 index 0000000..a582e9f --- /dev/null +++ b/scripts/achievement_popup.gd.uid @@ -0,0 +1 @@ +uid://cq5wwed4giwid diff --git a/scripts/camera_2d.gd b/scripts/camera_2d.gd index fe4cda4..c5240b0 100644 --- a/scripts/camera_2d.gd +++ b/scripts/camera_2d.gd @@ -15,7 +15,7 @@ func reset_camera(): zoom.y = 1 -func _process(delta: float) -> void: +func _process(_delta: float) -> void: if Input.is_action_pressed("up"): position.y -= CameraPanSpeed if Input.is_action_pressed("down"): @@ -36,12 +36,16 @@ func _unhandled_input(event): if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_RIGHT: if event.pressed: + if Controller.car_dragging: + dragging = false + return dragging = true last_mouse_position = get_viewport().get_mouse_position() else: dragging = false - if event is InputEventMouseMotion and dragging: + # Só move a câmera se não estiver arrastando o carro + if event is InputEventMouseMotion and dragging and not Controller.car_dragging: var current_mouse_position = get_viewport().get_mouse_position() var delta = last_mouse_position - current_mouse_position position += delta * zoom diff --git a/scripts/car.gd b/scripts/car.gd new file mode 100644 index 0000000..46aa4fa --- /dev/null +++ b/scripts/car.gd @@ -0,0 +1,74 @@ +class_name Car +extends CharacterBody2D + +@onready var click_area: Area2D = $ClickArea +@onready var sprite_2d: Sprite2D = $Sprite2D +@onready var shape: CollisionShape2D = $CollisionShape2D + +var dragging: bool = false +var drag_enabled: bool = true +var drag_time: float = 3.0 +var wait_time: float = 15.0 + + +func _ready(): + click_area.input_event.connect(_on_click_area_input_event) + + +func _process(_delta): + if dragging: + var mouse_pos = get_viewport().get_camera_2d().get_global_mouse_position() + + global_position = mouse_pos + sprite_2d.scale = Vector2(1.15, 1.15) + shape.disabled = true + + +func _on_click_area_input_event(_viewport, event, _shape_idx): + if event is InputEventMouseButton or event is InputEventScreenTouch: + if event.button_index == MOUSE_BUTTON_RIGHT: + if event.pressed: + if drag_enabled: + start_drag() + else: + if dragging: + stop_drag() + + +func start_drag(): + dragging = true + Controller.car_dragging = true + drag_enabled = true + await drag_duration() + + +func stop_drag(): + dragging = false + Controller.car_dragging = false + sprite_2d.scale = Vector2(1, 1) + shape.disabled = false + drag_enabled = false + await wait_duration() + + +func drag_duration(): + var total = drag_time + for i in range(total as int, 0, -1): + if Controller.ui: + Controller.ui.set_drag_timer_text("Drag: %ds" % i, Color.LIGHT_GREEN) + await get_tree().create_timer(1.0).timeout + + if dragging: + stop_drag() + + +func wait_duration(): + var total = wait_time + for i in range(total as int, 0, -1): + if Controller.ui: + Controller.ui.set_drag_timer_text("Espera: %ds" % i, Color.SALMON) + await get_tree().create_timer(1.0).timeout + + drag_enabled = true + if Controller.ui: + Controller.ui.set_drag_timer_text("Drag!", Color.GREEN) diff --git a/scripts/car.gd.uid b/scripts/car.gd.uid new file mode 100644 index 0000000..f4fab1c --- /dev/null +++ b/scripts/car.gd.uid @@ -0,0 +1 @@ +uid://cm0b1p0yaiy77 diff --git a/scripts/controller.gd b/scripts/controller.gd index 9b5f243..7842b11 100644 --- a/scripts/controller.gd +++ b/scripts/controller.gd @@ -10,9 +10,9 @@ var current_level: String var current_level_path: String var current_score := 0 var high_scores: Dictionary = {} - -@onready var patriota = preload("res://scenes/patriota.tscn") -@onready var particle = preload("res://scenes/DeathParticlesRayExplosion.tscn") +var sound_muted: bool = false +var car_dragging: bool = false +var can_shoot: bool = true var levels: Dictionary = { @@ -33,17 +33,20 @@ var levels: Dictionary = { } } +@onready var patriota = preload("res://scenes/patriota.tscn") +@onready var particle = preload("res://scenes/DeathParticlesRayExplosion.tscn") + func _level01() -> bool: return true func _level02() -> bool: - return high_scores.get("level01", 0) >= 5 + return high_scores.get("level01", 0) >= 1 func _level03() -> bool: - return high_scores.get("level02", 0) >= 10 + return high_scores.get("level02", 0) >= 1 func _ready(): @@ -64,11 +67,13 @@ func start_level(): level.timer.start() level.spawn.start() ui.main_menu.visible = false - ui.label.text = "Bom jogo!" camera_2d.reset_camera() + ui.header.visible = true current_score = 0 ui.update_score(current_score) + + ui.show_high_score( update_high_score() ) func restart_level(): @@ -79,7 +84,12 @@ func restart_level(): func change_level(load_level): - var level_path = "res://levels/" + load_level + var level_path: String + if load_level.begins_with("res://"): + level_path = load_level + else: + level_path = "res://levels/" + load_level + var new_level = load(level_path).instantiate() if level: @@ -92,13 +102,14 @@ func change_level(load_level): if levels[level_id]["url"] == load_level: current_level = level_id break - + current_level_path = level_path load_high_scores() update_high_score() ui._generate_level_buttons() + func spawn_patriota(): if total_patriotas_generated >= max_patriotas_by_level: level.spawn.stop() @@ -117,17 +128,10 @@ func spawn_patriota(): total_patriotas_generated += 1 -func add_point(): - current_score += 1 - ui.update_score(current_score) - - func game_over(): - print("game_over") level.visible = false level.timer.stop() - ui.label.text = "Game Over" ui.pause_menu.visible = false ui.main_menu.visible = false ui.game_over_menu.visible = true @@ -135,30 +139,43 @@ func game_over(): update_high_score() ui._generate_level_buttons() - + ui.show_high_score(update_high_score()) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("pause"): toggle_pause() - + if event is InputEventMouseButton and event.pressed: if event.button_index == MOUSE_BUTTON_LEFT: - var click_position = get_viewport().get_camera_2d().get_global_mouse_position() - - # Dispara o som do tiro - if ui: - scene_manager.ray_shot.play() + if not can_shoot: + return - # Instancia explosão onde o jogador clicou - var new_particle = particle.instantiate() - new_particle.global_position = click_position - new_particle.emitting = true - get_tree().current_scene.add_child(new_particle) + fire_shot() + + +func fire_shot(): + can_shoot = false + + var click_position = get_viewport().get_camera_2d().get_global_mouse_position() + + # Som do tiro + if ui: + scene_manager.ray_shot.play() + + # Instancia explosão + var new_particle = particle.instantiate() + new_particle.global_position = click_position + new_particle.emitting = true + get_tree().current_scene.add_child(new_particle) + + ui.animate_shoot_cooldown(0.45) + await get_tree().create_timer(0.5).timeout + can_shoot = true func toggle_pause(): - if ui.main_menu.visible || ui.game_over_menu.visible: + if ui.main_menu.visible || ui.game_over_menu.visible || ui.intro_container.visible: return if ui.is_paused: @@ -177,7 +194,6 @@ func back_to_main_menu(): get_tree().paused = false ui.main_menu.visible = true - ui.label.text = "Bem-vindo de volta!" camera_2d.reset_camera() update_high_score() ui._generate_level_buttons() @@ -189,12 +205,66 @@ func load_high_scores(): high_scores = file.get_var() +func add_point(): + current_score += 1 + ui.update_score(current_score) + update_high_score() + check_for_unlocked_levels() + + func update_high_score(): var level_id = current_level.get_basename() if not high_scores.has(level_id): high_scores[level_id] = 0 - - if current_score > high_scores[level_id]: + + var old_score = high_scores[level_id] + + if current_score > old_score: high_scores[level_id] = current_score var file = FileAccess.open("user://high_scores.save", FileAccess.WRITE) file.store_var(high_scores) + + check_for_unlocked_levels() + + return high_scores[level_id] + + +func check_for_unlocked_levels(): + var next_level = get_next_level_id(current_level) + if next_level == "": + return + + var data = levels[next_level] + var unlocked = is_unlocked(next_level) + var already_seen = high_scores.has("_seen_" + next_level) + + if unlocked and not already_seen: + high_scores["_seen_" + next_level] = true + var file = FileAccess.open("user://high_scores.save", FileAccess.WRITE) + file.store_var(high_scores) + + # Instancia o popup dentro do level atual + var popup_scene = preload("res://scenes/achievement_popup.tscn") + var popup = popup_scene.instantiate() + level.add_child(popup) + popup.show_achievement(data["label"]) + get_tree().paused = true + + +func get_next_level_id(current: String) -> String: + var keys = levels.keys() + var current_index = keys.find(current) + if current_index == -1 or current_index >= keys.size() - 1: + return "" + return keys[current_index + 1] + + +## Sounds +func toggle_sound() -> void: + sound_muted = !sound_muted + AudioServer.set_bus_mute(AudioServer.get_bus_index("Master"), sound_muted) + + if sound_muted: + ui.sound_toggle_button.texture_normal = preload("res://assets/media/images/sound-off.svg") + else: + ui.sound_toggle_button.texture_normal = preload("res://assets/media/images/sound-loud.svg") diff --git a/scripts/level.gd b/scripts/level.gd index d88780a..43cd3eb 100644 --- a/scripts/level.gd +++ b/scripts/level.gd @@ -6,12 +6,15 @@ class_name Level extends Node2D @export var max_patriotas: int = 10 + func _ready(): Controller.level = self Controller.start_level() + func _on_timer_timeout() -> void: Controller.game_over() + func _on_spawn_timeout() -> void: Controller.spawn_patriota() diff --git a/scripts/patriota.gd b/scripts/patriota.gd index e9b9a03..cd702cc 100644 --- a/scripts/patriota.gd +++ b/scripts/patriota.gd @@ -15,7 +15,7 @@ func _ready(): click_area.input_event.connect(_on_click) -func _on_click(viewport:Node, event:InputEvent, shape_idx:int): +func _on_click(_viewport:Node, event:InputEvent, _shape_idx:int): if (event is InputEventMouseButton or event is InputEventScreenTouch) and event.pressed: if event.button_index == MOUSE_BUTTON_LEFT: plock_sound.play() @@ -42,7 +42,7 @@ func kill(): var _particle = preload("res://scenes/DeathParticlesBloodExplosion.tscn").instantiate() animation_player.stop() - click_area.set_deferred("disabled", true) # evita múltiplos cliques + click_area.set_deferred("disabled", true) set_physics_process(false) await get_tree().create_timer(0.15).timeout diff --git a/scripts/scene_manager.gd b/scripts/scene_manager.gd index 80a2ba0..98375c1 100644 --- a/scripts/scene_manager.gd +++ b/scripts/scene_manager.gd @@ -3,6 +3,7 @@ class_name SceneManager extends Node2D @onready var sound_track: AudioStreamPlayer = $SoundPlayer/SoundTrack @onready var ray_shot: AudioStreamPlayer = $SoundPlayer/RayShot + func _ready(): Controller.scene_manager = self diff --git a/scripts/ui.gd b/scripts/ui.gd index 5b5667e..c56798d 100644 --- a/scripts/ui.gd +++ b/scripts/ui.gd @@ -2,7 +2,7 @@ class_name UI extends Node2D @onready var main_menu: CanvasLayer = $main_menu @onready var header: CanvasLayer = $header -@onready var label: Label = $header/HBoxContainer/MarginContainer/Label +@onready var high_score_label: Label = $header/HBoxContainer/MarginContainer/HighScoreLabel @onready var level_menu: CanvasLayer = $level_menu @onready var pause_menu: CanvasLayer = $pause_menu @onready var game_over_menu: CanvasLayer = $game_over_menu @@ -10,6 +10,9 @@ class_name UI extends Node2D @onready var intro_1: VideoStreamPlayer = $intro_container/intro1 @onready var intro_2: VideoStreamPlayer = $intro_container/intro2 @onready var score_label: Label = $header/HBoxContainer/MarginContainer2/ScoreLabel +@onready var sound_toggle_button: TextureButton = $sound_icons/VBoxContainer/sound_toggle_button +@onready var drag_timer_label: Label = $header/VBoxContainer/MarginContainer/DragTimerLabel +@onready var shot_progress_bar: ProgressBar = $header/VBoxContainer/ShotProgressBar var is_paused: bool = false @@ -19,6 +22,7 @@ func _ready(): main_menu.visible = false game_over_menu.visible = false + header.visible = false pause_menu.process_mode = Node.PROCESS_MODE_ALWAYS hide_pause_menu() @@ -32,6 +36,7 @@ func _ready(): _generate_level_buttons() Controller.load_high_scores() + ## Intro @@ -54,11 +59,6 @@ func _on_skip_intro_button_pressed() -> void: main_menu.visible = true -## Score -func update_score(current_score): - score_label.text = "Kills: %d" % current_score - - ## Main menu func _on_choose_level_button_pressed() -> void: main_menu.visible = false @@ -149,16 +149,15 @@ func _generate_level_buttons(): for level_id in level_keys: var level_data = Controller.levels[level_id] var label = level_data["label"] - var level_path = "res://levels/" + level_data["url"] + var _level_path = "res://levels/" + level_data["url"] var button = Button.new() - var high_score = Controller.high_scores.get(level_id, 0) var is_unlocked = Controller.is_unlocked(level_id) button.size_flags_horizontal = Control.SIZE_EXPAND_FILL if is_unlocked: - button.text = " %s - High Score: %d" % [label, high_score] + button.text = " " + label + " " button.disabled = false button.pressed.connect(func(): level_menu.visible = false @@ -176,3 +175,33 @@ func _on_back_to_main_menu_pressed() -> void: level_menu.visible = false get_tree().paused = false Controller.back_to_main_menu() + + +## Score +func update_score(current_score): + score_label.text = "Score: %d" % current_score + + +func show_high_score(high_score): + high_score_label.text = "High Score: %d" % high_score + + +## Sounds +func _on_sound_toggle_button_pressed() -> void: + Controller.toggle_sound() + + +func set_drag_timer_text(text: String, color: Color = Color.WHITE): + drag_timer_label.text = text + drag_timer_label.modulate = color + + +func animate_shoot_cooldown(duration: float) -> void: + shot_progress_bar.value = 0.0 + + var steps := 10 + for i in range(steps + 1): + shot_progress_bar.value = i / float(steps) + await get_tree().create_timer(duration / steps).timeout + + shot_progress_bar.value = 1.0