diff --git a/elements/player.tscn b/elements/player.tscn index 54b3132..74c84c6 100644 --- a/elements/player.tscn +++ b/elements/player.tscn @@ -4,31 +4,38 @@ script/source = "class_name Player extends Node3D -var velocity: Vector3 = Vector3(0, 0, 0) +signal velocity_change +var velocity := 0.0: + get: return velocity + set(value): + if value != velocity: + velocity_change.emit(value) + velocity = value +@onready var ball_position: Vector3 = $Sphere.position func _physics_process(_delta: float) -> void: + ball_position = $Sphere.position $SpotLightMain.position = $Sphere.position + Vector3(0, 5, 0) $SpotLightTop.position = $Sphere.position $SpotLightLeft.position = $Sphere.position $SpotLightRight.position = $Sphere.position $SpotLightBottom.position = $Sphere.position - ## Keep the ball at the center of the camera, at a distance + # Keep the ball at the center of the camera, at a distance $Camera.position = $Sphere.position + Vector3(0, 0, 15) - ## Angle of the camera, so the player can see where the ball is going - velocity = $Sphere.linear_velocity - var desired_x = max(min(velocity.y, 35), -35) - var desired_y = max(min(-velocity.x, 50), -50) + # Angle of the camera, so the player can see where the ball is going + velocity = abs($Sphere.linear_velocity.x) + abs($Sphere.linear_velocity.y) + var desired_x = max(min($Sphere.linear_velocity.y, 35), -35) + var desired_y = max(min(-$Sphere.linear_velocity.x, 50), -50) var difference_x = $Camera.rotation_degrees.x - desired_x var difference_y = $Camera.rotation_degrees.y - desired_y $Camera.rotation_degrees.x -= max(min(difference_x / 2, 3), -3) $Camera.rotation_degrees.y -= max(min(difference_y / 2, 3), -3) - ## FOV of the camera, so it unzooms more at higher velocities + # FOV of the camera, so it unzooms more at higher velocities const default_fov = 75 - var total_velocity = abs(velocity.x) + abs(velocity.y) - $Camera.fov = default_fov + (total_velocity / 12) + $Camera.fov = default_fov + (velocity / 12) " [sub_resource type="PhysicsMaterial" id="PhysicsMaterial_vumbr"] diff --git a/elements/tree.tscn b/elements/tree.tscn index 8d9690a..771ea82 100644 --- a/elements/tree.tscn +++ b/elements/tree.tscn @@ -29,6 +29,7 @@ vertex_color_use_as_albedo = true albedo_color = Color(0.212217, 0.468618, 0, 1) [node name="Tree" type="Node3D"] +physics_interpolation_mode = 2 script = SubResource("GDScript_tbkod") [node name="Dirt" type="CSGBox3D" parent="."] diff --git a/elements/gravity.tscn b/gui/gravity.tscn similarity index 59% rename from elements/gravity.tscn rename to gui/gravity.tscn index d84e534..ec75a02 100644 --- a/elements/gravity.tscn +++ b/gui/gravity.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://dw0xl8644x166"] +[gd_scene load_steps=7 format=3 uid="uid://dw0xl8644x166"] [ext_resource type="Texture2D" uid="uid://ctjugvy1v3y6b" path="res://graphics/arrow.svg" id="1_4mbho"] [ext_resource type="AudioStream" uid="uid://dmdbharecy448" path="res://sounds/gravity.ogg" id="2_5uwlw"] @@ -6,66 +6,35 @@ [sub_resource type="GDScript" id="GDScript_3yq1l"] script/source = "extends Control -var player_ready = false -const normal = 7 -const stronger = normal * 3 - -func _input(_event: InputEvent) -> void: - if player_ready: - var grav = stronger if Input.is_action_pressed(\"gravity_strong\") else normal - PhysicsServer3D.area_set_param(get_viewport().find_world_3d().space, PhysicsServer3D.AREA_PARAM_GRAVITY, grav) -" - -[sub_resource type="GDScript" id="GDScript_my602"] -script/source = "extends TextureRect +@onready var direction_node: TextureRect = $Direction +@onready var animation: AnimationPlayer = $Direction/AnimationPlayer +@onready var audio: AudioStreamPlayer = $Direction/AudioStreamPlayer func _ready() -> void: # set the indicator to be fully transparent when it first spawns - self.self_modulate = Color(1, 1, 1, 0) + direction_node.self_modulate = Color(1, 1, 1, 0) -func _input(ev: InputEvent) -> void: - if ev is InputEventKey and ev.is_pressed(): - var up := Input.is_action_just_pressed(\"gravity_up\") - var left := Input.is_action_just_pressed(\"gravity_left\") - var right := Input.is_action_just_pressed(\"gravity_right\") - var down := Input.is_action_just_pressed(\"gravity_down\") - - if up or left or right or down: - var direction = Vector3() - if up: - direction = Vector3.UP - self.rotation_degrees = -90 - $AudioStreamPlayer.pitch_scale = 1.05 - elif left: - direction = Vector3.LEFT - self.rotation_degrees = -180 - $AudioStreamPlayer.pitch_scale = 1.02 - elif right: - direction = Vector3.RIGHT - self.rotation_degrees = 0 - $AudioStreamPlayer.pitch_scale = 0.98 - elif down: - direction = Vector3.DOWN - self.rotation_degrees = 90 - $AudioStreamPlayer.pitch_scale = 0.95 - - if get_parent().player_ready == false or direction != PhysicsServer3D.area_get_param( - get_viewport().find_world_3d().space, - PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR - ): - get_parent().player_ready = true - PhysicsServer3D.area_set_param( - get_viewport().find_world_3d().space, - PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, - direction - ) - - if $AnimationPlayer.is_playing(): - $AnimationPlayer.stop() - $AnimationPlayer.play(\"grow_fadeout\") - - if Settings.sound_on_gravity_change: - $AudioStreamPlayer.play() +func react_to_gravity_change(direction: Vector3) -> void: + if direction == Vector3.UP: + direction_node.rotation_degrees = -90 + audio.pitch_scale = 1.05 + elif direction == Vector3.LEFT: + direction_node.rotation_degrees = -180 + audio.pitch_scale = 1.02 + elif direction == Vector3.RIGHT: + direction = Vector3.RIGHT + direction_node.rotation_degrees = 0 + audio.pitch_scale = 0.98 + elif direction == Vector3.DOWN: + direction_node.rotation_degrees = 90 + audio.pitch_scale = 0.95 + + if animation.is_playing(): + animation.stop() + animation.play(\"grow_fadeout\") + + if Settings.sound_on_gravity_change: + audio.play() " [sub_resource type="Animation" id="Animation_6rwl4"] @@ -137,6 +106,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 2 script = SubResource("GDScript_3yq1l") [node name="Direction" type="TextureRect" parent="."] @@ -155,8 +125,8 @@ grow_horizontal = 2 grow_vertical = 2 scale = Vector2(0.3, 0.3) pivot_offset = Vector2(400, 400) +mouse_filter = 2 texture = ExtResource("1_4mbho") -script = SubResource("GDScript_my602") [node name="AnimationPlayer" type="AnimationPlayer" parent="Direction"] libraries = { diff --git a/gui/map.tscn b/gui/map.tscn new file mode 100644 index 0000000..28a0ad2 --- /dev/null +++ b/gui/map.tscn @@ -0,0 +1,66 @@ +[gd_scene load_steps=2 format=3 uid="uid://bcxbw6wd54ksv"] + +[sub_resource type="GDScript" id="GDScript_r4thc"] +script/source = "extends Control + +func _ready() -> void: + $TemplateRing.hide() + +func display_rings(player: Node3D, rings: Array[Ring]) -> void: + var old_rings_display := $Rings.get_children() + var difference := len(rings) - len(old_rings_display) + + # Too many displayed, free them + for i in -difference: + old_rings_display[-(1 + i)].queue_free() + # Not enough displayed, create some + for i in difference: + var new_child := $TemplateRing.duplicate() + new_child.name = \"Ring\" + str(i + 1) + new_child.show() + $Rings.add_child(new_child) + + var rings_display := $Rings.get_children() + var center: Vector2i = get_viewport().size / 2 + for i in len(rings): + rings_display[i].color = rings[i].material.albedo_color + rings_display[i].rotation = rings[i].rotation.z + rings_display[i].position.x = center.x + rings[i].position.x - player.ball_position.x + rings_display[i].position.y = center.y - rings[i].position.y + player.ball_position.y + + if rings[i].collected: + rings_display[i].scale = $TemplateRing.scale * 2 * min(4, max(1, rings[i].scale.x)) + else: + rings_display[i].scale = $TemplateRing.scale * 3 * min(4, max(1, rings[i].scale.x)) +" + +[node name="Map" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = SubResource("GDScript_r4thc") + +[node name="ColorRect" type="ColorRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(1, 0, 1, 0.0392157) + +[node name="Rings" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="TemplateRing" type="ColorRect" parent="."] +layout_mode = 0 +offset_right = 2.0 +offset_bottom = 2.0 diff --git a/gui/timer.tscn b/gui/timer.tscn index ac9f85d..a5f88dd 100644 --- a/gui/timer.tscn +++ b/gui/timer.tscn @@ -8,13 +8,9 @@ script/source = "extends VBoxContainer var enabled := false: get: return enabled set(value): + if enabled == value: + return enabled = value - if value == true: - var save_file = SaveFiles.read(SaveFiles.selected_file) - if save_file.has(\"played_for\") and save_file.played_for is float: - seconds_spent_total = save_file.played_for - else: - seconds_spent_total = 0.0 if value == false: $AnimationPlayer.play(\"scale_linear\") await get_tree().create_timer($AnimationPlayer.current_animation_length).timeout @@ -31,10 +27,13 @@ var seconds_spent_level_attempt := 0.0: seconds_spent_level_attempt = value $Level.text = \"Level: \" + seconds_to_readable(seconds_spent_level_attempt) -func _ready(): - var data = SaveFiles.read(SaveFiles.selected_file) - if data.has(\"played_for\") and data.played_for is float: - seconds_spent_total = data.played_for +func _on_visibility_changed() -> void: + if self.visible and len(SaveFiles.selected_file): + var save_file = SaveFiles.read(SaveFiles.selected_file) + if save_file.has(\"played_for\") and save_file.played_for is float: + seconds_spent_total = save_file.played_for + else: + seconds_spent_total = 0.0 func seconds_to_readable(seconds: float) -> String: var minutes: int = floor(seconds / 60) @@ -77,23 +76,6 @@ tracks/0/keys = { "values": [Vector2(1, 1)] } -[sub_resource type="Animation" id="Animation_2a86r"] -resource_name = "scale_linear" -length = 0.25 -step = 0.25 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:scale") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.25), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [Vector2(1, 1), Vector2(1.2, 1.2)] -} - [sub_resource type="Animation" id="Animation_houb3"] resource_name = "scale_cubic" length = 0.25 @@ -111,6 +93,23 @@ tracks/0/keys = { "values": [Vector2(1, 1), Vector2(1.2, 1.2)] } +[sub_resource type="Animation" id="Animation_2a86r"] +resource_name = "scale_linear" +length = 0.25 +step = 0.25 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:scale") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.25), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Vector2(1, 1), Vector2(1.2, 1.2)] +} + [sub_resource type="AnimationLibrary" id="AnimationLibrary_jmgpn"] _data = { &"RESET": SubResource("Animation_m1tld"), @@ -120,27 +119,24 @@ _data = { [node name="Timer" type="VBoxContainer"] process_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_right = -992.0 -offset_bottom = -563.0 -grow_horizontal = 2 -grow_vertical = 2 +offset_right = 315.0 +offset_bottom = 85.0 theme_override_constants/separation = -5 script = SubResource("GDScript_q235s") [node name="Total" type="Label" parent="."] layout_mode = 2 -text = "Total timer" +text = "Total: 0:00.000" label_settings = SubResource("LabelSettings_m1tld") [node name="Level" type="Label" parent="."] layout_mode = 2 -text = "Level timer" +text = "Level: 0:00.000" label_settings = SubResource("LabelSettings_2a86r") [node name="AnimationPlayer" type="AnimationPlayer" parent="."] libraries = { &"": SubResource("AnimationLibrary_jmgpn") } + +[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] diff --git a/index.tscn b/index.tscn index cfef63e..c17011e 100644 --- a/index.tscn +++ b/index.tscn @@ -1,24 +1,26 @@ -[gd_scene load_steps=7 format=3 uid="uid://ccgnnif026wb4"] +[gd_scene load_steps=8 format=3 uid="uid://ccgnnif026wb4"] [ext_resource type="PackedScene" uid="uid://xd3nsiglcdfc" path="res://gui/timer.tscn" id="1_356j3"] +[ext_resource type="PackedScene" uid="uid://dw0xl8644x166" path="res://gui/gravity.tscn" id="1_ir7so"] [ext_resource type="PackedScene" uid="uid://cckeamgkt8bqo" path="res://gui/speed.tscn" id="2_2gn6w"] +[ext_resource type="PackedScene" uid="uid://bcxbw6wd54ksv" path="res://gui/map.tscn" id="2_d1yoc"] [ext_resource type="PackedScene" uid="uid://cn55m5dqo3m6u" path="res://gui/rings.tscn" id="3_mbj17"] [ext_resource type="PackedScene" uid="uid://dkxtwpcy4moyo" path="res://menus/pause_menu.tscn" id="4_3bfj3"] [sub_resource type="GDScript" id="GDScript_8n212"] script/source = "extends Node -var playing: bool = false -var changing_level: bool = false +var playing := false +var changing_level := false -var current_level_int: int = 0 -var levels = [ - preload(\"res://levels/base/level.tscn\"), - preload(\"res://levels/forest/level.tscn\"), - preload(\"res://levels/night/level.tscn\"), -] -var area_resource = preload(\"res://menus/main/area.tscn\") +var main_menu: Node +var current_level: Level +var current_level_scene: PackedScene +const res_main_menu = preload(\"res://menus/main/main_menu.tscn\") + +@onready var gui_gravity := $GUI/Gravity +@onready var gui_map := $GUI/Map @onready var gui_timer := $GUI/TopLeft/Timer @onready var gui_speed := $GUI/TopRight/Speed @onready var gui_rings := $GUI/BottomRight/Rings @@ -31,8 +33,6 @@ func _ready() -> void: $DevInfos.text += \"build \" + ProjectSettings.get_setting(\"application/config/version\") + \" (\" + OS.get_name() + \")\" ## Hide UI stuff that shouldn't be visible until later in the game - $VictoryScreen.hide() - $GUI.hide() pause_menu.hide() ## Connect to necessary signals @@ -40,107 +40,84 @@ func _ready() -> void: pause_menu.connect(\"request_fullscreen\", fullscreen_game) pause_menu.connect(\"request_restart\", restart_level) - launch_area() + set_main_menu() -func launch_area(): +func set_main_menu() -> void: Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) - var area := area_resource.instantiate() - area.connect(\"request_start\", start_game) - $Levels.add_child(area) + $GUI.hide() + + ## If no main menu exists yet, create one + if !is_instance_valid(main_menu): + main_menu = res_main_menu.instantiate() + main_menu.connect(\"request_play_level\", start_level) + add_child(main_menu) -## Get all the levels that are currently being played! -## In theory, there should ever be only zero or one, -## but it turns out it's more simple to handle things by using Arrays. -func get_current_levels() -> Array[Level]: - var children := $Levels.get_children(true) - var current_levels: Array[Level] = [] - for child in children: - if is_instance_of(child, Level): - current_levels.push_back(child) - return current_levels - -func start_level(level_scene: PackedScene) -> void: - $VictoryScreen.hide() - var level = level_scene.instantiate() - $Levels.add_child(level) - level.connect(\"completed\", stop_level) - level.connect(\"ring_collected\", func(): - gui_rings.remaining_rings = len(level.rings) - level.finished_rings_count +func start_level(level: Level, scene: PackedScene) -> void: + if is_instance_valid(current_level): + current_level.queue_free() + if is_instance_valid(main_menu) and self.is_ancestor_of(main_menu): + self.remove_child(main_menu) + + current_level = level + current_level_scene = scene + add_child(current_level) + + current_level.connect(\"started_playing\", func(): gui_timer.enabled = true) + current_level.connect(\"gravity_change\", gui_gravity.react_to_gravity_change) + current_level.connect(\"completed\", finish_current_level) + current_level.connect(\"ring_collected\", func(): + gui_rings.remaining_rings = len(current_level.rings) - current_level.finished_rings_count ) - gui_rings.remaining_rings = len(level.rings) - level.finished_rings_count + gui_rings.remaining_rings = len(current_level.rings) - current_level.finished_rings_count + + $GUI.show() + Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) + pause_game(false) + playing = true - PhysicsServer3D.area_set_param( - get_viewport().find_world_3d().space, - PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, - Vector3.DOWN - ) changing_level = false gui_timer.seconds_spent_level_attempt = 0.0 - gui_timer.enabled = true -func stop_level() -> void: +func finish_current_level() -> void: gui_timer.enabled = false - PhysicsServer3D.area_set_param(get_viewport().find_world_3d().space, PhysicsServer3D.AREA_PARAM_GRAVITY, 1) + PhysicsServer3D.area_set_param(get_viewport().find_world_3d().space, PhysicsServer3D.AREA_PARAM_GRAVITY, 0.5) playing = false changing_level = true SaveFiles.change_property(\"played_for\", gui_timer.seconds_spent_total, SaveFiles.selected_file) var save_file_data = SaveFiles.read(SaveFiles.selected_file) - var property_name := \"best_time_for_level_\" + str(current_level_int) + var property_name := current_level.id + \"_best_time\" if !save_file_data.has(property_name) or save_file_data[property_name] is not float or save_file_data[property_name] > gui_timer.seconds_spent_level_attempt: SaveFiles.change_property(property_name, gui_timer.seconds_spent_level_attempt, SaveFiles.selected_file) - var current_levels = get_current_levels() - for level in current_levels: - level.music.fadeOut(2) - await get_tree().create_timer(2).timeout - level.queue_free() - - if len(levels) > current_level_int + 1: - current_level_int += 1 - start_level(levels[current_level_int]) - else: - win_game() - -func win_game() -> void: - $VictoryScreen.show() + current_level.music.fadeOut(2) await get_tree().create_timer(2).timeout - launch_area() - $VictoryScreen.hide() + current_level.queue_free() + + set_main_menu() func restart_level() -> void: - var current_levels := get_current_levels() - if len(current_levels) && !changing_level: - pause_game(false) - changing_level = true - - for level in current_levels: - level.queue_free() - start_level(levels[current_level_int]) - -func start_game() -> void: - current_level_int = 0 - - var current_levels = $Levels.get_children(true) - for level in current_levels: - level.queue_free() - - $GUI.show() - Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) - start_level(levels[current_level_int]) + gui_timer.enabled = false + if !current_level or changing_level: + return + if current_level_scene is not PackedScene: + print(\"Tried to restart the level despite not having the level scene\") + return + SaveFiles.change_property(\"played_for\", gui_timer.seconds_spent_total, SaveFiles.selected_file) + start_level(current_level_scene.instantiate(), current_level_scene) func pause_game(to_pause: bool) -> void: - if !len(get_current_levels()) or changing_level: + if !is_instance_valid(current_level) or changing_level: return - SaveFiles.change_property(\"played_for\", gui_timer.seconds_spent_total, SaveFiles.selected_file) if to_pause: - $Levels.process_mode = Node.PROCESS_MODE_DISABLED + SaveFiles.change_property(\"played_for\", gui_timer.seconds_spent_total, SaveFiles.selected_file) + current_level.process_mode = Node.PROCESS_MODE_DISABLED playing = false pause_menu.show() Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) else: - $Levels.process_mode = Node.PROCESS_MODE_INHERIT + current_level.process_mode = Node.PROCESS_MODE_INHERIT playing = true pause_menu.hide() Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) @@ -153,12 +130,16 @@ func fullscreen_game() -> void: DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_WINDOWED) func _process(_delta: float) -> void: - var current_levels = get_current_levels() - if len(current_levels): - var current_level = current_levels[0] + if is_instance_valid(current_level): gui_speed.ball_velocity = current_level.velocity + if Input.is_action_pressed(\"display_map\"): + gui_map.display_rings(current_level.player, current_level.rings) func _input(_event: InputEvent) -> void: + if Input.is_action_just_released(\"display_map\"): + gui_map.hide() + if Input.is_action_just_pressed(\"display_map\"): + gui_map.show() if Input.is_action_just_pressed(\"restart_level\"): restart_level() @@ -166,11 +147,6 @@ func _on_btn_quit_pressed() -> void: get_tree().quit() " -[sub_resource type="LabelSettings" id="LabelSettings_1bs00"] -font_size = 160 -outline_size = 20 -outline_color = Color(0, 0, 0, 1) - [node name="Game" type="Node"] script = SubResource("GDScript_8n212") @@ -181,7 +157,14 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -mouse_filter = 1 +mouse_filter = 2 + +[node name="Gravity" parent="GUI" instance=ExtResource("1_ir7so")] +layout_mode = 1 + +[node name="Map" parent="GUI" instance=ExtResource("2_d1yoc")] +visible = false +layout_mode = 1 [node name="TopLeft" type="MarginContainer" parent="GUI"] layout_mode = 1 @@ -226,30 +209,6 @@ theme_override_constants/margin_bottom = 15 [node name="Rings" parent="GUI/BottomRight" instance=ExtResource("3_mbj17")] layout_mode = 2 -[node name="VictoryScreen" type="Control" parent="."] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="Label" type="Label" parent="VictoryScreen"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -text = "YOU WON!" -label_settings = SubResource("LabelSettings_1bs00") -horizontal_alignment = 1 -vertical_alignment = 1 - -[node name="Levels" type="Node" parent="."] - -[node name="PauseMenu" parent="." instance=ExtResource("4_3bfj3")] - [node name="DevInfos" type="Label" parent="."] anchors_preset = 12 anchor_top = 1.0 @@ -259,3 +218,5 @@ offset_top = -23.0 grow_horizontal = 2 grow_vertical = 0 horizontal_alignment = 1 + +[node name="PauseMenu" parent="." instance=ExtResource("4_3bfj3")] diff --git a/levels/base/level.tscn b/levels/base/level.tscn index 0683839..35859e2 100644 --- a/levels/base/level.tscn +++ b/levels/base/level.tscn @@ -1,12 +1,21 @@ -[gd_scene load_steps=12 format=3 uid="uid://ovtknjyj83gh"] +[gd_scene load_steps=11 format=3 uid="uid://ovtknjyj83gh"] -[ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_rj40i"] [ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="2_b00jj"] [ext_resource type="PackedScene" uid="uid://cpm3laywhlbq5" path="res://elements/ring.tscn" id="3_hel5x"] -[ext_resource type="PackedScene" uid="uid://dw0xl8644x166" path="res://elements/gravity.tscn" id="3_muudg"] [ext_resource type="PackedScene" uid="uid://c77bli40240nk" path="res://elements/sign.tscn" id="4_atq6y"] [ext_resource type="PackedScene" uid="uid://dnuakh7n3fuij" path="res://levels/base/music.tscn" id="4_uq42r"] +[sub_resource type="GDScript" id="GDScript_1yugx"] +script/source = "extends Level + +func _init() -> void: + self.id = \"base\" + +func _on_player_velocity_change(new_velocity: float) -> void: + self.velocity = new_velocity + self.music.adaptInstrumentsToVelocity(new_velocity * 1.5) +" + [sub_resource type="Gradient" id="Gradient_hs6gw"] [sub_resource type="GradientTexture2D" id="GradientTexture2D_hyysp"] @@ -30,11 +39,9 @@ ambient_light_color = Color(1, 1, 1, 1) reflected_light_source = 2 [node name="Base" type="Node3D"] -script = ExtResource("1_rj40i") +script = SubResource("GDScript_1yugx") metadata/_custom_type_script = "uid://w3fetao1pegm" -[node name="Gravity" parent="." instance=ExtResource("3_muudg")] - [node name="Environment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_y0yoy") @@ -155,6 +162,8 @@ transform = Transform3D(0.0172464, 0.899835, 0, -0.899835, 0.0172464, 0, 0, 0, 0 [node name="Ring13" parent="Rings" instance=ExtResource("3_hel5x")] transform = Transform3D(0.0114976, 0.59989, 0, -0.59989, 0.0114976, 0, 0, 0, 0.6, -414.274, 8.64172, 0) +[connection signal="velocity_change" from="Player" to="." method="_on_player_velocity_change"] + [editable path="Signs/Sign"] [editable path="Signs/Sign7"] [editable path="Signs/Sign2"] diff --git a/levels/demo.tscn b/levels/demo.tscn deleted file mode 100644 index 4e1e5ad..0000000 --- a/levels/demo.tscn +++ /dev/null @@ -1,50 +0,0 @@ -[gd_scene load_steps=11 format=3 uid="uid://0re2mcnpub4e"] - -[ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_scm0b"] -[ext_resource type="PackedScene" uid="uid://dw0xl8644x166" path="res://elements/gravity.tscn" id="2_fo4i1"] -[ext_resource type="PackedScene" uid="uid://drfy3vhe6skp1" path="res://levels/night/music.tscn" id="4_qr8kk"] -[ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="5_j5vh3"] -[ext_resource type="PackedScene" uid="uid://cpm3laywhlbq5" path="res://elements/ring.tscn" id="6_st6rs"] - -[sub_resource type="Gradient" id="Gradient_x6q8u"] - -[sub_resource type="GradientTexture1D" id="GradientTexture1D_lu6nv"] -gradient = SubResource("Gradient_x6q8u") - -[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_4yv3y"] -panorama = SubResource("GradientTexture1D_lu6nv") - -[sub_resource type="Sky" id="Sky_ys2yp"] -sky_material = SubResource("PanoramaSkyMaterial_4yv3y") - -[sub_resource type="Environment" id="Environment_8o42c"] -background_mode = 1 -sky = SubResource("Sky_ys2yp") -ambient_light_source = 2 -ambient_light_color = Color(1, 1, 1, 1) -reflected_light_source = 2 - -[node name="Demo" type="Node3D"] -script = ExtResource("1_scm0b") -metadata/_custom_type_script = "uid://w3fetao1pegm" - -[node name="Gravity" parent="." instance=ExtResource("2_fo4i1")] - -[node name="Environment" type="WorldEnvironment" parent="."] -environment = SubResource("Environment_8o42c") - -[node name="Music" parent="." instance=ExtResource("4_qr8kk")] - -[node name="Player" parent="." instance=ExtResource("5_j5vh3")] - -[node name="Rings" type="Node3D" parent="."] - -[node name="Ring" parent="Rings" instance=ExtResource("6_st6rs")] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 15.6731, -50) -visible = false - -[node name="Ring2" parent="Rings" instance=ExtResource("6_st6rs")] -transform = Transform3D(-4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0, 1, 10, -10, 0) - -[node name="Ring3" parent="Rings" instance=ExtResource("6_st6rs")] -transform = Transform3D(-4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0, 1, -10, -10, 0) diff --git a/levels/forest/level.tscn b/levels/forest/level.tscn index 36176ba..458d165 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -1,13 +1,32 @@ -[gd_scene load_steps=11 format=3 uid="uid://e1761h6d522a"] +[gd_scene load_steps=10 format=3 uid="uid://e1761h6d522a"] -[ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_fdxcj"] -[ext_resource type="PackedScene" uid="uid://dw0xl8644x166" path="res://elements/gravity.tscn" id="2_gxmta"] [ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="2_mjogx"] [ext_resource type="PackedScene" uid="uid://cakmsiye3hjfe" path="res://levels/forest/music.tscn" id="3_n1xsx"] [ext_resource type="PackedScene" uid="uid://cpm3laywhlbq5" path="res://elements/ring.tscn" id="4_p8yhq"] [ext_resource type="PackedScene" uid="uid://da6lkdiskdh8v" path="res://elements/tree.tscn" id="6_1e514"] [ext_resource type="PackedScene" uid="uid://c77bli40240nk" path="res://elements/sign.tscn" id="6_ifogr"] +[sub_resource type="GDScript" id="GDScript_mqwxc"] +script/source = "extends Level + +var cycle := 0 + +func _init() -> void: + self.id = \"forest\" + self.thumbnail = load(\"res://levels/forest/thumbnail.png\") + +func _process(_delta: float) -> void: + var moving_ring: Ring = $Rings/Ring18 + if !moving_ring.collected: + if moving_ring.position.y < 55 or moving_ring.position.y > 65: + cycle += 1 + moving_ring.position.y += 0.5 if cycle % 2 else -0.5 + +func _on_player_velocity_change(new_velocity: float) -> void: + self.velocity = new_velocity + self.music.adaptInstrumentsToVelocity(new_velocity) +" + [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_grcut"] sky_top_color = Color(0, 0.504542, 0.579919, 1) sky_horizon_color = Color(9.62615e-08, 0.776269, 0.496436, 1) @@ -29,11 +48,9 @@ ambient_light_color = Color(1, 1, 1, 1) reflected_light_source = 2 [node name="Forest" type="Node3D"] -script = ExtResource("1_fdxcj") +script = SubResource("GDScript_mqwxc") metadata/_custom_type_script = "uid://w3fetao1pegm" -[node name="Gravity" parent="." instance=ExtResource("2_gxmta")] - [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] transform = Transform3D(-0.920505, 0.195366, -0.338383, 0, 0.866025, 0.5, 0.390731, 0.460252, -0.797181, 0, 0, 0) shadow_enabled = true @@ -261,4 +278,6 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -218.208, 206.247, 0) [node name="Ring23" parent="Rings" instance=ExtResource("4_p8yhq")] transform = Transform3D(0.939693, 0.34202, 0, -0.34202, 0.939693, 0, 0, 0, 1, -1018.15, -96.9134, 0) +[connection signal="velocity_change" from="Player" to="." method="_on_player_velocity_change"] + [editable path="Sign"] diff --git a/levels/forest/thumbnail.png b/levels/forest/thumbnail.png new file mode 100644 index 0000000..07c40d6 Binary files /dev/null and b/levels/forest/thumbnail.png differ diff --git a/levels/forest/thumbnail.png.import b/levels/forest/thumbnail.png.import new file mode 100644 index 0000000..01c5a71 --- /dev/null +++ b/levels/forest/thumbnail.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dhyxogc13hnbe" +path="res://.godot/imported/thumbnail.png-6909560b6205778a3cb325a584af7ca5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://levels/forest/thumbnail.png" +dest_files=["res://.godot/imported/thumbnail.png-6909560b6205778a3cb325a584af7ca5.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/levels/level.gd b/levels/level.gd index 77f9795..ede459a 100644 --- a/levels/level.gd +++ b/levels/level.gd @@ -1,20 +1,47 @@ class_name Level extends Node3D +signal started_playing +signal gravity_change signal ring_collected signal completed -var seconds_spent: float = 0.00 +const gravity_strength_normal = 7 +const gravity_strength_strong = gravity_strength_normal * 3 + +## Lowercase, spaceless name of the level +var id: String +var thumbnail: CompressedTexture2D +var player: Node3D var music: Music -var velocity: float = 0.0 var rings: Array[Ring] = [] + var finished_rings_count: int = 0 +var velocity: float = 0.0 +var has_started_playing: bool = false + +func _init() -> void: + assert(len(id) > 0, self.name + " has no id!") func _ready() -> void: + PhysicsServer3D.area_set_param( + get_viewport().find_world_3d().space, + PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, + Vector3(0, 0, 0) + ) + + PhysicsServer3D.area_set_param( + get_viewport().find_world_3d().space, + PhysicsServer3D.AREA_PARAM_GRAVITY, + 0 + ) + var children = get_children() for child in children: if is_instance_of(child, Music): music = child + if child.name == "Player" and is_instance_of(child, Node3D): + player = child assert(is_instance_valid(music), self.name + " has no music!") var rings_node = get_node("Rings") @@ -32,7 +59,46 @@ func collect_ring(): if finished_rings_count >= len(rings): completed.emit() -func _process(delta: float) -> void: - seconds_spent += delta - velocity = abs($Player.velocity.x) + abs($Player.velocity.y) - music.adaptInstrumentsToVelocity(velocity, delta) +func _input(_event: InputEvent) -> void: + if Input.is_action_pressed("gravity_strong") or Input.is_action_just_released("gravity_strong"): + PhysicsServer3D.area_set_param( + get_viewport().find_world_3d().space, + PhysicsServer3D.AREA_PARAM_GRAVITY, + gravity_strength_strong if Input.is_action_pressed("gravity_strong") else gravity_strength_normal + ) + + var up := Input.is_action_just_pressed("gravity_up") + var left := Input.is_action_just_pressed("gravity_left") + var right := Input.is_action_just_pressed("gravity_right") + var down := Input.is_action_just_pressed("gravity_down") + + if up or left or right or down: + if !has_started_playing: + has_started_playing = true + started_playing.emit() + PhysicsServer3D.area_set_param( + get_viewport().find_world_3d().space, + PhysicsServer3D.AREA_PARAM_GRAVITY, + gravity_strength_strong if Input.is_action_pressed("gravity_strong") else gravity_strength_normal + ) + + var direction: Vector3 + if up: + direction = Vector3.UP + elif left: + direction = Vector3.LEFT + elif right: + direction = Vector3.RIGHT + elif down: + direction = Vector3.DOWN + + if direction && direction != PhysicsServer3D.area_get_param( + get_viewport().find_world_3d().space, + PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR + ): + PhysicsServer3D.area_set_param( + get_viewport().find_world_3d().space, + PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, + direction + ) + gravity_change.emit(direction) diff --git a/levels/music.gd b/levels/music.gd index 1d14740..a11f3c7 100644 --- a/levels/music.gd +++ b/levels/music.gd @@ -40,7 +40,9 @@ func changeVolume(db: float) -> void: AudioServer.set_bus_volume_db(bus_index, db + Settings.volume_music) AudioServer.set_bus_mute(bus_index, Settings.volume_music <= -15.0) -func adaptInstrumentsToVelocity(velocity: float, delta: float) -> void: +func adaptInstrumentsToVelocity(velocity: float) -> void: + ## temp + const delta = 0.00833333333333 var instruments_needed = floor(velocity / 8) var instruments_playing = instruments.filter(func(i: AudioStreamPlayer): return i.volume_db > -50) diff --git a/levels/night/level.tscn b/levels/night/level.tscn index 08f79de..1b0ee50 100644 --- a/levels/night/level.tscn +++ b/levels/night/level.tscn @@ -1,14 +1,27 @@ -[gd_scene load_steps=14 format=3 uid="uid://drnqmu4lka22d"] +[gd_scene load_steps=13 format=3 uid="uid://drnqmu4lka22d"] -[ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_3m1pa"] [ext_resource type="PackedScene" uid="uid://drfy3vhe6skp1" path="res://levels/night/music.tscn" id="1_npc74"] [ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="2_bc1ig"] -[ext_resource type="PackedScene" uid="uid://dw0xl8644x166" path="res://elements/gravity.tscn" id="2_lfplq"] [ext_resource type="PackedScene" uid="uid://cpm3laywhlbq5" path="res://elements/ring.tscn" id="4_brcr0"] [ext_resource type="PackedScene" uid="uid://c77bli40240nk" path="res://elements/sign.tscn" id="6_36yav"] [ext_resource type="PackedScene" uid="uid://b4jtpua36m6b1" path="res://elements/star.tscn" id="7_ltcl0"] [ext_resource type="PackedScene" uid="uid://da6lkdiskdh8v" path="res://elements/tree.tscn" id="8_kgstj"] +[sub_resource type="GDScript" id="GDScript_akhnt"] +script/source = "extends Level + +func _init() -> void: + self.id = \"night\" + +func _process(delta: float) -> void: + $Tree.rotate_x(delta / 3) + $Tree.rotate_y(delta) + +func _on_player_velocity_change(new_velocity: float) -> void: + self.velocity = new_velocity + self.music.adaptInstrumentsToVelocity(new_velocity * 2) +" + [sub_resource type="Gradient" id="Gradient_x6q8u"] [sub_resource type="GradientTexture1D" id="GradientTexture1D_lu6nv"] @@ -28,11 +41,9 @@ ambient_light_color = Color(1, 1, 1, 1) reflected_light_source = 2 [node name="Night" type="Node3D"] -script = ExtResource("1_3m1pa") +script = SubResource("GDScript_akhnt") metadata/_custom_type_script = "uid://w3fetao1pegm" -[node name="Gravity" parent="." instance=ExtResource("2_lfplq")] - [node name="Environment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_8o42c") @@ -193,7 +204,7 @@ transform = Transform3D(-1.31134e-07, -3, 0, 3, -1.31134e-07, 0, 0, 0, 3, -104.2 transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, -12.3896, -2, 0) [node name="Tree" parent="." instance=ExtResource("8_kgstj")] -transform = Transform3D(-3.79443, -7.04289, 0, 7.04289, -3.79443, 0, 0, 0, 8, 28.958, -413.185, -93.8242) +transform = Transform3D(-3.79271, -7.04382, 0, 7.04382, -3.79271, 0, 0, 0, 8, 28.958, -413.185, -93.8242) [node name="Signs" type="Node3D" parent="."] @@ -327,6 +338,8 @@ transform = Transform3D(-0.99693, 0.0731445, -0.0279554, -0.0555776, -0.912445, [node name="Star32" parent="Stars" instance=ExtResource("7_ltcl0")] transform = Transform3D(-0.99693, 0.0731445, -0.0279554, -0.0555776, -0.912445, -0.405407, -0.055161, -0.402609, 0.913709, 0.952739, 0.24193, 7.10684) +[connection signal="velocity_change" from="Player" to="." method="_on_player_velocity_change"] + [editable path="Signs/Sign"] [editable path="Signs/Sign4"] [editable path="Signs/Sign2"] diff --git a/menus/main/area.tscn b/menus/main/area.tscn deleted file mode 100644 index 0759372..0000000 --- a/menus/main/area.tscn +++ /dev/null @@ -1,146 +0,0 @@ -[gd_scene load_steps=10 format=3 uid="uid://ikeidrgprk8k"] - -[ext_resource type="PackedScene" uid="uid://wlhsarkeqe8r" path="res://menus/main/panel.tscn" id="1_qfa5o"] -[ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="2_0jxef"] - -[sub_resource type="GDScript" id="GDScript_bt14i"] -script/source = "extends Node3D - -signal request_start - -var start_menu = preload(\"res://menus/main/start_menu.tscn\") -var save_file_manager = preload(\"res://menus/main/save_file_manager.tscn\") -var settings_menu = preload(\"res://menus/main/settings_menu.tscn\") - -func _ready() -> void: - $Menus/Panel2.connect(\"request_return\", func(): rotate_cube_to(0)) - $Menus/Panel3.connect(\"request_return\", func(): rotate_cube_to(90)) - $Menus/Panel4.connect(\"request_return\", func(): rotate_cube_to(180)) - - var sm = start_menu.instantiate() - sm.connect(\"request_start\", start) - sm.connect(\"request_settings\", settings) - $Menus/Panel1.change_menu(sm, false) - -func start() -> void: - var svm = save_file_manager.instantiate() - svm.connect(\"request_start\", func(): request_start.emit()) - $Menus/Panel2.change_menu(svm) - rotate_cube_to(90) - -func settings() -> void: - var sm = settings_menu.instantiate() - $Menus/Panel2.change_menu(sm) - rotate_cube_to(90) - -func rotate_cube_to(degrees: int) -> void: - if $Menus/AnimationPlayer.is_playing(): - return - degrees = -degrees - - var animation: Animation = $Menus/AnimationPlayer.get_animation(\"rotate\") - var new_rotation = Vector3(0, degrees, 0) - animation.track_set_key_value(0, 0, $Menus.rotation_degrees) - animation.track_set_key_value(0, 1, new_rotation) - - $Menus/AnimationPlayer.play(\"rotate\") -" - -[sub_resource type="Animation" id="Animation_0jxef"] -length = 0.001 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:rotation_degrees") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [Vector3(0, 0, 0)] -} - -[sub_resource type="Animation" id="Animation_bt14i"] -resource_name = "rotate" -length = 0.3 -step = 0.3 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:rotation_degrees") -tracks/0/interp = 2 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.3), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [Vector3(0, 0, 0), Vector3(0, 0, 0)] -} - -[sub_resource type="AnimationLibrary" id="AnimationLibrary_3bwb0"] -_data = { -&"RESET": SubResource("Animation_0jxef"), -&"rotate": SubResource("Animation_bt14i") -} - -[sub_resource type="PhysicalSkyMaterial" id="PhysicalSkyMaterial_bt14i"] -ground_color = Color(0.794232, 0.673177, 0.531056, 1) - -[sub_resource type="Sky" id="Sky_0jxef"] -sky_material = SubResource("PhysicalSkyMaterial_bt14i") - -[sub_resource type="Environment" id="Environment_qfa5o"] -background_mode = 2 -background_color = Color(0.804743, 0.804743, 0.804743, 1) -sky = SubResource("Sky_0jxef") -ambient_light_source = 3 -ambient_light_color = Color(0.986752, 0.986752, 0.986752, 1) -reflected_light_source = 2 - -[node name="Area" type="Node3D"] -process_mode = 3 -script = SubResource("GDScript_bt14i") - -[node name="Menus" type="CSGBox3D" parent="."] -use_collision = true - -[node name="Panel1" parent="Menus" instance=ExtResource("1_qfa5o")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.501) - -[node name="Panel2" parent="Menus" instance=ExtResource("1_qfa5o")] -transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 0.501, 0, 0) - -[node name="Panel3" parent="Menus" instance=ExtResource("1_qfa5o")] -transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, -0.501) - -[node name="Panel4" parent="Menus" instance=ExtResource("1_qfa5o")] -transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -0.501, 0, 0) - -[node name="AnimationPlayer" type="AnimationPlayer" parent="Menus"] -libraries = { -&"": SubResource("AnimationLibrary_3bwb0") -} - -[node name="Camera3D" type="Camera3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2.5) -current = true -fov = 40.0 - -[node name="WorldEnvironment" type="WorldEnvironment" parent="."] -environment = SubResource("Environment_qfa5o") - -[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] -transform = Transform3D(0.819152, -0.412596, 0.39844, 0, 0.694658, 0.71934, -0.573576, -0.589249, 0.569031, 0, 0, 0) - -[node name="Player" parent="." instance=ExtResource("2_0jxef")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.87335, 0) - -[node name="Camera" parent="Player" index="0"] -visible = false -current = false - -[node name="GPUParticles3D" parent="Player/Sphere" index="2"] -visible = false - -[editable path="Player"] diff --git a/menus/main/start_menu.tscn b/menus/main/initial_menu.tscn similarity index 97% rename from menus/main/start_menu.tscn rename to menus/main/initial_menu.tscn index f62f59a..0df392b 100644 --- a/menus/main/start_menu.tscn +++ b/menus/main/initial_menu.tscn @@ -16,7 +16,7 @@ func _on_btn_exit_pressed() -> void: get_tree().quit() " -[node name="StartMenu" type="Control"] +[node name="InitialMenu" type="Control"] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 diff --git a/menus/main/level_selection_menu.tscn b/menus/main/level_selection_menu.tscn new file mode 100644 index 0000000..5fc9a1f --- /dev/null +++ b/menus/main/level_selection_menu.tscn @@ -0,0 +1,152 @@ +[gd_scene load_steps=2 format=3 uid="uid://d3b12iqla7uh6"] + +[sub_resource type="GDScript" id="GDScript_rmgh7"] +script/source = "extends Control + +signal request_play_level + +@onready var carousel := $VBoxContainer/Selection/Carousel +@onready var label_time := $VBoxContainer/Presentation/MarginContainer/VBoxContainer/BestTime +const levels = [ + \"res://levels/base/level.tscn\", + \"res://levels/forest/level.tscn\", + \"res://levels/night/level.tscn\", +] +var loaded_level: Level +var loaded_level_scene: PackedScene +var loaded_level_path: String + +func _enter_tree() -> void: + if len(loaded_level_path): + prepare_level(loaded_level_path) + +func _ready() -> void: + var placeholders := carousel.get_children() + for placeholder in placeholders: + placeholder.queue_free() + + for i in len(levels): + var btn := Button.new() + btn.text = \"Level \" + str(i + 1) + btn.connect(\"pressed\", func(): prepare_level(levels[i])) + carousel.add_child(btn) + +func prepare_level(level_scene_path: String): + if is_instance_valid(loaded_level): + loaded_level.queue_free() + + ResourceLoader.load_threaded_request(level_scene_path) + loaded_level_scene = ResourceLoader.load_threaded_get(level_scene_path) + loaded_level = loaded_level_scene.instantiate() + loaded_level_path = level_scene_path + $VBoxContainer/Presentation/Thumbnail.texture = loaded_level.thumbnail + $VBoxContainer/Presentation/MarginContainer/VBoxContainer/LevelName.text = \"The \" + loaded_level.id.capitalize() + $VBoxContainer/Presentation.show() + $VBoxContainer/MarginContainer/PlayButton.show() + display_file_data(SaveFiles.read(SaveFiles.selected_file)) + +func display_file_data(data: Variant) -> void: + label_time.text = \"Best time: \" + var property_name := loaded_level.id + \"_best_time\" + if data.has(property_name) and data[property_name] is float: + var seconds: float = data[property_name] + var minutes: int = floor(seconds / 60) + label_time.text += (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) + else: + label_time.text += \"00:00.000\" + +func _on_play_button_pressed() -> void: + if is_instance_valid(loaded_level): + request_play_level.emit(loaded_level, loaded_level_scene) + +func seconds_to_readable(seconds: float) -> String: + var minutes: int = floor(seconds / 60) + return (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) + +## This prevents a memory leak that is possible when this menu is destroyed +## and the loaded_level becomes unavailable to the rest of the game +## For example in the main menu, go to this menu, select a level, +## press \"return\" and go to this menu again, boom, memory leak prevented by this +func _notification(what: int) -> void: + match what: + NOTIFICATION_PREDELETE: + if is_instance_valid(loaded_level) && !loaded_level.is_inside_tree(): + loaded_level.queue_free() +" + +[node name="LevelSelectionMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = SubResource("GDScript_rmgh7") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Selection" type="ScrollContainer" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 + +[node name="Carousel" type="HBoxContainer" parent="VBoxContainer/Selection"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 10 +alignment = 1 + +[node name="Placeholder1" type="Button" parent="VBoxContainer/Selection/Carousel"] +layout_mode = 2 +theme_override_constants/icon_max_width = 150 +text = "Placeholder" + +[node name="Placeholder2" type="Button" parent="VBoxContainer/Selection/Carousel"] +layout_mode = 2 +text = "Placeholder" + +[node name="Placeholder3" type="Button" parent="VBoxContainer/Selection/Carousel"] +layout_mode = 2 +text = "Placeholder" + +[node name="Presentation" type="HBoxContainer" parent="VBoxContainer"] +visible = false +layout_mode = 2 + +[node name="Thumbnail" type="TextureRect" parent="VBoxContainer/Presentation"] +custom_minimum_size = Vector2(150, 150) +layout_mode = 2 +expand_mode = 1 +stretch_mode = 4 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/Presentation"] +layout_mode = 2 +theme_override_constants/margin_left = 5 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Presentation/MarginContainer"] +layout_mode = 2 +alignment = 1 + +[node name="LevelName" type="Label" parent="VBoxContainer/Presentation/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Placeholder" + +[node name="BestTime" type="Label" parent="VBoxContainer/Presentation/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Best time: 00:00.000" + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_top = 15 + +[node name="PlayButton" type="Button" parent="VBoxContainer/MarginContainer"] +visible = false +layout_mode = 2 +text = "Play!" + +[connection signal="pressed" from="VBoxContainer/MarginContainer/PlayButton" to="." method="_on_play_button_pressed"] diff --git a/menus/main/main_menu.tscn b/menus/main/main_menu.tscn new file mode 100644 index 0000000..235b9b6 --- /dev/null +++ b/menus/main/main_menu.tscn @@ -0,0 +1,296 @@ +[gd_scene load_steps=16 format=3 uid="uid://ikeidrgprk8k"] + +[ext_resource type="PackedScene" uid="uid://wlhsarkeqe8r" path="res://menus/main/panel.tscn" id="1_5vmsf"] +[ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="2_2rexg"] + +[sub_resource type="GDScript" id="GDScript_bt14i"] +script/source = "extends Node3D + +signal request_play_level + +func _enter_tree() -> void: + $Camera3D/AnimationPlayer.play_backwards(\"camera_pan\") + PhysicsServer3D.area_set_param( + get_viewport().find_world_3d().space, + PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, + Vector3.DOWN + ) + +# Display on Panel 1 +const res_initial_menu = preload(\"res://menus/main/initial_menu.tscn\") +func _ready() -> void: + $Menus/Panel2.connect(\"request_return\", func(): rotate_cube_to(0)) + $Menus/Panel3.connect(\"request_return\", func(): rotate_cube_to(90)) + $Menus/Panel4.connect(\"request_return\", func(): rotate_cube_to(180)) + + var initial_menu := res_initial_menu.instantiate() + initial_menu.connect(\"request_start\", start) + initial_menu.connect(\"request_settings\", settings) + $Menus/Panel1.change_menu(initial_menu, false) + +# Display on Panel 2 +const res_settings_menu = preload(\"res://menus/main/settings_menu.tscn\") +func settings() -> void: + var settings_menu := res_settings_menu.instantiate() + $Menus/Panel2.change_menu(settings_menu) + rotate_cube_to(90) + +const res_save_file_manager = preload(\"res://menus/main/save_file_manager.tscn\") +func start() -> void: + var save_file_manager := res_save_file_manager.instantiate() + save_file_manager.connect(\"request_start\", level_selection) + $Menus/Panel2.change_menu(save_file_manager) + rotate_cube_to(90) + +# Display on Panel 3 +const res_level_selection_menu = preload(\"res://menus/main/level_selection_menu.tscn\") +func level_selection() -> void: + var level_selection_menu := res_level_selection_menu.instantiate() + level_selection_menu.connect(\"request_play_level\", func(level: Level, scene: PackedScene): + $Camera3D/AnimationPlayer.play(\"camera_pan\") + await get_tree().create_timer(1.5).timeout + request_play_level.emit(level, scene) + ) + $Menus/Panel3.change_menu(level_selection_menu) + rotate_cube_to(180) + +# cube +func rotate_cube_to(degrees: int) -> void: + if $Menus/AnimationPlayer.is_playing(): + return + + var cube_animation: Animation = $Menus/AnimationPlayer.get_animation(\"rotate\") + cube_animation.track_set_key_value(0, 0, $Menus.rotation_degrees) + cube_animation.track_set_key_value(0, 1, Vector3(0, -degrees, 0)) + $Menus/AnimationPlayer.play(\"rotate\") + + if $AnimationPlayer.is_playing(): + await get_tree().create_timer($AnimationPlayer.current_animation_length).timeout + var sky_animation: Animation = $AnimationPlayer.get_animation(\"sky_brightness\") + sky_animation.track_set_key_value(0, 0, $WorldEnvironment.environment.sky.sky_material.energy_multiplier) + sky_animation.track_set_key_value(0, 1, max(90, degrees * 2) / 90) + $AnimationPlayer.play(\"sky_brightness\") +" + +[sub_resource type="Animation" id="Animation_0jxef"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:rotation_degrees") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector3(0, 0, 0)] +} + +[sub_resource type="Animation" id="Animation_bt14i"] +resource_name = "rotate" +length = 0.3 +step = 0.3 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:rotation_degrees") +tracks/0/interp = 2 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.3), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Vector3(0, 0, 0), Vector3(0, 0, 0)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_3bwb0"] +_data = { +&"RESET": SubResource("Animation_0jxef"), +&"rotate": SubResource("Animation_bt14i") +} + +[sub_resource type="Animation" id="Animation_2rexg"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("WorldEnvironment:environment:sky:sky_material:energy_multiplier") +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("Camera3D:fov") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [40.0] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Camera3D:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector3(0, 0, 2.5)] +} + +[sub_resource type="Animation" id="Animation_jinmx"] +resource_name = "camera_pan" +length = 1.5 +step = 0.1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Camera3D:fov") +tracks/0/interp = 2 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1.5), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [40.0, 75.0] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Camera3D:position") +tracks/1/interp = 2 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 1.5), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Vector3(0, 0, 2.5), Vector3(0, 0, 15)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_jinmx"] +_data = { +&"RESET": SubResource("Animation_2rexg"), +&"camera_pan": SubResource("Animation_jinmx") +} + +[sub_resource type="PhysicalSkyMaterial" id="PhysicalSkyMaterial_bt14i"] +ground_color = Color(1, 0.484315, 0.700558, 1) + +[sub_resource type="Sky" id="Sky_0jxef"] +sky_material = SubResource("PhysicalSkyMaterial_bt14i") + +[sub_resource type="Environment" id="Environment_qfa5o"] +background_mode = 2 +background_color = Color(0.804743, 0.804743, 0.804743, 1) +sky = SubResource("Sky_0jxef") +ambient_light_source = 3 +ambient_light_color = Color(0.986752, 0.986752, 0.986752, 1) +reflected_light_source = 2 + +[sub_resource type="Animation" id="Animation_j2kye"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("../../WorldEnvironment:environment:sky:sky_material:energy_multiplier") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [1.0] +} + +[sub_resource type="Animation" id="Animation_gma2u"] +resource_name = "sky_brightness" +length = 0.3 +step = 0.1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("../../WorldEnvironment:environment:sky:sky_material:energy_multiplier") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.3), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [1.0, 1.0] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_wruj6"] +_data = { +&"RESET": SubResource("Animation_j2kye"), +&"sky_brightness": SubResource("Animation_gma2u") +} + +[node name="MainMenu" type="Node3D"] +process_mode = 3 +script = SubResource("GDScript_bt14i") + +[node name="Menus" type="CSGBox3D" parent="."] +use_collision = true + +[node name="Panel1" parent="Menus" instance=ExtResource("1_5vmsf")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.501) + +[node name="Panel2" parent="Menus" instance=ExtResource("1_5vmsf")] +transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 0.501, 0, 0) + +[node name="Panel3" parent="Menus" instance=ExtResource("1_5vmsf")] +transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, -0.501) + +[node name="Panel4" parent="Menus" instance=ExtResource("1_5vmsf")] +transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -0.501, 0, 0) + +[node name="AnimationPlayer" type="AnimationPlayer" parent="Menus"] +libraries = { +&"": SubResource("AnimationLibrary_3bwb0") +} + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2.5) +current = true +fov = 40.0 + +[node name="AnimationPlayer" type="AnimationPlayer" parent="Camera3D"] +root_node = NodePath("../..") +libraries = { +&"": SubResource("AnimationLibrary_jinmx") +} + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_qfa5o") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(0.819152, -0.412596, 0.39844, 0, 0.694658, 0.71934, -0.573576, -0.589249, 0.569031, 0, 0, 0) + +[node name="Player" parent="." instance=ExtResource("2_2rexg")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 0) + +[node name="Camera" parent="Player" index="0"] +visible = false +current = false + +[node name="GPUParticles3D" parent="Player/Sphere" index="2"] +visible = false + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +root_node = NodePath("../Camera3D/AnimationPlayer") +libraries = { +&"": SubResource("AnimationLibrary_wruj6") +} + +[editable path="Player"] diff --git a/menus/main/save_file_manager.tscn b/menus/main/save_file_manager.tscn index 10688de..8054c7e 100644 --- a/menus/main/save_file_manager.tscn +++ b/menus/main/save_file_manager.tscn @@ -6,21 +6,31 @@ script/source = "extends Control signal request_start @onready var label_name = $VBoxContainer/MarginContainer/VBoxContainer/Description/Name -@onready var label_time = $VBoxContainer/MarginContainer/VBoxContainer/Description/Time + +func _ready() -> void: + $VBoxContainer/MarginContainer.hide() + +## Get the newest \"played_for\" when the main menu appears again +func _enter_tree() -> void: + if len(SaveFiles.selected_file): + display_file_data(SaveFiles.selected_file) func _on_save_1_pressed() -> void: label_name.text = $VBoxContainer/Save1.text - display_file_data(SaveFiles.read(SaveFiles.names[0])) + display_file_data(SaveFiles.names[0]) func _on_save_2_pressed() -> void: label_name.text = $VBoxContainer/Save2.text - display_file_data(SaveFiles.read(SaveFiles.names[1])) + display_file_data(SaveFiles.names[1]) func _on_save_3_pressed() -> void: label_name.text = $VBoxContainer/Save3.text - display_file_data(SaveFiles.read(SaveFiles.names[2])) + display_file_data(SaveFiles.names[2]) -func display_file_data(data: Variant) -> void: +func display_file_data(file_name: String) -> void: + $VBoxContainer/MarginContainer.show() + var label_time := $VBoxContainer/MarginContainer/VBoxContainer/Description/Time + var data = SaveFiles.read(file_name) $VBoxContainer/MarginContainer.show() label_time.text = \" | \" if data.has(\"played_for\") and data.played_for is float: @@ -28,14 +38,14 @@ func display_file_data(data: Variant) -> void: var minutes: int = floor(seconds / 60) label_time.text += (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) else: - label_time.text += \"00:00\" + label_time.text += \"00:00.000\" func _on_start_pressed() -> void: request_start.emit() func _on_delete_pressed() -> void: SaveFiles.empty(SaveFiles.selected_file) - display_file_data(SaveFiles.read(SaveFiles.selected_file)) + display_file_data(SaveFiles.selected_file) " [node name="SaveFileManager" type="Control"] diff --git a/menus/menu.tscn b/menus/menu.tscn index 62e1204..7551ab7 100644 --- a/menus/menu.tscn +++ b/menus/menu.tscn @@ -114,10 +114,12 @@ libraries = { } [node name="Label" type="Label" parent="VSplitContainer"] +custom_minimum_size = Vector2(1, 1) layout_mode = 2 text = "MENU NAME" label_settings = SubResource("LabelSettings_2d4ws") horizontal_alignment = 1 +autowrap_mode = 3 uppercase = true [node name="MarginContainer" type="MarginContainer" parent="VSplitContainer"] diff --git a/project.godot b/project.godot index 7bbf422..3a00814 100644 --- a/project.godot +++ b/project.godot @@ -13,7 +13,7 @@ config_version=5 config/name="DreamBall" config/description="Manipulate the gravity to make a ball go through every ring! https://kitsunes.dev/Taevas/DreamBall" -config/version="20250423.0" +config/version="20250503.0" run/main_scene="res://index.tscn" config/features=PackedStringArray("4.4", "GL Compatibility") boot_splash/show_image=false @@ -78,6 +78,13 @@ restart_level={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":2,"pressure":0.0,"pressed":false,"script":null) ] } +display_map={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":false,"script":null) +] +} [physics] @@ -86,6 +93,7 @@ common/physics_ticks_per_second=120 common/max_physics_steps_per_frame=20 3d/physics_engine="GodotPhysics3D" 3d/default_gravity=1.0 +3d/default_gravity_vector=Vector3(0, -0.5, 0) 3d/solver/solver_iterations=40 3d/solver/default_contact_bias=1.0 common/physics_interpolation=true diff --git a/save_files.gd b/save_files.gd index 2a5013c..8521082 100644 --- a/save_files.gd +++ b/save_files.gd @@ -6,7 +6,7 @@ const names := [ "user://save_file_3.save", ] -var selected_file := names[0] +var selected_file: String func _ready() -> void: for save_file_name in names: @@ -22,6 +22,7 @@ func empty(save_file_name: String) -> void: write(JSON.stringify({}), save_file_name) func read(save_file_name: String) -> Variant: + print("Reading from save file ", save_file_name) ensure_existence(save_file_name) selected_file = save_file_name @@ -41,6 +42,7 @@ func write(json_string: String, save_file_name: String) -> void: save_file.store_line("FOR YOUR SAFETY, ALWAYS CHECK IF THE DATA OF THE FILES YOU DOWNLOAD LOOKS OKAY") func change_property(property: String, value, save_file_name: String) -> void: + print("Changing property '", property, "' on save file ", save_file_name) ensure_existence(save_file_name) var data = read(save_file_name) data[property] = value