From a3e0c4d82d61372da391552fef0a7b293bfbc678 Mon Sep 17 00:00:00 2001 From: Taevas Date: Sun, 23 Mar 2025 14:48:54 +0100 Subject: [PATCH 01/20] move to Godot 4.4 (from Godot 4.3) --- elements/gravity.tscn | 6 +++--- elements/player.tscn | 2 +- index.tscn | 8 ++++---- levels/base/level.tscn | 2 +- levels/base/music.tscn | 2 +- levels/demo_level.tscn | 2 +- levels/forest/level.tscn | 2 +- levels/forest/music.tscn | 2 +- levels/level.gd.uid | 1 + levels/music.gd.uid | 1 + levels/night/level.tscn | 2 +- levels/night/music.tscn | 2 +- project.godot | 9 ++++----- 13 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 levels/level.gd.uid create mode 100644 levels/music.gd.uid diff --git a/elements/gravity.tscn b/elements/gravity.tscn index 05ae674..533c7a3 100644 --- a/elements/gravity.tscn +++ b/elements/gravity.tscn @@ -120,8 +120,8 @@ tracks/1/keys = { [sub_resource type="AnimationLibrary" id="AnimationLibrary_0w0ne"] _data = { -"RESET": SubResource("Animation_6rwl4"), -"grow_fadeout": SubResource("Animation_ll6d0") +&"RESET": SubResource("Animation_6rwl4"), +&"grow_fadeout": SubResource("Animation_ll6d0") } [node name="Gravity" type="Control"] @@ -154,7 +154,7 @@ script = SubResource("GDScript_my602") [node name="AnimationPlayer" type="AnimationPlayer" parent="Direction"] libraries = { -"": SubResource("AnimationLibrary_0w0ne") +&"": SubResource("AnimationLibrary_0w0ne") } [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="Direction"] diff --git a/elements/player.tscn b/elements/player.tscn index c08d971..eee888e 100644 --- a/elements/player.tscn +++ b/elements/player.tscn @@ -6,7 +6,7 @@ [sub_resource type="GDScript" id="GDScript_bnqcw"] script/source = "extends Node3D -func _process(_delta: float) -> void: +func _physics_process(_delta: float) -> void: $SpotLightMain.position = $Sphere.position + Vector3(0, 5, 0) $SpotLightTop.position = $Sphere.position $SpotLightLeft.position = $Sphere.position diff --git a/index.tscn b/index.tscn index d09f96c..1588f15 100644 --- a/index.tscn +++ b/index.tscn @@ -142,7 +142,7 @@ func _process(_delta: float) -> void: var current_level = current_levels[0] if is_instance_valid(current_level): if \"velocity\" in current_level: - $Informations/MarginContainer/VBoxContainer/Speed.text = str(floor(current_level.velocity)) + \" km/h\" + $Informations/MarginContainer/VBoxContainer/Speed.text = str(int(floor(current_level.velocity))) + \" km/h\" if \"rings_count\" in current_level and \"finished_rings_count\" in current_level: $Informations/MarginContainer/VBoxContainer/Rings.text = str(current_level.finished_rings_count) + \"/\" + str(current_level.rings_count) @@ -229,8 +229,8 @@ tracks/0/keys = { [sub_resource type="AnimationLibrary" id="AnimationLibrary_5cb2g"] _data = { -"RESET": SubResource("Animation_glb01"), -"fadeout": SubResource("Animation_75r06") +&"RESET": SubResource("Animation_glb01"), +&"fadeout": SubResource("Animation_75r06") } [node name="Game" type="Node"] @@ -433,7 +433,7 @@ horizontal_alignment = 1 [node name="AnimationPlayer" type="AnimationPlayer" parent="StartMenu"] libraries = { -"": SubResource("AnimationLibrary_5cb2g") +&"": SubResource("AnimationLibrary_5cb2g") } [node name="PauseMenu" type="Control" parent="."] diff --git a/levels/base/level.tscn b/levels/base/level.tscn index d0ec56b..9973e76 100644 --- a/levels/base/level.tscn +++ b/levels/base/level.tscn @@ -2,7 +2,7 @@ [ext_resource type="Material" uid="uid://brwwlwb1rpro7" path="res://graphics/unhappy_material.tres" id="1_dwsqy"] [ext_resource type="PackedScene" uid="uid://ygvokp5f78i5" path="res://levels/base/environment.tscn" id="1_n7fd8"] -[ext_resource type="Script" path="res://levels/level.gd" id="1_rj40i"] +[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://c77bli40240nk" path="res://elements/sign.tscn" id="4_atq6y"] diff --git a/levels/base/music.tscn b/levels/base/music.tscn index d6381e3..a562ee8 100644 --- a/levels/base/music.tscn +++ b/levels/base/music.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=8 format=3 uid="uid://cgt5st4qacruw"] [ext_resource type="AudioStream" uid="uid://b46a7y6vdqd4n" path="res://levels/base/music/hihat-closed.ogg" id="1_shxcq"] -[ext_resource type="Script" path="res://levels/music.gd" id="1_y1f1r"] +[ext_resource type="Script" uid="uid://dgpjhli3hpn0u" path="res://levels/music.gd" id="1_y1f1r"] [ext_resource type="AudioStream" uid="uid://buag3j2s22jpo" path="res://levels/base/music/hihat-open.ogg" id="3_xb717"] [ext_resource type="AudioStream" uid="uid://djmbrvp7k263k" path="res://levels/base/music/kick2.ogg" id="4_1da6f"] [ext_resource type="AudioStream" uid="uid://dma7btls87mg4" path="res://levels/base/music/kick.ogg" id="5_7thjx"] diff --git a/levels/demo_level.tscn b/levels/demo_level.tscn index 056ed1f..d5f31aa 100644 --- a/levels/demo_level.tscn +++ b/levels/demo_level.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://n40mg5tc3bnv"] -[ext_resource type="Script" path="res://levels/level.gd" id="1_ifscd"] +[ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_ifscd"] [ext_resource type="PackedScene" uid="uid://b6gnffoboc5j5" path="res://levels/night/environment.tscn" id="2_v6rg2"] [ext_resource type="PackedScene" uid="uid://q7frlnx37gv6" path="res://levels/night/music.tscn" id="3_fb0ct"] [ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="4_ge8id"] diff --git a/levels/forest/level.tscn b/levels/forest/level.tscn index 2f096e1..52a620c 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=8 format=3 uid="uid://dfvuxfxc6ooya"] [ext_resource type="PackedScene" uid="uid://w4h8ip754qnb" path="res://levels/forest/environment.tscn" id="1_7clrg"] -[ext_resource type="Script" path="res://levels/level.gd" id="1_fdxcj"] +[ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_fdxcj"] [ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="2_mjogx"] [ext_resource type="PackedScene" uid="uid://dp8nvfm55te85" path="res://levels/forest/music.tscn" id="3_n1xsx"] [ext_resource type="PackedScene" uid="uid://cpm3laywhlbq5" path="res://elements/ring.tscn" id="4_p8yhq"] diff --git a/levels/forest/music.tscn b/levels/forest/music.tscn index d56669b..bb697e1 100644 --- a/levels/forest/music.tscn +++ b/levels/forest/music.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=8 format=3 uid="uid://dp8nvfm55te85"] -[ext_resource type="Script" path="res://levels/music.gd" id="1_whsrg"] +[ext_resource type="Script" uid="uid://dgpjhli3hpn0u" path="res://levels/music.gd" id="1_whsrg"] [ext_resource type="AudioStream" uid="uid://pn2b2f3egpck" path="res://levels/forest/music/dong2.ogg" id="2_pm2p2"] [ext_resource type="AudioStream" uid="uid://dghw6a7i3adcu" path="res://levels/forest/music/dong.ogg" id="3_1eawt"] [ext_resource type="AudioStream" uid="uid://bu4kmowmputnr" path="res://levels/forest/music/hihat-closed.ogg" id="4_kv4yx"] diff --git a/levels/level.gd.uid b/levels/level.gd.uid new file mode 100644 index 0000000..0c108ea --- /dev/null +++ b/levels/level.gd.uid @@ -0,0 +1 @@ +uid://w3fetao1pegm diff --git a/levels/music.gd.uid b/levels/music.gd.uid new file mode 100644 index 0000000..decd46e --- /dev/null +++ b/levels/music.gd.uid @@ -0,0 +1 @@ +uid://dgpjhli3hpn0u diff --git a/levels/night/level.tscn b/levels/night/level.tscn index 9ae261d..53c873a 100644 --- a/levels/night/level.tscn +++ b/levels/night/level.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=9 format=3 uid="uid://c2rlx31om4gey"] -[ext_resource type="Script" path="res://levels/level.gd" id="1_3m1pa"] +[ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_3m1pa"] [ext_resource type="PackedScene" uid="uid://q7frlnx37gv6" 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://b6gnffoboc5j5" path="res://levels/night/environment.tscn" id="2_wintp"] diff --git a/levels/night/music.tscn b/levels/night/music.tscn index d065280..72c4cee 100644 --- a/levels/night/music.tscn +++ b/levels/night/music.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=8 format=3 uid="uid://q7frlnx37gv6"] -[ext_resource type="Script" path="res://levels/music.gd" id="1_ok6lg"] +[ext_resource type="Script" uid="uid://dgpjhli3hpn0u" path="res://levels/music.gd" id="1_ok6lg"] [ext_resource type="AudioStream" uid="uid://dcgmuddvkcw6" path="res://levels/night/music/hihat-closed.ogg" id="2_oeg0c"] [ext_resource type="AudioStream" uid="uid://deqxl1qmwu51s" path="res://levels/night/music/hihat-open.ogg" id="3_olgpr"] [ext_resource type="AudioStream" uid="uid://028dtbgdh0os" path="res://levels/night/music/kick.ogg" id="4_utlv1"] diff --git a/project.godot b/project.godot index ba95dec..605a759 100644 --- a/project.godot +++ b/project.godot @@ -11,12 +11,11 @@ config_version=5 [application] config/name="DreamBall" -config/description="I'm Taevas, and this is \"DreamBall\", my entry for the 21st edition of \"Alakajam!\", a gamejam that lasts for 48 hours and starts on 2025-02-21 19:00UTC -https://alakajam.com/21st-alakajam -https://kitsunes.dev/Taevas/AlakajamEntry" -config/version="20250223.2" +config/description="Manipulate the gravity to make a ball go through every ring! +https://kitsunes.dev/Taevas/DreamBall" +config/version="20250323.0" run/main_scene="res://index.tscn" -config/features=PackedStringArray("4.3", "GL Compatibility") +config/features=PackedStringArray("4.4", "GL Compatibility") boot_splash/show_image=false config/icon="res://icon.png" config/windows_native_icon="res://icon.ico" From c0aed38065407209eb0eb13dbd6fb6fd004600f5 Mon Sep 17 00:00:00 2001 From: Taevas Date: Mon, 24 Mar 2025 18:13:08 +0100 Subject: [PATCH 02/20] properly fade out the music when finishing a level --- icon.svg | 1 - icon.svg.import | 37 ---------------------- index.tscn | 14 +++------ levels/base/level.tscn | 2 +- levels/base/music.tscn | 3 +- levels/demo_level.tscn | 2 +- levels/forest/level.tscn | 2 +- levels/forest/music.tscn | 3 +- levels/level.gd | 32 +++++-------------- levels/music.gd | 66 +++++++++++++++++++++++++++++++++++----- levels/night/level.tscn | 2 +- levels/night/music.tscn | 3 +- 12 files changed, 80 insertions(+), 87 deletions(-) delete mode 100644 icon.svg delete mode 100644 icon.svg.import diff --git a/icon.svg b/icon.svg deleted file mode 100644 index 9d8b7fa..0000000 --- a/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import deleted file mode 100644 index 9a07b08..0000000 --- a/icon.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c4nwhhum2vht5" -path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://icon.svg" -dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.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/index.tscn b/index.tscn index 1588f15..814531d 100644 --- a/index.tscn +++ b/index.tscn @@ -48,19 +48,13 @@ func stop_level() -> void: $Timer.paused = true changing_level = true - AudioServer.set_bus_volume_db(0, -5) - await get_tree().create_timer(0.5).timeout - AudioServer.set_bus_volume_db(0, -10) - await get_tree().create_timer(0.5).timeout - AudioServer.set_bus_volume_db(0, -30) - await get_tree().create_timer(0.5).timeout - AudioServer.set_bus_volume_db(0, -50) - await get_tree().create_timer(0.5).timeout - var next_level: PackedScene var current_levels = $Levels.get_children(true) if len(current_levels): - var current_level = current_levels[0] + var current_level: Level = current_levels[0] + current_level.music.fadeOut(2) + await get_tree().create_timer(2).timeout + if current_level.get_meta(\"name\") == \"Base\": next_level = forest elif current_level.get_meta(\"name\") == \"Forest\": diff --git a/levels/base/level.tscn b/levels/base/level.tscn index 9973e76..faa000e 100644 --- a/levels/base/level.tscn +++ b/levels/base/level.tscn @@ -6,7 +6,7 @@ [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://c77bli40240nk" path="res://elements/sign.tscn" id="4_atq6y"] -[ext_resource type="PackedScene" uid="uid://cgt5st4qacruw" path="res://levels/base/music.tscn" id="4_uq42r"] +[ext_resource type="PackedScene" uid="uid://dnuakh7n3fuij" path="res://levels/base/music.tscn" id="4_uq42r"] [node name="Base" type="Node3D"] script = ExtResource("1_rj40i") diff --git a/levels/base/music.tscn b/levels/base/music.tscn index a562ee8..f6a2329 100644 --- a/levels/base/music.tscn +++ b/levels/base/music.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://cgt5st4qacruw"] +[gd_scene load_steps=8 format=3 uid="uid://dnuakh7n3fuij"] [ext_resource type="AudioStream" uid="uid://b46a7y6vdqd4n" path="res://levels/base/music/hihat-closed.ogg" id="1_shxcq"] [ext_resource type="Script" uid="uid://dgpjhli3hpn0u" path="res://levels/music.gd" id="1_y1f1r"] @@ -10,6 +10,7 @@ [node name="Music" type="Node"] script = ExtResource("1_y1f1r") +metadata/_custom_type_script = "uid://dgpjhli3hpn0u" [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] stream = ExtResource("1_shxcq") diff --git a/levels/demo_level.tscn b/levels/demo_level.tscn index d5f31aa..493312b 100644 --- a/levels/demo_level.tscn +++ b/levels/demo_level.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_ifscd"] [ext_resource type="PackedScene" uid="uid://b6gnffoboc5j5" path="res://levels/night/environment.tscn" id="2_v6rg2"] -[ext_resource type="PackedScene" uid="uid://q7frlnx37gv6" path="res://levels/night/music.tscn" id="3_fb0ct"] +[ext_resource type="PackedScene" uid="uid://drfy3vhe6skp1" path="res://levels/night/music.tscn" id="3_fb0ct"] [ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="4_ge8id"] [ext_resource type="PackedScene" uid="uid://cpm3laywhlbq5" path="res://elements/ring.tscn" id="5_j060p"] diff --git a/levels/forest/level.tscn b/levels/forest/level.tscn index 52a620c..deffdc1 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -3,7 +3,7 @@ [ext_resource type="PackedScene" uid="uid://w4h8ip754qnb" path="res://levels/forest/environment.tscn" id="1_7clrg"] [ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_fdxcj"] [ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="2_mjogx"] -[ext_resource type="PackedScene" uid="uid://dp8nvfm55te85" path="res://levels/forest/music.tscn" id="3_n1xsx"] +[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"] diff --git a/levels/forest/music.tscn b/levels/forest/music.tscn index bb697e1..5d3d541 100644 --- a/levels/forest/music.tscn +++ b/levels/forest/music.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://dp8nvfm55te85"] +[gd_scene load_steps=8 format=3 uid="uid://cakmsiye3hjfe"] [ext_resource type="Script" uid="uid://dgpjhli3hpn0u" path="res://levels/music.gd" id="1_whsrg"] [ext_resource type="AudioStream" uid="uid://pn2b2f3egpck" path="res://levels/forest/music/dong2.ogg" id="2_pm2p2"] @@ -10,6 +10,7 @@ [node name="Music" type="Node"] script = ExtResource("1_whsrg") +metadata/_custom_type_script = "uid://dgpjhli3hpn0u" [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] stream = ExtResource("2_pm2p2") diff --git a/levels/level.gd b/levels/level.gd index 025014a..7950043 100644 --- a/levels/level.gd +++ b/levels/level.gd @@ -1,20 +1,19 @@ +class_name Level extends Node3D +var music: Music var velocity: float = 0.0 var rings_count = 72727 var finished_rings_count = 0 var rings: Array[Node] = [] -var stream_players: Array[AudioStreamPlayer] = [] - func _ready() -> void: - var music_node = get_node("Music") - assert(is_instance_valid(music_node), self.name + " has no music!") - var music = music_node.get_children() - for music_player in music: - if music_player is AudioStreamPlayer: - stream_players.push_back(music_player) + var children = get_children() + for child in children: + if is_instance_of(child, Music): + music = child + assert(is_instance_valid(music), self.name + " has no music!") var rings_node = get_node("Rings") assert(is_instance_valid(rings_node), self.name + " has no rings!") @@ -33,19 +32,4 @@ func do_we_end_yet(): func _process(delta: float) -> void: var sphere = $Player/Sphere velocity = abs(sphere.linear_velocity.x) + abs(sphere.linear_velocity.y) - var instruments_needed = floor(velocity / 8) - var instruments_playing = stream_players.filter(func(p: AudioStreamPlayer): return p.volume_db > -50) - - for index_p in len(instruments_playing): - var playing = instruments_playing[index_p] - if index_p + 1 > instruments_needed: - playing.volume_db = max(playing.volume_db - (delta * 32), -50) - elif playing.volume_db <= 0: - playing.volume_db = min(playing.volume_db + (delta * 8), 0) - - if instruments_needed > len(instruments_playing): - var instruments_not_playing = stream_players.filter(func(p: AudioStreamPlayer): return p.volume_db <= -50) - if len(instruments_not_playing): - var to_play = instruments_not_playing.pick_random() - if is_instance_valid(to_play): - to_play.volume_db = min(to_play.volume_db + (delta * 16), 0) + music.adaptInstrumentsToVelocity(velocity, delta) diff --git a/levels/music.gd b/levels/music.gd index 801fb3e..a98710a 100644 --- a/levels/music.gd +++ b/levels/music.gd @@ -1,15 +1,65 @@ +class_name Music extends Node +## While the instruments are manipulated individually to match the gameplay, +## they are all regrouped in a single bus dedicated to this specific music. +## So we also manipulate the bus itself for the purposes of applying +## audio balance and settings and the such more efficiently. + +var bus_index: int = -1 +var instruments: Array[AudioStreamPlayer] = [] + func _ready() -> void: - var players = get_children() - for player in players: - if player is AudioStreamPlayer: - player.volume_db = -50 - var stream: AudioStreamOggVorbis = player.stream + bus_index = AudioServer.get_bus_index(self.name) + if bus_index == -1: + AudioServer.add_bus() + AudioServer.set_bus_name(AudioServer.bus_count - 1, self.name) + bus_index = AudioServer.get_bus_index(self.name) + + var current_effects = AudioServer.get_bus_effect_count(bus_index) + for i in current_effects: + AudioServer.remove_bus_effect(bus_index, i) + + var children = get_children() + for child in children: + if child is AudioStreamPlayer: + instruments.push_back(child) + child.bus = AudioServer.get_bus_name(bus_index) + child.volume_db = -50 + var stream: AudioStreamOggVorbis = child.stream if stream is AudioStreamOggVorbis: stream.loop = true stream.bpm = 124 - for player in players: - if player is AudioStreamPlayer: - player.play() + for instrument in instruments: + if instrument is AudioStreamPlayer: + instrument.play() + +func changeVolume(db: float) -> void: + AudioServer.set_bus_volume_db(bus_index, db) + +func adaptInstrumentsToVelocity(velocity: float, delta: float) -> void: + var instruments_needed = floor(velocity / 8) + var instruments_playing = instruments.filter(func(i: AudioStreamPlayer): return i.volume_db > -50) + + for index_p in len(instruments_playing): + var playing = instruments_playing[index_p] + if index_p + 1 > instruments_needed: + playing.volume_db = max(playing.volume_db - (delta * 32), -50) + elif playing.volume_db <= 0: + playing.volume_db = min(playing.volume_db + (delta * 8), 0) + + if instruments_needed > len(instruments_playing): + var instruments_not_playing = instruments.filter(func(i: AudioStreamPlayer): return i.volume_db <= -50) + if len(instruments_not_playing): + var to_play = instruments_not_playing.pick_random() + if is_instance_valid(to_play): + to_play.volume_db = min(to_play.volume_db + (delta * 16), 0) + +func fadeOut(seconds: float) -> void: + var tween: Tween = create_tween() + # https://github.com/godotengine/godot/issues/32882 + AudioServer.add_bus_effect(bus_index, AudioEffectAmplify.new()) + var effect_index: int = AudioServer.get_bus_effect_count(bus_index) - 1 + var effect: AudioEffectAmplify = AudioServer.get_bus_effect(bus_index, effect_index) + tween.tween_property(effect, "volume_linear", 0, seconds) diff --git a/levels/night/level.tscn b/levels/night/level.tscn index 53c873a..595b988 100644 --- a/levels/night/level.tscn +++ b/levels/night/level.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=9 format=3 uid="uid://c2rlx31om4gey"] [ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_3m1pa"] -[ext_resource type="PackedScene" uid="uid://q7frlnx37gv6" path="res://levels/night/music.tscn" id="1_npc74"] +[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://b6gnffoboc5j5" path="res://levels/night/environment.tscn" id="2_wintp"] [ext_resource type="PackedScene" uid="uid://cpm3laywhlbq5" path="res://elements/ring.tscn" id="4_brcr0"] diff --git a/levels/night/music.tscn b/levels/night/music.tscn index 72c4cee..ee2b7be 100644 --- a/levels/night/music.tscn +++ b/levels/night/music.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://q7frlnx37gv6"] +[gd_scene load_steps=8 format=3 uid="uid://drfy3vhe6skp1"] [ext_resource type="Script" uid="uid://dgpjhli3hpn0u" path="res://levels/music.gd" id="1_ok6lg"] [ext_resource type="AudioStream" uid="uid://dcgmuddvkcw6" path="res://levels/night/music/hihat-closed.ogg" id="2_oeg0c"] @@ -10,6 +10,7 @@ [node name="Music" type="Node"] script = ExtResource("1_ok6lg") +metadata/_custom_type_script = "uid://dgpjhli3hpn0u" [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] stream = ExtResource("2_oeg0c") From 4afec6386ca769543eab6e20ec6b40a3c070c5eb Mon Sep 17 00:00:00 2001 From: Taevas Date: Tue, 25 Mar 2025 13:39:03 +0100 Subject: [PATCH 03/20] graphics update: forest level --- elements/ring.tscn | 4 ++-- graphics/happy_material.tres | 4 ++-- graphics/unhappy_material.tres | 2 ++ index.tscn | 5 +++-- levels/forest/environment.tscn | 2 +- levels/forest/level.tscn | 32 +++++++++++++++++++++++++++++--- 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/elements/ring.tscn b/elements/ring.tscn index 898bee7..e10d852 100644 --- a/elements/ring.tscn +++ b/elements/ring.tscn @@ -33,8 +33,8 @@ stream_1/stream = ExtResource("3_lkqw0") use_collision = true inner_radius = 4.0 outer_radius = 8.0 -sides = 16 -ring_sides = 8 +sides = 32 +ring_sides = 16 material = ExtResource("1_48h5f") script = SubResource("GDScript_86vhg") diff --git a/graphics/happy_material.tres b/graphics/happy_material.tres index 4cdfa67..8d285ac 100644 --- a/graphics/happy_material.tres +++ b/graphics/happy_material.tres @@ -1,5 +1,5 @@ [gd_resource type="StandardMaterial3D" format=3 uid="uid://df0n6u78ylb4h"] [resource] -albedo_color = Color(1, 0, 1, 1) -metallic_specular = 0.0 +albedo_color = Color(6.73831e-07, 0.700198, 0.789641, 1) +metallic_specular = 1.0 diff --git a/graphics/unhappy_material.tres b/graphics/unhappy_material.tres index e5d0a3d..c5bb7a3 100644 --- a/graphics/unhappy_material.tres +++ b/graphics/unhappy_material.tres @@ -1,4 +1,6 @@ [gd_resource type="StandardMaterial3D" format=3 uid="uid://brwwlwb1rpro7"] [resource] +albedo_color = Color(1, 0.32686, 0.232259, 1) metallic = 1.0 +metallic_specular = 0.0 diff --git a/index.tscn b/index.tscn index 814531d..35fb4b5 100644 --- a/index.tscn +++ b/index.tscn @@ -20,7 +20,9 @@ func _ready() -> void: $Informations.hide() $PauseMenu.hide() ## Add the version of the game and the name of the OS to the footer of the start menu - $StartMenu/VBoxContainer/MarginContainer2/Notice.text += \" - build \" + ProjectSettings.get_setting(\"application/config/version\") + \" (\" + os_name + \")\" + if OS.has_feature(\"editor\"): + $StartMenu/VBoxContainer/MarginContainer2/Notice.text = \"dev \" + $StartMenu/VBoxContainer/MarginContainer2/Notice.text += \"build \" + ProjectSettings.get_setting(\"application/config/version\") + \" (\" + os_name + \")\" func start_level(level_scene: PackedScene) -> void: $VictoryScreen.hide() @@ -421,7 +423,6 @@ theme_override_constants/margin_bottom = 20 [node name="Notice" type="Label" parent="StartMenu/VBoxContainer/MarginContainer2"] layout_mode = 2 -text = "Made during the 21st edition of Alakajam!" label_settings = SubResource("LabelSettings_5jdmh") horizontal_alignment = 1 diff --git a/levels/forest/environment.tscn b/levels/forest/environment.tscn index 4fd7d86..b4b9656 100644 --- a/levels/forest/environment.tscn +++ b/levels/forest/environment.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=4 format=3 uid="uid://w4h8ip754qnb"] +[gd_scene load_steps=4 format=3 uid="uid://6nfv4ewj3jop"] [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_clvpw"] sky_top_color = Color(0, 0.504542, 0.579919, 1) diff --git a/levels/forest/level.tscn b/levels/forest/level.tscn index deffdc1..bb5308e 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -1,6 +1,6 @@ -[gd_scene load_steps=8 format=3 uid="uid://dfvuxfxc6ooya"] +[gd_scene load_steps=11 format=3 uid="uid://dfvuxfxc6ooya"] -[ext_resource type="PackedScene" uid="uid://w4h8ip754qnb" path="res://levels/forest/environment.tscn" id="1_7clrg"] +[ext_resource type="PackedScene" uid="uid://6nfv4ewj3jop" path="res://levels/forest/environment.tscn" id="1_7clrg"] [ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_fdxcj"] [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"] @@ -8,11 +8,37 @@ [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="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) +sky_curve = 0.219613 +ground_bottom_color = Color(0.078119, 0.275869, 4.81308e-08, 1) +ground_horizon_color = Color(0.300489, 0.406247, 0.0024617, 1) +sun_angle_max = 224.83 +sun_curve = 0.212132 + +[sub_resource type="Sky" id="Sky_gxmta"] +sky_material = SubResource("ProceduralSkyMaterial_grcut") + +[sub_resource type="Environment" id="Environment_y3whn"] +background_mode = 2 +sky = SubResource("Sky_gxmta") +sky_rotation = Vector3(0.0872665, 0.698132, 0.0872665) +ambient_light_source = 3 +ambient_light_color = Color(1, 1, 1, 1) +reflected_light_source = 2 + [node name="Forest" type="Node3D"] script = ExtResource("1_fdxcj") metadata/name = "Forest" +[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 +directional_shadow_mode = 0 + [node name="Environment" parent="." instance=ExtResource("1_7clrg")] +environment = SubResource("Environment_y3whn") [node name="Music" parent="." instance=ExtResource("3_n1xsx")] @@ -110,7 +136,7 @@ transform = Transform3D(-49.1471, 0, -9.19585, 0, 50, 0, 9.19585, 0, -49.1471, - transform = Transform3D(-28.781, 0, 40.8859, 0, 50, 0, -40.8859, 0, -28.781, -460.677, -329.892, 361.614) [node name="Tree28" parent="Trees" instance=ExtResource("6_1e514")] -transform = Transform3D(-28.781, 0, 40.8859, 0, 50, 0, -40.8859, 0, -28.781, -643.482, -296.989, 101.924) +transform = Transform3D(-28.781, 0, 40.8859, 0, 50, 0, -40.8859, 0, -28.781, -643.482, -296.989, 139.279) [node name="Tree29" parent="Trees" instance=ExtResource("6_1e514")] transform = Transform3D(-29.5106, 0, 40.3624, 0, 50, 0, -40.3624, 0, -29.5106, -418.092, -296.989, 136.2) From df7736a9d13f11d340e4da04d2838ea826404b90 Mon Sep 17 00:00:00 2001 From: Taevas Date: Tue, 25 Mar 2025 17:48:00 +0100 Subject: [PATCH 04/20] better in-game timer as in, use `_process()` and add a number could you lag on purpose before finishing to cut time? probably not --- elements/player.tscn | 67 ++++++++++++++--- elements/ring.tscn | 3 +- elements/sphere.tscn | 52 ------------- index.tscn | 157 ++++++++++++++++++--------------------- levels/base/level.tscn | 9 ++- levels/demo.tscn | 32 ++++++++ levels/demo_level.tscn | 29 -------- levels/forest/level.tscn | 7 +- levels/level.gd | 7 +- levels/night/level.tscn | 7 +- 10 files changed, 180 insertions(+), 190 deletions(-) delete mode 100644 elements/sphere.tscn create mode 100644 levels/demo.tscn delete mode 100644 levels/demo_level.tscn diff --git a/elements/player.tscn b/elements/player.tscn index eee888e..6df684a 100644 --- a/elements/player.tscn +++ b/elements/player.tscn @@ -1,10 +1,10 @@ -[gd_scene load_steps=4 format=3 uid="uid://cnnvwotv33u1b"] - -[ext_resource type="PackedScene" uid="uid://dw0xl8644x166" path="res://elements/gravity.tscn" id="1_h0h2o"] -[ext_resource type="PackedScene" uid="uid://df5m716pd6mej" path="res://elements/sphere.tscn" id="2_ophnk"] +[gd_scene load_steps=11 format=3 uid="uid://cnnvwotv33u1b"] [sub_resource type="GDScript" id="GDScript_bnqcw"] -script/source = "extends Node3D +script/source = "class_name Player +extends Node3D + +var velocity: Vector3 = Vector3(0, 0, 0) func _physics_process(_delta: float) -> void: $SpotLightMain.position = $Sphere.position + Vector3(0, 5, 0) @@ -17,7 +17,7 @@ func _physics_process(_delta: float) -> void: $Camera.position = $Sphere.position + Vector3(0, 0, 15) ## Angle of the camera, so the player can see where the ball is going - var velocity: Vector3 = $Sphere.linear_velocity + velocity = $Sphere.linear_velocity var desired_x = max(min(velocity.y, 35), -35) var desired_y = max(min(-velocity.x, 50), -50) var difference_x = $Camera.rotation_degrees.x - desired_x @@ -31,13 +31,43 @@ func _physics_process(_delta: float) -> void: $Camera.fov = default_fov + (total_velocity / 12) " +[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_vumbr"] +bounce = 0.2 + +[sub_resource type="SphereShape3D" id="SphereShape3D_xfgrm"] + +[sub_resource type="Gradient" id="Gradient_bgp4x"] +colors = PackedColorArray(0.942957, 7.89344e-06, 0.597148, 1, 0, 0.9912, 0.9912, 1) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_5d6uk"] +gradient = SubResource("Gradient_bgp4x") +fill_from = Vector2(0.504951, 0.00495049) +fill_to = Vector2(0.549505, 1) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_qcgmu"] +albedo_texture = SubResource("GradientTexture2D_5d6uk") + +[sub_resource type="SphereMesh" id="SphereMesh_bor8g"] +material = SubResource("StandardMaterial3D_qcgmu") + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_4tofj"] +direction = Vector3(0, 0, 0) +gravity = Vector3(0, 0, 0) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_2ybet"] +vertex_color_use_as_albedo = true +albedo_color = Color(0, 0.734117, 0.734117, 1) + +[sub_resource type="SphereMesh" id="SphereMesh_1m3ut"] +material = SubResource("StandardMaterial3D_2ybet") +radius = 0.2 +height = 0.4 +radial_segments = 8 +rings = 4 + [node name="Player" type="Node3D"] script = SubResource("GDScript_bnqcw") -[node name="Gravity" parent="." instance=ExtResource("1_h0h2o")] - -[node name="Sphere" parent="." instance=ExtResource("2_ophnk")] - [node name="Camera" type="Camera3D" parent="."] current = true @@ -61,3 +91,20 @@ spot_range = 50.0 [node name="SpotLightBottom" type="SpotLight3D" parent="."] transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, 0) spot_range = 50.0 + +[node name="Sphere" type="RigidBody3D" parent="."] +collision_priority = 100.0 +axis_lock_linear_z = true +physics_material_override = SubResource("PhysicsMaterial_vumbr") +contact_monitor = true + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Sphere"] +shape = SubResource("SphereShape3D_xfgrm") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Sphere"] +mesh = SubResource("SphereMesh_bor8g") + +[node name="GPUParticles3D" type="GPUParticles3D" parent="Sphere"] +amount = 16 +process_material = SubResource("ParticleProcessMaterial_4tofj") +draw_pass_1 = SubResource("SphereMesh_1m3ut") diff --git a/elements/ring.tscn b/elements/ring.tscn index e10d852..32e5295 100644 --- a/elements/ring.tscn +++ b/elements/ring.tscn @@ -5,7 +5,8 @@ [ext_resource type="AudioStream" uid="uid://cvvs74ep5d4wt" path="res://sounds/ring2.ogg" id="3_lkqw0"] [sub_resource type="GDScript" id="GDScript_86vhg"] -script/source = "extends CSGTorus3D +script/source = "class_name Ring +extends CSGTorus3D var happy_material = preload(\"res://graphics/happy_material.tres\") diff --git a/elements/sphere.tscn b/elements/sphere.tscn deleted file mode 100644 index 77e697e..0000000 --- a/elements/sphere.tscn +++ /dev/null @@ -1,52 +0,0 @@ -[gd_scene load_steps=10 format=3 uid="uid://df5m716pd6mej"] - -[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_vumbr"] -bounce = 0.2 - -[sub_resource type="SphereShape3D" id="SphereShape3D_xfgrm"] - -[sub_resource type="Gradient" id="Gradient_bgp4x"] -colors = PackedColorArray(0.942957, 7.89344e-06, 0.597148, 1, 0, 0.9912, 0.9912, 1) - -[sub_resource type="GradientTexture2D" id="GradientTexture2D_5d6uk"] -gradient = SubResource("Gradient_bgp4x") -fill_from = Vector2(0.504951, 0.00495049) -fill_to = Vector2(0.549505, 1) - -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_qcgmu"] -albedo_texture = SubResource("GradientTexture2D_5d6uk") - -[sub_resource type="SphereMesh" id="SphereMesh_bor8g"] -material = SubResource("StandardMaterial3D_qcgmu") - -[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_4tofj"] -direction = Vector3(0, 0, 0) -gravity = Vector3(0, 0, 0) - -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_2ybet"] -vertex_color_use_as_albedo = true -albedo_color = Color(0, 0.734117, 0.734117, 1) - -[sub_resource type="SphereMesh" id="SphereMesh_1m3ut"] -material = SubResource("StandardMaterial3D_2ybet") -radius = 0.2 -height = 0.4 -radial_segments = 8 -rings = 4 - -[node name="Sphere" type="RigidBody3D"] -collision_priority = 100.0 -axis_lock_linear_z = true -physics_material_override = SubResource("PhysicsMaterial_vumbr") -contact_monitor = true - -[node name="CollisionShape3D" type="CollisionShape3D" parent="."] -shape = SubResource("SphereShape3D_xfgrm") - -[node name="MeshInstance3D" type="MeshInstance3D" parent="."] -mesh = SubResource("SphereMesh_bor8g") - -[node name="GPUParticles3D" type="GPUParticles3D" parent="."] -amount = 16 -process_material = SubResource("ParticleProcessMaterial_4tofj") -draw_pass_1 = SubResource("SphereMesh_1m3ut") diff --git a/index.tscn b/index.tscn index 35fb4b5..0e835f4 100644 --- a/index.tscn +++ b/index.tscn @@ -1,15 +1,20 @@ [gd_scene load_steps=11 format=3 uid="uid://ccgnnif026wb4"] -[ext_resource type="PackedScene" uid="uid://n40mg5tc3bnv" path="res://levels/demo_level.tscn" id="1_08svf"] +[ext_resource type="PackedScene" uid="uid://dtfybhftwfn00" path="res://levels/demo.tscn" id="1_356j3"] [sub_resource type="GDScript" id="GDScript_8n212"] script/source = "extends Node -var seconds_spent: float = 0.0 +var playing: bool = false +var seconds_spent: float = 0.00 var changing_level: bool = false -var base = preload(\"res://levels/base/level.tscn\") -var forest = preload(\"res://levels/forest/level.tscn\") -var night = preload(\"res://levels/night/level.tscn\") + +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\"), +] func _ready() -> void: var os_name = OS.get_name() @@ -24,77 +29,76 @@ func _ready() -> void: $StartMenu/VBoxContainer/MarginContainer2/Notice.text = \"dev \" $StartMenu/VBoxContainer/MarginContainer2/Notice.text += \"build \" + ProjectSettings.get_setting(\"application/config/version\") + \" (\" + os_name + \")\" +## 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() - if level_scene and level_scene.can_instantiate(): - var level = level_scene.instantiate() - $Levels.add_child(level) - $Timer.paused = false - AudioServer.set_bus_volume_db(0, 0) - PhysicsServer3D.area_set_param( - get_viewport().find_world_3d().space, - PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, - Vector3.DOWN - ) - changing_level = false - else: - $VictoryScreen.show() - await get_tree().create_timer(2).timeout - $StartMenu/AnimationPlayer.play_backwards(\"fadeout\") - Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) - await get_tree().create_timer(1).timeout - $StartMenu.modulate = Color(1,1,1,1) + var level = level_scene.instantiate() + $Levels.add_child(level) + playing = true + PhysicsServer3D.area_set_param( + get_viewport().find_world_3d().space, + PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, + Vector3.DOWN + ) + changing_level = false func stop_level() -> void: PhysicsServer3D.area_set_param(get_viewport().find_world_3d().space, PhysicsServer3D.AREA_PARAM_GRAVITY, 1) - $Timer.paused = true + playing = false changing_level = true - var next_level: PackedScene - var current_levels = $Levels.get_children(true) - if len(current_levels): - var current_level: Level = current_levels[0] - current_level.music.fadeOut(2) + var current_levels = get_current_levels() + for level in current_levels: + level.music.fadeOut(2) await get_tree().create_timer(2).timeout - - if current_level.get_meta(\"name\") == \"Base\": - next_level = forest - elif current_level.get_meta(\"name\") == \"Forest\": - next_level = night - - for level in current_levels: - level.queue_free() + level.queue_free() - start_level(next_level) + 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() + await get_tree().create_timer(2).timeout + $StartMenu/AnimationPlayer.play_backwards(\"fadeout\") + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + await get_tree().create_timer(1).timeout + $StartMenu.modulate = Color(1,1,1,1) func restart_level() -> void: if changing_level == false: - var current_levels = $Levels.get_children(true) + var current_levels = get_current_levels() if len(current_levels): pause_game() changing_level = true - var current_level = current_levels[0] - - var next_level: PackedScene = base - if current_level.get_meta(\"name\") == \"Forest\": - next_level = forest - elif current_level.get_meta(\"name\") == \"Night\": - next_level = night for level in current_levels: level.queue_free() - start_level(next_level) + start_level(levels[current_level_int]) func pause_game() -> void: if $Levels.process_mode == PROCESS_MODE_INHERIT: $Levels.process_mode = Node.PROCESS_MODE_DISABLED - $Timer.paused = true + playing = false $PauseMenu.show() Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) else: $Levels.process_mode = Node.PROCESS_MODE_INHERIT - $Timer.paused = false + playing = true $PauseMenu.hide() Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) @@ -105,21 +109,18 @@ func fullscreen_game() -> void: else: DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_WINDOWED) -func _on_btn_fullscreen_pressed() -> void: - fullscreen_game() - func _on_btn_start_pressed() -> void: seconds_spent = 0.0 - var current_levels = $Levels.get_children(true) + current_level_int = 0 + + var current_levels = get_current_levels() for level in current_levels: level.queue_free() $Informations.show() $StartMenu/AnimationPlayer.play(\"fadeout\") Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) - start_level(base) - - $Timer.start(0) + start_level(levels[0]) func _on_animation_player_animation_finished(_anim_name: StringName) -> void: if $StartMenu.modulate == Color(1,1,1,1): @@ -127,20 +128,19 @@ func _on_animation_player_animation_finished(_anim_name: StringName) -> void: else: $StartMenu.hide() -func _on_timer_timeout() -> void: - seconds_spent = snapped(seconds_spent + 0.10, 0.01) - var minutes = floor(seconds_spent / 60) - $Informations/MarginContainer/Timer.text = (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.1f\" % [4, seconds_spent - (minutes * 60)]) +func update_timer(delta: float) -> void: + if playing: + seconds_spent += delta + var minutes = floor(seconds_spent / 60) + $Informations/MarginContainer/Timer.text = (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.2f\" % [5, seconds_spent - (minutes * 60)]) -func _process(_delta: float) -> void: - var current_levels = $Levels.get_children(true) +func _process(delta: float) -> void: + update_timer(delta) + var current_levels = get_current_levels() if len(current_levels): var current_level = current_levels[0] - if is_instance_valid(current_level): - if \"velocity\" in current_level: - $Informations/MarginContainer/VBoxContainer/Speed.text = str(int(floor(current_level.velocity))) + \" km/h\" - if \"rings_count\" in current_level and \"finished_rings_count\" in current_level: - $Informations/MarginContainer/VBoxContainer/Rings.text = str(current_level.finished_rings_count) + \"/\" + str(current_level.rings_count) + $Informations/MarginContainer/VBoxContainer/Speed.text = str(int(floor(current_level.velocity))) + \" km/h\" + $Informations/MarginContainer/VBoxContainer/Rings.text = str(current_level.finished_rings_count) + \"/\" + str(len(current_level.rings)) func _unhandled_key_input(event: InputEvent) -> void: if event.is_pressed() and event is InputEventKey: @@ -151,15 +151,6 @@ func _unhandled_key_input(event: InputEvent) -> void: pause_game() restart_level() -func _on_btn_resume_pressed() -> void: - pause_game() - -func _on_btn_fullscreen_2_pressed() -> void: - fullscreen_game() - -func _on_btn_restart_pressed() -> void: - restart_level() - func _on_btn_quit_pressed() -> void: get_tree().quit() " @@ -301,10 +292,7 @@ vertical_alignment = 1 [node name="Levels" type="Node" parent="."] -[node name="Demo" parent="Levels" instance=ExtResource("1_08svf")] - -[node name="Timer" type="Timer" parent="."] -wait_time = 0.1 +[node name="Demo" parent="Levels" instance=ExtResource("1_356j3")] [node name="StartMenu" type="Control" parent="."] layout_mode = 3 @@ -483,11 +471,10 @@ layout_mode = 2 size_flags_vertical = 6 text = "QUIT game" -[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] -[connection signal="pressed" from="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Buttons/HBoxContainer/Btn_Fullscreen" to="." method="_on_btn_fullscreen_pressed"] +[connection signal="pressed" from="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Buttons/HBoxContainer/Btn_Fullscreen" to="." method="fullscreen_game"] [connection signal="pressed" from="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Buttons/HBoxContainer/Btn_Start" to="." method="_on_btn_start_pressed"] [connection signal="animation_finished" from="StartMenu/AnimationPlayer" to="." method="_on_animation_player_animation_finished"] -[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Resume" to="." method="_on_btn_resume_pressed"] -[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Fullscreen2" to="." method="_on_btn_fullscreen_2_pressed"] -[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Restart" to="." method="_on_btn_restart_pressed"] +[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Resume" to="." method="pause_game"] +[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Fullscreen2" to="." method="fullscreen_game"] +[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Restart" to="." method="restart_level"] [connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Quit" to="." method="_on_btn_quit_pressed"] diff --git a/levels/base/level.tscn b/levels/base/level.tscn index faa000e..93021d3 100644 --- a/levels/base/level.tscn +++ b/levels/base/level.tscn @@ -1,17 +1,18 @@ -[gd_scene load_steps=8 format=3 uid="uid://b0mhosbyt1fit"] +[gd_scene load_steps=8 format=3 uid="uid://ovtknjyj83gh"] -[ext_resource type="Material" uid="uid://brwwlwb1rpro7" path="res://graphics/unhappy_material.tres" id="1_dwsqy"] [ext_resource type="PackedScene" uid="uid://ygvokp5f78i5" path="res://levels/base/environment.tscn" id="1_n7fd8"] [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"] [node name="Base" type="Node3D"] script = ExtResource("1_rj40i") -metadata/material = ExtResource("1_dwsqy") -metadata/name = "Base" +metadata/_custom_type_script = "uid://w3fetao1pegm" + +[node name="Gravity" parent="." instance=ExtResource("3_muudg")] [node name="Environment" parent="." instance=ExtResource("1_n7fd8")] diff --git a/levels/demo.tscn b/levels/demo.tscn new file mode 100644 index 0000000..51fed8b --- /dev/null +++ b/levels/demo.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=7 format=3 uid="uid://dtfybhftwfn00"] + +[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://b6gnffoboc5j5" path="res://levels/night/environment.tscn" id="3_j4ado"] +[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"] + +[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" parent="." instance=ExtResource("3_j4ado")] + +[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/demo_level.tscn b/levels/demo_level.tscn deleted file mode 100644 index 493312b..0000000 --- a/levels/demo_level.tscn +++ /dev/null @@ -1,29 +0,0 @@ -[gd_scene load_steps=6 format=3 uid="uid://n40mg5tc3bnv"] - -[ext_resource type="Script" uid="uid://w3fetao1pegm" path="res://levels/level.gd" id="1_ifscd"] -[ext_resource type="PackedScene" uid="uid://b6gnffoboc5j5" path="res://levels/night/environment.tscn" id="2_v6rg2"] -[ext_resource type="PackedScene" uid="uid://drfy3vhe6skp1" path="res://levels/night/music.tscn" id="3_fb0ct"] -[ext_resource type="PackedScene" uid="uid://cnnvwotv33u1b" path="res://elements/player.tscn" id="4_ge8id"] -[ext_resource type="PackedScene" uid="uid://cpm3laywhlbq5" path="res://elements/ring.tscn" id="5_j060p"] - -[node name="Demo" type="Node3D"] -script = ExtResource("1_ifscd") -metadata/name = "Demo" - -[node name="Environment" parent="." instance=ExtResource("2_v6rg2")] - -[node name="Music" parent="." instance=ExtResource("3_fb0ct")] - -[node name="Player" parent="." instance=ExtResource("4_ge8id")] - -[node name="Rings" type="Node3D" parent="."] - -[node name="Ring" parent="Rings" instance=ExtResource("5_j060p")] -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("5_j060p")] -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("5_j060p")] -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 bb5308e..7150392 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=11 format=3 uid="uid://dfvuxfxc6ooya"] +[gd_scene load_steps=12 format=3 uid="uid://e1761h6d522a"] [ext_resource type="PackedScene" uid="uid://6nfv4ewj3jop" path="res://levels/forest/environment.tscn" id="1_7clrg"] [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"] @@ -30,7 +31,9 @@ reflected_light_source = 2 [node name="Forest" type="Node3D"] script = ExtResource("1_fdxcj") -metadata/name = "Forest" +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) diff --git a/levels/level.gd b/levels/level.gd index 7950043..490218c 100644 --- a/levels/level.gd +++ b/levels/level.gd @@ -3,10 +3,8 @@ extends Node3D var music: Music var velocity: float = 0.0 - -var rings_count = 72727 -var finished_rings_count = 0 var rings: Array[Node] = [] +var finished_rings_count = 0 func _ready() -> void: var children = get_children() @@ -18,13 +16,12 @@ func _ready() -> void: var rings_node = get_node("Rings") assert(is_instance_valid(rings_node), self.name + " has no rings!") rings = rings_node.get_children() - rings_count = len(rings) for ring in rings: ring.connect("collect_signal", do_we_end_yet) func do_we_end_yet(): finished_rings_count += 1 - if finished_rings_count >= rings_count: + if finished_rings_count >= len(rings): var game = get_parent().get_parent() if is_instance_valid(game) and game.has_method("stop_level"): game.stop_level() diff --git a/levels/night/level.tscn b/levels/night/level.tscn index 595b988..4c7e8eb 100644 --- a/levels/night/level.tscn +++ b/levels/night/level.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=9 format=3 uid="uid://c2rlx31om4gey"] +[gd_scene load_steps=10 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://b6gnffoboc5j5" path="res://levels/night/environment.tscn" id="2_wintp"] [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"] @@ -11,7 +12,9 @@ [node name="Night" type="Node3D"] script = ExtResource("1_3m1pa") -metadata/name = "Night" +metadata/_custom_type_script = "uid://w3fetao1pegm" + +[node name="Gravity" parent="." instance=ExtResource("2_lfplq")] [node name="Environment" parent="." instance=ExtResource("2_wintp")] From f7e85724b312bb420abb89bf274d19364a6e3bca Mon Sep 17 00:00:00 2001 From: Taevas Date: Tue, 25 Mar 2025 23:18:33 +0100 Subject: [PATCH 05/20] reduce video memory usage by orders of magnitude big fonts use lots of big lumalpha8, so just scale up small fonts --- elements/ring.gd | 14 ++++++++++++++ elements/ring.gd.uid | 1 + elements/ring.tscn | 21 +++------------------ elements/sign.tscn | 2 +- index.tscn | 1 + levels/base/level.tscn | 10 +++++++--- levels/forest/level.tscn | 3 +-- levels/level.gd | 32 +++++++++++++++++++------------- levels/night/level.tscn | 15 +++++++-------- 9 files changed, 54 insertions(+), 45 deletions(-) create mode 100644 elements/ring.gd create mode 100644 elements/ring.gd.uid diff --git a/elements/ring.gd b/elements/ring.gd new file mode 100644 index 0000000..07924c7 --- /dev/null +++ b/elements/ring.gd @@ -0,0 +1,14 @@ +class_name Ring +extends CSGTorus3D + +var happy_material = preload("res://graphics/happy_material.tres") + +var collected = false +signal collect_signal + +func _on_area_3d_body_entered(body: Node3D) -> void: + if collected == false and body.name == "Sphere": + self.material = happy_material + collected = true + $AudioStreamPlayer.play() + collect_signal.emit() diff --git a/elements/ring.gd.uid b/elements/ring.gd.uid new file mode 100644 index 0000000..e2befce --- /dev/null +++ b/elements/ring.gd.uid @@ -0,0 +1 @@ +uid://d1tdcd6b2bp05 diff --git a/elements/ring.tscn b/elements/ring.tscn index 32e5295..3244ec2 100644 --- a/elements/ring.tscn +++ b/elements/ring.tscn @@ -2,25 +2,9 @@ [ext_resource type="Material" uid="uid://brwwlwb1rpro7" path="res://graphics/unhappy_material.tres" id="1_48h5f"] [ext_resource type="AudioStream" uid="uid://b0cl5v6q1ocbv" path="res://sounds/ring1.ogg" id="2_2so43"] +[ext_resource type="Script" uid="uid://d1tdcd6b2bp05" path="res://elements/ring.gd" id="2_288uq"] [ext_resource type="AudioStream" uid="uid://cvvs74ep5d4wt" path="res://sounds/ring2.ogg" id="3_lkqw0"] -[sub_resource type="GDScript" id="GDScript_86vhg"] -script/source = "class_name Ring -extends CSGTorus3D - -var happy_material = preload(\"res://graphics/happy_material.tres\") - -var collected = false -signal collect_signal - -func _on_area_3d_body_entered(body: Node3D) -> void: - if collected == false and body.name == \"Sphere\": - self.material = happy_material - collected = true - $AudioStreamPlayer.play() - collect_signal.emit() -" - [sub_resource type="CylinderShape3D" id="CylinderShape3D_tgirv"] radius = 4.0 @@ -37,7 +21,8 @@ outer_radius = 8.0 sides = 32 ring_sides = 16 material = ExtResource("1_48h5f") -script = SubResource("GDScript_86vhg") +script = ExtResource("2_288uq") +metadata/_custom_type_script = "uid://d1tdcd6b2bp05" [node name="Area3D" type="Area3D" parent="."] diff --git a/elements/sign.tscn b/elements/sign.tscn index e93e18e..d91f0ad 100644 --- a/elements/sign.tscn +++ b/elements/sign.tscn @@ -4,7 +4,7 @@ [node name="Text" type="Label3D" parent="."] text = "Sample text" -font_size = 1024 +font_size = 64 [node name="CSGBox3D" type="CSGBox3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -5.1) diff --git a/index.tscn b/index.tscn index 0e835f4..7c82b45 100644 --- a/index.tscn +++ b/index.tscn @@ -45,6 +45,7 @@ func start_level(level_scene: PackedScene) -> void: $VictoryScreen.hide() var level = level_scene.instantiate() $Levels.add_child(level) + level.connect(\"completed\", stop_level) playing = true PhysicsServer3D.area_set_param( get_viewport().find_world_3d().space, diff --git a/levels/base/level.tscn b/levels/base/level.tscn index 93021d3..0a7d2e2 100644 --- a/levels/base/level.tscn +++ b/levels/base/level.tscn @@ -28,6 +28,7 @@ transform = Transform3D(-1, 8.74228e-08, 0, -8.74228e-08, -1, 0, 0, 0, 1, -366.4 transform = Transform3D(-1, -8.74228e-08, 0, 8.74228e-08, -1, 0, 0, 0, 1, -366.478, 3.24249e-05, 0) [node name="Text" parent="Signs/Sign" index="0"] +transform = Transform3D(20, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0) text = "Press the arrow keys to change the gravity!" @@ -35,13 +36,14 @@ to change the gravity!" transform = Transform3D(4.37114e-08, -7.10543e-15, -1, -8.74228e-08, -1, 0, -1, 8.74228e-08, -4.37114e-08, 150, 40, 61) [node name="Text" parent="Signs/Sign7" index="0"] +transform = Transform3D(30, 0, 0, 0, 30, 0, 0, 0, 30, 0, 0, 0) text = "SLOW DOWN!!" -font_size = 2056 [node name="Sign2" parent="Signs" instance=ExtResource("4_atq6y")] transform = Transform3D(-1, -8.74228e-08, 0, 8.74228e-08, -1, 0, 0, 0, 1, -234.9, 2.09808e-05, 0) [node name="Text" parent="Signs/Sign2" index="0"] +transform = Transform3D(15, 0, 0, 0, 15, 0, 0, 0, 15, 0, 0, 0) text = "While the shift key is pressed, the gravity is much stronger!" @@ -49,6 +51,7 @@ the gravity is much stronger!" transform = Transform3D(-1, -8.74228e-08, 0, 8.74228e-08, -1, 0, 0, 0, 1, -131.629, 81.2564, 0) [node name="Text" parent="Signs/Sign4" index="0"] +transform = Transform3D(20, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0) text = "You can take the rings in any order!" @@ -56,6 +59,7 @@ in any order!" transform = Transform3D(-4.37114e-08, 8.74228e-08, 1, 0, -1, 8.74228e-08, 1, 3.82137e-15, 4.37114e-08, -490.634, 50.3763, 34.0693) [node name="Text" parent="Signs/Sign5" index="0"] +transform = Transform3D(20, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0) text = "The level is over once you get all the rings!" @@ -63,13 +67,13 @@ you get all the rings!" transform = Transform3D(-0.707107, 6.18172e-08, 0.707107, -8.74228e-08, -1, 0, 0.707107, -6.18172e-08, 0.707107, -532.475, 18.0064, 60) [node name="Text" parent="Signs/Sign6" index="0"] +transform = Transform3D(30, 0, 0, 0, 30, 0, 0, 0, 30, 0, 0, 0) text = "Have fun!" -font_size = 2048 [node name="Sign3" parent="Signs" instance=ExtResource("4_atq6y")] [node name="Text" parent="Signs/Sign3" index="0"] -transform = Transform3D(-1, 8.74228e-08, 0, -8.74228e-08, -1, 0, 0, 0, 1, 0, 0, 0) +transform = Transform3D(-20, 3.01992e-06, 0, -3.01992e-06, -20, 0, 0, 0, 20, 0, 0, 0) text = "The less time you take, the more you can flex!" diff --git a/levels/forest/level.tscn b/levels/forest/level.tscn index 7150392..31e9018 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -51,9 +51,8 @@ environment = SubResource("Environment_y3whn") transform = Transform3D(-4.37114e-08, 8.74228e-08, 1, 0, -1, 8.74228e-08, 1, 3.82137e-15, 4.37114e-08, -450, 132.977, 0) [node name="Text" parent="Sign" index="0"] -transform = Transform3D(-1, 8.74228e-08, 0, -8.74228e-08, -1, 0, 0, 0, 1, 0, 0, 0) +transform = Transform3D(-35, 5.28485e-06, 0, -5.28485e-06, -35, 0, 0, 0, 35, 0, 0, 0) text = "Watch out!" -font_size = 2048 [node name="Trees" type="Node3D" parent="."] diff --git a/levels/level.gd b/levels/level.gd index 490218c..77f9795 100644 --- a/levels/level.gd +++ b/levels/level.gd @@ -1,10 +1,14 @@ class_name Level extends Node3D +signal ring_collected +signal completed + +var seconds_spent: float = 0.00 var music: Music var velocity: float = 0.0 -var rings: Array[Node] = [] -var finished_rings_count = 0 +var rings: Array[Ring] = [] +var finished_rings_count: int = 0 func _ready() -> void: var children = get_children() @@ -14,19 +18,21 @@ func _ready() -> void: assert(is_instance_valid(music), self.name + " has no music!") var rings_node = get_node("Rings") - assert(is_instance_valid(rings_node), self.name + " has no rings!") - rings = rings_node.get_children() - for ring in rings: - ring.connect("collect_signal", do_we_end_yet) + assert(is_instance_valid(rings_node), self.name + " has no Rings node!") + var rings_node_children = rings_node.get_children() + for ring in rings_node_children: + if is_instance_of(ring, Ring): + rings.push_back(ring) + ring.connect("collect_signal", collect_ring) -func do_we_end_yet(): - finished_rings_count += 1 +func collect_ring(): + finished_rings_count = len(rings.filter(func(ring: Ring): return ring.collected)) + ring_collected.emit() + if finished_rings_count >= len(rings): - var game = get_parent().get_parent() - if is_instance_valid(game) and game.has_method("stop_level"): - game.stop_level() + completed.emit() func _process(delta: float) -> void: - var sphere = $Player/Sphere - velocity = abs(sphere.linear_velocity.x) + abs(sphere.linear_velocity.y) + seconds_spent += delta + velocity = abs($Player.velocity.x) + abs($Player.velocity.y) music.adaptInstrumentsToVelocity(velocity, delta) diff --git a/levels/night/level.tscn b/levels/night/level.tscn index 4c7e8eb..b40c61a 100644 --- a/levels/night/level.tscn +++ b/levels/night/level.tscn @@ -174,40 +174,42 @@ transform = Transform3D(-1.31134e-07, -3, 0, 3, -1.31134e-07, 0, 0, 0, 3, -104.2 [node name="Ring2" parent="Rings" instance=ExtResource("4_brcr0")] 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) + [node name="Signs" type="Node3D" parent="."] [node name="Sign" parent="Signs" instance=ExtResource("6_36yav")] transform = Transform3D(0.843785, 0, 0.536681, 0, 1, 0, -0.536681, 0, 0.843785, -159.605, -62.9326, 0) [node name="Text" parent="Signs/Sign" index="0"] +transform = Transform3D(37, 0, 0, 0, 37, 0, 0, 0, 37, 0, 0, 0) text = "^^^ Go UP!!" -font_size = 2048 [node name="Sign4" parent="Signs" instance=ExtResource("6_36yav")] transform = Transform3D(0.965926, 0, 0.258819, 0, 1, 0, -0.258819, 0, 0.965926, 231.92, -32.6467, 0) [node name="Text" parent="Signs/Sign4" index="0"] +transform = Transform3D(22, 0, 0, 0, 22, 0, 0, 0, 22, 0, 0, 0) text = "^^ Don't forget that one!" -font_size = 1500 [node name="Sign2" parent="Signs" instance=ExtResource("6_36yav")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -159.605, -30.0117, -45.6318) [node name="Text" parent="Signs/Sign2" index="0"] +transform = Transform3D(30, 0, 0, 0, 30, 0, 0, 0, 30, 0, 0, 0) text = "After taking this, go RIGHT!" -font_size = 1600 [node name="Sign3" parent="Signs" instance=ExtResource("6_36yav")] transform = Transform3D(-1, -8.74228e-08, 0, 8.74228e-08, -1, 0, 0, 0, 1, -64.0671, 131.627, -45.6318) [node name="Text" parent="Signs/Sign3" index="0"] -transform = Transform3D(-1, 8.74228e-08, 0, -8.74228e-08, -1, 0, 0, 0, 1, 0, 0, 0) +transform = Transform3D(-30, 4.52987e-06, 0, -4.52987e-06, -30, 0, 0, 0, 30, 0, 0, 0) text = "Go RIGHT!" -font_size = 2048 [node name="Stars" type="Node3D" parent="."] transform = Transform3D(4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 37.5124, -255.888) @@ -307,9 +309,6 @@ 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) -[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) - [editable path="Signs/Sign"] [editable path="Signs/Sign4"] [editable path="Signs/Sign2"] From eefda9e2bd6c0b2e24a653ab78bf4e6954a3d09e Mon Sep 17 00:00:00 2001 From: Taevas Date: Thu, 27 Mar 2025 16:00:39 +0100 Subject: [PATCH 06/20] start creating the framework for menus (#3) all very experimental so it'll see lots of changes --- menus/main/area.tscn | 110 +++++++++++++++++++++++++++++ menus/main/panel.tscn | 113 ++++++++++++++++++++++++++++++ menus/main/save_file_manager.tscn | 102 +++++++++++++++++++++++++++ menus/main/start_menu.tscn | 54 ++++++++++++++ menus/menu.tscn | 99 ++++++++++++++++++++++++++ project.godot | 2 +- 6 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 menus/main/area.tscn create mode 100644 menus/main/panel.tscn create mode 100644 menus/main/save_file_manager.tscn create mode 100644 menus/main/start_menu.tscn create mode 100644 menus/menu.tscn diff --git a/menus/main/area.tscn b/menus/main/area.tscn new file mode 100644 index 0000000..731bf77 --- /dev/null +++ b/menus/main/area.tscn @@ -0,0 +1,110 @@ +[gd_scene load_steps=7 format=3 uid="uid://ikeidrgprk8k"] + +[ext_resource type="PackedScene" uid="uid://wlhsarkeqe8r" path="res://menus/main/panel.tscn" id="1_qfa5o"] + +[sub_resource type="GDScript" id="GDScript_bt14i"] +script/source = "extends Node3D + +var start_menu = preload(\"res://menus/main/start_menu.tscn\") +var save_file_manager = preload(\"res://menus/main/save_file_manager.tscn\") + +func _ready() -> void: + var sm = start_menu.instantiate() + sm.connect(\"request_start\", start) + sm.connect(\"request_settings\", settings) + $Menus/Panel1.change_menu(sm) + +func start() -> void: + var svm = save_file_manager.instantiate() + $Menus/Panel2.change_menu(svm) + rotate_cube(90) + +func settings() -> void: + rotate_cube(90) + +func rotate_cube(degrees: int) -> void: + if $Menus/AnimationPlayer.is_playing(): + return + degrees = -degrees + + var animation: Animation = $Menus/AnimationPlayer.get_animation(\"rotate\") + var new_rotation = $Menus.rotation_degrees + 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="Environment" id="Environment_qfa5o"] +background_mode = 1 +background_color = Color(0.804743, 0.804743, 0.804743, 1) + +[node name="Area" type="Node3D"] +process_mode = 3 +script = SubResource("GDScript_bt14i") + +[node name="SpotLight3D" type="SpotLight3D" parent="."] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 1.90692, 0) + +[node name="Menus" type="CSGBox3D" parent="."] + +[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(0.975535, 0, 0.219846, 0, 1, 0, -0.219846, 0, 0.975535, 0.86618, 0.217345, 1.76032) + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_qfa5o") diff --git a/menus/main/panel.tscn b/menus/main/panel.tscn new file mode 100644 index 0000000..04553cd --- /dev/null +++ b/menus/main/panel.tscn @@ -0,0 +1,113 @@ +[gd_scene load_steps=7 format=3 uid="uid://wlhsarkeqe8r"] + +[ext_resource type="PackedScene" uid="uid://tasbgr10p84c" path="res://menus/menu.tscn" id="1_3roig"] + +[sub_resource type="GDScript" id="GDScript_gfsnp"] +script/source = "# this whole code is from \"GUI in 3D\" +# which is something I downloaded a long time ago so no link + +extends Node3D + +var is_mouse_inside := false +var last_event_pos2D := Vector2() +var last_event_time := -1.0 + +@onready var node_viewport: SubViewport = $SubViewport +@onready var node_quad: MeshInstance3D = $MeshInstance3D +@onready var node_area: Area3D = $MeshInstance3D/Area3D + +func _ready() -> void: + node_area.mouse_entered.connect(_mouse_entered_area) + node_area.mouse_exited.connect(_mouse_exited_area) + node_area.input_event.connect(_mouse_input_event) + +func _mouse_entered_area() -> void: + is_mouse_inside = true + +func _mouse_exited_area() -> void: + is_mouse_inside = false + +func _unhandled_input(event: InputEvent) -> void: + for mouse_event in [InputEventMouseButton, InputEventMouseMotion, InputEventScreenDrag, InputEventScreenTouch]: + if is_instance_of(event, mouse_event): + return + node_viewport.push_input(event) + +func _mouse_input_event(_camera: Camera3D, event: InputEvent, event_position: Vector3, _normal: Vector3, _shape_idx: int) -> void: + var quad_mesh_size: Vector2 = node_quad.mesh.size + var now := Time.get_ticks_msec() / 1000.0 + + var event_pos3D := event_position + event_pos3D = node_quad.global_transform.affine_inverse() * event_pos3D + + var event_pos2D := Vector2() + if is_mouse_inside: + event_pos2D = Vector2(event_pos3D.x, -event_pos3D.y) + + event_pos2D.x = event_pos2D.x / quad_mesh_size.x + event_pos2D.y = event_pos2D.y / quad_mesh_size.y + + event_pos2D.x += 0.5 + event_pos2D.y += 0.5 + + event_pos2D.x *= node_viewport.size.x + event_pos2D.y *= node_viewport.size.y + + elif last_event_pos2D != null: + event_pos2D = last_event_pos2D + + event.position = event_pos2D + if event is InputEventMouse: + event.global_position = event_pos2D + + if event is InputEventMouseMotion or event is InputEventScreenDrag: + if last_event_pos2D == null: + event.relative = Vector2(0, 0) + else: + event.relative = event_pos2D - last_event_pos2D + event.velocity = event.relative / (now - last_event_time) + + last_event_pos2D = event_pos2D + last_event_time = now + node_viewport.push_input(event) + +# original code + +func change_menu(new_menu: Control): + $SubViewport/Menu.add(new_menu) +" + +[sub_resource type="QuadMesh" id="QuadMesh_gfsnp"] + +[sub_resource type="ViewportTexture" id="ViewportTexture_gfsnp"] +viewport_path = NodePath("SubViewport") + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_a5lkq"] +resource_local_to_scene = true +transparency = 1 +shading_mode = 0 +albedo_texture = SubResource("ViewportTexture_gfsnp") + +[sub_resource type="BoxShape3D" id="BoxShape3D_gfsnp"] +size = Vector3(1, 1, 0.01) + +[node name="Panel" type="Node3D"] +process_mode = 3 +script = SubResource("GDScript_gfsnp") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("QuadMesh_gfsnp") +surface_material_override/0 = SubResource("StandardMaterial3D_a5lkq") + +[node name="Area3D" type="Area3D" parent="MeshInstance3D"] +collision_layer = 2 +input_capture_on_drag = true + +[node name="CollisionShape3D" type="CollisionShape3D" parent="MeshInstance3D/Area3D"] +shape = SubResource("BoxShape3D_gfsnp") + +[node name="SubViewport" type="SubViewport" parent="."] +gui_embed_subwindows = true +render_target_update_mode = 4 + +[node name="Menu" parent="SubViewport" instance=ExtResource("1_3roig")] diff --git a/menus/main/save_file_manager.tscn b/menus/main/save_file_manager.tscn new file mode 100644 index 0000000..da7f052 --- /dev/null +++ b/menus/main/save_file_manager.tscn @@ -0,0 +1,102 @@ +[gd_scene load_steps=5 format=3 uid="uid://w5te3qujco7d"] + +[sub_resource type="GDScript" id="GDScript_a5kgg"] +script/source = "extends MarginContainer + +func _ready() -> void: + $HSplitContainer/CenterContainer/Label.hide() + + +func _on_button_pressed() -> void: + $HSplitContainer/CenterContainer/Label.show() + $AnimationPlayer.play(\"add_information\") +" + +[sub_resource type="Animation" id="Animation_a5kgg"] +resource_name = "add_information" +length = 0.2 +step = 0.2 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("HSplitContainer/CenterContainer:size_flags_stretch_ratio") +tracks/0/interp = 2 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.2), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.0, 2.0] +} + +[sub_resource type="Animation" id="Animation_xs01b"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("HSplitContainer/CenterContainer:size_flags_stretch_ratio") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [0.0] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_36lt1"] +_data = { +&"RESET": SubResource("Animation_xs01b"), +&"add_information": SubResource("Animation_a5kgg") +} + +[node name="SaveFileManager" type="MarginContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 +script = SubResource("GDScript_a5kgg") + +[node name="HSplitContainer" type="HSplitContainer" parent="."] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +alignment = 1 + +[node name="Button" type="Button" parent="HSplitContainer/VBoxContainer"] +layout_mode = 2 +text = "Save file" + +[node name="Button2" type="Button" parent="HSplitContainer/VBoxContainer"] +layout_mode = 2 +text = "Save file" + +[node name="Button3" type="Button" parent="HSplitContainer/VBoxContainer"] +layout_mode = 2 +text = "Save file" + +[node name="CenterContainer" type="CenterContainer" parent="HSplitContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.0 + +[node name="Label" type="Label" parent="HSplitContainer/CenterContainer"] +show_behind_parent = true +layout_mode = 2 +text = "Information" + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +&"": SubResource("AnimationLibrary_36lt1") +} + +[connection signal="pressed" from="HSplitContainer/VBoxContainer/Button" to="." method="_on_button_pressed"] diff --git a/menus/main/start_menu.tscn b/menus/main/start_menu.tscn new file mode 100644 index 0000000..9182cc7 --- /dev/null +++ b/menus/main/start_menu.tscn @@ -0,0 +1,54 @@ +[gd_scene load_steps=2 format=3 uid="uid://dys7qyy4b3gjt"] + +[sub_resource type="GDScript" id="GDScript_vfuxb"] +script/source = "extends Control + +signal request_start +signal request_settings + +func _on_btn_start_pressed() -> void: + request_start.emit() + +func _on_btn_settings_pressed() -> void: + request_settings.emit() + +func _on_btn_exit_pressed() -> void: + get_tree().quit() +" + +[node name="StartMenu" 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_vfuxb") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 14 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_top = -50.5 +offset_bottom = 50.5 +grow_horizontal = 2 +grow_vertical = 2 +alignment = 1 + +[node name="BtnStart" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Start the game!" + +[node name="BtnSettings" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Check out the settings" + +[node name="BtnExit" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Exit the game" + +[connection signal="pressed" from="VBoxContainer/BtnStart" to="." method="_on_btn_start_pressed"] +[connection signal="pressed" from="VBoxContainer/BtnSettings" to="." method="_on_btn_settings_pressed"] +[connection signal="pressed" from="VBoxContainer/BtnExit" to="." method="_on_btn_exit_pressed"] diff --git a/menus/menu.tscn b/menus/menu.tscn new file mode 100644 index 0000000..dddea27 --- /dev/null +++ b/menus/menu.tscn @@ -0,0 +1,99 @@ +[gd_scene load_steps=6 format=3 uid="uid://tasbgr10p84c"] + +[sub_resource type="GDScript" id="GDScript_18a3y"] +script/source = "extends MarginContainer + +func add(node: Control): + var add_to := $VSplitContainer/MarginContainer/ColorRect + var children := add_to.get_children(true) + for child in children: + child.queue_free() + + var regex := RegEx.new() + regex.compile(\"([A-Z])\") + var menu_name := regex.sub(node.name, \" $1\", true, 1) + $VSplitContainer/Label.text = menu_name + add_to.add_child(node) + $VSplitContainer/AnimationPlayer.play(\"split_offset\") + + #await get_tree().create_timer(3.0).timeout + #$VSplitContainer/AnimationPlayer.play_backwards(\"split_offset\") +" + +[sub_resource type="Animation" id="Animation_y8e4h"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:split_offset") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [500] +} + +[sub_resource type="Animation" id="Animation_85u52"] +resource_name = "split_offset" +length = 0.5 +step = 0.5 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:split_offset") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.5), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [500, 0] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_hl3ms"] +_data = { +&"RESET": SubResource("Animation_y8e4h"), +&"split_offset": SubResource("Animation_85u52") +} + +[sub_resource type="LabelSettings" id="LabelSettings_2d4ws"] +font_size = 48 + +[node name="Menu" type="MarginContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 +script = SubResource("GDScript_18a3y") + +[node name="VSplitContainer" type="VSplitContainer" parent="."] +layout_mode = 2 +split_offset = 500 +dragger_visibility = 1 + +[node name="AnimationPlayer" type="AnimationPlayer" parent="VSplitContainer"] +libraries = { +&"": SubResource("AnimationLibrary_hl3ms") +} + +[node name="Label" type="Label" parent="VSplitContainer"] +layout_mode = 2 +text = "MENU NAME" +label_settings = SubResource("LabelSettings_2d4ws") +horizontal_alignment = 1 +uppercase = true + +[node name="MarginContainer" type="MarginContainer" parent="VSplitContainer"] +layout_mode = 2 +theme_override_constants/margin_top = 20 + +[node name="ColorRect" type="ColorRect" parent="VSplitContainer/MarginContainer"] +layout_mode = 2 +color = Color(0, 0, 0, 1) diff --git a/project.godot b/project.godot index 605a759..6595a5a 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="20250323.0" +config/version="20250326.0" run/main_scene="res://index.tscn" config/features=PackedStringArray("4.4", "GL Compatibility") boot_splash/show_image=false From a73b32610eb0d36c979104abb6ea5dd8cf24083c Mon Sep 17 00:00:00 2001 From: Taevas Date: Fri, 28 Mar 2025 13:42:53 +0100 Subject: [PATCH 07/20] working settings menu includes audio sliders, fullscreen, disabling gravity change sound --- default_bus_layout.tres | 9 ++++ elements/gravity.tscn | 5 +- elements/ring.tscn | 1 + levels/music.gd | 4 +- menus/main/area.tscn | 3 ++ menus/main/settings_menu.tscn | 96 +++++++++++++++++++++++++++++++++++ project.godot | 4 +- settings.gd | 78 ++++++++++++++++++++++++++++ settings.gd.uid | 1 + 9 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 default_bus_layout.tres create mode 100644 menus/main/settings_menu.tscn create mode 100644 settings.gd create mode 100644 settings.gd.uid diff --git a/default_bus_layout.tres b/default_bus_layout.tres new file mode 100644 index 0000000..715ca52 --- /dev/null +++ b/default_bus_layout.tres @@ -0,0 +1,9 @@ +[gd_resource type="AudioBusLayout" format=3 uid="uid://bgs4iykhuoux0"] + +[resource] +bus/1/name = &"Sounds" +bus/1/solo = false +bus/1/mute = false +bus/1/bypass_fx = false +bus/1/volume_db = 0.00282288 +bus/1/send = &"Master" diff --git a/elements/gravity.tscn b/elements/gravity.tscn index 533c7a3..533369c 100644 --- a/elements/gravity.tscn +++ b/elements/gravity.tscn @@ -58,8 +58,10 @@ func _input(ev: InputEvent) -> void: if $AnimationPlayer.is_playing(): $AnimationPlayer.stop() - $AudioStreamPlayer.play() $AnimationPlayer.play(\"grow_fadeout\") + + if Settings.sound_on_gravity_change: + $AudioStreamPlayer.play() " [sub_resource type="Animation" id="Animation_6rwl4"] @@ -161,3 +163,4 @@ libraries = { stream = ExtResource("2_5uwlw") volume_db = -6.0 mix_target = 2 +bus = &"Sounds" diff --git a/elements/ring.tscn b/elements/ring.tscn index 3244ec2..7e5786d 100644 --- a/elements/ring.tscn +++ b/elements/ring.tscn @@ -31,5 +31,6 @@ shape = SubResource("CylinderShape3D_tgirv") [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] stream = SubResource("AudioStreamRandomizer_251sn") +bus = &"Sounds" [connection signal="body_entered" from="Area3D" to="." method="_on_area_3d_body_entered"] diff --git a/levels/music.gd b/levels/music.gd index a98710a..1d14740 100644 --- a/levels/music.gd +++ b/levels/music.gd @@ -15,6 +15,7 @@ func _ready() -> void: AudioServer.add_bus() AudioServer.set_bus_name(AudioServer.bus_count - 1, self.name) bus_index = AudioServer.get_bus_index(self.name) + changeVolume(0) var current_effects = AudioServer.get_bus_effect_count(bus_index) for i in current_effects: @@ -36,7 +37,8 @@ func _ready() -> void: instrument.play() func changeVolume(db: float) -> void: - AudioServer.set_bus_volume_db(bus_index, db) + 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: var instruments_needed = floor(velocity / 8) diff --git a/menus/main/area.tscn b/menus/main/area.tscn index 731bf77..d7a6b56 100644 --- a/menus/main/area.tscn +++ b/menus/main/area.tscn @@ -7,6 +7,7 @@ script/source = "extends Node3D 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: var sm = start_menu.instantiate() @@ -20,6 +21,8 @@ func start() -> void: rotate_cube(90) func settings() -> void: + var sm = settings_menu.instantiate() + $Menus/Panel2.change_menu(sm) rotate_cube(90) func rotate_cube(degrees: int) -> void: diff --git a/menus/main/settings_menu.tscn b/menus/main/settings_menu.tscn new file mode 100644 index 0000000..285436f --- /dev/null +++ b/menus/main/settings_menu.tscn @@ -0,0 +1,96 @@ +[gd_scene load_steps=2 format=3 uid="uid://opwiiusu80nu"] + +[sub_resource type="GDScript" id="GDScript_wbybg"] +script/source = "extends Control + +func _ready() -> void: + $VBoxContainer/Fullscreen/CheckButton.button_pressed = Settings.fullscreen + $VBoxContainer/SoundGravity/CheckButton.button_pressed = Settings.sound_on_gravity_change + $VBoxContainer/VolumeMusic/HSlider.value = Settings.volume_music + $VBoxContainer/VolumeSounds/HSlider.value = Settings.volume_sounds + +func _on_fullscreen_check_button_toggled(toggled_on: bool) -> void: + Settings.fullscreen = toggled_on + +func _on_soundgravity_check_button_toggled(toggled_on: bool) -> void: + Settings.sound_on_gravity_change = toggled_on + +func _on_volumemusic_h_slider_value_changed(value: float) -> void: + Settings.volume_music = int(value) + +func _on_volumesounds_h_slider_value_changed(value: float) -> void: + Settings.volume_sounds = int(value) +" + +[node name="SettingsMenu" 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_wbybg") + +[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 +alignment = 1 + +[node name="Fullscreen" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="Label" type="Label" parent="VBoxContainer/Fullscreen"] +layout_mode = 2 +text = "Fullscreen" + +[node name="CheckButton" type="CheckButton" parent="VBoxContainer/Fullscreen"] +layout_mode = 2 + +[node name="SoundGravity" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="Label" type="Label" parent="VBoxContainer/SoundGravity"] +layout_mode = 2 +text = "Play sound on gravity change" + +[node name="CheckButton" type="CheckButton" parent="VBoxContainer/SoundGravity"] +layout_mode = 2 + +[node name="VolumeMusic" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="Label" type="Label" parent="VBoxContainer/VolumeMusic"] +layout_mode = 2 +text = "Music volume" + +[node name="HSlider" type="HSlider" parent="VBoxContainer/VolumeMusic"] +custom_minimum_size = Vector2(200, 28) +layout_mode = 2 +min_value = -15.0 +max_value = 15.0 + +[node name="VolumeSounds" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="Label" type="Label" parent="VBoxContainer/VolumeSounds"] +layout_mode = 2 +text = "Sound volume" + +[node name="HSlider" type="HSlider" parent="VBoxContainer/VolumeSounds"] +custom_minimum_size = Vector2(200, 28) +layout_mode = 2 +min_value = -15.0 +max_value = 15.0 + +[connection signal="toggled" from="VBoxContainer/Fullscreen/CheckButton" to="." method="_on_fullscreen_check_button_toggled"] +[connection signal="toggled" from="VBoxContainer/SoundGravity/CheckButton" to="." method="_on_soundgravity_check_button_toggled"] +[connection signal="value_changed" from="VBoxContainer/VolumeMusic/HSlider" to="." method="_on_volumemusic_h_slider_value_changed"] +[connection signal="value_changed" from="VBoxContainer/VolumeSounds/HSlider" to="." method="_on_volumesounds_h_slider_value_changed"] diff --git a/project.godot b/project.godot index 6595a5a..3a26eb9 100644 --- a/project.godot +++ b/project.godot @@ -20,9 +20,9 @@ boot_splash/show_image=false config/icon="res://icon.png" config/windows_native_icon="res://icon.ico" -[audio] +[autoload] -buses/default_bus_layout="" +Settings="*res://settings.gd" [physics] diff --git a/settings.gd b/settings.gd new file mode 100644 index 0000000..012d467 --- /dev/null +++ b/settings.gd @@ -0,0 +1,78 @@ +extends Node + +const settings_file_name := "user://settings.save" + +var fullscreen := false : set = _set_fullscreen, get = _get_fullscreen +func _get_fullscreen() -> bool: + return fullscreen +func _set_fullscreen(new_fullscreen: bool) -> void: + fullscreen = new_fullscreen + var new_state := DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN if new_fullscreen else DisplayServer.WindowMode.WINDOW_MODE_WINDOWED + DisplayServer.window_set_mode(new_state) + save_settings() + +var sound_on_gravity_change := true : set = _set_sound_on_gravity_change, get = _get_sound_on_gravity_change +func _get_sound_on_gravity_change() -> bool: + return sound_on_gravity_change +func _set_sound_on_gravity_change(new_sound_on_gravity_change: bool) -> void: + sound_on_gravity_change = new_sound_on_gravity_change + save_settings() + +var volume_music := 0.0 : set = _set_volume_music, get = _get_volume_music +func _get_volume_music() -> float: + return volume_music +func _set_volume_music(new_volume_music: float) -> void: + volume_music = new_volume_music + for i in AudioServer.bus_count: + var bus_name := AudioServer.get_bus_name(i) + if bus_name != "Master" and bus_name != "Sounds": + AudioServer.set_bus_volume_db(i, volume_music) + AudioServer.set_bus_mute(i, volume_music <= -15.0) + save_settings() + +var volume_sounds := 0.0 : set = _set_volume_sounds, get = _get_volume_sounds +func _get_volume_sounds() -> float: + return volume_sounds +func _set_volume_sounds(new_volume_sounds: float) -> void: + var bus_index := AudioServer.get_bus_index("Sounds") + if bus_index == -1: + print("The Sounds audio bus is missing, can't set new volume!") + return + + volume_sounds = new_volume_sounds + AudioServer.set_bus_mute(bus_index, volume_sounds <= -15.0) + AudioServer.set_bus_volume_db(bus_index, volume_sounds) + save_settings() + +func _ready() -> void: + if not FileAccess.file_exists(settings_file_name): + print("The settings file has not been found, not reading from it") + return + + var settings_file := FileAccess.open(settings_file_name, FileAccess.READ) + var json := JSON.new() + var json_string := settings_file.get_line() + + if json.parse(json_string) != OK: + print("(SETTINGS) JSON Parse Error: ", json.get_error_message(), " in ", json_string, " at line ", json.get_error_line()) + return + + if json.data.has("fullscreen") and typeof(json.data["fullscreen"]) == TYPE_BOOL: + fullscreen = json.data["fullscreen"] + if json.data.has("sound_on_gravity_change") and typeof(json.data["sound_on_gravity_change"]) == TYPE_BOOL: + sound_on_gravity_change = json.data["sound_on_gravity_change"] + if json.data.has("volume_music") and typeof(json.data["volume_music"]) == TYPE_FLOAT: + volume_music = json.data["volume_music"] + if json.data.has("volume_sounds") and typeof(json.data["volume_sounds"]) == TYPE_FLOAT: + volume_sounds = json.data["volume_sounds"] + +func save_settings() -> void: + var settings_file := FileAccess.open(settings_file_name, FileAccess.WRITE) + var json_string := JSON.stringify({ + "fullscreen": fullscreen, + "sound_on_gravity_change": sound_on_gravity_change, + "volume_music": volume_music, + "volume_sounds": volume_sounds, + }) + settings_file.store_line(json_string) + settings_file.store_line("FOR YOUR SAFETY, ALWAYS CHECK IF THE DATA OF THE FILES YOU DOWNLOAD LOOKS OKAY") diff --git a/settings.gd.uid b/settings.gd.uid new file mode 100644 index 0000000..6ac33eb --- /dev/null +++ b/settings.gd.uid @@ -0,0 +1 @@ +uid://6or7tnhtbqe6 From 8f18e43af77c7869f842a4cbc14035fafd228b4b Mon Sep 17 00:00:00 2001 From: Taevas Date: Sun, 30 Mar 2025 15:40:22 +0200 Subject: [PATCH 08/20] implement the new main menu into the actual game --- elements/player.tscn | 1 + index.tscn | 234 +++++------------------------------------- menus/main/area.tscn | 59 ++++++++--- menus/main/panel.tscn | 8 +- menus/menu.tscn | 18 +++- project.godot | 2 +- 6 files changed, 96 insertions(+), 226 deletions(-) diff --git a/elements/player.tscn b/elements/player.tscn index 6df684a..54b3132 100644 --- a/elements/player.tscn +++ b/elements/player.tscn @@ -96,6 +96,7 @@ spot_range = 50.0 collision_priority = 100.0 axis_lock_linear_z = true physics_material_override = SubResource("PhysicsMaterial_vumbr") +can_sleep = false contact_monitor = true [node name="CollisionShape3D" type="CollisionShape3D" parent="Sphere"] diff --git a/index.tscn b/index.tscn index 7c82b45..9c27b90 100644 --- a/index.tscn +++ b/index.tscn @@ -1,6 +1,4 @@ -[gd_scene load_steps=11 format=3 uid="uid://ccgnnif026wb4"] - -[ext_resource type="PackedScene" uid="uid://dtfybhftwfn00" path="res://levels/demo.tscn" id="1_356j3"] +[gd_scene load_steps=4 format=3 uid="uid://ccgnnif026wb4"] [sub_resource type="GDScript" id="GDScript_8n212"] script/source = "extends Node @@ -15,6 +13,7 @@ var levels = [ preload(\"res://levels/forest/level.tscn\"), preload(\"res://levels/night/level.tscn\"), ] +var area_resource = preload(\"res://menus/main/area.tscn\") func _ready() -> void: var os_name = OS.get_name() @@ -26,19 +25,25 @@ func _ready() -> void: $PauseMenu.hide() ## Add the version of the game and the name of the OS to the footer of the start menu if OS.has_feature(\"editor\"): - $StartMenu/VBoxContainer/MarginContainer2/Notice.text = \"dev \" - $StartMenu/VBoxContainer/MarginContainer2/Notice.text += \"build \" + ProjectSettings.get_setting(\"application/config/version\") + \" (\" + os_name + \")\" + $DevInfos.text = \"dev \" + $DevInfos.text += \"build \" + ProjectSettings.get_setting(\"application/config/version\") + \" (\" + os_name + \")\" + launch_area() + +func launch_area(): + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + var area := area_resource.instantiate() + area.connect(\"request_start\", start_game) + $Levels.add_child(area) ## 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 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: @@ -74,10 +79,8 @@ func stop_level() -> void: func win_game() -> void: $VictoryScreen.show() await get_tree().create_timer(2).timeout - $StartMenu/AnimationPlayer.play_backwards(\"fadeout\") - Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) - await get_tree().create_timer(1).timeout - $StartMenu.modulate = Color(1,1,1,1) + launch_area() + $VictoryScreen.hide() func restart_level() -> void: if changing_level == false: @@ -110,25 +113,18 @@ func fullscreen_game() -> void: else: DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_WINDOWED) -func _on_btn_start_pressed() -> void: +func start_game() -> void: seconds_spent = 0.0 current_level_int = 0 - var current_levels = get_current_levels() + var current_levels = $Levels.get_children(true) for level in current_levels: level.queue_free() $Informations.show() - $StartMenu/AnimationPlayer.play(\"fadeout\") Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) start_level(levels[0]) -func _on_animation_player_animation_finished(_anim_name: StringName) -> void: - if $StartMenu.modulate == Color(1,1,1,1): - $StartMenu.show() - else: - $StartMenu.hide() - func update_timer(delta: float) -> void: if playing: seconds_spent += delta @@ -145,7 +141,8 @@ func _process(delta: float) -> void: func _unhandled_key_input(event: InputEvent) -> void: if event.is_pressed() and event is InputEventKey: - if $StartMenu.visible == false and changing_level == false: + var current_levels := get_current_levels() + if len(current_levels) and changing_level == false: if event.keycode == KEY_ESCAPE: pause_game() elif event.keycode == KEY_R: @@ -166,61 +163,6 @@ font_size = 160 outline_size = 20 outline_color = Color(0, 0, 0, 1) -[sub_resource type="LabelSettings" id="LabelSettings_0jegv"] -font_size = 32 -shadow_size = 2 -shadow_color = Color(0, 0, 0, 1) -shadow_offset = Vector2(0, 0) - -[sub_resource type="LabelSettings" id="LabelSettings_nbp4i"] -font_size = 24 -shadow_size = 2 -shadow_color = Color(0, 0, 0, 1) -shadow_offset = Vector2(0, 0) - -[sub_resource type="LabelSettings" id="LabelSettings_5jdmh"] -font_size = 20 -shadow_size = 2 -shadow_color = Color(0, 0, 0, 1) -shadow_offset = Vector2(0, 0) - -[sub_resource type="Animation" id="Animation_glb01"] -length = 0.001 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:modulate") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [Color(1, 1, 1, 1)] -} - -[sub_resource type="Animation" id="Animation_75r06"] -resource_name = "fadeout" -step = 0.5 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:modulate") -tracks/0/interp = 2 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 1), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)] -} - -[sub_resource type="AnimationLibrary" id="AnimationLibrary_5cb2g"] -_data = { -&"RESET": SubResource("Animation_glb01"), -&"fadeout": SubResource("Animation_75r06") -} - [node name="Game" type="Node"] script = SubResource("GDScript_8n212") @@ -293,133 +235,6 @@ vertical_alignment = 1 [node name="Levels" type="Node" parent="."] -[node name="Demo" parent="Levels" instance=ExtResource("1_356j3")] - -[node name="StartMenu" 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="ColorRect" type="ColorRect" parent="StartMenu"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -color = Color(0.522749, 0.477374, 0.510111, 0.501961) - -[node name="VBoxContainer" type="VBoxContainer" parent="StartMenu"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="Presentation" type="MarginContainer" parent="StartMenu/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/margin_left = 20 -theme_override_constants/margin_top = 20 -theme_override_constants/margin_right = 20 -theme_override_constants/margin_bottom = 20 - -[node name="VBoxContainer" type="VBoxContainer" parent="StartMenu/VBoxContainer/Presentation"] -layout_mode = 2 - -[node name="Title" type="Label" parent="StartMenu/VBoxContainer/Presentation/VBoxContainer"] -layout_mode = 2 -text = "DreamBall" -label_settings = SubResource("LabelSettings_0jegv") -horizontal_alignment = 1 - -[node name="Title2" type="Label" parent="StartMenu/VBoxContainer/Presentation/VBoxContainer"] -layout_mode = 2 -text = "A game about changing gravity to make a ball fly through the air" -label_settings = SubResource("LabelSettings_nbp4i") -horizontal_alignment = 1 - -[node name="MarginContainer" type="MarginContainer" parent="StartMenu/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/margin_left = 20 -theme_override_constants/margin_top = 20 -theme_override_constants/margin_right = 20 -theme_override_constants/margin_bottom = 20 - -[node name="HBoxContainer" type="HBoxContainer" parent="StartMenu/VBoxContainer/MarginContainer"] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="Rules" type="VBoxContainer" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -alignment = 2 - -[node name="Label" type="Label" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Rules"] -layout_mode = 2 -text = "- Change the direction of the gravity with the ARROW KEYS" - -[node name="Label2" type="Label" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Rules"] -layout_mode = 2 -text = "- Keep LEFT SHIFT pressed to make the gravity stronger" - -[node name="Label3" type="Label" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Rules"] -layout_mode = 2 -text = "- Press ESCAPE to pause and unpause the game at any point" - -[node name="Label4" type="Label" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Rules"] -layout_mode = 2 -text = "- Press R to restart the current level from the beginning -(note that it won't reset the timer!)" - -[node name="Buttons" type="VBoxContainer" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -alignment = 2 - -[node name="Label5" type="Label" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Buttons"] -layout_mode = 2 -text = "Do your best to make the ball go through all the rings!" -horizontal_alignment = 1 - -[node name="HBoxContainer" type="HBoxContainer" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Buttons"] -layout_mode = 2 -theme_override_constants/separation = 20 -alignment = 1 - -[node name="Btn_Fullscreen" type="Button" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Buttons/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 4 -action_mode = 0 -text = "Toggle FULLSCREEN" - -[node name="Btn_Start" type="Button" parent="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Buttons/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 4 -text = "START the game!" - -[node name="MarginContainer2" type="MarginContainer" parent="StartMenu/VBoxContainer"] -layout_mode = 2 -theme_override_constants/margin_left = 20 -theme_override_constants/margin_top = 20 -theme_override_constants/margin_right = 20 -theme_override_constants/margin_bottom = 20 - -[node name="Notice" type="Label" parent="StartMenu/VBoxContainer/MarginContainer2"] -layout_mode = 2 -label_settings = SubResource("LabelSettings_5jdmh") -horizontal_alignment = 1 - -[node name="AnimationPlayer" type="AnimationPlayer" parent="StartMenu"] -libraries = { -&"": SubResource("AnimationLibrary_5cb2g") -} - [node name="PauseMenu" type="Control" parent="."] visible = false z_index = 2 @@ -472,9 +287,16 @@ layout_mode = 2 size_flags_vertical = 6 text = "QUIT game" -[connection signal="pressed" from="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Buttons/HBoxContainer/Btn_Fullscreen" to="." method="fullscreen_game"] -[connection signal="pressed" from="StartMenu/VBoxContainer/MarginContainer/HBoxContainer/Buttons/HBoxContainer/Btn_Start" to="." method="_on_btn_start_pressed"] -[connection signal="animation_finished" from="StartMenu/AnimationPlayer" to="." method="_on_animation_player_animation_finished"] +[node name="DevInfos" type="Label" parent="."] +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -23.0 +grow_horizontal = 2 +grow_vertical = 0 +horizontal_alignment = 1 + [connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Resume" to="." method="pause_game"] [connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Fullscreen2" to="." method="fullscreen_game"] [connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Restart" to="." method="restart_level"] diff --git a/menus/main/area.tscn b/menus/main/area.tscn index d7a6b56..152be3f 100644 --- a/menus/main/area.tscn +++ b/menus/main/area.tscn @@ -1,37 +1,45 @@ -[gd_scene load_steps=7 format=3 uid="uid://ikeidrgprk8k"] +[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) + $Menus/Panel1.change_menu(sm, false) func start() -> void: - var svm = save_file_manager.instantiate() - $Menus/Panel2.change_menu(svm) - rotate_cube(90) + request_start.emit() + #var svm = save_file_manager.instantiate() + #$Menus/Panel2.change_menu(svm) + #rotate_cube_to(90) func settings() -> void: var sm = settings_menu.instantiate() $Menus/Panel2.change_menu(sm) - rotate_cube(90) + rotate_cube_to(90) -func rotate_cube(degrees: int) -> void: +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 = $Menus.rotation_degrees + Vector3(0, degrees, 0) + 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) @@ -76,18 +84,26 @@ _data = { &"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 = 1 +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="SpotLight3D" type="SpotLight3D" parent="."] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 1.90692, 0) - [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) @@ -107,7 +123,24 @@ libraries = { } [node name="Camera3D" type="Camera3D" parent="."] -transform = Transform3D(0.975535, 0, 0.219846, 0, 1, 0, -0.219846, 0, 0.975535, 0.86618, 0.217345, 1.76032) +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/panel.tscn b/menus/main/panel.tscn index 04553cd..acadfdf 100644 --- a/menus/main/panel.tscn +++ b/menus/main/panel.tscn @@ -73,8 +73,11 @@ func _mouse_input_event(_camera: Camera3D, event: InputEvent, event_position: Ve # original code -func change_menu(new_menu: Control): - $SubViewport/Menu.add(new_menu) +signal request_return + +func change_menu(new_menu: Control, return_button: bool = true): + $SubViewport/Menu.add(new_menu, return_button) + $SubViewport/Menu.connect(\"disabled\", func(): request_return.emit()) " [sub_resource type="QuadMesh" id="QuadMesh_gfsnp"] @@ -85,7 +88,6 @@ viewport_path = NodePath("SubViewport") [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_a5lkq"] resource_local_to_scene = true transparency = 1 -shading_mode = 0 albedo_texture = SubResource("ViewportTexture_gfsnp") [sub_resource type="BoxShape3D" id="BoxShape3D_gfsnp"] diff --git a/menus/menu.tscn b/menus/menu.tscn index dddea27..bc66e2b 100644 --- a/menus/menu.tscn +++ b/menus/menu.tscn @@ -3,7 +3,9 @@ [sub_resource type="GDScript" id="GDScript_18a3y"] script/source = "extends MarginContainer -func add(node: Control): +signal disabled + +func add(node: Control, return_button: bool): var add_to := $VSplitContainer/MarginContainer/ColorRect var children := add_to.get_children(true) for child in children: @@ -16,8 +18,18 @@ func add(node: Control): add_to.add_child(node) $VSplitContainer/AnimationPlayer.play(\"split_offset\") - #await get_tree().create_timer(3.0).timeout - #$VSplitContainer/AnimationPlayer.play_backwards(\"split_offset\") + if return_button: + var button = Button.new() + button.text = \"Return\" + button.anchor_top = 1.0 + button.anchor_bottom = 1.0 + button.grow_vertical = Control.GROW_DIRECTION_BEGIN + button.connect(\"pressed\", disable) + node.add_child(button) + +func disable(): + $VSplitContainer/AnimationPlayer.play_backwards(\"split_offset\") + disabled.emit() " [sub_resource type="Animation" id="Animation_y8e4h"] diff --git a/project.godot b/project.godot index 3a26eb9..47b00e4 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="20250326.0" +config/version="20250330.0" run/main_scene="res://index.tscn" config/features=PackedStringArray("4.4", "GL Compatibility") boot_splash/show_image=false From ff8bd770d2818cda595763387f6c9312b8791ed8 Mon Sep 17 00:00:00 2001 From: Taevas Date: Mon, 31 Mar 2025 17:57:54 +0200 Subject: [PATCH 09/20] preliminary support for controllers --- elements/gravity.tscn | 18 +++++++++------ export_presets.cfg | 12 +++++++--- index.tscn | 4 ++-- project.godot | 54 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 12 deletions(-) diff --git a/elements/gravity.tscn b/elements/gravity.tscn index 533369c..d84e534 100644 --- a/elements/gravity.tscn +++ b/elements/gravity.tscn @@ -12,7 +12,7 @@ const stronger = normal * 3 func _input(_event: InputEvent) -> void: if player_ready: - var grav = stronger if Input.is_key_pressed(KEY_SHIFT) else normal + 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) " @@ -25,22 +25,26 @@ func _ready() -> void: func _input(ev: InputEvent) -> void: if ev is InputEventKey and ev.is_pressed(): - var k = ev.keycode - if k == KEY_UP or k == KEY_RIGHT or k == KEY_LEFT or k == KEY_DOWN: + 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 k == KEY_UP: + if up: direction = Vector3.UP self.rotation_degrees = -90 $AudioStreamPlayer.pitch_scale = 1.05 - elif k == KEY_LEFT: + elif left: direction = Vector3.LEFT self.rotation_degrees = -180 $AudioStreamPlayer.pitch_scale = 1.02 - elif k == KEY_RIGHT: + elif right: direction = Vector3.RIGHT self.rotation_degrees = 0 $AudioStreamPlayer.pitch_scale = 0.98 - elif k == KEY_DOWN: + elif down: direction = Vector3.DOWN self.rotation_degrees = 90 $AudioStreamPlayer.pitch_scale = 0.95 diff --git a/export_presets.cfg b/export_presets.cfg index 4833fd5..81b9e74 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -9,9 +9,11 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="exports/*" -export_path="exports/AlakajamEntry.x86_64" +export_path="exports/DreamBall.x86_64" +patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" +seed=0 encrypt_pck=false encrypt_directory=false script_export_mode=2 @@ -49,9 +51,11 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="exports/*" -export_path="exports/AlakajamEntry.exe" +export_path="exports/DreamBall.exe" +patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" +seed=0 encrypt_pck=false encrypt_directory=false script_export_mode=2 @@ -114,9 +118,11 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="exports/*" -export_path="exports/web/AlakajamEntry.html" +export_path="exports/web/DreamBall.html" +patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" +seed=0 encrypt_pck=false encrypt_directory=false script_export_mode=2 diff --git a/index.tscn b/index.tscn index 9c27b90..ae6d27c 100644 --- a/index.tscn +++ b/index.tscn @@ -143,9 +143,9 @@ func _unhandled_key_input(event: InputEvent) -> void: if event.is_pressed() and event is InputEventKey: var current_levels := get_current_levels() if len(current_levels) and changing_level == false: - if event.keycode == KEY_ESCAPE: + if Input.is_action_just_pressed(\"pause_game\"): pause_game() - elif event.keycode == KEY_R: + elif Input.is_action_just_pressed(\"restart_level\"): pause_game() restart_level() diff --git a/project.godot b/project.godot index 47b00e4..d96823e 100644 --- a/project.godot +++ b/project.godot @@ -24,6 +24,60 @@ config/windows_native_icon="res://icon.ico" Settings="*res://settings.gd" +[input] + +gravity_up={ +"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":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, 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":87,"key_label":0,"unicode":122,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":11,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null) +] +} +gravity_left={ +"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":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, 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":65,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":13,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null) +] +} +gravity_right={ +"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":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, 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":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":14,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null) +] +} +gravity_down={ +"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":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, 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":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":12,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null) +] +} +gravity_strong={ +"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":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":false,"script":null) +] +} +pause_game={ +"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":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":4,"pressure":0.0,"pressed":false,"script":null) +] +} +restart_level={ +"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":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":114,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":2,"pressure":0.0,"pressed":false,"script":null) +] +} + [physics] 3d/run_on_separate_thread=true From 18dae312eb26c898acc8eb7f9e9eec6c8580b094 Mon Sep 17 00:00:00 2001 From: Taevas Date: Mon, 14 Apr 2025 23:07:52 +0200 Subject: [PATCH 10/20] sound effects for the main menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit started working on this like 2 weeks ago then stuff happened and now I forgot what I was doing 👍 --- levels/base/environment.tscn | 26 -------------------------- levels/base/level.tscn | 28 +++++++++++++++++++++++++--- levels/demo.tscn | 24 +++++++++++++++++++++--- levels/forest/environment.tscn | 24 ------------------------ levels/forest/level.tscn | 5 ++--- levels/night/environment.tscn | 22 ---------------------- levels/night/level.tscn | 24 +++++++++++++++++++++--- menus/menu.tscn | 21 ++++++++++++++++++++- sounds/confirm.ogg | Bin 0 -> 34288 bytes sounds/confirm.ogg.import | 19 +++++++++++++++++++ sounds/select.ogg | Bin 0 -> 35713 bytes sounds/select.ogg.import | 19 +++++++++++++++++++ sounds/templateiguess.mmpz | Bin 1707 -> 0 bytes sounds/templateiguess.mmpz.bak | Bin 1757 -> 0 bytes 14 files changed, 127 insertions(+), 85 deletions(-) delete mode 100644 levels/base/environment.tscn delete mode 100644 levels/forest/environment.tscn delete mode 100644 levels/night/environment.tscn create mode 100644 sounds/confirm.ogg create mode 100644 sounds/confirm.ogg.import create mode 100644 sounds/select.ogg create mode 100644 sounds/select.ogg.import delete mode 100644 sounds/templateiguess.mmpz delete mode 100644 sounds/templateiguess.mmpz.bak diff --git a/levels/base/environment.tscn b/levels/base/environment.tscn deleted file mode 100644 index a3dab31..0000000 --- a/levels/base/environment.tscn +++ /dev/null @@ -1,26 +0,0 @@ -[gd_scene load_steps=6 format=3 uid="uid://ygvokp5f78i5"] - -[sub_resource type="Gradient" id="Gradient_hs6gw"] - -[sub_resource type="GradientTexture2D" id="GradientTexture2D_hyysp"] -gradient = SubResource("Gradient_hs6gw") -fill_from = Vector2(0.536082, 1) -fill_to = Vector2(0.536082, 0) - -[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_g3k38"] -panorama = SubResource("GradientTexture2D_hyysp") - -[sub_resource type="Sky" id="Sky_du8e4"] -sky_material = SubResource("PanoramaSkyMaterial_g3k38") -radiance_size = 1 - -[sub_resource type="Environment" id="Environment_y0yoy"] -background_mode = 2 -background_color = Color(0.758192, 0.758192, 0.758192, 1) -sky = SubResource("Sky_du8e4") -ambient_light_source = 3 -ambient_light_color = Color(1, 1, 1, 1) -reflected_light_source = 2 - -[node name="Environment" type="WorldEnvironment"] -environment = SubResource("Environment_y0yoy") diff --git a/levels/base/level.tscn b/levels/base/level.tscn index 0a7d2e2..0683839 100644 --- a/levels/base/level.tscn +++ b/levels/base/level.tscn @@ -1,6 +1,5 @@ -[gd_scene load_steps=8 format=3 uid="uid://ovtknjyj83gh"] +[gd_scene load_steps=12 format=3 uid="uid://ovtknjyj83gh"] -[ext_resource type="PackedScene" uid="uid://ygvokp5f78i5" path="res://levels/base/environment.tscn" id="1_n7fd8"] [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"] @@ -8,13 +7,36 @@ [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="Gradient" id="Gradient_hs6gw"] + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_hyysp"] +gradient = SubResource("Gradient_hs6gw") +fill_from = Vector2(0.536082, 1) +fill_to = Vector2(0.536082, 0) + +[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_g3k38"] +panorama = SubResource("GradientTexture2D_hyysp") + +[sub_resource type="Sky" id="Sky_du8e4"] +sky_material = SubResource("PanoramaSkyMaterial_g3k38") +radiance_size = 1 + +[sub_resource type="Environment" id="Environment_y0yoy"] +background_mode = 2 +background_color = Color(0.758192, 0.758192, 0.758192, 1) +sky = SubResource("Sky_du8e4") +ambient_light_source = 3 +ambient_light_color = Color(1, 1, 1, 1) +reflected_light_source = 2 + [node name="Base" type="Node3D"] script = ExtResource("1_rj40i") metadata/_custom_type_script = "uid://w3fetao1pegm" [node name="Gravity" parent="." instance=ExtResource("3_muudg")] -[node name="Environment" parent="." instance=ExtResource("1_n7fd8")] +[node name="Environment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_y0yoy") [node name="Music" parent="." instance=ExtResource("4_uq42r")] diff --git a/levels/demo.tscn b/levels/demo.tscn index 51fed8b..4e1e5ad 100644 --- a/levels/demo.tscn +++ b/levels/demo.tscn @@ -1,19 +1,37 @@ -[gd_scene load_steps=7 format=3 uid="uid://dtfybhftwfn00"] +[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://b6gnffoboc5j5" path="res://levels/night/environment.tscn" id="3_j4ado"] [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" parent="." instance=ExtResource("3_j4ado")] +[node name="Environment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_8o42c") [node name="Music" parent="." instance=ExtResource("4_qr8kk")] diff --git a/levels/forest/environment.tscn b/levels/forest/environment.tscn deleted file mode 100644 index b4b9656..0000000 --- a/levels/forest/environment.tscn +++ /dev/null @@ -1,24 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://6nfv4ewj3jop"] - -[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_clvpw"] -sky_top_color = Color(0, 0.504542, 0.579919, 1) -sky_horizon_color = Color(9.62615e-08, 0.776269, 0.496436, 1) -sky_curve = 0.219613 -ground_bottom_color = Color(0.078119, 0.275869, 4.81308e-08, 1) -ground_horizon_color = Color(0.300489, 0.406247, 0.0024617, 1) -sun_angle_max = 224.83 -sun_curve = 0.212132 - -[sub_resource type="Sky" id="Sky_8w3rf"] -sky_material = SubResource("ProceduralSkyMaterial_clvpw") - -[sub_resource type="Environment" id="Environment_nda78"] -background_mode = 2 -sky = SubResource("Sky_8w3rf") -sky_rotation = Vector3(0.0872665, 0.698132, 0.0872665) -ambient_light_source = 3 -ambient_light_color = Color(1, 1, 1, 1) -reflected_light_source = 2 - -[node name="Environment" type="WorldEnvironment"] -environment = SubResource("Environment_nda78") diff --git a/levels/forest/level.tscn b/levels/forest/level.tscn index 31e9018..36176ba 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -1,6 +1,5 @@ -[gd_scene load_steps=12 format=3 uid="uid://e1761h6d522a"] +[gd_scene load_steps=11 format=3 uid="uid://e1761h6d522a"] -[ext_resource type="PackedScene" uid="uid://6nfv4ewj3jop" path="res://levels/forest/environment.tscn" id="1_7clrg"] [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"] @@ -40,7 +39,7 @@ transform = Transform3D(-0.920505, 0.195366, -0.338383, 0, 0.866025, 0.5, 0.3907 shadow_enabled = true directional_shadow_mode = 0 -[node name="Environment" parent="." instance=ExtResource("1_7clrg")] +[node name="Environment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_y3whn") [node name="Music" parent="." instance=ExtResource("3_n1xsx")] diff --git a/levels/night/environment.tscn b/levels/night/environment.tscn deleted file mode 100644 index 127d6d0..0000000 --- a/levels/night/environment.tscn +++ /dev/null @@ -1,22 +0,0 @@ -[gd_scene load_steps=6 format=3 uid="uid://b6gnffoboc5j5"] - -[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="Environment" type="WorldEnvironment"] -environment = SubResource("Environment_8o42c") diff --git a/levels/night/level.tscn b/levels/night/level.tscn index b40c61a..08f79de 100644 --- a/levels/night/level.tscn +++ b/levels/night/level.tscn @@ -1,22 +1,40 @@ -[gd_scene load_steps=10 format=3 uid="uid://drnqmu4lka22d"] +[gd_scene load_steps=14 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://b6gnffoboc5j5" path="res://levels/night/environment.tscn" id="2_wintp"] [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="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="Night" type="Node3D"] script = ExtResource("1_3m1pa") metadata/_custom_type_script = "uid://w3fetao1pegm" [node name="Gravity" parent="." instance=ExtResource("2_lfplq")] -[node name="Environment" parent="." instance=ExtResource("2_wintp")] +[node name="Environment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_8o42c") [node name="Music" parent="." instance=ExtResource("1_npc74")] diff --git a/menus/menu.tscn b/menus/menu.tscn index bc66e2b..3883c59 100644 --- a/menus/menu.tscn +++ b/menus/menu.tscn @@ -1,4 +1,7 @@ -[gd_scene load_steps=6 format=3 uid="uid://tasbgr10p84c"] +[gd_scene load_steps=8 format=3 uid="uid://tasbgr10p84c"] + +[ext_resource type="AudioStream" uid="uid://bnbf1nfaxuagi" path="res://sounds/select.ogg" id="1_y8e4h"] +[ext_resource type="AudioStream" uid="uid://bq3tw55s0ud55" path="res://sounds/confirm.ogg" id="2_hl3ms"] [sub_resource type="GDScript" id="GDScript_18a3y"] script/source = "extends MarginContainer @@ -26,6 +29,14 @@ func add(node: Control, return_button: bool): button.grow_vertical = Control.GROW_DIRECTION_BEGIN button.connect(\"pressed\", disable) node.add_child(button) + + link_to_sounds(node) + +func link_to_sounds(node: Control): + var buttons := node.find_children(\"*\", \"Button\", true, false) + for button in buttons as Array[Button]: + button.mouse_entered.connect(func(): $Select.play()) + button.pressed.connect(func(): $Confirm.play()) func disable(): $VSplitContainer/AnimationPlayer.play_backwards(\"split_offset\") @@ -109,3 +120,11 @@ theme_override_constants/margin_top = 20 [node name="ColorRect" type="ColorRect" parent="VSplitContainer/MarginContainer"] layout_mode = 2 color = Color(0, 0, 0, 1) + +[node name="Select" type="AudioStreamPlayer" parent="."] +stream = ExtResource("1_y8e4h") +bus = &"Sounds" + +[node name="Confirm" type="AudioStreamPlayer" parent="."] +stream = ExtResource("2_hl3ms") +bus = &"Sounds" diff --git a/sounds/confirm.ogg b/sounds/confirm.ogg new file mode 100644 index 0000000000000000000000000000000000000000..f059bcc7e3c928c290f71f09d91f33ecec9bec85 GIT binary patch literal 34288 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 literal 0 HcmV?d00001 diff --git a/sounds/select.ogg.import b/sounds/select.ogg.import new file mode 100644 index 0000000..4eaade7 --- /dev/null +++ b/sounds/select.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://bnbf1nfaxuagi" +path="res://.godot/imported/select.ogg-c7e346119419231e86eb892abcba5520.oggvorbisstr" + +[deps] + +source_file="res://sounds/select.ogg" +dest_files=["res://.godot/imported/select.ogg-c7e346119419231e86eb892abcba5520.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/sounds/templateiguess.mmpz b/sounds/templateiguess.mmpz deleted file mode 100644 index d1d4b4b3ea27e989f4f5d76cfce7317070f9f492..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1707 zcmV;c22}X~02?rPoaI?tZ`(E$e($f~^wO8vlx)Y%94AGa6xe{KE!u3uP!t0#Q8rgf zWJx-Y#XHC})1M(0=JUsf&MTh5nvgS{>1xL43X(r0^C>h4l(~C#*Z?0Zn z-u&|B1>!}a2U3aOXlg*-UUu(7siMRP)e??|qgs3(y}Z7D7a_Bg^gPm{Tt*j<5Sp(j z$&g_M)ol4|MoUq!((6wmw2%ehOaQzf+E68#Nmu7ly}lJ3^gyu!DAxs5)$Q0Bu%_$< zTpC5vkEtjPVI@@vEU|+9PBVwNJ&#Tzv|*V63)9%y*tYg}ZolO|x@DRzI4qM0tteZr z%y~418@;v49@2sfhftCNly4N1oW9m60ZB^# zzs|{mXvm)dgp(&hu1LA`4z4xrlJBLfXhxM$Nj6eJCrD=bD8Au&cnrc3u3!?NqL} zTp6|8h3mm+;-c^-YrpE~oI372Ox!%pj-2w07=rS2ZQKE~<(_Y-@wTfnM7g3GE`}%j z&zy2_9C0me-43Ca>}B`#A!1A~H#Rj}C^;X&THBN84Ip0WJE;9mz-30c-zJ6EQ%>NG zucQ;L1kVCoYB()#E8XgUgJO;7U75nO@v&w!^|#_)tdOM#6DvINhY;%=`y|2t$w|0Svl61Lb4g;>en@ZXVwV%i!?YIlVf@xh{>yNC4Xqq`J zmc%xC+r4X?{oGop7Od52?&!n=>vWg$S~X*;-%ARXF)5Tmtdwh*A*3ur(!BuYsuG-2 zcPkQW)7DCCOWV@Si*G3xDKk6Uf-6-Z6-z~meN{iutePEf&5hG>P;PV*c%kKVhZ-C^B;u7^MFE)Q~nccg&PiQHbF2aMW0 zKs!~BQjPw@1N`0*w4m?h{c|`d_&L#~eeKY*kiz+%yXY+&yoWYZ+BDcO^|DOrH8 z!4A{2$zHOKg;qd11e_)sCquDZ-p$bh(0|R;dS-*~WSuc#n=+iJlxp2$Y*nM`=|MY{ zp+j&XWC&R2%HcZBu{I_gAsWPqJx!MM2X)@4v(iT4RJ4LF42>DkTVmd-w!tOsL zFEz@8p)hsC4~EFp5kC+*Q=OYWsR=t@gW6Pmy)0l3di zaF2#VTC#RF(rt{J#qa8hT9&L8Kd*D8cfjwIrQ4qsK zd5lB0haXJ*O5JH6wsko0FSS2yehoWnz1i3=oc*l5s@e-Ye>g|p4lT7iP1ba$VDABZ z)pVAx4ShokDZV!7wIhhO&>6j zc-$0`>sYqewkoadb>vDkZ20szpP{RDyUP7~-yGM5tDr-mVR>0M-nWp8pBUX7c$+zG z-e$%phu&toIcUANjGAUPmyDFc+Ui>vPn#^CP7hg_*q=NPWqESiSZGQG=8hfnJwwUg zm`RQpX@}uIBG{&``v%`v`26J$ zmna%w{PXw{(Z^CV z%uEK=G`GSF_Yvb_4g-#aV*CU;D?_)&4g+`#DviqtzL>jCADW|8z^T>%B)>(Rp+C@O z#S9&2Nm4q4qellDMdTU!^AWtJ+dD7j*c)4jY=^?wKsneLWv0$K5Yre*yk994VxTd@SN&VJ(j!RaMvgU}40&R~T zz)%U4U1h9Ph?H0a*F=&j1(TC7Sf7Lg-vbat3i1cejR_mGzQAO@P$yw8j3< z3alN%iq%%@3yP(xcqF6DI%M>~phN`>E(PHfzmplER2%OkfDBSfCBW}eS!qpFI4aHW z6iM(~ft%D7`c)6Fr(&adUQ;4q);%+mdczVohCDvl{0}Q47!l< zvlUjFw)M<01rviDXBAtfC@!y2VR!>7dA@xZfw`aR~$&$O~T06>$E00o$aTnAEHt16_#knSBTQ1@f9BZWAJqn*(2c5 zvIP@))vE$C>EIGUi?HR}P@0jesJ>t%IG)dmkpbhJ2s=&l=~XD9AA(}awR^F`yQEp;60ZYsUDvW_G0lsA2;u^}ZS8vW1s4R5?B#r{z zT`)?qjk>YYaXEs=QV zvEB^N`Um0JQM}X2?h4X=*-!>8xZK+R%@A$y{GJGHN9)~insfICw3Z$X4&b%n@Mgef zb4W(G;A+!_g$BWCICcQS#WjF470DsEct3tKOj`hMkKP8+CHF&ghkUpR;&sx&SAo2w z26@J*8s(+wA(+=mdN%>R-D0$7)dPK_w1Ird9Uz~R@oxaX!~ujo@F0$#+rN>A6Jkpr z9qCAIA%@=QLEw>#!J`3$&g1_+EPOD3_d&yI(}SiC2idmh*83uM$mGhT zEd&+q0=vkEYi+c-g99}(%owyyYSJkk4TniG80q(b&U0(!^K{#FbHt_EQ2(6W6TS{= zaW9vO6-W_Q?gDV+*38m=UHcusP5+)0ZRnK3cyj z+kXQ4bAn2yb!}_t4fjR4uPdXwR*nv77!4Z@O{zfO)7^dRVJ3UjBzL*H zwv|+0V01~B+X{~2eX(kX>W2GqAMToyr=QNwUtYfa)O-c{{_^5Y@czeFZ+m;rLT&nW{5H=K@k5gpd|P+SS^Uc z9m!FK$8bP#XC)9n4*q&FiOkN^No18xS-uIXwh_!gM`wl#LO0`QZ<%7jM^wsZ$96$N zh{j3GRW)Ck7>*%?HG>v2hNLPGJ=R|FT5{)$>To0wKo-8}XsgHOYIzK?7jsf}6yxKV zuibpg6y%OaqIjk}f-ve0Ma!Q8&bjK45+E5+j{_YQHw7kmnp2@e7jgL4e(NCUxPShv ziLE@Udleu&^Y(`FxGrRr1MaqU3&x2d>q=LrV}p^T{pc3iuWpmb|9tp2j!A!6$qZ$f From 7ae566720c2ddd9f389e7f2753fb54cffa974a98 Mon Sep 17 00:00:00 2001 From: Taevas Date: Wed, 16 Apr 2025 00:16:39 +0200 Subject: [PATCH 11/20] integrate a semi-working manager for save files (#3) --- menus/main/area.tscn | 8 +- menus/main/save_file_manager.tscn | 151 +++++++++++++++--------------- menus/main/start_menu.tscn | 7 +- menus/menu.tscn | 9 +- project.godot | 1 + save_files.gd | 38 ++++++++ save_files.gd.uid | 1 + 7 files changed, 131 insertions(+), 84 deletions(-) create mode 100644 save_files.gd create mode 100644 save_files.gd.uid diff --git a/menus/main/area.tscn b/menus/main/area.tscn index 152be3f..0759372 100644 --- a/menus/main/area.tscn +++ b/menus/main/area.tscn @@ -23,10 +23,10 @@ func _ready() -> void: $Menus/Panel1.change_menu(sm, false) func start() -> void: - request_start.emit() - #var svm = save_file_manager.instantiate() - #$Menus/Panel2.change_menu(svm) - #rotate_cube_to(90) + 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() diff --git a/menus/main/save_file_manager.tscn b/menus/main/save_file_manager.tscn index da7f052..9a86fb7 100644 --- a/menus/main/save_file_manager.tscn +++ b/menus/main/save_file_manager.tscn @@ -1,102 +1,105 @@ -[gd_scene load_steps=5 format=3 uid="uid://w5te3qujco7d"] +[gd_scene load_steps=2 format=3 uid="uid://w5te3qujco7d"] [sub_resource type="GDScript" id="GDScript_a5kgg"] -script/source = "extends MarginContainer +script/source = "extends Control -func _ready() -> void: - $HSplitContainer/CenterContainer/Label.hide() +signal request_start +@onready var label = $VBoxContainer/MarginContainer/VBoxContainer/Label -func _on_button_pressed() -> void: - $HSplitContainer/CenterContainer/Label.show() - $AnimationPlayer.play(\"add_information\") +func _on_save_1_pressed() -> void: + label.text = $VBoxContainer/Save1.text + display_file_data(SaveFiles.read(SaveFiles.names[0])) + +func _on_save_2_pressed() -> void: + label.text = $VBoxContainer/Save2.text + display_file_data(SaveFiles.read(SaveFiles.names[1])) + +func _on_save_3_pressed() -> void: + label.text = $VBoxContainer/Save3.text + display_file_data(SaveFiles.read(SaveFiles.names[2])) + +func display_file_data(data: Variant) -> void: + $VBoxContainer/MarginContainer.show() + label.text += \" | \" + if data.has(\"played_for\"): + label.text += data.played_for + else: + label.text += \"00:00\" + +func _on_start_pressed() -> void: + request_start.emit() + +func _on_delete_pressed() -> void: + SaveFiles.empty(SaveFiles.selected_file) " -[sub_resource type="Animation" id="Animation_a5kgg"] -resource_name = "add_information" -length = 0.2 -step = 0.2 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("HSplitContainer/CenterContainer:size_flags_stretch_ratio") -tracks/0/interp = 2 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.2), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [0.0, 2.0] -} - -[sub_resource type="Animation" id="Animation_xs01b"] -length = 0.001 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("HSplitContainer/CenterContainer:size_flags_stretch_ratio") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [0.0] -} - -[sub_resource type="AnimationLibrary" id="AnimationLibrary_36lt1"] -_data = { -&"RESET": SubResource("Animation_xs01b"), -&"add_information": SubResource("Animation_a5kgg") -} - -[node name="SaveFileManager" type="MarginContainer"] +[node name="SaveFileManager" type="Control"] +layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 -theme_override_constants/margin_left = 20 -theme_override_constants/margin_top = 20 -theme_override_constants/margin_right = 20 -theme_override_constants/margin_bottom = 20 script = SubResource("GDScript_a5kgg") -[node name="HSplitContainer" type="HSplitContainer" parent="."] -layout_mode = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer"] -layout_mode = 2 +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 14 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_top = -50.5 +offset_bottom = 50.5 +grow_horizontal = 2 +grow_vertical = 2 size_flags_horizontal = 3 alignment = 1 -[node name="Button" type="Button" parent="HSplitContainer/VBoxContainer"] +[node name="Save1" type="Button" parent="VBoxContainer"] layout_mode = 2 -text = "Save file" +text = "Save file #1" -[node name="Button2" type="Button" parent="HSplitContainer/VBoxContainer"] +[node name="Save2" type="Button" parent="VBoxContainer"] layout_mode = 2 -text = "Save file" +text = "Save file #2" -[node name="Button3" type="Button" parent="HSplitContainer/VBoxContainer"] +[node name="Save3" type="Button" parent="VBoxContainer"] layout_mode = 2 -text = "Save file" +text = "Save file #3" -[node name="CenterContainer" type="CenterContainer" parent="HSplitContainer"] +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] +visible = false +layout_mode = 2 +theme_override_constants/margin_left = 30 +theme_override_constants/margin_top = 30 +theme_override_constants/margin_right = 30 +theme_override_constants/margin_bottom = 30 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Save File" +horizontal_alignment = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 10 +alignment = 1 + +[node name="Start" type="Button" parent="VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer"] layout_mode = 2 size_flags_horizontal = 3 -size_flags_stretch_ratio = 0.0 +text = "Start" -[node name="Label" type="Label" parent="HSplitContainer/CenterContainer"] -show_behind_parent = true +[node name="Delete" type="Button" parent="VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer"] layout_mode = 2 -text = "Information" +text = "Delete" -[node name="AnimationPlayer" type="AnimationPlayer" parent="."] -libraries = { -&"": SubResource("AnimationLibrary_36lt1") -} - -[connection signal="pressed" from="HSplitContainer/VBoxContainer/Button" to="." method="_on_button_pressed"] +[connection signal="pressed" from="VBoxContainer/Save1" to="." method="_on_save_1_pressed"] +[connection signal="pressed" from="VBoxContainer/Save2" to="." method="_on_save_2_pressed"] +[connection signal="pressed" from="VBoxContainer/Save3" to="." method="_on_save_3_pressed"] +[connection signal="pressed" from="VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer/Start" to="." method="_on_start_pressed"] +[connection signal="pressed" from="VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer/Delete" to="." method="_on_delete_pressed"] diff --git a/menus/main/start_menu.tscn b/menus/main/start_menu.tscn index 9182cc7..f62f59a 100644 --- a/menus/main/start_menu.tscn +++ b/menus/main/start_menu.tscn @@ -27,12 +27,9 @@ script = SubResource("GDScript_vfuxb") [node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 1 -anchors_preset = 14 -anchor_top = 0.5 +anchors_preset = 15 anchor_right = 1.0 -anchor_bottom = 0.5 -offset_top = -50.5 -offset_bottom = 50.5 +anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 alignment = 1 diff --git a/menus/menu.tscn b/menus/menu.tscn index 3883c59..62e1204 100644 --- a/menus/menu.tscn +++ b/menus/menu.tscn @@ -33,10 +33,17 @@ func add(node: Control, return_button: bool): link_to_sounds(node) func link_to_sounds(node: Control): - var buttons := node.find_children(\"*\", \"Button\", true, false) + var children := node.find_children(\"*\", \"\", true, false) + + var buttons := children.filter(func(child): return is_instance_of(child, Button)) for button in buttons as Array[Button]: button.mouse_entered.connect(func(): $Select.play()) button.pressed.connect(func(): $Confirm.play()) + + var sliders := children.filter(func(child): return is_instance_of(child, HSlider)) + for slider in sliders as Array[HSlider]: + slider.mouse_entered.connect(func(): $Select.play()) + slider.drag_ended.connect(func(_value): $Confirm.play()) func disable(): $VSplitContainer/AnimationPlayer.play_backwards(\"split_offset\") diff --git a/project.godot b/project.godot index d96823e..1e0076e 100644 --- a/project.godot +++ b/project.godot @@ -23,6 +23,7 @@ config/windows_native_icon="res://icon.ico" [autoload] Settings="*res://settings.gd" +SaveFiles="*res://save_files.gd" [input] diff --git a/save_files.gd b/save_files.gd new file mode 100644 index 0000000..71e00f5 --- /dev/null +++ b/save_files.gd @@ -0,0 +1,38 @@ +extends Node + +const names := [ + "user://save_file_1.save", + "user://save_file_2.save", + "user://save_file_3.save", +] + +var selected_file := names[0] + +func _ready() -> void: + for save_file_name in names: + ensure_existence(save_file_name) + +func ensure_existence(save_file_name: String) -> void: + if save_file_name in names and not FileAccess.file_exists(save_file_name): + print("Save file ", save_file_name, " not found, creating it") + empty(save_file_name) + +func empty(save_file_name: String) -> void: + print("Writing an empty object on ", save_file_name) + var save_file := FileAccess.open(save_file_name, FileAccess.WRITE) + save_file.store_line(JSON.stringify({})) + save_file.store_line("FOR YOUR SAFETY, ALWAYS CHECK IF THE DATA OF THE FILES YOU DOWNLOAD LOOKS OKAY") + +func read(save_file_name: String) -> Variant: + ensure_existence(save_file_name) + selected_file = save_file_name + + var save_file := FileAccess.open(save_file_name, FileAccess.READ) + var json := JSON.new() + var json_string := save_file.get_line() + + if json.parse(json_string) != OK: + print("(SAVES) JSON Parse Error: ", json.get_error_message(), " in ", json_string, " at line ", json.get_error_line()) + return {} + + return json.data diff --git a/save_files.gd.uid b/save_files.gd.uid new file mode 100644 index 0000000..7ccd88d --- /dev/null +++ b/save_files.gd.uid @@ -0,0 +1 @@ +uid://crfwrvs7rlc5h From ac1f29d11153df588a165a2c2d543fff8a247e18 Mon Sep 17 00:00:00 2001 From: Taevas Date: Sun, 20 Apr 2025 23:25:51 +0200 Subject: [PATCH 12/20] new timer + save times to save files (#3) --- elements/timer.tscn | 81 ++++++++++++++++++++ fonts/Knewave/Knewave-Regular.ttf | Bin 0 -> 31280 bytes fonts/Knewave/Knewave-Regular.ttf.import | 35 +++++++++ fonts/Knewave/OFL.txt | 93 +++++++++++++++++++++++ index.tscn | 42 +++++----- menus/main/save_file_manager.tscn | 40 ++++++---- save_files.gd | 15 +++- 7 files changed, 269 insertions(+), 37 deletions(-) create mode 100644 elements/timer.tscn create mode 100644 fonts/Knewave/Knewave-Regular.ttf create mode 100644 fonts/Knewave/Knewave-Regular.ttf.import create mode 100644 fonts/Knewave/OFL.txt diff --git a/elements/timer.tscn b/elements/timer.tscn new file mode 100644 index 0000000..3a2ae36 --- /dev/null +++ b/elements/timer.tscn @@ -0,0 +1,81 @@ +[gd_scene load_steps=5 format=3 uid="uid://xd3nsiglcdfc"] + +[ext_resource type="FontFile" uid="uid://c3fsj6knyiuhl" path="res://fonts/Knewave/Knewave-Regular.ttf" id="1_m1tld"] + +[sub_resource type="GDScript" id="GDScript_q235s"] +script/source = "extends VBoxContainer + +var enabled := false: + get: return enabled + set(value): + 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 + +var seconds_spent_total := 0.0: + get: return seconds_spent_total + set(value): + seconds_spent_total = value + $Total.text = \"Total: \" + seconds_to_readable(seconds_spent_total) +var seconds_spent_level_attempt := 0.0: + get: return seconds_spent_level_attempt + set(value): + 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 seconds_to_readable(seconds: float) -> String: + var minutes: int = floor(seconds / 60) + return (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) + +func _physics_process(delta: float) -> void: + if enabled: + seconds_spent_total += delta + seconds_spent_level_attempt += delta +" + +[sub_resource type="LabelSettings" id="LabelSettings_m1tld"] +font = ExtResource("1_m1tld") +font_size = 32 +outline_size = 8 +outline_color = Color(1, 0, 1, 1) +shadow_size = 8 +shadow_color = Color(0, 0, 0, 1) + +[sub_resource type="LabelSettings" id="LabelSettings_2a86r"] +font = ExtResource("1_m1tld") +font_size = 24 +outline_size = 8 +outline_color = Color(0, 0, 1, 1) +shadow_size = 8 +shadow_color = Color(0, 0, 0, 1) + +[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 +theme_override_constants/separation = -5 +script = SubResource("GDScript_q235s") + +[node name="Total" type="Label" parent="."] +layout_mode = 2 +text = "Total timer" +label_settings = SubResource("LabelSettings_m1tld") + +[node name="Level" type="Label" parent="."] +layout_mode = 2 +text = "Level timer" +label_settings = SubResource("LabelSettings_2a86r") diff --git a/fonts/Knewave/Knewave-Regular.ttf b/fonts/Knewave/Knewave-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a98f750e3805519654e50a6764ad8c3bba8bc44a GIT binary patch literal 31280 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

LoaXAEoBO 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

j*TI-Rxgmz? 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 literal 0 HcmV?d00001 diff --git a/fonts/Knewave/Knewave-Regular.ttf.import b/fonts/Knewave/Knewave-Regular.ttf.import new file mode 100644 index 0000000..b563747 --- /dev/null +++ b/fonts/Knewave/Knewave-Regular.ttf.import @@ -0,0 +1,35 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://c3fsj6knyiuhl" +path="res://.godot/imported/Knewave-Regular.ttf-621ce63e5403426c07f1520fe86b0560.fontdata" + +[deps] + +source_file="res://fonts/Knewave/Knewave-Regular.ttf" +dest_files=["res://.godot/imported/Knewave-Regular.ttf-621ce63e5403426c07f1520fe86b0560.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/fonts/Knewave/OFL.txt b/fonts/Knewave/OFL.txt new file mode 100644 index 0000000..34f48fe --- /dev/null +++ b/fonts/Knewave/OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2010, Tyler Finck , with Reserved Font Name: "Knewave". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/index.tscn b/index.tscn index ae6d27c..324fcda 100644 --- a/index.tscn +++ b/index.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=4 format=3 uid="uid://ccgnnif026wb4"] +[gd_scene load_steps=5 format=3 uid="uid://ccgnnif026wb4"] + +[ext_resource type="PackedScene" uid="uid://xd3nsiglcdfc" path="res://elements/timer.tscn" id="1_356j3"] [sub_resource type="GDScript" id="GDScript_8n212"] script/source = "extends Node var playing: bool = false -var seconds_spent: float = 0.00 var changing_level: bool = false var current_level_int: int = 0 @@ -15,6 +16,8 @@ var levels = [ ] var area_resource = preload(\"res://menus/main/area.tscn\") +@onready var timer := $GUI/Timer + func _ready() -> void: var os_name = OS.get_name() if os_name == \"Web\": ## we can't quit the game on web @@ -58,11 +61,15 @@ func start_level(level_scene: PackedScene) -> void: Vector3.DOWN ) changing_level = false + timer.seconds_spent_level_attempt = 0.0 + timer.enabled = true func stop_level() -> void: + timer.enabled = false PhysicsServer3D.area_set_param(get_viewport().find_world_3d().space, PhysicsServer3D.AREA_PARAM_GRAVITY, 1) playing = false changing_level = true + SaveFiles.change_property(\"played_for\", timer.seconds_spent_total, SaveFiles.selected_file) var current_levels = get_current_levels() for level in current_levels: @@ -95,6 +102,7 @@ func restart_level() -> void: start_level(levels[current_level_int]) func pause_game() -> void: + SaveFiles.change_property(\"played_for\", timer.seconds_spent_total, SaveFiles.selected_file) if $Levels.process_mode == PROCESS_MODE_INHERIT: $Levels.process_mode = Node.PROCESS_MODE_DISABLED playing = false @@ -114,7 +122,6 @@ func fullscreen_game() -> void: DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_WINDOWED) func start_game() -> void: - seconds_spent = 0.0 current_level_int = 0 var current_levels = $Levels.get_children(true) @@ -125,14 +132,7 @@ func start_game() -> void: Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) start_level(levels[0]) -func update_timer(delta: float) -> void: - if playing: - seconds_spent += delta - var minutes = floor(seconds_spent / 60) - $Informations/MarginContainer/Timer.text = (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.2f\" % [5, seconds_spent - (minutes * 60)]) - -func _process(delta: float) -> void: - update_timer(delta) +func _process(_delta: float) -> void: var current_levels = get_current_levels() if len(current_levels): var current_level = current_levels[0] @@ -166,6 +166,18 @@ outline_color = Color(0, 0, 0, 1) [node name="Game" type="Node"] script = SubResource("GDScript_8n212") +[node name="GUI" type="MarginContainer" parent="."] +offset_right = 201.0 +offset_bottom = 104.0 +rotation = 0.0523599 +theme_override_constants/margin_left = 15 +theme_override_constants/margin_top = 15 +theme_override_constants/margin_right = 15 +theme_override_constants/margin_bottom = 15 + +[node name="Timer" parent="GUI" instance=ExtResource("1_356j3")] +layout_mode = 2 + [node name="Informations" type="Control" parent="."] visible = false layout_mode = 3 @@ -186,14 +198,6 @@ grow_vertical = 2 theme_override_constants/margin_left = 10 theme_override_constants/margin_right = 10 -[node name="Timer" type="Label" parent="Informations/MarginContainer"] -layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 8 -text = "00:00.0" -label_settings = SubResource("LabelSettings_0s07t") -vertical_alignment = 2 - [node name="VBoxContainer" type="VBoxContainer" parent="Informations/MarginContainer"] layout_mode = 2 theme_override_constants/separation = -5 diff --git a/menus/main/save_file_manager.tscn b/menus/main/save_file_manager.tscn index 9a86fb7..10688de 100644 --- a/menus/main/save_file_manager.tscn +++ b/menus/main/save_file_manager.tscn @@ -5,33 +5,37 @@ script/source = "extends Control signal request_start -@onready var label = $VBoxContainer/MarginContainer/VBoxContainer/Label +@onready var label_name = $VBoxContainer/MarginContainer/VBoxContainer/Description/Name +@onready var label_time = $VBoxContainer/MarginContainer/VBoxContainer/Description/Time func _on_save_1_pressed() -> void: - label.text = $VBoxContainer/Save1.text + label_name.text = $VBoxContainer/Save1.text display_file_data(SaveFiles.read(SaveFiles.names[0])) func _on_save_2_pressed() -> void: - label.text = $VBoxContainer/Save2.text + label_name.text = $VBoxContainer/Save2.text display_file_data(SaveFiles.read(SaveFiles.names[1])) func _on_save_3_pressed() -> void: - label.text = $VBoxContainer/Save3.text + label_name.text = $VBoxContainer/Save3.text display_file_data(SaveFiles.read(SaveFiles.names[2])) func display_file_data(data: Variant) -> void: $VBoxContainer/MarginContainer.show() - label.text += \" | \" - if data.has(\"played_for\"): - label.text += data.played_for + label_time.text = \" | \" + if data.has(\"played_for\") and data.played_for is float: + var seconds: float = data.played_for + var minutes: int = floor(seconds / 60) + label_time.text += (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) else: - label.text += \"00:00\" + label_time.text += \"00:00\" 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)) " [node name="SaveFileManager" type="Control"] @@ -69,7 +73,6 @@ layout_mode = 2 text = "Save file #3" [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] -visible = false layout_mode = 2 theme_override_constants/margin_left = 30 theme_override_constants/margin_top = 30 @@ -79,27 +82,34 @@ theme_override_constants/margin_bottom = 30 [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer"] layout_mode = 2 -[node name="Label" type="Label" parent="VBoxContainer/MarginContainer/VBoxContainer"] +[node name="Description" type="HBoxContainer" parent="VBoxContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="Name" type="Label" parent="VBoxContainer/MarginContainer/VBoxContainer/Description"] layout_mode = 2 text = "Save File" horizontal_alignment = 1 -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/MarginContainer/VBoxContainer"] +[node name="Time" type="Label" parent="VBoxContainer/MarginContainer/VBoxContainer/Description"] +layout_mode = 2 + +[node name="Buttons" type="HBoxContainer" parent="VBoxContainer/MarginContainer/VBoxContainer"] layout_mode = 2 theme_override_constants/separation = 10 alignment = 1 -[node name="Start" type="Button" parent="VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer"] +[node name="Start" type="Button" parent="VBoxContainer/MarginContainer/VBoxContainer/Buttons"] layout_mode = 2 size_flags_horizontal = 3 text = "Start" -[node name="Delete" type="Button" parent="VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer"] +[node name="Delete" type="Button" parent="VBoxContainer/MarginContainer/VBoxContainer/Buttons"] layout_mode = 2 text = "Delete" [connection signal="pressed" from="VBoxContainer/Save1" to="." method="_on_save_1_pressed"] [connection signal="pressed" from="VBoxContainer/Save2" to="." method="_on_save_2_pressed"] [connection signal="pressed" from="VBoxContainer/Save3" to="." method="_on_save_3_pressed"] -[connection signal="pressed" from="VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer/Start" to="." method="_on_start_pressed"] -[connection signal="pressed" from="VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer/Delete" to="." method="_on_delete_pressed"] +[connection signal="pressed" from="VBoxContainer/MarginContainer/VBoxContainer/Buttons/Start" to="." method="_on_start_pressed"] +[connection signal="pressed" from="VBoxContainer/MarginContainer/VBoxContainer/Buttons/Delete" to="." method="_on_delete_pressed"] diff --git a/save_files.gd b/save_files.gd index 71e00f5..2a5013c 100644 --- a/save_files.gd +++ b/save_files.gd @@ -19,9 +19,7 @@ func ensure_existence(save_file_name: String) -> void: func empty(save_file_name: String) -> void: print("Writing an empty object on ", save_file_name) - var save_file := FileAccess.open(save_file_name, FileAccess.WRITE) - save_file.store_line(JSON.stringify({})) - save_file.store_line("FOR YOUR SAFETY, ALWAYS CHECK IF THE DATA OF THE FILES YOU DOWNLOAD LOOKS OKAY") + write(JSON.stringify({}), save_file_name) func read(save_file_name: String) -> Variant: ensure_existence(save_file_name) @@ -36,3 +34,14 @@ func read(save_file_name: String) -> Variant: return {} return json.data + +func write(json_string: String, save_file_name: String) -> void: + var save_file := FileAccess.open(save_file_name, FileAccess.WRITE) + save_file.store_line(json_string) + 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: + ensure_existence(save_file_name) + var data = read(save_file_name) + data[property] = value + write(JSON.stringify(data), save_file_name) From b81eb08e058aed29da728ac681e9185e9eaa41e2 Mon Sep 17 00:00:00 2001 From: Taevas Date: Mon, 21 Apr 2025 16:06:56 +0200 Subject: [PATCH 13/20] new speed display + save best level times (#3) --- gui/speed.tscn | 34 ++++++++++++++++++ {elements => gui}/timer.tscn | 67 +++++++++++++++++++++++++++++++++++- index.tscn | 59 +++++++++++++++++++++---------- 3 files changed, 140 insertions(+), 20 deletions(-) create mode 100644 gui/speed.tscn rename {elements => gui}/timer.tscn (56%) diff --git a/gui/speed.tscn b/gui/speed.tscn new file mode 100644 index 0000000..710e61d --- /dev/null +++ b/gui/speed.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=4 format=3 uid="uid://cckeamgkt8bqo"] + +[ext_resource type="FontFile" uid="uid://c3fsj6knyiuhl" path="res://fonts/Knewave/Knewave-Regular.ttf" id="1_xaun4"] + +[sub_resource type="LabelSettings" id="LabelSettings_8g5s8"] +font = ExtResource("1_xaun4") +font_size = 32 +font_color = Color(1, 0, 0, 1) +outline_size = 8 +shadow_size = 8 +shadow_color = Color(0, 0, 0, 1) + +[sub_resource type="GDScript" id="GDScript_xaun4"] +script/source = "extends Label + +var ball_velocity := 0.0: + get: return ball_velocity + set(value): + ball_velocity = value + var rounded := int(floor(value)) + self.text = str(rounded) + \" km/h\" + + const min_px = 32.0 + const max_px = 56.0 + self.label_settings.font_size = min(max(rounded / 3, min_px), max_px) + self.label_settings.outline_size = min(max(rounded / 12, min_px / 4), max_px / 4) + self.label_settings.shadow_size = min(max(rounded / 12, min_px / 4), max_px / 4) +" + +[node name="Speed" type="Label"] +text = "0 km/h" +label_settings = SubResource("LabelSettings_8g5s8") +horizontal_alignment = 2 +script = SubResource("GDScript_xaun4") diff --git a/elements/timer.tscn b/gui/timer.tscn similarity index 56% rename from elements/timer.tscn rename to gui/timer.tscn index 3a2ae36..ac9f85d 100644 --- a/elements/timer.tscn +++ b/gui/timer.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=5 format=3 uid="uid://xd3nsiglcdfc"] +[gd_scene load_steps=9 format=3 uid="uid://xd3nsiglcdfc"] [ext_resource type="FontFile" uid="uid://c3fsj6knyiuhl" path="res://fonts/Knewave/Knewave-Regular.ttf" id="1_m1tld"] @@ -15,6 +15,10 @@ var enabled := false: 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 + $AnimationPlayer.play_backwards(\"scale_cubic\") var seconds_spent_total := 0.0: get: return seconds_spent_total @@ -58,6 +62,62 @@ outline_color = Color(0, 0, 1, 1) shadow_size = 8 shadow_color = Color(0, 0, 0, 1) +[sub_resource type="Animation" id="Animation_m1tld"] +length = 0.001 +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), +"transitions": PackedFloat32Array(1), +"update": 0, +"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 +step = 0.25 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:scale") +tracks/0/interp = 2 +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"), +&"scale_cubic": SubResource("Animation_houb3"), +&"scale_linear": SubResource("Animation_2a86r") +} + [node name="Timer" type="VBoxContainer"] process_mode = 3 anchors_preset = 15 @@ -79,3 +139,8 @@ label_settings = SubResource("LabelSettings_m1tld") layout_mode = 2 text = "Level timer" label_settings = SubResource("LabelSettings_2a86r") + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +&"": SubResource("AnimationLibrary_jmgpn") +} diff --git a/index.tscn b/index.tscn index 324fcda..a225fb6 100644 --- a/index.tscn +++ b/index.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=5 format=3 uid="uid://ccgnnif026wb4"] +[gd_scene load_steps=6 format=3 uid="uid://ccgnnif026wb4"] -[ext_resource type="PackedScene" uid="uid://xd3nsiglcdfc" path="res://elements/timer.tscn" id="1_356j3"] +[ext_resource type="PackedScene" uid="uid://xd3nsiglcdfc" path="res://gui/timer.tscn" id="1_356j3"] +[ext_resource type="PackedScene" uid="uid://cckeamgkt8bqo" path="res://gui/speed.tscn" id="2_2gn6w"] [sub_resource type="GDScript" id="GDScript_8n212"] script/source = "extends Node @@ -16,7 +17,8 @@ var levels = [ ] var area_resource = preload(\"res://menus/main/area.tscn\") -@onready var timer := $GUI/Timer +@onready var timer := $GUI/TopLeft/Timer +@onready var speed := $GUI/TopRight/Speed func _ready() -> void: var os_name = OS.get_name() @@ -69,7 +71,12 @@ func stop_level() -> void: PhysicsServer3D.area_set_param(get_viewport().find_world_3d().space, PhysicsServer3D.AREA_PARAM_GRAVITY, 1) playing = false changing_level = true + SaveFiles.change_property(\"played_for\", 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) + if !save_file_data.has(property_name) or save_file_data[property_name] is not float or save_file_data[property_name] > timer.seconds_spent_level_attempt: + SaveFiles.change_property(property_name, timer.seconds_spent_level_attempt, SaveFiles.selected_file) var current_levels = get_current_levels() for level in current_levels: @@ -136,7 +143,7 @@ func _process(_delta: float) -> void: var current_levels = get_current_levels() if len(current_levels): var current_level = current_levels[0] - $Informations/MarginContainer/VBoxContainer/Speed.text = str(int(floor(current_level.velocity))) + \" km/h\" + speed.ball_velocity = current_level.velocity $Informations/MarginContainer/VBoxContainer/Rings.text = str(current_level.finished_rings_count) + \"/\" + str(len(current_level.rings)) func _unhandled_key_input(event: InputEvent) -> void: @@ -166,20 +173,41 @@ outline_color = Color(0, 0, 0, 1) [node name="Game" type="Node"] script = SubResource("GDScript_8n212") -[node name="GUI" type="MarginContainer" parent="."] -offset_right = 201.0 -offset_bottom = 104.0 +[node name="GUI" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 + +[node name="TopLeft" type="MarginContainer" parent="GUI"] +layout_mode = 1 +offset_right = 160.0 +offset_bottom = 85.0 rotation = 0.0523599 theme_override_constants/margin_left = 15 -theme_override_constants/margin_top = 15 -theme_override_constants/margin_right = 15 -theme_override_constants/margin_bottom = 15 -[node name="Timer" parent="GUI" instance=ExtResource("1_356j3")] +[node name="Timer" parent="GUI/TopLeft" instance=ExtResource("1_356j3")] +layout_mode = 2 + +[node name="TopRight" type="MarginContainer" parent="GUI"] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -106.0 +offset_bottom = 51.0 +grow_horizontal = 0 +rotation = -0.0523599 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 15 + +[node name="Speed" parent="GUI/TopRight" instance=ExtResource("2_2gn6w")] layout_mode = 2 [node name="Informations" type="Control" parent="."] -visible = false layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -210,13 +238,6 @@ label_settings = SubResource("LabelSettings_0s07t") horizontal_alignment = 2 vertical_alignment = 2 -[node name="Speed" type="Label" parent="Informations/MarginContainer/VBoxContainer"] -layout_mode = 2 -text = "0 km/h" -label_settings = SubResource("LabelSettings_0s07t") -horizontal_alignment = 2 -vertical_alignment = 2 - [node name="VictoryScreen" type="Control" parent="."] layout_mode = 3 anchors_preset = 15 From 9890e0a6253ff4c3dbb767509708029a07042b79 Mon Sep 17 00:00:00 2001 From: Taevas Date: Wed, 23 Apr 2025 15:37:03 +0200 Subject: [PATCH 14/20] new ring counter, move some logic away from main scene --- gui/rings.tscn | 33 +++++++ gui/speed.tscn | 4 +- index.tscn | 213 +++++++++++++++--------------------------- menus/pause_menu.tscn | 78 ++++++++++++++++ project.godot | 2 +- 5 files changed, 187 insertions(+), 143 deletions(-) create mode 100644 gui/rings.tscn create mode 100644 menus/pause_menu.tscn diff --git a/gui/rings.tscn b/gui/rings.tscn new file mode 100644 index 0000000..e580c7e --- /dev/null +++ b/gui/rings.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=4 format=3 uid="uid://cn55m5dqo3m6u"] + +[ext_resource type="FontFile" uid="uid://c3fsj6knyiuhl" path="res://fonts/Knewave/Knewave-Regular.ttf" id="1_nja12"] + +[sub_resource type="LabelSettings" id="LabelSettings_cnn6y"] +font = ExtResource("1_nja12") +font_size = 32 +outline_size = 8 +outline_color = Color(0, 0, 0, 1) +shadow_size = 8 +shadow_color = Color(0, 0, 0, 1) + +[sub_resource type="GDScript" id="GDScript_nja12"] +script/source = "extends Label + +var remaining_rings := 0: + get: return remaining_rings + set(value): + remaining_rings = value + if value >= 1: + var rings_word := \"rings\" if value != 1 else \"ring\" + self.text = str(value) + \" \" + rings_word + \" to go!\" + else: + self.text = \"You did it!!\" +" + +[node name="Rings" type="Label"] +offset_right = 109.0 +offset_bottom = 23.0 +text = "69 rings to go!" +label_settings = SubResource("LabelSettings_cnn6y") +horizontal_alignment = 2 +script = SubResource("GDScript_nja12") diff --git a/gui/speed.tscn b/gui/speed.tscn index 710e61d..92fadba 100644 --- a/gui/speed.tscn +++ b/gui/speed.tscn @@ -17,8 +17,8 @@ var ball_velocity := 0.0: get: return ball_velocity set(value): ball_velocity = value - var rounded := int(floor(value)) - self.text = str(rounded) + \" km/h\" + var rounded: float = floor(value) + self.text = str(int(rounded)) + \" km/h\" const min_px = 32.0 const max_px = 56.0 diff --git a/index.tscn b/index.tscn index a225fb6..cfef63e 100644 --- a/index.tscn +++ b/index.tscn @@ -1,7 +1,9 @@ -[gd_scene load_steps=6 format=3 uid="uid://ccgnnif026wb4"] +[gd_scene load_steps=7 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://cckeamgkt8bqo" path="res://gui/speed.tscn" id="2_2gn6w"] +[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 @@ -17,21 +19,27 @@ var levels = [ ] var area_resource = preload(\"res://menus/main/area.tscn\") -@onready var timer := $GUI/TopLeft/Timer -@onready var speed := $GUI/TopRight/Speed +@onready var gui_timer := $GUI/TopLeft/Timer +@onready var gui_speed := $GUI/TopRight/Speed +@onready var gui_rings := $GUI/BottomRight/Rings +@onready var pause_menu := $PauseMenu func _ready() -> void: - var os_name = OS.get_name() - if os_name == \"Web\": ## we can't quit the game on web - $PauseMenu/VBoxContainer/Btn_Quit.hide() - - $VictoryScreen.hide() - $Informations.hide() - $PauseMenu.hide() ## Add the version of the game and the name of the OS to the footer of the start menu if OS.has_feature(\"editor\"): $DevInfos.text = \"dev \" - $DevInfos.text += \"build \" + ProjectSettings.get_setting(\"application/config/version\") + \" (\" + os_name + \")\" + $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 + pause_menu.connect(\"request_pause\", pause_game) + pause_menu.connect(\"request_fullscreen\", fullscreen_game) + pause_menu.connect(\"request_restart\", restart_level) + launch_area() func launch_area(): @@ -56,6 +64,10 @@ func start_level(level_scene: PackedScene) -> void: 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 + ) + gui_rings.remaining_rings = len(level.rings) - level.finished_rings_count playing = true PhysicsServer3D.area_set_param( get_viewport().find_world_3d().space, @@ -63,20 +75,20 @@ func start_level(level_scene: PackedScene) -> void: Vector3.DOWN ) changing_level = false - timer.seconds_spent_level_attempt = 0.0 - timer.enabled = true + gui_timer.seconds_spent_level_attempt = 0.0 + gui_timer.enabled = true func stop_level() -> void: - timer.enabled = false + gui_timer.enabled = false PhysicsServer3D.area_set_param(get_viewport().find_world_3d().space, PhysicsServer3D.AREA_PARAM_GRAVITY, 1) playing = false changing_level = true - SaveFiles.change_property(\"played_for\", timer.seconds_spent_total, SaveFiles.selected_file) + 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) - if !save_file_data.has(property_name) or save_file_data[property_name] is not float or save_file_data[property_name] > timer.seconds_spent_level_attempt: - SaveFiles.change_property(property_name, timer.seconds_spent_level_attempt, SaveFiles.selected_file) + 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: @@ -97,28 +109,40 @@ func win_game() -> void: $VictoryScreen.hide() func restart_level() -> void: - if changing_level == false: - var current_levels = get_current_levels() - if len(current_levels): - pause_game() - changing_level = true - - for level in current_levels: - level.queue_free() - - start_level(levels[current_level_int]) + 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 pause_game() -> void: - SaveFiles.change_property(\"played_for\", timer.seconds_spent_total, SaveFiles.selected_file) - if $Levels.process_mode == PROCESS_MODE_INHERIT: +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]) + +func pause_game(to_pause: bool) -> void: + if !len(get_current_levels()) 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 playing = false - $PauseMenu.show() + pause_menu.show() Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) else: $Levels.process_mode = Node.PROCESS_MODE_INHERIT playing = true - $PauseMenu.hide() + pause_menu.hide() Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) func fullscreen_game() -> void: @@ -128,43 +152,20 @@ func fullscreen_game() -> void: else: DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_WINDOWED) -func start_game() -> void: - current_level_int = 0 - - var current_levels = $Levels.get_children(true) - for level in current_levels: - level.queue_free() - - $Informations.show() - Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) - start_level(levels[0]) - func _process(_delta: float) -> void: var current_levels = get_current_levels() if len(current_levels): var current_level = current_levels[0] - speed.ball_velocity = current_level.velocity - $Informations/MarginContainer/VBoxContainer/Rings.text = str(current_level.finished_rings_count) + \"/\" + str(len(current_level.rings)) + gui_speed.ball_velocity = current_level.velocity -func _unhandled_key_input(event: InputEvent) -> void: - if event.is_pressed() and event is InputEventKey: - var current_levels := get_current_levels() - if len(current_levels) and changing_level == false: - if Input.is_action_just_pressed(\"pause_game\"): - pause_game() - elif Input.is_action_just_pressed(\"restart_level\"): - pause_game() - restart_level() +func _input(_event: InputEvent) -> void: + if Input.is_action_just_pressed(\"restart_level\"): + restart_level() func _on_btn_quit_pressed() -> void: get_tree().quit() " -[sub_resource type="LabelSettings" id="LabelSettings_0s07t"] -font_size = 32 -outline_size = 10 -outline_color = Color(0, 0, 0, 1) - [sub_resource type="LabelSettings" id="LabelSettings_1bs00"] font_size = 160 outline_size = 20 @@ -207,36 +208,23 @@ theme_override_constants/margin_right = 15 [node name="Speed" parent="GUI/TopRight" instance=ExtResource("2_2gn6w")] layout_mode = 2 -[node name="Informations" type="Control" parent="."] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 1 - -[node name="MarginContainer" type="MarginContainer" parent="Informations"] +[node name="BottomRight" type="MarginContainer" parent="GUI"] layout_mode = 1 -anchors_preset = 15 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_constants/margin_left = 10 -theme_override_constants/margin_right = 10 +offset_left = -222.0 +offset_top = -51.0 +grow_horizontal = 0 +grow_vertical = 0 +rotation = 0.0523599 +theme_override_constants/margin_right = 15 +theme_override_constants/margin_bottom = 15 -[node name="VBoxContainer" type="VBoxContainer" parent="Informations/MarginContainer"] +[node name="Rings" parent="GUI/BottomRight" instance=ExtResource("3_mbj17")] layout_mode = 2 -theme_override_constants/separation = -5 -alignment = 2 - -[node name="Rings" type="Label" parent="Informations/MarginContainer/VBoxContainer"] -layout_mode = 2 -text = "0/0" -label_settings = SubResource("LabelSettings_0s07t") -horizontal_alignment = 2 -vertical_alignment = 2 [node name="VictoryScreen" type="Control" parent="."] layout_mode = 3 @@ -260,57 +248,7 @@ vertical_alignment = 1 [node name="Levels" type="Node" parent="."] -[node name="PauseMenu" type="Control" parent="."] -visible = false -z_index = 2 -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 1 - -[node name="ColorRect" type="ColorRect" parent="PauseMenu"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -color = Color(1, 0.54902, 1, 0.109804) - -[node name="VBoxContainer" type="VBoxContainer" parent="PauseMenu"] -layout_mode = 1 -anchors_preset = 14 -anchor_top = 0.5 -anchor_right = 1.0 -anchor_bottom = 0.5 -offset_top = -50.5 -offset_bottom = 50.5 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_constants/separation = 10 - -[node name="Btn_Resume" type="Button" parent="PauseMenu/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 6 -text = "RESUME game" - -[node name="Btn_Fullscreen2" type="Button" parent="PauseMenu/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 6 -text = "Toggle FULLSCREEN" - -[node name="Btn_Restart" type="Button" parent="PauseMenu/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 6 -text = "RESTART the level" - -[node name="Btn_Quit" type="Button" parent="PauseMenu/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 6 -text = "QUIT game" +[node name="PauseMenu" parent="." instance=ExtResource("4_3bfj3")] [node name="DevInfos" type="Label" parent="."] anchors_preset = 12 @@ -321,8 +259,3 @@ offset_top = -23.0 grow_horizontal = 2 grow_vertical = 0 horizontal_alignment = 1 - -[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Resume" to="." method="pause_game"] -[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Fullscreen2" to="." method="fullscreen_game"] -[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Restart" to="." method="restart_level"] -[connection signal="pressed" from="PauseMenu/VBoxContainer/Btn_Quit" to="." method="_on_btn_quit_pressed"] diff --git a/menus/pause_menu.tscn b/menus/pause_menu.tscn new file mode 100644 index 0000000..27f7e68 --- /dev/null +++ b/menus/pause_menu.tscn @@ -0,0 +1,78 @@ +[gd_scene load_steps=2 format=3 uid="uid://dkxtwpcy4moyo"] + +[sub_resource type="GDScript" id="GDScript_d8v8v"] +script/source = "extends Control + +signal request_pause +signal request_fullscreen +signal request_restart + +func _ready() -> void: + var os_name = OS.get_name() + if os_name == \"Web\": ## you can't quit the game on web (you're stuck here forever :)) + $VBoxContainer/Btn_Quit.hide() + +func _input(_event: InputEvent) -> void: + if Input.is_action_just_pressed(\"pause_game\"): + request_pause.emit(!self.visible) + +func _on_btn_resume_pressed() -> void: + request_pause.emit(false) + +func _on_btn_fullscreen_pressed() -> void: + request_fullscreen.emit() + +func _on_btn_restart_pressed() -> void: + request_restart.emit() + +func _on_btn_quit_pressed() -> void: + get_tree().quit() +" + +[node name="PauseMenu" type="Control"] +z_index = 2 +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +script = SubResource("GDScript_d8v8v") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 14 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_top = -50.5 +offset_bottom = 50.5 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/separation = 10 + +[node name="Btn_Resume" type="Button" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 6 +text = "RESUME game" + +[node name="Btn_Fullscreen" type="Button" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 6 +text = "Toggle FULLSCREEN" + +[node name="Btn_Restart" type="Button" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 6 +text = "RESTART the level" + +[node name="Btn_Quit" type="Button" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 6 +text = "QUIT game" + +[connection signal="pressed" from="VBoxContainer/Btn_Resume" to="." method="_on_btn_resume_pressed"] +[connection signal="pressed" from="VBoxContainer/Btn_Fullscreen" to="." method="_on_btn_fullscreen_pressed"] +[connection signal="pressed" from="VBoxContainer/Btn_Restart" to="." method="_on_btn_restart_pressed"] +[connection signal="pressed" from="VBoxContainer/Btn_Quit" to="." method="_on_btn_quit_pressed"] diff --git a/project.godot b/project.godot index 1e0076e..7bbf422 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="20250330.0" +config/version="20250423.0" run/main_scene="res://index.tscn" config/features=PackedStringArray("4.4", "GL Compatibility") boot_splash/show_image=false From 59bed24acd44c5c6e1278a025b0e87f9ffa1f104 Mon Sep 17 00:00:00 2001 From: Taevas Date: Thu, 24 Apr 2025 14:54:54 +0200 Subject: [PATCH 15/20] simple level selection menu, change level logic (#3) --- elements/player.tscn | 23 ++-- elements/tree.tscn | 1 + gui/timer.tscn | 34 +++--- index.tscn | 102 ++++++------------ levels/base/level.tscn | 16 ++- levels/forest/level.tscn | 16 ++- levels/level.gd | 9 +- levels/music.gd | 4 +- levels/night/level.tscn | 22 +++- .../{start_menu.tscn => initial_menu.tscn} | 2 +- menus/main/level_selection_menu.tscn | 42 ++++++++ menus/main/{area.tscn => main_menu.tscn} | 60 ++++++----- menus/menu.tscn | 2 + 13 files changed, 199 insertions(+), 134 deletions(-) rename menus/main/{start_menu.tscn => initial_menu.tscn} (97%) create mode 100644 menus/main/level_selection_menu.tscn rename menus/main/{area.tscn => main_menu.tscn} (69%) diff --git a/elements/player.tscn b/elements/player.tscn index 54b3132..c377c79 100644 --- a/elements/player.tscn +++ b/elements/player.tscn @@ -4,7 +4,13 @@ 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 func _physics_process(_delta: float) -> void: $SpotLightMain.position = $Sphere.position + Vector3(0, 5, 0) @@ -13,22 +19,21 @@ func _physics_process(_delta: float) -> void: $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/gui/timer.tscn b/gui/timer.tscn index ac9f85d..059370f 100644 --- a/gui/timer.tscn +++ b/gui/timer.tscn @@ -77,23 +77,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 +94,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"), diff --git a/index.tscn b/index.tscn index cfef63e..eff4230 100644 --- a/index.tscn +++ b/index.tscn @@ -11,13 +11,10 @@ script/source = "extends Node var playing: bool = false var changing_level: bool = 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 + +const res_main_menu = preload(\"res://menus/main/main_menu.tscn\") @onready var gui_timer := $GUI/TopLeft/Timer @onready var gui_speed := $GUI/TopRight/Speed @@ -40,34 +37,31 @@ 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(): Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) - var area := area_resource.instantiate() - area.connect(\"request_start\", start_game) - $Levels.add_child(area) - -## 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 + main_menu = res_main_menu.instantiate() + main_menu.connect(\"request_play_level\", start_level) + add_child(main_menu) 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 + if is_instance_valid(current_level): + current_level.queue_free() + if is_instance_valid(main_menu): + main_menu.queue_free() + + current_level = level_scene.instantiate() + add_child(current_level) + 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) + playing = true PhysicsServer3D.area_set_param( get_viewport().find_world_3d().space, @@ -78,7 +72,7 @@ func start_level(level_scene: PackedScene) -> void: 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) playing = false @@ -86,51 +80,27 @@ func stop_level() -> void: 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() + current_level.music.fadeOut(2) + await get_tree().create_timer(2).timeout + current_level.queue_free() - if len(levels) > current_level_int + 1: - current_level_int += 1 - start_level(levels[current_level_int]) - else: - win_game() + win_game() func win_game() -> void: $VictoryScreen.show() await get_tree().create_timer(2).timeout - launch_area() + set_main_menu() $VictoryScreen.hide() 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]) + return #idk yet 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) @@ -153,9 +123,7 @@ 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 func _input(_event: InputEvent) -> void: @@ -246,8 +214,6 @@ 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="."] diff --git a/levels/base/level.tscn b/levels/base/level.tscn index 0683839..03d1a09 100644 --- a/levels/base/level.tscn +++ b/levels/base/level.tscn @@ -1,12 +1,22 @@ [gd_scene load_steps=12 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,7 +40,7 @@ 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")] @@ -155,6 +165,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/forest/level.tscn b/levels/forest/level.tscn index 36176ba..e12a221 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -1,6 +1,5 @@ [gd_scene load_steps=11 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"] @@ -8,6 +7,17 @@ [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 + +func _init() -> void: + self.id = \"forest\" + +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,7 +39,7 @@ 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")] @@ -261,4 +271,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/level.gd b/levels/level.gd index 77f9795..2d386b7 100644 --- a/levels/level.gd +++ b/levels/level.gd @@ -4,13 +4,15 @@ extends Node3D signal ring_collected signal completed -var seconds_spent: float = 0.00 +## Lowercase, spaceless name of the level +var id: String var music: Music var velocity: float = 0.0 var rings: Array[Ring] = [] var finished_rings_count: int = 0 func _ready() -> void: + assert(len(id) > 0, self.name + " has no id!") var children = get_children() for child in children: if is_instance_of(child, Music): @@ -31,8 +33,3 @@ 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) 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..76c77a7 100644 --- a/levels/night/level.tscn +++ b/levels/night/level.tscn @@ -1,6 +1,5 @@ [gd_scene load_steps=14 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"] @@ -9,6 +8,21 @@ [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,7 +42,7 @@ 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")] @@ -193,7 +207,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 +341,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/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..45b6a11 --- /dev/null +++ b/menus/main/level_selection_menu.tscn @@ -0,0 +1,42 @@ +[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 + +const levels = [ + preload(\"res://levels/base/level.tscn\"), + preload(\"res://levels/forest/level.tscn\"), + preload(\"res://levels/night/level.tscn\"), +] +var selected_level := -1 + +func _ready() -> void: + for i in len(levels): + var btn := Button.new() + btn.text = \"Level \" + str(i + 1) + btn.connect(\"pressed\", func(): request_play_level.emit(levels[i])) + $VBoxContainer/Carousel.add_child(btn) +" + +[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="Carousel" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 1 diff --git a/menus/main/area.tscn b/menus/main/main_menu.tscn similarity index 69% rename from menus/main/area.tscn rename to menus/main/main_menu.tscn index 0759372..87a94da 100644 --- a/menus/main/area.tscn +++ b/menus/main/main_menu.tscn @@ -1,38 +1,48 @@ [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"] +[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_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\") +signal request_play_level +# 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 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) + 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 sm = settings_menu.instantiate() - $Menus/Panel2.change_menu(sm) + 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: PackedScene): request_play_level.emit(level)) + $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 @@ -98,23 +108,23 @@ ambient_light_source = 3 ambient_light_color = Color(0.986752, 0.986752, 0.986752, 1) reflected_light_source = 2 -[node name="Area" type="Node3D"] +[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_qfa5o")] +[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_qfa5o")] +[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_qfa5o")] +[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_qfa5o")] +[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"] @@ -133,7 +143,7 @@ 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")] +[node name="Player" parent="." instance=ExtResource("2_2rexg")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.87335, 0) [node name="Camera" parent="Player" index="0"] 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"] From 71441a933588796dd2eeed217f14468a7b1d4a86 Mon Sep 17 00:00:00 2001 From: Taevas Date: Fri, 25 Apr 2025 16:03:36 +0200 Subject: [PATCH 16/20] show level name, best time, and thumbnail (#3) now thumbnails need to be made --- index.tscn | 5 +- levels/forest/level.tscn | 10 +++ levels/forest/thumbnail.png | Bin 0 -> 199336 bytes levels/forest/thumbnail.png.import | 34 +++++++++ levels/level.gd | 7 +- menus/main/level_selection_menu.tscn | 107 +++++++++++++++++++++++++-- menus/main/main_menu.tscn | 2 +- 7 files changed, 153 insertions(+), 12 deletions(-) create mode 100644 levels/forest/thumbnail.png create mode 100644 levels/forest/thumbnail.png.import diff --git a/index.tscn b/index.tscn index eff4230..75882ae 100644 --- a/index.tscn +++ b/index.tscn @@ -45,14 +45,15 @@ func set_main_menu(): main_menu.connect(\"request_play_level\", start_level) add_child(main_menu) -func start_level(level_scene: PackedScene) -> void: +func start_level(level: Level) -> void: if is_instance_valid(current_level): current_level.queue_free() if is_instance_valid(main_menu): main_menu.queue_free() - current_level = level_scene.instantiate() + current_level = level add_child(current_level) + 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 diff --git a/levels/forest/level.tscn b/levels/forest/level.tscn index e12a221..e9763f3 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -10,8 +10,18 @@ [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 diff --git a/levels/forest/thumbnail.png b/levels/forest/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..07c40d6f4840b1e95791c126f8df96ecfa8e58ab GIT binary patch literal 199336 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

sS2d>*qcn>Sy;4Ys>WY*yZpozJ)sQ z3FNuqmEDo2YW?&zvPGU?@Q2eY-_5-5^OoiT z2hc)YF%bm^sYQMG3)5{0)P?rRN9^;GsV~z0Z%Vn!2i!&D^h%%w8eMQH)zlJUF@$BW`fTL0bI=l<8p$(eh6-AP;uC6>B{Wz(^qRVvPV7N-wxEHBh>0u(N&n4Hr;g`C$ z-yh&HSjGCv}RWJrfhsx5_F$X)i0-NL)6*f zP&;v?F;R8sms{^g?xJXO7!Nr;dI<6)FWofj1IpO-J8(?*lH7$q;e)w78&fE<@#g2! zaSbgA*Y!hJ7g1DBV>SRmM*@9m%WK?^J$l!?*Y12mI_mjf$Z0_D)N2ulR()xOyYoH8^4@sCqM zy^E-IKKX%6?0Zf>_)-E~^G;ZHXngyXis*40S!QIdI6FOp1*jtYSasL}1 zm--#Cb@!)8I*--$n=x9+57Ky9hihi~Llc~)paqZ3F+;)>Dn-y2_8H*N8!us3B5|Fm z_Z4y^Xj?K*7uqp6YjIR6w+8?yo-LvXzlrueMrNiSl0J)S=E)w$AE3(H%hk6E0C?=M zWC+}MG6u33qkR5LTYQDh!TSMX?~nF*qfJKpk;Xze=f#kAwS$AvtD{gtBWfN4xPI6* zAnC9qp7XFnOLRq#UAuF;%hvGXdvyNi9>Z@ZD%qmE?VNplNTlJk4pY?;Vhk**;X#3#p~us=4lZ-!NN4Moy$lNGE=r(Vx%k`m4AxL=(1%qTlujB?8mNQY54 zog3SX+lHr+!@|VzxL}Mg)>I%jP!@}{Rs}Ifn3mpO^fTBurbW&%6H=mZPNBBg3aCL*tb^y!rCHOm~wh_y?lH5_Q@$G-8XI}O~fzXWprI>jB3cVj+Q%E zk<2Ner1WV;>0FJ!@>zw^0G=8N(-_Ul?v{Uk=(n>xgOj&_i+wGy2NX8X+ch3}xd^l;EMq-aP*|MytM zP(2x-Z^x{({`Ati>Br296i_2+<}`y|v`q2Obf(2pT;80hUByu40{PX-;#8kxrlQA_ zrCsjRcu(UAZ1?)xP|0vaW$e@$@??}Cu--`I4=Z1Oxm0s+!`CN4%^Oq$EQ`(h%I1oy z6;4X4oiAS=zHV@AbwzG9Ku)W&Vr&-NC=1jx+6!+#+l_8EXH1N;L?iC5va*KV`rGSLO@m*Wv~Af)77pLrN#a06IZy`;uO%3ZWo z&!UN>{_Y{nZ#S=*tXe_hsibwdkGSUE3@JD4TbA!&fz$m^i}QfaiKv3pvf{0>#)mBD zt7{g}!^5#v7&Y`LhU#VvD@(p~u#3?@APzfl#J!}8!S(j40Pkgh~WRGkU;0W7PG z9lKY6?E=_k{$y(oGOkL0h!rQQiPh@OIceO^YQGwzF~s0??I#M@NG?=fWbu~iz0=F4>z9|yjZ@$M!m)+nZk-2)I>F57% zXQz+ag|pdku^=OT%5{h2Ufu_=^r6Zz*8>)dSbgmWW;R1a&R^B$4=XLmi_-3&?0--R z^We<~sp`OZcY;KEsf^3kBW$76HD^q7u_Nlqb-q=X z|4ffhz+o7;TsIC^(6ACw*>bXrLcP{_##E&>($@>?>&Rkw#UrgYw~ zj&c&!B;QeWCrUF+z89~}_V~OQVi8WG>M=8*ZI-eOe!4GKl^0jtuyZcP2MM&1tUh*9 z>ymDAtBOoU^dIK_KNf(M>~lRP8dhMbfOOAByklKv{@4$*^LjBNt#~Xw^8*h>ew`uA zO!-lw0I_XBce7$g=r~tHm1{4wdIQ#X-tv&8RB4zPIuEabgb(f^F2(~LZ?>@LZAb41 znXs*UH_2JB2RBt%S!VgGmSHk+qLHi%`_%z^%D#6{l2(GxsWtvKt9gzdSh(7+2LJ(A zZ4J8OrHDeRpmpOV&3DpPpGZpou+T179`Vfnqh+b5cpr+>wDy0=4-}n@28pAk;+{=< z(ez%brAlUs*~H+!qBExa?Pa{OlTCPjc&MjqsPPCV=y}ife3&P4&r|m+bAR{!xzT#O zobK~AK!A0@wyLr)-I>ls%B@yr9`K2LwKm4^ddlZevUeIGX!dhQY8||mAGhG~x!%R^ z@9z5@j~i5Vx^~#FZ{!CxWUVt{9NlaE$+(x`tCzQB8Y7W?kacg{$^3S4fS-k(YXKLW zNN$F}*NDITY-B9kjwPlrG7TTUIP7pGyS>tgE>m!Vz=VcTQ)o&aVZh&!S#Y<4Hw2DcM(xZGeQxO5~I)RjG%&m4? z_d!?B+s`)jBpsFof|1AQuFmIxp02-+cFX#m-y!NtL;HzuR^7VjX>ja}*I|$_@R14G z`8Iq0tNx0?7E+~uh;yc_?lJ57Z=sk2{pL%$N7)Rfl5-4>jx}Q!>|0hNd_9{5f~ED? z9j4oo4$tzXgyD)aqX_%+UeK#y87kPO*Kn7({Fx+V!gImjjI8R3Af8tFuN1xbMj9@M z#>ch(AieRPz1qt%#lRKTJ=>u=F}n%y$_(7O)o_8^11_07(a+&Q^mRZE9bTQreA?7y*{JA&>lF080UqqQxKk*ZtX(WUmc z!xyMYqvxe0kQE%r88^z^d=Mhoio>~H3+3)-u5N#kZEq|qf7~(Ww~spTM4nvDvt*Nm zL+&rJw3Y{mFI?c@Jz$9JxWjodVZ#RoA);ffWdZ#~rUe0)rCS>f?1D@9LrD$N6AQxv z51VWOBqSDYzKkoCd$#o%W~$3*0$Dd8R7f05(i%=-rHIqikV7e)&u+v;>s0CORr1_$=m0!$28DUPY0SSLpEUDazgE<(UKl6KW_tk^WH!AVsCJAf}leK zz9!U>;b_5cCz8ol$g59rI&^F;zU#Ljup{gGz(ZTM{Dt$)NRm*qR^lUD#4mE#_TZBWGu8hYTB}YAkV#)hV_+}EsjNgN~DjrCs$f3wi(oGb*jo*3*za6Ks{U@*QU!yWm!b*J(%=?hml)Z4A zbY$R8J?N>k1!M8pC2_=h}vi=pY7u+G09nX+y!+0cQ#_icI z437xg-7(vH$kSKFp0Z znr8d?9JYGWR))V+SxLyc3MT4U*j&w!TbNR~{SS8r5smmBIAZ!-PU!Q`tRlu@Gspmg zGW44o*lFfsvm4(_lP9Naj}9GZ4OaO?`;9Ozch#tYDZ+8(l28ypUW@%_LZ9!k{2N09L*=C z4DR#M6d4^Q3}_m&a3}vZ9oat9RAbf8TNhCsS0lV^HKx+q77*36Et=wW_68z2{>EPP z+M3Cr`L>f0E^;C8ryS^_I~+gT#MY%ajgA`7A`GHmEVhG00RM5|4CFUCe{NRT-2bxY zXhlu(t79?)q2BOm83yG7=n#zP2xj|h~Oe38Td9T@eDo+Zwn!lu4 zF?B0Xt|GGFiPE2!`%AX&Cd=33V(d*OcI4!luZqQLNw?Hqlam7K6N$i=D+bNWA-CWh zybgY1sBbEUk6^g7MF15ldjwDD;xCZ|Eqse3Az+|wi!Yw098WDY*`BtCXF*`45r?pj zJAhcgXIBY*av(RLx>xEs?Dv>ha3QabapJ*RjXD=w4oP{vC-bATweZiHM4Xw^_ZJQHAbW&E{XGKet+occ~$e6Q+AS{Lx9>nhw(HL zd+K?a@Pi2E{~0<@u6R8`vyy?B!TYvqtTwBY)4sVgX-koW@bTN?023qvJ{t*2V&`92&33O+HB^|U zJgJ_|q~j_APvjo}q+ix(d*-^!?buv@{46n9(wk`Z#J^$p!4a+Cb$RB|Xu!EqoTP9; zwE52u%K(K=DTP&;Knn8R9alXo8IQB9FyeKxJmANzOWP7j^IhcSUtFti_W2ibB~*#b zpk%arSVwi$fs?lHvoe}YE2V4V=kd4M{sMR zu4k!s3PJQ&F8>}@a+!9zvQtC${1II7XFpiebI1Kfgr~XGmL!@$8I179awsc>Oc_?i zgK)Hj#HN3AvgER@`*k?^s?IzyX&gjYb25ebQjeCp4vEouVdY;MiosYIH+bLIi;Hu< z-F>3?z}wzC$hvf}j4zyz@U1xY;_7%i^8#LH`2B3E01Trm`-Of+T}*^pSEfBm25;-V zh-ZIH)>jGyPu++hEKCAUrkC_mkmY6mUuD4KlBZxR*4RPh2kSE1X1`AAeeVt7W){;@-Ky}dzSqVld$ z&+nSxQlz0aG-vrAFy}*;?o=4CfPIjn_Y2XEYw+}>mHSSj8J~zl!O&5pr!*&PmSs_< zx!f1>PEl8o%vUS{yEoY3(O-1gx+JxKMRT^eAFHEJN*wa$@hDMJIQ0Yb*=e!eJGA!E zD$4fK53ypxq^~z#JfyrkP4>dsjw!)y+X9%WtQNIlk})$*P&Eo-*{w@E!6&so)>^!zNxMYODZr`TMEnOGW{dCxat zX6!KNLIEdDdb2BiQ;v3X&9?BL3$`EPqEwZ*r+crL=zdu^)&8qdGT4V)UCEA&1d1HB zZ@-b~QPLKey0ya7@b|F%;53lm7P?+w4nY+b+#l2Y-?^=6uSkfV`aSc*#t+XZ0oI&- zrq694iNf6l^Ii=y5y<|%We5Ssd{O6Etv7&m9at9~z=}S9;5vubu)i`|r0O(UtS!6` zDpYuCzqNbPP0=FoUhY6pu2p3TXGgCzC}GI_ANTvVd*X64YweG&esf*<2O`jN_iu#OJ!i1pPOmu*bAiW&Qkj)9HA@Yn zGvYydPY}=HD3S{s-*D|KUw%FsZ|*q;av_70&mI)%BeqXz=byE~2IPjuPQraN#wtr% z#*h7H5uIPX;x)KkA9j>f?OUWrBZ%yor;3D~Bi|=O z0$fIzQT+2gaczzXO(OBr^5uGQB2L{Pu(O zc5Inm>x;)|wEf;mtV;^3B=iX)9{R#l25s)ht9L9JNk`~+;EF+n~Gh1 zWE)}9cwne&0JS1B1r*-rHOR`m3OR*bSzvK{${eUHllpKvIU3kyCEzvyXvk=na3!iL*ZRp%IMk*hCF2@1$1U;sz_P8Cw5H%%F=Xs1jLkI+}7NaLz|+0Q~mS zc~EFaOjudn>s)nex3}tc-{1U)?s~GPu2cG%MYkys9ZQX2zHYk--M%LEmwvuRVFR}$ z8!40cE9tvlr-xo9G|JAlKblkiz21e`+4tyaGa@!(kP4!RbYrn2Mp?jCo@J{iphFI_>gk)WO_W_4}x;0slYK0EDtm zcH)2Blu3lfhA2;&a|t;oG~><6AzV2e>|y52>?t>!|mr3=K; z$j482Nz|0kNKg3eC&1iMJl0Pw2ijJ)+^Jf|D!(x^jE&Y?CfC^C7wpivzIwa<->^Px z^d$L^7&g7+%xC)S#k9X(&rdI_#x=vOtP@J93&Z6HlA46*`1;j_QT$q#*Zo>g&yh2# zM&Bu+I8-ombg*SFZ;zuaU>Ds9C@ zG^HRvX%Cm*yYH7TFXf59>PjEKXZnLdewr_^4% zDyZY-i*T6`hYh)U53clhGyJdRwIzJ24y5?)fU>E|(^fh>bhDSnpG^&1HtlcZ*hH!< z7W?^nL~3$Ux1zB&GAZePNr}2}#;x|YMm?-cbuW;Yim9$4a%KBw-PLeZoLY*PPfv^J z#WP)GnkT(KmZzL|L?F)^za?Nwx*k=COuX>}p>Fcgd^Dyt7nCtaLGN9$%Bfy4&2KQV z{zL3Z5)I$=HXZBBz+I|uA*s!Qj|A(irYDb|dGhRJB@Y3o_U*PNetN$sns~6i4Us>; z2FHYPdIe7$PCoGPsJ*yr(|z{7n*-gcF?eVJgatDdKc02%=i}LZoQ~hp|$Ek?C-Al&wy1OoYb)MZ$>@e45=^zc$ z0{9CP2g@XO5V-#=z4R&Rd~mam!(eB`?4u>s|BRJUuTAI&qKyO8`8B3(8f18J?9I7@ zsKo29Mg?mgtO+FVzg4uqF?RSTj~$#WsD#x67YlxqsEMRyyxuEU6f|6AkbuiNjh;MG zpDyTj&QP%do;F(NKWx;HG0x*6@eMuZfUUl`Ju$hl8l|0n+=(TGm?P+$Yy~@qtZyH? zraf9@!1pFH1~}QO^df=w{{6rHLr-T*KJVl)YUsR-R!_Ih$(Va1Nkgi>F5Jyzx2+9J zc${@q-x73>msc6#!-~0I9eT=w?6-4@QAdY3o4zV&NpP3)nDWmVoNKCnPFR5BKhoNP zrX?B!k|yrECD1Os;WDxD-1&GKKYf(q6P|Ceho9D??2}CEbDkhu4&c0^Uk{&Pv?fnf zU|b?`*IMxz=J{6dx5!4rpTEO(>Vg-_l)K-&5?in=s=$gW) zPv&O5-ylXuz*ElG`FO0>?gS=h&bL-+06VnffbAeo*+AIUE~=mN#?>dnY2r!uec;rJ zIJ$}?U1U$MJ0PQY;|r3KJXZV4NOj6rWW$8jCR+TeM%d`c8d!ZJ{~!+IcFTR-xCb=y zbxwCqEz}zFm!U9lcHOuN$s_?XyPx*&$bIAzFTUF%(~xYy*iX#DH?%ET}lnj-1C~ z<+X^=ni`#wlCsZMR-|J|Pj&xjf6)DtqRx}@0hFQ+f?QzdNs$y7eE>(owYE`vg92xK&3)yvgXW9#@XH!RHqL zqq-7-fC)u{Pal4R*E`+(N9zb|@9Vpluz6=8Kj_096VQk147Gfn@5DEcfAMGp6nBd8 z=w?=B&6$)e%yHaAkoSd+g=2y`U>`*Ht_|mCN7O0jg-IR2;ErEz`rA?^?o4(iA1Z`X zSEl>!IeIU@%H9TS`j;tA2_B%m!m@6bQCt87hFN{N6pRRDzUhflv;ok!H4&0MwaGCQ zl}{Xrsuh1!gcj)pnEHEWwgjK3gnb32gfFxC>EBjb4{E@!!8tD%eZ7_vQPP3)SsIXN zPjSzYLo`)=J7G`zn*_1Risu9J?if7Ah^-42_2PI5A47ws8S&#+?`hs4ak?3#=}-w` zJk*c&Kh+3qnm_@ZehJ{b=p{d&3UD=lhcc!i zdh?1vvRurKA4w&qgdRpNt85obRETupHt(>FJ)LZ%tt%@nnvUA15)RPwwB zV;0noBn$F|eORhCfb+W6B4|&rrAtrk<14}AyuP}(&Y=j&$e`BV>Z&-!c4d^){E}h% z09&97u%3b(BT+gqZ^aH5r9BPM{>QqtCBX`>nK}O!6@571Q3xk3>n{~CzICeaxZ&~H zOtUKUvYv5WWY5F8ZFyz?H0Z*gQA*@Y#V1GpOvm3}I-bf2e+mWj@u)iRcE6ep^?lue z;d-9jKGA0`G;89tHJKpc@?ufiz`&@hudli!^FzVuVnk_r^NK52Co;kDJP=!Z2QK~L z%#euT?k9r<-QK(Y_wtUK4Rtn3>(Fm&hWlpro9|E(q2Qm+3Z?eO_aAQL2i*@EN?&lg z!w|1z-9c&|?Y*Du^fWG>S!hT#BYEb}+S>I6Y$It+vo7OkG+)#?DqF$Oz87=PQ_!C_ z>IV;(hroWWe7^p_r#w=x3ex{xMW|&ygLY;UyOl@WUw%_Ot{hwxLHt@e9QRRc_LgJp z60n#MLbXPAC!AVOJou-z&tms82P1Gtw_Bja7=_p$rgVLSHul?)lABf#F)Vw5PoN6^ z1nAUND|lB^;BT%n-Ip-=nM}+nYS`RDs_l$`(uSNcGiYRMYT8J*tNN{|hXeL6-UB}e<1c)?QvCbF*Law>ECPRIN_x2)6Q=$dkqlJgZnv+m9>Sv`oU zh`hO%eMe;`UGIHms1YQ<<<6~4kKpt=6U6a);itmJM4Bmgp&@uSrQUby_I!J(=liwo z>D=9cTI)5C`BCci2guMcbF%%x3g6i`)V%EltNp%Ub5Y$~#o;-caMZ!`@j+j#cEZwN z3U`r$_a0hjz7`RyZChM_J@U{&zB;x5Xovz!BLeE0@psduK#E;Qi|6=_lC`Y=u+qaO z-Q4k~orw)DHU(Wt$Po^XLAD)Vjot{g3!2 zCg?bmZ0=d!H0YHJ<@QMCw7j}l@|N}6%oJY+!LHAYK2EVq@shN7*z?hTEbn^5I;0nQ zPb0nki2|F5ur390xk|?T9rtob?tw5pJJ0eDmTGm8e@m8S4R2sn0FBmE@IBfcWV2KD zz>HjOD)a7UrIf>ll4)WGqm4R*OVhf}Z23}R-gheb`?{Q2ZOdmfkt_>2Rb)5LCdM>z zS`JnflwfyrKaxE;Fc@3pH?*JB*-6(5a^WXL_c=a5ftU0&ZUz_NPTPyBN&if(f;(WXOWtU|3T`9nC9VQ*c z)8Xp~=_vR#t-_(6^x4pk!>MN1brTizScED8<>`SNIaS|8FWjm}ql|Xz9!n@_ON_w$ z5BdYOAt8hx8mBn)s$A!*Wqqg>Cl73x&cne?h9kXJqrNMX1h|E`2ii34h=)m=#cfJ; zM%4Zw`PpUHZSW1uGAtD)(K1rNLA6vF{YcdLDIK?And@`~b18(3){2LauR%16oT%B@ zafvpjl1lV!RaEoH{!x&esPT-&k@U2&l3ZdqjmQVb+M^ZyoefT{+PGu4yZk*)Hu8TX z?e?MdCCi*T^r_gjxy|kJFnQ74Q+2&jacU8**A*bCdo#m+FBGG$O0KtwR`K_34=@0{ zF)!2=dLH~=c_|X@Jwhr^^`?U+lPt8!PGlG_Q?ZJAeyiOWLh?27rXTV&sC4cAETgy` zI8Nz%?`6~`B>;Xj26^`XumFynOD{ebQO`pVW63L)!dNVpIZ6ro$sWzejWA>^2IcXA zz8><+gK9CP_l}8`eqBVpa0n?64O(WBUS8dL|MZ=DnG50gaoB+@%#~Q@Wmm35FG_p! z{9RE_2|%vAsueRQ4T1QuWv=J;y!wWb7r6Xiwf+!78uxPMyW?h{XW*Fbyyqz+$HHP; z!vZVE>}+=(;ox^Gk<;+z)U*M+>xaGL5XQ7FW!bmw*bT*Nm-I=PEA(+0)=$zS19^p& zQ1AJO{?x2_P89wc!N!Mphm9niy&HRWe@qsV)1%Gx4+n{byirqSJ&HBDd{`~KeNnac zDz*3VF-3FHyH`D!%6RpKrL#m(jOP#cX!Rk?>Y5ug%CAL}BO9?NVFi7bJ`biEHh-C? z-Meup5qC$%{vl1b5ghPXMoyr0t;mw?g)!^T>@2_I7eiT1{)7F=6*V;>8KvxHnTd22 zU46ZFVkl$#!jtK?U&Z!<`a54_Mk`kG>xh5LbgRUgg1i5 z|J&VFQ3A)ZA7uOT8Lb9LKkI{&rBoX(>3+JQNmsEvjJP0&m;23V8fy5$ChM!<2_^n_ zGQA>hWL=D7X{PIpATr!i5O`QQxRE9YI9TCCGG$$1Jlhu~0Q4fvWJ-*q&NBjqD*;i- z&fYf6Hl8++FFtc$q$}ge>dBydL8bY5wo8663pG5vB82N2I1m~xmS9hor&Zv^>LOhkH zCl<5=AN63S`!>AzjtX7Bjf6au55MS*xA~bW=kFrAb`0%r{3v+T5uET^+#Hq0BI@)R!%>p`Tx`ZBRsfc-|cm*R7I_l`bi3@WSeEC ziuD!z8Ib-JQ!{;Yem8%!P56X#O1&ZPj7q*}Y<>|ZwORT<0Kz~$zXr;TOt5$QAOKFT ztzcti1&5~-9I^uC_Vsvtqe^?_0;oO)CelHhFSdf+@X+QKF3sjRy|ZhgI6c71k)TEK z0tfM!i24!oaRrIa`!8+b;jL|)-JRj#tsQ*hTQ5{-_ndxB@yRyk7#mF?K8Dl8W3_q- z8kt3YObDa?TX+#C`z;aIGt`4T4~9e9oVjK^!$46 z=e`g)yuLS1L&tfQK90jNAn52}fw8t_a1h+<1=q?m*_E`)!IfbN>gL5wJof10<-L;~ zfA3^x7c*Zynawbt&F}^P=*!#g*9~mN1e|;|JFpcgMFBzeQflcF4#TeQOg0G@R-_@Q zgPuGR4N#&;YBsr<98m;B@T){XI!`)vDriiu%#Sis4-e2HvGHq*wsN=G@&YL9Uc5}_ zg6q6M>07-#AjP)3@A|TWe?35Fz-%{v1-4IbXvd04d6c8MD5)RqRGSqEv?28#LQMEA z+%CZ3>7=Zf9G*;Zcsju?Yb%AODpE1w1xY?ubln8n396q78JympVRx~>&SF72Fo`m4 zke#<>oX`2OJ263cf%`%Y)KPtJ@_y{{7S8X^aA`ir>D}Fme(5Ku~YXholpJRU~r7_SnK38$5@&JTn#F&17< zhqOqCZEcZ~@rurk?Fi%m=^*UTHA%RbAP~P$h2M80Ub~e%FJuI z@O2)ZH|xS7t{2;1HBREysqsuW0IKX^hovP>U#eG#k-s1w?&vN6}TRjgCWHpWGP)0&ypDN7e`$z(dX) z4*=zW1qAy$PcL!?k6DZtWo-NBkS1Y z2r%gctCJ3gCKG(^^S9uWJZzuO>SF*R$SWp7@&JH`dd2pfg$Wd;XsBqLtfj5NdGU{a z4(U=<0KTu!GAMutE^XuDY=*7H0vBd8e9I4A!r zo#e+u90bl%UKsfJ7#)wtc4FeS8Yd<}F;1at8xMdA!0pk1=s5v(D_W0N zfoy++i{~$4cXx*4Cyw#PG(G0$j~}LWhwB8LC>@+3V-5>|Rc$z~n4`cQ#c&;*MlW=R zy;d;DLwLtkoZI~0c$WTM4;XGxfbsDD!4pb7T%&S_QW6z$Gd~6g%H3FRyfVLB5M}*b z-fR!=56E&};&%kjDf*W?QBl0%fJKRxE&dyfb;T9FVt;TuLzkGqM?wH@1yw2C5$Z7l^-P*>b*&JJo1uo8JyjpYv?m7D!zdB-36c;A9I!ozJ^c%=Tmf)KKVJ-vU(YC2c_IjMWT zs_D2KTJ-@?XoX$UzW4;?YUS{J?6w7JoRBBsjL@N6#BtxL<=6wL(z4T!54f2^sclzo zB#d0z+1kOQ4?Tv97cOCQbF+N!WXE2Ly%Es4<9hIwz=rVd2Pi=zr~{1->=QN}$J`0| zs7@kvqH-+f;XF&Q4T2;JBL@KnMULab_=%zfU@Spv>F>C0qRXo-j+^^4&WKy_^8RDI zOlwbY#deKt-uTLwlj#76F!mprYM}wL-5<<=#jM9-))XhB@0=(n*<7q>U7%1`g=lPJ z0<|p#JDn1Z$nG z5F*n73KydpZ9|;S-(wNLwa(!5%dsjxczFjGcW2m~FK}Tt$3J-MdAn}dCT~o%ISwr5 z@`PeO)fUI@Tio2o>v*asua=ihC}aMcCqcNJZtHP6H4gDOp*1+hr7g(3P@}PPtkra~ zF1f8O4qO)igBz3m*v@(SRwiG#?4$=;sNl77WP4P4S%3DXE`c9o7da?sIF!6+JhQxm z!mKJ%xcRjz4;LV>*7mJ^^Bhs_9g2NPN5%5E%VV;T<`UTJ+87)Z|9Zex@!XH{SAfy3 zd(h37Y&-Ml8C*Pn5u2BzIN9CV#q<8B&#Ss#59k1t)d){MbP5k@21#+sQb5!xSc-Gf z0i%yOriE564oVQ4;?C0pyA&zz+@LH_I(6m=4Xn1v@g$Nb{-A75ip;uE)D zG;YNx2&$<&0oG&Xuz;coFY@XVgj}}FMQkc!NNr79=?LoZV4_bFzXfb zzQ^`rfz8FD(1-2rIr|zw!+(r;Y2)7Gi066o_rzZ&T8lkO!VxKxaYKh2X=Psde1L=S z|2>N)i>I-ID0mUab;)?%&w05k}PMe8ZBVYKI^IOr&^8*4} zdxU)(c~XDR)c^{=JU&^^LI>VIcxE2HdEWNiR`bZR#<&-#B?NsAM3qV}a%s86;q$E9;cAA*2k=T1~AUvsi*!cIw~1i#WTJ;9Pk;%T@!wA&(w z{Q(#aPW%>-T_61w6+aIWmzgl$-*LTv$Z83z~N4Dz# zElHhwanhI1)-PW*$&YrdK(Wo&BD^3NW|DCQ6u|k}3_E>~T~+M#id|LQe|Zaczvl?l z=vb+N$A0j`_{a}DfcxL_A#5s(5*8{wI^(OV5bYs-kLMj;!HJa#POeM~4U|4e^kIEn zbi>U!%76H7ji4<+#(+J6S>I!B#mT&n#O!i7B4dLm9+wem1{xD%JJII&8x+SSMm!#2 zMRMeT!mH512p!VO#m!P2qfK7S0l{mG4rTXT;I2KliCcfYhHa&bsUXlO2qgXz1MAzQ}Ac#>;PYf2fIM_9*?!ct-!Q zkIPIFlr{Qy_7Yure6!pyt{L`4dJZh0-~$nVxl#bW3Tsa;6awL~|M1*$n{>dj|M7O5 z`I(0bNdX|LP>~Qq6w03VFX6$hIqul#vAy7G$=0IBPOtdd`~C#@&w;55^xz|(1-!UW z@X-<9fr)c&Pd~hlryg3vkAC$A=zkngiWNR308|757AJrm3vHoL>m&B4sj=-I$x#8! z7A#0uoH!5K)+E6_XJ3Ptocw<4L?5l=GG3Z2CPnzP`xpSz6@VH}6W;)RT*0_r`r<3C zP-3*=mHvQd&>7?9HuTs}IF=_|7X>Gz{GzDG{y3f5E&xpNR*w3GU>=)3>7`ac%=T_T z6#gFH)<|LolvS8#(o6h#3|pB=2Y^I>x3A-J49Td?Gp#QWXO9 zJR`h}VT&W`{g>mw@ItSKSD*XWy4B!=Q=gXOGBzBCD?)|SbjYXsxETr9591LS>n1>4 zIEW0`XSd}@^W6m=f8;by+;Y5pV&Zy2CkpacFxV_G0g6N2&AHSAn$E>w<$n$W(t$ay zC_oc(LA{%((A9{_K>>*a78Tn}(k?Y3{21N{uKYO>JsGh%;~6PrES zQ5FZ@E&yP~LE)Vz>@1Mss4C6@c<}NT&h5@{alXL0*&OHQbNuG#JO{grId=LvcKQW& zbdH(wllrn)pz9ZqegU;l>`UK6tWEkJpM7K%&p5J<|MnAZ0iY;SsTBswR`4*|9lOP$ zz!-gOWBc%h8g>?ovRaZ?Oh9uL+Y~=G|AC_TpZl-JW4==#;F$mWV?)4Y^NA_NwfQ#b z!02S0pbv$~7XI&dw#*IWQpJ3VhanvpufX9J`ZyiRo@{6Q_O=UYwK&W-#33c>1E7-K zW%Vtu3}qc$UTD~(THQ#p4CP^a9tWV&x%4Nl%kTZmSf(+kWvq2}TJ9&*)p4Cq@XF&n zHcrfmd*f7ew8bG_(b;8%U2{+%;<(z?CpoCDCs=-K1h);`UoG#wx>eWqLfixP)veVh z;N0o+IDh^EHZN`B883Tod-DZi@rpzx=~LfO~v^ABQj3K>N&l#a#I~N0oex zES&GXr~gUvTgFIC{Qj?S@>Kd%J@Eq`FKx_vvKMXWAjOmUe9^&tJJV_7ZHU7*$0G#o zC@##7)^A*&ptG!y8Q7)pFrG<{+cB`e8Jm(E}2r|d6X&WP6pT?uSULSq$uy8_}# z(w#gf$~NIS4bgC1FLFw8pxmI|KX{UdOXLBS2l! zdV9jKBEcWYeCX09&hE@`aX!a|*&L^LXZWp`J`b9Ulf@jem_z2aE#`m~DCm2jS3IQ$ zUi73>c+WRI1+V#%sd24~42o4sk0?$IsfcSbI!?Uo3b_9iXif>FyV`Y_io_oUbfO?n z=gS8Ai>OTmiV`C+5zzAlk-+ohS47FNGD>F*r#Bo;Dv@7_eV^m)QYx6_qSH(!8 zpZS#=gRHaJ#rgRJ>#{rjj2A^0WzYP)Uu~bDA3Orb>E!`up_uYSk%zL>Jj^a$WE2?o zXCKcYC~X{fWuK0?*}kpJD6E9Cr)z4kAPAo?h{zBdypo>w8jRdn%AJ6qd$?2*TC>c!7!5v~e4ckrBq81!|KBDTdu zohy1ej8E=4DM|_~vCSQ}6bEoev;av#5=o5m6&i+DJ*H6D^sCvb~E3w|4NZ&wpVlL@-|f zvjz7H%7ujj3jhn?=>mM$v!983{{HP&e2`ZZA`adL_-csqB;k62LfrtMS0f1t=dyhs ze*o0c7_N4LPJk8R$KKptZU*AB%e`m*iTNEpI8RD>qHl|JsK-%E0L6ckhkD_%tPsyW z-o|6*=M$65>M=(kzwG}dRtlYQeOer+4S4P_*#|w~FE)=~#)cnfrG9pK-``$Rq z=nyaDul-G(coi&T&|G)gW3bn?tA(qjceVY1Mvtpd%DWu2>z`e~nfLrf+j>>d3EY7J z4@5x@5*85d$fE8_o)~NeMBS4it#sXjU-nKZmj^VRA5dgNA6jo3bQZ z6;{eE`qu#&{3QAtt_N@Cr;>_{rauZV+QapW!6xpT?s^5rprF}yL%@h@-mG~k%z=Im zEaogm7QL^USRvA^n)v?6SKe*}f~mk5<~_iE;N^!?L@z{kkK4ZF`+oWBI_8A#Yz-JjXs9wvZBZuvS)sdQ*ubh_{`w?gk0G^q?L=M z;7K{M?wKBCV1Kux_am>cEfjT;0z2QMKld6m3__oFEJ=rUaeEA9_O0d73vmmy*D@~v z^t_=Q2giZ&sCkKcZ`8Q;Nr2jR5I89QUk0e`a5Y9T@UCOsKX4(o>j+Z@91sNv14)E| z=qb3y;a)(eQF9vstVj{`9pCDwLIOaM?*xdG-kLWq+G+Jxxbsv1Q1Dj}U61l>7PZ32 z-rN?W=RC+W{c^^nRWL~T%bn0Yzl9Icq>s@v`HDOk_E*EDMg%7E_%%U(d^0xQ*B$ga z@!{9gv4E|FpaalDV4tpscvIQ?FLW;a>_e2n63<|71MDji9Bdx{?&V!f~rwaU%8j~Aj?nvzUKe}{!&KOMjW6wyH{JAq&5l{cmf_N$2)LQ=f?? zREA{CzyypmFHP~0Dzht5YjW>ig>DjLF3v27{sIdvC{<@kNKA&)Q|dz zQnwn1xxu6@ntMFb*M)p4IuVynhjMW5@#AvTal|p89>yd6i3@_s8-oI1-lONTkt}hF_IH^vONwxN{XJ_emKrJ_<4W zL;+!Y!*OX8$^c3Q1i%0RV0M-yj|GYSr<&ww0>QZfIjL&;sh}qj%3OjZ7bz|e1kkKc z9lQd{{H9mF;=23YGN9~j>ve!RXjcHx9+(I^bm-w;WJ__N={m5ngMW>cz7%`nf@TJU z^x4Lf9>9HoY?mhcj zy!h1XV)BDPH2b*4flUOIiT)Q8N?oK-PSUChnZ7A@OqiBO6;CZrq35x}kx(#J>S70= z0Z`mxFPy4KN%};8%9i{4xsL;k2W=O+m!Iw$GYj1 z!7*sbgKc?oC!a7&P7v8y>5RnUBc0&svZHKFiryaxZJdU{b&Vhi8m>puz3bWEbjh|V zNyK=OK6&+K4_~T%<11f&L;Vgg6_nczWwrox2J{OwKO+{L>dK8yP=Uc!T$TlmQ47XJ9+WxW19NAOekohtPFF$|2i8^w=# znUodIk7zKS)*AF2R~3Kwy?=^cVMR$m^3=Fc=+RdNic@PWOb$;cSnE1GdCk8-TW?OP zY+zh@9BT19_i@Hw9U~Uk3ebOZJd#cwM~0L9Kl`(Tz~w*{PnEn)Jc=@Ape`*T9`z>K zRVrHE&t<2)5tG5g^GgPfjKK1OqUb)xqs>7+ssQa<>|r?BZr-nzSt^m3XMpAM1Z^81 zz{VnIWP2>510~ra}i1m9hd-CUr1C6%52qZE{yj zG)@NsRXSs*4E}r<5|P{VFDo+pTfuM~0V;jSljq?709>zF8Df76Cp$0w4Hxh<=XVX! zKQsVKy8r+n07*naRBT5%0IrnW2q>;JaJHKO3$k4R0Mwq`*pjU;nE+4`h!q3Jzvd}8 z|9?Mde_+p&ZNbbyfAG24p$G7Y0B?W%B3^K8j!)WH;PUzc7w2<4w7rA7-?M?ItgYee zo__``2k4*V1Rg8fcxG*o@~8%y+F(8e`tCq{DZA-3nKh6!8R$CSW%qO;yFcXa5OaeDZa6jmf@VRSGAAw1z_O+?&}e=CLBicLuae0peSOpW*#M96#E2D-}#CN&y5lQFxJb; zX2`{h-0@oEsiK$Ex>IJ-0~E#PW&^jY({p`*$Q@&=8?;?sNvUmD=!o%8{XO3y^GY_W z^f3^~AP3cX6M&%j0CQ{D2MB*FvSz=r$K@oJ#>wR>y4N__H>3Xu9UJe;zKzbM;50e? zxCT?y53d%M^=;c@2(AuVdN%`-i7L23MaaK+`}>M-e&-iHzr8$wsSmh7!LS7A;;4e3 zI38?7l;Y$OnAu}W044_}j&I&S2b=AU2QU(}#*_1arIdxPOVuY_@yzU0-fjt{nzGJZQMdz^bGaC$tdl;g4`s70Mens0xn% z?Wf}0zyB!BsSzK3UK!I46-LjX7vQ}Yw(!RnxAB=L4&%v(58>3>Do*dr@UgWWeBZkc zA7n6-Bu&pkq@EFJgd4{^5TenG$_?fz>Qnu#gDgF)Vk;bdVEKpc?O zaH0j=|EVM6n=zYWO=&xi^-}bktVoAt?pmsP%$|?-wka?rHxL@UlFCgD~-`iD1;V+Wq zLW27a5UtugSgZ4bGft;7`Xx~&T*qOXfS2ivA%|EzHFC5(5po2*A0Al<^Fl^%vzx3O;;j`ZH+gKtsP-JwugjWlXoCxABk-UKR4kcE9G7czr1W*xZuV9h`RDqL!_o+Dh(;tP+l@L`x6zZd=!eNHOMl3k00wU## zLh-J1m(iWy#Ix3>c=BNuC`YDK9GUX5$yfc-1fPBCI26FK=@fra z4j#C)g}0x+fQ16j#fR)uD12N~3KB5+DYQr=R+RAF7G7bYVzS!ta}KTG`05m=R#$Lj z+TqiVZV=y9PCiZ=^yOj_JB0fo;x04$IQnBdspb@kp=L)z5ukq|l+R z`LWoTJY`iN^D@oa{)HDvsv3jLQ#)QaUIU#Ahk$CbsCa<73t}vbtd5gptj3u*qVAi^ zaeTZ7I0IfGhjwAle0}6(2!7c}58w*j1I7@S5FJB(BH;!WBMx4aK&8a4Xo_naXO$jM z!CT(`+Yl9e_kSM+ihRp`zotZ@Ga)%E9k!{m%ND2v3=wNu)Kpb;mqm%DQ6z$!N zR`>&+CRgAO<#3*vX8hWR`qcQtI}Gw5zk$r@{2-5SMtyg*@ri_e3N!&Q1-dCPnRe)= z9VQcjOuErk6X^t}|IJ6B09P+SPAb*_01#W%;HKcUYSqN9eZq8|k6Dzpl0q>7ux6A_ zO@JdS6Rb}rI5e5y#Oex;u1v8po#M7bYq;a^Aw1*AIzIp2-}8l!x*!2qd;nmvq1>xo zA6x_cj|Z*;c>amQ_@s@)c-oOexZ}tn{9oVsBy@s$g``<6>6WgV053lIeNY*>(+7&b z7GF;>yUcIh=d-_8IPms}qu?>*nPZkbMsQ5lr94fZe1kE@Gp=E2d}hAms~ps;&OIo3 zBIDKlhISd?BI`4>Ly^7UB-%?>v{m-Vtx4vZ*HCsJSHITERq-mgL!MPz$*mzryIR!J ziDSw?Cdc3XCjKQ`#-fFr#hUsB-`VjHnhQ(ut_7rRI3QdPq2#6%BLMI=du&t25(>0_ zl8Wv!-l^>$T5&R^@dN@Lyze0Z#0J~%aN4_}(py>i($QI^`#B^GT&;lE9ZIL90_8x) zGfmPd)w0bL0u*J7l7>r2`k2yH@_>sJ6`kv@u91%DUiuTuZ3iIn>hYGlz5;u}Qg>T= z;fa8&6vkIh1n4^DuXIEp!hIP-1(S*bw|w2xaOS62oG8Moy>hT=VeqPQf(8_-0u{lN z){fYRUKJO$M-MHupgg{8ZKr48S7IFseSYlhQ`RPU=Fubgd!P6uJmt_Do^@;k&pvtt z&-tz235!_KA& zuNO`(4{>1q<1RB+6-1P3mfsh&z=fHC}Tf<{XWa+f`m0ac<(6a|0;}nR8nowSX45Y(XcI zB8d>EmLuz9REj}jX)=OEG=etzQ?Ag9kc%!!xEun(TSit&mb&NC3pX3Cvmgrq9nen{ z(kry%#0^zw4~U=?B~Uu$EnojMoKD3FNW#w5lA;h9f~OrhflEE#``EJLz~B2o-a|>R zoX5XBw8;v9h~SU5d;IbCd5D}x2f>Su9mZchzJdS!Q=ft79N)lKy!ZFnUrE~wpS=na zQQ$BXx390^*3}i9Se@dRzUwx0>UJ?dHbGS3jtzJ9Vl(jw|b}o;Yfq-JGbWjdcO9RWV{Qm-OVle^$ zZ~OK4qqmp^%wMW;&aoe3D9YS}L7pM%bZ|$}I5`Q?iQ3f?z`{B?c{@>uOS#bXQXZ7o zqsq4gRd^U3Mdh%AWnGh|Epl9T0SeC&fQ{||D&ytEP!gvr1Js38FL<-PQ?jS)?|JcN zz)cch4(Jq6RrI~`v4l;;{B?o(m!eF_w|v9XaQY`d;`y7BDmWasjA`gY9!$v}Zrtk}N_5{LO9Ym-JMM*77Sv^UOje4|ICcn6Szp7+)fK$>)>HU(RrpE?eVgbadlW11c1|89diW*) zUPTiJF;S|~03>40;vptuXNEz{=gCx*q}o^!z!X9;L7PK>u(@m0#u`8df?!hB)!Z&c??|F+~576>Y5vNNyxlho^ zUuv!BSRWOd`o`yRxp>D1@cFc&+)BY0+fCXXw~k#JSC(w{U*JM(<=vd1^qAttUK zo>1n%D$uR)U6aYQLsz|PBISLG_WcSG!J|L^0BJ~&yY<8gY%9{QVgT2XMF+Q1oT#FF ztPEVE@qc{727i6i=)UwF6B*)CM$i`pc;Ttn6<-WL9*b+XQs%%kn4y3!V zK_$;IAx^z=;ngvL`*sB+s&ylh=(MXh#Nj^k$WH2L0>s^2x#pF)0496rL!JegI{*y( zQ%u*L#`FFvI((;O%#m*>Tm{42UYQi~>Qlo~N7fDtu5@QEp6j@OKe3PkMrzjmXKV%o{P#Ec0iNE4*xg{nv~C^^LX z$3)AR^&*AWYI+3fS5Ly@Mar@p9SD6(3mwxr6&rw*foTh~YzI| z!$G@c{CfDV9Ww1FpdbN&x4q;2=#j@j=vtzHnvZMvPZqfy-(1K*Nh%jIP{FhWf&$Vw zXLZ+L*0$21Fexh+&Lm;FuwE1p$0mMl=r}G(kc&BUTO9DJ7b&4^wVVqzeZslh=jHyN zY9zc!SZ(n|eR8lKewM;M3M8cg}(PMM5!2kP2FU5;~`JH(BRPY(M9>*6y^>+No z|9k|ZfI`Vbg|ZagHI5~0Ah2VXEf+L~503*oADt+TMtk!yRvffNfyW?VeBp^E6*BxE z94+!J#ew6P90HiISyqc(5DgMVK^t?TQk0De(&9VkE8AB^N}80R#tS*-Yuf=yXX{_( zkZR>2%M%oyvE(hF(mC5Kd;v>&4DB);=g6A|jT{4c zt_r8-#p~RKpRggULMVlM3kq|*eZVpr2Z5HxetA2z{edU0Ao+FwCJF3@zXz%!Jz~V| z?6Hya10WCu!bd%3)!IU7m7Xgmk+8yn+W=@Qdl64T!z7dgfkKmZr=B3(@j8fteK9N! z;5q>&5f$RQC~g=_R)}D0laN47j!3*Y8O0+V8mI(z(x)UCCEi>9@wKj$jCtR6hJ&&& z$GWbtK(lH>e}mG$kbk8EPzIZTp@NG)dlo(5bw%+_f#OrwCb(nc5KgV~58@r_1QS(g z-(x=Mur(2^EAVNnyZFP+xzRR0BVg`SHa`AYupnVg!W9&7GVh_7i{QRXJ9upSA=^bD26t%(aTRQT?^&*z z)HejFu!mZ|?Ax`kFZ)nhndnA-{juy(43vsSaZuCC1z0_4MUf)5hsm5L+?1(Oern|a z&l8Vw!{a##4s~9L{ZQvYk&@+6w@dUN=8#CvVV}M#b_8+b)=u0Gv|TN8wwaGXm3JE+ zMIPss?b2M!H;Y-0TIX?DuZYvi(To$+aALsfV+RIH+gihDzqa8z!L`tRf(jCVt0Z1H z998G@Ul;SI(|hjbLB-DM;_#&ria3=^H@m|Xnoi!nA{IUBR!r*f1t!cRaB(O z)FH=up4>Sxk)a?bp_*PcWgD~Y%|pKVXz7_W-LoP+{xesNsVF&@^|%46IB*chb#t$hJR|HPA%v2Do3S4{_EL<+AuZ@TWTTwS=< zdeNU!L1g(4a5+$9Zf#g}w)rIU-xtmZuUco~><_{wW2XbS()F7SPjo?2`KA>YmA^!e z0wONqhh3D04hXtTL z>;+imvC#2-QkGtz9oS%PGgb7$aaI$^(iH^2skIestW0reGQrW62{t;x3V16eoqKqu%_`B=#&0|iB1h~RfFZsGXK6f0ea6%qX0Z=J+f z{f)CMKEQ$mutG!tQE;^ceoUI8@DQ@S075==WJEy&a7D%FzxV8G@xqg@EAb?-{!SOe z$rOcb#Aw8UZYWekjp=ch>WwS8f9@+wrxX@J?zae{~ArXdL1 zHSz#S=3IPfGX{m1jZfCS8cS5Z(DvUO2gr0tF}9?X9YXPTLKKoA6gmZ2zBb_paDrxF!F zhDjs>DOdY79Up#yu$ZV6NjQq~jH^meB}iyIASrFZo9_~fKIY_Y(E5K2TJKNXv5%)c zVK5dq*BfGRt{oPDPGH|O0aO+AUrwlM1&Jsf7YeSFsG>IkIKc;WB3SD>99@~>=yZaO ziQtF`ZkY)F#&7&?!o>b`8MA30A9@oDxK7nSa0}SA9zYMlZ=AV=+Yhhf=yZbn&hb;w z3V`pCq_|PQi!%{e0%l-(G8UxbgQ(VyC}<3y9vkrlU{I{*#9k&Qi}`9|Q-E9q!0=HF zz`USZ$6CZ({V2xyw#5OtQGsE8;5lWrcuYL18~}&t@3B8V@z-+1#2@kmFT>qS9-~ui zX1-kUDso_8-+IEYf3=k9WL`!IS?A%z&)lq%CKTf_ZWk|_>M;efzqlO9xBAG@q*Kef zH%`JWdE9x$IExA!-7YOGYu^{_w{wdZqj1geaZr$yd}Nyfjo8EZ8B#$;^SH_17LB|0a%}OIKDE)Efc}t`SkO+Qht28*N4e+vE>DTp+OLd(kI58%S_XLX zIK>#ckJ2~C3mNNgJf{0zF{plar%8}Bsk=ZUK6I$;G4 zQ9QVY`Y=fq9z_nR^a?pLmiXR9XuO_SlOiQy~2NgVK^ zL}5NB7AycR&li~YJtop&rR%Uhnc!=mdjQU?J=(zi zpS=3_(Rb-tc`-;49e;6Y6Hh;K2&Z>v_}JEt$u1V3rjG(pjp9Npk4rsPF19s z4MiQt^InXzpOZo@Pv{doxcPY3{bBNWTO~)IeIel_+^(-z&6m*uggT7zYUFV!zVGLQ zgC*w!xFW(np{0`sdkmi7g5<5g`My+?isd)~G{s|ynecH-V$e>3;=d8+$i8W9Wkn?W zrV=g2n>^fKIucL_gv>OM$%tFu^>p;kp150|5+q3>UlL*;b70#TtmHvq*+)znm>f(d ztd2{}EFp`;I?dD;}H!_%Y* z6dc;b<>rJMi0dX#Qs@(~DVJab81MzKCD~y4N&+EU%f=E0 ztlQ`|w{K>??|skeAHVfmYwdmBZ|*a?&63V{&e?n2_g-tSv(Nj!0{~W%kjIGb6=#f9 z(vAEg;tVDEn4igyJjjoEMf90d1kK}+dMHnjUyaL<5qjuvUMasQ7md0|29Wxc3=+7b zKAD4x&q_|L^qsEZU+M)*dENLFnR%pNtVoUQIhTx0ssIY07*na zR4q!bdf&~s`rq9`|7v{Puh#l_gysC;I+?hzjJTZR20(R0#z}39dFQcT=bqZE&6vi; zcq|I$X<4Ln#f49tF073Sp(eb1n##jmk=d0dP3jWGMbU>e&ck(A0L?B_K_h6Wj&Vmf z2B>&Y585Li{OX1LOi8i(2@3{bhJO$ivAAL$7vyXQ*v?hj!bNifK5vHmj~&Os)fz`< z76b3Q^SR^`0?ChTc$S>aZ7DfE|8Y|+yjE{ z;icpN9@Iy1IPw!P2eBcOB5`Yu7#}_&u8Tf3uB9^!&L{QBzRfv=c}hNn>ySC5@~%Z7 z=~Mr-U4AL!Q}b?-S&zC9v_eB5uil=dp>GJx?P+rqGCCYbV_9|Y^l@7?Uj2A)!11;v zpI+9;0~l=uy3W&v6VMHE7jUxv4(yy2Bwx^;iEsRxZ^r-h{Qq(6Iw4&3zFTnR`)@{# z%UIL9@m0(kV*a|FT~TZ~&M~7(+u0T}@sIe~WWJSR&cqdmCa@P0vE|EPY`h7BRFHTx znhRF25U235h6Z!qbWprXDeoB@lK0%oD<<;~^PvG)erPSXvgN=WnB2bffdcI@0Kay= zzkMSIJ=s8?HJJ^}hsL>R%@<}LF9Vv*nC6D{q9l_;$_u{F0CS0~> z!WSId3j@%7o`W-tbEN4iTFBZ^+6=raZ4E%_-_siSjUnOznqR=v0PLG4%x2i0XB=9s zKx)@Z?_qq-89M+r@eglBUG;(N zO8WX$V9Ijeg&t1dsA%Ps3juKfgkNRXVhk*8mfXrGUSk1}jI^i9(vdPY`EJxhb4fD$ zF*rHSI42W_Xu_%bXm0h8JWn4s(2n<~3L{;0|D8w5%^AYsrvPX`0}i$$IB@VFo_^V7 zBi2T^`hEGK);Lh+RAW&(LbDRrG4D9H9RonW?gt+q?`A~fIOkY)uQWB^CNGX1cWUPG zVjgT@003ktWvG~#g9d?00}P}F=hG?6HW;ylIZrbh5DLL%NS96ez-34mm;#6f(jh5j zo>;YIVMKk>X10YMpv>33{5-zCk$q2An2}!}HuD4McTIRa?S)?qm|>dPF6XI#)qu4b ze)s+Z_~RqT@F!cx@%dxN@&A4Hxrv@9UK{`D45J>2XOfa_ftR#4}N%}2yF>UP>fy06QH<+w>4vo zD25Xlb)u^~k}$;^`Nb}W(^v0FBgJ#gAvI=GM&^kCra)Q03U!B6x~Dzlm%19NJ>*1v zM*2v=MW^bIhRi$F{+>_Tkn#$jjXD=T7{<_b34I0`)iccLo5tA>DBqq)kMxz+h`DiY zq|E~k5~t?xO?*-x+NT}t=o5mWhk76OjIc|`hNd$H0}GO~ElLhu3LL()Z!_Re@4XL) zwzlvi-~IRdz5-Xj??2$G_w%d2Fu+=wo8e#Lve-}GYmPN=KGinSEA|2;!!+KF^I3Do z=c?MWkjXjBxj}mWNBsDKG;*gLRoV5)f|YnOnw#%jv>E+}&QfDNDUG`w(js>FlW+$Vvm6z;6mte-U^aBfES^B2$tiz+%?I7fEk(@rq(b| z{=`(K#tOXt7xz4b|KZElSeapEz`yv^v+=!Odk^^IW(I)spx*UuC+OoO>H7CH{@}@M z@ZUKR82;?jo{CE*(ywiXnc?m4y&OOIH;x3qW(NJ@E8zET7w4>BH!n6CeX{tbjxhpM zkmQGA(<<_$D8(paMDduvLDIm)(CupW4%{>cq)8>OW^lHm?P)u*=yVA8ZgKkmBZ(;ah0@4J%kT%7a|zqk*?qnU8h_revQ z)`l@CPn!VT=E5iZoc@tMnG+?0kXM`&)t(ri(e{NN+226_R^L^en0F__DK_(i+isU| z+%J`8xI6(2Sf@sG`f%4-79#*~U>_$zGcZ{IoEvOAc=#|596W^E|M0UTC9Zn^&Aopc z0B}y^+L_~>bE1r4e5f^XnQMp}ZEXg|u>{p#%uUCw(684n%h%*-ta4PH$`|xU8bSkS zG{_VlebtqQHRk~!E8mF?R2o#+kKObM*r1PSq)T&sQU((aWspZR>I2YuuRk9}NwNKj z1u|?rP7(ZP>gO5xG*&as=8q5D%)yb!FTiO?geLAga04j+<6*+r|zAwXKJ{_UqRVC<8m*m$dBA* zBS8E8lI_A9+r1&L*}phkf533_!$0dWS0c$uqH6UiNU@j+MsWz_!Vs|^bW48ZL4L{^ zitBl@)CI(2JS$OUE~#-#K79^x4uCP=Nd6{$=$4dU>8WetW#To+AG^^j&s1HjUtpe` z1L(hb5)RLqr;5$RXMmDC`*1FhK2^xCeEo^t7&N*HSeSpwaIN`ev$L$(y zZiX&@r|!ftr!Ok?H(kdDI9dOhz+FdpoDDp(7dWt&*B;w`ql>ux{Jr<$(AE}y>P7zu z01J|<-*-!(^l_lMfb?&jCk3|~=RzNI-N=I~`TAVu!rTmi=SCkdwT(M7f=*W-cRrdJ zgHn@6lVB_g7)r!l6a^N&Q~){gR7l7m+TaFxf)P#CCj-d!X-j=_%huB2(#QF9$pcEV z83b3p?7M>JS%ceH9sl@;YSf7gI~5!UFHYhYVZA8HZ|VRr;PUT!CRoADX!FeC#P74H z$>QXF4;{v@f9^hfEWi*8IgBzQ_=h&h))}bFyy4zo{=Abp zZWC|AfA+LX@zg#1<<|yaj~Vs=_^!WpF!^FBCkhVnEbVNg4(9YctDR?*d(rdk_jS?f zB1w0-P}U+vd4Wg22u95MRmR$;qJaEn&l!qq>XXo%Uiwtrnn%bdb5XY#yG1qeR@|zS za!03ov8no_Tc`_-Cn-kL9Li@7!UH`eqb7REV0j-_;K$oUd; zYV!ObJeS{2LD#7h=1MoT2pR>4DtQG4O_4#n7X>nKQ&AAc7A31bDTT$(CVgPSO8xaT zQ}v-PuD|Qr(D1q+KSw(yZqE}3WYC!!Rp~eIh@u2sO#OiI9?=GT!S{R_U^8rHak9tH zS)lNGZ7fiBY{on9e;C(%_HMlQ-u?K!d-mgpfB&g?!$-dq02g@a)ZcArr7D0K9(v8) zxbIbWV+H>5D`%-uz8U`fr4ydw|5nbvsiB!+Ujz0{&I6@r0C|Spyd7+({nBCV%XZ;Z zI?5tv;O2*40mUT@5gUxTj0;$eYbKVu2-ZZ?6$MIW5&JF}jLYWy0LH5DL`V63TvJBI zb@vnv#x-ya+?==IDV(I z^UFddZMP=743fdIsCHQDH9*%_XkgSrW8d>Dg_RW?Bu*@F(su(++U2%|tj+o?ptRX= zzkn0u6wBrFgJY`!~=ZN`_qJZn)#zSp>+KOxbty3Zrf1l zz>)Kszt^68Ck3AH0C&z(ABIS;k4^L9lix8w-c~;&(~LZtH5hPiUf;O9$3L8JLpJbV z1M{ASHUriS)_h@_eHxetoB?0>z0blU|L46vQ#=T!#(Qh9#<4YPSht25z(X+n%ljU| zo^}A=^aW4F$1i^>{`US$@Qi)?@RgT61<$x-AD*#qA71=l+(%k)0VNoI_txj&x9|Eq zK6CI0{@p`|u>yZXWeqO)Dii?2S6?#W>HGHJDSIYd(i--{a4Gmt?|kXLeE?Q41*GO? z*ysfan4Hy)#`(OduYUFfCVw@c(Px22MsOC4Ve5a1c`k8OKSR;rJfWCXUyWTtLngDx zA%5Rrv#v`fq*Wd}tI2R}*>7`HRDe9A&WPTa-5q)zU(RZI;f%^}cLju=ruRwa{ zOW|~%(?Hr0cDc?KMxXUM18A+!W&U}O{1z1b6ywy%Xn^@#f^1L!)PGSL0AQJarE`Vw z*PM@IVeT|79{QmLb>VMy}_Ed z>Dutfp+k7&&>{TZ%YG6sc+D%(EaM!gzRP(BYvk{UibI=bpnitP@M}Gk_}maS4wB16F|TG1vqizCbr?eumb4z<@9M zzAwikzjF_)HSiOHUX1v?&YUB_+!|&ytkH1&fg_j>Y@xM=|70)lXD+`C&$whCF59~Y zfBkcN`1N1o>!8&<Nv4hgC&3ViIwqCm-r0qI7EvECg847k-u}D9Qnso8i)F zFaKp@GhEt$eGRy5YIx35E&*Z-2r`8BmFxUgj^8)1NNFVL3X$gesIQR*&DC=MHTXRM zTA9$m_{_w>m;r)37Lposu|PVvy0G=%FcBZ-mwpi~ix&V`CeBzyoiEU+AF@bMo(X*n z^xL2h&n3AU(Z)Olr*C8cVEL^hT0&GpTRbpjk&=k97`x0E1Gl-#Snz2jfk(1WeKyIc zex_X<>rqCIWdkS9I2VLlc3oh*`_~O?p-zq`$Gpbcac7$`+Ztxh*JkpaXIwr}kG*Di>eO)Q1Uz+Wc-AF* z@$whUUL5!`>~_Gf-7uZl7k*gUUU9`Md^XZ0dl$xC6D1shQoiCyF{3!r7$`w0d3g9a z2hszIN8vIK$wP6Nu|GF+`Qil39>avM zkXWuykr_GqBsl52ij%V9D4gO+Ka~I=G|Je}v1}J~?S50+{%Gs`79{|8O@Q5XtVRUR zfNa3s_uY@fTU&q)^9DTnIg!we2dtmBDD44-w_-(dMBm_isvZDmddxSEYt7A=ztmQ3 zQyuVTnwVsl4C>@$a)<;m76nwJ7%kpjTonGH9CG4Ekz!1W&1Ed~0YC>*ml|Yr(4Y^1 zE6-%7#Qb>7gA{$-D4a0EKM0!(Yb?gy!V5nP&-j6_z?c5ObAXxjZEcv=Gum3ki3${6 z+dN}lmqKG+V_vgxF&%5-u$WQtQSL?OMGw5-S+7^Pq#5>t#}`f$zHHwkIXr_Xpx_IEo0yBjd3qr<&L_T(iRCD3WtK5c1w>^9C)6|3{qtgS9zx%b7P*0 zWIsutUgs*4G(FUvgUUa66&aZmB_qcrWO+`cJ><1X4;Z@zv{`+FJ`P+X&K{?Z57!Lh zjzAJ89`ud-v0c!$Z$LWV1xba4Y1Lt4Q?*sshKCOx#DPPH@a`Y~DMYLlKj-|dzPG^5 zSkjzgpWa3Y7pQYfbKDg&9?jCEI=7@j#f2ds2+CY&;J?xi?P^q|n+qW_1A7^4NrY)xjZb6(NM9SqANCG-i$ct8SZ*j=A9`b>$94UZ)VXZZ0rh7~Z+;Nn@n zI;;g&t7$g1fOK{cqT*6T?cyZvP6=1aZLI5BiP#BktHO5lEz-f_Ztm>~~b;OM##o&EPbR{n^asgz560Tyo z#=er_routE-GU#qq+jfAOf%_=uH!--i4!0$GWXtWEgheV5=_Pk9Rd^$&g_e)!vWTvrwX0PO<* zXS;V~d$E})ymz9oKk%0RA>TYY-;p$K^1;B12V`PU;h}^jJjR+QD34Vw+%QHP`H;T8 z9;puuDSy+#uVram_!BqdUki28Q>J2{f4RUZVTzXY(yQiAmnsn+@_= z(o>HpE*uv~X`?^71)Cx=fk*x%O+8Wui9(hk_*GfW3J#?ASZF$Z_=h_6x;lUL!;6ds zPS1z7_lqEL2zAkmj?y2nsZG%Jl^er_FGw7&y7ndweu1jnXsh+tetzfxcTH}8{Tur= z6q*B?|5+T=aqi=1IR*{hXJsrMx5iY?O^#*c8G*Oud~^LQV-aYWlzd{#%#X@c0l-2c z1%#2+u+|p^y+OofAweE6V%1~_r|F8OK_86zV5v_toZbgrrPHGh#W|0rQoE@z-k%Ft z1G9<~{m5Fp+A(N_t&1`bqbde`+24OIp7js@44(B5z7n-ai60&DVx_HtwlcIek7+ey zTFq#FpdMb(*m?%m<_Gv&_^$7tfBMt$$sc|;-t$Ax09+?m$#n+rRTOw{=A0}ts*Un< z7AVzj@nE0)AhOCw{tLf>yC$<2`E@~I%(0Dq2E}E@8kZX`_`=mfM|#C~Eao*~MSdVo zEmC@Y7}F^9Djb-Th$G!M-^J< zd>lwexyuE+$Y|+Tp2;u$l63jBN?d^bG0sJge!Nlo2LE(W=trFwILSx2oqs!h+@1>K z{mCHkt=<54Jqo)d0Is_BW?cRLTkwh7eiQ%eZ~MP_b5fh=+qf<#7;w-1`|%$?^O>A? zWo~x74P|a?3^k{i0~p_iI$rSh){P-?w8_xT%^I_ob5&61N5Ad^dt>o$`874}N^qL+ zY>;7d(>GlTj{j|FFc&r%tL^%!?gop3W1gH(OFl5i^0w5c@Ge$kDB1ka60M;*N5+{VCG>nSftLz`E5+*W}rOdgoYXA8iRlfw~u@?eEC26N`7s0 zYPjchp9>xJZNsW^XYy{g81Wh}0poknul~ia#mD_yF|8R4t}m}VwEV7lyEXD^M*CcV z6?@`aIR+qj02r~62db>zX5Ck5 zRpBb6Z0wl9dzyny25Vl0RzHa7q%d|`u+Tueq)z!(g))~pLFW^zyn~+yWP$E z!jEb+Japgy4jej+`>y$?`0|&$m?YB-6yJ66O5FMoLOz}pjW`XN%r+Gcf<@tAAWfmqvx<~3H1y9r;e60skL5oC zm<5FU5X(F#n*kpKcmlWdS)ZiIZQ;8vr#YX6J)$Qx&@WX6eeVdad+1v1Q6GJ{ELO;! znv8wqr+@U(aa?|mrOy`^2P_x1jeIu9A|L6)dAAXEX$IhZ1HuI?O0K;2roq1`<22}H z=qLXIjN{k*8ZBuIxOe~k`2AZyh8JoKz;Qxu(YJZVm8j{3RyZ;YcFmoQgl z@`7j>_gc`5h+W;E-6O>j&;o0QEbls@Qkl2h4~l|RNqXFH$4G4nJOJBEcm z>`8r2sVGU+&jXw#^G)PqTN&UphLPvF0K=KPhQ zNrg%jD4aMj7@qrMU*)-ufznz7y7T5X{xKN5BsN|dz~aH%RYh0>XQerQb9^H{&_H5c zyS>Kt){HiUjp9di81>iAiZ}63BU?S4!NvHc+d`U%HIGLF9I<6Qw*Dy^Fv=_RR+13; zG$~YKw&1#5^5G)kIWETz0GK$(g;w;El?ksDDNdgS!>9*!Z5@5)sb=&k<@H5Dzq8ZG zxAKX?O1{gn*j(&1A0O98dDHtAJ1Rir?Ho(uHV_R+e=4W;3kvlL|ArpqFT04->!W_a z{W}yOr;HP_mWybtC$26~&bp{zDEtb-1u96c{s4DJnsF|q?{fZq@)Pf%>u1AD{^?8c zmjC&hPSqaVps>q0X1YcLV0iEm?wTBW>p#QOUh;Q4{}%T~tmPc_xg~Ze;~euZ?T~YG z{-#~csHga4tZ+;0;7oERn#;j^;`DLl;51SMj%gDeHBW4$BA_Gv zur{NC4gwk{t6XQG{9-i6fix%5=ju~=edD^GJ=iYC8N%t1k0MW%4mf@M(}lC)?VsD2 z4YA;wU+c3H%0zJsU#!g=qt6uyK7J3Cu=?Adv#0bqf1X|5qJL-?zOW3TL*SQo>g+h( zeIUWfpo_YrB-*4;9+GKhFeJ_`4_*P7Ck8NyD0Y=M0Okk9q4ERom4v7R=`!}`sl=?~ zT2suDp0P`M<(tSI6W=}06LBR+^wi7wRat(;{F1n$O`VHfj!Wl@@&^-=)6Phq*Onh^Fc^H`+npxvb#hI#*d}VS7X3<@lJ}S^a~g_c4gE zIb9s|vceWnpFYQTWXt;i11EfGI?qMPRoC8(t3PlHuDbT-(6!D@z-N{Fzzmq@*}oB3 zd>c9!t!(pl$_KEJO#8pu2PK)%&p znDo$`%J~&1DAe1*qM$iVR9kgY85QC?l%W6sAOJ~3K~ypqtVG9R z2f>SU)iKgX+%-ALTS1aRoBF)rWJQS_bXp*yO~*NabII;x>h0EkKHyB{#0Ovzi)=2o zh=o=QY)w9FV-x@I+~p@HkuP*D{=&~QuY*_+__d$c!IOWIAH3c1dGGD;cJ(=)Y*g3g zqQmduvPJLhiBDN(fb^2xT(tYx)#Ak%PFNHkhFDO#B;@g27k)-HV-`S3bbXN_9Tf90 z#|2_hXCB!_RwQ8MEG5N0If`CB7#C6DhCI<5=$GQNPUoSp;*D}1ab;`p8FC(weocGw zkRQ@7)pwLNE{rNxfYdwn;CL^Jrh?NI5bF2i^l)5O*q+d@Iq?2^I3)W5h6n=P12~^W z$yL|hj4Q9bso2%`_cn7ZK6%?aU^9QDQO6IyFS~f~UWe1Wy!4&-lLKyf=w|^ib^(+JgK-g6JhBZ2kA24fvT$JB zR@{w+12IFsa!7sB7j6_Ok>rakhoE1`A`jY;vVi{KO!CH}AQG^pyiVt$U+P@=cuo}! zKwYF`e>x4FyFx<6eU*RjZ&+l=dP zd>wTWP8q{E1GH%Y#O>g&Egt6>uDbRnT>XKY`E_5H8FL~0%yVb6`qqZ^JY&6{{Rba; zETI_G*;Z}!3*8%cP2Twre-sT>FZ!@!IgUY{6UIY6ALmpHK8jV^*G%!zzWc>59`pqpD6Uh{`~_Qe1vHiRiB)33*)p%*`W1*g~u-Rd~F26uMvp8ALGrs?= zFQ(&0TLQcz`m5c#Fg)x4K>g9no1l1>3cnqJbfNYQ{-ok*9H{nvl&dnjPj zkr_JZF9?jPJelE4>^C5PrXJnm+6`&=R~>5OH7|WpHY$?EM`^cB?b()nl83w~JjQmD z%(yQ&fyV2C8MLW;xfhz+DlcynQK;>Il7~8y=9Y(kHsjGyVpaJ;amjcT?pQd?(^#a8 z3%}$QG$dgir$ zar(4&P3ryN2LtZi{{Y_kzx^0E=dy6B1#8aLzA&ic!#P&hbj`cuQ`h!7PV$4|pcX03 zk8b`NX(p=DPwr-=NGS|zk=LlIUS&%H`BFBVhp=mZwnwKG z&h_4}y&-6$AaMc}Bq`rugFGF)&o;ja`i_VAc%K%g#qNg3JQG%5q@1X5=oWs@0kv=# z7q4ajf_{A9*+rHT=a54d^oA~{NnW3LOX74Ml+o+l@o?-#vE%*BZ>b}!`h_w`-}_a5 zp-&Jvw2KYn0$KejJH{L>JLnJj$53@iJqCS>*1+yK>feMD2U4PNuKVz>#%qo1Zur%L zG4Khn?gRkm^3{zBlImm0uZ2kM=NzCPRCL9Tircfjy%W16Dn_(pGS4$!bj{zT+10KN zWVaBk;AYzc4?m29hYl}vZpH&v?C=_;h7C0i4AoB=Q`t62 zXfq--pEPhGk(7K=r%}$RF`(;~WOHE=ByMc($WTVo)j_NSO+K|qA&udUn?6$_y7LL4 zTRASmiQ!CqWZsL!^z&~0!7C{!!>Nb9 zkyee;J@v_ZwRJcE@8LECneZq$csRZqKDz(sGGTdQ>7IlLtmwKelu8aFqed(uygC3{y>u`M0TJj;@dOc(I&s?llD}3<7zncCj zoJiz^K@XgZg5;_X@Pn+czwRSx2a4}}9MZ2GXZpZWHP@@om(Se&cHH*Kw_~;9t`H9| zPSz|+RGciK7+P)a?gyV3aNqt1@E?Ei%8fcVAKy4OjMGM0-q%q5l7~3eVn=*9e%U7f z#v2spF*TfgcLFBDG6>E{6C9S6p@abd!&$0Q5yp!JU2|iUbh;fG8EYBPe0@{Ean37T z2pLagI1}ILcTHxT+OCO#*F^J$7(RC14$J#MbX@NeO%@%ZNqiDlf@i#m2N?52 zS7a3W`Y@!tl27rTJrEB^;sKz@*rYRMQb*Y=8u~+cnSc#I z7IOiEUq#als5*sRp-0LI|2Xelbl5)X(JXZjFogbHvEm@l$RA@tlbSm34LHIBsAJ;L z_S{9_heB8D6|TSG*NT6523b*OfCb4}7A3F0?jyMJ+M8nj0`hMj9K-bU!oQ`j+6)6e z^Z9qc0L&}v3KIQm=laU93bXJ2jqjrx6uCPvAt0UM*M8pr@WZ(EQ=i&Z=LX~hMx9&k z1Dmv_8xZp*{S$o}cO)+3nnxdxU3OULGf#7VD@TkrJy~#)H^2bOoI8c0+5r#-DFED9 z(bOa&Iw{TKgEFL(m#c1z3k)bobsAi!=Mh zgNT6l^6a!uT|6+fIUk;fek({ixjt-`d~YvkV_#pS@Iv>LPx`!coXR@*IBl@1C+q~!a+HuOGQCH;?@VE(4)`?mykuhV}UI6!tWn=;JWgt3uBe3 zJO-d7ZsAg9JuH&5<0>Z>eMVd7HteW~uERf!1%zFLUmRi`8To9$iN)G?4_2!+i<29FjX1@wpg)U(M4ubE>e`##H@*h?9MJk? z6Yq5_{mZs)3}T!bK7IGw183{#(cCR5MHtUsoSf3GNx23}mtW}KfcqYJ5cmJaZ|$Np zk8uY?b3=UOhp^oew_$g`$d^@hj%BG4HWWLPs_RX=6E*0mSYhCQ>!hrMDj;vtX>w_j zY7jLE0ECh(NDw-~^g-7P2*r*k4}`-v+&>Bq(d(QUv9O|iZFpI%)O*JR9G7hZ(V(1# z$Id*fEzaVB4gGBOc4G40d5Y4n#X~3ZNt+}<_bMhNH}8iwC-7$Sine9DX#QaT&t)7c z33;3^3I}A&#$pUWNv5ZV=M>T_Ph`xOq-M-Roh(BvY`rWvJsGAf>Rjb9ab_~?c({GF zC}2``9-wZtIr&ge#L+G0g@({b7xu_Mw5z!s(E*@cMw%)o{NcVAO@nZ#cPA_OQV+_k zdiXi~WNFB8pqg*A}BV**$+7-zXJs6dMq7GRJ8P%r^ppgH@%-*5*EN~(V4!N|V^;pEw6 zIbC2ntrI!8EKoj^0wu&h+*ft1vC;rDZ1_of0~(0uHc*vDR>Fbf#4S6CN?J8EqO(1~ z@eQt?^YXkfd(1Wx0^rZD*PpD9Jb_Bz_X!uU(0^{sA!9D0#~NT&){t)iGHFGY)AxDC zPeY!OeF7!m?%(<#p);UmlA$7nI7FeY^&hfxJ|QOQQXkp{a2yTgXX!o1n(GB0tBtb4 zcSIvjuSah)&*6_wANRj-rv9h+1IK?#AAtLO0}fF5(2m?KSySgJZEp>@?nA$VZ~5jQ zC7+=n;kRX^v!VNEQ=0L5R$tXMdGtEwPx}Li@g9B0I1r9d)32ZTlXoZ_eZ2V^XZF`O z^E_iV!)&t`E)ym+m;sZ2EoBKl!2x)Z$foF05M=xP4?Kv&*Zejvf9Z>oYneA;kC9IC zC?2x4i-TbdGmb2LHu^4eK=H3zYSRq3j0tdLfJzvd)j=dF$s<7kJ_r;FYk)C9hT(9f zU55BKLnsG11g=<>WWaA+$XycI|D=L5HTq0w&JfnXyatOCGX{INYob2&mo}K2NPUH) zsPk6-uXf3B9zjp!uw1-}<22$~`H4!nDEtdkUaPO#W$Y7;xs6U2ZK6fJFZ>H_*XDTg zCk)&*QG9xwQx@aA+ueXti;@vzT=d;`t`V0{W1jH9JON}5fiYn!7sY)iD|5#@5jPTE zkyrCfl_?%DVB8s!Oi)b#U=DMcl}0}4_>^3h40v=;t=`r`PO&TW5$)igZVXyyx9GHM z6#f-NU-6G*70l{a@oiT1&9NJC#EUr8aUshnD|So#m4<$JY`t2?E1eI1_*XDbb9^}U z%4=`JmDhcQc89N%cjvnb(UwdptRb>+!7TeIZt1r|$?2 zcI#`z@j9oG_QW8Blv&&Ef9N6ntAGEYPIrqqa8&hyb~Pa8NR?mb|2WP7@1r)TzAG{s zitj>(DvXAyPEHjNW%{yhm`th)b1)G`W-?-e2*nM~TS4Od4IWTDve=pN8$WiEA5J@0 zARV7HPzL3!;B4tn5mrF=yCxf6<@l;gd_&&*wVI+0zKHw%vM3q*H}wJJ94xaR1o1T? z6y&5S?dpqm(u>E53X;^V-aAgg+pVo@Hv^CfDi@zxWGH40Fi$v!Sili~m4t=`-)iiS z35&TUa*@mHiWFo1kRGY4k}LJ7IfyzAi>PF{%yCP8Q?a9msIkDRevv`Lj=QUQVReY1>`VarATjmeDSQZi$w1}pENmK1U5q*Zr+fWizyZ@ zIgxyt)fyob008ELnE#ra0E~S?AB&U;@L{$(Kvh`s+B6xWNb#cq!_$--=7Xmrc4R!T zvwEY!Z~D>i!v%q2-xC{(^Uo=qB0mo9nS;KaW5|Wq?_p|JpZ6ni{f4+07vmJeON;aJ zP9OmYz8yxidAsD8a}%Aq{W|n2G(1>DNM>iJc$ItNl4gO^wXr>l6g2_(gZ)>O*k*oc zKrEEq6BodE_MEY4f$Niya*5v?=`$(TMc5NyiUI)AJ1*(r%BRk`U{mKP3_{0DxRMz> zka9C|DwmP}!dA4v4SiikpJQp4@*PlQOjR$$(JXz}6+{JRA*=8OG!E&r?i~JceFmJc zyNpCG{M4i3RC{_pi8oJ%_q_X+u``mzNiNPS?V7AsSg+T?Cl1L#`gOU8F7*gL1#&L< zxh~kik{&7hGoO2V#-BNB=Wo#ftkx^qvRaj*q!uUsf!LY5MgS;2bS+AE`s*fT)wcT| zdy*!1(pFy#a;43NszcXd*v88PeD zW+*KIF058dqKV`H@*1}`FUac~x8;cqGV**IdcOICKe#sh8#-nNz0zUebN=Mv_kiO7 zT(H^C#Tb6Wd!Ca=B$RSif1xgP-Vg6ryW<8A-jTn+uPeW_O>MHb@O6Gf6QSry1IYfd zZ9}^s!LkNFP7q70TcjpqEkYlALDDsl?@3IkL(L`oZ=F@k;+Hw`yTY zU|*N3rH}UmK0c+dUzq5F@O@`b;shV0e_{MUXx%+n{?5mY)ed(}R;x9x{m^UDKW<+f z7<{+Tm7uY!>s#~j)uDyD?&5b)c;E5IZ}nJGyc)p&BaRG=)AcItWRb%A^~!(!)pt$S zYyQ5;cl^qCR;e2x1oY<(?e{uninA57-avZKKV|*t@2}zyI!!e zW70X_gQHnPh3-|1lrS)mJaFczudKwMIH_b5J5`cBw{yqO z!;Xo5bWOhq3xER^dR61{GH+6LZM*Nm2l3Cp|A#~Oh|8Fp^r`-=2qenK=rwHUHmg9n!r@1dlgd#14brfP>K{$8%nT<&V@QwI>Z(6ptWY__w|i* ze?meTnDYUr%=(m&&K$ZQT=T;W{jQ1Q8o+GeZI+Ap*ru!zWkYQ@mmyk5DHqNQf#3Ca z{CVxS!8Z_fTiQ1jB)aF}9h>ljmHUR|L1$(#(W))@n^;CajPb1njSH@jls^bsn3hEf z76~ef6weJnNmK<_^i+(wC+U+<%~jHId~hOA<~)@=6*S6YDX1!gZS+#%=p<@iKDi*NP1|HsAz5;WRGP zJAA#N&y%dy{uzvwc1_k;?P!;Q=j(2KH9Ft846b2)A)yG+JG~nG+Q`6l5kK#7K7s49d3Z;;?`#^|>V3ysIJBa+tM8-xuK+yuE94@W zIixN+W5UxmK5vw;l#D7p0Q3tw0Oq2QSra08FwbPZN*>HtDI;|bJ^%nO2H*=fM3ej% ze4K{V4n@YeGgEaM6Zob@J#i$9xU@TE#aIl`z&t({D=_Kn{*gY8)A_d87-L!Z#2B6m zPWX&Czx&Qt@Kw%g#o}bgcSd3-A-}Cs7j*3~eDEW$K|>ZGT~S*6JK7F|>*4wgbE3jX zKl>c}^qp^Gn9N}O%m$P=G9Y*<7r@|luCU{Wc2HJp|ICSwu0WwowB*?-+UcwtDbcsx z_uzxL<)>er{_*jSF-P%Tu{q{95N$BxHXv#RI>2!>7h;Yt{j1$H`4s>^W5Pf*0A6^& zg%|)IY)DX^ z=!^T>miJv7vW;wcW9D)L)nbuh%;pojpaGfWyClQ~NehNzK0gz}WE43faz{;ydKk(~ z(!FMZ=cG`WxSh^m_Nn;_a6T>*R+6*x>57!l)pe=%6kR1llDM!f?b6X~!=mmqd62G| za585%B(KqDHQ5WgLT1zlRvquL|719lyLEUAtDPM#&}G5ST?FnVXp!F8 zDLW_HO>rB$aT;ZX9)5P)i z=ZSs%yi(GT2mh+>nCNKaWHL@%SQaT7Fo1zti9YEYdpyT*;3#t#ysx$!@pwi#C!Yk-IZZTI z+=Aosh)M)qN0VsP?z-{N1)SDETZP`=D`75pxDv@uINku@*86@QitLR=iZvI)Jkk{@ zC^^J76e&*6B&jUID<-pkvB<=U(zcWt5IUyJj*rk{VRAl;MY-^aoXD&Qq)wqv_=Pg6 zA7UYJe)Y&5XyMYH1_ZyFe^Vb}QKM=r&*WQu6EeE7L>>i4i`HUM5BbEQ?{>$bJ)8&I zJ3H9kUSVf@2RnXVZF_Ozy9rU8tn|vqixNLR_@URN47Z~zM9N$V9M=O#|L}1C4)hi0 zjyvBHyS6-|>%Lv^ieu^gAZ;5B#lXtXC^COE&eQyUd%^p|;)o zzyrAd4gV~gt;~}ev)(7t80VPdj#EJMzu&z?$4hTjatsXeeM94pNj|;soIwC0`h*Qd z97~uoKt)O?#A-k)(J;B4^Vc^r{Ba9q1fdQE@4+X^43P^fsxTSY|IU+!xGlb2(g^FYzF zwi|zOwHZ~c3m{MvmZ2^L&EWUU3pE)jiv+!a$t-e+=y@o2)CCS;ev-_wD3HvA%t3YG z?w%6W2Fgqtgf7IWExa{u=&J8#fPdMAhcd)a)jqT$**xug8_Vy08y-3N=oz&MFUYw{vsh>BI z-;4h?>70G;*YFSh+l*tQqtf5;hi{D{B+teR)EMNU*a@u<-l(QmnD%QdfEPezvthAbIK6DgXSm4sVmmKo%4mzfY{~ORHTFthdBWN*rji7 zGbRd31~oCXGFb_(Y_m?0W=b%ra8yJdG@Ou>0R!Irqb~~Fvj*q+_yZ~axHaI>fi;i* z-_Y@6w_OvzrmGDwpkIU?H^w3RMjTkh5|C%`k7l;3$%I-P{JP`4xb_31O~nQmB$kmB z7x<+O$wMqq@Wk3+xb@!rHQ4@%DH9ox31;!SCYX7C33|3~${|25%1NJjWqc~DJh-T* zj*=OC0BkIFj5$rYvEzeG3i?3xH9*suM3?*k8D>rKB?=HfX20M1L!&3>HA{4>r|#u3|c z+zpz?4aD8pSH=ivm{>q05)plXx`56kKp?~Vc@fGZZYo?2qKtF;`X(IycmXTNML1(P z(XL6kE*ZmJt|Ve*D7XNVn0=(rGus`dv~_)OKF-705h_jKWLMnyb<#PFl>(yhm%wFT zGB@@6J=yj&L@aIOn!KD2V3q(fp7S9j7lQ8T+2FY&7enz-f{UkUr>TVjfJF{bj&el- zTNVYB!JIZOu$zk==DWygF0+CBnG1_Og+@A6y3sBj&C(9x#sgrw*6ZCMbCK0;4@tn2IiQ*lt}E&VP+#W?@~AOJ~3K~zBb1qZ;*H(goQ6ZbtD?!D)J z9NjvGqgzLD^vF?c9p1u`!&^9d8NuGNdLfcPKS^dLR^)$V-N zr>5HH_i3~EXB&AEIe7pE;)HI6SKT#%F%iL(sB$5QWCj%}H@T&Bk>=}Gk=cdLgpW+7` z18%?LEsTK~43VCa=Z6m*W(9s`8?nZEPy^YAm>+RuTl?U~No%Ny}?TgA!oURl9|>fnPAmHvX4* zKwZ2HO72QTOfY(g$)uFoB^Q8Yo{=E(VSedl_6tfa3Yd4A^(n7=r6j(F;6YvMLQA`- zD|&q*w|P5gm#mYn^}%i7pM{Jz$Vh*vSOKw|E^UPkvK&*zTD7_A(=5lJI~#L&^>42; z$E5mhGY+6};=_aCmXBOTTmUzG@JbwfpMIgZuN8bGRV01hA6!qF}6By4Z5;xia) zeIsXQ<)1@Q!Qx*b_Tr@Kqp?Y!rSB5Q1s7k zL;kD5ye~3fSZ}WxhXCds!{LL6ar`J>>*P0eSa{W)qV;->)oO)r`T74YC&Wf@<`NR1 z_Pl-53tk+(V~kW0<6P1>H(>Cc;=pT!GFLK2e2$uSI>m{{6a(ansCpMd1Nv=LXQUv5 zaUyp`im1I0*6@~p{C9BHU}MAigks>v<03rjF#h9y2B0JgUbEc{Fz_BA9?#j&8#t|a zd3*dVca9?-FfQPMQ+V|>f#FTwD}L%$pYsW_(2xCn(V>glpJdJ~*_eFgVoRH|NMbU; zt@r+1Nj90F82gzw$dJdT9Sjx_WicP}3}ujhEDCC#k<59jMG6o_it|WaQ)Vop1}J%w zt|njuZ3?R>->|StwxwOJ7m}ak^fF`*Z2`zDZKpk=73fMnlYeS{*0`t;;!bwl=i2ZeK5{kYX871Gp8yPqi1AZ}BL|MMIMHhzf5iZs zKxDtMUh%URJLTIuQLy;yj_YrHHL5?Pa|dC29RvBl;ZwJluLREq^Iu_I&ubPXv%zZd zvEgi9q`>+({DGf%Vc2W{R@*a<96rLGljHgMn)vs0eAk3KL%Z#o6d2pg|3tek4EOBc z-_2iv#y5AqvX33bG)GejW`VkSVP`3n;HQv{L+UWg2$Y=6o7eM|&qqcGfc*2<*8d}Ji+l6yy$A@Fm z0MtA~K9UYg8Fis-rcnWa%ul3GeVX$Cs;BUF8I9(HMZJTX>ps&vB$4gJL2mdfxWbQ z+3xphHsG2U`~Y71?sov)oI>au^S0R%$9ZS0IF9^l&Ij;0uKA}vXXBh~nz5HcG9a4) zhGg0$h`BFP>HtTP63RNS&VY;Xn1eeGjV&jK-HMq<1J;1;0Tb5jab(7%Vm?=m{dKW! zoakVHmU6?Vs!%~T9>2n>SHd%Rrb;69HouNI$ihR(%s{<^hdEBLF{mJER(0?DIvb@! z!=hg?X=VUGxp7JUo=PDe-irp{)_bnN*FXCe5%c|`&cC^M<|4K%Qu+mQo@PvQqipFo z6a^`x@-bhq$)}-oyX}3k(`bp2k95?-SR`Kl4?|7P{Q?K1KWkCJ!pQkp>XY=vW*Zmv z6UdT(tM8*&naIc7F1K~JVuh&HFU;{iX}~!&F6srxXNlPc9I^mBF5w4XY~(D^6r|qX zSYYeGQ5aYda3{}TzC>fYVe-PHO^t=Yv<4b*-G_e}-||gAK8z)8*L;!fIs?FMx4juK zs-hxf+zA0_IcKWJF9^yGnI*`Fa*WPs8%@Rb(G}2+B6A6Sl_1%`U;gR8f*XI~`fL`7 zz>IvZfJu$g=6A-nNJ(B)7(8|W)ZG#)M4qvu(tu`;(*Trf@@Jdmd4PghpR_==5#2ou zV7yWfl=1@`t&yp_75-zAx66(%%ByR7gzlYc#y z;UnCD2`t7MI2Ig`)FJ0C+m3JTpfz_=QI&Ws$o80FXYw2)RUE4$3K-k{zUXuPk6eN0 zU->gYG_j)GG^b#0-_SSuS2)M-nF<552AG^t z2bxbY8W1tBPahZINe-u5%slVuF)vRFE3hb;0H2}^Eb}?-hnYFgQXKgFR`nhUh8YAZ z6aXb0o}Jozn5g33&|$_aAGND!F~?}m%^G!w!L0eh?m9N<&ui|QP!F#_7@wg)a|MmI z`n~ZF$dI!5zi3@1;K`T#*aEn+01SU{?-lrq&whos(&CJL&mo>CWD1aZrC%WZDcl9+ zqL+O7U4?=BrpFIeLBCS9d)q)fMzJZ@5d5IjrAbAz~TQe10NKeX=@Gd`j`JU z#?hy4jECAc!PbFe-fuAM3)-D&CwENB&Pi))v<6&%!>jT9Z+uzs$@!9gx$P5g2EZ6) zG2#VDd{(6FmU#R&P+vtp|MqRvbqXG>Q3hICl18WX#||IofP1SB0N5j>?G^sZpL#xS zdgbr)ULrg2#!fkd@<=7R_Qsp={4(AE(42PM-FU{eH{yoReiA^#^Iq~pWIgpbm5&}4 z$G#dzQg3cE_QDx*aylb3`K@CxS&IsZY&^k^!G?xrOtON%_nv=^xBmCvnGF}=BAf_L zw~zr~-|+Osu8GyCw#Iqg02m7d1uEw>JL~I5Z}GpT8ShuwB22)bgaSr%UYCU)Xw(ms zWdQgJ#c`S!CqU}sdcy#qB)FkZ$wcyX89JC`0T#HVr*zlN)KP8F9qB%H&lULkXa7uD zv=sZIMZbgLyRotXZeuQ{#0?qnqM%#+@_|WwHWxb$dYA$>>`r*TlXU{fvZYU(VzZ`Dz-T-(Wx%)M5#j{`jLyLI>0AKup8vpj!HVJY$dA=MT(Se9=Jfar8vzQd?)b#MYss(1ndtE z=|^Et8NT=hUlvhqU{ENy48{b9KBRelg2WVk;QU8}S)pmpThZx}Y5=;|J)YoT&PQ?N zea(;&u42c?+nEI}479}=cDKNb*dG>U7wI^Ce#r+NK(=gp z&~ZfpztSSSEOyAJq2w!VYq*EbOivysc*;pLpD;cQ^P*z>= z7qVIlpGC3a2^~QAjzvM@5E7?h)ThNb3LDlJE94{p4TVs*ecys#=;$AQ-Pu{eFvD6I zrh0a_1Lj3c#poe+PRs!80}Pm^9ZY*#_hDGR((!AL? z>DSOlehDbP@AMH4&-29BMO|MElI-eb2}iq^JKH;$G2`+}FK3tR5FT&K*q<1Vmd~6R z0BgXsXBuARpqzp0Kl~~@|C?SOKL6y$t|^5_{!m)|JW2hWN%Nuvv5Q(_=f^d(c1~Qf$hvmw7Xs4S*L-N8k9* z@$Bb)r3eCU`0Q-}+6K0|;+^Kyk-OgoST9MrC1XC%C`I<(I+;|E&~whfiwaif_1&t` z4h%(#WstxSlXPYVya*TJaSeC>&YzA=Cxx$i=~qRpXq(rg)ME`ySZGXe3KfrL6(?qZ z5+9k7X%sHj^B%t%90f-zlLdzX0E;2NLA>hnd*uY$ARrE7e%K9gG_{QjF1u;u__>PZ zuEH|Eu-M`9psc*$>Ek&3ABKJmtIibyXR`8Yr`pxyXvgIE_BPh@8XtV`?oakC zu*cTjfyD`zXfwl}JrmlVhH08uL`?8kJAhXW`28DRjpu*U%kjxSxW-@AA9js` zz8TCva7v5m8%CtA)#}$~n7yFUj!E}fuv3Js14k*N9}XDyVA|fnH~jcF;-jzP&c+`?YnWxK$r_vGgx5V{c z_O@{BIN@kaGHQS`sN1LpA+0eH#3c^3x;hPPe(yvap)lE7Wp+%-0x6rT4Z ze-8NpSTKpTk`B!igkLlEJYhI4k#hlJJD3>70|1&AB+b03;1hu|8}IX5I{-kQAOHL> z;On3LGia?}n3zMHkCh^2EDAO}44tUh$^4ReCW;HtcJWSG`S}rnilpGwYq2%1IPzge)J!s>cEc z82mxq^VJehn1R*K3dfHg$Ig1?3w=S6{duwQ-HBl9z;WkeXsuyVVKROYc3Q`)olo4t zq9k@n%I8RO*CdM+Gb0}~v>8C_3l#ZY*Fdlhf$-8(h}s)6DOf3N0Oy%`0Bz{^q~)Fd*K!ebl!m%c4n>MXLoRyx zFocw4D8)`L+y>dI0%W%*eV${%?-uUm53VuaIUg{kSlKiNsfWhEi$FUCoEbQ}bqrfuN3p%L zO}+vH$B~z33`Y+f57$n6C%)40&zek8m~b9F^J!m#RTM~GoOodpJ0!7-LY&qb;&shr z^sE;snCRnXY5)^>~N_KJ}x6vacC$DWyxjo$@svP1xRCik+JFZh~%*V$$}_9 zQ<7L?k;8>Q?Ln0_=2jmh+SB`PQ?WAoby=(o`fT{zNGtxSg_#6#N>n|1dI0IOX0X|C z=+I#tI&cVo;p@H*pT6zRf?Bi}ZMK{o3i46kIhm%$S32ceI{F~&bH3!uv0kr-VkED% zJJSDV3D6p%Ai>;#2{7=!&Fn`5tX?;u!C*FFHuGygnn6FNXoDRmg*9;G&=!C)uFcSn zx3k(cdF?Oi)s5lhZ+rv(*0=pt-Wd?DpGQ!E1#fT0Ga_a{98GIg>zWx)*oE^|Ji%2ktWS9LqI+RG)%=uj3aqS&pqYIU`%mW4xOfq;l@m8Bq~1Ikc>Ao3pX z!F#-S@BO~-oQ(b>A~GX$@AKX7zCoz27x$jC&(4gDDI+6d=icXhMnC{jGvv4sBR~Kc zW*>wmO+koIQ4$aJZ}9j6MX(^m5ImY(s>(O{t_z~pmlkI5{KdP9m%HGoJ5=f5KqGJ6!aKiF`^qViC6y^HsuIEF4 z`*(cc{U3xFrk056c{*m8wapqDT)b%?u-h8bYO-(XO!mWTpYf^pYda`* zn9Iu~<~M@vycD%vl)5>n2$A{PXTXXQr(btX)QXdA!z%$jgbTM^h~;Q)zCEXvbEh7yA>@aMJ(D@c;o;v*(AD1EwGs5=Y?%NXbZY75?{3b z5}5HA7lMF|OII=9^s3)798nu13Sj_Ie(`B{6)v|M_RqM_#7r!J-p0N&7HsZ<+YWE~ z!MBY~JoZJ8@&%I$7VI3bz+2~39H*T(P(QQxcYN@RsB6yGHB_GC&PCg4 z)#_6DD%@}q9p_z>XNnWEA6!exIC!?(5=N^Q5JAVscu4~h)6&Hhi0flAi$vmYyDjZc z2{d`eVkJPJ#obPyUgu7aZU-dfF*#-hk^gxSi>>1>R2=W#` zzt6$JA*!$~#xZSLeb-K$N=sQmp+Ko_^Q!F1HIj*)79SeZYm2oSBsKr%rHsZ;zjO z=!1CUjW=;&TUbxR-X!cz6MK_z)!v$`_x3oyUhz41JIjCf`>!9V-31PR_&skPoA{ES z{`|TwH0CUVQJurO0)6fKdWv~e0ZkVy(IvxrjCEc5^*Bq$Qz|YTT72naQR9{4OQKW1 zt}K8h(N=(@0wVEh1ywH>m?e~W${NuS_lMTS z>D+K2*{sIlo=(Q;WzJSvz{&DB&TC)u+HT$e)b)+_#p5{W3gOlpF0$TRvs$ls^rIij zW)nq;7bL1}*Cg+f%x>X$Iu&iZCKE{ae2KII#jeI}{zMS}Tz5rlKA!-`&&5Hem&e&+&F2}X zUyv2Yw|Tp^Du?yWWTTgPb1UdOH0$U*A5i z;V@tG#<-4Ak4v$hm*iTKZKRjS_2cPc>s+2bHmPXE7oCz^S+7J}rRvbj(`a{n3Ac_< zICD9AoGecrN00NP&-v=QyeEPEjc=J~Tffmip5I5T> z+dTe?&*9_sb6XP(YXGt5UF@}DwVIeF+sRqU%07$m%P;)*qxe?@duR4IefAV5PVVu& zkNJ84zUTX%ivllx!4Fm2e&5f(w2nD0)Y~}@BEVvCLUaOf{)^A_HWFbqt?XM{PDEOwm6W_yHmA5-R)fXnz41+=CJNu z_`2+`)QwN2BXL_pY|TTBA&pnWDQBSmBF8f4VgFH_CN1uTTt>s_Wqm}a6hpM?KE|>b z6B>cb=scz@PnCldXPWM!@r@#vHN{rZKJJA)V=QYs;>|w&4v6dQ_6Qhv#H~@l>U$AhowJa$U76cC%EX#zqGGd75{+dC5| zHh*4+*zF$oI6+Yr>a84AZ?j@9c1~_D99}p?1b*;~zp+~Gg)i{kk=sy=90>}DX*UCP z`v9eE5&ZXL@=I5!!glNVQSGSUMT#O@kgxyKU)QLpSxBtAuTZcy+_^g(>&tDGp6`J4 zJQ6)`CvftEaWi_%+8D;DxLyN~?urGId43fmkjB(;DETTR%V2HuD;%81Ojd1jz4U3% z$kY5^2gLIwqPycs3Q98=`mQ~7ngifal`U!oi7KFo+6%RSfSN-G2tHpxy@ElQM0m{YH=YaojbTlCOIkah}$}YHd3utCc;F8@nc} z$#zUuGVv=fdc~;19Qr`l!HfRUcl8ax_x=3Ok4?83X1gW;G6+$@>;Q23aVHS5T_Krl z$7FB4MpnX`e&HpEV!q@)h6;+J5`QZvD(OUAoO*wSlB#2@O1B+{z5*B2m}0vUc>(0B zpvJ?Ty3&h<;2_ZLPV@l+P@^^SXuB;^Wgf2x;_58V{K#W=btYz)rn_JHs31zx-E7wM zF&;+AWY70%zxCCxH1hV=bd2%drzkId>L=N)quUuz?sw)4A1j5AYpKPE0-}ZP0kT@0 zC}21}2*sj84H7ECYi|5FulwjNeEB`@#?6O^y!@dL=OO3L@c2JI>emwQg5$#f^Yw2S zoA{jH|AD$T@>&THr27AOHQzAu?!>Sjp!(gBYaan+So_91)_H)f>-Jn);7a9{WcC=) zVw!-u%mc6Zm;JTIkVSn$XOB}gw)XKQVp!HW!z}(fT~o+l>7n6FM>5WUYXp{YVujAv zXoKjp<58GLKpOTH|Ky4G?VA#XiF#quZrQZLBo`=(7PX=T+Yk}3+cc$po(k}J?pzF?)Sayjw?ov0tJrqFl^ZtWKy#e6Ns=< zHS@(SoGoU9V!k3iy#Dg*QZlxMAk~rkMW2fl2>a%3!bte`Zm_gS1n{MgSzKFSnTlNr z_*h7bz6QLsupO`Q=ohU?W4N`baGART!Y6+$y2@nRyGFo{Ex`41Wf+T;s=Ju(N-BsV zz^L&MT&JY_x4!0;ot}iA*hF`2qAXpliV8pbWuN$6nyXKp<|ErJhgg6x`>PcsMcHb> zc1sH4+pef0YJOKu9aHNC2_mRWY{Yg=bej0B>u=(N7cO%1-LK*1!ws+dyf5G>Z@TOG zlRE-_;P+oQ^FE%MgYQb(k&G60-$ngYpqNr z6(GY8t_|@nVgQ)m!R3krJC3)Wb8JP;Uak0YSv7Q~$}{SoLEOZnJn{F=>{E^%-CLC2TgET99NvsV7`+0e>xLqq4wc9)3zh$C`2xWpvjlQTNcrg+$@mK~sb=n0le3tE!DDeGv zazWC4b=W}xTT$Wb85C65|IZutzHntbCb&(sAiCl)-|`&(@aKM}`WWtqDj)^cZO6dq zAvr<%!ggnkm(rYb+%dpNfcA@Ic4dG~|5&Ye&vH0!<*>7588{q9bPUdi#?q__>6DXyy*=(&SiI?w^bU&h2tO)6zEh!49-IDPw57*Lm zPbRuu6De_2>aCoD7c1%`0i_ltdAEe2IJumDBfRh*ednECjLaeF)io`&1A$+*1^arn zAY${fnpP#^(b|>2G*5YOe5GIV#>2@cIX%mYre5J|Z`Z;}Du+D$b-NbZRhu~v! z`lLB}zX(vn~A2-(Cmzy(cRX{@^iR$m9Qb{-EbwVDJNPc%8YC#@h8Y5`I6dT?o%?@t{v) z6G-69*IE}tDzC=Fii6-~FTslS;L)u$F-H~5C$eu_LFKNjh8624>tj4TUZ#X!<4xl* z$e4C8nA>h#mw?MbieX3CcaWzqhzz&ImnhUSufT~p2)RlZ8^PvaCTl>#F}s=?+Y-uD zwbC&)jYOhI!qT2Vu5hZv^|%+BOGy-@Uf{>+5+gS^|Z?&wrC9} zaBX234g-&VA*i_t0<%EZuI7yf&$G{La5KLxezB>_M(ty$eL@iRl&DV78 z7k}Ov(xgw|rs9G`XCVlI7##_nn;ua}Knnt6ffxa+X=J|8evvm9J4Qs4Z(J!{bMhp& z{!#I7-ubhEo~!b1>p$sX~MPc$w&N zWT(MXpZPcr5AAu9-9<@!Rx3h=f}|9+9dn(SybOtA1VAVV?RHMVV-57rmrR0qQDU1e zit5xCBmOEt-9-`QpMB3)@o!%Ao6d1JeD5oMnu$|m^CuJ(_bYoFdVx1Vd9?4E6npMx zr3KLyTh#nbLDkl$iji86jx1#7w7zk-1VV(iIP!uNwZ#fXudl%8313uF;*Q0pRn5j@ z3_|8Ca~>@SDO4vPb*;%*182@7f}?@S)e?4-OSCOWB?n+XIuAjrTz$amueb!mZ$UBX z<1pH+%7qX?G3${7UNs8L^yrEmpM^fC8qW&hFjoK!Ulht+=D-{Kkb{7VK3oL1^ zbXmVnv6l_mwj_E%x?b`EL<%Ou3EiB4GT5@TkHa(&CfKgD8-h8D7LQs92F+2!+}Zx1xq@%2!dCctSx&XsLa{lRP#$$=k8k+YyYuW%yAQu`-Az`5x;+rPD6t1z z?O1nCfHVK{x!oLn*Ppz~ERf_>umGjX+itvx8x9Wn$o>Ino}b{@Y*g1sn&+4U^M$2( zrrIv-+0hBT9D+`n)NyQ#@k(?Y*T$HSRXTNC^T|4wKxh~KTj_*PB>!p&&SZ@-9Ub?H z<+Wnaco`i4s$RH^#$Uzc2^f764I756Chr(u%q~=2!sg8`O?Q*1*`@Iap2kPiu$t|S zm->9=SB*2tc8!aD8t13iJ>)SxLawlZ$z${0%j2VC4%|L8SZYh~q-T5qhlhv$+ORp?)X({~If%XuhX)%D54LuGu;K8) z@V8t4_RiLenQi)9qL}GlT3>*AlhlAnwo z*dh@~JPM-i_EA<(r$YYQH(&wKzW+olvShTY={RoUm`wXOK%U`x;JfvyjVhoKDrp>G&83%LSc5XBSr#NLPSiU+Zo* zyi(ZL!nQI}^G0`S0Y2yCnn&LKDjt5-IiB+u@8|rf6A023Cv`hD&i&{}rdPTRwUUV< zZWjy`qw!x3l^ZVYbN#6k{L}yKhxs?p`{CLA1Rbq1XthV!M_ipCE~I%*^V4kSbQacH zrQ=w^Bi5ebxUN1fWHmpGT|R>tSA2ap|HXMV{?*5)fsQsGZ^_;1gi0=JM<>gp0_62* zcC9uHGGza?qm2h7ESLN@jw;nIeSB$hW#cwK>E|0`gD%)U#=ruv zYwxUKZM#9~1akP2r+prWhll+$CB=)25(R}lqn6KO2c>>P$9FZR6=Xh2-6KKhpEL0< z-q_}do2xwxYj(1SVOv}Nse`x>l3bL?l@%tRXlT18%k#6VeY-78E1RcnDXiSjRehbi zPoZ{PkIBnSWQ510cXWVhi$&Cvh1+`!Fu6#H1z-h8TZm(EAc2#DSjcPy3~NjTU2q*` z_5fibSg13Mw)mG5wt3~4u#J#_Qf0G6-?n}$5h7WnTV-7kYCKv1Pxe)_uaW~^uX{+O z__(g%G`R%RN5u+G($lvVJdK0##MH?&3bToITA4FJ5w+83^?2 zAQd}C!2D(`{$>TR(#><&P_!u^v^f}k!+4oIwJ4a9zZe&%(|lra&HA`8!5NOxSuS>r zzFQ^HDddX>sdP-%r36{MQ?vgDFQ+3~=c2Q#$BD4Z=$j#zi4gV?mY(GZi{+%*#hH}w zn_kU!eX$O>{G;te1>A3&K4H7jr|i(#H^seJGeVl@rMYxya=6TX{>C4BlFM$z1x6P0 zRiGJDCCMelL#y)~7rkj5dKu&Z(Io+-`|SD|QOUefB5l{M_Te)?~XvIJaJLD@rX&5(x0Q z=T36%i4)xYHRhrJ5EQN-;KV37$;#|vqs;1s_7N7E+w`~XIM|-vO-!% zl~?$S@qor-T$iS`ucPdnwE(d=BTlO66Gczhg``ies&W(j?C? z2R#HYofT9jtNrWsa=U1Z=cbRX**E{#ldJqHSRo;R+Pb#SCbuZ4IZ>SxTl+o^ zZAqgmQd$9`qR6({K=Gs6@0V;!x`1(;&OT=tVnVw& zw{4Ww+H+T%3KZ&YNjz&}Gc++x)+)f{pX@hHXtAgs75jsAR(RCkG4WR`N=)5j?U*Q4 z)^g8_OxK_8y8m7w$udItmA`d;V?n8t*&uwgU_MpEzm*ft4mjnu=*Pt?_<|uV77v^T zO&D{03oKp2$JmTTMP9UxhxZKu0syBsUTJlS$*3hBjhE>x*ec6GC5WVhAFHw_uEYKR5%tF{V^vQGJMalvX!RH2(bvDl$;nE$(ZI$Oz zq1xn1U~8U8AAtO`z&%c%^ZqTFx;ui@9T9uBM7&^WN5Z}uzS$@9NhS`1*ANUo zet5{icFRH8RvRhi6C)keEnIym{I2#rDd;$wkbElqMU4l@pSJ{G(4s1v@9^q^&^}77Gw5)L^fo9`ub) z;E)Os-B4p_WnELJp z3qrNF!1)BhD_;G2p7pdRxg1$&0|lhL)%H_wKgs4%?KzX8wsTdg|3Ub#e=DagvX+dU z18!tp2uA)7ZNaSW82G|63QjHs2url#$j(dVJZ!x2aLF4Fr&n>%mIxwHxg5bmgv7)9 zJeYtu59+uUsODEgQ4Ea1H5^uHF#q- z^>GGjJaW=&`nYYJM>j5gJRT@7ht4-e#pu^5GM;n)hi<;f^WDpe z1dnV(JBm6EZLN)ZHBQ*Lv;)%3flD$LR6!RMQFznyY4IxkukNm=iaWUNfcd5+^=sM4$U4RV_s`jlqBO@fAlGt zyvyg?)M~7y#p8-oMD4Q|7p=&!@ucFS{(TVzWd}~NzP8)Z=DmAhRn&JoTvnQol*G&E zHCY3<@)Gr;$8b*WOgBM{<(o0VF4cY=m)cZ>3~wA$QX)}ZK=B`+f6G*HisLw zhpxM?@62MmuN=sso%B-)fYyBx071M!=^li|ZHB$#!PuauqW*k|T5(boophlxUzuyPczjr+nu(^O~Q28Ro0a@9bMST~NGCFq3AHSdi7>eT?8v zE*6|cB?Ti}NH`9pEz%muotJ}P6Wz<>0ne*e2t6!7EEfkBXclk9sHvtb@TgLlb-)8& zP1ybJL`c3^;3?odEU_h-)dH%`Knsdvuou*16~`I%>Fmo)pH}QNSF&--SL*Self>-< zWm4>U4>4WscSeK{UEJqESDoYL&6YPl>5)ABKfT=r5c@9{1caE2bd18oWvvOn2*32i zul1S9vl*bATdz2?n)vmfe4bf9{AHY{>f+`wMpjxHvCMRD^OPQ?|*ofIh^1H4#?J`$bQ7r55f=@q;#M@N*ceeJGZx5W4!bfL}Q zA3W?c*}u5YHWejx2V}do*T!~34mTUhzVq|(#;O-+Q398JCko+QjmK}yT0?IuuGJWI|ylc{dVpd3J?Ts5h^JARco}iRf zD^8|TeKXcI_%kLj4~j$BD&n<5Q6mxFM`h zoOC>V0r?mOLZ2}K)Z!rgj;wQ{r#ayHxWpp@mf^VVhCtVfFAcG)(Yqq0f?(_0aa=Cr z;I?ynjs>1zrZM{(|55j#6muXt*T*r~Kj)vbY+yVqb)7gAiy?nB{ZS!q1$gr~${%g2~V+ZrC#y{j~ zRx$mRd9kjEPUp9Ntm~laGZcYUAJYhiwN{#3V6tYMHeXqHJG}J;Pw`+FI$~qQ=2pcL zZC7O27Iy^i?y{1rq>sY1O6Q7pC z(dJ?SImBJdj{Z5L$I3(n(FxS-I8DV06xwYa5Q+$*dckX9^#< z&);!!d!iPXr4+Urw{zM$wDnwn`L!7pY+?7w*c>h})N#6?m;#?D*aUf>2U&wO$&h#$ zJqwt@#lYn0ukMToeUYLq2&wb1LoZ{$BV+U;&^6q#fS3omoS1@V<6-)QT%e}82F~*C zL}=dvxXlGn^)cJI%-VDp+WgpC*2iU)z?s{uD^gtM(Pm{*UiP?uY;xR2m{9x5un4gr z)n7$~Kf3;AuG-t<#I)jPAO2vTaj$E6)PH=d=_mdoUZHo=E!qB8pYTt)r6?QcAgTz8 z{kNjzVW(Eyz6i#R6|27WXCwCMDF;AADm)B81N2g?;%5;W#a{a;vT7ByF z&9`>*-E1f>htX;Bg#UJH-?eMSf!hFaeawbJS_jpC-MAIz`T&Ub`UH=+zThd=XT-@# zv=tD39B~v&u#s@LZN8KpINpak9fFsp2Su%~UvpWve2in4PF?Xj8jChBF^}Ce@$4yMkN?d1Mc_f_u|%DF0k2b*c@)zYz{fx z-=gunN*WhrGpQ&_`nGg3z~(|hWcJ&%9oUVXl0LQHH_1CD@tKKQl+@cf-HY~nCh2us z;FD(8M4<(??gRz48!IsFd7$=;Ntx^cSewh{|NgCY3Dn|X46+$!0aIv>X z3>yR|peC6z>F~ADWV;~%hYbKw3+N+{I&aRW>Yi}Kc_@NKn6Pi?(?3_yulDr$3gM6y zE8P&cZvYUnrLW3|fLH$Q4Lto*?0J)0Hd|iy*$?D7@BA;mgW?HS?1;E7p$G7)C;u}( zvMpTF!U4YUJs6;hQm~)BFMP(ib9~zAle{N<(|y7w$?ZG%gec7h>R939)fa5M!8lLN zzSXDd{LbkJpgwnfu`|pQzH_0?Pv-OO39aHZUPeV0{I}6Tv})D)Z5z(pMLJm?r$;JMMqZU(;0%RGEkHt_gi{1?ojV+DgU<+u7kn>O z{FMciIEXj5Z|3A81+g~43Y1!KF#%FA3}^Nr0-%N!j^3_@a*<+-r4}9)*N)I+F10uqcMR+$WO*Vux?Ip4 z(8smJJ?wJm5)T6lzeca|@Sx~pK@*QY05>7bpqCbTggF8@@j?iI&LwZOe#J6{wv z$cLS~An&QE^6;y19|H=CZB&f&9lTZ2e7o-93xONDCo^0rY^OET_Arr9Kqx9OO?(9S zzdm#$Pr2rR8_u8QZ!aD2UH88a_c?ij`<^<<_q_i@)xH8C!grrP%g=nxxA5<;znM!| zQFDl*T2NIc|K&&3g8e<7!oyFmc`ZW@EsYMFOf2&>k z3xfc&gN=K~3%(RlC_+n+D)RzDBVd`|nSADf5{Y)$orv|NLRUbPK?aLgP6w@tmLXAx z9aXd|WK{eXjCz~XG2EtC^tD?s&MV2&=tN)BHDs+k9H)rs8anqfo5Hov>1CW&fFwFu zo{YnQL$h5G*I7-UjXz&vjs7uUJa;&CV$Fq*U$Sn0tEh=g^k*_*^V*Nea%@mIytt(; zuHg@Qu3(jRO;BG0Ix#8s4dJO+fr6k+?SWTSN>Qf5j-te0okmv%pESEBU=twi!(Z&Z zo7LfDamH`UwFj3A+o|wn&-q4P`>%cpXuE~=mtR2 zprYXmF$bv#bZ4oyOLN#2$r?VMeL>nhC78`q_(;WQ<2pYs!}A`)TCtJ+Y?eu950RQm zX^pvE!hiq#`A>Cu6m^2?<5a*BB`&-3i^Q03ZxA?6XV{&nh5b_Euk@^spi|}0;}tS4 zD?>7_yd-39vK~dJ%Df~`nd@Bj$i+e8r5Mgpg+tI;EI|BNiwj(ijH66xW z9FxuL5`S*uIK{!K6RimFVVHK_mT0v8)aD3%TRJ%|6ga%NsXMR$9zrXu5W5A9(BIav zyjSqu646$06ovTg=N1R-vx@1fsyzFF=bF}!{i1A{j;dnI_0SEQPKuAGt~e(!y~e8zah*f*CA*ry z0#2__O#Rk882*@;aN8Lp4+10c9)OdnK%{NWHwDTZpxHyiO2iI{miN?9(2sP28 zPA4V)ZYQPosnau@pi>KvAz|Y(H$99_Epn1RGdh8D1Rayd?Vj~=Il9C@@RBkVAVW+k zxa|Tb=^VB%l6WYnF5^^Q1CF|VqX3Cyp$g-lrSEaX3IxZ=&zE#CG)7UP>8+w@Ltk!d zCzjkDM8sKVk5PH@$_uy_L;}0;>L;Nug}ElYjJw+<*Ajjw{tZL1tzb@2G5XyCmBKgx{e8l6}JFtB&sG+2=RtI4L3dQqUQE9JJ9H ze7y6Qx{j2;dj8X0Jh23$rl(?ZMS!q#moF#UNapHv^X>$Wqu888qV>L@r_hpm!HXZa zcj8qG86%GsQO?8SS(Rf}q*Q&3M@PryiG&?;1pm%1u5*<&$=LDo@m1!GLzDS9I~ZFo zgQFS8_0c9z;Ismw={(Y@_7Nb)g!KK5xY0^N6$6M{)5b{H?g~Ehje=S%0f}<#}LEw*+B`RU;BYS;Ka#2&^?<=Rg@Lm&6eWDN!-dQt0+vwX!vnGUOi{KjDa(fb2eJH z^H79Zq@-X!ULCgL0MKP#qdZ`XA>)888lw=r^3IHyqUuFT<#9v+p9=Z6D8fEQzUh;B zbVZ5^iY5G}b1S|;jf%#CZgy=6&R!Dos94hJ0-e$5aRxg(PH($LZ`ZB2&<9K(NDJon z!Y(>{C(-?Vv(2O^Ti;flu&)LSMIfHhu>aDLPz~EEsA3Pg+H)o1zJ|iLzWtSmPN-_N zeOE{?*(KS&u7e6@`_3o)7%zqDOHo|JSaF|F(ppJA9yY7|V}9#A3-5ZtKcpsn#nyM1 zSn&1f2{PRtX}3UZzWe7qqE8`zd&GGe4^8xp$8idoz-=-&Itv{L^uAQg3v6`xY8bm1m@raWsyG)9rF3IvIy7 zkD6YFTj*7(eB2K4Xt(FBxB$(Hd6->8FHq-! zkFx@1yb6zFwrkU?-7*ujj;pqt6<=w7DsJdy`Y_YSv-7Z?vnei!R1t%H!a zxQ~zXC9a2x;k@Vh&oFmZBX53W2#VTtd7mXXy7gH^ZCoV3ZAv=Pr{0!|6$RrV&Li&_ zU`ONKOQSEd(^YU?3*DA)bX3%S?* zzjt?$61-gYCXXeZ8g#TJ3$R6qxXjM0D|-CZ$>Y3gkzx`7%!;qd1G9a$Ysh6dO`qT~ zD^fzEuwB@!+76p1A@T6Ouz~9Xbn~N~7IzNlDKAhi?zcvPX{%@$=_s&uP<`<^fBM^< z-&8xNd7f0dg&yFjhCiQzG0>0mmLpFqDCD)i z3(6%pWP=dsiJ-trtQdg_#EO!F#R)A1EncLkwqj-S^Bu1%f@A7&XF<8=rFPPZu59KBhvF!)&*UN1boCFNsHo zqXKGje7Ia?&kF=d`*`xi%4Krd9C9w6_iOFXW1t`BEsGy_Lf|ABJMjK;Nja=Pz?Qy7 z?BiBU6e(E5D*lX#-6po5DUkW?oUuSD<||iRn1IQdbhxu`=X}}!@)*|ZHLLZC-+b9? z#%{9l%b{}1O;(&J%I5H-@0$2Su-k(DQAT?R7N~1wjT$WxsmZD<5{dwVc`>wGFhepm zEL5Q-0;d-a>^P8kNxNTfYsz^ENuEf?w(}4&gYmllZ(h_*(1$Xge4cxAnEZed2SPZ?oXz zgMZ##z;VQr#kljZL?@EgP^5J76i_mqrgIU~xyo8`)A|~CNf{D>-jB8iM@xt25E}er%p}Q61xW)sZjhht^VLjEv!|7FfHz(+Jbc*5DtNEl) z<`MEaCtFB9E_ehqy@Z!P?vDLN&QUPoyv65F{4;Uh=Cyg;Fb4~T;`?6o6=p*e6`PMD zw8Z9pHpbh!XC}a;gZpVP&p;!UnyaHETo}i;XeOcBzZkbNV+Vlx|nqFOB zSI|%sQYS~1x!I+|@$s{r2WdJ-GCt}#k0J^hJQI!%SL!3LVGaT(Y!~ZH)p=>GZ)$u| zF=MMLTgNf}Jx=41csb6|U#A77*|%glDvsONJ422R$H(KiCEK^+4mf$90!PJ#HYU5| z0mtzvc0H?*litT_yM+h z69+^lD@s(&=km)76(;7pCjR=W=RAeq{F&Dc^p61wCLDLzuE|5b@>4l+YLD$^%QQ{M zRFKJvlC2+)VLT{s>&Gr~{_1mV5B-lXcF&pkG5k*aTREcA5_P(Ic0Jvx(;|;CKv<)V zFSG=iZjX?Ql$1=}@x}EE9!2K2NATpMlXBhrUgT{F^5oQn-^(tvg;n*bg;6RX5YQ~o zeES}RcfIY00pY<9|KDqIAlO1T9=(0rZG@oGWUD&+7{dsib5UUdLDc8KZ8zGr6;=)l zJn}plKbxn#qf_nFDBU)=0s{7&tLYAAg?RSW$`fz*0_A-#{&GYif~-QdQ^DJ6pCPDF z1Gx&r?uL5e2Aq!3Y<(8UeJ&E6?gzm{>(0X!(BwnEpF3*^Z>o;DGz)^8upvhkS zZN!Q3g>+QI)d6R)i>N!d3Q-hPIW!8qUY!42jHh=T^n^DT3b2J5eH_HzCZLG)&zIy~lkpX|aHMa)&7jOgyHc3$ zx6d{Ayo%H3PqR7PFs&!1!^sO1`(uSg3Ni`5^?xkf%E{tqSyVaPKV);b@vjZ1uMGQ+ z3EF&W?bo2@98R>}vT%n(G$ja7Ji!)mT&Ye*N*j?xEzwP&RNP&+M~u2bT=f=d$@IGS zFBBvTAU5NGHW^dmwuEiGR85}lHh_--(+KDi{=47$BgqjI)UDVR5dE%1vv2SS`?fjk zc8`*M!Ss80yG6#-=LLFamvdn>#WM)Ag4-Bv{K8`rA zZoJD{W0A|sf~*xM7NFxOCwX>l16%>xtEB z#deyQRuj`C{NAs>sUIA0l-NFj`xp1w9Gv!D6MGQ0?wAzi3E%j2yy4~l78QTSB(9nl zpJ#|bos$GbcWyjq_^o>R^8EZ`)&h`K%{6*4%TB+r<B;`9Ds(buoG4_*gxk@{vJN<6Zxk_q*`-gGb%ulEXv49237KmngkDLW zTugQ{$N1nGyd1ZaN3lGR4`W&0_0oe2qY@n|r%-7uw>7O;Iw1u=A zV102_PvBq~$f_P^7@DkP6wY)6m+Ef%2}m z_$~>RM4&;^BNr)QU%M?b*LiN=wtEy~DkIW3*Jr8FD^uxy2{&vI+NkI4L93V>wMUF!;L;sVYXY665B3b zffHBHmDfPWlE+-0oKT7yujTOyNuE4D<5aus;z84c&^gmtz)@PFtveS-;8b1<@|Yc> z&-6iMxj1n9lI+`f^f;By5>Db}{y$IVO)tRp8pl^Xa9MXI{=qH-ub=~3h1|sp=SAo> z@=^h}UDZq7E{Z{>vmi6BGqOx4ZsVLdVc(~~sQFv|Y`zpQ!&zwWd*-LN4{igotrsWI zc1#?mRp|?nC_M7#`eN~X8FsbrhI02?nQk~cYdaDYC~(7vZsLXy-PGEaV6lC%5ft)d!O+1Jihjh?{FzRSB=#w&tPcPpmy$~!4eulO9Oi9hb>l&SD{FM6iw;JT=Q z5?g~=iaQ5#(E+sEH{Q^S6nNi#-hvjJ6UAh$f%}SfFl;>9bZ&hOrzRgUkH|O$g*5+J zo{A$0C)qc~OF3c(LmQ3?#J4RvoY38Ig4cFIjuEFgkM^8M!f{@KQ|G@CW(YXFKtx)Sf;swD&#p)7u7j z0FezG?%SUrt>hHSTG9A5Q=R7D%t^Z@#IY+*?f}^axNI>Juv*dC=88qxF(5S`Ts&ZN zV1-HBG4XHbl!7RT`Y*qZI|pE$M%0ddf@=>;3d(rPqR3FB)bXDD%21@J(TXH|-TPlq z$1Q>lh?DdgP!sw3_rHjHe%kj`xmu9|ns~K@9lA@^y-4BRZ+>obmc&ZYnk1tIHGA}$ zAIH90Wjv03Ef{&Aa#*|-}ZJqVjo|l;2%30ho@%BkkU#I5Fjb4Ss<4WMjqU1fd zj4eGp@NQ$v5wIlm2+D<9E^zMZv-9Un{Du2ex9>J9Qv6u2!qu$PZbul7WeG1boLnT~ zBE#(w@5|4$L^2iN`uEwhB)im{&re{^dw`ngmu^3ag3@Hnx<^6MQJ=BWdZGj`*T(~b z1w2Q%oP80Wm~7w4Sv319ZeN}FyPx=K(?f7xEqEw?^z(w_h+13s@TUi}kxncaZYS&@ z@NM_MH~;PV}}tbR6v|{^FzTJ@gtU5(gt1>}z|6GjH#Eb~jxfZo{CZAu={J(1Z8^Hrv51Wxcx?DQX2s z9H(dpm4^3>Z+#-a{nFnX81<(e93kI5_HLyy_bP^-`qa$EK-gVjB#Sw?ic%Z z&iI^(?=n698@`4=_~n1se)%e61tBW0@%Bi&{nX%HZ}|~-kgnO*qC-C-38Mn)yBxVlNxJv)^!ClSVy^q~ zXORPh3LZ>cFHuh#-nVT*#Blrh3%k_>=kjXTXFL>zI9Cr;Ch{Ey_db1wi>mye*Wbie z?Wz$qPWH$549_ekyy=z=Z@P7#Nrdyjm)`R#{`A}}eDT@Se8;Vi<-5-OArzA}>{In| zor^1}I>*Tgg=D*GpA^Vd0k`XjW8)f*I#2s0P`CX8PM0jom3+gbm&fLeR2*C%bvjudm)V9MEwhCd;}OR~FEc<4jPY@cY9`OZ_?cap z7Y?0$ciArJ1Wu+S%9QBfI%krrmHw$UPi2R55=W8$*|U*l=iHrA^;5nSE_o z71WMVsNls(*M5{NBfRYZMEIhIe=hGl@kS;E^?5RrHea~=LlhOb@h=7%$AMg=06g$~ zf1Y8*Y7I*ztPEB?yeF1^-Xt!V-$Hz1bYNGFNj~xA1cb9%7?Q&oS zXL#^vZjDwD885SqO;jwbeEaF$f8w`o6r0%$%E)lW(d^{A901ziin;D5o{5-`5CLkQ zAe&QlbAktmg0&s>fMA+kLX*dMV4mplf zq=a3Jp#aUNIvnS5w9Ib1E0Jg575d=3sytOzbF;<1bi{2(mNm-}I7jN-)9HRptsaxk zPG_3YF&;kQO0Nf5~$3avq85cK{+R+T*edX%j6)E|s0`o3MO!0>m1m(iE_-o6>Qn<7&Tq=c& z+rpFXaWxxN9{l>Za6{R0h`PNCQmh~;#Xeh`+a{Puye4!KN|}&pLS^Dt|L&ta_STDB z++M?X>Ld8cYv1Z)O`izTsu1=a9|8?~%o1c79s=EgV+o)oi~M!kT{ts3+6tM9gM!aR zP6Q^8>x0u#pYOmKbgtt`@`Q~C9-3uVp92-pg(T`SXL&{*CbRLVI8~m6(-eRtPsb~8 z{8$)pf{xpl%kgsDkrz$4{H=jqkX zwJ7o6s}{Q7G@&Re%E@g!9nLQF;Df*#W0ruclRO%F~7Uv zFP(0J)y4B{wEzldE{xrmDN^P_A70#WxPMrmGs(Ae)E|Idj}KM~rjVw|tUC_AYXWJp z__*<5(gvou(e#~z+gXqt4L(!;arO)ctxcuyt~Wi`d@V1oEWVR#RJBfZW)-k47CcN} zOBTC+fUDv-EfuvOPt1XhZ{wW1`nejfF3}F(Lcw`;;{&(gfqlTK<88i6$cPIH-0S2i zwyHe%^>4;qHubh+oE6|e3tLrC+aalrShncag;7l3OhB-oah3uTbs=vjMVa_dH(%tS z814U69>I(6_BPDkrdN&GRd>K`S1_Gx;B_2Z#7E;S(Wwt_`mq)VOLRnyM zIQ06|@dJ*iFeX~E9Adnd0)87FLaOq|x|$F$3#Gn}k*q9ez^8R$$(uk!*< zI|>9X@ne7j5AY&If%gs%TmIEps9a^opf`gZ9XpyUT(1Avpv$zpO%F&(R?TnKi;{Th z!pn;kl-hRgQJ2_%^J)J(JiC}SgTL=XALgMC_)J@RQ}4fZp2Wu3pF6Qro99qLxb`7h z*VT<)q-f@U>uGH57dzhVu=PP-q(Gbh0S+$ZUxDqvHVpn3i9F2?yBj8)Eo_?cD^56W zGfya3142Do|407%#r9#z&LfOYennstPdd0OT);#xvH*#uk#T)+OuMrBx=c5!rCf;<*KG&Z3IOo?Zp11cXzVFnZ_-;q` z!y)Oo4RD;8gX4X}q_BXHDoiix?sMM}tA=Q(&`c0mjW{K40mJT|_Q zImTz4UGgZX>l5;T+b7|G>+HNhdpSzN2{~r6I*to4$`LYOo{sf(IGK*)RQo#49eSI^ zak8vSII%T<6wXMe_04fY=CN-^M|zwJ3U%?P=KA?F&$W~HE__?$ul#p20v_arN=IyW z)13#-Y>DU4RyJEJO15^~9WH`&eI~@gZB3^Lr<`Rf{J> zMYN!5#fg-H6y^ILa9^JPH`j5E!rQOAk$eAt?7e%qZP``O`5Wt;bKlQ;g(Q_K@~R>v zKw@4b@(hW}r=XGGCy0P;2j~{X#ug>r%1aW(M*tt7el4#+V@nX)rcsHp0Ye#HVyFtl z$~%>$lB&9OZ`FOAd(PQw^dDo)xyGDpuC>>`_g2gw8Qsh?z-goB<9Z`Elq3}!RF7o7~hk4SGLmV6_eNLOP zih}#xQ!%M7ZpJXQPcBAA!i9~+&aQWBpT?_L>^;7@j~x5Js0UK5u~B^ zoMWc?GGhlu#IOApBQts==!xQ|x8-kx_!<(_Ne}uQJ4fK5sn0pwUnMG&-we5tQu&jl zRoo~$*iUcgDE&%{L}a*Q!f?!YOH!RZNA2eDwg~&h0^Aa-zp}fF9=7pGYkLlI<2uUB z%$&N|_hUCxfijVVbG zj(y{%A3oqJYPUCEed|g7(F>lQtr_o)v6?@Nb1Q(dwrGfeqY_Ep=Oea4RsTa9hR zu4ba178B|U#_g!%YY9Lq8;)pT>bN9krfw0_ff+i()OuuGXGd$_($nSh22-&GBbA=W z1h#CCy)Dq_%U)RHnDN=(Z&PCwS5g4?FLC|L}7Wp8EW|c>lkBKYP-F_Eldl zc{2hK&YV2S#Ul@o$53qEtQ}`p8JVjeJ#5!uayW0ACTE}DvpY9tEjhmr=l8kvfeV~^ z=1pwxY}?P@Z*9@}8o!-gQy@(ou|#CJlLA&lz5`N!m{hU$2x!;y$b0`@(hQUd$dvfd zdQW71*CTxo7hmyw6w6rAhBb=W9nRyJTeU-zV)gbxC+I~68Lw+G3Zpv)GTgpTjMj`=2Cd0Gm(`9f3UcYnQ0O9DguVNPws@xzCB-}W{S?yoqyvtx;-&q5LSiWht)Z~m|E zLVRQZRF$`%zr^#;oaBKkyR42Szc7DLdTb2xt4u70Dg&bo<0BeB02}o9IMuIK=kBDW z>k2dt=)eSfQXi8xOEpf(p43;g$NMVmaXqN{gZD+FFFbbmTo~ICn8~;}Gvjm7*QB1( z*3k!X8_W_twLQ^}5z~!*j!f6mBMm(@)2K^FfoyY|33kXdBY_<;oicf6$9*p5Z(^oC z&wyf)3gGhsAk3 z2KYGjr1pt5hRDSBiBZh9I=IX(RXW`Umc7WN#oPEYhl)GGZjW@OwxGcxE&?em;! zBvx6E*Cjmy+2(Wyr0I`!nDwN->}d0U)K|1e1qqvIvN6cm` zd*f^JWGEI+@y>kV&T}n%{q*29FU}oZueeX)B#ua~slc)131BB>)2eOmLhMi5hM~b! z*Xh5LPZzI>^!G2iV4|K0X6R!L6YA2!ImaZQvmG+SNNH==VrKjqG2U0FkgxrTzM>wx z9?QORlWMal?5XZ@;EbT7w_s`}*&6GT&30){%)9U^yxt(Q&tZeCeZKiQX7j_pc?eGK zxj_@M_HIgYXhRt_^Ma~;wSPdr+VO84+Wyn`N~iX`ykmluspdN+h@BsK(Johh>nK|Y z2X!YSvHIPsd%j{;bDj7)xrl0G$-uq;$|# zD-GvmmF8!~nJSuI(U&z@dbJ+w*x|?gGvm(0xF#lF*eE$kNCVwqMjKOaiBXz(Q_N`7 z>G5-8?)(4BZ{%zwz0Xh}>k#4a))t4hwnAMgc2@+yTT-3l&k@M)ziJ*EP__l0dF)8_ z)m_P(I#jU>3?3IeE_T|8ogaHb>@w?PnHV9o_eji*fw{59R!mfACOcv_XQp9Cp(pq} z(v#vcp=b0H_|ayux9%qOEa|JZb&6>uAjz&tJuVrn-_rhqy#_qU~{tV`!V#U{4;y&f7eG7zF71 z@z+&rDUy`>H#V+SQlQGGN%~#rkK2bh^Z%O@6B1Qa2?>doZ{_$y)ZE+_;XoT5HRST{ zF8#4_eDfH10y34X=j1{gu4!3lV;bgEHSg-VSuV~&z8jMQbXj^*nW--G|>d|!92g>WAI9V&AiV#|P!1sYG zUiiQtei`q*w97|#ud)Kp>}+#(XPdA5FYnAB1dDIyARr?A!tZ~8-jsUWO?_%v%*xEfW)bVmFRM#) zbT$DA_QVZ8Zx(h^_7&{uR6qxPwawAibj)hg#?)Y<&7JE>eGEQR%)ddB9^k?bd67DE zkcsWxldKdJg`@s$koq?^rVx8Pzj$l2!rDmcYXNFC9&{Oza>48REW|*OF4bx9B2&g3~V_O?~YG#eC znF;KO>DI?()R*|0sE`)$*w>VvJn>zRFzA_2K*q64_Ow6e5%frDM@&R)sS;?-yUzXb zb&6ugDBZ);N1BOy!>ge z;BA-Rnq#=`ZI(O+d3->BxB7fZ-M1Q_ueB-xN%Ic|CePm~Mh0fP_(3oo7S~>UGNd}+ z*mnNZ4M<3$lyRLERm!^@Ya1)K-Eo|cfAC_)^2ATg!ntV+`m*}<{lt0&4r1)oFSW+2&(XN z4?WH;hYxdN`wIWhi|^u9_uL040^EBA?!B^yoO>J*;R{Y3;WJO441MI-%_dDT z*_rUC*mNp?s&OvHfC+mNBZJRNBvaYnRDit|gj(4t|GY|lH|3eFM8(@Ikb}M6J_MWZ zm{bo>K=Pwk$O#H)ul#|pdKGW`&0l5M^ipt&t_peR`~`OR_CgG&$EkUjLyWo7{Co)( ze}yxjdjL9&rNE%aF3!<)y$R!Q5Ug9X&#=9s`B$} zo1ahFR5Vrc+!*ySDzh{D=tG?xgo`c3cY=w(TACRjr~1NPlB9sz=GE9JRo&L2z4fI- z#5R_F*`izRBO<8Bz6Vg{|9;?M`lrI>RnJeq@NQms>IBdD_rGRw^xr2D5&q-(6~BDp z3QyTN%wv7eWmOL7QSC`TYi~c)*7JvMI?m7E`B57I_c{4K6+64o0+Z@8+t3qY2apSE zhQN&MXy~ciuVY63G?-8qKMRuls+lD|TYP4iiP&jN&zipC{2ejAxB}U)y8S3F(q=mE zYf?|=wzlTW+=gB2shIc!XLFlztta=Tc5K<4eNKIaF_`*{fhc+NO7?)orDUvq>?<*| zcXn(*bCQz!$o`1?9QqnD0H|R}vVPb)e3Vr_*WsZ7N>4BBfcBg@evI=wAvt+Zc>c_j z6df#)eeySRdVgrI_7n9f5m9?+kKHxtEiR~%5|*Pc++}z6r|F|u{gta%>9>b*Y9%0` zqhvBNDQbm;_=);>OMASBA6hNPdnvP>-f}>PjfrHWACLa&>4aotd?BgUW>9$+L|cWM z$U+4*rtUiySOhc&suqWfs#@V7H-GJmc<9G|(_@ImH!U!6fJkw625l!P-aa$plYwEN z(*i|bB^hBXi-j{Rc&-1##QM5EIdb#_SFSvc#mT;z-}gNab{#+R;fHy}nd3Zg>Nvmq z*d>1O`Oo3RR>!tBryEvEsP%-ta(o*Mkn5H(!+cr8=u*b&Rx;d1onkCL6Sfu` z(*)ys(!5J^s>Mv*d1&<|41LzGxUxSpec2@|#IL9peW{r-)xMYUZR!?N+tYW1O(Gq= zAwnww$vy{LQb#^zyUeGhpZUKFHN6my&q>RE= zXHinO36iSz_g0`3AN&*bNO*jQi_MP=V}SZ5O4&Z^QD-FR@%Cw;6;nP+QWBe(G=V9Z z_q^?xdvtAb_|PG)=!$OD(|2Y^x*M`mMZ1o-JbID0p1({d!t;)7b8=^g163Zpvd;s( z+0>)-e&O2xMr?OVy1s{gh4irhMerB(on~(GC&nfC1YI-Et z)6`Sj;|vV`qy@OeCO>J8neT{7O_~gFa)=n8EAjNf^`ttTMNy+{+$I=-l z>`9Dh_N!)MU$s4T0+Pn5u-O@*j7ur5AJ{p3l>8CcVlz!;)pLKGIChM)cfg~+AL?6D z|3`$)M8!HuiOJ-TtofRj&pW`+?Ko)Mkq-NMKqVf(D34#d1jj9Qbpn#cvL5doUs3vd zPougT;#b5`?Vl$y^<0q9#}pIon7XdF``!#vwAciXfAvd>G0f5$>^T`tB5QAd&Ag&& z#4iusZAsNMFZa_G0AZzqo7vpoH3-*AH7OLn^Q_lpWiJ90$V_i#6vZLJCz%YT<{1x}Py4GS+ zFt)5H8I%?lFlARHwh0Ov(AN4C^330)b zw~5WdIvo0H#aCTVJx)XNWbN~wQ^lq4{1(5?#3jpMPm&(0ElRDRC``{4P|lt_&WCQf zpHAWIXINh<7K7GrZ4?p{m1i6uR12)GDyw5X_9JULPD)Z)eb+?QO&)B%_!+mueILl@ zrKC8ReEi&_Y~SYN)R@0>Z=s07RqaWaZYc^1XJ2xFDtS%}iEErM)}pwMlr~e@p`vu! z+gC%i7gXWcSHFbgU-c65VAhWtDvJ8#xG~bwV?FrO5-BPM(eS6GvF68O^($jPF0m^iv8T9YxN~+w%Mj0>w+FiJwkV&Q=oZ%WCqywzZC3QY(rl=~Z8q&FM~vJ*<7QxfS2UB)hb-dCq5z4Bkg* zA6cty_?(y!7ilnkr-0j9#ibRynmPO8J$Uw$sl{5_-$~K@2Y|DuP7QGl$G(G`?zZ3C zxaqEpPs@8R!TbE7zzX^U`({o|Lh?i7gItD^%L}of56204?D7>pXX|A-W_d15T>CZS zCR8#MPU<1(iDRkOd=cYy+v%-_6JPTp>$PFhb&0Yzkk+FuEXL0_m{-}TwYX)GfM`B% zv;$Dh_Oywiw60H^jwtz+wO!!;%Ayxk#;<7eBA*P`#w!a^(zh(kIcGxVJNj%&voEa9 z`_j;tCZ{%(UG-$7S5QS%dC}1wUVZzm{Pp`kW^uZ5)q*8`4q83zqU8S7pR2;w$s>I8 zowxGgE4!@HhrD83qCY7vipMp?F2y%6wVpY~44Yy41x%%9sH+%lG1f;@PxUHj-7DJR zXmz8$idW%+FvO?stBBo9U-c{Fnu$KQ?1>+IE$d2qV9?Wd>2n^&=1ti&P1-7oO;^{G z+Gi%+4G6KzebmX@j6ab{~P;(Ouilyqk#KW$Ij-T@aTm*Oj!~cp00lzCzR#>;P|nlJc{?#T7SjH;OyPjRCK|e zl=oc%V3$iAdf9FE)!|;zzDG5Fbr^eYPxAA;eg?6~fEhj|R(Iv4^?=VoDDZ`l`o!0n=#@C%@*#{gixD=HeJ+@Lm0EVZ%zaYWHbRKUwIU z(pt~kLPiCxqO#=4f9{3Y-3I}+1DftoRd+d5ns-U;u8Y00F7A?q_VJF))LoL^M%_$Q zfqt;Z>JmFJ8ho+BmW<0+?o`sy3B2^yv;4{1pOhIrdKf)&7(IL#J#-ijuwPya58Ven z06BgXJ#iF0aRfPW1O+bduh>1X&+#OiNB;OR`=jIdj^Z+@XM*V#Ff)2IG7PrP=_wed z8A1GFZ`e`wRkyGE5~X;BJuvfjSYK8rNspMGWJi5hA?wL?#-{e^*t%oTvM1SWZM}}F zucXJNwl&8v?Zlz4q7TTMY0ba_e;E9U@wG&R=`jG3UxUqN$FR{llkAC1XPbm11%XNS zWG2}YnG_d>x;lof*bTOt_3H77%()A9^62?HIrmtVlvL6KaSgW)>`;{DqjJ-!6FhM9 z{T3e$W8h<$Vwe1}I!jW{+zn^%8WNT%dF-B#^YB|gO!1aZp4kJf!9qn*j)JmD_wkgHp`dJi>M1I<4lPJo;femu~HjCNe7lHsio4w6ax7VS4T+Hyk>IJ4wKqmV_ zSt}KCXscsz*2-Rf@(52pdW5_F<2yJi8Zh_sk0$%uUTz;=Qtax4 zGc({xj_Ispmp4j}@7dEIU{dniIa7BNv@Fy72Y|DuPFkNHpXg6aQU)gYjM)l9l5*y5 z``>n3^5DPwAP@YT_wv3s|F<$3iHV3>Ies_g3%~aE+)M%p#rB6J)kb;dr3YbdHuR)Es`;B4jA4?oy5*j_r2bovSy>AVqEN)6C2OiHvDBip zv#mkVTvZZnKFMUQUu?aA-KDSzH+7dI*Y);9eTO%kZ?v_RB8ccMNc7H-5S{1@f<$L9 zjBcWLB0`iw^cgjJ@4by~^xg^4Men`b`Q7#1_5A_wS~KsvpL5Q$&pu~2*Bgx2XJx+l zcPj0AJ4Q=oafXOvVLWORN9Fk@#hQDB)wXHRjM=-2ohjtDy1l$O9r(AkwZFB!Vnpu` zTVkzb`yH0=WLLEIMaMst0V%>&dQslU3}eYnHleJe2j~Nsa;s&!d4@DcnoDw_Nkm6@ z&FzEd-M{meCNus}*nFuO;y{jG1@l+RAYf%RoD_}U9ldQtPlB=LE|ZjS6~}Zczf7Kc zWeDgIn)|Qz#348^|6yy{85;||R=wVXMEA7WA7V2m+3Z92t0FQ`Ppa}Dnpkh^OB@m^ zv?)lclls!55z102fJfyXyJ{q(KvHX3_F#Cq@9aS~*6fXz3fsiWaSiCw2Kj*Z!1Whg zV2wZYu;(0~olOaO3dSi?QR<&-%Jx*)5PzIGJS|Ocj>47WLUzwe?d*Z-qPC7`Mb% zQhuBCXnuW!qNg4zU_Mowy6BItr_J=xk&Y^g@M4%yHn$OXjp0v*XV)GB3X8)a7TkQy zGvno^i}{KQ7A(e;JA3~=eG;_gz?{cbG=9_kIF?!e*PgtnQx6U5b{z2`ksjdbyfH)J z#d~9&CmrZ}(MCt%mLw=2X`|Ce9`?KwdnE?MmiY_+_Ad6P$4y0N`=)1lB40^R6$Je} zU#CZELHb;IR>>frbh>y)?wV^Laz666LacbL)R4mtcY6F-un`{#?(>~+M0)N8;y9@A zfb$ksrz7)%#V)hJcTc8GaAHzt58D>%=4tq@D<}HfE6NFb-znh9fXg#)hCo1HS}%Nm z+E2+a35@Xin*2;OrNB%_;JbhbpDasvY5UodpCBgDAu>rT29A4ODtT@3t!P|VE9J_X zLjW5p>>G3Hy%q45hkf|`PSr62nEI8PViRlgnTWr_#?s@O`&HTFS>5%%{|QYR>{o2e z&Hn3ToLvk5sZ(Je9!SYT79`3%IhG$Bx#-Ta{EL@4-U?14tu9!YCe)*QIs{LFXRmWf zXqG2O;p%Dm(pJjv&hKDass7d7WR{VBtF@uj4?9IXo~ocs~j{ zF=IRLYSfR??92bn?ah&$9{Q}QoDOH{{>oy>^_U-e-#k*9Z2qrYC7WbrwVSNH4gQq zY0%@NsU-~gWHrE35(yd_AIOoVHCE+2o_{Yj%wxobF>!q-ojL#D7Jym%|9Ueb=hDLf zJbZ9ktSo@k&*RY3gIq28>W(+d=R3UEK{7wb{}A6M%b7Np6K>_HzigEz-a2f=F%OGw z%dUSO5FLPM%sNSaJeu*my4o@`*_UhERRAaG%*qrSQxWQBoRY=3eRo zXh1=e^>mkcqp<`{5|Ac0pO);WrD7K(=9wP3W?bNEE~*(q%}EP-6nC+?p+hE{AZGNC zL13l(6!P?s_mRCOp~90-(i&bYlmx)ZHC)9TV-Y{Q%lDo2X}wk#n9ikax{ULT9QnHL zEgX+G8K_r8JY{i@r`;nfp6GY32fZ)J)MGueAE^(VX}y%@*$ortqKfvjQ0lYk#*1<| zc6Rq!?7^9T__6jHO!2+jHLPIm`ygMy`2?U!8g#tXukg@YO-I8k{hd}%y(OdT!qidK zTkC>#QgsepWYO?`@rl(CseOOVHP}@;F;}fGRVdiL ztqxluT_Z6$^EXvsMnmfWMHmiC z_Y6Y7-Fr_~_>t#UYJb7+2Hdy9web9QEIq;FJO@o3m^y)$k5R4MjLYYz%L>+#}F4nVuPqd^V)$0Cp2M^z_gvE;zOLW+-a} zGi9?I&&bc`aNf4osrq2$eaSb5F$heK8Gc{{gYevXSgCd>T=p2ZHKabn=edgM51}%8 zuA;r|X%^JaTX^-M53`;R6Eac9PwG-P_oL`mT-}%3qf5c=Q>PgNO?P*lQKv#nr$p#o z`8Mtf)t~@Y=J89hd*P848Lz`$Nb(HbvPke$2X2I%#J~1)MNuACP)i)BtXSxKN90F! z6DEhR5)z>mv9)x|@Vzgp-V@T#4D!y}UL{UihKJV98x|t3BGz#~kVp3a7Y^EW5RJ1rX93u6M*+ zVjd;T_@bTPdRjx|KJAx!#VkeyZzVjRIUN}++5B8;(2uVa#>z7AIyEsL{M?3vHfZi9 zJ&}3vqvwu*`ti!8#HMA~%_JfwW0$;XxV#dn`@_k}1lv5zf`mbU-HHRq0}+?#as5-B zmZaA}7i6c-dvsh!pVvGplz90VJ+e08f2{MVg0opChhinG@UG|?y zqg%&h8_a?!-F@(}WR6P{RB|=%`gU?HZtKTo{HzF%8*A6RdQZ2nF?G}D#3(v?S8-YY zLL7$kc(`F6DfrjD((yj|-GOptmf5d14|?ugx&eVa$u^K*H|T4YD)KTyQo=6R!Pc)d zLuv|7RV_V4l3B7vV*NU^nrdg=3|AnMSK^RCtM~Tc+23EU*PIX%aJF2AUTk{*dM>M8 zPS7LvH)eJ*yob(RWRT8eA8bCo9a2Ts`ZD->>*4u>CDlaZbZyJbZXyGpMjn~qYOUh@ ze(<=PFR>hZ?29k*LhiwpZBcjDmpYf9W&-t1U60kdQy3v;StaMXIs(9&=N3J@KjQK0 zBB$sB<17!aV<@VgEIL^wpWZ$bYpxr)4kX(meCe@g(#;8#7>J(=TlSrzU$1)jv+$}t z2A`;!TOMYHy@gE7H_je;JhoHr7WCfa)9jbpPo>|_ey4m}=JsLxM z^}Qm$ppsCcDCo9|Njb`1%{QV)sz;(u@O$4?m_URqU`OdaS@keyH!vVYKWFOzmC2rhp5e&^8w>1+>k_WeWy?$ARU$ zhm7)9-SfFyop1X(i>`U!G@qPHO1WQZom?9jUvf9Ej`(nnmVdGHd3U~WDFxvQL8Bcb$dT-!)bOuJ9)bSdwj$V*sJRXm>{03fu*WTCZ6{|?ehck3C zjKgR*JM(#|puqRw0NVu;p*SDYqA%0P&fk`?L-o~Dg?V-nxXG_n6C>(sUb~+-)?zn^G+zd{#{Jm_vnB=XAV9JIxpho`w2-fEw(73@i8>I z9)Qv9thgJVj?r&_zVHEKJ5#uZsPi3I`$=wvDd*P<0q%?CV~fr$nPR=~KUY7JE?pTd zuT~KC`u{v-KIxx%#Q5WUi1T;z;1x#H*=0m&mf6zV>e3oHpe0ZMz}}-N=m=H=T5F~X zT@S6A$1HF`UmNv5;jiX>A5@iTX5??r?X5#7wSOtS`lJ9uB0zyAfL>K25MA;oc_jc= zfq)0;;S%lQHVM1nxpcYMR{Wd1 zfC*ytg!=#z;(XszG&k~E%D%C$Rsyw*Wv*XjNlbSM@w{srRbWJSHhH)&VsU#@MnZ`! zJ3d6fL+jTg+&FkjS#Jbp0arXbxs9OrvBV(|pB5h0SNOmSR)D}mHSsSqwT6-sSt6>a zi5?(zo(-32$RB-qtTn%>7~|KNE1{B6;qXF=B=o+7hp`Nv-oFY*3Z(g{6I`>exyqVh z**Lctd(tak*`ap_t%mWGx1&1@t6OE>L-ecXlBb7^)y9Kj4hLy?&$qNMF$E7--={~i zY|iMTAD#B+RFEE`akq)6Ki-wqKuvd^a;Q$Y_`cHlyn|;c=X+;h#Ce36eW_J5vf-}S+s^m`?s8=-d#6nbFnN@1&?aU-#R&` z(5b%)``+d>)q!^jYIWU9JKsQFXADqr|Io+d3j@zS)x1|nbzjA%>C&vKs6Ka>>VFt2 z%^2qSrcC%36TiZC*P5G8g(P#;0R#Err^Md&1CDnOMeXlZAgA?CkGp-$P;^0T?w$@f z(5|~q3&V*%a0Ipq)z8i|rV^Di{W(K`3MkluhT+kHA}*M6IT0NYORm0hRWnji^_SmX zMdUlnd*xwFxukM357FtL zM`Sd-fXdbi;)jDj2q@|E#uf+v;&-mE5yAD=34S9ib`Yv z``PE?2%uVwui8+AbZWM_I6qxpZ+&<7hbdrM@)FAGq5tg89w6)%t>m1Mv<7f<2F9g6 zD=R0I;^IZDmg2r(Qy1|TiHncfqBS9J5-(|>qAb4_Ff%vV@ob8_j+DI6!8~M<=-UTs zo>a&Vt`>hqZ*H$|i4X|s&Z(uUaEv^yi&QZ`Nz(!h5R>R=i?_Uu=>UtAeQByQnDn6} zr&3c<3MicWEX)x;Uiz5wc%FmyF7)yY>!Np>|{#yfqn3qep^zwvtRH?R05m~YJSByq1amNpWLzNMg`ukZN7wI+^N1KG= zT_r#3u8%Fnf@b`V-kNxDTBeS4cJp5?!v4tmoI;4sWpnkjF*OZO8H`XfRH8&l6oPyW zWAZ)wl#6%|Y`>RgH2pO}NPry6wbt!x)NeJZO7T!4=4(TNg0gxz0bar_BRPf3Ad`1C zQDO)06Y4?3J^om;t>b_IL>P1;y#esNPkPs$JOWm%M4DP~tGxD^HMZkbqKGi%km#|9 zkoZLnyx@jyD;ywC0E{#4-xe-a;P0;--L}&x8cH19Ob2mHwSt&~ z%=OAuXq;o;8yTZyYUN9}d;bmsY$j^*D79!mF{x;F9^&+=SQza59dCA$V^P(I1lcHh z0@f#e7sD@~Jwzm(ahC9kRd$yh+IOl2M4ID$@V;YaUE{$9_V~ zh28kh#(+{!#V3|eFq04LMPo+JKgQ3yD;p1fO|K!MXd*G&XI+c2Gooxt>T~BaB(##) zB$z==QnXJysZsHtfgW-GW0~cMwj!wW~<^V}02If4c0aFT&X)Ojf$V+k7+)EOjq~&Wu9cvD+Gw6FtL~M-uLQDT-n?>{aOMa zfTyDLSd=AVOqn<(iMd)bLO~c+0h9b_&=iK!w;-H!}~hg|NiLeN?9m zd@G@yC|Hs7_9{hi&C8*uzSyD*H8-T}tF%{SBIXu-EWtwZ%LK*5xRcc5yyHb#th4wz zm^ESxNuF~Om9A9QscLC}aw|renc)UsI4{Lo~v^1;BA7uS3JdZo_VKZIDx;yAC&%X|} zW3nJ*)=@)9Dkv4;CWF5PPAulS2?G*CL1&ox3t}epoU#P(W?KPAY@DY<>qo#>}4G8$g@tP8<)4rBd9jpU^`^{kd~ zQBHzx*9c&_oIbVjC{9$esmzQ62=X9e19%pOYzy>wK&uo8ZX0Wp zRSW{xf2(Dbpdb1a`-&sqk>gkngn)^uNyPAxk9^}2F^OEorM^fh!}b!-pLp^0eoQBe zpuN(+J1;IS#_q+npFP_vd5Iow_kv44MqZ9FJ`ZCS7FQSd3TZvvCX?;2hY8l&M3)JH zi;8%NXTbclq8VtS4z`v|Sv7E@?0O6r%=M9;_T__JT1{w#5#&H(Ms@}9o;$zg^E`%! zs2G~Wpp_wJN=bx=o~I&n*RIg!IK675eQHQQD)~ zRlY8VcmAyKb=$Dh&m9dRZ~4rCX?Qz9yOda98zIYPa;st00>r2>Y15q5dt5uk;;OAb z0uHzq_SY1xH@Ow!4$qzoA_hKh8~w)*O{= zt3ggGvOiJ4EJUGgobA+yvEgu5n<}f`W`hy=OuqhU&bg)}3Cr)az5q_axOY-h!RFxz zf~3#bsZ`6)Ux-)m9x0iI%Nu|5)eyS``7&z!pB7*N#kWH(J4J`_0xIf{h}t_ihDsbh zaY03mr-~`N-%vW;d|1d+J6f#D`xXCjIcwh8<@S}1#-43*?rx4k-Sr9UA9Lv1U%XVP z;6{h7Z!Tv;e6!x-Z=~W2alhzE8uTJ&d~rzD6BfI#@2d{TYNv#?uEB1}M(8m(0K5Hg zc(Z=$zuHp~X%gGi-JN<}U#i6K#|`m!1^eH+bm{xbMBHMJElhG(lc^4?6s1x!+7->7 z!#oUKj`H5Cy^oCz-w?B!lDVp~2&92=uuf$|M8#>6YsHE$ z^=3KJtA-9ndVoO9SKgeKLw@tG2%;+9*~&keVV}t_Lv~`4Dk-vCc>3m|w;v^)OJCe$ zmtP!V_J`|=*g$@qm~m5bh=2KG)$S}_!NUV-a56vlYWR2bwzfL&^hc&bLw^4Poydsz z7SB;G#bUPt=$csDTjw9)g|IH|(Qpgd0;}55?VpoXQo_VUbiv7p1%oc9}5rHBv^S%F98f z{XR_4Qt(>L@feSsLg@uo>}m|cT-{vLka9=fxhTx2aG(|o-CC{~0ER&=D{Q;#USsbv z3JBmTj=OV*nhhzMxtKVmzI2=p132UhNG1R@1u~-AqhbIyVaMOp_yiw0diH1GZ~2zY zBP-yEg9oB&U!ZHU)})1x)}N*I?ii>35Lb0QOz(z=4Yei#k{Q9riVxN2g91YOOGj&x zTO$eV{x`=byK$R!dN7t`l%o>R5R?SFHAL~CucKs-Py0V=_yBF5J!7VtlbW|jlWO;G zMX%trxrThcr?UPss^7%C*_U~P*ZY2Cf2O#|d6y5cw1;->;*$nU$TpF(Vu{umlPB08C37M=HSwdBYg- zRECzJ0;07F72r-$xhDgXcr|vl2p?#i65fT`W~OsBG2}DB5h1;kWflUPnW~VP2RE#9 z*6mWZt-cLV^0V*%yY>zMB_`Qt+nOZqiB~(TAnNpzC&}u`*DpGAXOip5e9*lVj6BI| zJ<)PdUZBHyB-w-n9wTVs|E@|c?iwXr2NXQ_R%q^qQr;{-kd>_xK#d=UQaU!Pga2YW zwubGFt?>`=MH(Hun$A=iCJ|`5lw^LeJyF?cq>i1J+}E@8_$$7}ttg#}HKC-q*JO|S zWHT~Psx9t_Ir|ap>iFtsV@w~OWe)1*_~Q?+Q|0beS5Fxw``CJRZqJ?Qtdw2v(!$Dd zIWy;I0cLUGpb5!VU$igGO|0z&*SJviiGN{z_Tg@3+`AOe07;T=_ z8XxNuNm4&6TXT{Zrm`rjK%rFOd7TM|+klCCeX{}EmV&7(@mBAjO1*GeUTL+~m@g)V zk0M=7>w4w{4@_)B1~O+ooj##T6~B=%%G{DRB`Klrq|5PO4Dw~Mr~EMK6=$3DJA^5k zt?VfXhvD=rM4{`)f|&2}F)>>z-LO*MxnGXULZ*ZJCQ;BkE&S&t(87HDK|X&=N_L^_ z4cONWTGrFYu_t_7+Hh| zr%Dh)_+Qbp^gwre>wQr> zy!7R!NM~i|i_0NKDTn{gMe=pPPrA#yPkYYQIR4`fnNk~3Vbr^T*WEf5vunk5BQOww zBR(-Wnb6A8E9}b4``ChMUTYRJ+mXXg+uI>cq^Nzf>#r}9Z-5e*9SEyRK+}>8ZVrs^ zW6dl%l^`*+1!`Y?oq;F^3UZdfdSMKXABx$WwMDv}J_t;7@}hvu10ic3wJ zqk0t~gEQa91i_Q|GA8TPR9I`O3|V-T8b!$#%`bg;xFB3#K2f%MU=`WeKT@?>1k;9gXs#` znVthY69&Xemgi>wtB@xTAUBkm>9GA`taUG&QTSO%X%cHgnmSTCpaIN3E_~2GybpcA zC~l=z4lfFr)+^-XAy)nKVo0Yav&*j9LoOMvmh6tNnaTpLB4yQcyDZK8`7$f)o=?B* z3eRY%rD+w~@>j3<0D0*=F%~Q4^`P^yxhOW+_?0S{C>?MzoW=0xK{MQiWfnz6zsQL|E2 zJCc?aFn|K3N{e{68-`Xbk;hO^Irg%f+?m)(2o)LJMfB&a77gm<_AQ<4wwwnr-d(~C zLGRUWbQj9{rz6&XMQ2ZPm&C+7*}?K>>%}1JsSN!G)Y^R2&OLVuatkl&@CdI-T*0k& zUoA95{F{(}=LezyC6cNX?>q3dXIQEf@>Q^E5$*CcFS5sx-##K-_0EjseQx|Wrr;Ib zh3MrlG{qPYjmyIP%vDpLsWcS#V!dJG*m|fytgf-=Zi*vA*^7gvhThIjz%wO92`~uj zs(5{8{m^UJ9fD$Hefb4z++1^y<7tCLk2rs_@6sSZ%UR1pCU*xcOcG<)V>=gOeKld0 zNq0M1lo{4{gcHVJlO-v)GHFjg;D@iUV0FBkQTr^Q`k}VWcGoEl@MkPN0?}-`tn1ks z_4s=Ep~fHUoBv(7L5dGscs(g1H|?t$)i-d=%r$BFL#*(!4E3QT#2C%_b-CpXuwT zNL&ZcT%IxHKlhzEo*HaniO8h+TGxa$@4GcuBCIUcXXS7u>b#zh@2hLUU`M$@ z2zSpo1^hRwz2K0e_AWEDN3~~u2Dk&>_vzbfgO^ODzOsDbuZb12X;h*R?5T6L2b_Pk zY7|*&7L4=54T~S$>77+FY37M^Z~s9c%KCDfk0O_H?pMVGi%gQR7XAdRffU}e2m>xx zY*ztmCAdvBQ)%@EfUIw#-}3EPUf9Wp*=+2ocyl`WW+CsqI5s`H-w2o zIruPuI;qth+4|GKQ*2VuL*-Co0M7Us`W#PtwlIQipxhbx?{W z+X!cRSMYw)ZSp)vcYolaXUl2i^3GCrA$i#9Rnv!nD#xDBct_*1*$H)bN4rmM_N}VIh)Rx?880 zRI3fNOW#t-%W8VPFZ>>2c01=equNZyxUu-!jMAr+a*TsV0&^GV$I}Vr4_fpH$JQ4t zwS>nZFbx^{tn2TkmP~wp`XdE2J@w*4IP>=i3v&U;Y;^+Pn6_aqq6lqx$8lYA0$RV$nHe>JO?Ei>&c|GgR zKmCbJBM7OWEk55lq4s(u94g#L@mK?8yU9n(`VP})<%VRMW<^lgi>sl9KP7MnlU!s%GI7WU(5MaANgg|vqOp4Ara2&{;(;bS^Coi^ z%|-@?6`*CITOybhlnKCH9#rGVcP>J&aZrRh=Lg45RjEcfS;_W@|Rai&&Ca-b0c;# zqL`x#=kw{GT(}VyHbEjX}RDp^LBzr7U0%UgZOsd(m zDc0L`Z(WKJE+`eOEE0SXh^CWc3ACFO82>FBe6Ni^+dKfn$Ca&k9&`a=^mK3|6KEbX z^Mz63!Y-ppev&r1!lE`lV|@%%dN7yAiZ!dl1oH$TsgoVerL2%c4A@n33tR;U^86M~ z$3Fmmv|QkF`Y)$eYRVcVDc6f+s}}5Yh~^6+Q+*nHlT67cYY*@YJW5HH&7Sa>O}9PH zNKE*M3f$|D;oyJj(R8>6z5&19dp!W_%w%t}k*&d#icnB3T_RIM0yTy~wOxqxKb>FC z8ubkc3dl|92Y(JPw^Q7u`frZ+uFb;Snwx+fTzz(n3z4^4fM7Fnfaf@?rtLL zqhWj2%r7->s&G$dH<~c6YG!;%KsWcx^X4K!f0o6S%CxZ~Ni`|9Kjp&qq!qKUm$qID z6yuR39}}ZfC6{UATQ@;}C@iIMBP+j6^1YsDU>6G8>iQ2}~3>*m#Q)?KA zh2gz5vJ-*HnV$s3_4GwqD>`9e{jUA2%5MV%47*~jc*W2LMvzk2n>*bO;@5IC3Sz$H zUqXdMzXv9@e!((xH11V2SuFeTH2>W)gq_l-3Ef3I1xA)plGvwOEJns0cevsY|94Db zebn-bbb-6J0KE}Xk{fD!{cwbNQQrXQv9s-0_T)+TW2bv&ehGcssN1M%yp4i?Skj2L z#4L$Ws~k;9@k~{SDP^FkF9Izr+*6grK<|mL%Sxswo2$W%P7thvZ=dGk15u@ps4fGH zH!Ge7#74j9VCuzOyPthOv8cpQe|gpSlEY2kEMtUH@MyJG^+od@v^mZjxwY?!MINpP zc9L*;h}0iq4cc}-FxfRC90mQU3wk{mB(wP}%s!;a+X@Om7f-|bLnNHvqmI5-bX|qb z_i%Fz-n;nvBV+n zPgDW}EAv2>uxM);QPgrq!efDD#u&}>!{u$>>wy7U}%&PL|inBiP)Z#^$qqrDp_}%8s6^(UZ z4fgxeRxLrqh=RILp9RgcFbCIBjh=aSTW8(BjYWR&znn#kP6{9Uf*O@l-(8hi( z2gx3DGmomyIlmgEoafe`BKGtK;PkPu+vd1X#|FC1v!S#Q^PWg{WKEhG{^Az%U~cH3 z{9wndYc3?z%A-0bAJH@23n5UMA0aR7h2Jp8z9_t?Y4i#3{IGMQtwda;af?y>-6YTo z8!VL`HQ^WC|2; z@U%xLTv*=3=o=7 zyjQ$15fkbLfkzP zGkkzJ_j($|`px>fD&)Aql^cINQpK#cf^juMB;=2|kC15?Q_hfGQm;9rbC%m|a#Q)viN=-;3 zn{MN+ITXBDW#`oG-#Xj09`!~&C! zEg=3Srnd4@{?xXC815qN&~JjOK4$G95A%F#r}j3$v6Uh4#kC~QXT0{1_PX2@bgs{o zG)W8;U`IY#3yLpc3?_ifJ2_^-*9qA^h@xQRTdI^P*4!uIRCTU0ETvjnH1-tv`rf63oJF*bQVDdG*GI~6gYWhfRG&w^1T##oM;S~)9FpB?y7o0!Ght-}J zTL8TwUZ!b|qOi~hE>lfj64z*stKFhJBMmuy)Mcww1D!+tR&OM8(9fvJ@D=R&Ce zRdl!}57!UnnQQe?d|B3Epb->q@Fy7QG%mPQ}|<1<>pRKD@w}Lhl3_Cqvm7* zD`AlA&~yL*G{cW0|5TowUr;?!;O}t*dCzVed6jM*9M^Xc(3d65vO8wYvTF^%BSw5z zI;}Iq+59i|xF*Y)EBj-u?Qg*gmU$YYiI;m2F@y(y47zg4J~o4Admrx9AO6~5#qietl6r@x0@ptC`AJU!ZRinkU&;G|Ub=oK zcqEp$VL1a_Y4FRd0@yuh_#99yurM>@4{NHMl;NIzy8BOIEG&`EeUsrkq$InaN&9L9 zyvjY83>Lh4fEkvn2eKKKnBWVMHAjRQ+zfcjb=t?^YRJ@O!H;ZDX9jZegu_Qs=X_(+ z%%MOCWVI4~!Qb#ArUJ?5$?bk;iX{UBTmqjGAnNlRTWrENsqy5!TBJI&er5bFO*-|5 zUbYq4{oaZA8~d;0VV$f1uhK5*o1(dFMz_ftI{kUjz-A)gHq8dOiV*X`@*knTIP#pq zT^@gW@B?st@nKU#{Ki5kxX^I@d;}GU)R~n)-h?Rj;br&^T*+%EXvU7f%Ox#ZkLBEn zcJR9_#6|`ImLPIhN$9uAB5*{&4ug{&VFU8`ogYAW0#{o6fg>cqZ7@eP0#Zz9TApPh z2r~kj2Y1PyRz(kfwr872%C*hp%6eh_u7Y7>;tN||{`Q2_6I{<(6ae1ksx_{cv`Z^-pPM1KkyU9bk$Sx!y-%2`O=o8KN2Mp;aBi4nI zZGNF4zazBfiDWZ|*-4Ls!$REdvGpzfRVa_-aM67K{b|d`JH6$yoQTLdGUPfkLkYvq9%A#Qw z1vq_#xa!8Nn9o5~4j(Hwl8B!tJ>jIZCBlPgwT8rDJyX_)4_ z@AcJJOa?yf9S&@v;6dGyb=MB~I@O0<1Ff{a&uwjhoIZ|NE;N4hpRJR`m!004^xD;2 z^3Of{^(!^qfZfdLaLrFlYiCDaAAG_l-r0OMs1n+%f|Yxqxh7|- zeXj<8X4uOHyR7W0_wfqeNHM%ka`rYg8xo*?w*Kh|Ekc``gsML+DPC;Ml*tH3LN1yK zjBi;VHlwCxp8pmfEvtC-C4*3A?vp3>Bwol0>ive*8GZNfS{=A%BE}>Ictpx{k+QEm zmC^+Q((;4t<7DyAQlV{&+~WIcM5@y9%ginsa!cI0CXJGR&3x;*@?@jnAH!=WMt_gK z*DXwY`;4295=YR)$xln%nEe_q!a^wauXTW&d%fICO~fDc82nu@%H-=?8LEVlK*}j2 zZ&QZg^Cusx?T63)u9@GK>iOyWO*cjy*Vya5Uh|qy#Dq%tE^~{y{|2p<{-y6`VjA$9 zfU%<(?%)<}>juIJ@k)q_$aYu&_+<3$1Ss1M`0p)b$BPq218${&YqAPhY=1$c20X)e zi>qaNXGjYs((GZ<7M1@s>>6EIeQ3M(eJgN{~#!m0Q;CV@=4ntpG3@0apH1( z=Ju#{<*q2(*VsI%3o2`uc>OIYMSm90`u~cp`bz%ce2(iEj;S>+= z+-gbGx&~>Fw>A9lM;7y^ylnBF>mVK^=HqFCP_z#}ssicaI_Tq89X9}$a9!*Pv`ESC7rLXZ>S zN6684O@w6^q3?;x4Fk2r7S3Vhtttk6cAz)bB*+GPeh=> zl(Uf5ClG-!EE2fw`x-rG#lKRuZzaQmIXOlGl1EKdEWnxsn3Vm+SLNA*2h1mq}nm50Zfvqd9LTN60>{)?f)62VGs!}YOquzy?4{jb%U`;_lt zeKokAFs(sa3jQKsssb7%UxHhHGhsX)KDpnI*{8%YCUt+Y*1~p>`@cs)gHm$l0{z>= znj%e&Nn7KNuQBqCSMg%`2LF_XwUjxx4C{B_KTaZMo#H#0eLKKK6bSU^mkoj4K&JG}Fqk@RFh89K>T^SAnu!?&B_` zBmmn$;r8r%CVogi?o-^kEnF6lb_o8rlwKtkOqIbEoHRFHeyyHw=*&1u&LyJ4xTN-%u2?qh_ir5lIDt>w-qn z5NcVufxDoJE;WspgMJo^l~@cVszxSqpkgGd8`c}eu98ol%r-vX6STReHpoV{1?|%; z_=A|BNT*Q(HUoBnb4okll9bldNF{Et4!=Q%r?Gf!zu`|{3FoEjcF3GM-HWP`_x3f6 zEL-|ph5UYWoD}@H7>i1d@k(v?z%&cyMO-lxWh12Ijd*8y3Nz&7`^oAOH&OGaP4cEJ z-fG$jZ;i!2-B&&sSFU+{&b%+GcIwsxed*#Gjy9kVP6SeB9rZ{m5 zb=}G!nzjAp%Y3{i351)rixi$Rvj=k*5x-6*>>2k_447>zSmvPt;6+MXAbL6E##szv zd>vm46#~Zr-de*eBNYZ$x{~M7fGIfr3*3Ke@*R&V==fTeg5OjUovJQUWLQQ_z`4BS zSZiC=V@D?0r@;SSw2ZhrfT7VY~y@NNn01mKTqskMFNYhuH z{i185?mQnz2Qq4z5=m-Iir5H?xoM)lZr6At6Ll{+rRbLyYQJ*Rm*hUYSInLrOKiP} zx39XQP}E&|HN^`acN?VDO5zg8A#YnnaXCzI01INNyx(Z4d0=3;Wmf+G2sy%M0b|ti zeh%-ZAWe_zusbdHEp-P7$3_&KhM>{B{p3k71-Enj|z_QEd z3@UX?6H~MV69TR1!udZL~-WS;sO`mXIc4h-}#sF%v>ci$W>c$G#3)2qChT zWo+4(e(!74efRl1&-eH}_aFChR7Z8qdA*m{c22a6rs+kdV6a)#uO~!QRM%(IpZc?B zzvabsD(;)&HwbHuylG(+M~}xpK0(THt<-60p2l-G)0~9FaQ?zNuTQ7pTqLBDHNH*G z^35Aqc?(;^477DVOI7pP+IQvd2o%WHJ~oV;;6y-Bq+G$|)RS@d`|c~~3f5(D#cChY zUQ?a~*Bmz2C-6AO7li*csze*z;;b zM&s%^`Rvcx_rPqa>F`pP-*%Wo+^@*Sn6Z*{F+gGpe zD}nEwtsl?dN;a397Rm^@rz~VqeGabt=fxWtQeWGMWrXZ98>n9@?SyaJvo3AOc1oUF zgI(e{O>E{ivK)6Gu^d-WCTUbTBTzAc;ZzVzl${M7Q^DXACGXL)wNpK;&MBUrO7Am> zKRLn-$22zt6`Qq>)J5WMl)nw5HU|}pU}VvKkEd1hvCoJeH#bNoS&1Y`B;`1XnKM5h zG`#Zk+ScP53Vhcm+7~3}^2=vu#!phLIs%x4!z)lfF@1Qwfo-2+uZvm8(q7wGCEE^B z>bQGLJE;Yd^+U1(6+L2W%KN*i_;P&r$xfb<6P;vytC3y&<;os|>frc_k3qh$6xYZF zcPg3>pt(`%oWOjJUdub80$cdYlU=J#=3cd_UGQ^L8IJB<*hFf?SQ#0sb6S+=8d@x< zZJ~Gg`(k^zr6zZUh05~faO?PdI2Mw9x2Gj@sW~ihX_()%Kfl0ZA~mw0oOWd2nCUr! z&|L$xoKrO)|B=-9DPB#{!rob?{vEcg1|Q+pap<{BD3TnXr#a z3jHI}sz#0&FHKEx_Y1`=oG7{_6cmuVxUf{Xwu z>Wv@hfim2?g@+fj_J*o&+nTW_b5GPyoNQ+Lc!_)!IZxoimp6kvRZ5;XJ;@~+{g?Q3 zanqeG;nsc1`|>*~^VrZC)9bKSpF*iQEvTMq?hJ>Eu&f(e`fznhE|IEe&%5W*uo>0+ z?^NZ-TZWnlO-=Pv)ecnxZj$43+k6TgeR4%-yB6cY$2~dj zCA*=xgVH4XgQX@pj`wJp(A+z0zu%xwO5v~Fm5;G2m5w|vQ0VYws|+&dlFy^#!z`-N z{Zyxix6z&tPgAR^hWJXx!_mrzejZ4`Qc`)49~5bqHsFL%15LGWt@Q~o0R!?*kvf4J z=NT0PJ~HZp=cjVe<*$(+ceoSHzZbU+KQ5c-dt+?BX6=|lX^_a}ss$Q_vM@Eto30%` z*Kg%GKhNliuO%51mlv_UrlL|2@3qQw;j>jwrkw6qDK%4JmVNG>Ps)8jG&w+;yd{ez zSo;!Zj|7W%OOP7V#?AMO6$KT)m}>_#U%g@chgcKA+#JKBmltbBjs8SwH>$@nv}>-A zt3m{E&qcqf%JkWvFI~H+F!iC+*znc%6pbUMj-owVdf!*zzUEQb&V@Fp|N7We&~&_11xo&IpzicCTZDLu%7S4v8w->pLZpTqnhrRhkBjke8y`w{`Y0Q>46C3YVHX z{o0h;Jauu6>Z49v#^-v4)bH&3qRvZhD6Va({=_-><$5`a>S7u?{IEgHi`+5qr7_*u z(Qt-djyr9NB9A|v7Qd$)_&5}Kvkf6DH=RF0^^j7;yK>&LQ64VWFKv)u0*IvDA0oSJp25#5oo zI!C~9E|zcR>l4XK-!9)d;!(72zU%AmtN9%l+KZ5L9U!rz=q*elNmi-;&Fmf^)%e~X zM}h8><1kjwY|-ogprin&X>pJ>R<&-oe-I4<2K4>t(n{gkIDA$GhJ2W2SX0Vk*XXE> zu24Kd5*3}LhgL4n>9{KZ8}QJSlef*U(7?^scz&@w>8=l5_X)e5nl00IZV$V=d9Vxj zyf%L}UV85K_(Cn&M5Au()vxE+w*eTBmrJ_4HW}Jz#AwAqCTpVnOU2<1%c>cGtg z&#V`(Cr{n!cf6e)XYEelhb;iycqZHaSJ#adNFPVnZ_}$dg31FZfHacS_yO8YQD1{G zC+_YEwtb7&7!Sg_P7qBN4>8(;ih~!w@)>X7dHB9i-fAM+d20g!U-3h7rk(yp-8ncr zW_wS55Bl@^_eQ=uZIR9{zIABef#+K%f)~j+)@UtvJ%5Hv6BwWC8+omUHP88 z$zFuX+|2z#U`v3$243?5?;q<{UMppOu3gvf_a?pX9!hbjbiux;7%iKA=2)xdgt0R% zU>mu${*d|Fw2#FU#7D1?+sgdem$61r(?LrN<5%pES{}odSl2wM`d!?z~RU z5uBP^aP6&a?437?-YBWz8A45tZ{4EDtj8YN_w_Y9Cv7a=_%fr|x}4pwHwjCKKaAau z=pbcj&jJ!DTueEOiIHtaWw$7o>v%r(2+vrl#G55EMGlQRtyO{rSwmUd*FCT9Uh#-w zvH7#%Xx8(1-vGZiPDzEYUdZDvf0@te8!OKm|M;eSP}FStNbi!i>HM*lH?9#ysKIrv zsl&9_Yn z^Xf)al$V`G1G=R9%LbZm<(@jvHzIE`oHSdN-FDU&PA#D=Tf>T*2d8z8Eo6*~t9TOU zYh&`f-j(rtJ$SmJoD*30oiDXAI4g<6ehx>j2^F8^Q`P0C+NEap3&SiGS5l|#Vzjjj zNj(_deQ5L{Hie>$3~v~VrZ*E!MEZDpqumn!h8KcJn>p}- zRQ6~tM_>GDVogK|N2tp?o6p%-HTh>Z-c2QF>{Mo&SXG*OEl#9)UgSrO_NFZj=dtbE z?UksmdmN2Q5U-j#?!+q141R;wlKQixak>uuu8H(+{x9oszP#}6#r84=*S zql`MXneldHw@_tQ6s=A9PN(v)Q1{6LJbKKt$ttdoV(Z)au8}l~uJ}E;#mQIm#QJ-c z&W{Rj3$ZLit4oA!O3qL65>8=wl&(C1hS#3RRcfp8z`Ws!sL&{Zp6&=}aWc79(X>&= zVztV4Mj@}2cdVFF9u|jshKc2LDAP_0^VooYC^i*zbHDy#_-f;!S69N#Rc%tWH3|Dr zUk*$!3=7jy^Hs7P3PG_{rWE@LqhgpE2h&j(Xzx_T7(^`;fV}siI}oVI{NNm5Hd%fbkaz zy_?<)w;dskj@&O4Qeb6o{XE<5+}sq1PjzQ9j8o*)58Q;22k^~=$to+a}* z`*-YZVwEyg)K~0!VsK~g2S(J%eqX>Kw^e;zCkJV}$c58voSUq=%E}z=EAHxAUbw9E zgfaEi{7a*>Q_<9TVB-!U|$ZUF@%(TjjKAYSpg%i!!hqA@2rTJas+``(Mn@72rJZP6E)=6EGUSF+Yp_7xO54uIl@Z z@`QN;`BO_Zaz4=`QmI=sJydGNPJPWFH`XcbIVZ`fi+!CH|REg*m(K9Z?yGTV2x7+j2 z&_8@V54u)kd$%&I+pX3WcMu7qp!2Ri)8%sd=hF|_AMeB+aTDTM)LuI`EojAA8~+Xi7I5qebEr2$z4~! zTy22Z1}=xeky8?jSMqw+Ars#`vflZpxDoY*|3-;h93=rivPe6oC~qWqtv=LdR`l(C zmeGjtPb+o>JSDZZ zO5wX~eQ#EayM~_3w4xG4!Zss>rdAGBpbLXTsC!%mW>*zLKEVo76Pj|}$#|%G6SQRY7NH z*6XwsH`Q{1g$Je?>h=}u)d#hwDq3?f9zU%^G z*-L`%N{{``SN`iT3rFxJi>Lb|EFcXa*@$CB9!r6ONAz{(?hQQV;EQRTKze--WOR(0A32W z0s#zp#Y8LxzI#y0h|%ybk)B>fkKm2i@LANo;XUco>7?CD-D^e9+4pi=U78t*)Ssms zd-+AOufpSS?VfpO<(+2pqGmB1=SRXt&n~*|d2g>6$w7ntpZ!Pal!sm)Im9wYTD@~`wGUO1#my{Lrd)UrWo{0jieahGz z|G;rr3Slb0oojl{67Rcg?S6SG~QxrM1<&!5KqPiYt^??q*|>b@)PdDspTGI zX^r}K$xlZr8=EkNGN{S^g3h*w+jI=>SCkE}W4wy@dKK_22_X29px}DhFL$(cn8u_9 zqv+kOafo=?eDsJV=xJWluW|ON>dV8S#~0KRWQqzw33dw6Q}fH+Ll-X$F)ijp+U0es zvMtW#>4?42={Z3hAFf1-Jgkiy(ThEbHtwI>qSgIg@k-Yz!wRERmCy4+zhczwTqhXq z*(R!e!{m+g|>)(iVa+MwpGCN%-h9q`Iv)dYjBhZIlk5-2*SoI zk*k1;EEpvnSN+Xi92cB0$j?3PIGm?1$9R|IQih=Kh|7QAwSlJoT)iOZ~IX}R^Cr`C23G>dq^>)p~TspyXI)6@se}v1?Fw5^6 zZl#VfU-ks<69!#=Yq3YnmO+g8sbeD$f6__3Q-YLkBi21(U^RdSe0`4AzR`L?O@#jQ zpo=%rL1gJ4>Cn$Iu4dMYBA-UFVmXiioctjP=b-GhuJ9X zs!P?Z<5AKl^GLa6`-ssk%zu94aI|v(27W>#WocHz&8N>;aA5n=+s-)h;;e2i`S@3l zG}4e)Ub3Y=9Us6{xLso${hkB z3b=^lDfq8JFpajBhX`gT1-s+Bk-A-p`Cr(})8L`rZBVoO#&Y|U04|)T3n~FRrlhKV z$~DZxvY`ff@1vUtTiyt6FS9YZV38J*t$BRH@dU+e@~~;6^3-In3My~0E;q+$pJ;G4 zJOAq|xg1hB#B$rcuQcnpcM3W)oRhKpymO4S`8eNhQclZy64O3V47ywDx-RuBUA%id zoGKGvm{tgAcEWeO23FLN77@@1XjsfG$)VvW9S|IARA}WJoCrm%TAhVjHD%SRLnXuf z#k)>NQ;j#fcI3%?NzJ`!OaBmd^-{h=zs8sSvllCr{Q81kLyAGe7~6Vt_d7Nw@jc%E zINmHXdrC*lf+l8rp?t>u9BnUbA>dFz^D$PO0E=98Pn>xo7B7GrcxN%?4kq}-fUBRs6RQidaDbZSp!p?|J}5_jBP=DDt0rCI+S-&xOF5 zRv7>^U?=@3F6FFe25q0~@)-T-XlobTJ|!_IrQzq0yI3oXYV|$Ubl794)%A@B?45Zw zZd6WL`o`})@GgB=|N-r9^ z3+#_v-mbjP*mdHNwtRCCWAf~=9iD7aJ2<^Ie1Cv@%_c%he~Q*r4y_?Hgx$oi& z1Vl?R+4zJ$Vf@(H-yOwv*ghjxtIbi4x!nU`%h!sMMdySA_eA`<^-AP-rGUNu(Hj6^ z^p}q7Qe)sSe>bZf$aYumsrst)C@3My2t2tgbU2tGVPm;~_CRS$hi?;)0wcNrweQT0 zqedUzzCRqGU$D#cx?R_)_g4z0WGm)!f^J%@O1axvvOVh<{vW(gNm`3B;iILB^O!zWthjTW@XffxYD6+uo>=8EgFua`CJGwsl#r{XMJsw}&etw|(u@VZ^}pC|?CyWQtT3E{n)jO9 zi2MNLV-xda09e6(u2HJt&nAwb6vqZZpnKjCXE3QP7B%66oR-bF%7`*xo2`hyAjfxs zG|;;eqE1m^#j#h&OU0$-9R353v(j8b6T%X%2N4%dY_Nq_@&ZP`4eWlD+47)4RiLk8 zHgQDFGprZ3$2+d|OZz6Y&JK>No1S@)7}K-O&+cw$qPPu9=j>C=c0CAx|3%GIJflWx z0gm$tn94g!@}&V6AT@UV2O?*K3yVY{?t+?NBC>o))(8dCqqBJMtouw{jxa5QS!YUp z5A;TUtU#Wd`EJq~?bHVPxAW4ql1nOW(JauFW8Nq`y{Q{g!j5>9vhoRx#e0ZKot0 zs3W;XokJS%Ai!<`;8vFcaGXNe>6Gx1CEj!t&}1Tw(y|=G%L+gk6rcy zm~u*BKC*Zd{Ouhwl8_6?o10|ZSN$Iydc)7vhK3J&0A;{Q5bupqIAw%ru^+? zM=jIs9@npVwoUDu%WxXJeQEYwqqB#{jui4nx^X zW}x8hcXNvhOssE<7Dw_5*N~%qzs4Wf#*n)-zhn|9xv=;ljgQ@*OvvKZO8Yw1!{T`F2|_1Ra6muq;&q zgo1q!872$F9!rE6#=jIbnAy9HRI6R6U$YF9Fy7zW@6doDAO>xc}f;#E>pHw zO!-Hbj(R2;ta=&k_wl~SiCcERPEM`v;AeAEp6m-UZrL4c5vsSzbFncxic@3mJjH6TpuIR4Ta%|AbKOdPLu}(?VZnUYFEWdN?F=sC@VQ=OtMg18_g1kZI(s$vFjj!(H+JVMfEf-tv@CrPyU2q%D!J~5_ZHf{^4apUr;sAH`PbNXezX>FS55Sg{&>T-jS4L1w(L#^1DOmu3@ z-LNZlc=+45WbwMTw%WC9-@odP?VIl(n4Wt4{>s9|ieEqNYTBp2=5Y;`ny;Z`k0%eCpz4*Tld z{>;TIRuAOElpkFl5nVgv%^`)oDiyel&TXnAU4Mc)zihd{N{4emf;lzF$3IVR)2?7G zNB~0d!i(K>z-Y zu{*cX-RgKIW=FN#4ThNO=_$->EEh%_abZ*u7sd!CZ5vvNUgTR9M_klAKUzo)#dDxm z51YD&bnLQ;w6JyOhecXj%4C-20VV^hgcPuD$Guqj!*#6H+w!Qf!G7Uht*_oYA>u1m*_B4WH}8fU=BPcx$5v(3Kr9W zLBb00VwxBo#$ujg>cGfzihJ<1fZ3 z1!x_8K6aV2oI&t{zSu?@wA*eh*&X=0g+<{&H3Q+FTJLWMoF&$fA#OPp{Xsvc!mDe->K`ERzy^U9g%%NkIM!wb$+` zqGE5qR)@{i9WyP^cv?TvZ8WsrYhkj9J>FrYV44kkglkSgQ@Pda=)Ie*(mW8FT3*tz zqxytsNSW*|ubpHCiV0B26p8ApUc)mQZSxO?_(=RHK^j0wTpva1EaD+1iXHu(|%54ByX2{pn3S9S9U ze}FXSKc5|GbCn{XWl-1vX(3ARHLmyy8OC`u{vYO{G`$`1!6rGh9Zd&y)l&O4(eFKb zKHl0GgxXLu-~aZ7>xm^MH!3CUWn^?NMOTa0Pm$9XmLojUco2tmvrIUUbO`CQo8`1D zJ6C^M>1*L=5@y;b5G$#_B(2LHG^?u!Z#-59fvaAvY;fKW_54SKWtJa<9MCq~`LX8j ze)?js7Gvb+yEP@xIoukl;cfl-A-ZwN=%8^ES4o)})uECtVw=x(UET5!hynD&mGEl! zN5|(8C51=W?Hgd?F((1N{&3VOI-&tNok+%UVPvVz4hWs71F2x~Gys;M?r3n_Al?nU zf|Q=tMC(HI*%iVj)D0zbD^R-6g!&CLUwy7$fBa!VOwr!wy~f4`Syy})Rk!qpdc5h5 zXPz7%)>vwf?+Z#a#9qa%0*uMyQ&!7K&)N)gSRQhUu+)hr&7?uG0WG3CLG}+bxUN!! z#XMO&U@@Rphh-Q$4L3FQd7JKTS_W<8&-j1y!<#{5lVKHF5cJ{1KDzHLLBV(P$!=WY z&pI)a8^Lj^nct7yD6IDIu}mcG^Da6aWf;>po`3vTY?OgbN3fmYP1-k&U-|Jjw{DU= z*t+hMzh#zKrq*i9@^v&)gnr!-bA_TSq5EsOI7OxkBeTJNU|H&#i-ZsD+mM1eUWci{ zSgpjI$LRqz{5*ohqF}dzImc+wt^klw0xRV#2GRNR+DbM#V)*r(O#tKYQuOL9zHN+{ zf5>>kYjVRu?O~JTLinNs4OQvW(3;q0bFK>k@3B*ZeK-7wCKt-jb>P@gnoQ>Ntmky4 z#mH&3hU7kpoenlVTTm60Ij8MPld;2#VhR}aCdvKMRJLEeBe~?G;~%)Lqg|3PmzPG4vVBU4ob)q@ywCSpZw7_iHgGw`Rm1t9>$-~KW1R%XXwa zX^%+XnutT)0~an=20c00k+4A@Wfx~ah~!Rp!r~+5ccH5Imh2+qX9Is-Sf^9ubscv# z=+X@cj+qs`z<(eGp%HJ_1VltJgkQT$AG(5AU+i>2L`L)xrOD6oEjqyyp5+rFH(_^K ze;9d?fuy)-K*YH7Uf{!3eObYnu;R8eWS)qAIh=QHK+Cvp9z@_+1JPizi>j)q!?$mM z*B@uEyRys+?HW0zAAz-W^ANDV-FrGIQuOn;x4i6AgZ$g4tFpgWFSRxmgj#aT_eSUw z0;;{g@fq{+2rHoP8MN3|L-?EJ?M@FUU+Wi(a;~|8`DytXmtq29S@vzjRUo#%RBFRm znWTfmJMKCujz^+9d%=u~cb^x@b$4BjK-g-_vZY~;j&CZ^g zJ4sQWHduY?03m=IrN9=YTvfh_l*y4%orNPrx_;M@kiw`Hx0xrmNZ%u>nEqvFHICGQ zrN~z^yHc}V9$5=WqqT(Au#zH(7oYUV(&0^2mU*V`ty`)OI(VK$ZO@HpOGo!c7HIXq+zp)mXONYnB zp}2Q|k6vjE8*zLCL=S7!DT99^h|{w>ARX8V91Gx|;)NJcOP;_19ab`|69CxPmS(M( zgU#NHlyp-GpFdssW5u&Xcd6}MO=}zFKJMs!?ywZ`OLz656_w@zyLYGu@ML76Q*XR7 zn?0hF9xg(vc`^YPKUmzZ$N64IVa-`aRJ}FghkDr3|MXKv@p1ymHB?9{D+nGIoSc70 zJ>o_qe~u8&4Q%#09gXV!H*I#hNL>*KS#hq?QDI8B?~O+0c*%=4hu&FT#k6`S_wM+m z>1(*i5-L=+z#6{DA_Go2<#0!r+G1WH-JxR^3b?Nb4Nw_wLXe)*iQjbKTt3_}_-{J! z58E^vIZwk0-ConIz(BFEjz`}dq)j#XY4MKVG`>HTOmutgY*~8dh zDJzDlK8zxlbh2UZqGnHEW9Q&A8co%>h)zksU z$MI{|iM_LkaagKx=rsJ*2G4 z3H?e|A*i1q3F1?%87skxB=!-*s}R;P+z;!!bFw}QR3kW%^jOJK7z;WPCp%dRD=jAm zJ6l|Og&0}K9CCJ$AirrWt$cb(U(~A{NOp z%yeJDo#frVw7*GsBis`~>}UZm?S-7F^}laB0oY}!pw}mB0Ph_cc{G4mm=<$3poLSG7QTy`io3Cz?vREZfOhGh{oG9%w4m3IUVn$9uW-j%UGP6 z_%&td4>An;TEbSpjR$>;y3D^*OVJ=cR2!{DV!OXbN*bkJsUfb6nL1X}UWQ1(?ofy# zk2H)J&JaIHVp3mx>Wb}$;)bowuNH) z{BFv(4oVEh&%WsTERUwZ!{T&y5AVNT2VyxK24=((KuqoPm9tkJSEj-=*eBrTLrp)X!%~mUF0+5 ztM=BVws?nw90S5E83@vB*9`IpfFlD`DEuS|;@wDd0P_Bmu8Rs|M7pPk39=g27j9Ip zSeB5^EwTb+|1?Y(^Zb%rzdLBmo3?p4#m25w4dCuUP4cM*$Wnipe@Ew_!HyTS+YLd@ z^7?y7UqFp5Tc7|z|3&YA`hhx!#AIM0f<-t!y^xc=J?ux1X^1j%~now6sKhmTRA@LAwSao2=sJ@rk0rXM-DDNPB=C3;lTK zA0?ZQ%qo!*CkiXEahzbf0f`n@DTn`c-+`rdM7p-!sma+B^`g|{<-LoCvq*XQ(FUHL zJr$Jes{6Q?HXSg9H?PkZN#vSCvXzBbGl-C{P+Ssl-Eh520J$~0WZ5$V5>eQ~e;kln2&cFQqu zBA2dt^Zql`RuA4oU!_IyE)v{cuUkF?!}RV5omH5bL`=u5oajCvxqn~e2+o^cn|xgt z8Pp)*AZFzsX!sHLI+_=vq$`&b0RX>W<`($|PI8DIggb*$ZA~|JO??lx zKsm-2^JX!Pa?O*ZjsLO+X(X5d%I+V(xxpa<$wk9n%9~R!`j0cgXLLTc_%pQoS7!8S zfxK$@prxH(Q3q8x=PkN!Zed$X=9PLsW0!k=-{2!(FyK3nOX2myr`o@Hx;mz3Wq1F6 z5GMW|eu%_AUZq34jdZdc@-F^^mHnQ_f9?|K4KcOApkkRm(eoZh-RKa;38on#gx9+_ zi_Wz^sJ(8C`PfSj}CgKtd4tY2Q|@Z$N*2HsW4PUV(gp-0ZHqP*i3 zP7zow*c5EucFCxB&IeM+ProP_AQ^R@Mp!Y&5Y zEe|gkVFn*el%V~@1@CV)b z?*6~9=81Wi|BUS1K*qs8ksX2M(EReuV1! zJ6TfEM;wfG72s7qh+xZ-B$sV`dq!*)_DAuYx02&g*^~U+HNHPI=NelHj$g5vqi%N2 zJ|L-g!{0Z4DqQds5Py>;d>Q{fspq=SRZ8gu~Y z5J&}_HVZV9EZk>Ad=C2~z5aug*$bKK$B;DdAJwe-oFe(t>!=Ig#egce_m^v_5UUI zQrHmp7P+SLVis;cfCrp#NdMIax06kiEz@V?Hy+*-HM+-%yU1M5+n+btA{nG-qqs~n z5t5y*or=^hXHXXUS%P!@VMIZY0_kXZqe85M^@&+YWoK`|9@Mp%Ff))>%Nz4|(vka# z%f}W!L9u}AcE}~Bu;K)(WCRod$$85gLDV-L{a~>-X@yRic@jH5k8u7SU<< zqKi-j#jGY+PCUv{)Df|L+4&&E3kMJVwNAGo_tUeg;fqY1&Vgrgf(85j&S$6;!L1Az zA|Pxl!RZZ62VXUJd5O!n`bd5^PLHP7jdEy;T}#kH5>1SCI`xk-06LP65t=Jjk zeFJ%do#i{q-|oNY8FxH*zv=E5A%n-tt|hR&*ntKbc*X}P=z#YRvG@t0?g<0Zx;&P2 zemfeP^5M+m;F!xR()(67XyZeW7XKf&K>r7>M$&JP4?qrU3%rB=S9ORvw;Z*w$$!rq z;Y{=>a;j9XObF79(YzU?Ykb~N;FRojJKRbbmS1dTfiLBlE!%P|-?Cs2BJ4ysYys&P zJ!_ea5;Y{Cs@1qCLQ}~_`~T9F)mwse}uc)B-XuO{oP{ST2t&FwC(kJGnniwGXM$- zZY3k$LJh+k!WF-bATU*z!{auL&DOrNN0`5<$=_*^?QOpa>NArijpR_@S3+)CQ7HKD NsMd*tsRzuw|39FB)}R0Y literal 0 HcmV?d00001 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 2d386b7..36bd750 100644 --- a/levels/level.gd +++ b/levels/level.gd @@ -6,13 +6,16 @@ signal completed ## Lowercase, spaceless name of the level var id: String +var thumbnail: CompressedTexture2D var music: Music -var velocity: float = 0.0 var rings: Array[Ring] = [] var finished_rings_count: int = 0 +var velocity: float = 0.0 + +func _init() -> void: + assert(len(id) > 0, self.name + " has no id!") func _ready() -> void: - assert(len(id) > 0, self.name + " has no id!") var children = get_children() for child in children: if is_instance_of(child, Music): diff --git a/menus/main/level_selection_menu.tscn b/menus/main/level_selection_menu.tscn index 45b6a11..152645d 100644 --- a/menus/main/level_selection_menu.tscn +++ b/menus/main/level_selection_menu.tscn @@ -5,19 +5,56 @@ 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 = [ - preload(\"res://levels/base/level.tscn\"), - preload(\"res://levels/forest/level.tscn\"), - preload(\"res://levels/night/level.tscn\"), + \"res://levels/base/level.tscn\", + \"res://levels/forest/level.tscn\", + \"res://levels/night/level.tscn\", ] -var selected_level := -1 +var loaded_level: Level 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(): request_play_level.emit(levels[i])) - $VBoxContainer/Carousel.add_child(btn) + 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) + var level_scene = ResourceLoader.load_threaded_get(level_scene_path) + loaded_level = level_scene.instantiate() + $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) + +func seconds_to_readable(seconds: float) -> String: + var minutes: int = floor(seconds / 60) + return (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) " [node name="LevelSelectionMenu" type="Control"] @@ -37,6 +74,62 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="Carousel" type="HBoxContainer" parent="VBoxContainer"] +[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 index 87a94da..7437b53 100644 --- a/menus/main/main_menu.tscn +++ b/menus/main/main_menu.tscn @@ -38,7 +38,7 @@ func start() -> void: 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: PackedScene): request_play_level.emit(level)) + level_selection_menu.connect(\"request_play_level\", func(level: Level): request_play_level.emit(level)) $Menus/Panel3.change_menu(level_selection_menu) rotate_cube_to(180) From 38897c706a6862ac2185de52092f1ad6c45d005f Mon Sep 17 00:00:00 2001 From: Taevas Date: Sun, 27 Apr 2025 21:30:03 +0200 Subject: [PATCH 17/20] "save" & animate main menu, fix pause menu & restart (#3) pause menu had classic issues of mouse inputs being eaten by something else (gravity) restart didn't have working code anymore because of level logic changes also remove the demo scene which I believe is unused beyond the jam version --- elements/gravity.tscn | 2 + index.tscn | 34 ++++-- levels/demo.tscn | 50 --------- menus/main/level_selection_menu.tscn | 13 ++- menus/main/main_menu.tscn | 160 +++++++++++++++++++++++++-- 5 files changed, 184 insertions(+), 75 deletions(-) delete mode 100644 levels/demo.tscn diff --git a/elements/gravity.tscn b/elements/gravity.tscn index d84e534..0a5a992 100644 --- a/elements/gravity.tscn +++ b/elements/gravity.tscn @@ -137,6 +137,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,6 +156,7 @@ 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") diff --git a/index.tscn b/index.tscn index 75882ae..389b119 100644 --- a/index.tscn +++ b/index.tscn @@ -13,6 +13,7 @@ var changing_level: bool = false var main_menu: Node var current_level: Level +var current_level_scene: PackedScene const res_main_menu = preload(\"res://menus/main/main_menu.tscn\") @@ -41,17 +42,19 @@ func _ready() -> void: func set_main_menu(): Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) - main_menu = res_main_menu.instantiate() - main_menu.connect(\"request_play_level\", start_level) + if !is_instance_valid(main_menu): + main_menu = res_main_menu.instantiate() + main_menu.connect(\"request_play_level\", start_level) add_child(main_menu) -func start_level(level: Level) -> void: +func start_level(level: Level, scene: PackedScene) -> void: if is_instance_valid(current_level): current_level.queue_free() - if is_instance_valid(main_menu): - main_menu.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(\"completed\", finish_current_level) @@ -62,6 +65,7 @@ func start_level(level: Level) -> void: $GUI.show() Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN) + pause_game(false) playing = true PhysicsServer3D.area_set_param( @@ -98,20 +102,25 @@ func win_game() -> void: $VictoryScreen.hide() func restart_level() -> void: - return #idk yet + if !current_level: + return + if current_level_scene is not PackedScene: + print(\"Tried to restart the level despite not having the level scene\") + return + start_level(current_level_scene.instantiate(), current_level_scene) func pause_game(to_pause: bool) -> void: - if is_instance_valid(current_level) 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 + 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) @@ -150,7 +159,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -mouse_filter = 1 +mouse_filter = 2 [node name="TopLeft" type="MarginContainer" parent="GUI"] layout_mode = 1 @@ -202,6 +211,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 2 [node name="Label" type="Label" parent="VictoryScreen"] layout_mode = 1 @@ -215,8 +225,6 @@ label_settings = SubResource("LabelSettings_1bs00") horizontal_alignment = 1 vertical_alignment = 1 -[node name="PauseMenu" parent="." instance=ExtResource("4_3bfj3")] - [node name="DevInfos" type="Label" parent="."] anchors_preset = 12 anchor_top = 1.0 @@ -226,3 +234,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/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/menus/main/level_selection_menu.tscn b/menus/main/level_selection_menu.tscn index 152645d..4bfe215 100644 --- a/menus/main/level_selection_menu.tscn +++ b/menus/main/level_selection_menu.tscn @@ -13,6 +13,12 @@ const levels = [ \"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() @@ -30,8 +36,9 @@ func prepare_level(level_scene_path: String): loaded_level.queue_free() ResourceLoader.load_threaded_request(level_scene_path) - var level_scene = ResourceLoader.load_threaded_get(level_scene_path) - loaded_level = level_scene.instantiate() + 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() @@ -50,7 +57,7 @@ func display_file_data(data: Variant) -> void: func _on_play_button_pressed() -> void: if is_instance_valid(loaded_level): - request_play_level.emit(loaded_level) + request_play_level.emit(loaded_level, loaded_level_scene) func seconds_to_readable(seconds: float) -> String: var minutes: int = floor(seconds / 60) diff --git a/menus/main/main_menu.tscn b/menus/main/main_menu.tscn index 7437b53..004089c 100644 --- a/menus/main/main_menu.tscn +++ b/menus/main/main_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=10 format=3 uid="uid://ikeidrgprk8k"] +[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"] @@ -8,6 +8,14 @@ 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: @@ -38,7 +46,11 @@ func start() -> void: 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): request_play_level.emit(level)) + 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) @@ -46,14 +58,18 @@ func level_selection() -> void: 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) + 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"] @@ -94,8 +110,82 @@ _data = { &"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(0.794232, 0.673177, 0.531056, 1) +ground_color = Color(1, 0.484315, 0.700558, 1) [sub_resource type="Sky" id="Sky_0jxef"] sky_material = SubResource("PhysicalSkyMaterial_bt14i") @@ -108,6 +198,44 @@ 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_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="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="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") @@ -137,6 +265,12 @@ 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") @@ -144,7 +278,7 @@ environment = SubResource("Environment_qfa5o") 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, 1.87335, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 0) [node name="Camera" parent="Player" index="0"] visible = false @@ -153,4 +287,10 @@ 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"] From 709ce8eea3ff72a23165423e4696f466f1a1fc20 Mon Sep 17 00:00:00 2001 From: Taevas Date: Tue, 29 Apr 2025 21:18:04 +0200 Subject: [PATCH 18/20] split gravity logic, various bug fixes fixed gravity strength being possibly strong at level beginning fixed restarting being allowed after getting the last ring fixed save file's "played_for" being reset or set to another file's fixed the displayed save file total play time not being updated on main menu maybe there's other stuff --- {elements => gui}/gravity.tscn | 84 ++++++++++--------------------- gui/timer.tscn | 5 -- index.tscn | 24 +++++---- levels/base/level.tscn | 5 +- levels/forest/level.tscn | 5 +- levels/level.gd | 60 ++++++++++++++++++++++ levels/night/level.tscn | 5 +- menus/main/main_menu.tscn | 30 +++++------ menus/main/save_file_manager.tscn | 14 ++++-- project.godot | 1 + save_files.gd | 4 +- 11 files changed, 131 insertions(+), 106 deletions(-) rename {elements => gui}/gravity.tscn (59%) diff --git a/elements/gravity.tscn b/gui/gravity.tscn similarity index 59% rename from elements/gravity.tscn rename to gui/gravity.tscn index 0a5a992..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"] @@ -158,7 +127,6 @@ 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/timer.tscn b/gui/timer.tscn index 059370f..2453b08 100644 --- a/gui/timer.tscn +++ b/gui/timer.tscn @@ -31,11 +31,6 @@ 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 seconds_to_readable(seconds: float) -> String: var minutes: int = floor(seconds / 60) return (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) diff --git a/index.tscn b/index.tscn index 389b119..5a4c3c1 100644 --- a/index.tscn +++ b/index.tscn @@ -1,6 +1,7 @@ -[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://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"] @@ -8,8 +9,8 @@ [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 main_menu: Node var current_level: Level @@ -17,6 +18,7 @@ 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_timer := $GUI/TopLeft/Timer @onready var gui_speed := $GUI/TopRight/Speed @onready var gui_rings := $GUI/BottomRight/Rings @@ -57,6 +59,7 @@ func start_level(level: Level, scene: PackedScene) -> void: current_level_scene = scene add_child(current_level) + 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 @@ -68,18 +71,13 @@ func start_level(level: Level, scene: PackedScene) -> void: 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 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 @@ -102,19 +100,20 @@ func win_game() -> void: $VictoryScreen.hide() func restart_level() -> void: - if !current_level: + 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 !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: + 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() @@ -161,6 +160,9 @@ grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 +[node name="Gravity" parent="GUI" instance=ExtResource("1_ir7so")] +layout_mode = 1 + [node name="TopLeft" type="MarginContainer" parent="GUI"] layout_mode = 1 offset_right = 160.0 diff --git a/levels/base/level.tscn b/levels/base/level.tscn index 03d1a09..35859e2 100644 --- a/levels/base/level.tscn +++ b/levels/base/level.tscn @@ -1,8 +1,7 @@ -[gd_scene load_steps=12 format=3 uid="uid://ovtknjyj83gh"] +[gd_scene load_steps=11 format=3 uid="uid://ovtknjyj83gh"] [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"] @@ -43,8 +42,6 @@ reflected_light_source = 2 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") diff --git a/levels/forest/level.tscn b/levels/forest/level.tscn index e9763f3..458d165 100644 --- a/levels/forest/level.tscn +++ b/levels/forest/level.tscn @@ -1,6 +1,5 @@ -[gd_scene load_steps=11 format=3 uid="uid://e1761h6d522a"] +[gd_scene load_steps=10 format=3 uid="uid://e1761h6d522a"] -[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"] @@ -52,8 +51,6 @@ reflected_light_source = 2 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 diff --git a/levels/level.gd b/levels/level.gd index 36bd750..df629f8 100644 --- a/levels/level.gd +++ b/levels/level.gd @@ -1,14 +1,20 @@ class_name Level extends Node3D +signal started_playing +signal gravity_change signal ring_collected signal completed +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 music: Music var rings: Array[Ring] = [] + var finished_rings_count: int = 0 var velocity: float = 0.0 @@ -16,6 +22,18 @@ 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): @@ -36,3 +54,45 @@ func collect_ring(): if finished_rings_count >= len(rings): completed.emit() + +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: + 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/night/level.tscn b/levels/night/level.tscn index 76c77a7..1b0ee50 100644 --- a/levels/night/level.tscn +++ b/levels/night/level.tscn @@ -1,8 +1,7 @@ -[gd_scene load_steps=14 format=3 uid="uid://drnqmu4lka22d"] +[gd_scene load_steps=13 format=3 uid="uid://drnqmu4lka22d"] [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"] @@ -45,8 +44,6 @@ reflected_light_source = 2 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") diff --git a/menus/main/main_menu.tscn b/menus/main/main_menu.tscn index 004089c..235b9b6 100644 --- a/menus/main/main_menu.tscn +++ b/menus/main/main_menu.tscn @@ -198,6 +198,21 @@ 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 @@ -215,21 +230,6 @@ tracks/0/keys = { "values": [1.0, 1.0] } -[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="AnimationLibrary" id="AnimationLibrary_wruj6"] _data = { &"RESET": SubResource("Animation_j2kye"), diff --git a/menus/main/save_file_manager.tscn b/menus/main/save_file_manager.tscn index 10688de..2377ab0 100644 --- a/menus/main/save_file_manager.tscn +++ b/menus/main/save_file_manager.tscn @@ -8,19 +8,25 @@ signal request_start @onready var label_name = $VBoxContainer/MarginContainer/VBoxContainer/Description/Name @onready var label_time = $VBoxContainer/MarginContainer/VBoxContainer/Description/Time +## 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: + var data = SaveFiles.read(file_name) $VBoxContainer/MarginContainer.show() label_time.text = \" | \" if data.has(\"played_for\") and data.played_for is float: diff --git a/project.godot b/project.godot index 7bbf422..4379775 100644 --- a/project.godot +++ b/project.godot @@ -86,6 +86,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 From edf99640cf11375752e32a96c22119b79d39f467 Mon Sep 17 00:00:00 2001 From: Taevas Date: Sat, 3 May 2025 14:33:49 +0200 Subject: [PATCH 19/20] Fix memory leak and save file deletion + other stuff might also be the commit where timer starts only when you move --- gui/timer.tscn | 31 ++++++++++---------- index.tscn | 44 +++++----------------------- levels/level.gd | 5 +++- menus/main/level_selection_menu.tscn | 10 +++++++ menus/main/save_file_manager.tscn | 10 +++++-- project.godot | 2 +- 6 files changed, 45 insertions(+), 57 deletions(-) diff --git a/gui/timer.tscn b/gui/timer.tscn index 2453b08..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,6 +27,14 @@ var seconds_spent_level_attempt := 0.0: seconds_spent_level_attempt = value $Level.text = \"Level: \" + seconds_to_readable(seconds_spent_level_attempt) +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) return (\"%0*d\" % [2, minutes]) + \":\" + (\"%0*.3f\" % [6, seconds - (minutes * 60)]) @@ -115,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 5a4c3c1..6c6a276 100644 --- a/index.tscn +++ b/index.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=3 uid="uid://ccgnnif026wb4"] +[gd_scene load_steps=7 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"] @@ -31,8 +31,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 @@ -42,8 +40,11 @@ func _ready() -> void: set_main_menu() -func set_main_menu(): +func set_main_menu() -> void: Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + $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) @@ -59,6 +60,7 @@ func start_level(level: Level, scene: PackedScene) -> void: 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(): @@ -73,7 +75,6 @@ func start_level(level: Level, scene: PackedScene) -> void: playing = true changing_level = false gui_timer.seconds_spent_level_attempt = 0.0 - gui_timer.enabled = true func finish_current_level() -> void: gui_timer.enabled = false @@ -91,15 +92,10 @@ func finish_current_level() -> void: await get_tree().create_timer(2).timeout current_level.queue_free() - win_game() - -func win_game() -> void: - $VictoryScreen.show() - await get_tree().create_timer(2).timeout set_main_menu() - $VictoryScreen.hide() func restart_level() -> void: + gui_timer.enabled = false if !current_level or changing_level: return if current_level_scene is not PackedScene: @@ -143,11 +139,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") @@ -206,27 +197,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 -mouse_filter = 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="DevInfos" type="Label" parent="."] anchors_preset = 12 anchor_top = 1.0 diff --git a/levels/level.gd b/levels/level.gd index df629f8..b823630 100644 --- a/levels/level.gd +++ b/levels/level.gd @@ -17,6 +17,7 @@ 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!") @@ -69,7 +70,9 @@ func _input(_event: InputEvent) -> void: var down := Input.is_action_just_pressed("gravity_down") if up or left or right or down: - started_playing.emit() + 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, diff --git a/menus/main/level_selection_menu.tscn b/menus/main/level_selection_menu.tscn index 4bfe215..5fc9a1f 100644 --- a/menus/main/level_selection_menu.tscn +++ b/menus/main/level_selection_menu.tscn @@ -62,6 +62,16 @@ func _on_play_button_pressed() -> void: 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"] diff --git a/menus/main/save_file_manager.tscn b/menus/main/save_file_manager.tscn index 2377ab0..8054c7e 100644 --- a/menus/main/save_file_manager.tscn +++ b/menus/main/save_file_manager.tscn @@ -6,7 +6,9 @@ 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: @@ -26,6 +28,8 @@ func _on_save_3_pressed() -> void: display_file_data(SaveFiles.names[2]) 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 = \" | \" @@ -34,14 +38,14 @@ func display_file_data(file_name: String) -> 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/project.godot b/project.godot index 4379775..011b267 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 From 2f646169fb17938f2586df121308092a63796418 Mon Sep 17 00:00:00 2001 From: Taevas Date: Sun, 4 May 2025 00:36:12 +0200 Subject: [PATCH 20/20] initial ring map support (#2) --- elements/player.tscn | 2 ++ gui/map.tscn | 66 ++++++++++++++++++++++++++++++++++++++++++++ index.tscn | 14 +++++++++- levels/level.gd | 3 ++ project.godot | 7 +++++ 5 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 gui/map.tscn diff --git a/elements/player.tscn b/elements/player.tscn index c377c79..74c84c6 100644 --- a/elements/player.tscn +++ b/elements/player.tscn @@ -11,8 +11,10 @@ var velocity := 0.0: 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 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/index.tscn b/index.tscn index 6c6a276..c17011e 100644 --- a/index.tscn +++ b/index.tscn @@ -1,8 +1,9 @@ -[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"] @@ -19,6 +20,7 @@ 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 @@ -130,8 +132,14 @@ func fullscreen_game() -> void: func _process(_delta: float) -> void: 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() @@ -154,6 +162,10 @@ 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 offset_right = 160.0 diff --git a/levels/level.gd b/levels/level.gd index b823630..ede459a 100644 --- a/levels/level.gd +++ b/levels/level.gd @@ -12,6 +12,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 rings: Array[Ring] = [] @@ -39,6 +40,8 @@ func _ready() -> void: 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") diff --git a/project.godot b/project.godot index 011b267..3a00814 100644 --- a/project.godot +++ b/project.godot @@ -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]