58 Commits

Author SHA1 Message Date
b4a2bb6144 biome generation optimization 2026-05-14 12:29:41 +02:00
90b9739ee0 Merge branch 'main' of https://gitea.jmpgames.it/jmp-games/tgcc 2026-05-13 18:15:58 +02:00
43556ac110 add uniqueness par for props 2026-05-13 18:15:37 +02:00
7ea515c824 Merge branch 'polish3' into main 2026-05-13 15:50:14 +00:00
4658250206 fix 2026-05-13 17:05:29 +02:00
c25e3bb4f6 add uniqueness property to chunk and manage it on biome generator 2026-05-13 12:25:16 +02:00
Matteo Sonaglioni
c796ea289d well 2026-05-12 16:54:20 +02:00
Matteo Sonaglioni
a18dc64a4f prop1 2026-05-12 16:44:21 +02:00
c52b11388a add chunks
chunk river
hero chunk
2026-05-11 13:32:31 +00:00
ce8a977541 Merge pull request 'fix river water direction2' (#15) from river_direction into main
Reviewed-on: #15
2026-05-10 16:45:11 +00:00
8ceb6cb16b fix river water direction2 2026-05-10 18:44:43 +02:00
bcf854fd74 Merge branch 'main' of https://gitea.jmpgames.it/jmp-games/tgcc 2026-05-10 11:09:41 +02:00
10b437d236 add fog update based on day 2026-05-10 11:09:27 +02:00
9c41da14a6 Merge pull request 'fix_snow_rain_fog' (#14) from fix_snow_rain_fog into main
Reviewed-on: #14
2026-05-10 09:02:01 +00:00
2aa7d1f7d4 fix snow 2026-05-10 11:01:22 +02:00
040ded5f0b fix rain 2026-05-09 14:57:07 +02:00
ddec91d515 update cloud density when is rainy or snowy 2026-05-06 23:14:51 +02:00
d8efa735dd Merge pull request 'polish2' (#12) from polish2 into main
Reviewed-on: #12
2026-05-06 20:29:12 +00:00
Matteo Sonaglioni
9bc7f38d07 add river curve 2026-05-06 22:27:54 +02:00
Matteo Sonaglioni
f4c8775a45 add river chunk 2026-05-06 21:43:34 +02:00
70d3dfe15c add ai, photo_mode and radio 2026-05-05 20:33:00 +00:00
Matteo Sonaglioni
9827b98b3e river water shader 2026-05-05 18:45:16 +02:00
Matteo Sonaglioni
977e8012aa river chunk 2026-05-05 17:03:24 +02:00
72d06560b1 add groups to scenes 2026-05-05 00:04:02 +02:00
9d62d97422 Merge pull request 'biome_gen_river' (#10) from biome_gen_river into main
Reviewed-on: #10
2026-05-04 22:00:08 +00:00
c1e99a3b8e fix merge scenes 2026-05-04 23:59:27 +02:00
8e1ba915a2 add scenes 2026-05-04 23:43:22 +02:00
Matteo Sonaglioni
577b4570da add river chunk 2026-05-04 23:35:29 +02:00
36dff9c070 remove train radar 2026-05-04 10:09:20 +02:00
56591bb907 fix snow cap 2026-05-02 18:10:51 +02:00
9f254d9725 Merge pull request 'polish1' (#6) from polish1 into main
Reviewed-on: #6
2026-05-02 16:09:44 +00:00
abae8434fc add river bioma gen + label weather random 2026-04-30 23:23:49 +02:00
Matteo Sonaglioni
e211f89fca revamp pylon 2026-04-30 17:48:08 +02:00
3c7cbdc662 Merge pull request 'biome_gen_overload' (#5) from biome_gen_overload into main
Reviewed-on: #5
2026-04-30 14:42:30 +00:00
8c7f1c3c82 fix stutter 2026-04-30 16:21:08 +02:00
b7308c5d66 fix fog 2026-04-30 12:20:12 +02:00
Matteo Sonaglioni
d709d9236c Merge branch 'main' into polish1 2026-04-30 11:24:50 +02:00
Matteo Sonaglioni
9ee6d08918 chunk fix 2026-04-30 11:05:18 +02:00
ff93a7ab2c add fog 2026-04-30 07:13:34 +02:00
122ec189eb fix water colors 2026-04-30 06:55:28 +02:00
889334e7a3 fix snow and water ripples + add water colors 2026-04-30 06:53:54 +02:00
Matteo Sonaglioni
3521839ed9 add bambu 2026-04-29 17:54:13 +02:00
Matteo Sonaglioni
6a62f92ed5 fog planes 2026-04-29 14:14:58 +02:00
Matteo Sonaglioni
ead4e557c4 Squashed commit of the following:
commit d8496f4cc7
Author: Matteo Sonaglioni <sonaglioni.matteo7@gmail.com>
Date:   Wed Apr 29 11:01:07 2026 +0200

    tweak enviromentconfig

commit 84538679f3
Author: Matteo Sonaglioni <sonaglioni.matteo7@gmail.com>
Date:   Wed Apr 29 09:29:48 2026 +0200

    museum_map
2026-04-29 11:02:24 +02:00
Matteo Sonaglioni
b868584b78 Squashed commit of the following:
commit 53646e36f0
Author: Matteo Sonaglioni <sonaglioni.matteo7@gmail.com>
Date:   Thu Apr 23 17:24:36 2026 +0200

    map - zoos

commit 88dd7c3ad8
Author: Matteo Sonaglioni <sonaglioni.matteo7@gmail.com>
Date:   Thu Apr 23 16:48:14 2026 +0200

    fix railway chunk
2026-04-28 10:06:03 +02:00
d668804038 add label to see current weather 2026-04-24 17:51:49 +02:00
e8f2c4db84 add random weather function 2026-04-24 17:44:43 +02:00
72c933ba73 add random start for train o start from a stop 2026-04-24 17:08:24 +02:00
8f02afa796 update snow parameters 2026-04-24 16:18:36 +02:00
211455f5da fix materials for wind and add new snow system 2026-04-24 15:36:10 +02:00
ab87c7f478 fix set shader on nodes 2026-04-24 12:22:46 +02:00
b95e8598b2 some fix 2026-04-24 10:58:07 +02:00
Matteo Sonaglioni
5ddf363368 Squashed commit of the following:
commit 75286521d1
Author: Matteo Sonaglioni <sonaglioni.matteo7@gmail.com>
Date:   Thu Apr 23 15:41:28 2026 +0200

    railway chunk

commit b975e13c8d
Merge: ae9b75c f3120c6
Author: Matteo Sonaglioni <sonaglioni.matteo7@gmail.com>
Date:   Tue Apr 21 23:37:06 2026 +0200

    Merge branch 'Chunk' of https://gitea.jmpgames.it/jmp-games/tgcc into Chunk

commit ae9b75cc53
Author: Matteo Sonaglioni <sonaglioni.matteo7@gmail.com>
Date:   Tue Apr 21 23:34:41 2026 +0200

    nomenclatura chunk rotaie
2026-04-23 16:03:35 +02:00
fda8188102 Merge pull request 'rev_ffabbrizi_newbiome' (#3) from rev_ffabbrizi_newbiome into main
Reviewed-on: #3
2026-04-23 13:38:16 +00:00
7470937b49 add controls to daytime scene + change main scene on run 2026-04-23 15:37:25 +02:00
35618b5656 fix snow and rain for grass 2026-04-23 15:29:03 +02:00
8b2cbab23b fix shaders 2026-04-23 15:07:04 +02:00
174ae98242 turnoff ripples when is not rainy 2026-04-22 00:03:01 +02:00
248 changed files with 22240 additions and 644 deletions

13
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/contentModel.xml
/modules.xml
/projectSettingsUpdater.xml
/.idea.tgcc.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

4
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

8
.idea/indexLayout.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,51 @@
extends CharacterBody3D
class_name AIBase
@export var speed: float = 4.0
var _enable_state_machine: bool = true
@export var enable_state_machine: bool:
set(value):
_enable_state_machine = value
toggle_enable_state_machine()
get:
return _enable_state_machine
@onready var patrol_radius_shape: CollisionShape3D = $%PatrolRadiusShape
@onready var nav_agent: NavigationAgent3D = $%NavigationAgent3D
@onready var state_machine: StateMachine = $%StateMachine
func _ready() -> void:
randomize()
toggle_enable_state_machine()
func toggle_enable_state_machine() -> void:
state_machine.enable = _enable_state_machine
func _physics_process(delta: float) -> void:
if !_enable_state_machine:
return
state_machine.physics_process(delta)
if nav_agent.is_navigation_finished():
velocity = Vector3.ZERO
move_and_slide()
return
var next_point = nav_agent.get_next_path_position()
var direction = global_position.direction_to(next_point)
velocity.x = direction.x * speed
velocity.z = direction.z * speed
move_and_slide()
func navigate_to_random_point() -> void:
var nav_map_rid = get_world_3d().get_navigation_map()
var patrol_radius = patrol_radius_shape.shape.radius
var target_point_in_space = global_position + Vector3(randf_range(-1, 1), 0, randf_range(-1, 1)).normalized() * randf_range(0, patrol_radius)
var closest_valid_point = NavigationServer3D.map_get_closest_point(nav_map_rid, target_point_in_space)
nav_agent.target_position = closest_valid_point

View File

@@ -0,0 +1 @@
uid://b30p1yqojbbbk

View File

@@ -0,0 +1,44 @@
[gd_scene format=3 uid="uid://clx701xdwelgx"]
[ext_resource type="Script" uid="uid://b30p1yqojbbbk" path="res://core/ai/agents/base/ai_base.gd" id="1_4d1nn"]
[ext_resource type="Script" uid="uid://ps3vlu2qvmop" path="res://core/ai/framework/state_machine.gd" id="2_q1hg3"]
[ext_resource type="Script" uid="uid://nga8qx56iwgu" path="res://core/ai/agents/base/idle_state.gd" id="3_26faq"]
[ext_resource type="Script" uid="uid://bngfthvt04ivv" path="res://core/ai/agents/base/patrol_state.gd" id="4_lim44"]
[sub_resource type="CapsuleMesh" id="CapsuleMesh_mh3lg"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_mh3lg"]
[sub_resource type="SphereShape3D" id="SphereShape3D_lim44"]
radius = 20.0
[node name="AiBase" type="CharacterBody3D" unique_id=1228675528]
script = ExtResource("1_4d1nn")
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=214482161]
mesh = SubResource("CapsuleMesh_mh3lg")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=92811823]
shape = SubResource("CapsuleShape3D_mh3lg")
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="." unique_id=1370024449]
unique_name_in_owner = true
[node name="StateMachine" type="Node" parent="." unique_id=1286404264 node_paths=PackedStringArray("initial_state")]
unique_name_in_owner = true
script = ExtResource("2_q1hg3")
initial_state = NodePath("IdleState")
[node name="IdleState" type="Node" parent="StateMachine" unique_id=763668255]
script = ExtResource("3_26faq")
state_id = &"idle"
[node name="PatrolState" type="Node" parent="StateMachine" unique_id=2054606629]
script = ExtResource("4_lim44")
state_id = &"patrol"
[node name="PatrolRadiusShape" type="CollisionShape3D" parent="." unique_id=1379515938]
unique_name_in_owner = true
shape = SubResource("SphereShape3D_lim44")
disabled = true
debug_color = Color(1, 1, 0, 1)

View File

@@ -0,0 +1,26 @@
extends State
const PATROL_STATE_ID: StringName = &"patrol"
@export var wait_time: float = 3.0
var runtime_timer: Timer
func enter() -> void:
runtime_timer = Timer.new()
runtime_timer.name = "IdleTimer"
add_child(runtime_timer)
runtime_timer.one_shot = true
runtime_timer.timeout.connect(_on_timer_timeout)
runtime_timer.start(wait_time)
func exit() -> void:
if runtime_timer:
if runtime_timer.is_connected("timeout", _on_timer_timeout):
runtime_timer.timeout.disconnect(_on_timer_timeout)
runtime_timer.queue_free()
runtime_timer = null
func _on_timer_timeout() -> void:
transitioned.emit(self, PATROL_STATE_ID)

View File

@@ -0,0 +1 @@
uid://nga8qx56iwgu

View File

@@ -0,0 +1,12 @@
extends State
const IDLE_STATE_ID: StringName = &"idle"
@onready var agent: AIBase = owner
func enter() -> void:
agent.navigate_to_random_point()
func physics_update(_delta) -> void:
if agent.nav_agent.is_navigation_finished():
transitioned.emit(self, IDLE_STATE_ID)

View File

@@ -0,0 +1 @@
uid://bngfthvt04ivv

View File

@@ -0,0 +1,20 @@
extends Node
class_name State
@export var state_id: StringName = &""
@warning_ignore("unused_signal")
signal transitioned(state: State, new_state_id: StringName)
func enter() -> void:
pass
func exit() -> void:
pass
func update(_delta) -> void:
pass
func physics_update(_delta) -> void:
pass

View File

@@ -0,0 +1 @@
uid://dpso7abi5ftyw

View File

@@ -0,0 +1,61 @@
extends Node
class_name StateMachine
@export var initial_state: State
var current_state: State
var states: Dictionary[StringName, State] = {}
var is_initialized: bool = false
var _enabled: bool = true
var enable: bool:
set(value):
_enabled = value
_set_enabled(value)
get:
return _enabled
func _ready() -> void:
_initialize_states()
_set_enabled(enable)
func _initialize_states() -> void:
if is_initialized:
return
for state in get_children():
if state is State:
states[state.state_id] = state
state.transitioned.connect(_on_state_transition)
is_initialized = true
func _set_enabled(value: bool) -> void:
_enabled = value
if not _enabled:
return
_initialize_states()
if initial_state and current_state == null:
current_state = initial_state
current_state.enter()
func physics_process(delta) -> void:
if current_state and _enabled:
current_state.physics_update(delta)
func _on_state_transition(state: State, new_state_id: StringName) -> void:
if state != current_state:
return
var new_state = states.get(new_state_id)
if not new_state:
return
if current_state:
current_state.exit()
current_state = new_state
current_state.enter()

View File

@@ -0,0 +1 @@
uid://ps3vlu2qvmop

View File

@@ -0,0 +1,31 @@
extends Node
const MIN_VOLUME: float = 0.0
const MAX_VOLUME: float = 1.0
func _ready() -> void:
load_save_data()
func load_save_data() -> void:
for bus_name in GameState.save_data.audio_bus_volumes:
_apply_audio_bus_volume(bus_name, GameState.save_data.audio_bus_volumes[bus_name])
func set_audio_bus_volume(bus_name: StringName, linear_value: float) -> void:
var normalized_bus_name := str(bus_name)
var clamped_value := clampf(linear_value, MIN_VOLUME, MAX_VOLUME)
GameState.save_data.audio_bus_volumes[normalized_bus_name] = clamped_value
_apply_audio_bus_volume(normalized_bus_name, clamped_value)
GameState.save_game()
func get_audio_bus_volume(bus_name: StringName, default_value: float = 1.0) -> float:
return float(GameState.save_data.audio_bus_volumes.get(str(bus_name), default_value))
func _apply_audio_bus_volume(bus_name: String, linear_value: float) -> void:
var bus_index := AudioServer.get_bus_index(bus_name)
if bus_index == -1:
return
var clamped_value := clampf(linear_value, MIN_VOLUME, MAX_VOLUME)
AudioServer.set_bus_volume_db(bus_index, linear_to_db(clamped_value))
AudioServer.set_bus_mute(bus_index, is_zero_approx(clamped_value))

View File

@@ -0,0 +1 @@
uid://dcttbbavtwtsg

View File

@@ -0,0 +1,56 @@
extends VBoxContainer
class_name AudioOption
const MIN_VOLUME: float = 0.0
const MAX_VOLUME: float = 1.0
@export var bus_name: StringName = &"Master"
@export var label_text: String = "Master Volume"
@export_range(0.0, 1.0, 0.01) var default_volume: float = 1.0
@onready var label: Label = $Label
@onready var slider: HSlider = $HSlider
func _ready() -> void:
slider.min_value = MIN_VOLUME
slider.max_value = MAX_VOLUME
slider.step = 0.01
slider.value_changed.connect(_on_slider_value_changed)
refresh()
func refresh() -> void:
label.text = label_text
if not has_bus():
slider.set_value_no_signal(default_volume)
slider.editable = false
label.text = "%s (bus missing)" % label_text
return
slider.editable = true
var saved_volume := AudioManager.get_audio_bus_volume(bus_name, default_volume)
slider.set_value_no_signal(saved_volume)
apply_current_value()
func reset_to_default() -> void:
slider.value = default_volume
func apply_current_value() -> void:
_set_bus_volume(slider.value)
func has_bus() -> bool:
return get_bus_index() != -1
func get_bus_index() -> int:
return AudioServer.get_bus_index(bus_name)
func _set_bus_volume(linear_value: float) -> void:
if not has_bus():
return
var clamped_value := clampf(linear_value, MIN_VOLUME, MAX_VOLUME)
AudioManager.set_audio_bus_volume(bus_name, clamped_value)
func _on_slider_value_changed(value: float) -> void:
_set_bus_volume(value)

View File

@@ -0,0 +1 @@
uid://bnh1y560h55vc

View File

@@ -0,0 +1,14 @@
[gd_scene format=3 uid="uid://cescrovsjlwke"]
[ext_resource type="Script" uid="uid://bnh1y560h55vc" path="res://core/audio/settings/audio_option.gd" id="1_orafl"]
[node name="AudioOption" type="VBoxContainer" unique_id=1509773712]
custom_minimum_size = Vector2(300, 0)
script = ExtResource("1_orafl")
[node name="Label" type="Label" parent="." unique_id=781845786]
layout_mode = 2
text = "Master Volume"
[node name="HSlider" type="HSlider" parent="." unique_id=1286896253]
layout_mode = 2

View File

@@ -0,0 +1,36 @@
extends Control
class_name SettingsMenu
@onready var audio_options_container: VBoxContainer = $%AudioOptionsContainer
@onready var reset_button: Button = $%ResetToDefault_Button
@onready var close_button: Button = $%Close_Button
func _ready() -> void:
reset_button.pressed.connect(_on_reset_to_default_button_pressed)
close_button.pressed.connect(_on_close_button_pressed)
_refresh_audio_options()
func _get_audio_options() -> Array[AudioOption]:
var audio_options: Array[AudioOption] = []
for child in audio_options_container.get_children():
if child is AudioOption:
audio_options.append(child)
return audio_options
func _refresh_audio_options() -> void:
for audio_option in _get_audio_options():
audio_option.refresh()
func _apply_current_audio_values() -> void:
for audio_option in _get_audio_options():
audio_option.apply_current_value()
func _on_reset_to_default_button_pressed() -> void:
for audio_option in _get_audio_options():
audio_option.reset_to_default()
func _on_close_button_pressed() -> void:
visible = false

View File

@@ -0,0 +1 @@
uid://cvr25q1blyxg

View File

@@ -0,0 +1,74 @@
[gd_scene format=3 uid="uid://caqf471x0kttc"]
[ext_resource type="Script" uid="uid://cvr25q1blyxg" path="res://core/audio/settings/settings_menu.gd" id="1_4lekq"]
[ext_resource type="PackedScene" uid="uid://cescrovsjlwke" path="res://core/audio/settings/audio_option.tscn" id="1_hwcco"]
[node name="SettingsMenu" type="Control" unique_id=1639777294]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_4lekq")
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=315821242]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -79.5
offset_top = -110.0
offset_right = 79.5
offset_bottom = 110.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=648635784]
layout_mode = 2
size_flags_vertical = 0
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="AudioOptionsContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer" unique_id=824023653]
unique_name_in_owner = true
layout_mode = 2
theme_override_constants/separation = 12
[node name="MasterVolume_AudioOption" parent="PanelContainer/MarginContainer/AudioOptionsContainer" unique_id=1509773712 instance=ExtResource("1_hwcco")]
layout_mode = 2
[node name="HSeparator" type="HSeparator" parent="PanelContainer/MarginContainer/AudioOptionsContainer" unique_id=1053430754]
layout_mode = 2
[node name="MusicVolume_AudioOption" parent="PanelContainer/MarginContainer/AudioOptionsContainer" unique_id=690924616 instance=ExtResource("1_hwcco")]
layout_mode = 2
bus_name = &"Music"
label_text = "Music Volume"
[node name="HSeparator2" type="HSeparator" parent="PanelContainer/MarginContainer/AudioOptionsContainer" unique_id=453716295]
layout_mode = 2
[node name="SFXVolume_AudioOption" parent="PanelContainer/MarginContainer/AudioOptionsContainer" unique_id=2096291127 instance=ExtResource("1_hwcco")]
layout_mode = 2
bus_name = &"SFX"
label_text = "SFX Volume"
[node name="HSeparator3" type="HSeparator" parent="PanelContainer/MarginContainer/AudioOptionsContainer" unique_id=498553283]
layout_mode = 2
[node name="ResetToDefault_Button" type="Button" parent="PanelContainer/MarginContainer/AudioOptionsContainer" unique_id=720420423]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 0
text = "Reset To Default"
[node name="Close_Button" type="Button" parent="PanelContainer/MarginContainer/AudioOptionsContainer" unique_id=1137906684]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 0
text = "Close"

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@ class_name ChunkInfo
@export_group("Set Piece Rules (Unique pieces)")
@export var exclusive_biome: String = "" # Es: "Forest"
@export_range(0, 5, 1) var uniqueness: int = 0 #0=common; 5=unique
@export_group("Base exit")
@export var north: bool = false
@@ -12,6 +13,12 @@ class_name ChunkInfo
@export var south: bool = false
@export var west: bool = false
@export_group("River exit")
@export var river_north: bool = false
@export var river_est: bool = false
@export var river_south: bool = false
@export var river_west: bool = false
@export_group("Margin heights (level 0, 1, 2)")
@export_range(0, 2, 1) var height_north: int = 0
@export_range(0, 2, 1) var height_est: int = 0
@@ -29,14 +36,17 @@ func _ready() -> void:
func get_rotated_data(steps_90: int) -> Dictionary:
var original_exit = [north, est, south, west]
var original_river_exit = [river_north, river_est, river_south, river_west]
var original_height = [height_north, height_est, height_south, height_west]
var calculated_exit = []
var calculated_river_exit = []
var calculated_height = []
for i in range(4):
var index = (i - steps_90 + 4) % 4
calculated_exit.append(original_exit[index])
calculated_river_exit.append(original_river_exit[index])
calculated_height.append(original_height[index])
return {
@@ -44,6 +54,10 @@ func get_rotated_data(steps_90: int) -> Dictionary:
"north": calculated_exit[0], "est": calculated_exit[1],
"south": calculated_exit[2], "west": calculated_exit[3]
},
"river_connections": {
"north": calculated_river_exit[0], "est": calculated_river_exit[1],
"south": calculated_river_exit[2], "west": calculated_river_exit[3]
},
"heights": {
"north": calculated_height[0], "est": calculated_height[1],
"south": calculated_height[2], "west": calculated_height[3]

View File

@@ -0,0 +1,5 @@
extends Marker3D
class_name PropInfo
@export var available_props: Array[PackedScene]
@export_range(0, 5, 1) var uniqueness: int = 0 #0=common; 5=unique

View File

@@ -0,0 +1 @@
uid://dg6ngy4pmtsyc

View File

@@ -47,15 +47,112 @@ var next_stop_index: int = 0
var stop_multiply: float = 1.0
var is_restarting: bool = false
var tween_restart: Tween
var environment_config: EnvironmentConfig
func _ready() -> void:
_cache_environment_config()
if curve != null and curve.get_baked_length() > 0:
build_rails()
build_train()
_plan_stops()
_apply_initial_train_start()
else:
print("WARNING: Draw Path3D for rails!")
func _cache_environment_config() -> void:
var parent_node: Node = get_parent()
if parent_node != null:
var day_night_node := parent_node.get_node_or_null("DayNight") as EnvironmentManagerRoot
if day_night_node != null:
environment_config = day_night_node.environment_config
return
var current_scene: Node = get_tree().current_scene
if current_scene == null:
return
for child in current_scene.get_children():
var environment_manager := child as EnvironmentManagerRoot
if environment_manager != null:
environment_config = environment_manager.environment_config
return
func _apply_initial_train_start() -> void:
if train_instance == null or curve == null:
return
var total_length: float = curve.get_baked_length()
if total_length <= 0.0:
return
#start from a random position
if environment_config != null and environment_config.train_start_from_random_position:
train_progress = randf() * total_length
_update_next_stop_index_from_progress()
elif environment_config != null and not stop_offset.is_empty():
#start from a specific stop
var stop_index: int = clampi(environment_config.train_start_stop_index, 0, stop_offset.size() - 1)
train_progress = stop_offset[stop_index]
_set_next_stop_index_from_current(stop_index)
else:
train_progress = wrapf(train_progress, 0.0, total_length)
_update_next_stop_index_from_progress()
_snap_train_to_progress()
func _set_next_stop_index_from_current(current_stop_index: int) -> void:
if stop_offset.is_empty():
next_stop_index = 0
return
if train_speed >= 0.0:
next_stop_index = (current_stop_index + 1) % stop_offset.size()
else:
next_stop_index = current_stop_index - 1
if next_stop_index < 0:
next_stop_index = stop_offset.size() - 1
func _update_next_stop_index_from_progress() -> void:
if stop_offset.is_empty():
next_stop_index = 0
return
if train_speed >= 0.0:
next_stop_index = 0
for i in range(stop_offset.size()):
if stop_offset[i] > train_progress + 0.001:
next_stop_index = i
return
return
next_stop_index = stop_offset.size() - 1
for i in range(stop_offset.size() - 1, -1, -1):
if stop_offset[i] < train_progress - 0.001:
next_stop_index = i
return
func _snap_train_to_progress() -> void:
if train_instance == null or curve == null:
return
var total_length: float = curve.get_baked_length()
if total_length <= 0.0:
return
train_progress = wrapf(train_progress, 0.0, total_length)
var center_position: Vector3 = to_global(curve.sample_baked(train_progress, true))
var prog_forward: float = wrapf(train_progress + 2.0, 0.0, total_length)
var forward_position: Vector3 = to_global(curve.sample_baked(prog_forward, true))
train_instance.global_position = center_position
if center_position.distance_to(forward_position) > 0.01:
train_instance.look_at(forward_position, Vector3.UP)
train_instance.rotate_y(PI)
if cameras:
cameras.global_position = center_position
cameras.global_basis = train_instance.global_basis
func build_rails() -> void:
var mat_wood = StandardMaterial3D.new()
mat_wood.albedo_color = Color(0.35, 0.2, 0.1)

View File

@@ -0,0 +1,7 @@
[gd_resource type="NoiseTexture2D" format=3 uid="uid://b2xcpt2y8v32x"]
[sub_resource type="FastNoiseLite" id="FastNoiseLite_oqwsk"]
[resource]
noise = SubResource("FastNoiseLite_oqwsk")
seamless = true

View File

@@ -1,9 +1,17 @@
shader_type canvas_item;
// Cattura tutto quello che c'è sotto questo nodo
uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap;
uniform float blur_amount : hint_range(0.0, 5.0) = 2.5;
uniform float focus_size : hint_range(0.0, 1.0) = 0.4;
uniform float transition_softness : hint_range(0.01, 1.0) = 0.3;
void fragment() {
COLOR = textureLod(screen_texture, SCREEN_UV, blur_amount);
float dist_from_center = abs(SCREEN_UV.y - 0.5);
float mask = smoothstep(focus_size / 2.0, (focus_size / 2.0) + transition_softness, dist_from_center);
// Applica il blur
vec4 blurred_screen = textureLod(screen_texture, SCREEN_UV, blur_amount * mask);
COLOR = blurred_screen;
}

View File

@@ -1,16 +1,21 @@
class_name DayNightController
extends Node
const TIME_OPTION_SUNRISE: int = 0
const TIME_OPTION_DAY: int = 1
const TIME_OPTION_SUNSET: int = 2
const TIME_OPTION_NIGHT: int = 3
var environment_config: EnvironmentConfig
#Multiply for debug
@export var time_scale: float = 1.0
@export var paused: bool = false
signal time_changed(time: float)
var current_time: float = 0.0
var _current_time_option_index: int = -1
func _init(environmentconfig: EnvironmentConfig) -> void:
environment_config = environmentconfig
@@ -19,6 +24,7 @@ func _ready() -> void:
#connect events
UIEvents.set_day_time.connect(_on_set_day_time)
UIEvents.toggle_pause_daytime.connect(_on_toggle_pause_daytime)
UIEvents.time_option_item_changed.connect(_time_option_changed)
current_time = environment_config.start_time
update_time(current_time)
@@ -44,9 +50,34 @@ func set_time(t: float) -> void:
update_time(current_time)
func update_time(currtime: float) -> void:
var time_option_index: int = get_time_option_index(currtime)
if time_option_index != _current_time_option_index:
_current_time_option_index = time_option_index
UIEvents.day_time_option_changed.emit(time_option_index)
emit_signal("time_changed", currtime)
UIEvents.day_time_changed.emit(currtime, get_time_string())
func get_time_option_index(currtime: float) -> int:
if currtime >= environment_config.night or currtime < environment_config.sunrise:
return TIME_OPTION_NIGHT
if currtime < environment_config.day:
return TIME_OPTION_SUNRISE
if currtime < environment_config.sunset:
return TIME_OPTION_DAY
return TIME_OPTION_SUNSET
func _time_option_changed(index: int) -> void:
match index:
TIME_OPTION_SUNRISE:
set_time(environment_config.sunrise)
TIME_OPTION_DAY:
set_time(environment_config.day)
TIME_OPTION_SUNSET:
set_time(environment_config.sunset)
TIME_OPTION_NIGHT:
set_time(environment_config.night)
func _on_set_day_time(value: float) -> void:
set_time(value)

View File

@@ -9,6 +9,7 @@
[ext_resource type="Material" uid="uid://codq5gx71rj4o" path="res://core/daynight/environment_dust_material.tres" id="8_amaf0"]
[ext_resource type="Shader" uid="uid://bkn6eqgbn37jx" path="res://core/daynight/blur.gdshader" id="9_amaf0"]
[ext_resource type="Shader" uid="uid://b5wa82soyhsqm" path="res://core/daynight/environment_shadows.gdshader" id="10_tuauy"]
[ext_resource type="Texture2D" uid="uid://b2xcpt2y8v32x" path="res://core/daynight/FogNoise.tres" id="11_tuauy"]
[ext_resource type="FastNoiseLite" uid="uid://c0mtwrkptjcuw" path="res://core/daynight/environment_shadows_noise_clouds.tres" id="11_yn8v8"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_ruhh7"]
@@ -31,6 +32,7 @@ render_priority = 0
shader = ExtResource("2_r4tfj")
shader_parameter/use_red_as_alpha = true
shader_parameter/fog_color = Color(0.8, 0.85, 0.9, 0.5)
shader_parameter/fog_density = 0.25
shader_parameter/scroll_speed = Vector2(0.05, 0.01)
shader_parameter/texture_scale = Vector2(1, 1)
shader_parameter/edge_softness_y = 0.2
@@ -40,9 +42,58 @@ shader_parameter/sun_color = Color(0.2841828, 0.2023238, 0.27953526, 1)
[sub_resource type="Resource" id="Resource_r4tfj"]
script = ExtResource("2_h6hjk")
morning_color = Color(0.93333334, 0.78039217, 0.6039216, 1)
afternoon_color = Color(0.9490196, 0.62352943, 0.38039216, 1)
night_color = Color(0.18431373, 0.18431373, 0.4862745, 1)
sun_rotation_morning = Vector3(-50, -90, 0)
sun_rotation_afternoon = Vector3(-120, -90, 0)
sun_rotation_night = Vector3(-130, -90, 0)
night_light_energy = 1.0
exposure_morning = 0.8
exposure_afternoon = 0.7
exposure_night = 0.7
sky_top_morning = Color(0.11764706, 0.35686275, 0.5411765, 1)
sky_top_afternoon = Color(0.7019608, 0.4509804, 0.2, 1)
sky_top_night = Color(0.050980393, 0.050980393, 0.14901961, 1)
sky_horizon_morning = Color(0.4627451, 0.6745098, 0.8627451, 1)
sky_horizon_afternoon = Color(0.93333334, 0.6, 0.8039216, 1)
sky_horizon_night = Color(0.14901961, 0.101960786, 0.2509804, 1)
grad_top_morning = Color(0.7921569, 0.8784314, 0.95686275, 1)
grad_top_afternoon = Color(0.8627451, 0.24313726, 0.5411765, 1)
grad_top_night = Color(0.16470589, 0.16470589, 0.26666668, 1)
grad_bot_morning = Color(0.05882353, 0.078431375, 0.21568628, 1)
grad_bot_afternoon = Color(0.2509804, 0.019607844, 0.043137256, 1)
grad_intensity_morning = 0.05
grad_intensity_afternoon = 0.1
grad_intensity_night = 0.5
fog_color_morning = Color(0.7490196, 0.8509804, 0.9490196, 1)
fog_color_afternoon = Color(0.9823975, 0.8034449, 0.8778439, 1)
fog_color_night = Color(0.38409987, 0.28720453, 0.5907528, 1)
fog_density_morning = 0.01
fog_density_afternoon = 0.02
glow_morning = 0.4
glow_night = 0.8
material_fog = SubResource("ShaderMaterial_b5atu")
material_drops = SubResource("StandardMaterial3D_r4tfj")
material_clouds = SubResource("ShaderMaterial_ruhh7")
lightning_color = Color(1, 0.9843137, 0.4627451, 1)
lightning_min = 2
lightning_max = 4
lightning_scale_min = 2.0
lightning_scale_max = 5.0
godray_max_rain = 30
godray_spawn_radius = 100.0
godray_spawn_offset = Vector3(20, 80, 20)
godray_rotation_degrees = Vector3(50, 30, 0)
godray_scale = Vector3(2, 25, 50)
snow_amount = 2000.0
wind_amount = 50
cloud_speed = 0.01
fireflies_amount = 550
fireflies_spawn_ray = 60.0
water_color_morning = Color(0.33333334, 0.654902, 0.5294118, 1)
water_color_afternoon = Color(0.5803922, 0.5137255, 0.2901961, 1)
water_color_night = Color(0.48235294, 0.45490196, 0.69411767, 1)
metadata/_custom_type_script = "uid://butda6k2tli3o"
[sub_resource type="Gradient" id="Gradient_i3hjl"]
@@ -85,7 +136,7 @@ gradient = SubResource("Gradient_djqkg")
lifetime_randomness = 0.5
emission_shape_scale = Vector3(5, 5, 5)
emission_shape = 3
emission_box_extents = Vector3(25, 5, 25)
emission_box_extents = Vector3(1, 1, 1)
direction = Vector3(0, 1, 0)
initial_velocity_min = 6.0
initial_velocity_max = 6.0
@@ -99,10 +150,9 @@ point_count = 3
[sub_resource type="ShaderMaterial" id="ShaderMaterial_cer0u"]
render_priority = 0
shader = ExtResource("5_djqkg")
shader_parameter/wave_frequency = 1.0
shader_parameter/base_amplitude = 0.5
shader_parameter/secondary_ratio = 0.6
shader_parameter/time_scale_base = 3.0
shader_parameter/time_scale = 3.065
shader_parameter/wave_amplitude = 0.3
shader_parameter/wave_frequency = 0.25
[sub_resource type="TubeTrailMesh" id="TubeTrailMesh_vo82p"]
material = SubResource("ShaderMaterial_cer0u")
@@ -174,7 +224,9 @@ size = Vector2(0.05, 0.8)
[sub_resource type="ShaderMaterial" id="ShaderMaterial_kavln"]
shader = ExtResource("9_amaf0")
shader_parameter/blur_amount = 0.6000000285
shader_parameter/blur_amount = 0.50000002375
shader_parameter/focus_size = 0.54400002584
shader_parameter/transition_softness = 0.00999999977648
[sub_resource type="QuadMesh" id="QuadMesh_sxgg7"]
size = Vector2(2, 2)
@@ -184,12 +236,12 @@ noise = ExtResource("11_yn8v8")
seamless = true
[sub_resource type="ShaderMaterial" id="ShaderMaterial_oqt1s"]
render_priority = 0
render_priority = -1
shader = ExtResource("10_tuauy")
shader_parameter/noise_texture = SubResource("NoiseTexture2D_0fcg4")
shader_parameter/cloud_scale = 0.0070000003325
shader_parameter/cloud_speed = 0.0030000001425
shader_parameter/direction = Vector2(0.5, 0.2)
shader_parameter/direction = Vector2(0.1, 0.025)
shader_parameter/sun_angle = Vector2(0.5, 0.5)
shader_parameter/cloud_density = 0.400000019
shader_parameter/center_sharpness = 0.138000006555
@@ -201,7 +253,24 @@ shader_parameter/fade_end = 800.000035625
shader_parameter/height_fade_start = 10.0
shader_parameter/height_fade_end = 50.0
[node name="EnvironmentManager" type="Node3D" unique_id=1611939731 node_paths=PackedStringArray("particles_wind", "particles_snow", "particles_fireflies", "particles_rain", "environment_dust", "blur", "environment_shadows")]
[sub_resource type="QuadMesh" id="QuadMesh_yn8v8"]
size = Vector2(2, 2)
[sub_resource type="ShaderMaterial" id="ShaderMaterial_sxgg7"]
render_priority = 2
shader = ExtResource("2_r4tfj")
shader_parameter/fog_noise = ExtResource("11_tuauy")
shader_parameter/use_red_as_alpha = true
shader_parameter/fog_color = Color(0.8, 0.8509804, 0.9019608, 0.2509804)
shader_parameter/fog_density = 0.25
shader_parameter/scroll_speed = Vector2(0, 0.01)
shader_parameter/texture_scale = Vector2(1, 1)
shader_parameter/edge_softness_y = 0.16400000779
shader_parameter/edge_softness_x = 0.5
shader_parameter/night_intensity = 0.0
shader_parameter/sun_color = Color(1, 1, 1, 1)
[node name="EnvironmentManager" type="Node3D" unique_id=1611939731 node_paths=PackedStringArray("particles_wind", "particles_snow", "particles_fireflies", "particles_rain", "environment_dust", "blur", "environment_shadows", "fog")]
script = ExtResource("1_wecen")
environment_config = SubResource("Resource_r4tfj")
particles_wind = NodePath("Particles_Wind")
@@ -212,6 +281,7 @@ godray = ExtResource("5_411rw")
environment_dust = NodePath("EnvironmentDust")
blur = NodePath("Blur")
environment_shadows = NodePath("EnvironmentCloudsShadows")
fog = NodePath("Fog")
[node name="Particles_Fireflies" type="GPUParticles3D" parent="." unique_id=947538133]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
@@ -222,9 +292,10 @@ process_material = SubResource("ParticleProcessMaterial_i3hjl")
draw_pass_1 = SubResource("QuadMesh_b5atu")
[node name="Particles_Wind" type="GPUParticles3D" parent="." unique_id=1238214694]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 50, 0)
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 50, 0)
cast_shadow = 0
emitting = false
amount = 100
amount = 25
lifetime = 3.0
fixed_fps = 60
process_material = SubResource("ParticleProcessMaterial_ruhh7")
@@ -271,3 +342,15 @@ mouse_filter = 2
extra_cull_margin = 16384.0
mesh = SubResource("QuadMesh_sxgg7")
surface_material_override/0 = SubResource("ShaderMaterial_oqt1s")
[node name="Fog" type="Node3D" parent="." unique_id=1626352238]
[node name="MeshInstance3D" type="MeshInstance3D" parent="Fog" unique_id=96635311]
transform = Transform3D(-42.075005, 7.553328e-06, 1.7196172e-13, 0, -3.7766642e-06, 45, 3.6783138e-06, 86.4, 1.9670128e-06, -41.926987, 14, 0)
mesh = SubResource("QuadMesh_yn8v8")
surface_material_override/0 = SubResource("ShaderMaterial_sxgg7")
[node name="MeshInstance3D2" type="MeshInstance3D" parent="Fog" unique_id=10121049]
transform = Transform3D(-42.075005, 7.553328e-06, 1.7196172e-13, 0, -3.7766642e-06, 45, 3.6783138e-06, 86.4, 1.9670128e-06, 59.320038, 14, 0)
mesh = SubResource("QuadMesh_yn8v8")
surface_material_override/0 = SubResource("ShaderMaterial_sxgg7")

View File

@@ -1,6 +1,10 @@
class_name EnvironmentManagerRoot
extends Node3D
const NOISE_TEXTURE: Texture2D = preload("res://core/daynight/noise.tres")
const WEATHER_SHADER: Material = preload("res://core/daynight/weather_overlay.tres")
const DYNAMIC_ENVIRONMENT_UPDATES_PER_FRAME: int = 2 #how many update of the environment (apply materials) will be done per frame
@export var environment_config: EnvironmentConfig
@export_group("Camera")
@@ -16,6 +20,7 @@ extends Node3D
@export var environment_dust: ColorRect
@export var blur: ColorRect
@export var environment_shadows: MeshInstance3D
@export var fog: Node3D
@export_group("Sound")
@export var thunder_sounds: Array[AudioStream]
@@ -30,9 +35,13 @@ var weather_controller: WeatherController
var day_tween: Tween
var day_time: float = 0.0
var pending_environment_nodes: Dictionary = {}
func _ready() -> void:
#connect shader when new node is added
get_tree().node_added.connect(_on_tree_node_added)
#set noise texture 2d to materials in special group
ApplyWindNoiseToMaterials()
#set weather overlay (snow + rain) to materials in special group
@@ -55,6 +64,7 @@ func _ready() -> void:
godray,
environment_dust,
blur,
fog,
environment_shadows,
sun,
environment,
@@ -67,10 +77,67 @@ func _ready() -> void:
weather_controller.name = "WeatherController"
add_child(weather_controller)
func _process(_delta: float) -> void:
_process_pending_environment_nodes()
func _on_tree_node_added(node: Node) -> void:
if node.is_in_group("wind_node") or node.is_in_group("weather_node") or node.is_in_group("weather_vegetables_node"):
_queue_dynamic_environment_node(node)
func _queue_dynamic_environment_node(node: Node) -> void:
if not is_instance_valid(node):
return
var ancestor = node.get_parent()
while ancestor != null:
if pending_environment_nodes.has(ancestor.get_instance_id()):
return
ancestor = ancestor.get_parent()
var stale_ids: Array[int] = []
for node_id in pending_environment_nodes.keys():
var pending_node = pending_environment_nodes[node_id]
if not is_instance_valid(pending_node):
stale_ids.append(node_id)
continue
if node.is_ancestor_of(pending_node):
stale_ids.append(node_id)
for node_id in stale_ids:
pending_environment_nodes.erase(node_id)
pending_environment_nodes[node.get_instance_id()] = node
func _process_pending_environment_nodes() -> void:
var processed = 0
for node_id in pending_environment_nodes.keys():
if processed >= DYNAMIC_ENVIRONMENT_UPDATES_PER_FRAME:
break
var node = pending_environment_nodes[node_id]
pending_environment_nodes.erase(node_id)
if not is_instance_valid(node):
continue
_apply_dynamic_environment_materials(node)
processed += 1
func _apply_dynamic_environment_materials(node: Node) -> void:
if not is_instance_valid(node):
return
if node.is_in_group("wind_node"):
_apply_wind_noise_to_node(node, NOISE_TEXTURE)
if node.is_in_group("weather_node"):
_apply_weather_overlay_to_node(node, WEATHER_SHADER)
if node.is_in_group("weather_vegetables_node"):
_clear_weather_overlay_from_node(node)
func ApplyWindNoiseToMaterials():
var noise_tex = preload("res://core/daynight/noise.tres")
for node in get_tree().get_nodes_in_group("wind_node"):
_apply_wind_noise_to_node(node, noise_tex)
_apply_wind_noise_to_node(node, NOISE_TEXTURE)
func _apply_wind_noise_to_node(node: Node, noise_tex: Texture2D) -> void:
if node is GeometryInstance3D:
@@ -88,15 +155,57 @@ func _apply_wind_noise_to_node(node: Node, noise_tex: Texture2D) -> void:
_apply_wind_noise_to_node(child, noise_tex)
func ApplyWeatherShaderToMaterials():
var weather_shader = preload("res://core/daynight/weather_overlay.tres")
for node in get_tree().get_nodes_in_group("weather_node"):
_apply_weather_overlay_to_node(node, WEATHER_SHADER)
for node in get_tree().get_nodes_in_group("weather_vegetables_node"):
_clear_weather_overlay_from_node(node)
func _apply_weather_overlay_to_node(node: Node, material: Material) -> void:
if node.is_in_group("weather_vegetables_node"):
_clear_weather_overlay_from_node(node)
return
if node is GeometryInstance3D:
node.material_overlay = weather_shader
if _geometry_uses_alpha_texture(node):
node.material_overlay = null
else:
node.material_overlay = material
for child in node.get_children():
if child is GeometryInstance3D:
child.material_overlay = weather_shader
_apply_weather_overlay_to_node(child, material)
func _clear_weather_overlay_from_node(node: Node) -> void:
if node is GeometryInstance3D:
node.material_overlay = null
for child in node.get_children():
_clear_weather_overlay_from_node(child)
func _geometry_uses_alpha_texture(node: GeometryInstance3D) -> bool:
var material_override := node.material_override as ShaderMaterial
if _shader_material_uses_alpha_texture(material_override):
return true
if node is MeshInstance3D:
for surface_index in node.get_surface_override_material_count():
var surface_material := node.get_surface_override_material(surface_index) as ShaderMaterial
if _shader_material_uses_alpha_texture(surface_material):
return true
if node.mesh:
for surface_index in node.mesh.get_surface_count():
var mesh_material := node.mesh.surface_get_material(surface_index) as ShaderMaterial
if _shader_material_uses_alpha_texture(mesh_material):
return true
return false
func _shader_material_uses_alpha_texture(material: ShaderMaterial) -> bool:
if material == null or material.shader == null:
return false
return material.shader.code.find("alpha_texture") != -1
func select_day_time(normalized_time: float) -> void:
#set show_day_time_debug = true to show debug on screen

View File

@@ -67,7 +67,6 @@ void fragment() {
float is_center = step(center_sharpness, internal_n);
ALBEDO = shadow_color;
ALPHA = mix(opacity_edge, opacity_center, is_center) * final_fade;
}
}

View File

@@ -7,6 +7,7 @@ uniform bool use_red_as_alpha = true;
group_uniforms Settings;
uniform vec4 fog_color : source_color = vec4(0.8, 0.85, 0.9, 0.5);
uniform float fog_density : hint_range(0.0, 1.0) = 0.25;
uniform vec2 scroll_speed = vec2(0.05, 0.01);
uniform vec2 texture_scale = vec2(1.0, 1.0);
@@ -36,5 +37,5 @@ void fragment() {
vec3 dark_fog = tinted_fog * 0.5;
ALBEDO = mix(tinted_fog, dark_fog, night_intensity);
ALPHA = fog_color.a * noise_alpha * edge_mask;
ALPHA = fog_color.a * fog_density * noise_alpha * edge_mask;
}

View File

@@ -8,7 +8,12 @@ global uniform float global_wind_speed;
global uniform float global_wind_strength;
global uniform vec2 global_wind_direction;
//global uniform sampler2D global_wind_noise : filter_linear_mipmap;
global uniform float global_snow_amount;
global uniform float global_snow_start_time = -1.0;
global uniform float global_snow_accumulation_speed = 0.005;
global uniform float global_snow_melt_time = -1.0;
global uniform float global_snow_melt_speed = 0.1;
global uniform float global_snow_amount = 0.0;
global uniform float global_rain_intensity;
// --- PARAMETRI ESTETICI ---
uniform bool billboard_enabled = true;
@@ -28,6 +33,7 @@ uniform vec4 variance_color : source_color = vec4(0.3, 0.5, 0.2, 1.0); // Colore
uniform float variance_intensity : hint_range(0.0, 1.0) = 0.4; // Quanto si vedono le chiazze
uniform vec4 snow_color : source_color = vec4(0.85, 0.9, 0.95, 1.0);
uniform float snow_visibility : hint_range(0.0, 1.0) = 1.0;
// --- CONTROLLO GRADIENTE E RANDOM ---
uniform float height_min = 0.0;
@@ -38,6 +44,7 @@ uniform float light_steps : hint_range(1.0, 10.0) = 4.0;
uniform float random_mix : hint_range(0.0, 1.0) = 0.3;
uniform float cast_shadow_strength : hint_range(0.0, 1.0) = 0.6;
uniform float wetness_darkening : hint_range(0.0, 0.5) = 0.25;
varying vec3 v_final_color;
varying float v_shade_factor;
@@ -49,6 +56,21 @@ float hash(vec3 p) {
return fract((p.x + p.y) * p.z);
}
float get_snow_progress() {
float snow_progress = clamp(global_snow_amount, 0.0, 1.0);
if (global_snow_start_time >= 0.0) {
float timed_progress = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
snow_progress = max(snow_progress, timed_progress);
}
if (global_snow_melt_time >= 0.0) {
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
snow_progress = min(snow_progress, 1.0 - melt);
}
return snow_progress;
}
void vertex() {
vec3 instance_pos = MODEL_MATRIX[3].xyz;
v_world_pos = instance_pos; // Salviamo la posizione dell'istanza
@@ -110,21 +132,26 @@ void fragment() {
// Applichiamo la variazione al colore finale dell'erba
vec3 varied_grass_color = mix(v_final_color, variance_color.rgb, noise_sample * variance_intensity);
float snow_amount = smoothstep(0.0, 1.0, get_snow_progress());
float top_mask = 1.0 - shifted_uv.y;
float snow_mask = smoothstep(1.0 - global_snow_amount, 1.2 - global_snow_amount, top_mask);
snow_mask *= step(0.01, global_snow_amount);
float snow_mask = smoothstep(0.65, 1.0, top_mask) * snow_amount * snow_visibility;
snow_mask *= step(0.01, snow_amount);
vec3 dark_snow = snow_color.rgb * (1.0 - shadow_intensity);
vec3 shaded_snow = mix(dark_snow, snow_color.rgb, v_shade_factor);
// Mescoliamo il colore variato con la neve
vec3 final_albedo = mix(varied_grass_color, shaded_snow, snow_mask);
float rain_int = clamp(global_rain_intensity, 0.0, 1.0);
final_albedo *= mix(1.0, 1.0 - wetness_darkening, rain_int);
float final_roughness = mix(0.02, 0.005, rain_int);
ALBEDO = final_albedo;
ALPHA = tex.r * opacity;
ALPHA_SCISSOR_THRESHOLD = 0.5;
ROUGHNESS = 0.02;
ROUGHNESS = final_roughness;
}
void light() {

107
core/daynight/snow_cap.gd Normal file
View File

@@ -0,0 +1,107 @@
## Procedural snow cap geometry for flat roofs and vehicles.
extends Node3D
enum PlacementMode {
AUTO_TARGET_AABB,
MANUAL_SURFACE,
}
const SNOW_CAP_SHADER: Shader = preload("res://core/daynight/snow_cap.gdshader")
@export var placement_mode: PlacementMode = PlacementMode.AUTO_TARGET_AABB
@export var target_path: NodePath
@export var size_inset: Vector2 = Vector2(0.25, 1.0)
@export var position_offset: Vector3 = Vector3(0.0, -0.08, 0.0)
@export var max_snow_height: float = 0.2
@export_range(1, 24) var subdivide_width: int = 6
@export_range(1, 24) var subdivide_depth: int = 18
@export var manual_surface_center: Vector3 = Vector3.ZERO
@export var manual_surface_size: Vector2 = Vector2(2.0, 6.0)
var _mesh_instance: MeshInstance3D
func _ready() -> void:
add_to_group("weather_overlay_ignore")
_ensure_mesh_instance()
_rebuild()
func rebuild() -> void:
_rebuild()
func _ensure_mesh_instance() -> void:
_mesh_instance = get_node_or_null("SnowCapMesh") as MeshInstance3D
if _mesh_instance != null:
return
_mesh_instance = MeshInstance3D.new()
_mesh_instance.name = "SnowCapMesh"
_mesh_instance.add_to_group("weather_overlay_ignore")
add_child(_mesh_instance)
func _rebuild() -> void:
var cap_width: float = 0.0
var cap_depth: float = 0.0
var cap_surface_center := Vector3.ZERO
if placement_mode == PlacementMode.MANUAL_SURFACE:
cap_width = manual_surface_size.x
cap_depth = manual_surface_size.y
cap_surface_center = manual_surface_center
else:
var target_mesh: MeshInstance3D = get_node_or_null(target_path) as MeshInstance3D
if target_mesh == null:
if _mesh_instance:
_mesh_instance.visible = false
return
var target_aabb: AABB = _get_target_aabb(target_mesh)
cap_width = max(target_aabb.size.x - size_inset.x, 0.1)
cap_depth = max(target_aabb.size.z - size_inset.y, 0.1)
cap_surface_center = Vector3(
target_aabb.position.x + target_aabb.size.x * 0.5,
target_aabb.position.y + target_aabb.size.y,
target_aabb.position.z + target_aabb.size.z * 0.5
)
var cap_center := cap_surface_center + Vector3(0.0, max_snow_height * 0.5, 0.0)
var box_mesh := BoxMesh.new()
box_mesh.size = Vector3(cap_width, max_snow_height, cap_depth)
box_mesh.subdivide_width = subdivide_width
box_mesh.subdivide_depth = subdivide_depth
_mesh_instance.mesh = box_mesh
_mesh_instance.position = cap_center + position_offset
_mesh_instance.material_override = _build_material(cap_width, cap_depth)
_mesh_instance.visible = true
func _build_material(cap_width: float, cap_depth: float) -> ShaderMaterial:
var material := ShaderMaterial.new()
material.shader = SNOW_CAP_SHADER
material.set_shader_parameter("max_height", max_snow_height)
material.set_shader_parameter("half_width", cap_width * 0.5)
material.set_shader_parameter("half_depth", cap_depth * 0.5)
return material
func _get_target_aabb(target_mesh: MeshInstance3D) -> AABB:
var points: Array[Vector3] = []
for point in _get_aabb_points(target_mesh.get_aabb()):
points.append(target_mesh.transform * point)
var merged := AABB(points[0], Vector3.ZERO)
for point in points:
merged = merged.expand(point)
return merged
func _get_aabb_points(aabb: AABB) -> Array[Vector3]:
var p: Vector3 = aabb.position
var s: Vector3 = aabb.size
return [
p,
p + Vector3(s.x, 0.0, 0.0),
p + Vector3(0.0, s.y, 0.0),
p + Vector3(0.0, 0.0, s.z),
p + Vector3(s.x, s.y, 0.0),
p + Vector3(s.x, 0.0, s.z),
p + Vector3(0.0, s.y, s.z),
p + s,
]

View File

@@ -0,0 +1 @@
uid://bxc6ketfgedxw

View File

@@ -0,0 +1,116 @@
shader_type spatial;
render_mode blend_mix, cull_back, depth_draw_opaque;
global uniform float global_snow_start_time = -1.0;
global uniform float global_snow_accumulation_speed = 0.005;
global uniform float global_snow_melt_time = -1.0;
global uniform float global_snow_melt_speed = 0.1;
global uniform float global_snow_amount = 0.0;
global uniform vec4 global_snow_color = vec4(0.92, 0.96, 1.0, 1.0);
uniform float max_height : hint_range(0.02, 1.0) = 0.2;
uniform float half_width : hint_range(0.01, 20.0) = 1.0;
uniform float half_depth : hint_range(0.01, 20.0) = 1.0;
uniform float noise_scale : hint_range(0.05, 2.0) = 0.35;
uniform float noise_strength : hint_range(0.0, 0.4) = 0.18;
uniform float edge_rounding : hint_range(0.0, 0.3) = 0.12;
uniform float side_shade : hint_range(0.2, 1.0) = 0.82;
uniform float sparkle_strength : hint_range(0.0, 0.1) = 0.03;
varying float v_snow_amount;
varying float v_noise;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}
float value_noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
float fbm(vec2 p) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for (int i = 0; i < 4; i++) {
value += value_noise(p * frequency) * amplitude;
frequency *= 2.0;
amplitude *= 0.5;
}
return clamp(value, 0.0, 1.0);
}
float get_snow_amount() {
float snow_amount = clamp(global_snow_amount, 0.0, 1.0);
if (global_snow_start_time >= 0.0) {
float timed_amount = clamp(
(TIME - global_snow_start_time) * global_snow_accumulation_speed,
0.0,
1.0
);
snow_amount = max(snow_amount, timed_amount);
}
if (global_snow_melt_time >= 0.0) {
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
snow_amount = min(snow_amount, 1.0 - melt);
}
return snow_amount;
}
void vertex() {
vec3 base_vertex = VERTEX;
vec3 world_pos = (MODEL_MATRIX * vec4(base_vertex, 1.0)).xyz;
float snow_amount = get_snow_amount();
float noise_val = fbm(world_pos.xz * noise_scale);
float thickness = max_height * snow_amount * mix(
1.0 - noise_strength,
1.0 + noise_strength,
noise_val
);
float footprint_growth = smoothstep(0.0, 0.35, snow_amount);
float from_bottom = clamp((base_vertex.y + max_height * 0.5) / max_height, 0.0, 1.0);
VERTEX.y = -max_height * 0.5 + thickness * from_bottom;
VERTEX.x *= mix(0.92, 1.0, footprint_growth);
VERTEX.z *= mix(0.94, 1.0, footprint_growth);
float edge_x = abs(base_vertex.x) / max(half_width, 0.001);
float edge_z = abs(base_vertex.z) / max(half_depth, 0.001);
float edge_mask = smoothstep(0.55, 1.0, max(edge_x, edge_z));
float top_mask = smoothstep(0.6, 1.0, from_bottom) * snow_amount;
float round_factor = edge_rounding * edge_mask * top_mask;
VERTEX.x *= 1.0 - round_factor;
VERTEX.z *= 1.0 - round_factor;
v_snow_amount = snow_amount;
v_noise = noise_val;
}
void fragment() {
if (v_snow_amount <= 0.001) {
ALPHA = 0.0;
ALBEDO = global_snow_color.rgb;
ROUGHNESS = 1.0;
SPECULAR = 0.0;
} else {
float facing_up = clamp(NORMAL.y, 0.0, 1.0);
float shade = mix(-sparkle_strength, sparkle_strength, v_noise);
vec3 snow_albedo = clamp(global_snow_color.rgb + vec3(shade), 0.0, 1.0);
ALBEDO = mix(snow_albedo * side_shade, snow_albedo, facing_up);
ROUGHNESS = mix(0.95, 0.72, facing_up);
SPECULAR = 0.18;
ALPHA = smoothstep(0.0, 0.28, v_snow_amount);
}
}

View File

@@ -0,0 +1 @@
uid://c7js7ytvewmab

View File

@@ -6,10 +6,11 @@ global uniform vec2 global_wind_direction;
global uniform float global_wind_scale;
global uniform float global_wind_strength;
global uniform float global_wind_fade;
global uniform float global_snow_start_time;
global uniform float global_snow_accumulation_speed;
global uniform float global_snow_melt_time;
global uniform float global_snow_melt_speed;
global uniform float global_snow_start_time = -1.0;
global uniform float global_snow_accumulation_speed = 0.005;
global uniform float global_snow_melt_time = -1.0;
global uniform float global_snow_melt_speed = 0.1;
global uniform float global_snow_amount = 0.0;
global uniform vec4 global_snow_color;
global uniform float global_rain_intensity;
@@ -30,6 +31,7 @@ uniform float light_steps : hint_range(1.0, 10.0) = 4.0;
uniform float random_mix : hint_range(0.0, 1.0) = 0.3;
uniform float cast_shadow_strength : hint_range(0.0, 1.0) = 0.6;
uniform float wetness_darkening : hint_range(0.0, 0.5) = 0.25;
uniform float snow_visibility : hint_range(0.0, 1.0) = 1.0;
varying vec3 v_final_color;
varying float v_shade_factor;
@@ -40,6 +42,21 @@ float hash(vec3 p) {
return fract((p.x + p.y) * p.z);
}
float get_snow_progress() {
float snow_progress = clamp(global_snow_amount, 0.0, 1.0);
if (global_snow_start_time >= 0.0) {
float timed_progress = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
snow_progress = max(snow_progress, timed_progress);
}
if (global_snow_melt_time >= 0.0) {
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
snow_progress = min(snow_progress, 1.0 - melt);
}
return snow_progress;
}
void vertex() {
vec3 instance_pos = MODEL_MATRIX[3].xyz;
@@ -88,17 +105,10 @@ void fragment() {
vec4 tex = texture(alpha_texture, shifted_uv);
// Snow accumulation
float snow_amount = 0.0;
if (global_snow_start_time >= 0.0) {
snow_amount = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
}
if (global_snow_melt_time >= 0.0) {
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
snow_amount *= (1.0 - melt);
}
float snow_amount = pow(get_snow_progress(), 0.55);
float top_mask = 1.0 - shifted_uv.y;
float snow_mask = smoothstep(1.0 - snow_amount, 1.2 - snow_amount, top_mask);
float snow_mask = smoothstep(1.0 - snow_amount * 1.35, 1.08 - snow_amount * 1.35, top_mask) * snow_visibility;
snow_mask *= step(0.01, snow_amount);
vec3 dark_snow = global_snow_color.rgb * (1.0 - shadow_intensity);

View File

@@ -1,6 +1,15 @@
shader_type spatial;
render_mode blend_mix, depth_draw_always;
global uniform float global_rain_intensity;
global uniform vec4 global_water_color = vec4(0.285, 0.534, 0.487, 1.0);
global uniform float global_snow_start_time = -1.0;
global uniform float global_snow_accumulation_speed = 0.005;
global uniform float global_snow_melt_time = -1.0;
global uniform float global_snow_melt_speed = 0.1;
global uniform float global_snow_amount = 0.0;
global uniform vec4 global_snow_color = vec4(0.92, 0.96, 1.0, 1.0);
//Water color
uniform vec4 deep_water_color : source_color = vec4(0.0, 0.1, 0.2, 1.0);
uniform vec4 shallow_water_color : source_color = vec4(0.0, 0.4, 0.7, 1.0);
@@ -26,6 +35,21 @@ uniform sampler2D depth_texture : hint_depth_texture, filter_linear_mipmap;
varying vec2 world_pos_xz;
varying vec2 local_uv;
float get_snow_progress() {
float snow_progress = clamp(global_snow_amount, 0.0, 1.0);
if (global_snow_start_time >= 0.0) {
float timed_progress = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
snow_progress = max(snow_progress, timed_progress);
}
if (global_snow_melt_time >= 0.0) {
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
snow_progress = min(snow_progress, 1.0 - melt);
}
return snow_progress;
}
void vertex() {
world_pos_xz = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xz;
local_uv = UV;
@@ -41,12 +65,15 @@ void fragment() {
float water_depth_gradient = exp(-depth_difference * beer_law_factor);
vec4 base_water = mix(deep_water_color, shallow_water_color, water_depth_gradient);
float rain_intensity = clamp(global_rain_intensity, 0.0, 1.0);
base_water.rgb = mix(base_water.rgb, global_water_color.rgb, 0.7);
vec2 pos = world_pos_xz * ripple_scale;
vec2 cell = floor(pos);
vec2 local_pos = fract(pos) - 0.5;
float random_val = fract(sin(dot(cell, vec2(12.9898, 78.233))) * 43758.5453);
float is_active_cell = step(1.0 - ripple_density, random_val);
float active_ripple_density = ripple_density * rain_intensity;
float is_active_cell = step(1.0 - active_ripple_density, random_val);
float ring = 0.0;
if (is_active_cell > 0.0) {
@@ -56,6 +83,7 @@ void fragment() {
- smoothstep(local_time, local_time + ripple_thickness, dist);
ring *= (1.0 - local_time);
}
ring *= rain_intensity;
vec2 ref_uv = SCREEN_UV;
@@ -75,8 +103,15 @@ void fragment() {
float is_sky = step(ref_depth_raw, 0.00001); // Protezione anti-cielo
reflection_mask *= (1.0 - is_sky);
vec3 final_rgb = mix(base_water.rgb, screen_ref, reflection_strength * reflection_mask);
float snow_progress = get_snow_progress();
float active_snowfall = step(0.0, global_snow_start_time) * (1.0 - step(0.0, global_snow_melt_time));
float reflection_snow_damping = max(smoothstep(0.0, 0.08, snow_progress), active_snowfall * 0.75);
float effective_reflection_strength = reflection_strength * (1.0 - reflection_snow_damping * 0.85);
vec3 final_rgb = mix(base_water.rgb, screen_ref, effective_reflection_strength * reflection_mask);
final_rgb = mix(final_rgb, ripple_color.rgb, ring * ripple_color.a);
float water_snow_amount = smoothstep(0.15, 1.0, snow_progress) * 0.18;
final_rgb = mix(final_rgb, global_snow_color.rgb, water_snow_amount);
ALBEDO = final_rgb;

View File

@@ -0,0 +1,241 @@
#define USE_CAUSTICS 1
#define USE_REFRACTION 1
#define USE_DISPLACEMENT 1
#define USE_STYLIZED_LIGHTING 0
#define USE_UNSHADED 0
shader_type spatial;
#if USE_UNSHADED
render_mode unshaded, depth_draw_never;
#else
render_mode depth_draw_never;
#endif
uniform sampler2D DEPTH_TEXTURE: hint_depth_texture;
group_uniforms Color;
uniform vec4 surface_color : source_color = vec4(0.2,1.0,0.8,1.0);
uniform vec4 depth_color : source_color = vec4(0.08,0.2,0.4,1.0);
uniform vec4 foam_color : source_color = vec4(1.0);
uniform float depth_size = 12.0;
group_uniforms Roughness;
uniform float surface_roughness : hint_range(0.0, 1.0, 0.01) = 0.05;
uniform float foam_roughness : hint_range(0.0, 1.0, 0.01) = 0.05;
#if USE_CAUSTICS
group_uniforms Caustics;
uniform sampler2D caustics_texture;
uniform float caustics_strength = 2.0;
uniform vec2 caustics_scale = vec2(0.5);
#endif
group_uniforms Wave;
uniform sampler2D wave_texture;
uniform float wave_softness : hint_range(0.0, 10.0, 0.1) = 3.0;
uniform vec2 wave_scale = vec2(0.2);
uniform vec2 wave_layer_scale = vec2(1.5);
uniform float wave_highlight : hint_range(0.0, 1.0, 0.05) = 0.5;
group_uniforms Wave.Motion;
uniform vec2 wave_velocity = vec2(0.02);
instance uniform vec2 river_flow_direction = vec2(0.0, 0.0);
group_uniforms Foam;
uniform sampler2D foam_texture;
uniform float edge_foam_depth_size = 1.0;
uniform float wave_foam_amount : hint_range(0.0, 1.0, 0.01) = 0.8;
uniform float foam_start : hint_range(0.0, 1.0, 0.05) = 0.15;
uniform float foam_end : hint_range(0.0, 1.0, 0.05) = 0.3;
uniform float foam_exponent = 2.0;
#if USE_REFRACTION
group_uniforms Refraction;
uniform float refraction_amount = 0.5;
uniform float refraction_exponent = 0.5;
#endif
#if USE_DISPLACEMENT
group_uniforms Displacement;
uniform float displacement_amount = 0.3;
#endif
#if USE_STYLIZED_LIGHTING && !USE_UNSHADED
group_uniforms Lighting;
uniform float diffuse_steps = 12.0;
uniform float diffuse_smoothness : hint_range(0.0, 1.0, 0.01) = 0.2;
uniform float specular_steps = 12.0;
uniform float specular_smoothness : hint_range(0.0, 1.0, 0.01) = 0.2;
#endif
uniform sampler2D screen_texture : hint_screen_texture;
varying vec3 world_pos;
varying vec2 world_wave_velocity;
#if USE_CAUSTICS
vec3 sample_caustics(vec2 uv){
vec2 caustics_uv = uv * caustics_scale;
return vec3(
texture(caustics_texture, caustics_uv).r,
texture(caustics_texture, caustics_uv+vec2(0.02,0.02)).r,
texture(caustics_texture, caustics_uv+vec2(0.03,0.01)).r
);
}
#endif
vec4 sample_world_dpos(vec2 screen_uv, mat4 inv_proj_mat, mat4 inv_view_mat){
vec4 clip_pos = vec4(screen_uv * 2.0 - 1.0, texture(DEPTH_TEXTURE, screen_uv).r, 1.0);
vec4 view_pos = inv_proj_mat * clip_pos;
view_pos /= view_pos.w;
vec4 world_dpos = inv_view_mat * view_pos;
return world_dpos;
}
vec4 sample_wave(sampler2D tex, vec2 uv, vec2 velocity, float lod){
vec2 base_uv = uv * wave_scale;
vec2 wave_uv1 = (base_uv * wave_layer_scale) + (TIME * -velocity);
float wave1 = textureLod(tex, wave_uv1, lod).r;
vec2 wave_uv2 = base_uv + (TIME * velocity);
vec4 wave2 = textureLod(tex, wave_uv2 - (wave1 * 0.1), lod);
return wave2;
}
vec3 sample_wave_normal(vec2 uv, vec2 velocity, float center_wave) {
vec2 normal_offset = vec2(0.25, 0.0);
float wave_x = sample_wave(wave_texture, uv + normal_offset.xy, velocity, wave_softness).r;
float wave_z = sample_wave(wave_texture, uv + normal_offset.yx, velocity, wave_softness).r;
vec2 slope = vec2(center_wave - wave_x, center_wave - wave_z) * 0.35;
return normalize(vec3(slope, 1.0)) * 0.5 + 0.5;
}
vec2 get_world_wave_velocity(mat4 model_matrix) {
float velocity_length = length(wave_velocity);
if (velocity_length <= 0.0001) {
return vec2(0.0);
}
float flow_direction_length = length(river_flow_direction);
if (flow_direction_length > 0.0001) {
return (river_flow_direction / flow_direction_length) * velocity_length;
}
vec3 world_direction_3d = (model_matrix * vec4(0.0, 1.0, 0.0, 0.0)).xyz;
vec2 world_direction = world_direction_3d.xz;
float world_direction_length = length(world_direction);
if (world_direction_length <= 0.0001) {
return wave_velocity;
}
return (world_direction / world_direction_length) * velocity_length;
}
void vertex(){
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
world_wave_velocity = get_world_wave_velocity(MODEL_MATRIX);
#if USE_DISPLACEMENT
float wave = sample_wave(wave_texture, world_pos.xz, world_wave_velocity, wave_softness).r;
VERTEX.y += wave*displacement_amount;
#endif
}
void fragment() {
float wave = sample_wave(wave_texture, world_pos.xz, world_wave_velocity, wave_softness).r;
wave = smoothstep(0.0,1.0,wave);
vec2 screen_uv = SCREEN_UV;
// Refraction
#if USE_REFRACTION
screen_uv += ((pow(wave, refraction_exponent)*2.0 - 0.5) * 0.01 * refraction_amount);
vec4 world_dpos = sample_world_dpos(screen_uv, INV_PROJECTION_MATRIX, INV_VIEW_MATRIX);
float pre_depth = pow(clamp((world_dpos.y - world_pos.y + depth_size)/depth_size, 0.0, 1.0), 4.0);
screen_uv = mix(screen_uv, SCREEN_UV, pre_depth);
if(world_dpos.y - world_pos.y > 0.0){
screen_uv = SCREEN_UV;
}
#else
vec4 world_dpos = sample_world_dpos(screen_uv, INV_PROJECTION_MATRIX, INV_VIEW_MATRIX);
#endif
world_dpos = sample_world_dpos(screen_uv, INV_PROJECTION_MATRIX, INV_VIEW_MATRIX);
vec2 surface_uv = world_dpos.xz * 0.2;
float depth = pow(clamp((world_dpos.y - world_pos.y + depth_size)/depth_size, 0.0, 1.0), 4.0);
// Caustics
#if USE_CAUSTICS
vec3 caustics1 = sample_caustics(surface_uv + (TIME * -world_wave_velocity));
vec3 caustics2 = sample_caustics((surface_uv + (caustics1.r*0.05)) + (TIME * (world_wave_velocity*0.5)));
vec3 caustics = caustics2 * (1.0 - depth);
#endif
// Edge Foam
float edge_foam_depth = clamp((world_dpos.y - world_pos.y + edge_foam_depth_size)/edge_foam_depth_size, 0.0, 1.0);
// Wave Foam
float wave_foam = wave;
float foam = max(edge_foam_depth, wave_foam * wave_foam_amount);
float foam_shape = 1.0 - texture(foam_texture, world_pos.xz* 0.5).r;
foam = clamp((foam - foam_start) / (foam_end - foam_start), 0.0, 1.0);
foam = clamp((foam - foam_shape) / (1.0 - foam_shape), 0.0, 1.0);
foam = pow(foam, foam_exponent);
vec3 flat_color = mix(depth_color, surface_color, depth).rgb;
vec4 screen = texture(screen_texture, screen_uv);
vec3 color = screen.rgb;
#if USE_CAUSTICS
color += vec3(pow(caustics * caustics_strength, vec3(2.0)));
#endif
color = mix(flat_color, color, 0.4 * depth);
color = mix(color, surface_color.rgb, wave * wave_highlight);
color = mix(color, foam_color.rgb, foam);
#if !USE_UNSHADED
vec3 wave_normal_map = sample_wave_normal(world_pos.xz, world_wave_velocity, wave);
NORMAL_MAP = wave_normal_map;
#endif
ROUGHNESS = mix(surface_roughness, foam_roughness, foam);
ALBEDO = color;
}
#if USE_STYLIZED_LIGHTING && !USE_UNSHADED
void light(){
float ndotl = dot(NORMAL, LIGHT) * ATTENUATION;
//ndotl = smoothstep(0.0,1.0-ROUGHNESS,ndotl);
float light = ndotl;
float light_mult = light * diffuse_steps;
float light_step_base = floor(light_mult);
float light_factor = light_mult - light_step_base;
light_factor = smoothstep(0.5 - diffuse_smoothness * 0.5, 0.5 + diffuse_smoothness * 0.5, light_factor);
light = (light_step_base + light_factor) / diffuse_steps;
DIFFUSE_LIGHT += (LIGHT_COLOR+ALBEDO) * light / PI;
float roughness = mix(0.01, 0.99, ROUGHNESS);
vec3 h = normalize(VIEW + LIGHT);
float ndoth = clamp(dot(NORMAL, h), 0.0, 1.0) * ATTENUATION;
float specular = clamp(pow(ndoth, 16.0/(roughness)), 0.1, 0.99);
specular = mix(pow(specular, 2.0-roughness),0.00,pow(roughness, 0.1));
float specular_mult = specular * specular_steps;
float specular_step_base = floor(specular_mult);
float specular_factor = specular_mult - specular_step_base;
specular_factor = smoothstep(0.5 - specular_smoothness * 0.5, 0.5 + specular_smoothness * 0.5, specular_factor);
specular = (specular_step_base + specular_factor) / specular_steps;
SPECULAR_LIGHT += (LIGHT_COLOR + ALBEDO) * specular;
}
#endif

View File

@@ -0,0 +1 @@
uid://cb12c2j8rfu6a

View File

@@ -14,6 +14,7 @@ var godray: PackedScene
var environment_dust: ColorRect
var blur: ColorRect
var environment_shadows: MeshInstance3D
var fog: Node3D
var sun: DirectionalLight3D
var environment: WorldEnvironment
@@ -33,12 +34,15 @@ var rain_tween: Tween
var rain_audio_tween: Tween
var puddle_tween: Tween
var puddle_amount: float = 0.0
var clouds_tween: Tween
var snow_tween: Tween
var snow_weather_tween: Tween
var snow_particles_tween: Tween
var is_snowing: bool = false
var is_snow_accumulated: bool = false
var actual_snow_amount: float = 0.0
var snow_weather_amount: float = 0.0
var is_storm: bool = false
var cold_tween: Tween
@@ -49,10 +53,13 @@ var current_wind_speed: float = 0.0
var current_wind_strength: float = 0.0
var current_wind_fade: float = 0.0
var max_wind_amount: int = 0
var random_weather_restore_tween: Tween
var random_event_remaining: float = 0.0
var random_event_label_seconds: int = -1
func _init(wind: GPUParticles3D, snow: GPUParticles3D,
fireflies: GPUParticles3D, rain: GPUParticles3D, ray: PackedScene,
dust: ColorRect, blurr: ColorRect, envshadows: MeshInstance3D,
dust: ColorRect, blurr: ColorRect, thefog: Node3D, envshadows: MeshInstance3D,
thesun: DirectionalLight3D, theenvironment: WorldEnvironment, environmentconfig: EnvironmentConfig,
thecamera: Camera3D, thecamerapivot: Node3D, thundersounds: Array[AudioStream], rainsounds: Array[AudioStream]) -> void:
particles_wind = wind
@@ -63,6 +70,7 @@ func _init(wind: GPUParticles3D, snow: GPUParticles3D,
sun = thesun
environment_dust = dust
blur = blurr
fog = thefog
environment_shadows = envshadows
environment = theenvironment
environment_config = environmentconfig
@@ -86,6 +94,7 @@ func _init(wind: GPUParticles3D, snow: GPUParticles3D,
toggle_dust(environment_config.enable_dust)
toggle_blur(environment_config.enable_blur)
toggle_fog(environment_config.enable_fog)
#set camera pivot if available
if camera_pivot:
@@ -98,13 +107,17 @@ func _ready() -> void:
UIEvents.toggle_snow.connect(toggle_snow)
UIEvents.toggle_wind.connect(toggle_wind)
UIEvents.wind_change.connect(change_wind_strength)
UIEvents.trigger_random_weather.connect(trigger_random_weather_event)
UIEvents.toggle_fireflies.connect(toggle_fireflies)
UIEvents.toggle_storm.connect(toggle_storm)
UIEvents.toggle_dust.connect(toggle_dust)
UIEvents.toggle_blur.connect(toggle_blur)
UIEvents.toggle_fog.connect(toggle_fog)
UIEvents.toggle_shadows.connect(toggle_shadows)
_emit_weather_event_label()
func _process(delta: float) -> void:
_update_random_event_label(delta)
_follow_camera()
@@ -122,6 +135,7 @@ func _process(delta: float) -> void:
var base_grad_top: Color
var base_grad_bot: Color
var base_grad_intensity: float
var base_water_color: Color
var base_rotation_sun: Vector3
if is_raining:
@@ -149,6 +163,7 @@ func _process(delta: float) -> void:
base_grad_top = environment_config.grad_top_morning.lerp(environment_config.grad_top_afternoon, t)
base_grad_bot = environment_config.grad_top_morning.lerp(environment_config.grad_bot_afternoon, t)
base_grad_intensity = lerp(environment_config.grad_intensity_morning, environment_config.grad_intensity_afternoon, t)
base_water_color = environment_config.water_color_morning.lerp(environment_config.water_color_afternoon, t)
base_rotation_sun = environment_config.sun_rotation_morning.lerp(environment_config.sun_rotation_afternoon, t)
else:
var t = day_time - 2.0
@@ -162,6 +177,7 @@ func _process(delta: float) -> void:
base_grad_top = environment_config.grad_top_afternoon.lerp(environment_config.grad_top_night, t)
base_grad_bot = environment_config.grad_bot_afternoon.lerp(environment_config.grad_bot_night, t)
base_grad_intensity = lerp(environment_config.grad_intensity_afternoon, environment_config.grad_intensity_night, t)
base_water_color = environment_config.water_color_afternoon.lerp(environment_config.water_color_night, t)
base_rotation_sun = environment_config.sun_rotation_afternoon.lerp(environment_config.sun_rotation_night, t)
var weather_color = environment_config.rain_mode_color
@@ -173,11 +189,12 @@ func _process(delta: float) -> void:
var final_sky_horizon = base_sky_horizon.lerp(base_sky_horizon * weather_color, clamp(rain_intensity, 0.0, 1.0))
var final_fog_color = base_fog_color.lerp(base_fog_color * weather_color, clamp(rain_intensity, 0.0, 1.0))
var final_fog_density = lerp(base_fog_density, base_fog_density * 4.0, clamp(rain_intensity, 0.0, 1.0))
var final_water_color = base_water_color.darkened(clamp(environment_config.water_darkening_rain, 0.0, 1.0) * clamp(rain_intensity, 0.0, 1.0))
final_tint = final_tint.lerp(final_tint * environment_config.snow_mode_color, actual_snow_amount)
final_sky_top = final_sky_top.lerp(final_sky_top * environment_config.snow_mode_color, actual_snow_amount)
final_sky_horizon = final_sky_horizon.lerp(final_sky_horizon * environment_config.snow_mode_color, actual_snow_amount)
final_fog_color = final_fog_color.lerp(final_fog_color * environment_config.snow_mode_color, actual_snow_amount)
final_tint = final_tint.lerp(final_tint * environment_config.snow_mode_color, snow_weather_amount)
final_sky_top = final_sky_top.lerp(final_sky_top * environment_config.snow_mode_color, snow_weather_amount)
final_sky_horizon = final_sky_horizon.lerp(final_sky_horizon * environment_config.snow_mode_color, snow_weather_amount)
final_fog_color = final_fog_color.lerp(final_fog_color * environment_config.snow_mode_color, snow_weather_amount)
#Shader parameters for global trunk_shader
var final_grad_top = base_grad_top.lerp(base_grad_top * weather_color, clamp(rain_intensity, 0.0, 1.0))
@@ -186,12 +203,13 @@ func _process(delta: float) -> void:
RenderingServer.global_shader_parameter_set("global_gradient_color_top", final_grad_top)
RenderingServer.global_shader_parameter_set("global_gradient_color_bot", final_grad_bot)
RenderingServer.global_shader_parameter_set("global_gradient_intensity", base_grad_intensity)
RenderingServer.global_shader_parameter_set("global_water_color", final_water_color)
var night_val = clamp(day_time - 2.0, 0.0, 1.0)
#Snow exposure compensation
var snow_light_attenuation = lerp(1.0, 0.55, actual_snow_amount * (1.0 - night_val))
var snow_glow_attenuation = lerp(1.0, 0.5, actual_snow_amount)
var snow_light_attenuation = lerp(1.0, 0.55, snow_weather_amount * (1.0 - night_val))
var snow_glow_attenuation = lerp(1.0, 0.5, snow_weather_amount)
var final_bloom = lerp(base_bloom, base_bloom * 0.5, rain_intensity) * snow_glow_attenuation
# We calculate the final exposure by applying snow damping directly to the camera exposure
@@ -232,16 +250,37 @@ func _process(delta: float) -> void:
sky_mat.set_shader_parameter("sun_color", final_tint)
sky_mat.set_shader_parameter("night_intensity", night_val)
var cloud_density_amount: float = maxf(clamp(rain_intensity, 0.0, 1.0), clamp(snow_weather_amount, 0.0, 1.0))
var current_cloud_density: float = lerp(environment_config.base_cloud_density, environment_config.rain_cloud_density, cloud_density_amount)
if environment_config.material_clouds:
var current_density = lerp(0.4, 1.0, rain_intensity)
environment_config.material_clouds.set_shader_parameter("cloud_density", current_density)
var current_sharpness = lerp(0.14, 0.0, rain_intensity)
environment_config.material_clouds.set_shader_parameter("cloud_density", current_cloud_density)
var current_sharpness = lerp(0.14, 0.0, cloud_density_amount)
environment_config.material_clouds.set_shader_parameter("center_sharpness", current_sharpness)
var env_shadow_mat = environment_shadows.get_surface_override_material(0) if environment_shadows else null
if env_shadow_mat:
env_shadow_mat.set_shader_parameter("cloud_density", current_cloud_density)
if environment_config.material_fog:
environment_config.material_fog.set_shader_parameter("fog_color", final_fog_color)
environment_config.material_fog.set_shader_parameter("fog_density", clamp(final_fog_density * 25.0, 0.0, 1.0))
environment_config.material_fog.set_shader_parameter("night_intensity", night_val)
environment_config.material_fog.set_shader_parameter("sun_color", final_tint)
if fog:
for child in fog.get_children():
var fog_mesh := child as MeshInstance3D
if fog_mesh == null:
continue
var fog_material := fog_mesh.get_surface_override_material(0) as ShaderMaterial
if fog_material == null:
continue
fog_material.set_shader_parameter("fog_color", final_fog_color)
fog_material.set_shader_parameter("fog_density", clamp(final_fog_density * 25.0, 0.0, 1.0))
fog_material.set_shader_parameter("night_intensity", night_val)
fog_material.set_shader_parameter("sun_color", final_tint)
func create_sound_players():
rain_audio_player = AudioStreamPlayer.new()
rain_audio_player.name = "RainAudioPlayer"
@@ -265,6 +304,23 @@ func _get_weather_anchor_position() -> Variant:
return camera.global_position
return null
func _get_weather_anchor_basis() -> Variant:
var source_basis: Basis
if camera_pivot and is_instance_valid(camera_pivot):
source_basis = camera_pivot.global_basis
elif camera and is_instance_valid(camera):
source_basis = camera.global_basis
else:
return null
var forward := -source_basis.z
forward.y = 0.0
if forward.length_squared() <= 0.0001:
return Basis.IDENTITY
forward = forward.normalized()
var right := forward.cross(Vector3.UP).normalized()
return Basis(right, Vector3.UP, -forward)
func _follow_camera() -> void:
var cam_pos = _get_weather_anchor_position()
if cam_pos == null:
@@ -277,6 +333,12 @@ func _follow_camera() -> void:
particles_wind.global_position = Vector3(cam_pos.x, cam_pos.y, cam_pos.z)
if particles_fireflies:
particles_fireflies.global_position = Vector3(cam_pos.x, particles_fireflies.position.y, cam_pos.z)
if fog:
fog.global_position = Vector3(cam_pos.x, fog.global_position.y, cam_pos.z)
#used to follow camera pivot or camera
var anchor_basis = _get_weather_anchor_basis()
if anchor_basis != null:
fog.global_basis = anchor_basis
#endregion
#region Fireflies
@@ -307,6 +369,7 @@ func toggle_wind(value: bool):
particles_wind.emitting = is_windy
_apply_wind_state()
_emit_weather_event_label()
#disable wind and set default values and materials
func init_wind():
@@ -410,6 +473,67 @@ func _update_wind_amount_from_strength(value: float) -> void:
environment_config.wind_amount = roundi(max_wind_amount * amount_ratio)
func trigger_random_weather_event(duration: float = 0.0) -> void:
if random_weather_restore_tween and random_weather_restore_tween.is_valid():
random_weather_restore_tween.kill()
random_event_remaining = 0.0
random_event_label_seconds = -1
var previous_rain: bool = is_raining
var previous_snow: bool = is_snowing
var previous_wind: bool = is_windy
var previous_storm: bool = is_storm
var random_event_index: int = randi_range(0, 3)
match random_event_index:
0:
_apply_weather_event_state(false, true, false, false)
1:
_apply_weather_event_state(true, false, false, false)
2:
_apply_weather_event_state(false, false, true, false)
_:
_apply_weather_event_state(true, false, false, true)
if duration > 0.0:
random_event_remaining = duration
_emit_weather_event_label()
random_weather_restore_tween = create_tween()
random_weather_restore_tween.tween_interval(duration)
random_weather_restore_tween.tween_callback(func():
random_event_remaining = 0.0
random_event_label_seconds = -1
_apply_weather_event_state(previous_rain, previous_snow, previous_wind, previous_storm)
)
func _update_random_event_label(delta: float) -> void:
if random_event_remaining <= 0.0:
return
random_event_remaining = maxf(random_event_remaining - delta, 0.0)
var display_seconds: int = ceili(random_event_remaining)
if display_seconds != random_event_label_seconds:
random_event_label_seconds = display_seconds
_emit_weather_event_label()
func _apply_weather_event_state(rain_enabled: bool, snow_enabled: bool, wind_enabled: bool, storm_enabled: bool = false) -> void:
if is_storm and not storm_enabled:
toggle_storm(false)
if is_raining and not rain_enabled:
toggle_rain(false)
if is_snowing and not snow_enabled:
toggle_snow(false)
if is_windy and not wind_enabled:
toggle_wind(false)
if not is_raining and rain_enabled:
toggle_rain(true)
if not is_snowing and snow_enabled:
toggle_snow(true)
if not is_windy and wind_enabled:
toggle_wind(true)
if not is_storm and storm_enabled:
toggle_storm(true)
func _apply_cloud_config() -> void:
var dir = environment_config.wind_direction
var cloud_scale = environment_config.cloud_scale
@@ -441,6 +565,7 @@ func _apply_cloud_config() -> void:
func toggle_storm(value: bool):
is_storm = value
_emit_weather_event_label()
#set lightning
func init_lightning():
@@ -554,6 +679,7 @@ func toggle_rain(value: bool):
is_raining = value
if not particles_rain:
_emit_weather_event_label()
return
if is_raining and is_snowing:
@@ -587,6 +713,7 @@ func toggle_rain(value: bool):
trigger_after_rain())
puddle_tween = create_tween()
puddle_tween.tween_method(set_puddle_amount, puddle_amount, 0.0, environment_config.puddle_dry_time)
_emit_weather_event_label()
func trigger_morning() -> void:
spawn_single_godray()
@@ -683,12 +810,47 @@ func toggle_snow(value: bool):
var target_snow_amount: float = 1.0 if is_snowing else 0.0
if snow_tween and snow_tween.is_valid():
snow_tween.kill()
if snow_weather_tween and snow_weather_tween.is_valid():
snow_weather_tween.kill()
var snow_amount_transition_duration: float = _get_snow_amount_transition_duration(is_snowing)
snow_tween = create_tween()
snow_tween.tween_method(init_snow_amount, actual_snow_amount, target_snow_amount, environment_config.snow_transaction_time)
snow_tween.tween_method(
init_snow_amount,
actual_snow_amount,
target_snow_amount,
snow_amount_transition_duration
)
snow_weather_tween = create_tween()
snow_weather_tween.tween_method(
init_snow_weather_amount,
snow_weather_amount,
target_snow_amount,
environment_config.snow_fade_time
)
_emit_weather_event_label()
func _emit_weather_event_label() -> void:
var weather_label := "Weather: Clear"
if is_storm:
weather_label = "Weather: Storm"
else:
var active_events: Array[String] = []
if is_snowing:
active_events.append("Snow")
if is_raining:
active_events.append("Rain")
if is_windy:
active_events.append("Wind")
if not active_events.is_empty():
weather_label = "Weather: %s" % " + ".join(active_events)
if random_event_remaining > 0.0:
weather_label += " (%s'')" % ceili(random_event_remaining)
UIEvents.weather_event_changed.emit(weather_label)
#disable snow and set default values and shader
func init_snow(value: float = 0.0):
actual_snow_amount = value
snow_weather_amount = value
RenderingServer.global_shader_parameter_set("global_snow_amount", value)
if particles_snow:
@@ -699,6 +861,13 @@ func init_snow(value: float = 0.0):
RenderingServer.global_shader_parameter_set("global_snow_color", environment_config.snow_color)
RenderingServer.global_shader_parameter_set("global_snow_accumulation_speed", environment_config.snow_accumulation_speed)
RenderingServer.global_shader_parameter_set("global_snow_melt_speed", environment_config.snow_melt_speed)
RenderingServer.global_shader_parameter_set("global_snow_max_accumulation", environment_config.snow_max_accumulation)
RenderingServer.global_shader_parameter_set("global_snow_cap_enabled", environment_config.show_snow_accumulation_volume)
RenderingServer.global_shader_parameter_set("global_snow_cap_height", environment_config.snow_cap_height)
RenderingServer.global_shader_parameter_set("global_snow_cap_flatness_start", environment_config.snow_cap_flatness_start)
RenderingServer.global_shader_parameter_set("global_snow_cap_flatness_end", environment_config.snow_cap_flatness_end)
RenderingServer.global_shader_parameter_set("global_snow_cap_noise_scale", environment_config.snow_cap_noise_scale)
RenderingServer.global_shader_parameter_set("global_snow_cap_noise_strength", environment_config.snow_cap_noise_strength)
RenderingServer.global_shader_parameter_set("global_snow_start_time", -1.0)
RenderingServer.global_shader_parameter_set("global_snow_melt_time", -1.0)
@@ -706,6 +875,9 @@ func init_snow_amount(value: float):
actual_snow_amount = value
RenderingServer.global_shader_parameter_set("global_snow_amount", value)
func init_snow_weather_amount(value: float):
snow_weather_amount = value
func start_snow_accumulation() -> void:
RenderingServer.global_shader_parameter_set("global_snow_melt_time", -1.0)
RenderingServer.global_shader_parameter_set("global_snow_start_time", Time.get_ticks_msec() / 1000.0)
@@ -714,6 +886,18 @@ func start_snow_melt() -> void:
RenderingServer.global_shader_parameter_set("global_snow_melt_time", Time.get_ticks_msec() / 1000.0)
RenderingServer.global_shader_parameter_set("global_snow_melt_speed", environment_config.snow_melt_speed)
func _get_snow_amount_transition_duration(is_accumulating: bool) -> float:
if environment_config == null:
return 0.0
var speed: float = environment_config.snow_melt_speed
if is_accumulating:
speed = environment_config.snow_accumulation_speed
if speed <= 0.0:
return environment_config.snow_transaction_time
return 1.0 / speed
#endregion
#region Post-Process
@@ -728,6 +912,10 @@ func toggle_blur(value: bool) -> void:
ApplyPostProcessBlurConfig()
func toggle_fog(value: bool) -> void:
if fog:
fog.visible = value
func toggle_shadows(value: bool) -> void:
if environment_shadows:
environment_shadows.visible = value

View File

@@ -3,11 +3,19 @@ render_mode blend_mix, depth_draw_never;
//Snow globals
global uniform float global_snow_start_time = -1.0;
global uniform float global_snow_accumulation_speed = 0.1;
global uniform float global_snow_accumulation_speed = 0.005;
global uniform float global_snow_melt_time = -1.0;
global uniform float global_snow_melt_speed = 0.1;
global uniform float global_snow_amount = 0.0;
global uniform float global_snow_threshold = 0.5;
global uniform vec4 global_snow_color = vec4(0.92, 0.96, 1.0, 1.0);
global uniform float global_snow_max_accumulation = 0.72;
global uniform bool global_snow_cap_enabled = true;
global uniform float global_snow_cap_height = 0.06;
global uniform float global_snow_cap_flatness_start = 0.72;
global uniform float global_snow_cap_flatness_end = 0.96;
global uniform float global_snow_cap_noise_scale = 0.22;
global uniform float global_snow_cap_noise_strength = 0.18;
uniform float snow_noise_scale : hint_range(0.01, 1.0) = 0.15;
uniform float snow_edge_softness : hint_range(0.01, 0.5) = 0.2;
uniform float snow_roughness_variation : hint_range(0.0, 0.3) = 0.15;
@@ -29,6 +37,55 @@ uniform float puddle_threshold : hint_range(0.0, 0.8) = 0.45;
uniform sampler2D noise_texture : filter_linear_mipmap, repeat_enable;
varying float v_snow_amount;
varying float v_snow_cap_mask;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}
float value_noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
float fbm(vec2 p) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for (int i = 0; i < 3; i++) {
value += value_noise(p * frequency) * amplitude;
frequency *= 2.0;
amplitude *= 0.5;
}
return clamp(value, 0.0, 1.0);
}
float get_snow_progress() {
float snow_progress = clamp(global_snow_amount, 0.0, 1.0);
if (global_snow_start_time >= 0.0) {
float timed_progress = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
snow_progress = max(snow_progress, timed_progress);
}
if (global_snow_melt_time >= 0.0) {
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
snow_progress = min(snow_progress, 1.0 - melt);
}
return snow_progress;
}
float ripple_ring(vec2 uv, float time_offset) {
float d = length(uv);
float ring = sin(d * 30.0 - TIME * ripple_speed + time_offset) * 0.5 + 0.5;
@@ -36,6 +93,27 @@ float ripple_ring(vec2 uv, float time_offset) {
return ring * fade;
}
void vertex() {
float snow_progress = get_snow_progress();
float snow_accumulation = snow_progress * global_snow_max_accumulation;
vec3 world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
vec3 world_normal = normalize((MODEL_MATRIX * vec4(NORMAL, 0.0)).xyz);
float flat_surface = smoothstep(
max(global_snow_threshold, global_snow_cap_flatness_start),
global_snow_cap_flatness_end,
world_normal.y
);
float accumulation_growth = smoothstep(0.08, 0.65, snow_progress);
float cap_noise = fbm(world_pos.xz * global_snow_cap_noise_scale + vec2(11.3, 4.7));
float cap_variation = mix(1.0 - global_snow_cap_noise_strength, 1.0 + global_snow_cap_noise_strength, cap_noise);
v_snow_amount = snow_progress;
v_snow_cap_mask = flat_surface * accumulation_growth * cap_variation * (global_snow_cap_enabled ? 1.0 : 0.0);
VERTEX += NORMAL * (global_snow_cap_height * snow_accumulation * v_snow_cap_mask);
}
void fragment() {
vec3 world_pos = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
vec3 world_normal = normalize((INV_VIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
@@ -43,25 +121,24 @@ void fragment() {
float noise_val = texture(noise_texture, world_pos.xz * snow_noise_scale).r;
//Snow
float snow_amount = 0.0;
if (global_snow_start_time >= 0.0) {
snow_amount = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
}
if (global_snow_melt_time >= 0.0) {
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
snow_amount *= (1.0 - melt);
}
float snow_progress = v_snow_amount;
float snow_accumulation = snow_progress * global_snow_max_accumulation;
float snow_edge = smoothstep(
global_snow_threshold - snow_edge_softness,
global_snow_threshold + snow_edge_softness,
facing_up + (noise_val - 0.5) * 0.4
facing_up
);
float snow_coverage = smoothstep(0.0, 0.6, noise_val + snow_amount - 0.4);
float snow_factor = snow_edge * snow_coverage * snow_amount;
float flat_accumulation = smoothstep(0.0, 0.35, v_snow_cap_mask) * snow_accumulation;
float snow_variation = mix(0.75, 1.15, noise_val);
float snow_coverage = clamp(snow_progress * snow_variation + flat_accumulation * 0.25, 0.0, 1.0);
float snow_factor = max(snow_edge * snow_coverage, flat_accumulation);
float snow_opacity = clamp(snow_factor, 0.0, 1.0);
snow_opacity = max(snow_opacity, flat_accumulation * 0.9);
float shade = mix(-snow_color_variation, snow_color_variation, noise_val);
vec3 snow_albedo = clamp(global_snow_color.rgb + vec3(shade), 0.0, 1.0);
snow_albedo = mix(snow_albedo, vec3(1.0), 0.18 + snow_opacity * 0.12);
float snow_rough = mix(0.15 - snow_roughness_variation, 0.15 + snow_roughness_variation, noise_val);
//Rain
@@ -173,7 +250,7 @@ void fragment() {
ALBEDO = snow_albedo;
ROUGHNESS = snow_rough;
METALLIC = 0.0;
ALPHA = snow_factor;
ALPHA = snow_opacity;
} else {
ALBEDO = rain_albedo;
ROUGHNESS = rain_rough;

View File

@@ -5,3 +5,17 @@
[resource]
render_priority = 0
shader = ExtResource("1_weather")
shader_parameter/snow_noise_scale = 0.15
shader_parameter/snow_edge_softness = 0.2
shader_parameter/snow_roughness_variation = 0.15
shader_parameter/snow_color_variation = 0.05
shader_parameter/ripple_scale = 1.5
shader_parameter/ripple_speed = 2.0
shader_parameter/ripple_layers = 3.0
shader_parameter/streak_scale = 2.0
shader_parameter/streak_speed = 0.8
shader_parameter/wetness_darkening = 0.25
shader_parameter/wet_roughness = 0.05
shader_parameter/rain_normal_strength = 0.4
shader_parameter/puddle_noise_scale = 0.08
shader_parameter/puddle_threshold = 0.45

View File

@@ -2,21 +2,19 @@ shader_type spatial;
render_mode blend_mix, depth_draw_never, cull_disabled;
// Snow globals
global uniform float global_snow_start_time;
global uniform float global_snow_accumulation_speed;
global uniform float global_snow_melt_time;
global uniform float global_snow_melt_speed;
global uniform float global_snow_start_time = -1.0;
global uniform float global_snow_accumulation_speed = 0.005;
global uniform float global_snow_melt_time = -1.0;
global uniform float global_snow_melt_speed = 0.1;
global uniform float global_snow_amount = 0.0;
global uniform vec4 global_snow_color;
// Rain globals
global uniform float global_rain_intensity;
global uniform float global_rain_puddle_amount;
// Snow parameters
uniform float snow_edge_softness : hint_range(0.01, 0.5) = 0.15;
uniform float snow_color_variation : hint_range(0.0, 0.15) = 0.05;
// Rain wetness parameters
global uniform float global_rain_intensity;
global uniform float global_rain_puddle_amount;
uniform float ripple_scale : hint_range(0.1, 5.0) = 1.5;
uniform float ripple_speed : hint_range(0.5, 5.0) = 2.0;
uniform float ripple_layers : hint_range(1.0, 4.0) = 3.0;
@@ -37,6 +35,21 @@ float ripple_ring(vec2 uv, float time_offset) {
return ring * fade;
}
float get_snow_progress() {
float snow_progress = clamp(global_snow_amount, 0.0, 1.0);
if (global_snow_start_time >= 0.0) {
float timed_progress = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
snow_progress = max(snow_progress, timed_progress);
}
if (global_snow_melt_time >= 0.0) {
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
snow_progress = min(snow_progress, 1.0 - melt);
}
return snow_progress;
}
void fragment() {
vec3 world_pos = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
@@ -44,18 +57,10 @@ void fragment() {
float facing_up = 1.0;
// Snow accumulation
float snow_amount = 0.0;
if (global_snow_start_time >= 0.0) {
snow_amount = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
}
if (global_snow_melt_time >= 0.0) {
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
snow_amount *= (1.0 - melt);
}
float snow_amount = get_snow_progress();
// UV-based snow mask: accumulates from the top of the quad
float top_mask = 1.0 - UV.y;
float snow_mask = smoothstep(1.0 - snow_amount, 1.0 - snow_amount + snow_edge_softness, top_mask);
// Plain surfaces fade in uniformly to avoid patchy strip-like accumulation.
float snow_mask = smoothstep(0.0, 1.0, snow_amount);
snow_mask *= step(0.01, snow_amount);
// Snow color with slight variation

View File

@@ -1,7 +1,21 @@
[gd_resource type="ShaderMaterial" format=3]
[gd_resource type="ShaderMaterial" format=3 uid="uid://b34bnqbrtxktw"]
[ext_resource type="Shader" path="res://core/daynight/weather_plain_shader.gdshader" id="1_weather_plain"]
[ext_resource type="Shader" uid="uid://do8puw7u8dvry" path="res://core/daynight/weather_plain_shader.gdshader" id="1_weather_plain"]
[ext_resource type="Texture2D" uid="uid://bpswbjh4jj1ou" path="res://core/daynight/noise.tres" id="2_noise"]
[resource]
render_priority = 0
shader = ExtResource("1_weather_plain")
shader_parameter/snow_edge_softness = 0.15
shader_parameter/snow_color_variation = 0.05
shader_parameter/ripple_scale = 1.5
shader_parameter/ripple_speed = 2.0
shader_parameter/ripple_layers = 3.0
shader_parameter/streak_scale = 2.0
shader_parameter/streak_speed = 0.8
shader_parameter/wetness_darkening = 0.25
shader_parameter/wet_roughness = 0.05
shader_parameter/rain_normal_strength = 0.4
shader_parameter/puddle_noise_scale = 0.08
shader_parameter/puddle_threshold = 0.45
shader_parameter/noise_texture = ExtResource("2_noise")

View File

@@ -1,31 +1,26 @@
shader_type spatial;
render_mode unshaded;
global uniform float global_wind_speed = 1.0; //0.0 = calm, 1.0 = normal, 2.0+ = strong
global uniform vec2 global_wind_direction = vec2(1.0, 0.0);
uniform float wave_frequency = 1.0;
uniform float base_amplitude = 0.5;
uniform float secondary_ratio = 0.6;
uniform float time_scale_base = 3.0;
// --- PARAMETRI ---
uniform float time_scale = 3.0;
uniform float wave_amplitude = 0.5; // Quanto curva a destra/sinistra
uniform float wave_frequency = 1.0; // Ogni quanto fa la curva
void vertex() {
// Creiamo un offset unico per ogni particella in modo che non si muovano tutte uguali
float random_seed_offset = INSTANCE_CUSTOM.x * 100.0;
float scaled_time = (TIME + random_seed_offset) * (time_scale_base * global_wind_speed);
float amplitude = base_amplitude * global_wind_speed;
float scaled_time = (TIME + random_seed_offset) * time_scale;
vec2 wind_dir = normalize(global_wind_direction);
vec2 wind_perp = vec2(-wind_dir.y, wind_dir.x); //90° perpendicular
// Applichiamo SOLO la curva morbida all'asse X (o Z, a seconda di come orienti la scena)
// Usiamo VERTEX.y per far sì che la curva si propaghi lungo tutta la lunghezza della scia
VERTEX.x += sin(VERTEX.y * wave_frequency + scaled_time) * wave_amplitude;
float primary_sway = sin(VERTEX.y * wave_frequency + scaled_time) * amplitude;
float secondary_sway = sin(VERTEX.y * wave_frequency * 1.3 + scaled_time * 0.7 + 1.57)
* (amplitude * secondary_ratio);
VERTEX.x += primary_sway * wind_dir.x + secondary_sway * wind_perp.x;
VERTEX.z += primary_sway * wind_dir.y + secondary_sway * wind_perp.y;
// Niente più "VERTEX.y += t;", quindi niente più teletrasporti!
}
void fragment() {
// Il colore e l'alpha (per lo spawn dolce e la morte lenta)
// vengono comandati dalla Color Ramp del GPUParticles3D!
ALBEDO = COLOR.rgb;
ALPHA = COLOR.a;
}

View File

@@ -81,8 +81,9 @@ extends Resource
#Post-process effect toggles (initial values)
@export_group("Post Process")
@export var enable_dust: bool = false #Floating dust particles overlay
@export var enable_blur: bool = false #Screen blur effect
@export var enable_blur: bool = true #Screen blur effect
@export var enable_environment_shadows: bool = true #Cloud shadow projection on terrain
@export var enable_fog: bool = false #Enable fog around the train
@export var blur_amount: float = 0.6
@export_group("Dust")
@@ -97,19 +98,21 @@ extends Resource
@export var material_fog: ShaderMaterial #Fog overlay shader material
@export var material_drops: StandardMaterial3D #Rain drops material (albedo tinted by sky)
@export var material_clouds: ShaderMaterial #Cloud layer shader material
@export var base_cloud_density: float = 0.4 #default cloud density
#Rain weather settings
@export_group("Rain")
@export var rain_mode_color: Color = Color(0.5, 0.6, 0.7, 1.0) #Sky/light darkening color during rain
@export var rain_mode_color: Color = Color(0.85, 0.89, 0.93, 1.0) #Sky/light darkening color during rain
@export var rain_fade_time: float = 5.0 #Seconds for rain particles to fade in/out
@export var rain_audio_volume_db: float = -10.0 #Target rain loop volume in decibels
@export var puddle_form_time: float = 15.0 #Seconds for puddles to fully form
@export var puddle_dry_time: float = 20.0 #Seconds for puddles to fully dry after rain stops
@export var rain_cloud_density: float = 1.0 #the cloud density when is rainy
#Storm settings (applied on top of rain when storm is active)
@export_group("Storm")
@export var storm_rain_intensity_multiplier: float = 1.5 #Rain intensity target during storm (1.0 = normal rain)
@export var storm_mode_color: Color = Color(0.476, 0.531, 0.639, 1.0) #Additional sky darkening color during storm
@export var storm_mode_color: Color = Color(0.78, 0.83, 0.89, 1.0) #Additional sky darkening color during storm
#Lightning and thunder configuration
@export_group("Lightning and Thunders")
@@ -133,15 +136,27 @@ extends Resource
@export var godray_spawn_height: float = 5.0 #Y world position where god rays spawn
@export var godray_scale: Vector3 = Vector3(2.0, 6.0, 2.0) #Scale of the god ray mesh
#Train start settings
@export_group("Train Start")
@export var train_start_from_random_position: bool = true #When enabled the train starts from a random offset on the rail curve instead of a stop
@export_range(0, 64, 1, "or_greater") var train_start_stop_index: int = 1 #Stop index used for the train start when random start is disabled
#Snow settings
@export_group("Snow")
@export var snow_amount: float = 0.0 #Initial snow coverage amount (0.0 = none, 1.0 = full)
@export var snow_transaction_time: float = 10.0 #Seconds for snow shader to fully transition in/out
@export var snow_fade_time: float = 5.0 #Seconds for snow particles to fade in/out
@export var snow_threshold: float = 0.4 #Normal Y threshold for snow accumulation on surfaces
@export var snow_accumulation_speed: float = 0.02 #Accumulation speed: 0.1 -> 10s, 0.05 -> 20s, 0.02 -> 50s, 0.2 -> 5s
@export var snow_accumulation_speed: float = 0.005 #Accumulation speed: 0.005 -> ~200s, 0.01 -> 100s, 0.02 -> 50s
@export var snow_melt_speed: float = 0.05 #Speed at which accumulated snow melts away
@export var show_snow_accumulation_volume: bool = true #Enables vertex snow buildup; when false snow only changes surface color
@export var snow_max_accumulation: float = 0.25 #Maximum accumulated snow factor applied to coverage and thickness
@export var snow_color: Color = Color(0.9, 0.95, 1.0) #Albedo color of snow on surfaces
@export var snow_cap_height: float = 0.03 #Maximum snow thickness extruded on flat surfaces
@export var snow_cap_flatness_start: float = 0.85 #Surface normal Y where cap buildup starts
@export var snow_cap_flatness_end: float = 0.96 #Surface normal Y where cap buildup reaches full effect
@export var snow_cap_noise_scale: float = 0.22 #Noise scale used to vary the snow cap silhouette
@export var snow_cap_noise_strength: float = 0.12 #How much the cap noise changes the accumulated thickness
@export var snow_mode_color: Color = Color(0.556, 0.62, 0.683, 1.0) #Sky/light darkening color during snow
#Wind settings
@@ -152,7 +167,7 @@ extends Resource
@export var wind_direction: Vector2 = Vector2(0.5, 0.4) #Global wind direction (XZ)
@export var wind_speed: float = 0.2 #Global wind animation speed
@export var wind_scale: float = 0.025 #Global wind noise scale
@export var wind_strength: float = 0.6 #Global wind displacement strength
@export var wind_strength: float = 0.3 #Global wind displacement strength
@export var cloud_scale: float = 0.5 #Cloud noise scale in sky shader
@export var cloud_speed: float = 0.05 #Cloud movement speed in sky shader
@export_range(0.01, 1.0) var cloud_scale_envshadows_rate: float = 0.01 #Cloud scale multiplier for environment shadows
@@ -164,3 +179,14 @@ extends Resource
@export var fireflies_amount: int = 60 #Number of firefly particles
@export var fireflies_spawn_ray: float = 20.0 #Horizontal spawn radius
@export var fireflies_spawn_height: float = 3.0 #Vertical spawn range
#Water settings
@export_group("Water")
@export var water_color_morning: Color = Color(0.285, 0.534, 0.487, 1.0) #Tint for morning
@export var water_color_afternoon: Color = Color(0.889, 0.733, 0.205, 1.0) #Tint for afternoon (sunset)
@export var water_color_night: Color = Color(0.728, 0.54, 0.986, 1.0); #Tint for night
@export var water_darkening_rain: float = 0.7 #Percentage of darkening when is rainy
#Random events
@export_group("Random Events")
@export var random_event_duration: float = 30.0 #Duration time for random event

View File

@@ -0,0 +1,32 @@
extends Node
const SAVE_PATH: String = "user://savegame.json"
var save_data: SaveGameData = SaveGameData.new()
var is_loaded: bool = false
func _ready() -> void:
load_game()
func save_game() -> void:
var save_file = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
var json_string = JSON.stringify(save_data.to_dictionary())
save_file.store_line(json_string)
func load_game() -> void:
if not FileAccess.file_exists(SAVE_PATH):
save_data = SaveGameData.new()
is_loaded = true
return
var file = FileAccess.open(SAVE_PATH, FileAccess.READ)
var json_string = file.get_as_text()
var json = JSON.new()
var parse_result = json.parse(json_string)
save_data = SaveGameData.new()
if parse_result == OK and json.data is Dictionary:
save_data = SaveGameData.from_dictionary(json.data)
is_loaded = true

View File

@@ -0,0 +1 @@
uid://b17g2w2g101o6

View File

@@ -0,0 +1,34 @@
class_name SaveGameData
extends Resource
var unlocked_collectibles_ids: Array[StringName] = []
var saved_photos: Array[String] = []
var audio_bus_volumes: Dictionary[String, float] = {}
func to_dictionary() -> Dictionary:
var serialized_collectible_ids: Array[String] = []
for collectible_id in unlocked_collectibles_ids:
serialized_collectible_ids.append(str(collectible_id))
return {
"unlocked_collectibles_ids": serialized_collectible_ids,
"saved_photos": saved_photos,
"audio_bus_volumes": audio_bus_volumes,
}
static func from_dictionary(data: Dictionary) -> SaveGameData:
var save_game_data := SaveGameData.new()
if data.has("unlocked_collectibles_ids"):
for collectible_id in data["unlocked_collectibles_ids"]:
save_game_data.unlocked_collectibles_ids.append(StringName(str(collectible_id)))
if data.has("saved_photos"):
for photo_path in data["saved_photos"]:
save_game_data.saved_photos.append(str(photo_path))
if data.has("audio_bus_volumes") and data["audio_bus_volumes"] is Dictionary:
for bus_name in data["audio_bus_volumes"]:
save_game_data.audio_bus_volumes[str(bus_name)] = float(data["audio_bus_volumes"][bus_name])
return save_game_data

View File

@@ -0,0 +1 @@
uid://bxmvs842r82vl

View File

@@ -0,0 +1,4 @@
class_name CollectibleLibrary
extends Resource
@export var collectibles: Array[CollectibleResource]

View File

@@ -0,0 +1 @@
uid://hhuufh87skq5

View File

@@ -0,0 +1,31 @@
[gd_resource type="Resource" script_class="CollectibleLibrary" format=3 uid="uid://c6pkpvsvimafb"]
[ext_resource type="Script" uid="uid://bj34o0org55ei" path="res://core/photo_mode/data/collectible_resource.gd" id="1_3cgvf"]
[ext_resource type="Script" uid="uid://hhuufh87skq5" path="res://core/photo_mode/data/collectible_library.gd" id="2_ggu10"]
[ext_resource type="Texture2D" uid="uid://cx8d233y32kmu" path="res://icon.svg" id="2_pxmdy"]
[sub_resource type="Resource" id="Resource_3cgvf"]
script = ExtResource("1_3cgvf")
id = &"cane"
title = "Cane"
image = ExtResource("2_pxmdy")
metadata/_custom_type_script = "uid://bj34o0org55ei"
[sub_resource type="Resource" id="Resource_ggu10"]
script = ExtResource("1_3cgvf")
id = &"gatto"
title = "Gatto"
image = ExtResource("2_pxmdy")
metadata/_custom_type_script = "uid://bj34o0org55ei"
[sub_resource type="Resource" id="Resource_pxmdy"]
script = ExtResource("1_3cgvf")
id = &"farfalla"
title = "Farfalla"
image = ExtResource("2_pxmdy")
metadata/_custom_type_script = "uid://bj34o0org55ei"
[resource]
script = ExtResource("2_ggu10")
collectibles = Array[ExtResource("1_3cgvf")]([SubResource("Resource_3cgvf"), SubResource("Resource_ggu10"), SubResource("Resource_pxmdy")])
metadata/_custom_type_script = "uid://hhuufh87skq5"

View File

@@ -0,0 +1,6 @@
class_name CollectibleResource
extends Resource
@export var id: StringName
@export var title: String
@export var image: Texture2D

View File

@@ -0,0 +1 @@
uid://bj34o0org55ei

View File

@@ -0,0 +1,60 @@
extends Node
signal on_collectible_unlocked(collectible_id: StringName)
signal on_photo_saved(filePath: String)
var collectibles_library: CollectibleLibrary = preload("res://core/photo_mode/data/collectible_library.tres")
var unlocked_collectibles_ids: Array[StringName] = []
var saved_photos: Array[String] = []
func _ready() -> void:
load_save_data()
func unlock_collectible(collectible_id: StringName) -> void:
if not collectible_id in unlocked_collectibles_ids:
var new_collectible = get_collectible_by_id(collectible_id)
if new_collectible:
unlocked_collectibles_ids.append(collectible_id)
sync_save_data()
on_collectible_unlocked.emit(collectible_id)
func get_collectible_by_id(id: StringName) -> CollectibleResource:
for collectible in collectibles_library.collectibles:
if collectible.id == id:
return collectible
return null
func get_all_collectibles() -> Array[CollectibleResource]:
return collectibles_library.collectibles
func get_unlocked_collectible_ids() -> Array[StringName]:
return unlocked_collectibles_ids
func save_photo_to_disk_async() -> void:
await RenderingServer.frame_post_draw
var image = get_viewport().get_texture().get_image()
if not DirAccess.dir_exists_absolute("user://photos"):
DirAccess.make_dir_absolute("user://photos")
var timestamp = str(Time.get_unix_time_from_system())
var file_path = "user://photos/photo_" + timestamp + ".png"
image.save_png(file_path)
saved_photos.append(file_path)
sync_save_data()
on_photo_saved.emit(file_path)
func sync_save_data() -> void:
GameState.save_data.unlocked_collectibles_ids = unlocked_collectibles_ids.duplicate()
GameState.save_data.saved_photos = saved_photos.duplicate()
func load_save_data() -> void:
unlocked_collectibles_ids = GameState.save_data.unlocked_collectibles_ids.duplicate()
saved_photos = GameState.save_data.saved_photos.duplicate()
if unlocked_collectibles_ids.is_empty() and saved_photos.is_empty():
unlocked_collectibles_ids.append(&"cane")
sync_save_data()

View File

@@ -0,0 +1 @@
uid://c3kq1qddpm8tf

View File

@@ -0,0 +1,6 @@
extends Area3D
@export var collectible_data: CollectibleResource
func _ready() -> void:
add_to_group("collectible")

View File

@@ -0,0 +1 @@
uid://62d14boivr3g

View File

@@ -0,0 +1,11 @@
[gd_scene format=3 uid="uid://bmkxt6btcx8qr"]
[ext_resource type="Script" uid="uid://62d14boivr3g" path="res://core/photo_mode/runtime/collectible.gd" id="1_xse8c"]
[sub_resource type="BoxShape3D" id="BoxShape3D_xse8c"]
[node name="Collectible" type="Area3D" unique_id=1229019813]
script = ExtResource("1_xse8c")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1479480920]
shape = SubResource("BoxShape3D_xse8c")

View File

@@ -0,0 +1,94 @@
extends Node3D
@export_group("Speed")
@export var pan_speed: float = 5.0
@export var rotation_speed: float = 0.003
@export_group("Bounds")
@export var movement_bounds: AABB
@export_group("Ref")
@export var rotation_target: Node3D
@onready var camera: Camera3D = $%Camera3D
var total_yaw: float = 0.0
var total_pitch: float = 0.0
var orbit_distance: float = 0.0
var current_pan: Vector2 = Vector2.ZERO
var is_active: bool = false
func _ready() -> void:
total_yaw = global_rotation.y
total_pitch = global_rotation.x
if rotation_target:
orbit_distance = global_position.distance_to(rotation_target.global_position)
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("toggle_photo_mode"):
is_active = !is_active
get_tree().paused = is_active
if !is_active:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
else:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
if event is InputEventMouseMotion and is_active:
total_yaw -= event.relative.x * rotation_speed
if event.is_action_pressed("take_photo"):
take_photo_async()
func _process(delta: float) -> void:
if not rotation_target:
return
var pan_input = Input.get_vector("photo_pan_left", "photo_pan_right", "photo_pan_up", "photo_pan_down")
var rot_basis = Basis.from_euler(Vector3(total_pitch, total_yaw, 0))
if pan_input != Vector2.ZERO and is_active:
current_pan.x += pan_input.x * pan_speed * delta
current_pan.y += -pan_input.y * pan_speed * delta
if movement_bounds.size != Vector3.ZERO:
current_pan.x = clampf(current_pan.x, movement_bounds.position.x, movement_bounds.position.x + movement_bounds.size.x)
current_pan.y = clampf(current_pan.y, movement_bounds.position.y, movement_bounds.position.y + movement_bounds.size.y)
var right_dir = rot_basis.x.normalized()
var up_dir = rot_basis.y.normalized()
var final_pan_offset = (right_dir * current_pan.x) + (up_dir * current_pan.y)
var orbit_pos = rotation_target.global_position + (rot_basis * Vector3(0, 0, orbit_distance))
global_position = orbit_pos + final_pan_offset
global_basis = rot_basis
func take_photo_async() -> void:
if !is_active:
return
var targets = get_tree().get_nodes_in_group("collectible")
var space_state = get_world_3d().direct_space_state
await CollectionManager.save_photo_to_disk_async()
for target in targets:
if not target.collectible_data:
continue
if camera.is_position_in_frustum(target.global_position):
var query = PhysicsRayQueryParameters3D.create(camera.global_position, target.global_position)
query.collide_with_areas = true
query.collide_with_bodies = true
var result = space_state.intersect_ray(query)
if result and result.collider == target:
CollectionManager.unlock_collectible(target.collectible_data.id)
GameState.save_game()

View File

@@ -0,0 +1 @@
uid://d7ln6iru6mq6

View File

@@ -0,0 +1,10 @@
[gd_scene format=3 uid="uid://vni5kjalum6d"]
[ext_resource type="Script" uid="uid://d7ln6iru6mq6" path="res://core/photo_mode/runtime/photo_mode_controller.gd" id="1_uhpya"]
[node name="PhotoModeController" type="Node3D" unique_id=695158870]
process_mode = 3
script = ExtResource("1_uhpya")
[node name="Camera3D" type="Camera3D" parent="." unique_id=1369039645]
unique_name_in_owner = true

View File

@@ -0,0 +1,34 @@
extends Control
@export var collectible_ui_scene: PackedScene
@onready var grid_container: GridContainer = $%CollectibleGrid
func _ready() -> void:
CollectionManager.on_collectible_unlocked.connect(_unlock_collectible)
setup()
func setup() -> void:
var unlocked_collectible_ids = CollectionManager.get_unlocked_collectible_ids()
var collectibles = CollectionManager.get_all_collectibles()
for child in grid_container.get_children():
child.queue_free()
for collectible in collectibles:
_add_collectible(collectible)
if unlocked_collectible_ids.has(collectible.id):
_unlock_collectible(collectible.id)
func on_collectible_unlocked(collectible_id: StringName) -> void:
_unlock_collectible(collectible_id)
func _add_collectible(collectible: CollectibleResource) -> void:
var collectible_ui: CollectibleUI = collectible_ui_scene.instantiate()
collectible_ui.setup(collectible)
grid_container.add_child(collectible_ui)
func _unlock_collectible(collectible_id: StringName) -> void:
for collectible_ui in grid_container.get_children():
if collectible_ui.collectible_resource.id == collectible_id:
collectible_ui.unlock()

View File

@@ -0,0 +1 @@
uid://dq3qtcrdnikl7

View File

@@ -0,0 +1,50 @@
[gd_scene format=3 uid="uid://bvw086glfpcba"]
[ext_resource type="Script" uid="uid://dq3qtcrdnikl7" path="res://core/photo_mode/ui/collectible_gallery.gd" id="1_67tug"]
[ext_resource type="PackedScene" uid="uid://dp7dvfauh5rpx" path="res://core/photo_mode/ui/collectible_ui.tscn" id="2_or234"]
[node name="CollectibleGallery" type="Control" unique_id=354419843]
layout_mode = 3
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_67tug")
collectible_ui_scene = ExtResource("2_or234")
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=640179265]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -350.0
offset_top = -350.0
offset_right = 350.0
offset_bottom = 350.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=125147979]
layout_mode = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer" unique_id=348576028]
layout_mode = 2
draw_focus_border = true
[node name="CollectibleGrid" type="GridContainer" parent="PanelContainer/MarginContainer/ScrollContainer" unique_id=1187865988]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 2
theme_override_constants/h_separation = 12
theme_override_constants/v_separation = 12
columns = 3

View File

@@ -0,0 +1,22 @@
extends Control
class_name CollectibleUI
@onready var label: Label = $%CollectibleName
@onready var texture_rect: TextureRect = $%CollectibleTexture
var collectible_resource: CollectibleResource
func setup(collectible: CollectibleResource) -> void:
collectible_resource = collectible
if !label:
label = $%CollectibleName
label.text = collectible_resource.title
if collectible.image:
if !texture_rect:
texture_rect = $%CollectibleTexture
texture_rect.texture = collectible_resource.image
func unlock() -> void:
if !texture_rect:
texture_rect = $%CollectibleTexture
texture_rect.set_modulate(Color(1,1,1,1))

View File

@@ -0,0 +1 @@
uid://dxh8e1n16qjpp

View File

@@ -0,0 +1,49 @@
[gd_scene format=3 uid="uid://dp7dvfauh5rpx"]
[ext_resource type="Script" uid="uid://dxh8e1n16qjpp" path="res://core/photo_mode/ui/collectible_ui.gd" id="1_tu5nx"]
[ext_resource type="Texture2D" uid="uid://cx8d233y32kmu" path="res://icon.svg" id="1_x3wje"]
[node name="CollectibleUI" type="Control" unique_id=201865221]
custom_minimum_size = Vector2(200, 200)
layout_mode = 3
anchors_preset = 0
offset_right = 200.0
offset_bottom = 200.0
script = ExtResource("1_tu5nx")
[node name="Panel" type="PanelContainer" parent="." unique_id=1372516531]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="Panel" unique_id=1141643525]
layout_mode = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer" unique_id=1841409877]
layout_mode = 2
alignment = 1
[node name="CollectibleName" type="Label" parent="Panel/MarginContainer/VBoxContainer" unique_id=117774359]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 0
text = "Collectible"
horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="Panel/MarginContainer/VBoxContainer" unique_id=1345822244]
layout_mode = 2
[node name="CollectibleTexture" type="TextureRect" parent="Panel/MarginContainer/VBoxContainer" unique_id=506844785]
unique_name_in_owner = true
modulate = Color(0.16206557, 0.1620656, 0.16206557, 1)
layout_mode = 2
size_flags_vertical = 3
texture = ExtResource("1_x3wje")
stretch_mode = 3

View File

@@ -0,0 +1,11 @@
extends Control
class_name Photo
@onready var texture_rect: TextureRect = $%PhotoTexture
func setup(texture: Texture) -> void:
if texture:
if !texture_rect:
texture_rect = $%PhotoTexture
texture_rect.texture = texture

View File

@@ -0,0 +1 @@
uid://dsey5dvc11vpq

View File

@@ -0,0 +1,38 @@
[gd_scene format=3 uid="uid://cvus62qkop3qi"]
[ext_resource type="Script" uid="uid://dsey5dvc11vpq" path="res://core/photo_mode/ui/photo.gd" id="1_u0arp"]
[node name="Photo" type="Control" unique_id=201865221]
custom_minimum_size = Vector2(200, 200)
layout_mode = 3
anchors_preset = 0
offset_right = 200.0
offset_bottom = 200.0
script = ExtResource("1_u0arp")
[node name="ColorRect" type="ColorRect" parent="." unique_id=1208008621]
layout_mode = 0
offset_right = 200.0
offset_bottom = 200.0
[node name="MarginContainer" type="MarginContainer" parent="ColorRect" unique_id=1141643525]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="Panel" type="PanelContainer" parent="ColorRect/MarginContainer" unique_id=1372516531]
layout_mode = 2
[node name="PhotoTexture" type="TextureRect" parent="ColorRect/MarginContainer/Panel" unique_id=506844785]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
expand_mode = 1
stretch_mode = 6

View File

@@ -0,0 +1,27 @@
extends Control
@export var photo_scene: PackedScene
@onready var grid_container: GridContainer = $%PhotoGrid
func _ready() -> void:
CollectionManager.on_photo_saved.connect(_create_photo_thumbnail)
load_gallery()
func load_gallery() -> void:
for child in grid_container.get_children():
child.queue_free()
var saved_photos = CollectionManager.saved_photos
for file_path in saved_photos:
if FileAccess.file_exists(file_path):
_create_photo_thumbnail(file_path)
func _create_photo_thumbnail(file_path: String) -> void:
var image = Image.load_from_file(file_path)
if image:
var photo: Photo = photo_scene.instantiate()
var texture = ImageTexture.create_from_image(image)
photo.setup(texture)
grid_container.add_child(photo)

View File

@@ -0,0 +1 @@
uid://k4iqrlkbjppl

View File

@@ -0,0 +1,50 @@
[gd_scene format=3 uid="uid://b6r787sik5yil"]
[ext_resource type="Script" uid="uid://k4iqrlkbjppl" path="res://core/photo_mode/ui/photo_gallery.gd" id="1_bp5uf"]
[ext_resource type="PackedScene" uid="uid://cvus62qkop3qi" path="res://core/photo_mode/ui/photo.tscn" id="2_45wok"]
[node name="PhotoGallery" type="Control" unique_id=354419843]
layout_mode = 3
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_bp5uf")
photo_scene = ExtResource("2_45wok")
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=640179265]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -350.0
offset_top = -350.0
offset_right = 350.0
offset_bottom = 350.0
grow_horizontal = 2
grow_vertical = 2
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=125147979]
layout_mode = 2
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer" unique_id=348576028]
layout_mode = 2
draw_focus_border = true
[node name="PhotoGrid" type="GridContainer" parent="PanelContainer/MarginContainer/ScrollContainer" unique_id=1187865988]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 2
theme_override_constants/h_separation = 12
theme_override_constants/v_separation = 12
columns = 3

31
core/radio/radio.gd Normal file
View File

@@ -0,0 +1,31 @@
extends AudioStreamPlayer
class_name Radio
@export var playlist: Array[AudioStream] = []
var current_track_index: int = 0
func _ready():
finished.connect(next_track)
func play_track(index: int = current_track_index):
if playlist.is_empty():
return
current_track_index = posmod(index, playlist.size())
stream = playlist[current_track_index]
play()
stream_paused = false
func next_track():
play_track(current_track_index + 1)
func prev_track():
play_track(current_track_index - 1)
func toggle_pause():
stream_paused = !stream_paused
func stop_radio():
stop()
stream_paused = false

1
core/radio/radio.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://bwrgh6xlvhqch

7
core/radio/radio.tscn Normal file
View File

@@ -0,0 +1,7 @@
[gd_scene format=3 uid="uid://cpeyt1dgrtglc"]
[ext_resource type="Script" uid="uid://bwrgh6xlvhqch" path="res://core/radio/radio.gd" id="1_2akjj"]
[node name="Radio" type="AudioStreamPlayer" unique_id=1234112225]
bus = &"Music"
script = ExtResource("1_2akjj")

View File

@@ -10,6 +10,10 @@ signal toggle_wind(value: bool)
@warning_ignore("unused_signal")
signal wind_change(value: float)
@warning_ignore("unused_signal")
signal trigger_random_weather(duration: float)
@warning_ignore("unused_signal")
signal weather_event_changed(description: String)
@warning_ignore("unused_signal")
signal toggle_fireflies(value: bool)
@warning_ignore("unused_signal")
signal toggle_storm(value: bool)
@@ -23,9 +27,19 @@ signal toggle_dust(value: bool)
signal toggle_blur(value: bool)
@warning_ignore("unused_signal")
signal toggle_shadows(value: bool)
@warning_ignore("unused_signal")
signal toggle_fog(value: bool)
#railway signals
@warning_ignore("unused_signal")
signal update_rail_chunks()
#day cycle signals
@warning_ignore("unused_signal")
signal toggle_pause_daytime(value: bool)
@warning_ignore("unused_signal")
signal day_time_changed(value: float, time_string: String)
@warning_ignore("unused_signal")
signal time_option_item_changed(index: int)
@warning_ignore("unused_signal")
signal day_time_option_changed(index: int)

15
default_bus_layout.tres Normal file
View File

@@ -0,0 +1,15 @@
[gd_resource type="AudioBusLayout" format=3 uid="uid://cqvx62pwvvboj"]
[resource]
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"
bus/2/name = &"SFX"
bus/2/solo = false
bus/2/mute = false
bus/2/bypass_fx = false
bus/2/volume_db = 0.0
bus/2/send = &"Master"

92
docs/gyms/ai/gym_ai.tscn Normal file
View File

@@ -0,0 +1,92 @@
[gd_scene format=3 uid="uid://dqvrhiqgkd3w1"]
[ext_resource type="PackedScene" uid="uid://clx701xdwelgx" path="res://core/ai/agents/base/ai_base.tscn" id="1_a2xtd"]
[sub_resource type="NavigationMesh" id="NavigationMesh_optuv"]
vertices = PackedVector3Array(10.984741, 0.25012434, -1.0140381, 11.234741, 0.25012434, 0.23596191, 19.484741, 0.25012434, 0.23596191, 19.484741, 0.25012434, -19.514038, 9.234741, 0.25012434, -1.0140381, -19.515259, 0.25012434, -19.514038, -19.515259, 0.25012434, -0.014038086, 8.984741, 0.25012434, -0.014038086, 10.984741, 0.25012434, 1.2359619, 19.484741, 0.25012434, 19.235962, 9.234741, 0.25012434, 1.2359619, -19.515259, 0.25012434, 19.235962)
polygons = [PackedInt32Array(1, 0, 2), PackedInt32Array(2, 0, 3), PackedInt32Array(6, 5, 4), PackedInt32Array(4, 5, 3), PackedInt32Array(3, 0, 4), PackedInt32Array(4, 7, 6), PackedInt32Array(1, 2, 8), PackedInt32Array(8, 2, 9), PackedInt32Array(6, 10, 11), PackedInt32Array(11, 10, 9), PackedInt32Array(10, 8, 9), PackedInt32Array(6, 7, 10)]
[sub_resource type="PlaneMesh" id="PlaneMesh_optuv"]
[sub_resource type="BoxShape3D" id="BoxShape3D_xtjak"]
size = Vector3(1.9942131, 0.09710693, 1.984024)
[sub_resource type="BoxMesh" id="BoxMesh_lmjyn"]
[sub_resource type="BoxShape3D" id="BoxShape3D_lmjyn"]
[sub_resource type="Environment" id="Environment_lmjyn"]
[node name="GymAI" type="Node3D" unique_id=868339787]
[node name="NavigationRegion3D" type="NavigationRegion3D" parent="." unique_id=774907858]
navigation_mesh = SubResource("NavigationMesh_optuv")
[node name="Floor" type="MeshInstance3D" parent="NavigationRegion3D" unique_id=1976415311]
transform = Transform3D(20, 0, 0, 0, 20, 0, 0, 0, 20, 0, -0.83473957, 0)
mesh = SubResource("PlaneMesh_optuv")
[node name="StaticBody3D" type="StaticBody3D" parent="NavigationRegion3D/Floor" unique_id=2007477719]
[node name="CollisionShape3D" type="CollisionShape3D" parent="NavigationRegion3D/Floor/StaticBody3D" unique_id=1446628130]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0036563873, -0.009703338, -0.00868988)
shape = SubResource("BoxShape3D_xtjak")
[node name="Obstacle" type="MeshInstance3D" parent="NavigationRegion3D" unique_id=710701082]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10.148018, 0.06508446, 0)
mesh = SubResource("BoxMesh_lmjyn")
[node name="StaticBody3D" type="StaticBody3D" parent="NavigationRegion3D/Obstacle" unique_id=67152580]
[node name="CollisionShape3D" type="CollisionShape3D" parent="NavigationRegion3D/Obstacle/StaticBody3D" unique_id=1663742898]
shape = SubResource("BoxShape3D_lmjyn")
[node name="AIBase" parent="." unique_id=1228675528 instance=ExtResource("1_a2xtd")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.48246834, 0.21202153, 0.11262059)
[node name="Camera3D" type="Camera3D" parent="." unique_id=1148396057]
transform = Transform3D(-2.0613477e-08, 0.8818225, -0.47158146, 3.854569e-08, 0.47158146, 0.8818225, 1, -3.5527137e-15, -4.371139e-08, -18.21907, 28.71355, 0)
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1772424900]
environment = SubResource("Environment_lmjyn")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=269230449]
transform = Transform3D(1, 0, 0, 0, 0.17952333, 0.98375374, 0, -0.98375374, 0.17952333, 0, 18.920979, 0)
[node name="Control" type="Control" parent="." unique_id=1072240389]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="PanelContainer" type="PanelContainer" parent="Control" unique_id=1647272132]
layout_mode = 0
offset_left = 50.0
offset_top = 50.0
offset_right = 400.0
offset_bottom = 827.0
[node name="Label" type="Label" parent="Control/PanelContainer" unique_id=272421245]
custom_minimum_size = Vector2(1, 1)
layout_mode = 2
size_flags_vertical = 1
text = "System Logic:
- The NPC uses a finite state machine to switch between behaviors.
- State logic runs during physics updates, while movement is handled by the agent through NavigationAgent3D.
AI States:
- IdleState: The agent waits in place for the duration defined by wait_time.
- PatrolState: The agent selects a valid random destination and moves toward it.
Core Components:
- NavigationAgent3D: Calculates the path and reports when the destination has been reached.
- PatrolRadiusShape: Defines the random movement area used when choosing the next patrol point.
- StateMachine: Initializes the available states and handles transitions between them.
Parameters:
- Speed: Movement speed of the agent.
- Wait Time: Delay in seconds on the IdleState node.
- Area Radius: Radius of the SphereShape3D used by PatrolRadiusShape."
autowrap_mode = 2

View File

@@ -0,0 +1,7 @@
extends Node3D
@onready var gallery: Control = $%Gallery
func _on_button_gallery_pressed() -> void:
gallery.visible = !gallery.visible

View File

@@ -0,0 +1 @@
uid://7oglx4br38d5

View File

@@ -0,0 +1,138 @@
[gd_scene format=3 uid="uid://bwc8slnbu7dmn"]
[ext_resource type="Script" uid="uid://7oglx4br38d5" path="res://docs/gyms/photo_mode/gym_photo_mode.gd" id="1_cl1h5"]
[ext_resource type="PackedScene" uid="uid://vni5kjalum6d" path="res://core/photo_mode/runtime/photo_mode_controller.tscn" id="2_q5kgc"]
[ext_resource type="PackedScene" uid="uid://bmkxt6btcx8qr" path="res://core/photo_mode/runtime/collectible.tscn" id="3_734hf"]
[ext_resource type="Texture2D" uid="uid://c3grftlmap4q5" path="res://tgcc/chunk/prop/tree/leaf1_alpha.png" id="4_00joj"]
[ext_resource type="Script" uid="uid://bj34o0org55ei" path="res://core/photo_mode/data/collectible_resource.gd" id="5_crfij"]
[ext_resource type="PackedScene" uid="uid://bvw086glfpcba" path="res://core/photo_mode/ui/collectible_gallery.tscn" id="6_6aab5"]
[ext_resource type="PackedScene" uid="uid://b6r787sik5yil" path="res://core/photo_mode/ui/photo_gallery.tscn" id="7_tb1ii"]
[sub_resource type="Environment" id="Environment_48mla"]
[sub_resource type="PlaneMesh" id="PlaneMesh_hh1ka"]
[sub_resource type="BoxShape3D" id="BoxShape3D_ynvi2"]
size = Vector3(1.9942131, 0.09710693, 1.984024)
[sub_resource type="Resource" id="Resource_ynvi2"]
script = ExtResource("5_crfij")
id = &"gatto"
title = "Gatto"
image = ExtResource("4_00joj")
metadata/_custom_type_script = "uid://bj34o0org55ei"
[sub_resource type="BoxMesh" id="BoxMesh_hh1ka"]
[node name="GymPhotoMode" type="Node3D" unique_id=1314600330]
script = ExtResource("1_cl1h5")
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=213183287]
environment = SubResource("Environment_48mla")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1274975837]
transform = Transform3D(1, 0, 0, 0, 0.17952333, 0.98375374, 0, -0.98375374, 0.17952333, 0, 18.920979, 0)
[node name="Floor" type="MeshInstance3D" parent="." unique_id=958865480]
transform = Transform3D(20, 0, 0, 0, 20, 0, 0, 0, 20, 0, -0.83473957, 0)
mesh = SubResource("PlaneMesh_hh1ka")
[node name="StaticBody3D" type="StaticBody3D" parent="Floor" unique_id=2096983240]
[node name="CollisionShape3D" type="CollisionShape3D" parent="Floor/StaticBody3D" unique_id=188185045]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0036563873, -0.009703338, -0.00868988)
shape = SubResource("BoxShape3D_ynvi2")
[node name="PhotoModeController" parent="." unique_id=695158870 node_paths=PackedStringArray("rotation_target") instance=ExtResource("2_q5kgc")]
transform = Transform3D(1, 0, 0, 0, 0.49999994, 0.8660254, 0, -0.8660254, 0.49999994, 0, 30, 20)
movement_bounds = AABB(-5, -5, -5, 10, 10, 10)
rotation_target = NodePath("../Node3D")
[node name="Node3D" type="Node3D" parent="." unique_id=570786250]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 10, 0)
[node name="Node3D2" type="Node3D" parent="." unique_id=15037296]
[node name="Collectible" parent="Node3D2" unique_id=1229019813 instance=ExtResource("3_734hf")]
collectible_data = SubResource("Resource_ynvi2")
[node name="MeshInstance3D" type="MeshInstance3D" parent="Node3D2" unique_id=553040412]
mesh = SubResource("BoxMesh_hh1ka")
[node name="Control" type="Control" parent="." unique_id=1802496396]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="ButtonGallery" type="Button" parent="Control" unique_id=539195532]
layout_mode = 0
offset_left = 50.0
offset_top = 50.0
offset_right = 163.0
offset_bottom = 81.0
text = "Menu"
[node name="Gallery" type="HSplitContainer" parent="Control" unique_id=1388833682]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 420.0
grow_horizontal = 2
grow_vertical = 2
[node name="CollectibleGallery" parent="Control/Gallery" unique_id=354419843 instance=ExtResource("6_6aab5")]
layout_mode = 2
size_flags_horizontal = 3
[node name="PhotoGallery" parent="Control/Gallery" unique_id=263414560 instance=ExtResource("7_tb1ii")]
layout_mode = 2
size_flags_horizontal = 3
[node name="PanelContainer" type="PanelContainer" parent="Control" unique_id=2015953740]
layout_mode = 0
offset_left = 50.0
offset_top = 150.0
offset_right = 400.0
offset_bottom = 927.0
[node name="ScrollContainer" type="ScrollContainer" parent="Control/PanelContainer" unique_id=1474258594]
layout_mode = 2
[node name="Label" type="Label" parent="Control/PanelContainer/ScrollContainer" unique_id=1982569320]
custom_minimum_size = Vector2(1, 1)
layout_mode = 2
size_flags_horizontal = 3
text = "System Logic:
- The system allows players to toggle a free camera mode to photograph and collect museum items.
- Game Pause: Activating the mode pauses the SceneTree, freezing the world while allowing the camera to remain functional.
- Taking a photo saves the captured image to disk and checks which collectibles are visible and unobstructed before unlocking them.
- CollectionManager acts as a global singleton that tracks unlocked collectibles, saved photos, and the data used by the UI.
Photo Mode Actions:
- Toggle Mode(P): Switches between normal gameplay and the photo camera, capturing or releasing the mouse, and toggles the global pause state.
- Rotate(Mouse): The camera orbits around the configured target while photo mode is active.
- Pan(Input Actions): The camera can be moved horizontally and vertically within the configured bounds.
- Capture(F): Saves the current frame and performs frustum and raycast checks against collectibles in view.
Core Components:
- PhotoModeController: Manages camera inputs, movement logic, and the photo-taking process.
- Collectible: An Area3D node placed on objects to make them identifiable by the camera.
- CollectionManager: Handles the logic for unlocking items and provides data to the UI.
- CollectibleGallery: Displays all collectibles and visually highlights the unlocked ones.
- PhotoGallery: Displays the photos saved by the player during the session.
Parameters:
- Pan & Rotation Speed: Defines how fast the camera moves and rotates.
- Movement Bounds: An AABB that restricts the camera's panning area.
- Collectible Data: Resource files containing the ID, title, and image for each item.
- Library: A central list of all CollectibleResources registered in the game."
autowrap_mode = 2
[connection signal="pressed" from="Control/ButtonGallery" to="." method="_on_button_gallery_pressed"]

