Revamp underwater title presentation
This commit is contained in:
parent
e560cc0b3e
commit
1c801509af
22 changed files with 1341 additions and 63 deletions
BIN
art/source/ui/title/bubbles/bubble1.xcf
Normal file
BIN
art/source/ui/title/bubbles/bubble1.xcf
Normal file
Binary file not shown.
BIN
art/source/ui/title/bubbles/bubble2.xcf
Normal file
BIN
art/source/ui/title/bubbles/bubble2.xcf
Normal file
Binary file not shown.
BIN
art/source/ui/title/bubbles/bubble3.xcf
Normal file
BIN
art/source/ui/title/bubbles/bubble3.xcf
Normal file
Binary file not shown.
BIN
audio/music/title/as_in_four_wolves.ogg
Normal file
BIN
audio/music/title/as_in_four_wolves.ogg
Normal file
Binary file not shown.
19
audio/music/title/as_in_four_wolves.ogg.import
Normal file
19
audio/music/title/as_in_four_wolves.ogg.import
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="oggvorbisstr"
|
||||||
|
type="AudioStreamOggVorbis"
|
||||||
|
uid="uid://bgkfluumuen4m"
|
||||||
|
path="res://.godot/imported/as_in_four_wolves.ogg-3d5f167a45d25a52b56a9dd816196d44.oggvorbisstr"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://audio/music/title/as_in_four_wolves.ogg"
|
||||||
|
dest_files=["res://.godot/imported/as_in_four_wolves.ogg-3d5f167a45d25a52b56a9dd816196d44.oggvorbisstr"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=true
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
15
default_bus_layout.tres
Normal file
15
default_bus_layout.tres
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
[gd_resource type="AudioBusLayout" format=3]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
bus/0/name = &"Master"
|
||||||
|
bus/0/solo = false
|
||||||
|
bus/0/mute = false
|
||||||
|
bus/0/bypass_fx = false
|
||||||
|
bus/0/volume_db = 0.0
|
||||||
|
bus/0/send = &""
|
||||||
|
bus/1/name = &"Music"
|
||||||
|
bus/1/solo = false
|
||||||
|
bus/1/mute = false
|
||||||
|
bus/1/bypass_fx = false
|
||||||
|
bus/1/volume_db = 0.0
|
||||||
|
bus/1/send = &"Master"
|
||||||
125
main/main.gd
125
main/main.gd
|
|
@ -25,10 +25,16 @@ const FishingShopInteractionType = preload(
|
||||||
"res://world/fishing_shop_interaction.gd"
|
"res://world/fishing_shop_interaction.gd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const TITLE_MUSIC_SILENCE_DB: float = -80.0
|
||||||
|
|
||||||
@export var fish_catalog: FishPoolType
|
@export var fish_catalog: FishPoolType
|
||||||
@export var pelican_buyer_profile: FishBuyerProfileType
|
@export var pelican_buyer_profile: FishBuyerProfileType
|
||||||
@export var main_shop_buyer_profile: FishBuyerProfileType
|
@export var main_shop_buyer_profile: FishBuyerProfileType
|
||||||
@export var item_catalog: ItemCatalogType
|
@export var item_catalog: ItemCatalogType
|
||||||
|
@export_category("Title Music")
|
||||||
|
@export_range(-40.0, 0.0, 0.5) var title_music_volume_db: float = -14.0
|
||||||
|
@export_range(0.0, 5.0, 0.05) var title_music_fade_in_seconds: float = 1.0
|
||||||
|
@export_range(0.0, 5.0, 0.05) var title_music_fade_out_seconds: float = 0.75
|
||||||
|
|
||||||
@onready var _test_world: TestWorldType = $TestWorld
|
@onready var _test_world: TestWorldType = $TestWorld
|
||||||
@onready var _player: PlayerType = %Player
|
@onready var _player: PlayerType = %Player
|
||||||
|
|
@ -37,9 +43,14 @@ const FishingShopInteractionType = preload(
|
||||||
@onready var _water_recovery: WaterRecoveryControllerType = %WaterRecovery
|
@onready var _water_recovery: WaterRecoveryControllerType = %WaterRecovery
|
||||||
@onready var _save_manager: PlayerSaveManagerType = %PlayerSaveManager
|
@onready var _save_manager: PlayerSaveManagerType = %PlayerSaveManager
|
||||||
@onready var _settings_manager: PlayerSettingsManagerType = %PlayerSettingsManager
|
@onready var _settings_manager: PlayerSettingsManagerType = %PlayerSettingsManager
|
||||||
|
@onready var _title_music: AudioStreamPlayer = %TitleMusic
|
||||||
|
|
||||||
var _gameplay_started: bool = false
|
var _gameplay_started: bool = false
|
||||||
var _shop_interaction: FishingShopInteractionType
|
var _shop_interaction: FishingShopInteractionType
|
||||||
|
var _title_music_tween: Tween
|
||||||
|
var _title_music_transition_generation: int = 0
|
||||||
|
var _title_music_requested: bool = false
|
||||||
|
var _quit_in_progress: bool = false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -144,6 +155,7 @@ func _ready() -> void:
|
||||||
_player.hotbar.get_selected_slot(),
|
_player.hotbar.get_selected_slot(),
|
||||||
_player.hotbar.get_selected_item_id()
|
_player.hotbar.get_selected_item_id()
|
||||||
)
|
)
|
||||||
|
_show_title_music()
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
|
|
@ -226,20 +238,26 @@ func _set_gameplay_active(active: bool) -> void:
|
||||||
|
|
||||||
|
|
||||||
func _on_gameplay_requested() -> void:
|
func _on_gameplay_requested() -> void:
|
||||||
if _gameplay_started:
|
if _gameplay_started or _quit_in_progress:
|
||||||
return
|
return
|
||||||
|
_fade_out_title_music()
|
||||||
_game_ui.get_title_screen().hide()
|
_game_ui.get_title_screen().hide()
|
||||||
_set_gameplay_active(true)
|
_set_gameplay_active(true)
|
||||||
|
|
||||||
|
|
||||||
func _on_return_to_title_requested() -> void:
|
func _on_return_to_title_requested() -> void:
|
||||||
|
if _quit_in_progress:
|
||||||
|
return
|
||||||
var pause_menu: PauseMenuType = _game_ui.get_pause_menu()
|
var pause_menu: PauseMenuType = _game_ui.get_pause_menu()
|
||||||
pause_menu.close_for_title_transition()
|
pause_menu.close_for_title_transition()
|
||||||
_set_gameplay_active(false)
|
_set_gameplay_active(false)
|
||||||
_game_ui.get_title_screen().reopen()
|
_game_ui.get_title_screen().reopen()
|
||||||
|
_show_title_music()
|
||||||
|
|
||||||
|
|
||||||
func _on_reset_progress_requested() -> void:
|
func _on_reset_progress_requested() -> void:
|
||||||
|
if _quit_in_progress:
|
||||||
|
return
|
||||||
var pause_menu: PauseMenuType = _game_ui.get_pause_menu()
|
var pause_menu: PauseMenuType = _game_ui.get_pause_menu()
|
||||||
if not _save_manager.delete_progression_save():
|
if not _save_manager.delete_progression_save():
|
||||||
pause_menu.report_reset_failure()
|
pause_menu.report_reset_failure()
|
||||||
|
|
@ -250,6 +268,7 @@ func _on_reset_progress_requested() -> void:
|
||||||
pause_menu.close_for_title_transition()
|
pause_menu.close_for_title_transition()
|
||||||
_set_gameplay_active(false)
|
_set_gameplay_active(false)
|
||||||
_game_ui.get_title_screen().reopen()
|
_game_ui.get_title_screen().reopen()
|
||||||
|
_show_title_music()
|
||||||
|
|
||||||
|
|
||||||
func _on_water_recovery_starting() -> void:
|
func _on_water_recovery_starting() -> void:
|
||||||
|
|
@ -282,12 +301,116 @@ func _refresh_active_hotbar_item() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _on_quit_requested() -> void:
|
func _on_quit_requested() -> void:
|
||||||
|
if _quit_in_progress:
|
||||||
|
return
|
||||||
|
_quit_in_progress = true
|
||||||
_settings_manager.save_if_dirty()
|
_settings_manager.save_if_dirty()
|
||||||
if _gameplay_started:
|
if _gameplay_started:
|
||||||
_save_manager.save_if_dirty()
|
_save_manager.save_if_dirty()
|
||||||
|
_fade_out_title_music(_finish_quit)
|
||||||
|
|
||||||
|
|
||||||
|
func _show_title_music() -> void:
|
||||||
|
_title_music_requested = true
|
||||||
|
var generation: int = _replace_title_music_transition()
|
||||||
|
if not _title_music.playing:
|
||||||
|
_title_music.volume_db = TITLE_MUSIC_SILENCE_DB
|
||||||
|
_title_music.play()
|
||||||
|
if is_equal_approx(_title_music.volume_db, title_music_volume_db):
|
||||||
|
return
|
||||||
|
if title_music_fade_in_seconds <= 0.0:
|
||||||
|
_title_music.volume_db = title_music_volume_db
|
||||||
|
return
|
||||||
|
_title_music_tween = create_tween()
|
||||||
|
_title_music_tween.set_trans(Tween.TRANS_CUBIC)
|
||||||
|
_title_music_tween.set_ease(Tween.EASE_OUT)
|
||||||
|
_title_music_tween.tween_property(
|
||||||
|
_title_music,
|
||||||
|
"volume_db",
|
||||||
|
title_music_volume_db,
|
||||||
|
title_music_fade_in_seconds
|
||||||
|
)
|
||||||
|
_title_music_tween.finished.connect(
|
||||||
|
_on_title_music_faded_in.bind(generation),
|
||||||
|
CONNECT_ONE_SHOT
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _fade_out_title_music(on_complete: Callable = Callable()) -> void:
|
||||||
|
_title_music_requested = false
|
||||||
|
var generation: int = _replace_title_music_transition()
|
||||||
|
if not _title_music.playing:
|
||||||
|
_title_music.volume_db = title_music_volume_db
|
||||||
|
if on_complete.is_valid():
|
||||||
|
on_complete.call()
|
||||||
|
return
|
||||||
|
if title_music_fade_out_seconds <= 0.0:
|
||||||
|
_complete_title_music_fade_out(generation, on_complete)
|
||||||
|
return
|
||||||
|
_title_music_tween = create_tween()
|
||||||
|
_title_music_tween.set_trans(Tween.TRANS_CUBIC)
|
||||||
|
_title_music_tween.set_ease(Tween.EASE_IN)
|
||||||
|
_title_music_tween.tween_property(
|
||||||
|
_title_music,
|
||||||
|
"volume_db",
|
||||||
|
TITLE_MUSIC_SILENCE_DB,
|
||||||
|
title_music_fade_out_seconds
|
||||||
|
)
|
||||||
|
_title_music_tween.finished.connect(
|
||||||
|
_complete_title_music_fade_out.bind(generation, on_complete),
|
||||||
|
CONNECT_ONE_SHOT
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _replace_title_music_transition() -> int:
|
||||||
|
_title_music_transition_generation += 1
|
||||||
|
if _title_music_tween != null:
|
||||||
|
_title_music_tween.kill()
|
||||||
|
_title_music_tween = null
|
||||||
|
return _title_music_transition_generation
|
||||||
|
|
||||||
|
|
||||||
|
func _on_title_music_faded_in(generation: int) -> void:
|
||||||
|
if (
|
||||||
|
generation != _title_music_transition_generation
|
||||||
|
or not _title_music_requested
|
||||||
|
):
|
||||||
|
return
|
||||||
|
_title_music_tween = null
|
||||||
|
_title_music.volume_db = title_music_volume_db
|
||||||
|
|
||||||
|
|
||||||
|
func _complete_title_music_fade_out(
|
||||||
|
generation: int,
|
||||||
|
on_complete: Callable,
|
||||||
|
) -> void:
|
||||||
|
if (
|
||||||
|
generation != _title_music_transition_generation
|
||||||
|
or _title_music_requested
|
||||||
|
):
|
||||||
|
return
|
||||||
|
_title_music_tween = null
|
||||||
|
_title_music.stop()
|
||||||
|
_title_music.volume_db = title_music_volume_db
|
||||||
|
if on_complete.is_valid():
|
||||||
|
on_complete.call()
|
||||||
|
|
||||||
|
|
||||||
|
func _finish_quit() -> void:
|
||||||
|
_title_music.queue_free()
|
||||||
|
await get_tree().process_frame
|
||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
if _title_music_tween != null:
|
||||||
|
_title_music_tween.kill()
|
||||||
|
_title_music_tween = null
|
||||||
|
if is_instance_valid(_title_music):
|
||||||
|
_title_music.stop()
|
||||||
|
_title_music.stream = null
|
||||||
|
|
||||||
|
|
||||||
func _on_shop_range_changed(in_range: bool) -> void:
|
func _on_shop_range_changed(in_range: bool) -> void:
|
||||||
if not in_range:
|
if not in_range:
|
||||||
_game_ui.get_fishing_shop().close_for_range_exit()
|
_game_ui.get_fishing_shop().close_for_range_exit()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=13 format=3]
|
[gd_scene load_steps=14 format=3]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
|
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
|
||||||
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
|
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
[ext_resource type="Script" path="res://settings/player_settings_manager.gd" id="10_settings"]
|
[ext_resource type="Script" path="res://settings/player_settings_manager.gd" id="10_settings"]
|
||||||
[ext_resource type="Resource" path="res://items/catalog/item_catalog.tres" id="11_items"]
|
[ext_resource type="Resource" path="res://items/catalog/item_catalog.tres" id="11_items"]
|
||||||
[ext_resource type="Resource" path="res://economy/buyers/main_fishing_shop.tres" id="12_main_shop"]
|
[ext_resource type="Resource" path="res://economy/buyers/main_fishing_shop.tres" id="12_main_shop"]
|
||||||
|
[ext_resource type="AudioStream" path="res://audio/music/title/as_in_four_wolves.ogg" id="13_title_music"]
|
||||||
|
|
||||||
[node name="Main" type="Node3D"]
|
[node name="Main" type="Node3D"]
|
||||||
script = ExtResource("3_main")
|
script = ExtResource("3_main")
|
||||||
|
|
@ -44,5 +45,11 @@ script = ExtResource("9_save")
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
script = ExtResource("10_settings")
|
script = ExtResource("10_settings")
|
||||||
|
|
||||||
|
[node name="TitleMusic" type="AudioStreamPlayer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
stream = ExtResource("13_title_music")
|
||||||
|
volume_db = -80.0
|
||||||
|
bus = &"Music"
|
||||||
|
|
||||||
[node name="GameUI" parent="." instance=ExtResource("5_ui")]
|
[node name="GameUI" parent="." instance=ExtResource("5_ui")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
|
||||||
BIN
ui/assets/title/bubbles/bubble1.png
Normal file
BIN
ui/assets/title/bubbles/bubble1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 372 B |
40
ui/assets/title/bubbles/bubble1.png.import
Normal file
40
ui/assets/title/bubbles/bubble1.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dkvilbotou8bp"
|
||||||
|
path="res://.godot/imported/bubble1.png-c853cfc8f5bc5b89bb69a71c23de669d.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/assets/title/bubbles/bubble1.png"
|
||||||
|
dest_files=["res://.godot/imported/bubble1.png-c853cfc8f5bc5b89bb69a71c23de669d.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
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/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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
|
||||||
BIN
ui/assets/title/bubbles/bubble2.png
Normal file
BIN
ui/assets/title/bubbles/bubble2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 357 B |
40
ui/assets/title/bubbles/bubble2.png.import
Normal file
40
ui/assets/title/bubbles/bubble2.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dh11c1271egs7"
|
||||||
|
path="res://.godot/imported/bubble2.png-3fe11ab91543c1b1505cfc1100bfd0c7.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/assets/title/bubbles/bubble2.png"
|
||||||
|
dest_files=["res://.godot/imported/bubble2.png-3fe11ab91543c1b1505cfc1100bfd0c7.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
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/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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
|
||||||
BIN
ui/assets/title/bubbles/bubble3.png
Normal file
BIN
ui/assets/title/bubbles/bubble3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 360 B |
40
ui/assets/title/bubbles/bubble3.png.import
Normal file
40
ui/assets/title/bubbles/bubble3.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b5praw1x4vukl"
|
||||||
|
path="res://.godot/imported/bubble3.png-7e3e34f881bccf21cec803168829b93e.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/assets/title/bubbles/bubble3.png"
|
||||||
|
dest_files=["res://.godot/imported/bubble3.png-7e3e34f881bccf21cec803168829b93e.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
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/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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
|
||||||
BIN
ui/assets/title/netfishing_logo.png
Normal file
BIN
ui/assets/title/netfishing_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
40
ui/assets/title/netfishing_logo.png.import
Normal file
40
ui/assets/title/netfishing_logo.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ckqayis1ckb2v"
|
||||||
|
path="res://.godot/imported/netfishing_logo.png-82ab9fce5851dcc5239eca178e8cde99.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://ui/assets/title/netfishing_logo.png"
|
||||||
|
dest_files=["res://.godot/imported/netfishing_logo.png-82ab9fce5851dcc5239eca178e8cde99.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
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/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
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
|
||||||
48
ui/title_logo_underwater.gdshader
Normal file
48
ui/title_logo_underwater.gdshader
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
uniform float horizontal_displacement_pixels : hint_range(0.0, 2.0, 0.25) = 1.5;
|
||||||
|
uniform float wave_speed : hint_range(0.0, 2.0, 0.05) = 0.55;
|
||||||
|
uniform float wave_scale : hint_range(0.0, 0.20, 0.005) = 0.055;
|
||||||
|
uniform vec3 underwater_tint : source_color = vec3(0.78, 0.96, 1.08);
|
||||||
|
uniform float tint_strength : hint_range(0.0, 0.35, 0.01) = 0.18;
|
||||||
|
uniform float highlight_strength : hint_range(0.0, 0.15, 0.01) = 0.06;
|
||||||
|
uniform float bob_amount_pixels : hint_range(0.0, 4.0, 1.0) = 4.0;
|
||||||
|
uniform float bob_speed : hint_range(0.0, 2.0, 0.05) = 0.65;
|
||||||
|
|
||||||
|
void vertex() {
|
||||||
|
VERTEX.y += round(
|
||||||
|
sin(TIME * bob_speed) * bob_amount_pixels
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
float stepped_time = floor(TIME * 12.0) / 12.0;
|
||||||
|
float source_row = floor(UV.y / max(TEXTURE_PIXEL_SIZE.y, 0.000001));
|
||||||
|
float wave = sin(
|
||||||
|
source_row * wave_scale + stepped_time * wave_speed
|
||||||
|
);
|
||||||
|
float pixel_offset = clamp(
|
||||||
|
round(wave * horizontal_displacement_pixels),
|
||||||
|
-2.0,
|
||||||
|
2.0
|
||||||
|
);
|
||||||
|
vec2 sample_uv = UV + vec2(pixel_offset * TEXTURE_PIXEL_SIZE.x, 0.0);
|
||||||
|
vec4 source = texture(TEXTURE, sample_uv);
|
||||||
|
|
||||||
|
vec3 treated_color = mix(
|
||||||
|
source.rgb,
|
||||||
|
source.rgb * underwater_tint,
|
||||||
|
tint_strength
|
||||||
|
);
|
||||||
|
float highlight_wave = sin(
|
||||||
|
UV.y * 26.0 - stepped_time * wave_speed * 0.8
|
||||||
|
) * 0.5 + 0.5;
|
||||||
|
float highlight_level = floor(highlight_wave * 3.0) / 3.0;
|
||||||
|
treated_color += (
|
||||||
|
vec3(0.12, 0.30, 0.34)
|
||||||
|
* highlight_level
|
||||||
|
* highlight_strength
|
||||||
|
* source.a
|
||||||
|
);
|
||||||
|
COLOR = vec4(treated_color, source.a);
|
||||||
|
}
|
||||||
1
ui/title_logo_underwater.gdshader.uid
Normal file
1
ui/title_logo_underwater.gdshader.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bu14y18kegufh
|
||||||
|
|
@ -7,6 +7,52 @@ const SettingsManagerType = preload(
|
||||||
"res://settings/player_settings_manager.gd"
|
"res://settings/player_settings_manager.gd"
|
||||||
)
|
)
|
||||||
const SettingsPanelType = preload("res://ui/settings_panel.gd")
|
const SettingsPanelType = preload("res://ui/settings_panel.gd")
|
||||||
|
const DECORATIVE_FISH_TEXTURES: Array[Texture2D] = [
|
||||||
|
preload("res://fish/species/bass/fish_bass_striped.png"),
|
||||||
|
preload("res://fish/species/bluegill/fish_bluegill.png"),
|
||||||
|
preload("res://fish/species/carp/fish_carp_common.png"),
|
||||||
|
preload("res://fish/species/sunfish/fish_sunfish.png"),
|
||||||
|
]
|
||||||
|
const DECORATIVE_BUBBLE_TEXTURES: Array[Texture2D] = [
|
||||||
|
preload("res://ui/assets/title/bubbles/bubble1.png"),
|
||||||
|
preload("res://ui/assets/title/bubbles/bubble2.png"),
|
||||||
|
preload("res://ui/assets/title/bubbles/bubble3.png"),
|
||||||
|
]
|
||||||
|
|
||||||
|
const FIRST_FISH_DELAY_MIN: float = 2.0
|
||||||
|
const FIRST_FISH_DELAY_MAX: float = 5.0
|
||||||
|
const NEXT_FISH_DELAY_MIN: float = 5.0
|
||||||
|
const NEXT_FISH_DELAY_MAX: float = 12.0
|
||||||
|
const CROSSING_DURATION_MIN: float = 8.0
|
||||||
|
const CROSSING_DURATION_MAX: float = 16.0
|
||||||
|
const FISH_LONGEST_SIDE_MIN: float = 120.0
|
||||||
|
const FISH_LONGEST_SIDE_MAX: float = 210.0
|
||||||
|
const FISH_EDGE_MARGIN: float = 24.0
|
||||||
|
const BUBBLE_EVENT_DELAY_MIN: float = 4.0
|
||||||
|
const BUBBLE_EVENT_DELAY_MAX: float = 10.0
|
||||||
|
const BUBBLE_CLUSTER_DELAY_MIN: float = 0.25
|
||||||
|
const BUBBLE_CLUSTER_DELAY_MAX: float = 0.65
|
||||||
|
const BUBBLE_TRAVEL_DURATION_MIN: float = 7.0
|
||||||
|
const BUBBLE_TRAVEL_DURATION_MAX: float = 14.0
|
||||||
|
const BUBBLE_SCALE_MIN: float = 0.65
|
||||||
|
const BUBBLE_SCALE_MAX: float = 1.15
|
||||||
|
const BUBBLE_OPACITY_MIN: float = 0.55
|
||||||
|
const BUBBLE_OPACITY_MAX: float = 0.90
|
||||||
|
const BUBBLE_DRIFT_MIN: float = 10.0
|
||||||
|
const BUBBLE_DRIFT_MAX: float = 45.0
|
||||||
|
const BUBBLE_WOBBLE_MIN: float = 3.0
|
||||||
|
const BUBBLE_WOBBLE_MAX: float = 9.0
|
||||||
|
const BUBBLE_EDGE_MARGIN: float = 16.0
|
||||||
|
const MAX_DECORATIVE_BUBBLES: int = 6
|
||||||
|
const LOGO_ASPECT_RATIO: float = 2560.0 / 760.0
|
||||||
|
const LOGO_WIDTH_FACTOR: float = 0.52
|
||||||
|
const LOGO_MIN_WIDTH: float = 320.0
|
||||||
|
const LOGO_MAX_WIDTH: float = 720.0
|
||||||
|
const BUTTON_COLUMN_WIDTH: float = 360.0
|
||||||
|
const TITLE_HORIZONTAL_MARGIN: float = 48.0
|
||||||
|
const START_PROMPT_MIN_SCALE: float = 0.985
|
||||||
|
const START_PROMPT_MAX_SCALE: float = 1.015
|
||||||
|
const START_PROMPT_CYCLE_SECONDS: float = 3.0
|
||||||
|
|
||||||
signal gameplay_requested
|
signal gameplay_requested
|
||||||
signal quit_requested
|
signal quit_requested
|
||||||
|
|
@ -21,17 +67,41 @@ enum ConfirmationAction {
|
||||||
@onready var _new_game_button: Button = %NewGameButton
|
@onready var _new_game_button: Button = %NewGameButton
|
||||||
@onready var _settings_button: Button = %SettingsButton
|
@onready var _settings_button: Button = %SettingsButton
|
||||||
@onready var _delete_button: Button = %DeleteSaveButton
|
@onready var _delete_button: Button = %DeleteSaveButton
|
||||||
|
@onready var _quit_button: Button = %QuitButton
|
||||||
@onready var _feedback_label: Label = %FeedbackLabel
|
@onready var _feedback_label: Label = %FeedbackLabel
|
||||||
@onready var _confirmation_panel: PanelContainer = %ConfirmationPanel
|
@onready var _confirmation_panel: PanelContainer = %ConfirmationPanel
|
||||||
@onready var _confirmation_text: Label = %ConfirmationText
|
@onready var _confirmation_text: Label = %ConfirmationText
|
||||||
@onready var _confirm_button: Button = %ConfirmButton
|
@onready var _confirm_button: Button = %ConfirmButton
|
||||||
@onready var _settings_panel: SettingsPanelType = %SettingsPanel
|
@onready var _settings_panel: SettingsPanelType = %SettingsPanel
|
||||||
|
@onready var _decorative_fish_layer: Control = %DecorativeFishLayer
|
||||||
|
@onready var _decorative_fish_timer: Timer = %DecorativeFishTimer
|
||||||
|
@onready var _decorative_bubble_layer: Control = %DecorativeBubbleLayer
|
||||||
|
@onready var _decorative_bubble_event_timer: Timer = %DecorativeBubbleEventTimer
|
||||||
|
@onready var _decorative_bubble_cluster_timer: Timer = %DecorativeBubbleClusterTimer
|
||||||
|
@onready var _title_logo: TextureRect = %TitleLogo
|
||||||
|
@onready var _button_center: CenterContainer = %ButtonCenter
|
||||||
|
@onready var _button_stack: VBoxContainer = %ButtonStack
|
||||||
|
@onready var _start_prompt_center: CenterContainer = %StartPromptCenter
|
||||||
|
@onready var _start_prompt_label: Label = %StartPromptLabel
|
||||||
|
|
||||||
var _save_manager: SaveManagerType
|
var _save_manager: SaveManagerType
|
||||||
var _settings_manager: SettingsManagerType
|
var _settings_manager: SettingsManagerType
|
||||||
var _inspection: SaveInspectionType
|
var _inspection: SaveInspectionType
|
||||||
var _confirmation_action: ConfirmationAction = ConfirmationAction.NONE
|
var _confirmation_action: ConfirmationAction = ConfirmationAction.NONE
|
||||||
var _action_in_progress: bool = false
|
var _action_in_progress: bool = false
|
||||||
|
var _decorative_rng := RandomNumberGenerator.new()
|
||||||
|
var _decorative_fish: TextureRect
|
||||||
|
var _decorative_fish_tween: Tween
|
||||||
|
var _decorative_presentation_ready: bool = false
|
||||||
|
var _decorative_presentation_active: bool = false
|
||||||
|
var _decorative_generation: int = 0
|
||||||
|
var _decorative_bubbles: Array[TextureRect] = []
|
||||||
|
var _decorative_bubble_tweens: Dictionary[int, Tween] = {}
|
||||||
|
var _pending_cluster_bubbles: int = 0
|
||||||
|
var _awaiting_start_input: bool = false
|
||||||
|
var _start_prompt_elapsed: float = 0.0
|
||||||
|
var _navigation_focus_active: bool = false
|
||||||
|
var _modal_restore_navigation_focus: bool = false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -44,6 +114,20 @@ func _ready() -> void:
|
||||||
%CancelConfirmButton.pressed.connect(_close_confirmation)
|
%CancelConfirmButton.pressed.connect(_close_confirmation)
|
||||||
_settings_panel.applied.connect(_on_settings_applied)
|
_settings_panel.applied.connect(_on_settings_applied)
|
||||||
_settings_panel.closed.connect(_on_settings_closed)
|
_settings_panel.closed.connect(_on_settings_closed)
|
||||||
|
_decorative_fish_timer.timeout.connect(_on_decorative_fish_timer_timeout)
|
||||||
|
_decorative_bubble_event_timer.timeout.connect(
|
||||||
|
_on_decorative_bubble_event_timer_timeout
|
||||||
|
)
|
||||||
|
_decorative_bubble_cluster_timer.timeout.connect(
|
||||||
|
_on_decorative_bubble_cluster_timer_timeout
|
||||||
|
)
|
||||||
|
visibility_changed.connect(_on_title_visibility_changed)
|
||||||
|
resized.connect(_update_title_layout)
|
||||||
|
_start_prompt_label.resized.connect(_update_start_prompt_pivot)
|
||||||
|
_decorative_rng.randomize()
|
||||||
|
set_process(false)
|
||||||
|
call_deferred("_update_title_layout")
|
||||||
|
call_deferred("_update_start_prompt_pivot")
|
||||||
|
|
||||||
|
|
||||||
func setup(
|
func setup(
|
||||||
|
|
@ -55,20 +139,104 @@ func setup(
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||||
_refresh_save_inspection()
|
_refresh_save_inspection()
|
||||||
show()
|
show()
|
||||||
call_deferred("_focus_initial_button")
|
_decorative_presentation_ready = true
|
||||||
|
_start_decorative_presentation()
|
||||||
|
_begin_title_entry()
|
||||||
|
|
||||||
|
|
||||||
func reopen() -> void:
|
func reopen() -> void:
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||||
|
_prepare_awaiting_start_input()
|
||||||
_close_confirmation()
|
_close_confirmation()
|
||||||
_settings_panel.hide()
|
_settings_panel.hide()
|
||||||
_refresh_save_inspection()
|
_refresh_save_inspection()
|
||||||
show()
|
show()
|
||||||
call_deferred("_focus_initial_button")
|
_start_decorative_presentation()
|
||||||
|
_start_entry_prompt_animation()
|
||||||
|
|
||||||
|
|
||||||
|
func is_awaiting_start_input() -> bool:
|
||||||
|
return _awaiting_start_input
|
||||||
|
|
||||||
|
|
||||||
|
func is_decorative_presentation_active() -> bool:
|
||||||
|
return _decorative_presentation_active
|
||||||
|
|
||||||
|
|
||||||
|
func get_active_decorative_fish_count() -> int:
|
||||||
|
return 1 if is_instance_valid(_decorative_fish) else 0
|
||||||
|
|
||||||
|
|
||||||
|
func get_active_decorative_bubble_count() -> int:
|
||||||
|
return _decorative_bubbles.size()
|
||||||
|
|
||||||
|
|
||||||
|
func is_decorative_bubble_scheduler_active() -> bool:
|
||||||
|
return (
|
||||||
|
_decorative_presentation_active
|
||||||
|
and (
|
||||||
|
not _decorative_bubble_event_timer.is_stopped()
|
||||||
|
or not _decorative_bubble_cluster_timer.is_stopped()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _update_title_layout() -> void:
|
||||||
|
if not is_node_ready():
|
||||||
|
return
|
||||||
|
var available_width: float = maxf(1.0, size.x - TITLE_HORIZONTAL_MARGIN)
|
||||||
|
var logo_width: float = minf(
|
||||||
|
clampf(
|
||||||
|
size.x * LOGO_WIDTH_FACTOR,
|
||||||
|
LOGO_MIN_WIDTH,
|
||||||
|
LOGO_MAX_WIDTH
|
||||||
|
),
|
||||||
|
available_width
|
||||||
|
)
|
||||||
|
_title_logo.custom_minimum_size = Vector2(
|
||||||
|
logo_width,
|
||||||
|
logo_width / LOGO_ASPECT_RATIO
|
||||||
|
)
|
||||||
|
_button_stack.custom_minimum_size = Vector2(
|
||||||
|
minf(BUTTON_COLUMN_WIDTH, available_width),
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
if (
|
||||||
|
not _awaiting_start_input
|
||||||
|
or not visible
|
||||||
|
or not _start_prompt_center.visible
|
||||||
|
):
|
||||||
|
return
|
||||||
|
_start_prompt_elapsed = fmod(
|
||||||
|
_start_prompt_elapsed + delta,
|
||||||
|
START_PROMPT_CYCLE_SECONDS
|
||||||
|
)
|
||||||
|
var phase: float = _start_prompt_elapsed / START_PROMPT_CYCLE_SECONDS
|
||||||
|
var pulse_weight: float = (sin(phase * TAU - PI * 0.5) + 1.0) * 0.5
|
||||||
|
var prompt_scale: float = lerpf(
|
||||||
|
START_PROMPT_MIN_SCALE,
|
||||||
|
START_PROMPT_MAX_SCALE,
|
||||||
|
pulse_weight
|
||||||
|
)
|
||||||
|
_start_prompt_label.scale = Vector2.ONE * prompt_scale
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
if not visible or not event.is_action_pressed("ui_cancel"):
|
if not visible:
|
||||||
|
return
|
||||||
|
if _awaiting_start_input:
|
||||||
|
if not _is_start_prompt_reveal_event(event):
|
||||||
|
return
|
||||||
|
_reveal_primary_menu()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
|
if _handle_primary_menu_focus_input(event):
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
|
if not event.is_action_pressed("ui_cancel"):
|
||||||
return
|
return
|
||||||
if _confirmation_panel.visible:
|
if _confirmation_panel.visible:
|
||||||
_close_confirmation()
|
_close_confirmation()
|
||||||
|
|
@ -79,6 +247,119 @@ func _input(event: InputEvent) -> void:
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
|
func _is_start_prompt_reveal_event(event: InputEvent) -> bool:
|
||||||
|
if event is InputEventKey:
|
||||||
|
var key_event := event as InputEventKey
|
||||||
|
return key_event.pressed and not key_event.echo
|
||||||
|
if event is InputEventJoypadButton:
|
||||||
|
return (event as InputEventJoypadButton).pressed
|
||||||
|
if event is InputEventMouseButton:
|
||||||
|
return (event as InputEventMouseButton).pressed
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_primary_menu_focus_input(event: InputEvent) -> bool:
|
||||||
|
if (
|
||||||
|
not _button_center.visible
|
||||||
|
or _confirmation_panel.visible
|
||||||
|
or _settings_panel.visible
|
||||||
|
):
|
||||||
|
return false
|
||||||
|
if event is InputEventMouseMotion:
|
||||||
|
_navigation_focus_active = false
|
||||||
|
_release_primary_menu_focus()
|
||||||
|
return false
|
||||||
|
if event is InputEventKey and (event as InputEventKey).echo:
|
||||||
|
return false
|
||||||
|
var moves_forward: bool = (
|
||||||
|
event.is_action_pressed("ui_down")
|
||||||
|
or event.is_action_pressed("ui_right")
|
||||||
|
)
|
||||||
|
var moves_backward: bool = (
|
||||||
|
event.is_action_pressed("ui_up")
|
||||||
|
or event.is_action_pressed("ui_left")
|
||||||
|
)
|
||||||
|
if not moves_forward and not moves_backward:
|
||||||
|
return false
|
||||||
|
_navigation_focus_active = true
|
||||||
|
if _primary_menu_has_focus():
|
||||||
|
return false
|
||||||
|
if moves_forward:
|
||||||
|
_get_first_available_menu_button().grab_focus()
|
||||||
|
else:
|
||||||
|
_quit_button.grab_focus()
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func _get_first_available_menu_button() -> Button:
|
||||||
|
return _continue_button if not _continue_button.disabled else _new_game_button
|
||||||
|
|
||||||
|
|
||||||
|
func _primary_menu_has_focus() -> bool:
|
||||||
|
var focus_owner: Control = get_viewport().gui_get_focus_owner()
|
||||||
|
return focus_owner != null and _button_center.is_ancestor_of(focus_owner)
|
||||||
|
|
||||||
|
|
||||||
|
func _release_primary_menu_focus() -> void:
|
||||||
|
var focus_owner: Control = get_viewport().gui_get_focus_owner()
|
||||||
|
if focus_owner != null and _button_center.is_ancestor_of(focus_owner):
|
||||||
|
focus_owner.release_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func _release_title_focus() -> void:
|
||||||
|
var focus_owner: Control = get_viewport().gui_get_focus_owner()
|
||||||
|
if focus_owner != null and is_ancestor_of(focus_owner):
|
||||||
|
focus_owner.release_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func _begin_title_entry() -> void:
|
||||||
|
_prepare_awaiting_start_input()
|
||||||
|
_start_entry_prompt_animation()
|
||||||
|
|
||||||
|
|
||||||
|
func _prepare_awaiting_start_input() -> void:
|
||||||
|
_awaiting_start_input = true
|
||||||
|
_navigation_focus_active = false
|
||||||
|
_modal_restore_navigation_focus = false
|
||||||
|
_button_center.hide()
|
||||||
|
_feedback_label.hide()
|
||||||
|
_start_prompt_center.show()
|
||||||
|
var focus_owner: Control = get_viewport().gui_get_focus_owner()
|
||||||
|
if focus_owner != null and is_ancestor_of(focus_owner):
|
||||||
|
focus_owner.release_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func _start_entry_prompt_animation() -> void:
|
||||||
|
_start_prompt_elapsed = 0.0
|
||||||
|
_start_prompt_label.scale = Vector2.ONE * START_PROMPT_MIN_SCALE
|
||||||
|
_update_start_prompt_pivot()
|
||||||
|
set_process(true)
|
||||||
|
|
||||||
|
|
||||||
|
func _stop_entry_prompt_animation() -> void:
|
||||||
|
set_process(false)
|
||||||
|
_start_prompt_elapsed = 0.0
|
||||||
|
_start_prompt_label.scale = Vector2.ONE
|
||||||
|
|
||||||
|
|
||||||
|
func _update_start_prompt_pivot() -> void:
|
||||||
|
if not is_node_ready():
|
||||||
|
return
|
||||||
|
_start_prompt_label.pivot_offset = _start_prompt_label.size * 0.5
|
||||||
|
|
||||||
|
|
||||||
|
func _reveal_primary_menu() -> void:
|
||||||
|
if not _awaiting_start_input:
|
||||||
|
return
|
||||||
|
_awaiting_start_input = false
|
||||||
|
_stop_entry_prompt_animation()
|
||||||
|
_start_prompt_center.hide()
|
||||||
|
_button_center.show()
|
||||||
|
_feedback_label.show()
|
||||||
|
_navigation_focus_active = false
|
||||||
|
_release_title_focus()
|
||||||
|
|
||||||
|
|
||||||
func _on_continue_pressed() -> void:
|
func _on_continue_pressed() -> void:
|
||||||
if (
|
if (
|
||||||
_action_in_progress
|
_action_in_progress
|
||||||
|
|
@ -90,11 +371,11 @@ func _on_continue_pressed() -> void:
|
||||||
return
|
return
|
||||||
_action_in_progress = true
|
_action_in_progress = true
|
||||||
if _save_manager.load_player_data():
|
if _save_manager.load_player_data():
|
||||||
_feedback_label.text = "Save loaded."
|
_feedback_label.text = "save loaded."
|
||||||
gameplay_requested.emit()
|
gameplay_requested.emit()
|
||||||
else:
|
else:
|
||||||
_refresh_save_inspection()
|
_refresh_save_inspection()
|
||||||
_feedback_label.text = "Failed to load save. The original was preserved."
|
_feedback_label.text = "failed to load save. the original was preserved."
|
||||||
_action_in_progress = false
|
_action_in_progress = false
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -112,17 +393,17 @@ func _on_new_game_pressed() -> void:
|
||||||
return
|
return
|
||||||
if _inspection.status == SaveInspectionType.Status.UNSUPPORTED_VERSION:
|
if _inspection.status == SaveInspectionType.Status.UNSUPPORTED_VERSION:
|
||||||
_feedback_label.text = (
|
_feedback_label.text = (
|
||||||
"This save is from a newer game version. "
|
"this save is from a newer game version. "
|
||||||
+ "Use Delete Save before starting over."
|
+ "use delete save before starting over."
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if _inspection.status == SaveInspectionType.Status.IO_ERROR:
|
if _inspection.status == SaveInspectionType.Status.IO_ERROR:
|
||||||
_feedback_label.text = "The existing save cannot be accessed safely."
|
_feedback_label.text = "the existing save cannot be accessed safely."
|
||||||
return
|
return
|
||||||
_open_confirmation(
|
_open_confirmation(
|
||||||
ConfirmationAction.NEW_GAME,
|
ConfirmationAction.NEW_GAME,
|
||||||
"Start a new game? Existing progression will be deleted.",
|
"start a new game? existing progression will be deleted.",
|
||||||
"Start New Game"
|
"start new game"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -137,8 +418,8 @@ func _on_delete_pressed() -> void:
|
||||||
return
|
return
|
||||||
_open_confirmation(
|
_open_confirmation(
|
||||||
ConfirmationAction.DELETE_SAVE,
|
ConfirmationAction.DELETE_SAVE,
|
||||||
"Delete your saved progression? This cannot be undone.",
|
"delete your saved progression? this cannot be undone.",
|
||||||
"Delete"
|
"delete"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -149,7 +430,7 @@ func _on_confirmation_accepted() -> void:
|
||||||
_close_confirmation()
|
_close_confirmation()
|
||||||
_action_in_progress = true
|
_action_in_progress = true
|
||||||
if not _save_manager.delete_progression_save():
|
if not _save_manager.delete_progression_save():
|
||||||
_feedback_label.text = "Failed to delete saved progression."
|
_feedback_label.text = "failed to delete saved progression."
|
||||||
_action_in_progress = false
|
_action_in_progress = false
|
||||||
_refresh_save_inspection()
|
_refresh_save_inspection()
|
||||||
return
|
return
|
||||||
|
|
@ -158,7 +439,7 @@ func _on_confirmation_accepted() -> void:
|
||||||
if action == ConfirmationAction.NEW_GAME:
|
if action == ConfirmationAction.NEW_GAME:
|
||||||
gameplay_requested.emit()
|
gameplay_requested.emit()
|
||||||
else:
|
else:
|
||||||
_feedback_label.text = "Saved progression deleted."
|
_feedback_label.text = "saved progression deleted."
|
||||||
_action_in_progress = false
|
_action_in_progress = false
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -167,6 +448,7 @@ func _open_confirmation(
|
||||||
message: String,
|
message: String,
|
||||||
confirm_text: String,
|
confirm_text: String,
|
||||||
) -> void:
|
) -> void:
|
||||||
|
_modal_restore_navigation_focus = _navigation_focus_active
|
||||||
_confirmation_action = action
|
_confirmation_action = action
|
||||||
_confirmation_text.text = message
|
_confirmation_text.text = message
|
||||||
_confirm_button.text = confirm_text
|
_confirm_button.text = confirm_text
|
||||||
|
|
@ -175,14 +457,23 @@ func _open_confirmation(
|
||||||
|
|
||||||
|
|
||||||
func _close_confirmation() -> void:
|
func _close_confirmation() -> void:
|
||||||
|
var restore_navigation_focus: bool = _modal_restore_navigation_focus
|
||||||
|
_modal_restore_navigation_focus = false
|
||||||
_confirmation_action = ConfirmationAction.NONE
|
_confirmation_action = ConfirmationAction.NONE
|
||||||
_confirmation_panel.visible = false
|
_confirmation_panel.visible = false
|
||||||
|
if _awaiting_start_input:
|
||||||
|
return
|
||||||
|
if restore_navigation_focus:
|
||||||
_focus_initial_button()
|
_focus_initial_button()
|
||||||
|
else:
|
||||||
|
_navigation_focus_active = false
|
||||||
|
_release_title_focus()
|
||||||
|
|
||||||
|
|
||||||
func _open_settings() -> void:
|
func _open_settings() -> void:
|
||||||
if _confirmation_panel.visible or _action_in_progress:
|
if _confirmation_panel.visible or _action_in_progress:
|
||||||
return
|
return
|
||||||
|
_modal_restore_navigation_focus = _navigation_focus_active
|
||||||
_feedback_label.text = ""
|
_feedback_label.text = ""
|
||||||
_settings_panel.open_panel(_settings_manager)
|
_settings_panel.open_panel(_settings_manager)
|
||||||
|
|
||||||
|
|
@ -192,12 +483,23 @@ func _close_settings() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _on_settings_applied() -> void:
|
func _on_settings_applied() -> void:
|
||||||
_feedback_label.text = "Settings saved."
|
_feedback_label.text = "settings saved."
|
||||||
_settings_button.grab_focus()
|
_restore_settings_focus()
|
||||||
|
|
||||||
|
|
||||||
func _on_settings_closed() -> void:
|
func _on_settings_closed() -> void:
|
||||||
|
_restore_settings_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func _restore_settings_focus() -> void:
|
||||||
|
var restore_navigation_focus: bool = _modal_restore_navigation_focus
|
||||||
|
_modal_restore_navigation_focus = false
|
||||||
|
if restore_navigation_focus:
|
||||||
|
_navigation_focus_active = true
|
||||||
_settings_button.grab_focus()
|
_settings_button.grab_focus()
|
||||||
|
else:
|
||||||
|
_navigation_focus_active = false
|
||||||
|
_release_title_focus()
|
||||||
|
|
||||||
|
|
||||||
func _refresh_save_inspection() -> void:
|
func _refresh_save_inspection() -> void:
|
||||||
|
|
@ -217,12 +519,18 @@ func _refresh_save_inspection() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _focus_initial_button() -> void:
|
func _focus_initial_button() -> void:
|
||||||
if not is_node_ready() or not visible:
|
if (
|
||||||
|
not is_node_ready()
|
||||||
|
or not visible
|
||||||
|
or _awaiting_start_input
|
||||||
|
or not _button_center.visible
|
||||||
|
):
|
||||||
return
|
return
|
||||||
if not _continue_button.disabled:
|
if not _continue_button.disabled:
|
||||||
_continue_button.grab_focus()
|
_continue_button.grab_focus()
|
||||||
else:
|
else:
|
||||||
_new_game_button.grab_focus()
|
_new_game_button.grab_focus()
|
||||||
|
_navigation_focus_active = true
|
||||||
|
|
||||||
|
|
||||||
func _on_quit_pressed() -> void:
|
func _on_quit_pressed() -> void:
|
||||||
|
|
@ -232,3 +540,430 @@ func _on_quit_pressed() -> void:
|
||||||
and not _settings_panel.visible
|
and not _settings_panel.visible
|
||||||
):
|
):
|
||||||
quit_requested.emit()
|
quit_requested.emit()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_title_visibility_changed() -> void:
|
||||||
|
if not _decorative_presentation_ready:
|
||||||
|
return
|
||||||
|
if visible:
|
||||||
|
_start_decorative_presentation()
|
||||||
|
else:
|
||||||
|
_stop_entry_prompt_animation()
|
||||||
|
_navigation_focus_active = false
|
||||||
|
_modal_restore_navigation_focus = false
|
||||||
|
_stop_decorative_presentation()
|
||||||
|
|
||||||
|
|
||||||
|
func _start_decorative_presentation() -> void:
|
||||||
|
if (
|
||||||
|
not _decorative_presentation_ready
|
||||||
|
or not visible
|
||||||
|
or _decorative_presentation_active
|
||||||
|
):
|
||||||
|
return
|
||||||
|
_decorative_generation += 1
|
||||||
|
_decorative_presentation_active = true
|
||||||
|
_schedule_next_decorative_fish(
|
||||||
|
FIRST_FISH_DELAY_MIN,
|
||||||
|
FIRST_FISH_DELAY_MAX
|
||||||
|
)
|
||||||
|
_schedule_next_decorative_bubble_event(
|
||||||
|
BUBBLE_EVENT_DELAY_MIN,
|
||||||
|
BUBBLE_EVENT_DELAY_MAX
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _stop_decorative_presentation() -> void:
|
||||||
|
_decorative_generation += 1
|
||||||
|
_decorative_presentation_active = false
|
||||||
|
_decorative_fish_timer.stop()
|
||||||
|
if _decorative_fish_tween != null:
|
||||||
|
_decorative_fish_tween.kill()
|
||||||
|
_decorative_fish_tween = null
|
||||||
|
if is_instance_valid(_decorative_fish):
|
||||||
|
_decorative_fish.queue_free()
|
||||||
|
_decorative_fish = null
|
||||||
|
_decorative_bubble_event_timer.stop()
|
||||||
|
_decorative_bubble_cluster_timer.stop()
|
||||||
|
_pending_cluster_bubbles = 0
|
||||||
|
for bubble_tween: Tween in _decorative_bubble_tweens.values():
|
||||||
|
if bubble_tween != null:
|
||||||
|
bubble_tween.kill()
|
||||||
|
_decorative_bubble_tweens.clear()
|
||||||
|
for bubble: TextureRect in _decorative_bubbles:
|
||||||
|
if is_instance_valid(bubble):
|
||||||
|
bubble.queue_free()
|
||||||
|
_decorative_bubbles.clear()
|
||||||
|
|
||||||
|
|
||||||
|
func _schedule_next_decorative_fish(
|
||||||
|
minimum_delay: float,
|
||||||
|
maximum_delay: float,
|
||||||
|
) -> void:
|
||||||
|
if not _decorative_presentation_active or not visible:
|
||||||
|
return
|
||||||
|
_decorative_fish_timer.start(
|
||||||
|
_decorative_rng.randf_range(minimum_delay, maximum_delay)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_decorative_fish_timer_timeout() -> void:
|
||||||
|
if (
|
||||||
|
not _decorative_presentation_active
|
||||||
|
or not visible
|
||||||
|
or is_instance_valid(_decorative_fish)
|
||||||
|
):
|
||||||
|
return
|
||||||
|
_spawn_decorative_fish(_decorative_generation)
|
||||||
|
|
||||||
|
|
||||||
|
func _spawn_decorative_fish(generation: int) -> void:
|
||||||
|
if (
|
||||||
|
generation != _decorative_generation
|
||||||
|
or not _decorative_presentation_active
|
||||||
|
or not visible
|
||||||
|
or DECORATIVE_FISH_TEXTURES.is_empty()
|
||||||
|
):
|
||||||
|
return
|
||||||
|
var layer_size: Vector2 = _decorative_fish_layer.size
|
||||||
|
if layer_size.x <= 1.0 or layer_size.y <= 1.0:
|
||||||
|
_schedule_next_decorative_fish(0.5, 1.0)
|
||||||
|
return
|
||||||
|
var texture: Texture2D = DECORATIVE_FISH_TEXTURES[
|
||||||
|
_decorative_rng.randi_range(0, DECORATIVE_FISH_TEXTURES.size() - 1)
|
||||||
|
]
|
||||||
|
var source_size: Vector2 = texture.get_size()
|
||||||
|
if source_size.x <= 0.0 or source_size.y <= 0.0:
|
||||||
|
_schedule_next_decorative_fish(
|
||||||
|
NEXT_FISH_DELAY_MIN,
|
||||||
|
NEXT_FISH_DELAY_MAX
|
||||||
|
)
|
||||||
|
return
|
||||||
|
var longest_side: float = _decorative_rng.randf_range(
|
||||||
|
FISH_LONGEST_SIDE_MIN,
|
||||||
|
FISH_LONGEST_SIDE_MAX
|
||||||
|
)
|
||||||
|
var presentation_scale: float = longest_side / maxf(
|
||||||
|
source_size.x,
|
||||||
|
source_size.y
|
||||||
|
)
|
||||||
|
var presentation_size: Vector2 = source_size * presentation_scale
|
||||||
|
var fish_control := TextureRect.new()
|
||||||
|
fish_control.name = "DecorativeFish"
|
||||||
|
fish_control.texture = texture
|
||||||
|
fish_control.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
fish_control.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
fish_control.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
fish_control.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
fish_control.custom_minimum_size = presentation_size
|
||||||
|
fish_control.size = presentation_size
|
||||||
|
fish_control.modulate.a = 0.46
|
||||||
|
|
||||||
|
var direction: float = (
|
||||||
|
1.0
|
||||||
|
if _decorative_rng.randi_range(0, 1) == 1
|
||||||
|
else -1.0
|
||||||
|
)
|
||||||
|
fish_control.flip_h = direction > 0.0
|
||||||
|
var minimum_y: float = maxf(24.0, layer_size.y * 0.08)
|
||||||
|
var maximum_y: float = maxf(
|
||||||
|
minimum_y,
|
||||||
|
layer_size.y - presentation_size.y - maxf(24.0, layer_size.y * 0.08)
|
||||||
|
)
|
||||||
|
var base_y: float = _decorative_rng.randf_range(minimum_y, maximum_y)
|
||||||
|
var bob_amplitude: float = _decorative_rng.randf_range(3.0, 8.0)
|
||||||
|
var bob_speed: float = _decorative_rng.randf_range(0.65, 1.15)
|
||||||
|
var crossing_duration: float = _decorative_rng.randf_range(
|
||||||
|
CROSSING_DURATION_MIN,
|
||||||
|
CROSSING_DURATION_MAX
|
||||||
|
)
|
||||||
|
fish_control.position = Vector2(
|
||||||
|
-presentation_size.x - FISH_EDGE_MARGIN
|
||||||
|
if direction > 0.0
|
||||||
|
else layer_size.x + FISH_EDGE_MARGIN,
|
||||||
|
base_y
|
||||||
|
)
|
||||||
|
_decorative_fish_layer.add_child(fish_control)
|
||||||
|
_decorative_fish = fish_control
|
||||||
|
_decorative_fish_tween = create_tween()
|
||||||
|
_decorative_fish_tween.tween_method(
|
||||||
|
_update_decorative_fish.bind(
|
||||||
|
fish_control,
|
||||||
|
direction,
|
||||||
|
base_y,
|
||||||
|
bob_amplitude,
|
||||||
|
bob_speed,
|
||||||
|
crossing_duration,
|
||||||
|
generation
|
||||||
|
),
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
crossing_duration
|
||||||
|
)
|
||||||
|
_decorative_fish_tween.finished.connect(
|
||||||
|
_on_decorative_fish_finished.bind(fish_control, generation),
|
||||||
|
CONNECT_ONE_SHOT
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _update_decorative_fish(
|
||||||
|
progress: float,
|
||||||
|
fish_control: TextureRect,
|
||||||
|
direction: float,
|
||||||
|
base_y: float,
|
||||||
|
bob_amplitude: float,
|
||||||
|
bob_speed: float,
|
||||||
|
crossing_duration: float,
|
||||||
|
generation: int,
|
||||||
|
) -> void:
|
||||||
|
if (
|
||||||
|
generation != _decorative_generation
|
||||||
|
or not _decorative_presentation_active
|
||||||
|
or not is_instance_valid(fish_control)
|
||||||
|
):
|
||||||
|
return
|
||||||
|
var start_x: float = (
|
||||||
|
-fish_control.size.x - FISH_EDGE_MARGIN
|
||||||
|
if direction > 0.0
|
||||||
|
else _decorative_fish_layer.size.x + FISH_EDGE_MARGIN
|
||||||
|
)
|
||||||
|
var end_x: float = (
|
||||||
|
_decorative_fish_layer.size.x + FISH_EDGE_MARGIN
|
||||||
|
if direction > 0.0
|
||||||
|
else -fish_control.size.x - FISH_EDGE_MARGIN
|
||||||
|
)
|
||||||
|
fish_control.position.x = lerpf(start_x, end_x, progress)
|
||||||
|
var bobbed_y: float = (
|
||||||
|
base_y
|
||||||
|
+ sin(progress * crossing_duration * bob_speed) * bob_amplitude
|
||||||
|
)
|
||||||
|
fish_control.position.y = clampf(
|
||||||
|
bobbed_y,
|
||||||
|
8.0,
|
||||||
|
maxf(8.0, _decorative_fish_layer.size.y - fish_control.size.y - 8.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_decorative_fish_finished(
|
||||||
|
fish_control: TextureRect,
|
||||||
|
generation: int,
|
||||||
|
) -> void:
|
||||||
|
if generation != _decorative_generation:
|
||||||
|
return
|
||||||
|
_decorative_fish_tween = null
|
||||||
|
if is_instance_valid(fish_control):
|
||||||
|
fish_control.queue_free()
|
||||||
|
if _decorative_fish == fish_control:
|
||||||
|
_decorative_fish = null
|
||||||
|
_schedule_next_decorative_fish(
|
||||||
|
NEXT_FISH_DELAY_MIN,
|
||||||
|
NEXT_FISH_DELAY_MAX
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _schedule_next_decorative_bubble_event(
|
||||||
|
minimum_delay: float,
|
||||||
|
maximum_delay: float,
|
||||||
|
) -> void:
|
||||||
|
if not _decorative_presentation_active or not visible:
|
||||||
|
return
|
||||||
|
_decorative_bubble_event_timer.start(
|
||||||
|
_decorative_rng.randf_range(minimum_delay, maximum_delay)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_decorative_bubble_event_timer_timeout() -> void:
|
||||||
|
if not _decorative_presentation_active or not visible:
|
||||||
|
return
|
||||||
|
var available_slots: int = (
|
||||||
|
MAX_DECORATIVE_BUBBLES - _decorative_bubbles.size()
|
||||||
|
)
|
||||||
|
if available_slots <= 0:
|
||||||
|
_schedule_next_decorative_bubble_event(
|
||||||
|
BUBBLE_EVENT_DELAY_MIN,
|
||||||
|
BUBBLE_EVENT_DELAY_MAX
|
||||||
|
)
|
||||||
|
return
|
||||||
|
var event_size: int = _choose_decorative_bubble_event_size()
|
||||||
|
event_size = mini(event_size, available_slots)
|
||||||
|
if not _spawn_decorative_bubble(_decorative_generation):
|
||||||
|
_schedule_next_decorative_bubble_event(0.5, 1.0)
|
||||||
|
return
|
||||||
|
_pending_cluster_bubbles = event_size - 1
|
||||||
|
if _pending_cluster_bubbles > 0:
|
||||||
|
_start_decorative_bubble_cluster_timer()
|
||||||
|
else:
|
||||||
|
_schedule_next_decorative_bubble_event(
|
||||||
|
BUBBLE_EVENT_DELAY_MIN,
|
||||||
|
BUBBLE_EVENT_DELAY_MAX
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _choose_decorative_bubble_event_size() -> int:
|
||||||
|
var roll: float = _decorative_rng.randf()
|
||||||
|
if roll < 0.75:
|
||||||
|
return 1
|
||||||
|
if roll < 0.95:
|
||||||
|
return 2
|
||||||
|
return 3
|
||||||
|
|
||||||
|
|
||||||
|
func _start_decorative_bubble_cluster_timer() -> void:
|
||||||
|
_decorative_bubble_cluster_timer.start(
|
||||||
|
_decorative_rng.randf_range(
|
||||||
|
BUBBLE_CLUSTER_DELAY_MIN,
|
||||||
|
BUBBLE_CLUSTER_DELAY_MAX
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_decorative_bubble_cluster_timer_timeout() -> void:
|
||||||
|
if (
|
||||||
|
not _decorative_presentation_active
|
||||||
|
or not visible
|
||||||
|
or _pending_cluster_bubbles <= 0
|
||||||
|
):
|
||||||
|
_pending_cluster_bubbles = 0
|
||||||
|
return
|
||||||
|
if _decorative_bubbles.size() < MAX_DECORATIVE_BUBBLES:
|
||||||
|
_spawn_decorative_bubble(_decorative_generation)
|
||||||
|
_pending_cluster_bubbles -= 1
|
||||||
|
if _pending_cluster_bubbles > 0:
|
||||||
|
_start_decorative_bubble_cluster_timer()
|
||||||
|
else:
|
||||||
|
_schedule_next_decorative_bubble_event(
|
||||||
|
BUBBLE_EVENT_DELAY_MIN,
|
||||||
|
BUBBLE_EVENT_DELAY_MAX
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _spawn_decorative_bubble(generation: int) -> bool:
|
||||||
|
if (
|
||||||
|
generation != _decorative_generation
|
||||||
|
or not _decorative_presentation_active
|
||||||
|
or not visible
|
||||||
|
or _decorative_bubbles.size() >= MAX_DECORATIVE_BUBBLES
|
||||||
|
or DECORATIVE_BUBBLE_TEXTURES.is_empty()
|
||||||
|
):
|
||||||
|
return false
|
||||||
|
var layer_size: Vector2 = _decorative_bubble_layer.size
|
||||||
|
if layer_size.x <= 1.0 or layer_size.y <= 1.0:
|
||||||
|
return false
|
||||||
|
var texture: Texture2D = DECORATIVE_BUBBLE_TEXTURES[
|
||||||
|
_decorative_rng.randi_range(
|
||||||
|
0,
|
||||||
|
DECORATIVE_BUBBLE_TEXTURES.size() - 1
|
||||||
|
)
|
||||||
|
]
|
||||||
|
var presentation_scale: float = _decorative_rng.randf_range(
|
||||||
|
BUBBLE_SCALE_MIN,
|
||||||
|
BUBBLE_SCALE_MAX
|
||||||
|
)
|
||||||
|
var presentation_size: Vector2 = texture.get_size() * presentation_scale
|
||||||
|
var bubble := TextureRect.new()
|
||||||
|
bubble.name = "DecorativeBubble"
|
||||||
|
bubble.texture = texture
|
||||||
|
bubble.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
bubble.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
bubble.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
bubble.size = presentation_size
|
||||||
|
bubble.modulate.a = _decorative_rng.randf_range(
|
||||||
|
BUBBLE_OPACITY_MIN,
|
||||||
|
BUBBLE_OPACITY_MAX
|
||||||
|
)
|
||||||
|
_decorative_bubble_layer.add_child(bubble)
|
||||||
|
_decorative_bubbles.append(bubble)
|
||||||
|
|
||||||
|
var normalized_x: float = _decorative_rng.randf_range(0.08, 0.92)
|
||||||
|
var drift_direction: float = (
|
||||||
|
1.0 if _decorative_rng.randi_range(0, 1) == 1 else -1.0
|
||||||
|
)
|
||||||
|
var horizontal_drift: float = (
|
||||||
|
_decorative_rng.randf_range(
|
||||||
|
BUBBLE_DRIFT_MIN,
|
||||||
|
BUBBLE_DRIFT_MAX
|
||||||
|
)
|
||||||
|
* drift_direction
|
||||||
|
)
|
||||||
|
var wobble_amplitude: float = _decorative_rng.randf_range(
|
||||||
|
BUBBLE_WOBBLE_MIN,
|
||||||
|
BUBBLE_WOBBLE_MAX
|
||||||
|
)
|
||||||
|
var wobble_cycles: float = _decorative_rng.randf_range(0.8, 1.6)
|
||||||
|
var wobble_phase: float = _decorative_rng.randf_range(0.0, TAU)
|
||||||
|
var travel_duration: float = _decorative_rng.randf_range(
|
||||||
|
BUBBLE_TRAVEL_DURATION_MIN,
|
||||||
|
BUBBLE_TRAVEL_DURATION_MAX
|
||||||
|
)
|
||||||
|
var bubble_tween: Tween = create_tween()
|
||||||
|
_decorative_bubble_tweens[bubble.get_instance_id()] = bubble_tween
|
||||||
|
bubble_tween.tween_method(
|
||||||
|
_update_decorative_bubble.bind(
|
||||||
|
bubble,
|
||||||
|
normalized_x,
|
||||||
|
horizontal_drift,
|
||||||
|
wobble_amplitude,
|
||||||
|
wobble_cycles,
|
||||||
|
wobble_phase,
|
||||||
|
generation
|
||||||
|
),
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
travel_duration
|
||||||
|
)
|
||||||
|
bubble_tween.finished.connect(
|
||||||
|
_on_decorative_bubble_finished.bind(bubble, generation),
|
||||||
|
CONNECT_ONE_SHOT
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func _update_decorative_bubble(
|
||||||
|
progress: float,
|
||||||
|
bubble: TextureRect,
|
||||||
|
normalized_x: float,
|
||||||
|
horizontal_drift: float,
|
||||||
|
wobble_amplitude: float,
|
||||||
|
wobble_cycles: float,
|
||||||
|
wobble_phase: float,
|
||||||
|
generation: int,
|
||||||
|
) -> void:
|
||||||
|
if (
|
||||||
|
generation != _decorative_generation
|
||||||
|
or not _decorative_presentation_active
|
||||||
|
or not is_instance_valid(bubble)
|
||||||
|
):
|
||||||
|
return
|
||||||
|
var layer_size: Vector2 = _decorative_bubble_layer.size
|
||||||
|
var start_y: float = (
|
||||||
|
layer_size.y + bubble.size.y + BUBBLE_EDGE_MARGIN
|
||||||
|
)
|
||||||
|
var end_y: float = -bubble.size.y - BUBBLE_EDGE_MARGIN
|
||||||
|
var base_x: float = normalized_x * layer_size.x
|
||||||
|
var wobble: float = sin(
|
||||||
|
wobble_phase + progress * TAU * wobble_cycles
|
||||||
|
) * wobble_amplitude
|
||||||
|
bubble.position = Vector2(
|
||||||
|
base_x
|
||||||
|
+ horizontal_drift * progress
|
||||||
|
+ wobble
|
||||||
|
- bubble.size.x * 0.5,
|
||||||
|
lerpf(start_y, end_y, progress)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_decorative_bubble_finished(
|
||||||
|
bubble: TextureRect,
|
||||||
|
generation: int,
|
||||||
|
) -> void:
|
||||||
|
if generation != _decorative_generation:
|
||||||
|
return
|
||||||
|
if is_instance_valid(bubble):
|
||||||
|
_decorative_bubble_tweens.erase(bubble.get_instance_id())
|
||||||
|
_decorative_bubbles.erase(bubble)
|
||||||
|
bubble.queue_free()
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
_stop_decorative_presentation()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
[gd_scene load_steps=4 format=3]
|
[gd_scene load_steps=9 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://ui/title_screen.gd" id="1_script"]
|
[ext_resource type="Script" path="res://ui/title_screen.gd" id="1_script"]
|
||||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||||
[ext_resource type="PackedScene" path="res://ui/settings_panel.tscn" id="3_settings"]
|
[ext_resource type="PackedScene" path="res://ui/settings_panel.tscn" id="3_settings"]
|
||||||
|
[ext_resource type="Shader" path="res://ui/title_water_background.gdshader" id="4_water_shader"]
|
||||||
|
[ext_resource type="Texture2D" path="res://ui/assets/title/netfishing_logo.png" id="5_logo"]
|
||||||
|
[ext_resource type="Shader" path="res://ui/title_logo_underwater.gdshader" id="6_logo_shader"]
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_water"]
|
||||||
|
shader = ExtResource("4_water_shader")
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_title_logo"]
|
||||||
|
shader = ExtResource("6_logo_shader")
|
||||||
|
|
||||||
[node name="TitleScreen" type="Control"]
|
[node name="TitleScreen" type="Control"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
@ -18,15 +27,78 @@ theme = ExtResource("2_theme")
|
||||||
script = ExtResource("1_script")
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
[node name="Background" type="ColorRect" parent="."]
|
[node name="Background" type="ColorRect" parent="."]
|
||||||
|
material = SubResource("ShaderMaterial_title_water")
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
color = Color(0.047, 0.102, 0.145, 1)
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="DecorativeFishLayer" type="Control" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
z_index = 1
|
||||||
|
clip_contents = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="DecorativeFishTimer" type="Timer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
one_shot = true
|
||||||
|
|
||||||
|
[node name="DecorativeBubbleLayer" type="Control" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
z_index = 2
|
||||||
|
clip_contents = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="DecorativeBubbleEventTimer" type="Timer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
one_shot = true
|
||||||
|
|
||||||
|
[node name="DecorativeBubbleClusterTimer" type="Timer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
one_shot = true
|
||||||
|
|
||||||
|
[node name="StartPromptCenter" type="CenterContainer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
z_index = 12
|
||||||
|
layout_mode = 1
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = -104.0
|
||||||
|
offset_bottom = -48.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="StartPromptLabel" type="Label" parent="StartPromptCenter"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||||
|
theme_override_colors/font_outline_color = Color(0.02, 0.075, 0.11, 0.9)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 26
|
||||||
|
text = "press any key to start"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
[node name="Center" type="CenterContainer" parent="."]
|
[node name="Center" type="CenterContainer" parent="."]
|
||||||
|
z_index = 10
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
|
|
@ -34,75 +106,77 @@ anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="MainPanel" type="PanelContainer" parent="Center"]
|
[node name="MainContent" type="VBoxContainer" parent="Center"]
|
||||||
custom_minimum_size = Vector2(440, 0)
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 8
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
[node name="Margin" type="MarginContainer" parent="Center/MainPanel"]
|
[node name="TitleLogo" type="TextureRect" parent="Center/MainContent"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(640, 190)
|
||||||
|
material = SubResource("ShaderMaterial_title_logo")
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_constants/margin_left = 36
|
mouse_filter = 2
|
||||||
theme_override_constants/margin_top = 30
|
texture_filter = 1
|
||||||
theme_override_constants/margin_right = 36
|
texture = ExtResource("5_logo")
|
||||||
theme_override_constants/margin_bottom = 30
|
expand_mode = 1
|
||||||
|
stretch_mode = 5
|
||||||
|
|
||||||
[node name="Content" type="VBoxContainer" parent="Center/MainPanel/Margin"]
|
[node name="PlaytestLabel" type="Label" parent="Center/MainContent"]
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/separation = 12
|
|
||||||
|
|
||||||
[node name="Title" type="Label" parent="Center/MainPanel/Margin/Content"]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_font_sizes/font_size = 36
|
|
||||||
text = "NETFISHING"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="Subtitle" type="Label" parent="Center/MainPanel/Margin/Content"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "A cozy social fishing game"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="PlaytestLabel" type="Label" parent="Center/MainPanel/Margin/Content"]
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1)
|
theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1)
|
||||||
theme_override_font_sizes/font_size = 13
|
theme_override_font_sizes/font_size = 13
|
||||||
text = "Pre-Alpha Playtest 0.1"
|
text = "pre-alpha playtest 0.1"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="Spacer" type="Control" parent="Center/MainPanel/Margin/Content"]
|
[node name="Spacer" type="Control" parent="Center/MainContent"]
|
||||||
custom_minimum_size = Vector2(0, 12)
|
custom_minimum_size = Vector2(0, 4)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="ContinueButton" type="Button" parent="Center/MainPanel/Margin/Content"]
|
[node name="ButtonCenter" type="CenterContainer" parent="Center/MainContent"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="ButtonStack" type="VBoxContainer" parent="Center/MainContent/ButtonCenter"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(360, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
|
[node name="ContinueButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(0, 44)
|
custom_minimum_size = Vector2(0, 44)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Continue"
|
text = "continue"
|
||||||
|
|
||||||
[node name="NewGameButton" type="Button" parent="Center/MainPanel/Margin/Content"]
|
[node name="NewGameButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(0, 44)
|
custom_minimum_size = Vector2(0, 44)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "New Game"
|
text = "new game"
|
||||||
|
|
||||||
[node name="SettingsButton" type="Button" parent="Center/MainPanel/Margin/Content"]
|
[node name="SettingsButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(0, 44)
|
custom_minimum_size = Vector2(0, 44)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Settings"
|
text = "settings"
|
||||||
|
|
||||||
[node name="DeleteSaveButton" type="Button" parent="Center/MainPanel/Margin/Content"]
|
[node name="DeleteSaveButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(0, 44)
|
custom_minimum_size = Vector2(0, 44)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Delete Save"
|
text = "delete save"
|
||||||
|
|
||||||
[node name="QuitButton" type="Button" parent="Center/MainPanel/Margin/Content"]
|
[node name="QuitButton" type="Button" parent="Center/MainContent/ButtonCenter/ButtonStack"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(0, 44)
|
custom_minimum_size = Vector2(0, 44)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Quit"
|
text = "quit"
|
||||||
|
|
||||||
[node name="FeedbackLabel" type="Label" parent="Center/MainPanel/Margin/Content"]
|
[node name="FeedbackLabel" type="Label" parent="Center/MainContent"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
custom_minimum_size = Vector2(0, 42)
|
custom_minimum_size = Vector2(0, 42)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = ""
|
text = ""
|
||||||
|
|
@ -141,7 +215,7 @@ theme_override_constants/separation = 14
|
||||||
[node name="ConfirmationText" type="Label" parent="ConfirmationPanel/Margin/Content"]
|
[node name="ConfirmationText" type="Label" parent="ConfirmationPanel/Margin/Content"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Confirm?"
|
text = "confirm?"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
autowrap_mode = 2
|
autowrap_mode = 2
|
||||||
|
|
||||||
|
|
@ -154,13 +228,13 @@ alignment = 1
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(150, 42)
|
custom_minimum_size = Vector2(150, 42)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Confirm"
|
text = "confirm"
|
||||||
|
|
||||||
[node name="CancelConfirmButton" type="Button" parent="ConfirmationPanel/Margin/Content/Buttons"]
|
[node name="CancelConfirmButton" type="Button" parent="ConfirmationPanel/Margin/Content/Buttons"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(120, 42)
|
custom_minimum_size = Vector2(120, 42)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Cancel"
|
text = "cancel"
|
||||||
|
|
||||||
[node name="SettingsPanel" parent="." instance=ExtResource("3_settings")]
|
[node name="SettingsPanel" parent="." instance=ExtResource("3_settings")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
|
||||||
95
ui/title_water_background.gdshader
Normal file
95
ui/title_water_background.gdshader
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
uniform vec4 surface_color : source_color = vec4(0.10, 0.45, 0.62, 1.0);
|
||||||
|
uniform vec4 middle_color : source_color = vec4(0.055, 0.285, 0.40, 1.0);
|
||||||
|
uniform vec4 depth_color : source_color = vec4(0.02, 0.16, 0.27, 1.0);
|
||||||
|
uniform vec2 virtual_pixel_density = vec2(320.0, 180.0);
|
||||||
|
uniform float color_step_count : hint_range(2.0, 16.0, 1.0) = 14.0;
|
||||||
|
uniform float continuous_depth_blend : hint_range(0.0, 0.25, 0.01) = 0.12;
|
||||||
|
uniform float wave_speed : hint_range(0.0, 0.5, 0.01) = 0.10;
|
||||||
|
uniform float wave_scale : hint_range(0.5, 8.0, 0.1) = 2.4;
|
||||||
|
uniform float wave_strength : hint_range(0.0, 0.05, 0.001) = 0.012;
|
||||||
|
uniform float caustic_strength : hint_range(0.0, 0.08, 0.001) = 0.018;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
vec2 grid_size = max(virtual_pixel_density, vec2(1.0));
|
||||||
|
vec2 pixel_uv = (floor(UV * grid_size) + vec2(0.5)) / grid_size;
|
||||||
|
float motion = TIME * wave_speed;
|
||||||
|
|
||||||
|
float broad_wave = sin(
|
||||||
|
pixel_uv.x * wave_scale + motion
|
||||||
|
);
|
||||||
|
float secondary_wave = sin(
|
||||||
|
pixel_uv.x * 1.1 + pixel_uv.y * 1.6 - motion * 0.65
|
||||||
|
);
|
||||||
|
float movement_falloff = 1.0 - pixel_uv.y * 0.75;
|
||||||
|
float depth_displacement = (
|
||||||
|
(broad_wave * 0.65 + secondary_wave * 0.35)
|
||||||
|
* wave_strength
|
||||||
|
* movement_falloff
|
||||||
|
);
|
||||||
|
float continuous_depth = clamp(
|
||||||
|
pixel_uv.y
|
||||||
|
+ depth_displacement,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
float depth_steps = max(2.0, floor(color_step_count));
|
||||||
|
float band_index = floor(
|
||||||
|
continuous_depth * (depth_steps - 1.0) + 0.5
|
||||||
|
);
|
||||||
|
float band_depth = band_index / (depth_steps - 1.0);
|
||||||
|
float display_depth = mix(
|
||||||
|
band_depth,
|
||||||
|
continuous_depth,
|
||||||
|
clamp(continuous_depth_blend, 0.0, 0.25)
|
||||||
|
);
|
||||||
|
|
||||||
|
vec3 water_color;
|
||||||
|
if (display_depth < 0.5) {
|
||||||
|
water_color = mix(
|
||||||
|
surface_color.rgb,
|
||||||
|
middle_color.rgb,
|
||||||
|
display_depth * 2.0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
water_color = mix(
|
||||||
|
middle_color.rgb,
|
||||||
|
depth_color.rgb,
|
||||||
|
(display_depth - 0.5) * 2.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
float caustic_wave = clamp(
|
||||||
|
(
|
||||||
|
sin(
|
||||||
|
pixel_uv.x * 4.0
|
||||||
|
+ pixel_uv.y * 1.2
|
||||||
|
+ motion * 0.7
|
||||||
|
)
|
||||||
|
* sin(
|
||||||
|
pixel_uv.x * 1.7
|
||||||
|
- pixel_uv.y * 2.2
|
||||||
|
- motion * 0.55
|
||||||
|
)
|
||||||
|
- 0.25
|
||||||
|
) / 0.75,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
float caustic_level = floor(
|
||||||
|
caustic_wave * 3.0
|
||||||
|
) / 3.0;
|
||||||
|
float upper_water_mask = 1.0 - smoothstep(
|
||||||
|
0.18,
|
||||||
|
0.34,
|
||||||
|
pixel_uv.y
|
||||||
|
);
|
||||||
|
water_color += (
|
||||||
|
vec3(0.05, 0.12, 0.14)
|
||||||
|
* caustic_level
|
||||||
|
* upper_water_mask
|
||||||
|
* caustic_strength
|
||||||
|
);
|
||||||
|
COLOR = vec4(clamp(water_color, vec3(0.0), vec3(1.0)), 1.0);
|
||||||
|
}
|
||||||
1
ui/title_water_background.gdshader.uid
Normal file
1
ui/title_water_background.gdshader.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://c8s1g7tn771du
|
||||||
Loading…
Add table
Add a link
Reference in a new issue