diff --git a/CREDITS.md b/CREDITS.md index ed05a07..416cd73 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -9,3 +9,10 @@ _À compléter au fur et à mesure des phases_ | Asset | Source | Licence | |-------|--------|---------| | icon.svg | Créé manuellement | CC0 | +| audio/music/underwater_theme.mp3 | https://opengameart.org/content/underwater-theme — Cleyton RX | CC-BY 3.0 | +| audio/sfx/underwater_ambient.ogg | https://freesound.org/people/Zozzy/sounds/56678/ (nemoscape2.mp3) — Zozzy | CC0 | +| audio/sfx/bubbles.mp3 | https://freesound.org/people/ristooooo1/sounds/539823/ — ristooooo1 | CC0 | +| audio/sfx/whale_call.mp3 | https://freesound.org/people/taure/sounds/361423/ (Whalesong.wav) — taure | CC0 | + +## Attribution requise +- **Cleyton RX - Underwater Theme** : musique sous Creative Commons Attribution 3.0. Crédit : "Underwater Theme by Cleyton RX, https://opengameart.org/content/underwater-theme, CC-BY 3.0" diff --git a/audio/music/underwater_theme.mp3 b/audio/music/underwater_theme.mp3 new file mode 100644 index 0000000..bc75707 Binary files /dev/null and b/audio/music/underwater_theme.mp3 differ diff --git a/audio/sfx/bubbles.mp3 b/audio/sfx/bubbles.mp3 new file mode 100644 index 0000000..da35ad7 Binary files /dev/null and b/audio/sfx/bubbles.mp3 differ diff --git a/audio/sfx/underwater_ambient.ogg b/audio/sfx/underwater_ambient.ogg new file mode 100644 index 0000000..63f8c58 Binary files /dev/null and b/audio/sfx/underwater_ambient.ogg differ diff --git a/audio/sfx/whale_call.mp3 b/audio/sfx/whale_call.mp3 new file mode 100644 index 0000000..aa04d92 Binary files /dev/null and b/audio/sfx/whale_call.mp3 differ diff --git a/project.godot b/project.godot index 6cf6e8e..7a86978 100644 --- a/project.godot +++ b/project.godot @@ -89,6 +89,7 @@ escape={ [autoload] BlockDatabase="*res://scripts/world/BlockDatabase.gd" +AudioManager="*res://scripts/ambience/AudioManager.gd" [rendering] environment/defaults/default_clear_color=Color(0.05, 0.15, 0.25, 1) diff --git a/scenes/MainMenu.tscn b/scenes/MainMenu.tscn new file mode 100644 index 0000000..09f5709 --- /dev/null +++ b/scenes/MainMenu.tscn @@ -0,0 +1,141 @@ +[gd_scene load_steps=3 format=3 uid="uid://dauphincraft_mainmenu"] + +[ext_resource type="Script" path="res://scripts/ambience/MainMenu.gd" id="1_mainmenu"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_btn"] +bg_color = Color(0.05, 0.15, 0.3, 0.75) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.3, 0.8, 1.0, 1.0) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 + +[node name="MainMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("1_mainmenu") + +[node name="Background" type="ColorRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +color = Color(0.02, 0.08, 0.18, 1.0) + +[node name="CenterContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -200.0 +offset_top = -160.0 +offset_right = 200.0 +offset_bottom = 160.0 +alignment = 1 + +[node name="Title" type="Label" parent="CenterContainer"] +layout_mode = 2 +text = "DAUPHINCRAFT" +horizontal_alignment = 1 +theme_override_font_sizes/font_size = 80 +theme_override_colors/font_color = Color(0.7, 0.95, 1.0, 1.0) +theme_override_colors/font_shadow_color = Color(0.0, 0.3, 0.6, 0.8) +theme_override_constants/shadow_offset_x = 3 +theme_override_constants/shadow_offset_y = 3 + +[node name="Subtitle" type="Label" parent="CenterContainer"] +layout_mode = 2 +text = "Un monde voxel sous l'océan" +horizontal_alignment = 1 +theme_override_font_sizes/font_size = 22 +theme_override_colors/font_color = Color(0.5, 0.75, 0.9, 0.85) + +[node name="Spacer" type="Control" parent="CenterContainer"] +layout_mode = 2 +custom_minimum_size = Vector2(0, 30) + +[node name="DiveButton" type="Button" parent="CenterContainer"] +layout_mode = 2 +custom_minimum_size = Vector2(300, 56) +text = "Plonger" +theme_override_font_sizes/font_size = 26 +theme_override_colors/font_color = Color(0.8, 0.97, 1.0, 1.0) +theme_override_styles/normal = SubResource("StyleBoxFlat_btn") + +[node name="OptionsButton" type="Button" parent="CenterContainer"] +layout_mode = 2 +custom_minimum_size = Vector2(300, 56) +text = "Options" +theme_override_font_sizes/font_size = 26 +theme_override_colors/font_color = Color(0.8, 0.97, 1.0, 1.0) +theme_override_styles/normal = SubResource("StyleBoxFlat_btn") + +[node name="QuitButton" type="Button" parent="CenterContainer"] +layout_mode = 2 +custom_minimum_size = Vector2(300, 56) +text = "Quitter" +theme_override_font_sizes/font_size = 26 +theme_override_colors/font_color = Color(0.8, 0.97, 1.0, 1.0) +theme_override_styles/normal = SubResource("StyleBoxFlat_btn") + +[node name="Credits" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 12 +anchor_left = 0.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -30.0 +text = "Assets: Kenney, OpenGameArt, Freesound (voir CREDITS.md)" +horizontal_alignment = 1 +theme_override_font_sizes/font_size = 13 +theme_override_colors/font_color = Color(0.4, 0.6, 0.7, 0.6) + +[node name="OptionsPopup" type="Window" parent="."] +title = "Options" +size = Vector2i(360, 220) +visible = false + +[node name="VBox" type="VBoxContainer" parent="OptionsPopup"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 20.0 +offset_top = 20.0 +offset_right = -20.0 +offset_bottom = -20.0 + +[node name="MusicLabel" type="Label" parent="OptionsPopup/VBox"] +text = "Volume Musique" + +[node name="MusicSlider" type="HSlider" parent="OptionsPopup/VBox"] +layout_mode = 2 +min_value = 0.0 +max_value = 1.0 +step = 0.01 +value = 0.7 + +[node name="SFXLabel" type="Label" parent="OptionsPopup/VBox"] +text = "Volume SFX" + +[node name="SFXSlider" type="HSlider" parent="OptionsPopup/VBox"] +layout_mode = 2 +min_value = 0.0 +max_value = 1.0 +step = 0.01 +value = 0.8 + +[connection signal="pressed" from="CenterContainer/DiveButton" to="." method="_on_dive_pressed"] +[connection signal="pressed" from="CenterContainer/OptionsButton" to="." method="_on_options_pressed"] +[connection signal="pressed" from="CenterContainer/QuitButton" to="." method="_on_quit_pressed"] +[connection signal="value_changed" from="OptionsPopup/VBox/MusicSlider" to="." method="_on_music_slider_value_changed"] +[connection signal="value_changed" from="OptionsPopup/VBox/SFXSlider" to="." method="_on_sfx_slider_value_changed"] diff --git a/scripts/ambience/AudioManager.gd b/scripts/ambience/AudioManager.gd new file mode 100644 index 0000000..c4af826 --- /dev/null +++ b/scripts/ambience/AudioManager.gd @@ -0,0 +1,124 @@ +extends Node + +var music_player: AudioStreamPlayer +var ambient_loop: AudioStreamPlayer +var whale_player: AudioStreamPlayer + +var _whale_timer: Timer +var _music_tween: Tween + +const MUSIC_PATH := "res://audio/music/" +const SFX_PATH := "res://audio/sfx/" + +func _ready() -> void: + music_player = AudioStreamPlayer.new() + music_player.bus = "Music" + music_player.volume_db = -10.0 + add_child(music_player) + + ambient_loop = AudioStreamPlayer.new() + ambient_loop.bus = "SFX" + ambient_loop.volume_db = -12.0 + add_child(ambient_loop) + + whale_player = AudioStreamPlayer.new() + whale_player.bus = "SFX" + whale_player.volume_db = -15.0 + add_child(whale_player) + + _whale_timer = Timer.new() + _whale_timer.one_shot = true + _whale_timer.timeout.connect(_on_whale_timer) + add_child(_whale_timer) + _schedule_next_whale() + + +func play_music(track_name: String) -> void: + var path := MUSIC_PATH + track_name + if not ResourceLoader.exists(path): + return + + var stream: AudioStream = load(path) + if stream == null: + return + + music_player.stream = stream + music_player.stream.loop = true + music_player.volume_db = -40.0 + music_player.play() + + if _music_tween: + _music_tween.kill() + _music_tween = create_tween() + _music_tween.tween_property(music_player, "volume_db", -10.0, 3.0).set_trans(Tween.TRANS_SINE) + + +func play_ambient_loop(loop_name: String) -> void: + var path := SFX_PATH + loop_name + if not ResourceLoader.exists(path): + return + + var stream: AudioStream = load(path) + if stream == null: + return + + ambient_loop.stream = stream + ambient_loop.stream.loop = true + ambient_loop.play() + + +func play_whale_call_random() -> void: + var path := SFX_PATH + "whale_call.ogg" + if not ResourceLoader.exists(path): + return + if whale_player.playing: + return + + var stream: AudioStream = load(path) + if stream == null: + return + + whale_player.stream = stream + whale_player.play() + + +func play_bubble_sfx(position: Vector3) -> void: + var path := SFX_PATH + "bubbles.ogg" + if not ResourceLoader.exists(path): + return + + var player := AudioStreamPlayer3D.new() + player.bus = "SFX" + player.volume_db = -8.0 + player.max_distance = 20.0 + player.attenuation_model = AudioStreamPlayer3D.ATTENUATION_INVERSE_SQUARE_DISTANCE + add_child(player) + player.global_position = position + + var stream: AudioStream = load(path) + if stream == null: + player.queue_free() + return + + player.stream = stream + player.play() + player.finished.connect(player.queue_free) + + +func set_music_volume(db: float) -> void: + music_player.volume_db = db + + +func set_sfx_volume(db: float) -> void: + ambient_loop.volume_db = db + whale_player.volume_db = db - 3.0 + + +func _on_whale_timer() -> void: + play_whale_call_random() + _schedule_next_whale() + + +func _schedule_next_whale() -> void: + _whale_timer.wait_time = randf_range(30.0, 90.0) + _whale_timer.start() diff --git a/scripts/ambience/MainMenu.gd b/scripts/ambience/MainMenu.gd new file mode 100644 index 0000000..ebce57b --- /dev/null +++ b/scripts/ambience/MainMenu.gd @@ -0,0 +1,29 @@ +extends Control + +@onready var _options_popup: Window = $OptionsPopup +@onready var _music_slider: HSlider = $OptionsPopup/VBox/MusicSlider +@onready var _sfx_slider: HSlider = $OptionsPopup/VBox/SFXSlider + +func _ready() -> void: + AudioManager.play_music("underwater_theme.mp3") + AudioManager.play_ambient_loop("underwater_ambient.ogg") + + +func _on_dive_pressed() -> void: + get_tree().change_scene_to_file("res://scenes/Main.tscn") + + +func _on_options_pressed() -> void: + _options_popup.popup_centered() + + +func _on_quit_pressed() -> void: + get_tree().quit() + + +func _on_music_slider_value_changed(value: float) -> void: + AudioManager.set_music_volume(linear_to_db(value)) + + +func _on_sfx_slider_value_changed(value: float) -> void: + AudioManager.set_sfx_volume(linear_to_db(value)) diff --git a/scripts/ambience/PlanktonParticles.gd b/scripts/ambience/PlanktonParticles.gd new file mode 100644 index 0000000..03b97d8 --- /dev/null +++ b/scripts/ambience/PlanktonParticles.gd @@ -0,0 +1,57 @@ +extends GPUParticles3D + +func _ready() -> void: + amount = 500 + lifetime = 10.0 + one_shot = false + explosiveness = 0.0 + randomness = 1.0 + visibility_aabb = AABB(Vector3(-50, -30, -50), Vector3(100, 60, 100)) + + var process_mat := ParticleProcessMaterial.new() + process_mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX + process_mat.emission_box_extents = Vector3(50.0, 30.0, 50.0) + + process_mat.direction = Vector3(0.0, 0.0, 0.0) + process_mat.spread = 180.0 + process_mat.initial_velocity_min = 0.0 + process_mat.initial_velocity_max = 0.1 + + process_mat.linear_accel_min = -0.05 + process_mat.linear_accel_max = 0.05 + process_mat.radial_accel_min = -0.02 + process_mat.radial_accel_max = 0.02 + + process_mat.damping_min = 0.5 + process_mat.damping_max = 1.0 + + process_mat.scale_min = 0.02 + process_mat.scale_max = 0.08 + + process_mat.color = Color(0.85, 0.92, 1.0, 0.8) + + var grad := Gradient.new() + grad.set_color(0, Color(0.85, 0.92, 1.0, 0.0)) + grad.set_color(1, Color(0.85, 0.92, 1.0, 0.0)) + grad.add_point(0.1, Color(0.85, 0.92, 1.0, 0.8)) + grad.add_point(0.9, Color(0.85, 0.92, 1.0, 0.8)) + var grad_tex := GradientTexture1D.new() + grad_tex.gradient = grad + process_mat.color_ramp = grad_tex + + process_material = process_mat + + var quad := QuadMesh.new() + quad.size = Vector2(0.05, 0.05) + + var mat := StandardMaterial3D.new() + mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED + mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA + mat.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED + mat.emission_enabled = true + mat.emission = Color(0.8, 0.9, 1.0) + mat.emission_energy_multiplier = 1.5 + mat.albedo_color = Color(0.85, 0.92, 1.0, 0.8) + + quad.surface_set_material(0, mat) + draw_pass_1 = quad diff --git a/scripts/ambience/UnderwaterEnvironment.gd b/scripts/ambience/UnderwaterEnvironment.gd new file mode 100644 index 0000000..b9a9187 --- /dev/null +++ b/scripts/ambience/UnderwaterEnvironment.gd @@ -0,0 +1,44 @@ +extends WorldEnvironment + +func _ready() -> void: + var env := Environment.new() + + env.background_mode = Environment.BG_COLOR + env.background_color = Color(0.03, 0.12, 0.22) + + env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR + env.ambient_light_color = Color(0.2, 0.4, 0.6) + env.ambient_light_energy = 0.4 + + env.fog_enabled = true + env.fog_light_color = Color(0.1, 0.3, 0.5) + env.fog_density = 0.02 + env.fog_aerial_perspective = 0.3 + + env.volumetric_fog_enabled = true + env.volumetric_fog_density = 0.04 + env.volumetric_fog_albedo = Color(0.2, 0.5, 0.7) + env.volumetric_fog_emission = Color(0.02, 0.06, 0.1) + env.volumetric_fog_emission_energy = 0.1 + env.volumetric_fog_length = 64.0 + env.volumetric_fog_detail_spread = 2.0 + + env.glow_enabled = true + env.glow_intensity = 0.5 + env.glow_bloom = 0.1 + env.glow_blend_mode = Environment.GLOW_BLEND_MODE_SOFTLIGHT + + env.ssao_enabled = true + env.ssao_radius = 1.0 + env.ssao_intensity = 1.0 + env.ssao_power = 1.5 + env.ssao_detail = 0.5 + + env.tone_mapper = Environment.TONE_MAPPER_FILMIC + env.exposure = 1.0 + + env.adjustment_enabled = true + env.adjustment_saturation = 1.1 + env.adjustment_color_correction = null + + environment = env diff --git a/shaders/caustics.gdshader b/shaders/caustics.gdshader new file mode 100644 index 0000000..00163c0 --- /dev/null +++ b/shaders/caustics.gdshader @@ -0,0 +1,21 @@ +shader_type spatial; +render_mode unshaded, blend_add, cull_disabled, depth_draw_never; + +uniform float speed : hint_range(0.1, 3.0) = 0.8; +uniform float scale : hint_range(1.0, 20.0) = 8.0; +uniform float intensity : hint_range(0.0, 2.0) = 0.6; +uniform vec3 caustic_color : source_color = vec3(0.7, 0.9, 1.0); + +void fragment() { + vec2 uv = UV * scale; + + float pattern1 = sin(TIME * speed + uv.x * 10.0 + sin(uv.y * 10.0 + TIME * speed)); + float pattern2 = sin(TIME * speed * 0.7 + uv.y * 12.0 + sin(uv.x * 8.0 + TIME * speed * 1.3)); + float pattern3 = sin(TIME * speed * 1.1 + (uv.x + uv.y) * 7.0 + sin(uv.x * 5.0 - TIME * speed * 0.5)); + + float caustic = (pattern1 + pattern2 + pattern3) / 3.0; + caustic = pow(max(caustic, 0.0), 2.0) * intensity; + + EMISSION = caustic_color * caustic; + ALBEDO = vec3(0.0); +} diff --git a/shaders/godrays.gdshader b/shaders/godrays.gdshader new file mode 100644 index 0000000..28a8e63 --- /dev/null +++ b/shaders/godrays.gdshader @@ -0,0 +1,20 @@ +shader_type canvas_item; +render_mode blend_add; + +uniform float ray_count : hint_range(5.0, 40.0) = 20.0; +uniform float speed : hint_range(0.1, 2.0) = 0.4; +uniform float intensity : hint_range(0.0, 2.0) = 0.5; +uniform vec3 ray_color : source_color = vec3(0.5, 0.8, 1.0); + +void fragment() { + vec2 uv = UV; + + float vertical_fade = pow(1.0 - uv.y, 2.0); + + float ray_pattern = fract(uv.x * ray_count + sin(TIME * speed + uv.y * 5.0) * 0.3); + float ray = smoothstep(0.0, 0.15, ray_pattern) * smoothstep(0.4, 0.15, ray_pattern); + + float brightness = ray * vertical_fade * intensity; + + COLOR = vec4(ray_color * brightness, brightness * 0.6); +} diff --git a/shaders/underwater_fog.gdshader b/shaders/underwater_fog.gdshader new file mode 100644 index 0000000..7afd539 --- /dev/null +++ b/shaders/underwater_fog.gdshader @@ -0,0 +1,21 @@ +shader_type fog; + +uniform float surface_y : hint_range(-100.0, 100.0) = 0.0; +uniform float abyss_depth : hint_range(-200.0, 0.0) = -60.0; + +void fog() { + float depth_factor = clamp((WORLD_POSITION.y - abyss_depth) / (surface_y - abyss_depth), 0.0, 1.0); + + vec3 surface_color = vec3(0.15, 0.45, 0.6); + vec3 abyss_color = vec3(0.02, 0.08, 0.15); + vec3 fog_color = mix(abyss_color, surface_color, depth_factor); + + float current_wave = sin(TIME * 0.3 + WORLD_POSITION.x * 0.05) * 0.02 + + sin(TIME * 0.17 + WORLD_POSITION.z * 0.04) * 0.01; + + float base_density = mix(0.3, 0.05, depth_factor) + current_wave; + base_density = clamp(base_density, 0.0, 0.4); + + ALBEDO = fog_color; + DENSITY = base_density; +}