View File

@@ -0,0 +1,35 @@
extends Node3D
@onready var radio: Radio = $%Radio
@onready var button_pause_resume: Button = $%Button_Pause_Resume
@onready var settings_menu: SettingsMenu = $%SettingsMenu
func _on_button_play_pressed() -> void:
if !radio.playing:
radio.play_track()
func _on_button_pause_pressed() -> void:
radio.toggle_pause()
if !button_pause_resume:
button_pause_resume = $%Button_Pause_Resume
if radio.stream_paused:
button_pause_resume.text = "Resume"
else:
button_pause_resume.text = "Pause"
func _on_button_stop_pressed() -> void:
radio.stop_radio()
func _on_button_next_pressed() -> void:
radio.next_track()
func _on_button_previous_pressed() -> void:
radio.prev_track()
func _on_button_settings_menu_pressed() -> void:
settings_menu.visible = !settings_menu.visible

View File

@@ -0,0 +1 @@
uid://ew4b78u5tkiu

View File

@@ -0,0 +1,104 @@
[gd_scene format=3 uid="uid://bdkdhn1oallke"]
[ext_resource type="PackedScene" uid="uid://cpeyt1dgrtglc" path="res://core/radio/radio.tscn" id="1_8yomj"]
[ext_resource type="Script" uid="uid://ew4b78u5tkiu" path="res://docs/gyms/radio/gym_radio.gd" id="1_h5r8c"]
[ext_resource type="AudioStream" uid="uid://70cc8she43re" path="res://docs/gyms/radio/lofi_01.ogg" id="3_ltpvc"]
[ext_resource type="PackedScene" uid="uid://caqf471x0kttc" path="res://core/audio/settings/settings_menu.tscn" id="4_8pw2j"]
[node name="GymRadio" type="Node3D" unique_id=204859062]
script = ExtResource("1_h5r8c")
[node name="Radio" parent="." unique_id=1234112225 instance=ExtResource("1_8yomj")]
unique_name_in_owner = true
playlist = Array[AudioStream]([ExtResource("3_ltpvc")])
[node name="Control" type="Control" parent="." unique_id=1232082176]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="SettingsMenu" parent="Control" unique_id=1639777294 instance=ExtResource("4_8pw2j")]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
[node name="VBoxContainer2" type="VBoxContainer" parent="Control" unique_id=2092627240]
layout_mode = 1
anchors_preset = 9
anchor_bottom = 1.0
offset_right = 229.0
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="Control/VBoxContainer2" unique_id=2097365775]
layout_mode = 2
[node name="Button_Play" type="Button" parent="Control/VBoxContainer2/VBoxContainer" unique_id=1422696340]
layout_mode = 2
text = "Play"
[node name="Button_Pause_Resume" type="Button" parent="Control/VBoxContainer2/VBoxContainer" unique_id=2016690081]
unique_name_in_owner = true
layout_mode = 2
text = "Pause"
[node name="Button_Stop" type="Button" parent="Control/VBoxContainer2/VBoxContainer" unique_id=61861870]
layout_mode = 2
text = "Stop"
[node name="Button_Next" type="Button" parent="Control/VBoxContainer2/VBoxContainer" unique_id=957606385]
layout_mode = 2
text = "Next"
[node name="Button_Previous" type="Button" parent="Control/VBoxContainer2/VBoxContainer" unique_id=929072518]
layout_mode = 2
text = "Previous"
[node name="Button_SettingsMenu" type="Button" parent="Control/VBoxContainer2/VBoxContainer" unique_id=431867154]
layout_mode = 2
text = "SettingsMenu"
[node name="PanelContainer" type="PanelContainer" parent="Control/VBoxContainer2" unique_id=1558442050]
layout_mode = 2
size_flags_vertical = 3
[node name="ScrollContainer" type="ScrollContainer" parent="Control/VBoxContainer2/PanelContainer" unique_id=7789304]
layout_mode = 2
[node name="Label" type="Label" parent="Control/VBoxContainer2/PanelContainer/ScrollContainer" unique_id=2043293564]
custom_minimum_size = Vector2(1, 1)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "System Logic:
- The system is an extension of AudioStreamPlayer designed to manage sequential audio playback.
- It automatically triggers the next track in the sequence when the current one finishes via the finished signal.
Core Functionality:
- Playlist Management: Stores a list of AudioStream resources and tracks the current song index.
- Track Navigation: Allows cycling forward and backward through the playlist with index wrapping (loops back to start/end).
- Playback Control: Supports jumping to specific tracks, toggling the pause state, and stopping the stream.
- Audio Settings: If the Music bus exists, the Settings menu can be used to test and adjust playback volume.
Key Methods:
- play_track: Loads a specific stream from the playlist and begins playback.
- next_track / prev_track: Increments or decrements the index to navigate the playlist.
- toggle_pause: Resumes or pauses the current stream without resetting its position.
Adjustable Parameters:
- Playlist: An Array in the inspector where you can assign one or more AudioStream resources."
autowrap_mode = 2
[connection signal="pressed" from="Control/VBoxContainer2/VBoxContainer/Button_Play" to="." method="_on_button_play_pressed"]
[connection signal="pressed" from="Control/VBoxContainer2/VBoxContainer/Button_Pause_Resume" to="." method="_on_button_pause_pressed"]
[connection signal="pressed" from="Control/VBoxContainer2/VBoxContainer/Button_Stop" to="." method="_on_button_stop_pressed"]
[connection signal="pressed" from="Control/VBoxContainer2/VBoxContainer/Button_Next" to="." method="_on_button_next_pressed"]
[connection signal="pressed" from="Control/VBoxContainer2/VBoxContainer/Button_Previous" to="." method="_on_button_previous_pressed"]
[connection signal="pressed" from="Control/VBoxContainer2/VBoxContainer/Button_SettingsMenu" to="." method="_on_button_settings_menu_pressed"]

BIN
docs/gyms/radio/lofi_01.ogg Normal file

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://70cc8she43re"
path="res://.godot/imported/lofi_01.ogg-0aacd597266806080412021811923091.oggvorbisstr"
[deps]
source_file="res://docs/gyms/radio/lofi_01.ogg"
dest_files=["res://.godot/imported/lofi_01.ogg-0aacd597266806080412021811923091.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -8,12 +8,13 @@
[ext_resource type="Script" uid="uid://wv6kcqkibium" path="res://core/biome_generator/biome.gd" id="5_2wjys"]
[ext_resource type="PackedScene" uid="uid://cv5xmnow451kl" path="res://core/camera.tscn" id="5_8tojn"]
[ext_resource type="PackedScene" uid="uid://crlk31ecl480n" path="res://tgcc/chunk/countryside/scene/chunk_country_corner_03.tscn" id="6_21usy"]
[ext_resource type="Script" uid="uid://dboerd4a6dwj7" path="res://docs/museums/biome_generator/rails.gd" id="6_pypsn"]
[ext_resource type="Script" uid="uid://dboerd4a6dwj7" path="res://core/biome_generator/rails.gd" id="6_pypsn"]
[ext_resource type="PackedScene" uid="uid://dvk3bytqn3m5s" path="res://tgcc/train/main_train.tscn" id="6_wjpfq"]
[ext_resource type="PackedScene" uid="uid://brpp7fe5noq8v" path="res://tgcc/chunk/countryside/scene/chunk_country_cross_3_01.tscn" id="7_qe84w"]
[ext_resource type="PackedScene" uid="uid://0kgjaqijaqku" path="res://docs/museums/biome_generator/rails.tscn" id="8_mb5yv"]
[ext_resource type="PackedScene" uid="uid://dn1btv0e0ses7" path="res://core/fireworks.tscn" id="8_xtogr"]
[ext_resource type="PackedScene" uid="uid://cu2chsjh8wuvp" path="res://tgcc/chunk/countryside/scene/chunk_country_straight_02.tscn" id="9_sw7bt"]
[ext_resource type="PackedScene" uid="uid://c73yk6858dmwn" path="res://tgcc/chunk/countryside/scene/chunk_country_cross_4_01.tscn" id="10_c7mkj"]
[ext_resource type="PackedScene" uid="uid://cmuvmmp7xam4o" path="res://core/daynight/environment_management.tscn" id="16_c7mkj"]
[ext_resource type="AudioStream" uid="uid://dvabvoqyya0g5" path="res://core/daynight/sounds/thunder_3.mp3" id="17_sw7bt"]
[ext_resource type="AudioStream" uid="uid://creenugno7hm" path="res://core/daynight/sounds/thunder_4.mp3" id="18_2l6dd"]
@@ -100,7 +101,7 @@ compositor_effects = Array[CompositorEffect]([SubResource("CompositorEffect_q52t
[sub_resource type="Resource" id="Resource_3qrd0"]
script = ExtResource("5_2wjys")
name = "countryside"
available_chunks = Array[PackedScene]([ExtResource("6_21usy"), ExtResource("7_qe84w"), ExtResource("9_sw7bt")])
available_chunks = Array[PackedScene]([ExtResource("6_21usy"), ExtResource("7_qe84w"), ExtResource("9_sw7bt"), ExtResource("10_c7mkj")])
metadata/_custom_type_script = "uid://wv6kcqkibium"
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_pypsn"]
@@ -178,10 +179,10 @@ script = ExtResource("23_27ile")
[node name="Snow" type="CheckButton" parent="Control" unique_id=454394427]
layout_mode = 0
offset_left = 21.0
offset_top = 13.0
offset_right = 109.0
offset_bottom = 44.0
offset_left = 8.0
offset_top = 173.0
offset_right = 98.0
offset_bottom = 204.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
@@ -192,10 +193,10 @@ text = "Snow"
[node name="Rain" type="CheckButton" parent="Control" unique_id=837805524]
layout_mode = 0
offset_left = 21.0
offset_top = 41.0
offset_right = 109.0
offset_bottom = 72.0
offset_left = 8.0
offset_top = 201.0
offset_right = 96.0
offset_bottom = 232.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
@@ -206,10 +207,10 @@ text = "Rain"
[node name="Wind" type="CheckButton" parent="Control" unique_id=1013004982]
layout_mode = 0
offset_left = 21.0
offset_top = 91.0
offset_right = 109.0
offset_bottom = 122.0
offset_left = 8.0
offset_top = 231.0
offset_right = 96.0
offset_bottom = 262.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
@@ -220,19 +221,19 @@ text = "Wind"
[node name="WindSlider" type="HSlider" parent="Control" unique_id=28050098]
layout_mode = 0
offset_left = 26.0
offset_top = 124.0
offset_right = 205.0
offset_bottom = 140.0
offset_left = 103.0
offset_top = 238.0
offset_right = 187.0
offset_bottom = 254.0
max_value = 1.0
step = 0.01
[node name="Fireflies" type="CheckButton" parent="Control" unique_id=1046228444]
layout_mode = 0
offset_left = 21.0
offset_top = 169.0
offset_right = 130.0
offset_bottom = 200.0
offset_left = 8.0
offset_top = 259.0
offset_right = 117.0
offset_bottom = 290.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
@@ -243,10 +244,10 @@ text = "Fireflies"
[node name="Storm" type="CheckButton" parent="Control" unique_id=1792625744]
layout_mode = 0
offset_left = 21.0
offset_top = 66.0
offset_right = 117.0
offset_bottom = 97.0
offset_left = 98.0
offset_top = 201.0
offset_right = 194.0
offset_bottom = 232.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
@@ -257,73 +258,88 @@ text = "Storm"
[node name="DayTimeLabel" type="Label" parent="Control" unique_id=1124935856]
layout_mode = 0
offset_left = 26.0
offset_top = 150.0
offset_right = 360.0
offset_bottom = 173.0
offset_left = 13.0
offset_top = 15.0
offset_right = 347.0
offset_bottom = 38.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_outline_color = Color(1, 1, 1, 1)
text = "Time 00:00"
[node name="DayTimeSlider" type="HSlider" parent="Control" unique_id=1865778345]
layout_mode = 0
offset_left = 26.0
offset_top = 228.0
offset_right = 205.0
offset_bottom = 244.0
offset_left = 13.0
offset_top = 40.0
offset_right = 138.0
offset_bottom = 56.0
max_value = 1.0
step = 0.01
[node name="DayTimeOptions" type="OptionButton" parent="Control" unique_id=928646468]
layout_mode = 0
offset_left = 13.0
offset_top = 59.0
offset_right = 124.0
offset_bottom = 90.0
selected = 1
item_count = 4
popup/item_0/text = "Sunrise"
popup/item_0/id = 1
popup/item_1/text = "Day"
popup/item_1/id = 2
popup/item_2/text = "Sunset"
popup/item_2/id = 3
popup/item_3/text = "Night"
popup/item_3/id = 4
[node name="Dust" type="CheckButton" parent="Control" unique_id=760281365]
layout_mode = 0
offset_left = 21.0
offset_top = 304.0
offset_right = 150.0
offset_bottom = 335.0
offset_left = 8.0
offset_top = 91.0
offset_right = 137.0
offset_bottom = 122.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
theme_override_colors/font_hover_color = Color(1, 1, 1, 1)
theme_override_colors/font_hover_pressed_color = Color(1, 1, 1, 1)
theme_override_colors/font_outline_color = Color(1, 1, 1, 1)
button_pressed = true
text = "Dust"
[node name="Blur" type="CheckButton" parent="Control" unique_id=1083708100]
layout_mode = 0
offset_left = 21.0
offset_top = 332.0
offset_right = 150.0
offset_bottom = 363.0
offset_left = 8.0
offset_top = 118.0
offset_right = 137.0
offset_bottom = 149.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
theme_override_colors/font_hover_color = Color(1, 1, 1, 1)
theme_override_colors/font_hover_pressed_color = Color(1, 1, 1, 1)
theme_override_colors/font_outline_color = Color(1, 1, 1, 1)
button_pressed = true
text = "Blur"
[node name="Pause" type="CheckButton" parent="Control" unique_id=1587043871]
layout_mode = 0
offset_left = 21.0
offset_top = 243.0
offset_right = 158.0
offset_bottom = 274.0
offset_left = 143.0
offset_top = 31.0
offset_right = 238.0
offset_bottom = 62.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
theme_override_colors/font_hover_color = Color(1, 1, 1, 1)
theme_override_colors/font_hover_pressed_color = Color(1, 1, 1, 1)
theme_override_colors/font_outline_color = Color(1, 1, 1, 1)
text = "Pause Time"
text = "Pause"
[node name="Shadows" type="CheckButton" parent="Control" unique_id=283354318]
layout_mode = 0
offset_left = 21.0
offset_top = 356.0
offset_right = 150.0
offset_bottom = 387.0
offset_left = 8.0
offset_top = 145.0
offset_right = 137.0
offset_bottom = 176.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
@@ -333,20 +349,29 @@ theme_override_colors/font_outline_color = Color(1, 1, 1, 1)
button_pressed = true
text = "Shadows"
[node name="Guide" type="Label" parent="Control" unique_id=2121474959]
[node name="RandomWeather" type="Button" parent="Control" unique_id=1047710802]
layout_mode = 0
offset_left = 26.0
offset_top = 401.0
offset_right = 339.0
offset_bottom = 606.0
text = "R-> Reset isometric camera
C-> Toggle cinematic mode
Z-> soft zoom
Manual controls (1-5)
T -> go to next stop
F -> fireworks
BACKSPACE -> stop train
ARROW UP/DOWN -> change train speed"
offset_left = 12.0
offset_top = 289.0
offset_right = 157.0
offset_bottom = 320.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_focus_color = Color(1, 1, 1, 1)
theme_override_colors/font_pressed_color = Color(1, 1, 1, 1)
theme_override_colors/font_hover_color = Color(1, 1, 1, 1)
theme_override_colors/font_hover_pressed_color = Color(1, 1, 1, 1)
theme_override_colors/font_outline_color = Color(1, 1, 1, 1)
text = "Random Weather (10\")"
[node name="WeatherEventLabel" type="Label" parent="Control" unique_id=641424797]
layout_mode = 0
offset_left = 1656.0
offset_top = 11.0
offset_right = 1908.0
offset_bottom = 34.0
theme_override_colors/font_color = Color(1, 1, 1, 1)
theme_override_colors/font_outline_color = Color(1, 1, 1, 1)
text = "Weather: Clear"
[connection signal="toggled" from="Control/Snow" to="Control" method="_on_snow_toggled"]
[connection signal="toggled" from="Control/Rain" to="Control" method="_on_rain_toggled"]
@@ -355,7 +380,9 @@ ARROW UP/DOWN -> change train speed"
[connection signal="toggled" from="Control/Fireflies" to="Control" method="_on_fireflies_toggled"]
[connection signal="toggled" from="Control/Storm" to="Control" method="_on_storm_toggled"]
[connection signal="value_changed" from="Control/DayTimeSlider" to="Control" method="_on_day_time_slider_value_changed"]
[connection signal="item_selected" from="Control/DayTimeOptions" to="Control" method="_on_option_button_item_selected"]
[connection signal="toggled" from="Control/Dust" to="Control" method="_on_dust_toggled"]
[connection signal="toggled" from="Control/Blur" to="Control" method="_on_blur_toggled"]
[connection signal="toggled" from="Control/Pause" to="Control" method="_on_pause_toggled"]
[connection signal="toggled" from="Control/Shadows" to="Control" method="_on_shadows_toggled"]
[connection signal="pressed" from="Control/RandomWeather" to="Control" method="_on_random_weather_pressed"]

File diff suppressed because one or more lines are too long

View File

@@ -1,16 +1,22 @@
extends Control
@onready var day_night: EnvironmentManagerRoot = get_node_or_null("../DayNight") as EnvironmentManagerRoot
@onready var day_night: EnvironmentManagerRoot = $"../DayNight"
@onready var weather_event_label: Label = $WeatherEventLabel
@onready var day_time_label: Label = $DayTimeLabel
@onready var day_time_slider: HSlider = $DayTimeSlider
@onready var wind_slider: HSlider = get_node_or_null("WindSlider") as HSlider
@onready var wind_slider: HSlider = $WindSlider
@onready var day_time_option: OptionButton = $DayTimeOptions
var default_random_event_duration: float = 10.0
func _ready() -> void:
UIEvents.day_time_changed.connect(_on_day_time_changed)
UIEvents.day_time_option_changed.connect(_on_day_time_option_changed)
UIEvents.weather_event_changed.connect(_on_weather_event_changed)
if day_night != null and day_night.environment_config != null and wind_slider != null:
wind_slider.set_value_no_signal(day_night.environment_config.wind_strength)
_sync_day_time_ui()
_on_weather_event_changed("Weather: Clear")
func _on_rain_toggled(toggled_on: bool) -> void:
UIEvents.toggle_rain.emit(toggled_on)
@@ -24,6 +30,12 @@ func _on_wind_toggled(toggled_on: bool) -> void:
func _on_wind_slider_value_changed(value: float) -> void:
UIEvents.wind_change.emit(value)
func _on_random_weather_pressed() -> void:
var duration: float = default_random_event_duration
if day_night != null and day_night.environment_config != null:
duration = day_night.environment_config.random_event_duration
UIEvents.trigger_random_weather.emit(duration)
func _on_fireflies_toggled(toggled_on: bool) -> void:
UIEvents.toggle_fireflies.emit(toggled_on)
@@ -44,6 +56,10 @@ func _on_day_time_changed(value: float, time_string: String) -> void:
label_text = "Time %s | nt %.3f | dt %.3f" % [time_string, value, day_night.day_time]
day_time_label.text = label_text
func _on_weather_event_changed(description: String) -> void:
if weather_event_label:
weather_event_label.text = description
func _on_fog_overlay_toggled(toggled_on: bool) -> void:
UIEvents.toggle_fog_overlay.emit(toggled_on)
@@ -74,3 +90,20 @@ func _sync_day_time_ui() -> void:
day_night_controller.current_time,
day_night_controller.get_time_string()
)
_on_day_time_option_changed(
day_night_controller.get_time_option_index(day_night_controller.current_time)
)
func _on_day_time_option_changed(index: int) -> void:
if day_time_option == null:
return
day_time_option.select(index)
func _on_option_button_item_selected(index: int) -> void:
UIEvents.time_option_item_changed.emit(index)
func _on_fog_toggled(toggled_on: bool) -> void:
UIEvents.toggle_fog.emit(toggled_on)
func _on_update_rails_pressed() -> void:
UIEvents.update_rail_chunks.emit()

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,57 @@
[gd_scene format=3 uid="uid://b7rhqxsyv87r1"]
[ext_resource type="PackedScene" uid="uid://cjv1e8pc6gde1" path="res://tgcc/chunk/countryside/scene/chunk_country_corner_01.tscn" id="1_eexnc"]
[ext_resource type="PackedScene" uid="uid://dqoai3665vb0a" path="res://tgcc/chunk/countryside/scene/chunk_country_corner_02.tscn" id="2_jh6kr"]
[ext_resource type="PackedScene" uid="uid://crlk31ecl480n" path="res://tgcc/chunk/countryside/scene/chunk_country_corner_03.tscn" id="3_pftht"]
[ext_resource type="PackedScene" uid="uid://brpp7fe5noq8v" path="res://tgcc/chunk/countryside/scene/chunk_country_cross_3_01.tscn" id="4_jexg3"]
[ext_resource type="PackedScene" uid="uid://qmt8bkiksgj3" path="res://tgcc/chunk/countryside/scene/chunk_country_cross_3_02.tscn" id="5_hieqo"]
[ext_resource type="PackedScene" uid="uid://d1tfkhuktwthn" path="res://tgcc/chunk/countryside/scene/chunk_country_cross_3_03.tscn" id="6_re3ik"]
[ext_resource type="PackedScene" uid="uid://c73yk6858dmwn" path="res://tgcc/chunk/countryside/scene/chunk_country_cross_4_01.tscn" id="7_v47u4"]
[ext_resource type="PackedScene" uid="uid://b8blm6bwintf7" path="res://tgcc/chunk/countryside/scene/chunk_country_empty_01.tscn" id="8_bag0j"]
[ext_resource type="PackedScene" uid="uid://b002wu5ltuqi4" path="res://tgcc/chunk/countryside/scene/chunk_country_end_01.tscn" id="9_1vyrv"]
[ext_resource type="PackedScene" uid="uid://dtfmvcbninrcu" path="res://tgcc/chunk/countryside/scene/chunk_country_end_02.tscn" id="10_sdnjr"]
[ext_resource type="PackedScene" uid="uid://h6xj43vo84uf" path="res://tgcc/chunk/countryside/scene/chunk_country_straight_01.tscn" id="11_arwwt"]
[ext_resource type="PackedScene" uid="uid://cu2chsjh8wuvp" path="res://tgcc/chunk/countryside/scene/chunk_country_straight_02.tscn" id="12_l62i2"]
[ext_resource type="PackedScene" uid="uid://pxmcy0lhne5d" path="res://tgcc/chunk/countryside/scene/chunk_country_straight_03.tscn" id="13_f3g5s"]
[node name="ZooCountryside" type="Node3D" unique_id=1742985179]
[node name="country" type="Node3D" parent="." unique_id=672430814]
[node name="chunk_country_corner_01" parent="country" unique_id=200417581 instance=ExtResource("1_eexnc")]
[node name="chunk_country_corner_02" parent="country" unique_id=85657005 instance=ExtResource("2_jh6kr")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 22.5)
[node name="chunk_country_corner_03" parent="country" unique_id=1046737870 instance=ExtResource("3_pftht")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 45)
[node name="chunk_country_cross3_01" parent="country" unique_id=319840865 instance=ExtResource("4_jexg3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -22.5, 0, 0)
[node name="chunk_country_cross3_02" parent="country" unique_id=1492245999 instance=ExtResource("5_hieqo")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -22.5, 0, 22.5)
[node name="chunk_country_cross3_03" parent="country" unique_id=643218732 instance=ExtResource("6_re3ik")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -22.5, 0, 45)
[node name="chunk_country_cross4_01" parent="country" unique_id=680431911 instance=ExtResource("7_v47u4")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45, 0, 0)
[node name="chunk_country_empty_01" parent="country" unique_id=385761952 instance=ExtResource("8_bag0j")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -67.5, 0, 0)
[node name="chunk_country_end_01" parent="country" unique_id=913263516 instance=ExtResource("9_1vyrv")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -90, 0, 0)
[node name="chunk_country_end_02" parent="country" unique_id=1712924087 instance=ExtResource("10_sdnjr")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -90, 0, 22.5)
[node name="chunk_country_straight_01" parent="country" unique_id=926368352 instance=ExtResource("11_arwwt")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -113, 0, 0)
[node name="chunk_country_straight_02" parent="country" unique_id=1847746456 instance=ExtResource("12_l62i2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -113, 0, 22.5)
[node name="chunk_country_straight_03" parent="country" unique_id=626808879 instance=ExtResource("13_f3g5s")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -113, 0, 45)

Some files were not shown because too many files have changed in this diff Show More