Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2358dc8b53 | |||
| 185b1b4744 | |||
| e86da0c2bc | |||
| 5341fdd3f5 | |||
|
|
d58c31ac65 | ||
|
|
48ba1761e0 | ||
|
|
0ce2cc6b04 | ||
| 8ff14f3455 | |||
| 6da7021a58 | |||
| b87be91521 | |||
|
|
9f3acbccf9 | ||
| 4aaf8e3fd3 | |||
| dc76181cc0 | |||
| 0f7bb6308a | |||
| a944492dbd | |||
| 15f38c096b | |||
| c296531214 | |||
| bdca270dca | |||
| d47bc26583 | |||
|
|
cc7f819628 | ||
| 802a7ef7c1 | |||
| 3e2739cf02 | |||
| 7be800398d | |||
| 7db1df9ab6 | |||
| 8198f7df1f | |||
| 952058b0e0 | |||
| 475749c4df | |||
| 086167a5a6 | |||
| 885bac9a4a | |||
| 496a271946 | |||
| d0d052f65b | |||
| 67a99051bd | |||
| fb848a9848 | |||
| bc30a169dc | |||
| f5576959b7 | |||
| b8ab8f4971 | |||
| 22653d62bc | |||
| 6a2a2d2e8a |
BIN
addons/PageFlip/Assets/back.png
Normal file
|
After Width: | Height: | Size: 766 KiB |
@@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://d4e1lbng4uqk"
|
uid="uid://bjowpx1ap4uxt"
|
||||||
path="res://.godot/imported/quad_main.png-af26b29d5c3350fc4d75e08ad350ccc7.ctex"
|
path="res://.godot/imported/back.png-bddbf3418b746eb53bc79fd4d0989d41.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://core/game_menu/assets/page_3/quad_main.png"
|
source_file="res://addons/PageFlip/Assets/back.png"
|
||||||
dest_files=["res://.godot/imported/quad_main.png-af26b29d5c3350fc4d75e08ad350ccc7.ctex"]
|
dest_files=["res://.godot/imported/back.png-bddbf3418b746eb53bc79fd4d0989d41.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
BIN
addons/PageFlip/Assets/blank_page.png
Normal file
|
After Width: | Height: | Size: 300 KiB |
40
addons/PageFlip/Assets/blank_page.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cen51wqc15b14"
|
||||||
|
path="res://.godot/imported/blank_page.png-589b6dfce0585742cc1d305c391c9cd9.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/PageFlip/Assets/blank_page.png"
|
||||||
|
dest_files=["res://.godot/imported/blank_page.png-589b6dfce0585742cc1d305c391c9cd9.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
addons/PageFlip/Assets/book_impact.ogg
Normal file
19
addons/PageFlip/Assets/book_impact.ogg.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="oggvorbisstr"
|
||||||
|
type="AudioStreamOggVorbis"
|
||||||
|
uid="uid://bhitebdghyhua"
|
||||||
|
path="res://.godot/imported/book_impact.ogg-ae2f194921fcd1bc549a3ddb37fab4ba.oggvorbisstr"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/PageFlip/Assets/book_impact.ogg"
|
||||||
|
dest_files=["res://.godot/imported/book_impact.ogg-ae2f194921fcd1bc549a3ddb37fab4ba.oggvorbisstr"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
addons/PageFlip/Assets/front.png
Normal file
|
After Width: | Height: | Size: 765 KiB |
40
addons/PageFlip/Assets/front.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dbdwbowx32d3v"
|
||||||
|
path="res://.godot/imported/front.png-76dff7071666496195badceef4e0ce67.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/PageFlip/Assets/front.png"
|
||||||
|
dest_files=["res://.godot/imported/front.png-76dff7071666496195badceef4e0ce67.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
addons/PageFlip/Assets/interior.png
Normal file
|
After Width: | Height: | Size: 772 KiB |
40
addons/PageFlip/Assets/interior.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dt1tiecgw5rip"
|
||||||
|
path="res://.godot/imported/interior.png-d4b06daf084fdb8a90836ec57d2dd53f.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/PageFlip/Assets/interior.png"
|
||||||
|
dest_files=["res://.godot/imported/interior.png-d4b06daf084fdb8a90836ec57d2dd53f.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
addons/PageFlip/Assets/page_flip.ogg
Normal file
19
addons/PageFlip/Assets/page_flip.ogg.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="oggvorbisstr"
|
||||||
|
type="AudioStreamOggVorbis"
|
||||||
|
uid="uid://bylfc3b5pmbij"
|
||||||
|
path="res://.godot/imported/page_flip.ogg-aabb6ae51ff161ae6afa52da11fe75de.oggvorbisstr"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/PageFlip/Assets/page_flip.ogg"
|
||||||
|
dest_files=["res://.godot/imported/page_flip.ogg-aabb6ae51ff161ae6afa52da11fe75de.oggvorbisstr"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
49
addons/PageFlip/Assets/page_flip_final.gdshader
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
uniform sampler2D front_texture : source_color, filter_linear_mipmap;
|
||||||
|
uniform sampler2D back_texture : source_color, filter_linear_mipmap;
|
||||||
|
|
||||||
|
uniform sampler2D spine_shadow_gradient : source_color, filter_linear, repeat_disable;
|
||||||
|
uniform float shadow_intensity : hint_range(0.0, 1.0) = 0.0;
|
||||||
|
uniform float max_shadow_spread : hint_range(0.0, 1.0) = 0.35;
|
||||||
|
|
||||||
|
uniform bool debug_mode = false;
|
||||||
|
uniform bool is_backward = false;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
vec2 uv_dx = dFdx(UV);
|
||||||
|
vec2 uv_dy = dFdy(UV);
|
||||||
|
float face_dir = uv_dx.x * uv_dy.y - uv_dx.y * uv_dy.x;
|
||||||
|
bool is_front = face_dir > 0.0;
|
||||||
|
|
||||||
|
vec4 base_color;
|
||||||
|
|
||||||
|
if (is_front) {
|
||||||
|
if (is_backward) {
|
||||||
|
// Let's try flipping Y just in case they meant upside down!
|
||||||
|
// And keep X normal, since UV goes 0 to 1.
|
||||||
|
base_color = texture(front_texture, UV);
|
||||||
|
} else {
|
||||||
|
base_color = texture(front_texture, UV);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_backward) {
|
||||||
|
base_color = texture(back_texture, vec2(1.0 - UV.x, UV.y));
|
||||||
|
} else {
|
||||||
|
base_color = texture(back_texture, vec2(1.0 - UV.x, UV.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float current_spread = max_shadow_spread * (shadow_intensity + 0.001);
|
||||||
|
float sample_x = UV.x / current_spread;
|
||||||
|
float shadow_alpha = texture(spine_shadow_gradient, vec2(sample_x, 0.0)).a;
|
||||||
|
|
||||||
|
if (sample_x > 1.0) {
|
||||||
|
shadow_alpha = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
shadow_alpha *= shadow_intensity;
|
||||||
|
|
||||||
|
vec3 final_rgb = mix(base_color.rgb, vec3(0.0, 0.0, 0.0), shadow_alpha);
|
||||||
|
COLOR = vec4(final_rgb, base_color.a);
|
||||||
|
}
|
||||||
1
addons/PageFlip/Assets/page_flip_final.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b57sdbj15qx37
|
||||||
9
addons/PageFlip/Assets/page_flip_shader_material.tres
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://ddw2ie8wnrnre"]
|
||||||
|
|
||||||
|
[ext_resource type="Shader" uid="uid://b57sdbj15qx37" path="res://addons/PageFlip/Assets/page_flip_final.gdshader" id="1_cl0rr"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
shader = ExtResource("1_cl0rr")
|
||||||
|
shader_parameter/shadow_intensity = 0.0
|
||||||
|
shader_parameter/max_shadow_spread = 0.35
|
||||||
|
shader_parameter/debug_mode = false
|
||||||
8
addons/PageFlip/Assets/shadow_gradient.tres
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[gd_resource type="GradientTexture2D" format=3 uid="uid://rm7bporlv3cf"]
|
||||||
|
|
||||||
|
[sub_resource type="Gradient" id="Gradient_3i6i7"]
|
||||||
|
offsets = PackedFloat32Array(0, 0.2731183, 0.44731182, 1)
|
||||||
|
colors = PackedColorArray(0, 0, 0, 0.521, 0, 0, 0, 0.492, 0, 0, 0, 0.358, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
gradient = SubResource("Gradient_3i6i7")
|
||||||
BIN
addons/PageFlip/Assets/spine.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
40
addons/PageFlip/Assets/spine.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cit41jypw2sy1"
|
||||||
|
path="res://.godot/imported/spine.png-c2fa10bef4996b84a954651c908149a7.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/PageFlip/Assets/spine.png"
|
||||||
|
dest_files=["res://.godot/imported/spine.png-c2fa10bef4996b84a954651c908149a7.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
229
addons/PageFlip/book_api.gd
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
@tool
|
||||||
|
class_name BookAPI
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
## Static utility class for the PageFlip2D system by Newold.
|
||||||
|
## Provides helper methods to configure the book and facilitate interaction
|
||||||
|
## from embedded scenes (UI, puzzles, maps).
|
||||||
|
##
|
||||||
|
## [b]How to use:[/b][br]
|
||||||
|
## This class is designed to be used statically. You do not need to instance it.
|
||||||
|
## Simply call [code]BookAPI.function_name()[/code] from anywhere in your project.
|
||||||
|
## It automatically tracks the active [PageFlip2D] instance.
|
||||||
|
## (When a PageFlip scene enters the tree, it registers itself as the current_book.)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# ENUMS (Mirrored for easy access)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
enum JumpTarget {
|
||||||
|
## Jump to the closed state showing the Front Cover.
|
||||||
|
FRONT_COVER,
|
||||||
|
## Jump to the closed state showing the Back Cover.
|
||||||
|
BACK_COVER,
|
||||||
|
## Jump to a specific content page number.
|
||||||
|
CONTENT_PAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
# Internal reference to the currently active book.
|
||||||
|
static var _current_book: PageFlip2D
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SETUP & REFERENCES
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
## Registers a book instance as the currently active one.
|
||||||
|
## Automatically called by [PageFlip2D] in its [method Node._ready].
|
||||||
|
static func set_current_book(book: PageFlip2D) -> void:
|
||||||
|
_current_book = book
|
||||||
|
|
||||||
|
|
||||||
|
## Returns the currently active book instance, or null if none is registered.
|
||||||
|
static func get_current_book() -> PageFlip2D:
|
||||||
|
return _current_book
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# NAVIGATION CONTROLS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
## Turns the page forward (to the next page/spread).
|
||||||
|
## Does nothing if the book is animating or at the end.
|
||||||
|
static func next_page() -> void:
|
||||||
|
if is_instance_valid(_current_book):
|
||||||
|
force_release_control(_current_book)
|
||||||
|
_current_book.next_page()
|
||||||
|
|
||||||
|
|
||||||
|
## Turns the page backward (to the previous page/spread).
|
||||||
|
## Does nothing if the book is animating or at the beginning.
|
||||||
|
static func prev_page() -> void:
|
||||||
|
if is_instance_valid(_current_book):
|
||||||
|
force_release_control(_current_book)
|
||||||
|
_current_book.prev_page()
|
||||||
|
|
||||||
|
|
||||||
|
## Forces the active book to close towards a specific cover.
|
||||||
|
## [param to_front_cover]: If true, closes to the Front Cover (Right to Left). If false, closes to Back Cover.
|
||||||
|
static func force_close_book(to_front_cover: bool) -> void:
|
||||||
|
if is_instance_valid(_current_book):
|
||||||
|
force_release_control(_current_book)
|
||||||
|
_current_book.force_close_book(to_front_cover)
|
||||||
|
|
||||||
|
|
||||||
|
## Navigates to a specific page number (1-based index).
|
||||||
|
## Acts as a wrapper for go_to_spread, calculating the correct index automatically.
|
||||||
|
## [b]ASYNC:[/b] Must be called with 'await' if animated is true.
|
||||||
|
## [param page_num]: The 1-based page number (1 = first texture in pages_paths).
|
||||||
|
## [param target]: Specifies if the target is a content page or a cover.
|
||||||
|
static func go_to_page(page_num: int = 1, target: JumpTarget = JumpTarget.CONTENT_PAGE, animated: bool = true) -> void:
|
||||||
|
var book = _current_book
|
||||||
|
if not is_instance_valid(book): return
|
||||||
|
|
||||||
|
var target_spread_idx: int = 0
|
||||||
|
|
||||||
|
match target:
|
||||||
|
JumpTarget.FRONT_COVER:
|
||||||
|
target_spread_idx = -1
|
||||||
|
JumpTarget.BACK_COVER:
|
||||||
|
target_spread_idx = book.total_spreads
|
||||||
|
JumpTarget.CONTENT_PAGE:
|
||||||
|
# Calculate spread index: 1-2 -> Spread 0, 3-4 -> Spread 1, etc.
|
||||||
|
var safe_page = max(1, page_num)
|
||||||
|
target_spread_idx = int(safe_page / 2.0)
|
||||||
|
target_spread_idx = clampi(target_spread_idx, 0, book.total_spreads - 1)
|
||||||
|
|
||||||
|
await go_to_spread(book, target_spread_idx, animated)
|
||||||
|
|
||||||
|
|
||||||
|
## Navigates to a specific spread index directly.
|
||||||
|
## [b]ASYNC:[/b] Must be called with 'await' if animated is true.
|
||||||
|
## - Animated: Fast-forwards through pages with dynamic speed.
|
||||||
|
## - Instant: Snaps to page and manually triggers the scene activation handshake.
|
||||||
|
static func go_to_spread(book: PageFlip2D, target_spread: int, animated: bool = true) -> void:
|
||||||
|
if not is_instance_valid(book): return
|
||||||
|
|
||||||
|
# Clamp target
|
||||||
|
var final_target = clampi(target_spread, -1, book.total_spreads)
|
||||||
|
var diff = final_target - book.current_spread
|
||||||
|
|
||||||
|
if diff == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
force_release_control(book)
|
||||||
|
|
||||||
|
if not animated:
|
||||||
|
# INSTANT TELEPORT
|
||||||
|
book.current_spread = final_target
|
||||||
|
book.call("_update_static_visuals_immediate")
|
||||||
|
book.call("_update_volume_visuals")
|
||||||
|
# Force check for interactive scenes after teleport
|
||||||
|
book.call("_check_scene_activation")
|
||||||
|
|
||||||
|
else:
|
||||||
|
# ANIMATED FAST-FORWARD
|
||||||
|
if book.is_animating: return # Don't interrupt an existing animation
|
||||||
|
|
||||||
|
var original_speed = book.anim_player.speed_scale
|
||||||
|
|
||||||
|
# --- DYNAMIC SPEED CALCULATION ---
|
||||||
|
var steps = abs(diff)
|
||||||
|
# Base speed: 1.5x -> Max speed: 10.0x
|
||||||
|
var dynamic_speed = remap(float(steps), 0.0, float(book.total_spreads), 1.5, 10.0)
|
||||||
|
book.anim_player.speed_scale = dynamic_speed
|
||||||
|
|
||||||
|
var going_forward = diff > 0
|
||||||
|
|
||||||
|
for i in range(steps):
|
||||||
|
if not is_instance_valid(book): break
|
||||||
|
|
||||||
|
if going_forward: book.next_page()
|
||||||
|
else: book.prev_page()
|
||||||
|
|
||||||
|
# Wait for the physical page turn to finish before starting the next one.
|
||||||
|
if book.anim_player.is_playing():
|
||||||
|
await book.anim_player.animation_finished
|
||||||
|
else:
|
||||||
|
await book.get_tree().process_frame
|
||||||
|
|
||||||
|
# RESTORE STATE
|
||||||
|
if is_instance_valid(book):
|
||||||
|
book.anim_player.speed_scale = original_speed
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# STATE & INTERACTION HELPERS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
## Returns true if the book (active or specific) is currently animating.
|
||||||
|
static func is_busy(book_instance: PageFlip2D = null) -> bool:
|
||||||
|
var book = book_instance if book_instance else _current_book
|
||||||
|
if not is_instance_valid(book): return false
|
||||||
|
return book.is_animating
|
||||||
|
|
||||||
|
|
||||||
|
## Locates the PageFlip2D controller ancestor from any node inside an interactive page.
|
||||||
|
static func find_book_controller(caller_node: Node) -> PageFlip2D:
|
||||||
|
var current = caller_node
|
||||||
|
while current:
|
||||||
|
if current is PageFlip2D:
|
||||||
|
return current
|
||||||
|
current = current.get_parent()
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
## Safely locks or unlocks the book's ability to turn pages manually.
|
||||||
|
## WARNING: If locked, the interactive scene MUST be responsible for unlocking it later.
|
||||||
|
static func set_interaction_lock(book: PageFlip2D, locked: bool) -> void:
|
||||||
|
if not is_instance_valid(book): return
|
||||||
|
book.call("_pageflip_set_input_enabled", not locked)
|
||||||
|
|
||||||
|
|
||||||
|
## Forces the book to regain input control immediately.
|
||||||
|
## Useful as a failsafe if an interactive scene closes unexpectedly.
|
||||||
|
static func force_release_control(book: PageFlip2D) -> void:
|
||||||
|
if not is_instance_valid(book): return
|
||||||
|
book.call("_pageflip_set_input_enabled", true)
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# CONFIGURATION HELPERS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
## Configures the visual properties of a Book instance via a dictionary.
|
||||||
|
static func configure_visuals(book: PageFlip2D, data: Dictionary) -> void:
|
||||||
|
if not is_instance_valid(book): return
|
||||||
|
|
||||||
|
if "pages" in data:
|
||||||
|
book.pages_paths = data["pages"]
|
||||||
|
book.call("_prepare_book_content")
|
||||||
|
|
||||||
|
if "cover_front_out" in data: book.tex_cover_front_out = data["cover_front_out"]
|
||||||
|
if "cover_front_in" in data: book.tex_cover_front_in = data["cover_front_in"]
|
||||||
|
if "cover_back_in" in data: book.tex_cover_back_in = data["cover_back_in"]
|
||||||
|
if "cover_back_out" in data: book.tex_cover_back_out = data["cover_back_out"]
|
||||||
|
|
||||||
|
if "spine_col" in data: book.spine_color = data["spine_col"]
|
||||||
|
if "spine_width" in data:
|
||||||
|
book.spine_width = data["spine_width"]
|
||||||
|
book.call("_build_spine")
|
||||||
|
|
||||||
|
if "size" in data:
|
||||||
|
book.target_page_size = data["size"]
|
||||||
|
book.call("_apply_new_size")
|
||||||
|
|
||||||
|
book.call("_update_static_visuals_immediate")
|
||||||
|
book.call("_update_volume_visuals")
|
||||||
|
|
||||||
|
|
||||||
|
## Configures the physics simulation of the page turning effect.
|
||||||
|
static func configure_physics(book: PageFlip2D, data: Dictionary) -> void:
|
||||||
|
if not is_instance_valid(book) or not book.dynamic_poly: return
|
||||||
|
|
||||||
|
var rigger = book.dynamic_poly
|
||||||
|
for key in data.keys():
|
||||||
|
rigger.set(key, data[key])
|
||||||
|
|
||||||
|
if rigger.has_method("rebuild"):
|
||||||
|
rigger.rebuild(book.target_page_size)
|
||||||
1
addons/PageFlip/book_api.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c2dx1dksgeey6
|
||||||
1416
addons/PageFlip/page_flip.gd
Normal file
1
addons/PageFlip/page_flip.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ctw83iqgqxjjt
|
||||||
1980
addons/PageFlip/page_flip.tscn
Normal file
587
addons/PageFlip/page_rigger.gd
Normal file
@@ -0,0 +1,587 @@
|
|||||||
|
@tool
|
||||||
|
class_name DynamicPage
|
||||||
|
extends Polygon2D
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# ENUMS & CONSTANTS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
## Defines the material physical properties for the page simulation.
|
||||||
|
enum PagePreset {
|
||||||
|
CUSTOM,
|
||||||
|
DEFAULT,
|
||||||
|
STANDARD_PAPER,
|
||||||
|
HEAVY_GRIMOIRE,
|
||||||
|
LIGHT_MAGAZINE,
|
||||||
|
OLD_SCROLL,
|
||||||
|
RIGID_BOARD,
|
||||||
|
WET_CLOTH,
|
||||||
|
PLASTIC_SHEET,
|
||||||
|
METAL_PLATE
|
||||||
|
}
|
||||||
|
|
||||||
|
## Determines the direction of the page turn curve.
|
||||||
|
enum CurlMode {
|
||||||
|
STRAIGHT,
|
||||||
|
TOP_CORNER_FIRST,
|
||||||
|
BOTTOM_CORNER_FIRST
|
||||||
|
}
|
||||||
|
|
||||||
|
const SKELETON_NAME = "AutoSkeleton"
|
||||||
|
const SHADOW_NAME = "AutoShadow"
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SIGNALS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
signal change_page_requested
|
||||||
|
signal end_animation
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# INTERNAL CACHE
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
var _custom_cache: Dictionary = {}
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# CONFIGURATION
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
@export_category("Rig Generator")
|
||||||
|
|
||||||
|
## Horizontal mesh subdivision. Higher values create smoother bends.
|
||||||
|
@export_range(1, 20) var subdivision_x: int = 8
|
||||||
|
## Vertical mesh subdivision. Higher values allow for better corner curling.
|
||||||
|
@export_range(1, 10) var subdivision_y: int = 5
|
||||||
|
## Generates the mesh, skeleton, weights and animations.
|
||||||
|
@export var rebuild_all: bool = false : set = _on_generate_pressed
|
||||||
|
|
||||||
|
|
||||||
|
@export_category("Animation Generator")
|
||||||
|
|
||||||
|
@export_group("Quick Presets")
|
||||||
|
## Select a material style to auto-configure physics.
|
||||||
|
@export var animation_preset: PagePreset = PagePreset.CUSTOM : set = _on_preset_changed
|
||||||
|
|
||||||
|
|
||||||
|
@export_group("Manual Configuration")
|
||||||
|
## The AnimationPlayer node to store the generated animations.
|
||||||
|
@export var anim_player: AnimationPlayer
|
||||||
|
## Duration of the page turn animation in seconds.
|
||||||
|
@export var anim_duration: float = 0.75
|
||||||
|
|
||||||
|
|
||||||
|
@export_subgroup("Paper Physics")
|
||||||
|
## Stiffness of the material. 0.5 = Rubber, 5.0 = Wood.
|
||||||
|
@export_range(0.5, 5.0, 0.1) var paper_stiffness: float = 2.0
|
||||||
|
## Bend angle during the lift phase (Negative = Tip drags down).
|
||||||
|
@export_range(-180.0, 180.0) var lift_bend: float = -10.0
|
||||||
|
## Bend angle during the landing phase (Negative = Tip floats up).
|
||||||
|
@export_range(-180.0, 180.0) var land_bend: float = -8.0
|
||||||
|
|
||||||
|
|
||||||
|
@export_subgroup("Curl Effect")
|
||||||
|
## Which part of the page initiates the movement.
|
||||||
|
@export var curl_mode: CurlMode = CurlMode.TOP_CORNER_FIRST
|
||||||
|
## Time delay for the trailing corner (0.0 = None, 1.0 = High lag).
|
||||||
|
@export_range(0.0, 1.0, 0.05) var curl_lag: float = 0.8
|
||||||
|
|
||||||
|
|
||||||
|
@export_subgroup("Shadow FX")
|
||||||
|
## Create a dynamic shadow behind the page (Background Shadow).
|
||||||
|
@export var enable_shadow: bool = false
|
||||||
|
## The gradient texture used for the back shadow.
|
||||||
|
@export var shadow_gradient: GradientTexture2D
|
||||||
|
|
||||||
|
|
||||||
|
@export_subgroup("Fine Timing")
|
||||||
|
## Normalized time (0.0 - 1.0) for the peak lift position.
|
||||||
|
@export_range(0.05, 0.45, 0.01) var timing_peak_lift: float = 0.15
|
||||||
|
## Normalized time (0.0 - 1.0) for the landing contact.
|
||||||
|
@export_range(0.55, 0.95, 0.01) var timing_peak_land: float = 0.85
|
||||||
|
|
||||||
|
@export var generate_anims_btn: bool = false : set = _on_anim_pressed
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# RUNTIME INITIALIZATION
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
self.z_index = 10
|
||||||
|
if animation_preset == PagePreset.CUSTOM:
|
||||||
|
_save_state_to_cache()
|
||||||
|
|
||||||
|
|
||||||
|
func rebuild(current_page_size: Vector2 = Vector2.ZERO) -> void:
|
||||||
|
if not anim_player: return
|
||||||
|
_clean_previous_rig()
|
||||||
|
_create_rig_logic(current_page_size)
|
||||||
|
_generate_animations_logic()
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# PRESET LOGIC
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
func _on_preset_changed(val):
|
||||||
|
if animation_preset == PagePreset.CUSTOM:
|
||||||
|
_save_state_to_cache()
|
||||||
|
|
||||||
|
animation_preset = val
|
||||||
|
|
||||||
|
if val == PagePreset.CUSTOM:
|
||||||
|
if not _custom_cache.is_empty():
|
||||||
|
_load_state_from_cache()
|
||||||
|
print("[PageRigger] Restored Custom Settings.")
|
||||||
|
notify_property_list_changed()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
curl_mode = CurlMode.BOTTOM_CORNER_FIRST
|
||||||
|
curl_lag = 0.3
|
||||||
|
enable_shadow = false
|
||||||
|
anim_duration = 0.75
|
||||||
|
|
||||||
|
match val:
|
||||||
|
PagePreset.DEFAULT:
|
||||||
|
paper_stiffness = 2.0; lift_bend = -15.0; land_bend = 5.0
|
||||||
|
curl_mode = CurlMode.TOP_CORNER_FIRST; curl_lag = 0.5
|
||||||
|
anim_duration = 0.75; timing_peak_lift = 0.15; timing_peak_land = 0.85
|
||||||
|
|
||||||
|
PagePreset.STANDARD_PAPER:
|
||||||
|
paper_stiffness = 2.5; lift_bend = -12.0; land_bend = 3.0
|
||||||
|
curl_mode = CurlMode.TOP_CORNER_FIRST; curl_lag = 0.4
|
||||||
|
anim_duration = 0.65; timing_peak_lift = 0.12; timing_peak_land = 0.88
|
||||||
|
|
||||||
|
PagePreset.HEAVY_GRIMOIRE:
|
||||||
|
paper_stiffness = 4.0; lift_bend = -3.0; land_bend = 1.0
|
||||||
|
curl_mode = CurlMode.STRAIGHT; curl_lag = 0.1
|
||||||
|
anim_duration = 1.1; timing_peak_lift = 0.25; timing_peak_land = 0.75
|
||||||
|
|
||||||
|
PagePreset.LIGHT_MAGAZINE:
|
||||||
|
paper_stiffness = 0.8; lift_bend = -16.0; land_bend = 0.0
|
||||||
|
curl_mode = CurlMode.BOTTOM_CORNER_FIRST; curl_lag = 0.6
|
||||||
|
anim_duration = 0.8; timing_peak_lift = 0.10; timing_peak_land = 0.92
|
||||||
|
|
||||||
|
PagePreset.OLD_SCROLL:
|
||||||
|
paper_stiffness = 1.8; lift_bend = -20.0; land_bend = 8.0
|
||||||
|
curl_mode = CurlMode.TOP_CORNER_FIRST; curl_lag = 0.9
|
||||||
|
anim_duration = 0.9; timing_peak_lift = 0.18; timing_peak_land = 0.82
|
||||||
|
|
||||||
|
PagePreset.RIGID_BOARD:
|
||||||
|
paper_stiffness = 5.0; lift_bend = 0.0; land_bend = 0.0
|
||||||
|
curl_mode = CurlMode.STRAIGHT; curl_lag = 0.0
|
||||||
|
anim_duration = 0.8; timing_peak_lift = 0.2; timing_peak_land = 0.8
|
||||||
|
|
||||||
|
PagePreset.WET_CLOTH:
|
||||||
|
paper_stiffness = 0.3; lift_bend = -28.0; land_bend = -0.0
|
||||||
|
curl_mode = CurlMode.BOTTOM_CORNER_FIRST; curl_lag = 0.6
|
||||||
|
anim_duration = 1.3; timing_peak_lift = 0.2; timing_peak_land = 0.95
|
||||||
|
|
||||||
|
PagePreset.PLASTIC_SHEET:
|
||||||
|
paper_stiffness = 3.5; lift_bend = -12.0; land_bend = 12.0
|
||||||
|
curl_mode = CurlMode.TOP_CORNER_FIRST; curl_lag = 0.2
|
||||||
|
anim_duration = 0.5; timing_peak_lift = 0.08; timing_peak_land = 0.92
|
||||||
|
|
||||||
|
PagePreset.METAL_PLATE:
|
||||||
|
paper_stiffness = 5.0; lift_bend = 0.0; land_bend = 0.0
|
||||||
|
curl_mode = CurlMode.STRAIGHT; curl_lag = 0.0
|
||||||
|
anim_duration = 1.4; timing_peak_lift = 0.15; timing_peak_land = 0.98
|
||||||
|
|
||||||
|
notify_property_list_changed()
|
||||||
|
if anim_player and skeleton != NodePath(""):
|
||||||
|
_generate_animations_logic()
|
||||||
|
print("[PageRigger] Preset Applied: ", PagePreset.keys()[val])
|
||||||
|
|
||||||
|
|
||||||
|
func _save_state_to_cache():
|
||||||
|
_custom_cache = {
|
||||||
|
"stiffness": paper_stiffness, "lift": lift_bend, "land": land_bend,
|
||||||
|
"curl_m": curl_mode, "curl_l": curl_lag,
|
||||||
|
"t_lift": timing_peak_lift, "t_land": timing_peak_land,
|
||||||
|
"dur": anim_duration, "shadow": enable_shadow
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _load_state_from_cache():
|
||||||
|
paper_stiffness = _custom_cache.get("stiffness", 1.5)
|
||||||
|
lift_bend = _custom_cache.get("lift", -30.0)
|
||||||
|
land_bend = _custom_cache.get("land", -15.0)
|
||||||
|
curl_mode = _custom_cache.get("curl_m", CurlMode.BOTTOM_CORNER_FIRST)
|
||||||
|
curl_lag = _custom_cache.get("curl_l", 0.3)
|
||||||
|
timing_peak_lift = _custom_cache.get("t_lift", 0.15)
|
||||||
|
timing_peak_land = _custom_cache.get("t_land", 0.85)
|
||||||
|
anim_duration = _custom_cache.get("dur", 1.0)
|
||||||
|
enable_shadow = false # Force disable shadow
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# RIGGING LOGIC
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
func _calculate_polygon_rect() -> Rect2:
|
||||||
|
if polygon.size() == 0:
|
||||||
|
return Rect2(0,0,0,0)
|
||||||
|
|
||||||
|
var min_v = polygon[0]
|
||||||
|
var max_v = polygon[0]
|
||||||
|
|
||||||
|
for v in polygon:
|
||||||
|
min_v.x = min(min_v.x, v.x)
|
||||||
|
min_v.y = min(min_v.y, v.y)
|
||||||
|
max_v.x = max(max_v.x, v.x)
|
||||||
|
max_v.y = max(max_v.y, v.y)
|
||||||
|
|
||||||
|
return Rect2(min_v, max_v - min_v)
|
||||||
|
|
||||||
|
|
||||||
|
func _create_rig_logic(current_page_size: Vector2 = Vector2.ZERO):
|
||||||
|
self.z_index = 10
|
||||||
|
|
||||||
|
var original_size = Vector2(512, 820)
|
||||||
|
var tex_size: Vector2 = Vector2.ZERO
|
||||||
|
if current_page_size != Vector2.ZERO:
|
||||||
|
tex_size = current_page_size
|
||||||
|
elif texture:
|
||||||
|
tex_size = texture.get_size()
|
||||||
|
if tex_size == Vector2.ZERO:
|
||||||
|
tex_size = original_size
|
||||||
|
|
||||||
|
var step_x = tex_size.x / subdivision_x
|
||||||
|
var step_y = tex_size.y / subdivision_y
|
||||||
|
|
||||||
|
# Prepare Gradient Texture for Background Shadow
|
||||||
|
var applied_shadow_tex: Texture2D = null
|
||||||
|
if not shadow_gradient:
|
||||||
|
shadow_gradient = preload("uid://rm7bporlv3cf")
|
||||||
|
var dup = shadow_gradient.duplicate()
|
||||||
|
if dup is GradientTexture2D:
|
||||||
|
dup.width = int(tex_size.x)
|
||||||
|
applied_shadow_tex = dup
|
||||||
|
|
||||||
|
# --- 0. BACKGROUND SHADOW ---
|
||||||
|
if enable_shadow:
|
||||||
|
var shadow = Polygon2D.new()
|
||||||
|
shadow.name = SHADOW_NAME
|
||||||
|
shadow.z_index = -1
|
||||||
|
|
||||||
|
if applied_shadow_tex:
|
||||||
|
shadow.texture = applied_shadow_tex
|
||||||
|
shadow.color = Color.WHITE
|
||||||
|
else:
|
||||||
|
shadow.color = Color(0, 0, 0, 0.5)
|
||||||
|
|
||||||
|
var margin = 4.0
|
||||||
|
shadow.polygon = PackedVector2Array([
|
||||||
|
Vector2(margin, margin), Vector2(tex_size.x - margin, margin),
|
||||||
|
Vector2(tex_size.x - margin, tex_size.y - margin), Vector2(margin, tex_size.y - margin)
|
||||||
|
])
|
||||||
|
shadow.uv = shadow.polygon
|
||||||
|
add_child(shadow)
|
||||||
|
|
||||||
|
# --- 1. MESH & UV ---
|
||||||
|
var new_uvs = PackedVector2Array()
|
||||||
|
for y in range(subdivision_y + 1):
|
||||||
|
for x in range(subdivision_x + 1):
|
||||||
|
new_uvs.append(Vector2(x * step_x, y * step_y))
|
||||||
|
self.uv = new_uvs
|
||||||
|
self.polygon = new_uvs
|
||||||
|
|
||||||
|
var new_polygons = []
|
||||||
|
var rc = subdivision_x + 1
|
||||||
|
for y in range(subdivision_y):
|
||||||
|
for x in range(subdivision_x):
|
||||||
|
var i = y * rc + x
|
||||||
|
new_polygons.append(PackedInt32Array([i, i + 1, i + rc + 1, i + rc]))
|
||||||
|
self.polygons = new_polygons
|
||||||
|
|
||||||
|
# --- 2. SKELETON ---
|
||||||
|
var sk = Skeleton2D.new()
|
||||||
|
sk.name = SKELETON_NAME
|
||||||
|
add_child(sk)
|
||||||
|
self.skeleton = NodePath(SKELETON_NAME)
|
||||||
|
|
||||||
|
# --- 3. BONES (Structured Hierarchy) ---
|
||||||
|
for y in range(subdivision_y + 1):
|
||||||
|
var parent_bone: Bone2D = null
|
||||||
|
|
||||||
|
for x in range(subdivision_x + 1):
|
||||||
|
var b = Bone2D.new()
|
||||||
|
b.name = "Bone_%d_%d" % [x, y]
|
||||||
|
b.set_autocalculate_length_and_angle(false)
|
||||||
|
b.set_length(step_x)
|
||||||
|
|
||||||
|
if x == 0:
|
||||||
|
# Root bone (Spine)
|
||||||
|
sk.add_child(b)
|
||||||
|
b.position = Vector2(0, y * step_y)
|
||||||
|
else:
|
||||||
|
# Child bone
|
||||||
|
if parent_bone:
|
||||||
|
parent_bone.add_child(b)
|
||||||
|
b.position = Vector2(step_x, 0)
|
||||||
|
|
||||||
|
b.set_rest(b.transform)
|
||||||
|
parent_bone = b
|
||||||
|
|
||||||
|
# --- 4. WEIGHTS (Gradient Skinning) ---
|
||||||
|
_apply_weights_to_polygon(self, sk)
|
||||||
|
|
||||||
|
queue_redraw()
|
||||||
|
notify_property_list_changed()
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_weights_to_polygon(poly: Polygon2D, sk: Skeleton2D):
|
||||||
|
poly.clear_bones()
|
||||||
|
|
||||||
|
var bone_map = {}
|
||||||
|
for y in range(subdivision_y + 1):
|
||||||
|
for x in range(subdivision_x + 1):
|
||||||
|
var bname = "Bone_%d_%d" % [x, y]
|
||||||
|
var bone_node = sk.find_child(bname, true, false)
|
||||||
|
if bone_node:
|
||||||
|
bone_map[bname] = bone_node.get_path()
|
||||||
|
|
||||||
|
var vertex_count = poly.polygon.size()
|
||||||
|
|
||||||
|
var weights_per_bone = {}
|
||||||
|
|
||||||
|
for bpath in bone_map.values():
|
||||||
|
var w_arr = PackedFloat32Array()
|
||||||
|
w_arr.resize(vertex_count)
|
||||||
|
w_arr.fill(0.0)
|
||||||
|
weights_per_bone[bpath] = w_arr
|
||||||
|
|
||||||
|
var vert_idx = 0
|
||||||
|
for y in range(subdivision_y + 1):
|
||||||
|
for x in range(subdivision_x + 1):
|
||||||
|
var primary_name = "Bone_%d_%d" % [x, y]
|
||||||
|
var prev_name = "Bone_%d_%d" % [x - 1, y]
|
||||||
|
var next_name = "Bone_%d_%d" % [x + 1, y]
|
||||||
|
|
||||||
|
var primary_path = bone_map.get(primary_name)
|
||||||
|
var prev_path = bone_map.get(prev_name)
|
||||||
|
var next_path = bone_map.get(next_name)
|
||||||
|
|
||||||
|
var w_center = 0.7
|
||||||
|
var w_neighbor = 0.15
|
||||||
|
|
||||||
|
if primary_path: weights_per_bone[primary_path][vert_idx] += w_center
|
||||||
|
if prev_path: weights_per_bone[prev_path][vert_idx] += w_neighbor
|
||||||
|
else: weights_per_bone[primary_path][vert_idx] += w_neighbor
|
||||||
|
if next_path: weights_per_bone[next_path][vert_idx] += w_neighbor
|
||||||
|
else: weights_per_bone[primary_path][vert_idx] += w_neighbor
|
||||||
|
|
||||||
|
vert_idx += 1
|
||||||
|
|
||||||
|
for bpath in weights_per_bone:
|
||||||
|
poly.add_bone(bpath, weights_per_bone[bpath])
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# ANIMATION LOGIC
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
func _generate_animations_logic():
|
||||||
|
var library: AnimationLibrary
|
||||||
|
if anim_player.has_animation_library(""): library = anim_player.get_animation_library("")
|
||||||
|
else: library = AnimationLibrary.new(); anim_player.add_animation_library("", library)
|
||||||
|
|
||||||
|
_create_single_anim(library, "turn_flexible_page", false, false)
|
||||||
|
_create_single_anim(library, "turn_rigid_page", true, false)
|
||||||
|
_create_single_anim(library, "turn_flexible_page_mirror", false, true)
|
||||||
|
_create_single_anim(library, "turn_rigid_page_mirror", true, true)
|
||||||
|
notify_property_list_changed()
|
||||||
|
|
||||||
|
|
||||||
|
func _create_single_anim(library: AnimationLibrary, anim_name: String, is_rigid: bool, is_mirror: bool):
|
||||||
|
var anim = Animation.new()
|
||||||
|
anim.length = anim_duration
|
||||||
|
anim.step = 0.01
|
||||||
|
|
||||||
|
if library.has_animation(anim_name): library.remove_animation(anim_name)
|
||||||
|
library.add_animation(anim_name, anim)
|
||||||
|
|
||||||
|
var sk_node = find_child(SKELETON_NAME, true, false)
|
||||||
|
if not sk_node: return
|
||||||
|
|
||||||
|
var my_path = anim_player.get_node(anim_player.root_node).get_path_to(self)
|
||||||
|
|
||||||
|
var visible_col = Color(1, 1, 1, 1)
|
||||||
|
var invisible_col = Color(1, 1, 1, 0)
|
||||||
|
var faded_col = Color(1, 1, 1, 0.6)
|
||||||
|
|
||||||
|
var t_mid = anim_duration * 0.5
|
||||||
|
var t_end_snap = anim_duration * 0.95
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# 1. VISIBILITY & Z-INDEX
|
||||||
|
# ==========================================================================
|
||||||
|
var z_track = anim.add_track(Animation.TYPE_VALUE)
|
||||||
|
anim.track_set_path(z_track, str(my_path) + ":z_index")
|
||||||
|
anim.track_set_interpolation_type(z_track, Animation.INTERPOLATION_NEAREST)
|
||||||
|
anim.track_insert_key(z_track, 0.0, 10)
|
||||||
|
anim.track_insert_key(z_track, anim_duration * 0.15, 25)
|
||||||
|
anim.track_insert_key(z_track, anim_duration * 0.65, 10)
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# 2. BACKGROUND SHADOW
|
||||||
|
# ==========================================================================
|
||||||
|
var shadow_node = find_child(SHADOW_NAME, true, false)
|
||||||
|
if shadow_node and enable_shadow:
|
||||||
|
var shadow_path = anim_player.get_node(anim_player.root_node).get_path_to(shadow_node)
|
||||||
|
var t_scale = anim.add_track(Animation.TYPE_VALUE)
|
||||||
|
anim.track_set_path(t_scale, str(shadow_path) + ":scale")
|
||||||
|
anim.track_set_interpolation_type(t_scale, Animation.INTERPOLATION_CUBIC)
|
||||||
|
var t_mod = anim.add_track(Animation.TYPE_VALUE)
|
||||||
|
anim.track_set_path(t_mod, str(shadow_path) + ":modulate")
|
||||||
|
|
||||||
|
var s_start = Vector2(-1.0, 1.0) if is_mirror else Vector2(1.0, 1.0)
|
||||||
|
var s_end = Vector2(1.0, 1.0) if is_mirror else Vector2(-1.0, 1.0)
|
||||||
|
|
||||||
|
anim.track_insert_key(t_scale, 0.0, s_start)
|
||||||
|
anim.track_insert_key(t_mod, 0.0, invisible_col)
|
||||||
|
anim.track_insert_key(t_mod, anim_duration * 0.1, visible_col)
|
||||||
|
|
||||||
|
anim.track_insert_key(t_scale, t_mid, Vector2(0.01, 1.0))
|
||||||
|
anim.track_insert_key(t_mod, t_mid, faded_col)
|
||||||
|
|
||||||
|
anim.track_insert_key(t_scale, t_end_snap, s_end)
|
||||||
|
anim.track_insert_key(t_scale, anim_duration, s_end)
|
||||||
|
anim.track_insert_key(t_mod, t_end_snap, invisible_col)
|
||||||
|
anim.track_insert_key(t_mod, anim_duration, invisible_col)
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# 3. BONE ANIMATION
|
||||||
|
# ==========================================================================
|
||||||
|
for y in range(subdivision_y + 1):
|
||||||
|
var row_factor = 1.0
|
||||||
|
if not is_rigid and curl_mode != CurlMode.STRAIGHT:
|
||||||
|
var y_ratio = float(y) / float(max(1, subdivision_y))
|
||||||
|
if curl_mode == CurlMode.TOP_CORNER_FIRST:
|
||||||
|
row_factor = lerp(1.0, 1.0 - curl_lag, y_ratio)
|
||||||
|
elif curl_mode == CurlMode.BOTTOM_CORNER_FIRST:
|
||||||
|
row_factor = lerp(1.0 - curl_lag, 1.0, y_ratio)
|
||||||
|
|
||||||
|
var time_offset = (1.0 - row_factor) * (anim_duration * 0.1)
|
||||||
|
|
||||||
|
for x in range(subdivision_x + 1):
|
||||||
|
var bone_name = "Bone_%d_%d" % [x, y]
|
||||||
|
var bone = sk_node.find_child(bone_name, true, false)
|
||||||
|
if not bone: continue
|
||||||
|
|
||||||
|
var t_idx = anim.add_track(Animation.TYPE_VALUE)
|
||||||
|
var path = anim_player.get_node(anim_player.root_node).get_path_to(bone)
|
||||||
|
anim.track_set_path(t_idx, str(path) + ":rotation_degrees")
|
||||||
|
anim.track_set_interpolation_type(t_idx, Animation.INTERPOLATION_CUBIC)
|
||||||
|
|
||||||
|
var deg_flat_right = 0.0; var deg_flat_left = -179.9; var deg_mid = -90.0
|
||||||
|
|
||||||
|
if is_rigid:
|
||||||
|
if x == 0:
|
||||||
|
var s = deg_flat_left if is_mirror else deg_flat_right
|
||||||
|
var e = deg_flat_right if is_mirror else deg_flat_left
|
||||||
|
anim.track_insert_key(t_idx, 0.0, s)
|
||||||
|
anim.track_insert_key(t_idx, anim_duration * 0.5, deg_mid)
|
||||||
|
anim.track_insert_key(t_idx, anim_duration, e)
|
||||||
|
else:
|
||||||
|
anim.track_insert_key(t_idx, 0.0, 0.0)
|
||||||
|
anim.track_insert_key(t_idx, anim_duration, 0.0)
|
||||||
|
else:
|
||||||
|
var x_ratio = float(x) / float(subdivision_x)
|
||||||
|
var influence = pow(x_ratio, paper_stiffness)
|
||||||
|
var t_lift = clamp((anim_duration * timing_peak_lift) + time_offset, 0.0, (anim_duration * 0.5) - 0.05)
|
||||||
|
var t_mid_bone = anim_duration * 0.5
|
||||||
|
var t_land = clamp((anim_duration * timing_peak_land) - time_offset, t_mid_bone + 0.05, anim_duration)
|
||||||
|
|
||||||
|
if x == 0:
|
||||||
|
# ROOT BONE
|
||||||
|
var s = deg_flat_left if is_mirror else deg_flat_right
|
||||||
|
var e = deg_flat_right if is_mirror else deg_flat_left
|
||||||
|
var sl = -15.0 * row_factor
|
||||||
|
if is_mirror: sl = deg_flat_left - sl
|
||||||
|
var slt = lerp(-90.0, -180.0, timing_peak_land)
|
||||||
|
if is_mirror: slt = lerp(-90.0, 0.0, timing_peak_land)
|
||||||
|
|
||||||
|
anim.track_insert_key(t_idx, 0.0, s)
|
||||||
|
anim.track_insert_key(t_idx, t_lift, sl)
|
||||||
|
anim.track_insert_key(t_idx, t_mid_bone, deg_mid)
|
||||||
|
anim.track_insert_key(t_idx, t_land, slt)
|
||||||
|
|
||||||
|
var t_settle = lerp(t_land, anim_duration, 0.7)
|
||||||
|
var s_settle = lerp(slt, e, 0.85)
|
||||||
|
anim.track_insert_key(t_idx, t_settle, s_settle)
|
||||||
|
|
||||||
|
anim.track_insert_key(t_idx, anim_duration, e)
|
||||||
|
else:
|
||||||
|
# SURFACE BONES (CURVED)
|
||||||
|
var bm = -1.0 if is_mirror else 1.0
|
||||||
|
|
||||||
|
anim.track_insert_key(t_idx, 0.0, 0.0)
|
||||||
|
anim.track_insert_key(t_idx, t_lift, lift_bend * row_factor * bm * influence)
|
||||||
|
# REMOVED FORCED 0.0 AT MIDPOINT FOR ORGANIC CURVE
|
||||||
|
|
||||||
|
var val_land = land_bend * row_factor * bm * influence
|
||||||
|
anim.track_insert_key(t_idx, t_land, val_land)
|
||||||
|
|
||||||
|
var t_settle = lerp(t_land, anim_duration, 0.75)
|
||||||
|
anim.track_insert_key(t_idx, t_settle, val_land * 0.15)
|
||||||
|
|
||||||
|
anim.track_insert_key(t_idx, anim_duration, 0.0)
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# 4. MAIN POLYGON SCALE ("SQUASH & STRETCH")
|
||||||
|
# ==========================================================================
|
||||||
|
if not is_rigid:
|
||||||
|
var t_main_scale = anim.add_track(Animation.TYPE_VALUE)
|
||||||
|
anim.track_set_path(t_main_scale, str(my_path) + ":scale")
|
||||||
|
anim.track_set_interpolation_type(t_main_scale, Animation.INTERPOLATION_LINEAR)
|
||||||
|
var squash_x = 0.88
|
||||||
|
|
||||||
|
anim.track_insert_key(t_main_scale, 0.0, Vector2(1, 1))
|
||||||
|
anim.track_insert_key(t_main_scale, t_mid, Vector2(1, 1))
|
||||||
|
anim.track_insert_key(t_main_scale, t_mid + (anim_duration * 0.1), Vector2(squash_x, 1))
|
||||||
|
anim.track_insert_key(t_main_scale, anim_duration * 0.85, Vector2(1, 1))
|
||||||
|
anim.track_insert_key(t_main_scale, anim_duration, Vector2(1, 1))
|
||||||
|
|
||||||
|
var method_track = anim.add_track(Animation.TYPE_METHOD)
|
||||||
|
anim.track_set_path(method_track, str(my_path))
|
||||||
|
anim.track_insert_key(method_track, anim_duration * 0.5, {"method": "_trigger_midpoint", "args": []})
|
||||||
|
anim.track_insert_key(method_track, anim_duration, {"method": "_trigger_end", "args": []})
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# HELPER FUNCTIONS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
func _trigger_midpoint(): emit_signal("change_page_requested")
|
||||||
|
func _trigger_end(): emit_signal("end_animation")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_generate_pressed(val):
|
||||||
|
if val:
|
||||||
|
rebuild_all=false
|
||||||
|
_clean_previous_rig()
|
||||||
|
_create_rig_logic()
|
||||||
|
_generate_animations_logic()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_anim_pressed(val):
|
||||||
|
if val:
|
||||||
|
generate_anims_btn=false
|
||||||
|
if anim_player and skeleton!=NodePath(""):
|
||||||
|
_generate_animations_logic()
|
||||||
|
|
||||||
|
|
||||||
|
func _clean_previous_rig():
|
||||||
|
for c in get_children():
|
||||||
|
remove_child(c)
|
||||||
|
c.free()
|
||||||
|
var old_shadow = find_child(SHADOW_NAME, true, false)
|
||||||
|
if old_shadow: old_shadow.free()
|
||||||
|
clear_bones()
|
||||||
|
skeleton = NodePath("")
|
||||||
1
addons/PageFlip/page_rigger.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ce5ib2f4qff24
|
||||||
@@ -5,6 +5,28 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://4y4tffc723lg" path="res://core/ai/agents/fox/Fox_rig.fbx" id="2_xaafp"]
|
[ext_resource type="PackedScene" uid="uid://4y4tffc723lg" path="res://core/ai/agents/fox/Fox_rig.fbx" id="2_xaafp"]
|
||||||
[ext_resource type="Script" uid="uid://bngfthvt04ivv" path="res://core/ai/framework/patrol_state.gd" id="3_d1sp2"]
|
[ext_resource type="Script" uid="uid://bngfthvt04ivv" path="res://core/ai/framework/patrol_state.gd" id="3_d1sp2"]
|
||||||
[ext_resource type="Script" uid="uid://b5y4u0itec8g1" path="res://core/ai/framework/run_state.gd" id="4_d1sp2"]
|
[ext_resource type="Script" uid="uid://b5y4u0itec8g1" path="res://core/ai/framework/run_state.gd" id="4_d1sp2"]
|
||||||
|
[ext_resource type="Shader" uid="uid://d0ch5ofrgf7y6" path="res://core/daynight/trunk_shader.gdshader" id="6_oqxvo"]
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_4pdxi"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("6_oqxvo")
|
||||||
|
shader_parameter/albedo_color = Color(0.5408599, 0.27705142, 0.12841594, 1)
|
||||||
|
shader_parameter/use_texture = true
|
||||||
|
shader_parameter/uv_scale = Vector2(3, 3)
|
||||||
|
shader_parameter/palette_shift_y = 0.0
|
||||||
|
shader_parameter/gradient_start_y = 0.0
|
||||||
|
shader_parameter/gradient_end_y = 1.5
|
||||||
|
shader_parameter/light_steps = 3.0
|
||||||
|
shader_parameter/step_softness = 0.1
|
||||||
|
shader_parameter/shadow_color = Color(0.4, 0.4, 0.6, 1)
|
||||||
|
shader_parameter/shadow_offset = 0.0
|
||||||
|
shader_parameter/cast_shadow_strength = 0.6
|
||||||
|
shader_parameter/use_ghibli_glint = true
|
||||||
|
shader_parameter/glint_color = Color(1, 0.95, 0.85, 1)
|
||||||
|
shader_parameter/glint_intensity = 1.0
|
||||||
|
shader_parameter/glint_sharpness = 32.0
|
||||||
|
shader_parameter/emission_color = Color(0, 0, 0, 1)
|
||||||
|
shader_parameter/emission_energy = 0.0
|
||||||
|
|
||||||
[node name="AiFox" unique_id=1228675528 node_paths=PackedStringArray("anim_player") instance=ExtResource("1_m3efo")]
|
[node name="AiFox" unique_id=1228675528 node_paths=PackedStringArray("anim_player") instance=ExtResource("1_m3efo")]
|
||||||
navigation_layers = 4
|
navigation_layers = 4
|
||||||
@@ -61,4 +83,7 @@ bones/26/rotation = Quaternion(-3.3980676e-05, -0.00016874354, 0.9618461, -0.273
|
|||||||
bones/27/rotation = Quaternion(-1.9324155e-08, 4.5031282e-08, -0.47071794, 0.8822838)
|
bones/27/rotation = Quaternion(-1.9324155e-08, 4.5031282e-08, -0.47071794, 0.8822838)
|
||||||
bones/28/rotation = Quaternion(4.549153e-05, -8.8271285e-05, 0.5290293, 0.84860367)
|
bones/28/rotation = Quaternion(4.549153e-05, -8.8271285e-05, 0.5290293, 0.84860367)
|
||||||
|
|
||||||
|
[node name="fox" parent="Fox_rig/Rig_Fox/Skeleton3D" index="0" unique_id=2042924802]
|
||||||
|
surface_material_override/1 = SubResource("ShaderMaterial_4pdxi")
|
||||||
|
|
||||||
[editable path="Fox_rig"]
|
[editable path="Fox_rig"]
|
||||||
|
|||||||
@@ -4,6 +4,50 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://cumnmnbdjbgas" path="res://core/ai/agents/goat/Goat_rig.fbx" id="2_oxwbf"]
|
[ext_resource type="PackedScene" uid="uid://cumnmnbdjbgas" path="res://core/ai/agents/goat/Goat_rig.fbx" id="2_oxwbf"]
|
||||||
[ext_resource type="Script" uid="uid://d3hy70ec8vqo5" path="res://core/ai/framework/run_animation_state.gd" id="2_ucoix"]
|
[ext_resource type="Script" uid="uid://d3hy70ec8vqo5" path="res://core/ai/framework/run_animation_state.gd" id="2_ucoix"]
|
||||||
[ext_resource type="Script" uid="uid://bngfthvt04ivv" path="res://core/ai/framework/patrol_state.gd" id="3_lqqd8"]
|
[ext_resource type="Script" uid="uid://bngfthvt04ivv" path="res://core/ai/framework/patrol_state.gd" id="3_lqqd8"]
|
||||||
|
[ext_resource type="Shader" uid="uid://d0ch5ofrgf7y6" path="res://core/daynight/trunk_shader.gdshader" id="5_lqqd8"]
|
||||||
|
[ext_resource type="Material" uid="uid://cn20mp8ep3yxu" path="res://tgcc/chunk/material/wires.tres" id="6_xcxu7"]
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_xcxu7"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("5_lqqd8")
|
||||||
|
shader_parameter/albedo_color = Color(0.46774077, 0.51844496, 0.510305, 1)
|
||||||
|
shader_parameter/use_texture = true
|
||||||
|
shader_parameter/uv_scale = Vector2(3, 3)
|
||||||
|
shader_parameter/palette_shift_y = 0.0
|
||||||
|
shader_parameter/gradient_start_y = 0.0
|
||||||
|
shader_parameter/gradient_end_y = 1.5
|
||||||
|
shader_parameter/light_steps = 3.0
|
||||||
|
shader_parameter/step_softness = 0.1
|
||||||
|
shader_parameter/shadow_color = Color(0.4, 0.4, 0.6, 1)
|
||||||
|
shader_parameter/shadow_offset = 0.0
|
||||||
|
shader_parameter/cast_shadow_strength = 0.6
|
||||||
|
shader_parameter/use_ghibli_glint = true
|
||||||
|
shader_parameter/glint_color = Color(1, 0.95, 0.85, 1)
|
||||||
|
shader_parameter/glint_intensity = 1.0
|
||||||
|
shader_parameter/glint_sharpness = 32.0
|
||||||
|
shader_parameter/emission_color = Color(0, 0, 0, 1)
|
||||||
|
shader_parameter/emission_energy = 0.0
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_lqqd8"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("5_lqqd8")
|
||||||
|
shader_parameter/albedo_color = Color(0.49370736, 0.43829006, 0.29924837, 1)
|
||||||
|
shader_parameter/use_texture = true
|
||||||
|
shader_parameter/uv_scale = Vector2(3, 3)
|
||||||
|
shader_parameter/palette_shift_y = 0.0
|
||||||
|
shader_parameter/gradient_start_y = 0.0
|
||||||
|
shader_parameter/gradient_end_y = 1.5
|
||||||
|
shader_parameter/light_steps = 3.0
|
||||||
|
shader_parameter/step_softness = 0.1
|
||||||
|
shader_parameter/shadow_color = Color(0.4, 0.4, 0.6, 1)
|
||||||
|
shader_parameter/shadow_offset = 0.0
|
||||||
|
shader_parameter/cast_shadow_strength = 0.6
|
||||||
|
shader_parameter/use_ghibli_glint = true
|
||||||
|
shader_parameter/glint_color = Color(1, 0.95, 0.85, 1)
|
||||||
|
shader_parameter/glint_intensity = 1.0
|
||||||
|
shader_parameter/glint_sharpness = 32.0
|
||||||
|
shader_parameter/emission_color = Color(0, 0, 0, 1)
|
||||||
|
shader_parameter/emission_energy = 0.0
|
||||||
|
|
||||||
[node name="AiGoat" unique_id=1228675528 node_paths=PackedStringArray("anim_player") instance=ExtResource("1_f7hhh")]
|
[node name="AiGoat" unique_id=1228675528 node_paths=PackedStringArray("anim_player") instance=ExtResource("1_f7hhh")]
|
||||||
anim_player = NodePath("Goat_rig/AnimationPlayer")
|
anim_player = NodePath("Goat_rig/AnimationPlayer")
|
||||||
@@ -28,7 +72,7 @@ exit_on_action_finished = true
|
|||||||
|
|
||||||
[node name="Goat_rig" parent="." index="3" unique_id=1336829719 instance=ExtResource("2_oxwbf")]
|
[node name="Goat_rig" parent="." index="3" unique_id=1336829719 instance=ExtResource("2_oxwbf")]
|
||||||
|
|
||||||
[node name="Skeleton3D" parent="Goat_rig/Rig_Goat" parent_id_path=PackedInt32Array(1336829719, 123281310) index="0" unique_id=1526518923]
|
[node name="Skeleton3D" parent="Goat_rig/Rig_Goat" parent_id_path=PackedInt32Array(1336829719, 1679574624) index="0" unique_id=1406072579]
|
||||||
bones/1/position = Vector3(-8.352713e-09, 0.36986786, 0.8951362)
|
bones/1/position = Vector3(-8.352713e-09, 0.36986786, 0.8951362)
|
||||||
bones/1/rotation = Quaternion(0.1276911, -0.695482, -0.12769035, 0.69548184)
|
bones/1/rotation = Quaternion(0.1276911, -0.695482, -0.12769035, 0.69548184)
|
||||||
bones/2/rotation = Quaternion(-8.7811394e-08, -9.458198e-08, -0.5904155, 0.80709946)
|
bones/2/rotation = Quaternion(-8.7811394e-08, -9.458198e-08, -0.5904155, 0.80709946)
|
||||||
@@ -51,4 +95,10 @@ bones/26/rotation = Quaternion(2.713829e-07, 8.5825764e-07, 0.9653876, -0.260819
|
|||||||
bones/27/rotation = Quaternion(-3.8362674e-07, 5.6200027e-09, -0.3680436, 0.92980856)
|
bones/27/rotation = Quaternion(-3.8362674e-07, 5.6200027e-09, -0.3680436, 0.92980856)
|
||||||
bones/28/rotation = Quaternion(-0.00015313082, -7.842175e-05, 0.45790914, 0.888999)
|
bones/28/rotation = Quaternion(-0.00015313082, -7.842175e-05, 0.45790914, 0.888999)
|
||||||
|
|
||||||
|
[node name="Goat" parent="Goat_rig/Rig_Goat/Skeleton3D" index="0" unique_id=1545212389]
|
||||||
|
surface_material_override/0 = SubResource("ShaderMaterial_xcxu7")
|
||||||
|
surface_material_override/1 = SubResource("ShaderMaterial_lqqd8")
|
||||||
|
surface_material_override/2 = ExtResource("6_xcxu7")
|
||||||
|
surface_material_override/3 = ExtResource("6_xcxu7")
|
||||||
|
|
||||||
[editable path="Goat_rig"]
|
[editable path="Goat_rig"]
|
||||||
|
|||||||
@@ -3,6 +3,31 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://mvh2v6v72stt" path="res://core/ai/agents/human/ai_human.tscn" id="1_l2y7m"]
|
[ext_resource type="PackedScene" uid="uid://mvh2v6v72stt" path="res://core/ai/agents/human/ai_human.tscn" id="1_l2y7m"]
|
||||||
[ext_resource type="Script" uid="uid://bngfthvt04ivv" path="res://core/ai/framework/patrol_state.gd" id="3_2oda6"]
|
[ext_resource type="Script" uid="uid://bngfthvt04ivv" path="res://core/ai/framework/patrol_state.gd" id="3_2oda6"]
|
||||||
[ext_resource type="Script" uid="uid://d3hy70ec8vqo5" path="res://core/ai/framework/run_animation_state.gd" id="4_2oda6"]
|
[ext_resource type="Script" uid="uid://d3hy70ec8vqo5" path="res://core/ai/framework/run_animation_state.gd" id="4_2oda6"]
|
||||||
|
[ext_resource type="Shader" uid="uid://d0ch5ofrgf7y6" path="res://core/daynight/trunk_shader.gdshader" id="6_ejkex"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://jn4seoee6vtw" path="res://tgcc/chunk/prop/house/main_texture.png" id="7_ax7ee"]
|
||||||
|
[ext_resource type="Material" uid="uid://b2lg2y1k7grmo" path="res://tgcc/chunk/prop/tree/trunk.tres" id="9_gownw"]
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_gownw"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("6_ejkex")
|
||||||
|
shader_parameter/albedo_color = Color(1, 1, 1, 1)
|
||||||
|
shader_parameter/albedo_texture = ExtResource("7_ax7ee")
|
||||||
|
shader_parameter/use_texture = true
|
||||||
|
shader_parameter/uv_scale = Vector2(1, 1)
|
||||||
|
shader_parameter/palette_shift_y = 0.0
|
||||||
|
shader_parameter/gradient_start_y = 0.0
|
||||||
|
shader_parameter/gradient_end_y = 10.0
|
||||||
|
shader_parameter/light_steps = 3.0
|
||||||
|
shader_parameter/step_softness = 0.1
|
||||||
|
shader_parameter/shadow_color = Color(0.4, 0.4, 0.6, 1)
|
||||||
|
shader_parameter/shadow_offset = 0.0
|
||||||
|
shader_parameter/cast_shadow_strength = 0.6
|
||||||
|
shader_parameter/use_ghibli_glint = true
|
||||||
|
shader_parameter/glint_color = Color(1, 0.95, 0.85, 1)
|
||||||
|
shader_parameter/glint_intensity = 0.446000021185
|
||||||
|
shader_parameter/glint_sharpness = 7.000000285
|
||||||
|
shader_parameter/emission_color = Color(0, 0, 0, 1)
|
||||||
|
shader_parameter/emission_energy = 0.0
|
||||||
|
|
||||||
[node name="AiFarmer" unique_id=1228675528 instance=ExtResource("1_l2y7m")]
|
[node name="AiFarmer" unique_id=1228675528 instance=ExtResource("1_l2y7m")]
|
||||||
navigation_layers = 2
|
navigation_layers = 2
|
||||||
@@ -71,4 +96,27 @@ bones/53/rotation = Quaternion(-0.25056988, -0.06629382, 0.018621337, 0.96564656
|
|||||||
bones/54/rotation = Quaternion(0.549743, -0.041815143, -0.046032935, 0.83301574)
|
bones/54/rotation = Quaternion(0.549743, -0.041815143, -0.046032935, 0.83301574)
|
||||||
bones/55/rotation = Quaternion(0.3627838, -0.05369254, 0.01255593, 0.9302406)
|
bones/55/rotation = Quaternion(0.3627838, -0.05369254, 0.01255593, 0.9302406)
|
||||||
|
|
||||||
|
[node name="hat_001" parent="Farmer1/BaseMesh/Skeleton3D" parent_id_path=PackedInt32Array(925374995, 224377346) index="1" unique_id=1950307793]
|
||||||
|
surface_material_override/0 = SubResource("ShaderMaterial_gownw")
|
||||||
|
|
||||||
|
[node name="pants" parent="Farmer1/BaseMesh/Skeleton3D" parent_id_path=PackedInt32Array(925374995, 224377346) index="2" unique_id=1080342530]
|
||||||
|
surface_material_override/0 = ExtResource("9_gownw")
|
||||||
|
|
||||||
|
[node name="shirt2" parent="Farmer1/BaseMesh/Skeleton3D" parent_id_path=PackedInt32Array(925374995, 224377346) index="3" unique_id=1846653994]
|
||||||
|
surface_material_override/0 = ExtResource("9_gownw")
|
||||||
|
surface_material_override/1 = ExtResource("9_gownw")
|
||||||
|
surface_material_override/2 = ExtResource("9_gownw")
|
||||||
|
|
||||||
|
[node name="pants" parent="Farmer2/BaseMesh/Skeleton3D" parent_id_path=PackedInt32Array(1239853450, 1824387143) index="1" unique_id=1777820543]
|
||||||
|
surface_material_override/0 = ExtResource("9_gownw")
|
||||||
|
|
||||||
|
[node name="shirt1" parent="Farmer2/BaseMesh/Skeleton3D" parent_id_path=PackedInt32Array(1239853450, 1824387143) index="2" unique_id=1243989703]
|
||||||
|
surface_material_override/0 = ExtResource("9_gownw")
|
||||||
|
surface_material_override/1 = ExtResource("9_gownw")
|
||||||
|
|
||||||
|
[node name="DressHuman" parent="." index="6" unique_id=107727715]
|
||||||
|
colors = Array[Color]([Color(0.8, 0.45490196, 0.32156864, 1), Color(0.84797, 0.6868576, 0.24382448, 1), Color(0.4862745, 0.60784316, 0.6901961, 1), Color(0.24718326, 0.3552445, 0.23638204, 1), Color(0.34000763, 0.32978323, 0.29223797, 1)])
|
||||||
|
|
||||||
[editable path="BaseMesh"]
|
[editable path="BaseMesh"]
|
||||||
|
[editable path="Farmer1"]
|
||||||
|
[editable path="Farmer2"]
|
||||||
|
|||||||
@@ -7,8 +7,13 @@
|
|||||||
[ext_resource type="Animation" uid="uid://oq22ilhxjejj" path="res://core/ai/agents/human/idle.res" id="4_cuxdk"]
|
[ext_resource type="Animation" uid="uid://oq22ilhxjejj" path="res://core/ai/agents/human/idle.res" id="4_cuxdk"]
|
||||||
[ext_resource type="Animation" uid="uid://6xnsf8x7nmsl" path="res://core/ai/agents/human/walking.res" id="6_cuxdk"]
|
[ext_resource type="Animation" uid="uid://6xnsf8x7nmsl" path="res://core/ai/agents/human/walking.res" id="6_cuxdk"]
|
||||||
[ext_resource type="PackedScene" uid="uid://b3j2dbcqwodx1" path="res://core/ai/agents/human/Farmer1.fbx" id="7_hguxe"]
|
[ext_resource type="PackedScene" uid="uid://b3j2dbcqwodx1" path="res://core/ai/agents/human/Farmer1.fbx" id="7_hguxe"]
|
||||||
|
[ext_resource type="Material" uid="uid://blqelpjvdv23j" path="res://tgcc/chunk/material/grassflat_chunk.tres" id="8_362f3"]
|
||||||
[ext_resource type="PackedScene" uid="uid://c5x3ycaxixpxy" path="res://core/ai/agents/human/Farmer2.fbx" id="8_ke7sb"]
|
[ext_resource type="PackedScene" uid="uid://c5x3ycaxixpxy" path="res://core/ai/agents/human/Farmer2.fbx" id="8_ke7sb"]
|
||||||
[ext_resource type="Script" uid="uid://dauqq8cqmwrqh" path="res://core/ai/agents/human/dress_human.gd" id="9_2tpdi"]
|
[ext_resource type="Script" uid="uid://dauqq8cqmwrqh" path="res://core/ai/agents/human/dress_human.gd" id="9_2tpdi"]
|
||||||
|
[ext_resource type="Material" uid="uid://b4luvp3fi0p43" path="res://tgcc/chunk/material/railway_chunk.tres" id="9_12auf"]
|
||||||
|
[ext_resource type="Shader" uid="uid://d0ch5ofrgf7y6" path="res://core/daynight/trunk_shader.gdshader" id="10_3t1a4"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://jn4seoee6vtw" path="res://tgcc/chunk/prop/house/main_texture.png" id="11_o6vi4"]
|
||||||
|
[ext_resource type="Material" uid="uid://wkegx4a21x7u" path="res://tgcc/chunk/prop/fence/fences.tres" id="11_v2pkc"]
|
||||||
|
|
||||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_8kg0q"]
|
[sub_resource type="AnimationLibrary" id="AnimationLibrary_8kg0q"]
|
||||||
_data = {
|
_data = {
|
||||||
@@ -18,6 +23,28 @@ _data = {
|
|||||||
&"walking": ExtResource("6_cuxdk")
|
&"walking": ExtResource("6_cuxdk")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_vuqrb"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("10_3t1a4")
|
||||||
|
shader_parameter/albedo_color = Color(1, 1, 1, 1)
|
||||||
|
shader_parameter/albedo_texture = ExtResource("11_o6vi4")
|
||||||
|
shader_parameter/use_texture = true
|
||||||
|
shader_parameter/uv_scale = Vector2(1, 1)
|
||||||
|
shader_parameter/palette_shift_y = 0.0
|
||||||
|
shader_parameter/gradient_start_y = 0.0
|
||||||
|
shader_parameter/gradient_end_y = 10.0
|
||||||
|
shader_parameter/light_steps = 3.0
|
||||||
|
shader_parameter/step_softness = 0.1
|
||||||
|
shader_parameter/shadow_color = Color(0.4, 0.4, 0.6, 1)
|
||||||
|
shader_parameter/shadow_offset = 0.0
|
||||||
|
shader_parameter/cast_shadow_strength = 0.6
|
||||||
|
shader_parameter/use_ghibli_glint = false
|
||||||
|
shader_parameter/glint_color = Color(1, 0.95, 0.85, 1)
|
||||||
|
shader_parameter/glint_intensity = 2.000000095
|
||||||
|
shader_parameter/glint_sharpness = 7.000000285
|
||||||
|
shader_parameter/emission_color = Color(0, 0, 0, 1)
|
||||||
|
shader_parameter/emission_energy = 0.0
|
||||||
|
|
||||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_ke7sb"]
|
[sub_resource type="AnimationLibrary" id="AnimationLibrary_ke7sb"]
|
||||||
_data = {
|
_data = {
|
||||||
&"gathering": ExtResource("3_8kg0q"),
|
&"gathering": ExtResource("3_8kg0q"),
|
||||||
@@ -26,6 +53,28 @@ _data = {
|
|||||||
&"walking": ExtResource("6_cuxdk")
|
&"walking": ExtResource("6_cuxdk")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_v2pkc"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("10_3t1a4")
|
||||||
|
shader_parameter/albedo_color = Color(1, 1, 1, 1)
|
||||||
|
shader_parameter/albedo_texture = ExtResource("11_o6vi4")
|
||||||
|
shader_parameter/use_texture = true
|
||||||
|
shader_parameter/uv_scale = Vector2(1, 1)
|
||||||
|
shader_parameter/palette_shift_y = 0.0
|
||||||
|
shader_parameter/gradient_start_y = 0.0
|
||||||
|
shader_parameter/gradient_end_y = 10.0
|
||||||
|
shader_parameter/light_steps = 3.0
|
||||||
|
shader_parameter/step_softness = 0.1
|
||||||
|
shader_parameter/shadow_color = Color(0.4, 0.4, 0.6, 1)
|
||||||
|
shader_parameter/shadow_offset = 0.0
|
||||||
|
shader_parameter/cast_shadow_strength = 0.6
|
||||||
|
shader_parameter/use_ghibli_glint = false
|
||||||
|
shader_parameter/glint_color = Color(1, 0.95, 0.85, 1)
|
||||||
|
shader_parameter/glint_intensity = 2.000000095
|
||||||
|
shader_parameter/glint_sharpness = 7.000000285
|
||||||
|
shader_parameter/emission_color = Color(0, 0, 0, 1)
|
||||||
|
shader_parameter/emission_energy = 0.0
|
||||||
|
|
||||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_2tpdi"]
|
[sub_resource type="AnimationLibrary" id="AnimationLibrary_2tpdi"]
|
||||||
_data = {
|
_data = {
|
||||||
&"gathering": ExtResource("3_8kg0q"),
|
&"gathering": ExtResource("3_8kg0q"),
|
||||||
@@ -40,7 +89,7 @@ anim_player = NodePath("BaseMesh/AnimationPlayer")
|
|||||||
[node name="BaseMesh" parent="." index="3" unique_id=1846171523 instance=ExtResource("3_ef4cu")]
|
[node name="BaseMesh" parent="." index="3" unique_id=1846171523 instance=ExtResource("3_ef4cu")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.9, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.9, 0)
|
||||||
|
|
||||||
[node name="Skeleton3D" parent="BaseMesh/BaseMesh" parent_id_path=PackedInt32Array(1846171523, 2032005240) index="0" unique_id=1876883359]
|
[node name="Skeleton3D" parent="BaseMesh/BaseMesh" parent_id_path=PackedInt32Array(1846171523, 1363988051) index="0" unique_id=1848874512]
|
||||||
bones/0/position = Vector3(-0.0024333135, 0.9952637, 6.9276895e-05)
|
bones/0/position = Vector3(-0.0024333135, 0.9952637, 6.9276895e-05)
|
||||||
bones/0/rotation = Quaternion(-0.0049152793, -0.050206795, -0.026513178, 0.99837476)
|
bones/0/rotation = Quaternion(-0.0049152793, -0.050206795, -0.026513178, 0.99837476)
|
||||||
bones/1/rotation = Quaternion(-0.025209453, 0.006135946, 0.009180705, 0.9996213)
|
bones/1/rotation = Quaternion(-0.025209453, 0.006135946, 0.009180705, 0.9996213)
|
||||||
@@ -83,14 +132,14 @@ bones/53/rotation = Quaternion(-0.10502596, -0.00032293255, 0.008123855, 0.99443
|
|||||||
bones/54/rotation = Quaternion(0.51151097, 0.066536464, -0.07767137, 0.8531686)
|
bones/54/rotation = Quaternion(0.51151097, 0.066536464, -0.07767137, 0.8531686)
|
||||||
bones/55/rotation = Quaternion(0.3453064, -0.067497015, 0.02391528, 0.93575406)
|
bones/55/rotation = Quaternion(0.3453064, -0.067497015, 0.02391528, 0.93575406)
|
||||||
|
|
||||||
[node name="AnimationPlayer" parent="BaseMesh" index="1" unique_id=226905852]
|
[node name="AnimationPlayer" parent="BaseMesh" index="1" unique_id=930749540]
|
||||||
libraries/custom = SubResource("AnimationLibrary_8kg0q")
|
libraries/custom = SubResource("AnimationLibrary_8kg0q")
|
||||||
|
|
||||||
[node name="Farmer1" parent="." index="4" unique_id=925374995 instance=ExtResource("7_hguxe")]
|
[node name="Farmer1" parent="." index="4" unique_id=925374995 instance=ExtResource("7_hguxe")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.9, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.9, 0)
|
||||||
visible = false
|
visible = false
|
||||||
|
|
||||||
[node name="Skeleton3D" parent="Farmer1/BaseMesh" parent_id_path=PackedInt32Array(925374995, 35893719) index="0" unique_id=264693097]
|
[node name="Skeleton3D" parent="Farmer1/BaseMesh" parent_id_path=PackedInt32Array(925374995, 1608049295) index="0" unique_id=224377346]
|
||||||
bones/0/position = Vector3(0.00060560287, 0.9719096, 0.007789615)
|
bones/0/position = Vector3(0.00060560287, 0.9719096, 0.007789615)
|
||||||
bones/0/rotation = Quaternion(-0.03158553, -0.024531638, -0.086598895, 0.9954402)
|
bones/0/rotation = Quaternion(-0.03158553, -0.024531638, -0.086598895, 0.9954402)
|
||||||
bones/1/rotation = Quaternion(-0.068302006, -0.011589706, 0.088431515, 0.99367017)
|
bones/1/rotation = Quaternion(-0.068302006, -0.011589706, 0.088431515, 0.99367017)
|
||||||
@@ -133,14 +182,31 @@ bones/53/rotation = Quaternion(-0.20515193, 0.049833227, 0.0071537, 0.9774345)
|
|||||||
bones/54/rotation = Quaternion(0.55275166, -0.049315806, -0.007871797, 0.8318483)
|
bones/54/rotation = Quaternion(0.55275166, -0.049315806, -0.007871797, 0.8318483)
|
||||||
bones/55/rotation = Quaternion(0.326561, -0.058840513, 0.019667907, 0.9431378)
|
bones/55/rotation = Quaternion(0.326561, -0.058840513, 0.019667907, 0.9431378)
|
||||||
|
|
||||||
[node name="AnimationPlayer" parent="Farmer1" index="1" unique_id=1795570086]
|
[node name="Cesto" parent="Farmer1/BaseMesh/Skeleton3D" index="0" unique_id=1473592366]
|
||||||
|
surface_material_override/0 = ExtResource("8_362f3")
|
||||||
|
surface_material_override/1 = ExtResource("9_12auf")
|
||||||
|
surface_material_override/2 = ExtResource("9_12auf")
|
||||||
|
|
||||||
|
[node name="hat_001" parent="Farmer1/BaseMesh/Skeleton3D" index="1" unique_id=1950307793]
|
||||||
|
surface_material_override/0 = SubResource("ShaderMaterial_vuqrb")
|
||||||
|
surface_material_override/1 = ExtResource("11_v2pkc")
|
||||||
|
|
||||||
|
[node name="pants" parent="Farmer1/BaseMesh/Skeleton3D" index="2" unique_id=1080342530]
|
||||||
|
surface_material_override/0 = ExtResource("11_v2pkc")
|
||||||
|
|
||||||
|
[node name="shirt2" parent="Farmer1/BaseMesh/Skeleton3D" index="3" unique_id=1846653994]
|
||||||
|
surface_material_override/0 = ExtResource("11_v2pkc")
|
||||||
|
surface_material_override/1 = ExtResource("11_v2pkc")
|
||||||
|
surface_material_override/2 = ExtResource("11_v2pkc")
|
||||||
|
|
||||||
|
[node name="AnimationPlayer" parent="Farmer1" index="1" unique_id=2137081705]
|
||||||
libraries/custom = SubResource("AnimationLibrary_ke7sb")
|
libraries/custom = SubResource("AnimationLibrary_ke7sb")
|
||||||
|
|
||||||
[node name="Farmer2" parent="." index="5" unique_id=1239853450 instance=ExtResource("8_ke7sb")]
|
[node name="Farmer2" parent="." index="5" unique_id=1239853450 instance=ExtResource("8_ke7sb")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.9, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.9, 0)
|
||||||
visible = false
|
visible = false
|
||||||
|
|
||||||
[node name="Skeleton3D" parent="Farmer2/BaseMesh" parent_id_path=PackedInt32Array(1239853450, 2123768022) index="0" unique_id=1560566445]
|
[node name="Skeleton3D" parent="Farmer2/BaseMesh" parent_id_path=PackedInt32Array(1239853450, 2068119799) index="0" unique_id=1824387143]
|
||||||
bones/0/position = Vector3(0.00060560287, 0.9719096, 0.007789615)
|
bones/0/position = Vector3(0.00060560287, 0.9719096, 0.007789615)
|
||||||
bones/0/rotation = Quaternion(-0.03158553, -0.024531638, -0.086598895, 0.9954402)
|
bones/0/rotation = Quaternion(-0.03158553, -0.024531638, -0.086598895, 0.9954402)
|
||||||
bones/1/rotation = Quaternion(-0.068302006, -0.011589706, 0.088431515, 0.99367017)
|
bones/1/rotation = Quaternion(-0.068302006, -0.011589706, 0.088431515, 0.99367017)
|
||||||
@@ -183,13 +249,24 @@ bones/53/rotation = Quaternion(-0.20515193, 0.049833227, 0.0071537, 0.9774345)
|
|||||||
bones/54/rotation = Quaternion(0.55275166, -0.049315806, -0.007871797, 0.8318483)
|
bones/54/rotation = Quaternion(0.55275166, -0.049315806, -0.007871797, 0.8318483)
|
||||||
bones/55/rotation = Quaternion(0.326561, -0.058840513, 0.019667907, 0.9431378)
|
bones/55/rotation = Quaternion(0.326561, -0.058840513, 0.019667907, 0.9431378)
|
||||||
|
|
||||||
[node name="AnimationPlayer" parent="Farmer2" index="1" unique_id=916643412]
|
[node name="hat_001" parent="Farmer2/BaseMesh/Skeleton3D" index="0" unique_id=1838787788]
|
||||||
|
surface_material_override/0 = SubResource("ShaderMaterial_v2pkc")
|
||||||
|
surface_material_override/1 = ExtResource("11_v2pkc")
|
||||||
|
|
||||||
|
[node name="pants" parent="Farmer2/BaseMesh/Skeleton3D" index="1" unique_id=1777820543]
|
||||||
|
surface_material_override/0 = ExtResource("11_v2pkc")
|
||||||
|
|
||||||
|
[node name="shirt1" parent="Farmer2/BaseMesh/Skeleton3D" index="2" unique_id=1243989703]
|
||||||
|
surface_material_override/0 = ExtResource("11_v2pkc")
|
||||||
|
surface_material_override/1 = ExtResource("11_v2pkc")
|
||||||
|
|
||||||
|
[node name="AnimationPlayer" parent="Farmer2" index="1" unique_id=995025267]
|
||||||
libraries/custom = SubResource("AnimationLibrary_2tpdi")
|
libraries/custom = SubResource("AnimationLibrary_2tpdi")
|
||||||
|
|
||||||
[node name="DressHuman" type="Node3D" parent="." index="6" unique_id=107727715 node_paths=PackedStringArray("farmers", "main_anim_player")]
|
[node name="DressHuman" type="Node3D" parent="." index="6" unique_id=107727715 node_paths=PackedStringArray("farmers", "main_anim_player")]
|
||||||
script = ExtResource("9_2tpdi")
|
script = ExtResource("9_2tpdi")
|
||||||
farmers = [NodePath("../Farmer1"), NodePath("../Farmer2")]
|
farmers = [NodePath("../Farmer1"), NodePath("../Farmer2")]
|
||||||
colors = Array[Color]([Color(0, 0, 0, 1), Color(1, 1, 1, 1), Color(1, 0, 0, 1), Color(0, 1, 0, 1), Color(0, 0, 1, 1)])
|
colors = Array[Color]([Color(0.4089676, 0.23789883, 0.083061926, 1), Color(0.4862745, 0.60784316, 0.6901961, 1), Color(0.3529412, 0.41960785, 0.32941177, 1), Color(0.31533447, 0.3330399, 0.29725653, 1)])
|
||||||
main_anim_player = NodePath("../BaseMesh/AnimationPlayer")
|
main_anim_player = NodePath("../BaseMesh/AnimationPlayer")
|
||||||
|
|
||||||
[editable path="BaseMesh"]
|
[editable path="BaseMesh"]
|
||||||
|
|||||||
@@ -4,6 +4,71 @@
|
|||||||
[ext_resource type="Script" uid="uid://d3hy70ec8vqo5" path="res://core/ai/framework/run_animation_state.gd" id="2_euyvm"]
|
[ext_resource type="Script" uid="uid://d3hy70ec8vqo5" path="res://core/ai/framework/run_animation_state.gd" id="2_euyvm"]
|
||||||
[ext_resource type="PackedScene" uid="uid://dg6eq5bjdv77x" path="res://core/ai/agents/wagyu/Wagyu_rig.fbx" id="2_kinfw"]
|
[ext_resource type="PackedScene" uid="uid://dg6eq5bjdv77x" path="res://core/ai/agents/wagyu/Wagyu_rig.fbx" id="2_kinfw"]
|
||||||
[ext_resource type="Script" uid="uid://bngfthvt04ivv" path="res://core/ai/framework/patrol_state.gd" id="3_stslt"]
|
[ext_resource type="Script" uid="uid://bngfthvt04ivv" path="res://core/ai/framework/patrol_state.gd" id="3_stslt"]
|
||||||
|
[ext_resource type="Shader" uid="uid://d0ch5ofrgf7y6" path="res://core/daynight/trunk_shader.gdshader" id="5_stslt"]
|
||||||
|
[ext_resource type="Material" uid="uid://cn20mp8ep3yxu" path="res://tgcc/chunk/material/wires.tres" id="7_qxgon"]
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_jndo6"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("5_stslt")
|
||||||
|
shader_parameter/albedo_color = Color(0.3003036, 0.13408488, 0.037156556, 1)
|
||||||
|
shader_parameter/use_texture = true
|
||||||
|
shader_parameter/uv_scale = Vector2(3, 3)
|
||||||
|
shader_parameter/palette_shift_y = 0.0
|
||||||
|
shader_parameter/gradient_start_y = 0.0
|
||||||
|
shader_parameter/gradient_end_y = 1.5
|
||||||
|
shader_parameter/light_steps = 3.0
|
||||||
|
shader_parameter/step_softness = 0.1
|
||||||
|
shader_parameter/shadow_color = Color(0.4, 0.4, 0.6, 1)
|
||||||
|
shader_parameter/shadow_offset = 0.0
|
||||||
|
shader_parameter/cast_shadow_strength = 0.6
|
||||||
|
shader_parameter/use_ghibli_glint = true
|
||||||
|
shader_parameter/glint_color = Color(1, 0.95, 0.85, 1)
|
||||||
|
shader_parameter/glint_intensity = 1.0
|
||||||
|
shader_parameter/glint_sharpness = 32.0
|
||||||
|
shader_parameter/emission_color = Color(0, 0, 0, 1)
|
||||||
|
shader_parameter/emission_energy = 0.0
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_ffprj"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("5_stslt")
|
||||||
|
shader_parameter/albedo_color = Color(0.8936267, 0.48305064, 0.25341055, 1)
|
||||||
|
shader_parameter/use_texture = true
|
||||||
|
shader_parameter/uv_scale = Vector2(3, 3)
|
||||||
|
shader_parameter/palette_shift_y = 0.0
|
||||||
|
shader_parameter/gradient_start_y = 0.0
|
||||||
|
shader_parameter/gradient_end_y = 1.5
|
||||||
|
shader_parameter/light_steps = 3.0
|
||||||
|
shader_parameter/step_softness = 0.1
|
||||||
|
shader_parameter/shadow_color = Color(0.4, 0.4, 0.6, 1)
|
||||||
|
shader_parameter/shadow_offset = 0.0
|
||||||
|
shader_parameter/cast_shadow_strength = 0.6
|
||||||
|
shader_parameter/use_ghibli_glint = true
|
||||||
|
shader_parameter/glint_color = Color(1, 0.95, 0.85, 1)
|
||||||
|
shader_parameter/glint_intensity = 1.0
|
||||||
|
shader_parameter/glint_sharpness = 32.0
|
||||||
|
shader_parameter/emission_color = Color(0, 0, 0, 1)
|
||||||
|
shader_parameter/emission_energy = 0.0
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_4po7g"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("5_stslt")
|
||||||
|
shader_parameter/albedo_color = Color(0.91262335, 0.6514041, 0.37075257, 1)
|
||||||
|
shader_parameter/use_texture = true
|
||||||
|
shader_parameter/uv_scale = Vector2(3, 3)
|
||||||
|
shader_parameter/palette_shift_y = 0.0
|
||||||
|
shader_parameter/gradient_start_y = 0.0
|
||||||
|
shader_parameter/gradient_end_y = 1.5
|
||||||
|
shader_parameter/light_steps = 3.0
|
||||||
|
shader_parameter/step_softness = 0.1
|
||||||
|
shader_parameter/shadow_color = Color(0.4, 0.4, 0.6, 1)
|
||||||
|
shader_parameter/shadow_offset = 0.0
|
||||||
|
shader_parameter/cast_shadow_strength = 0.6
|
||||||
|
shader_parameter/use_ghibli_glint = true
|
||||||
|
shader_parameter/glint_color = Color(1, 0.95, 0.85, 1)
|
||||||
|
shader_parameter/glint_intensity = 1.0
|
||||||
|
shader_parameter/glint_sharpness = 32.0
|
||||||
|
shader_parameter/emission_color = Color(0, 0, 0, 1)
|
||||||
|
shader_parameter/emission_energy = 0.0
|
||||||
|
|
||||||
[node name="AiWagyu" unique_id=1228675528 node_paths=PackedStringArray("anim_player") instance=ExtResource("1_01jto")]
|
[node name="AiWagyu" unique_id=1228675528 node_paths=PackedStringArray("anim_player") instance=ExtResource("1_01jto")]
|
||||||
anim_player = NodePath("Wagyu_rig/AnimationPlayer")
|
anim_player = NodePath("Wagyu_rig/AnimationPlayer")
|
||||||
@@ -28,7 +93,7 @@ exit_on_action_finished = true
|
|||||||
|
|
||||||
[node name="Wagyu_rig" parent="." index="3" unique_id=434062845 instance=ExtResource("2_kinfw")]
|
[node name="Wagyu_rig" parent="." index="3" unique_id=434062845 instance=ExtResource("2_kinfw")]
|
||||||
|
|
||||||
[node name="Skeleton3D" parent="Wagyu_rig/Rig_wagyu" parent_id_path=PackedInt32Array(434062845, 984050088) index="0" unique_id=1381831503]
|
[node name="Skeleton3D" parent="Wagyu_rig/Rig_wagyu" parent_id_path=PackedInt32Array(434062845, 1389976890) index="0" unique_id=1274413548]
|
||||||
bones/1/position = Vector3(3.0605813e-06, 0.93198204, 1.5827273)
|
bones/1/position = Vector3(3.0605813e-06, 0.93198204, 1.5827273)
|
||||||
bones/1/rotation = Quaternion(0.12769, -0.69548225, -0.12769146, 0.6954816)
|
bones/1/rotation = Quaternion(0.12769, -0.69548225, -0.12769146, 0.6954816)
|
||||||
bones/2/rotation = Quaternion(-7.474808e-08, -1.0355375e-07, -0.5904155, 0.80709946)
|
bones/2/rotation = Quaternion(-7.474808e-08, -1.0355375e-07, -0.5904155, 0.80709946)
|
||||||
@@ -51,4 +116,10 @@ bones/26/rotation = Quaternion(3.2779312e-07, 1.2327837e-06, 0.9652686, -0.26125
|
|||||||
bones/27/rotation = Quaternion(-6.191086e-07, -2.305114e-07, -0.36804327, 0.9298087)
|
bones/27/rotation = Quaternion(-6.191086e-07, -2.305114e-07, -0.36804327, 0.9298087)
|
||||||
bones/28/rotation = Quaternion(-0.0001528551, -7.8230434e-05, 0.45790893, 0.8889991)
|
bones/28/rotation = Quaternion(-0.0001528551, -7.8230434e-05, 0.45790893, 0.8889991)
|
||||||
|
|
||||||
|
[node name="Wagyu" parent="Wagyu_rig/Rig_wagyu/Skeleton3D" index="0" unique_id=2005444162]
|
||||||
|
surface_material_override/0 = SubResource("ShaderMaterial_jndo6")
|
||||||
|
surface_material_override/1 = SubResource("ShaderMaterial_ffprj")
|
||||||
|
surface_material_override/2 = ExtResource("7_qxgon")
|
||||||
|
surface_material_override/3 = SubResource("ShaderMaterial_4po7g")
|
||||||
|
|
||||||
[editable path="Wagyu_rig"]
|
[editable path="Wagyu_rig"]
|
||||||
|
|||||||
@@ -86,7 +86,9 @@ const RIVER_NEIGHBOUR_OFFSETS: Dictionary = {
|
|||||||
##max cells to search around a lamppost chunk
|
##max cells to search around a lamppost chunk
|
||||||
@export_range(1, 6, 1) var lamppost_search_radius_cells: int = 3
|
@export_range(1, 6, 1) var lamppost_search_radius_cells: int = 3
|
||||||
##max allowed distance between lamppost pairs
|
##max allowed distance between lamppost pairs
|
||||||
@export var lamppost_max_wire_distance: float = 60.0
|
@export var lamppost_max_wire_distance: float = 35.0
|
||||||
|
##minimum horizontal distance between generated wires and the railway centerline
|
||||||
|
@export var lamppost_rail_clearance_distance: float = 4.0
|
||||||
##if true wire connections blocked by colliders are discarded
|
##if true wire connections blocked by colliders are discarded
|
||||||
@export var lamppost_obstacle_check_enabled: bool = true
|
@export var lamppost_obstacle_check_enabled: bool = true
|
||||||
|
|
||||||
@@ -210,6 +212,17 @@ func _physics_process(_delta: float) -> void:
|
|||||||
last_pos_train = current_pos
|
last_pos_train = current_pos
|
||||||
_refresh_world(current_pos)
|
_refresh_world(current_pos)
|
||||||
|
|
||||||
|
func generate_initial_world_now() -> void:
|
||||||
|
if rail_path == null or rail_path.train_instance == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
var train_pos = rail_path.train_instance.global_position
|
||||||
|
var current_pos = Vector2i(roundi(train_pos.x / chunk_size), roundi(train_pos.z / chunk_size))
|
||||||
|
last_pos_train = current_pos
|
||||||
|
_rebuild_generation_queue(current_pos)
|
||||||
|
_drain_generation_queue_now()
|
||||||
|
_drain_wire_queue_now()
|
||||||
|
|
||||||
func collect_all_chunkinfo(root: Node, list: Array[Node]) -> void:
|
func collect_all_chunkinfo(root: Node, list: Array[Node]) -> void:
|
||||||
if root == null: return
|
if root == null: return
|
||||||
|
|
||||||
@@ -533,6 +546,20 @@ func _drain_generation_queue() -> void:
|
|||||||
pending_generation_cells.clear()
|
pending_generation_cells.clear()
|
||||||
pending_generation_cursor = 0
|
pending_generation_cursor = 0
|
||||||
|
|
||||||
|
func _drain_generation_queue_now() -> void:
|
||||||
|
while pending_generation_cursor < pending_generation_cells.size():
|
||||||
|
var grid_pos: Vector2i = pending_generation_cells[pending_generation_cursor]
|
||||||
|
pending_generation_cursor += 1
|
||||||
|
if board.has(grid_pos):
|
||||||
|
continue
|
||||||
|
|
||||||
|
var is_obstacle = _register_cell_with_ray(grid_pos)
|
||||||
|
if not is_obstacle:
|
||||||
|
_add_compatible_biome(grid_pos)
|
||||||
|
|
||||||
|
pending_generation_cells.clear()
|
||||||
|
pending_generation_cursor = 0
|
||||||
|
|
||||||
func _drain_cleanup_queue() -> void:
|
func _drain_cleanup_queue() -> void:
|
||||||
var processed: int = 0
|
var processed: int = 0
|
||||||
var start_usec: int = Time.get_ticks_usec()
|
var start_usec: int = Time.get_ticks_usec()
|
||||||
@@ -593,6 +620,22 @@ func _drain_wire_queue() -> void:
|
|||||||
pending_wire_cells.clear()
|
pending_wire_cells.clear()
|
||||||
pending_wire_cursor = 0
|
pending_wire_cursor = 0
|
||||||
|
|
||||||
|
func _drain_wire_queue_now() -> void:
|
||||||
|
while pending_wire_cursor < pending_wire_cells.size():
|
||||||
|
var grid_pos: Vector2i = pending_wire_cells[pending_wire_cursor]
|
||||||
|
pending_wire_cursor += 1
|
||||||
|
pending_wire_lookup.erase(grid_pos)
|
||||||
|
|
||||||
|
if not board.has(grid_pos):
|
||||||
|
continue
|
||||||
|
if not board[grid_pos].get("have_lamppost", false):
|
||||||
|
continue
|
||||||
|
|
||||||
|
_connect_lamppost_wires(grid_pos)
|
||||||
|
|
||||||
|
pending_wire_cells.clear()
|
||||||
|
pending_wire_cursor = 0
|
||||||
|
|
||||||
func _generate_pieces_around_train(center: Vector2i) -> void:
|
func _generate_pieces_around_train(center: Vector2i) -> void:
|
||||||
for x in range(-eye_line, eye_line + 1):
|
for x in range(-eye_line, eye_line + 1):
|
||||||
for z in range(-eye_line, eye_line + 1):
|
for z in range(-eye_line, eye_line + 1):
|
||||||
@@ -1423,6 +1466,25 @@ func _get_wire_sample_point(p1: Vector3, p2: Vector3, t: float) -> Vector3:
|
|||||||
point.y -= gravity
|
point.y -= gravity
|
||||||
return point
|
return point
|
||||||
|
|
||||||
|
func _is_wire_too_close_to_rail(p1: Vector3, p2: Vector3) -> bool:
|
||||||
|
if rail_path == null or rail_path.curve == null:
|
||||||
|
return false
|
||||||
|
if lamppost_rail_clearance_distance <= 0.0:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var clearance_sq: float = lamppost_rail_clearance_distance * lamppost_rail_clearance_distance
|
||||||
|
var samples: int = 10
|
||||||
|
for i in range(samples + 1):
|
||||||
|
var t: float = float(i) / float(samples)
|
||||||
|
var wire_point: Vector3 = _get_wire_sample_point(p1, p2, t)
|
||||||
|
var rail_local_point: Vector3 = rail_path.to_local(wire_point)
|
||||||
|
var closest_rail_point: Vector3 = rail_path.to_global(rail_path.curve.get_closest_point(rail_local_point))
|
||||||
|
var horizontal_delta := Vector2(wire_point.x - closest_rail_point.x, wire_point.z - closest_rail_point.z)
|
||||||
|
if horizontal_delta.length_squared() < clearance_sq:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
func _collect_wire_excluded_rids(root: Node, excluded_rids: Array[RID]) -> void:
|
func _collect_wire_excluded_rids(root: Node, excluded_rids: Array[RID]) -> void:
|
||||||
if root == null:
|
if root == null:
|
||||||
return
|
return
|
||||||
@@ -1441,6 +1503,9 @@ func _get_wire_excluded_rids(excluded_roots: Array) -> Array[RID]:
|
|||||||
return excluded_rids
|
return excluded_rids
|
||||||
|
|
||||||
func _is_wire_path_blocked(p1: Vector3, p2: Vector3, excluded_roots: Array = []) -> bool:
|
func _is_wire_path_blocked(p1: Vector3, p2: Vector3, excluded_roots: Array = []) -> bool:
|
||||||
|
if _is_wire_too_close_to_rail(p1, p2):
|
||||||
|
return true
|
||||||
|
|
||||||
if not lamppost_obstacle_check_enabled:
|
if not lamppost_obstacle_check_enabled:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
extends Path3D
|
extends Path3D
|
||||||
|
|
||||||
const STEAM_DISTANCE_STAT: String = "stat_distance_km"
|
const DISTANCE_KM_PER_UNIT: float = 0.01
|
||||||
const STEAM_DISTANCE_KM_PER_UNIT: float = 1#0.001
|
const STATION_STOP_GROUP: StringName = &"railway_station"
|
||||||
|
const TRACK_ZERO_SPACING_FIX_DISTANCE: float = 45.0
|
||||||
|
const TRACK_ZERO_SPACING_BLEND_DISTANCE: float = 18.0
|
||||||
|
const TRACK_ZERO_SPACING_SEARCH_EXTRA: float = 35.0
|
||||||
|
const TRACK_ZERO_SPACING_SEARCH_STEP: float = 0.5
|
||||||
|
const TRACK_ZERO_SPACING_TARGET_RATIO: float = 0.97
|
||||||
|
const TRACK_ZERO_SPACING_MIN_DISTANCE: float = 1.0
|
||||||
|
|
||||||
|
enum TrainStartMode { RANDOM_POSITION, SPECIFIED_STATION, SPECIFIED_POSITION }
|
||||||
|
enum TrainFacingDirection { KEEP_PATH_DIRECTION, LOCOMOTIVE_LEFT, LOCOMOTIVE_RIGHT }
|
||||||
|
|
||||||
@export_group("Train")
|
@export_group("Train")
|
||||||
##train speed
|
##train speed
|
||||||
@@ -31,11 +40,24 @@ const STEAM_DISTANCE_KM_PER_UNIT: float = 1#0.001
|
|||||||
##swing of the train during the ran
|
##swing of the train during the ran
|
||||||
@export_range(0.0, 1.0) var basic_swing: float = 0.1
|
@export_range(0.0, 1.0) var basic_swing: float = 0.1
|
||||||
|
|
||||||
|
@export_group("Train Start")
|
||||||
|
@export_enum("random_position", "specified_station", "specified_position") var train_start_mode: int = TrainStartMode.RANDOM_POSITION
|
||||||
|
@export_range(0, 64, 1, "or_greater") var train_start_stop_index: int = 1
|
||||||
|
@export var train_start_position: float = 0.0
|
||||||
|
@export var train_start_delay_seconds: float = 0.0
|
||||||
|
@export var train_start_after_delay: bool = true
|
||||||
|
@export var train_start_acceleration_seconds: float = 4.0
|
||||||
|
@export_enum("keep_path_direction", "locomotive_left", "locomotive_right") var train_facing_direction: int = TrainFacingDirection.KEEP_PATH_DIRECTION
|
||||||
|
|
||||||
@export_group("Rails Bulding")
|
@export_group("Rails Bulding")
|
||||||
|
##if true builds visible rail pieces along the Path3D
|
||||||
|
@export var build_track_visuals: bool = true
|
||||||
##distance between sleepers of the rails
|
##distance between sleepers of the rails
|
||||||
@export var sleepers_distance: float = 1.0
|
@export var sleepers_distance: float = 1.0
|
||||||
##nodel for the sleeper of the rail
|
##nodel for the sleeper of the rail
|
||||||
@export var sleepers_model: PackedScene
|
@export var sleepers_model: PackedScene
|
||||||
|
##if true add procedural rail bars even when sleepers_model is assigned
|
||||||
|
@export var add_procedural_rails_to_sleepers_model: bool = false
|
||||||
|
|
||||||
@export_group("Manual Controls")
|
@export_group("Manual Controls")
|
||||||
##max speed
|
##max speed
|
||||||
@@ -48,6 +70,10 @@ const STEAM_DISTANCE_KM_PER_UNIT: float = 1#0.001
|
|||||||
@export_group("Train Stops")
|
@export_group("Train Stops")
|
||||||
##if true the train stop the ran on stops
|
##if true the train stop the ran on stops
|
||||||
@export var enable_stops: bool = true
|
@export var enable_stops: bool = true
|
||||||
|
##if true the train only stops on markers with a generated station nearby
|
||||||
|
@export var require_station_for_stop: bool = true
|
||||||
|
##max horizontal distance between a stop marker and a station chunk
|
||||||
|
@export var station_stop_detection_radius: float = 35.0
|
||||||
##time of stop
|
##time of stop
|
||||||
@export var stop_time: float = 4.0
|
@export var stop_time: float = 4.0
|
||||||
##distance when the train start to brake
|
##distance when the train start to brake
|
||||||
@@ -79,42 +105,29 @@ var next_stop_index: int = 0
|
|||||||
var stop_multiply: float = 1.0
|
var stop_multiply: float = 1.0
|
||||||
var is_restarting: bool = false
|
var is_restarting: bool = false
|
||||||
var tween_restart: Tween
|
var tween_restart: Tween
|
||||||
var environment_config: EnvironmentConfig
|
|
||||||
var wagon_instances: Array[Node3D] = []
|
var wagon_instances: Array[Node3D] = []
|
||||||
var wagon_progress_offsets: Array[float] = []
|
var wagon_progress_offsets: Array[float] = []
|
||||||
var _pending_steam_distance_km: float = 0.0
|
var _initial_is_inmotion: bool = true
|
||||||
|
var _is_photo_mode_active: bool = false
|
||||||
|
var _train_direction_sign: float = 1.0
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
randomize()
|
randomize()
|
||||||
|
GameState.on_enable_photo_mode_request.connect(func(): _is_photo_mode_active = true)
|
||||||
|
GameState.on_disable_photo_mode_request.connect(func(): _is_photo_mode_active = false)
|
||||||
|
|
||||||
_cache_environment_config()
|
_initial_is_inmotion = is_inmotion
|
||||||
|
|
||||||
if curve != null and curve.get_baked_length() > 0:
|
if curve != null and curve.get_baked_length() > 0:
|
||||||
build_rails()
|
if build_track_visuals:
|
||||||
|
build_rails()
|
||||||
build_train()
|
build_train()
|
||||||
_plan_stops()
|
_plan_stops()
|
||||||
_apply_initial_train_start()
|
_apply_initial_train_start()
|
||||||
|
_apply_train_start_delay()
|
||||||
else:
|
else:
|
||||||
print("WARNING: Draw Path3D for rails!")
|
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:
|
func _apply_initial_train_start() -> void:
|
||||||
if train_instance == null or curve == null:
|
if train_instance == null or curve == null:
|
||||||
return
|
return
|
||||||
@@ -123,21 +136,68 @@ func _apply_initial_train_start() -> void:
|
|||||||
if total_length <= 0.0:
|
if total_length <= 0.0:
|
||||||
return
|
return
|
||||||
|
|
||||||
#start from a random position
|
match train_start_mode:
|
||||||
if environment_config != null and environment_config.train_start_from_random_position:
|
TrainStartMode.RANDOM_POSITION:
|
||||||
train_progress = randf() * total_length
|
train_progress = randf() * total_length
|
||||||
_update_next_stop_index_from_progress()
|
_update_next_stop_index_from_progress()
|
||||||
elif environment_config != null and not stop_offset.is_empty():
|
TrainStartMode.SPECIFIED_STATION:
|
||||||
#start from a specific stop
|
if stop_offset.is_empty():
|
||||||
var stop_index: int = clampi(environment_config.train_start_stop_index, 0, stop_offset.size() - 1)
|
train_progress = wrapf(train_progress, 0.0, total_length)
|
||||||
train_progress = stop_offset[stop_index]
|
_update_next_stop_index_from_progress()
|
||||||
_set_next_stop_index_from_current(stop_index)
|
else:
|
||||||
else:
|
var stop_index: int = clampi(train_start_stop_index, 0, stop_offset.size() - 1)
|
||||||
train_progress = wrapf(train_progress, 0.0, total_length)
|
train_progress = stop_offset[stop_index]
|
||||||
_update_next_stop_index_from_progress()
|
_set_next_stop_index_from_current(stop_index)
|
||||||
|
TrainStartMode.SPECIFIED_POSITION:
|
||||||
|
train_progress = wrapf(train_start_position, 0.0, total_length)
|
||||||
|
_update_next_stop_index_from_progress()
|
||||||
|
|
||||||
|
_apply_train_facing_direction(total_length)
|
||||||
|
_update_next_stop_index_from_progress()
|
||||||
_snap_train_to_progress()
|
_snap_train_to_progress()
|
||||||
|
|
||||||
|
func _apply_train_facing_direction(total_length: float) -> void:
|
||||||
|
_train_direction_sign = 1.0
|
||||||
|
if train_facing_direction == TrainFacingDirection.KEEP_PATH_DIRECTION or curve == null:
|
||||||
|
return
|
||||||
|
if total_length <= 0.0:
|
||||||
|
return
|
||||||
|
|
||||||
|
var center_progress: float = wrapf(train_progress, 0.0, total_length)
|
||||||
|
var center_position: Vector3 = to_global(curve.sample_baked(center_progress, true))
|
||||||
|
var forward_progress: float = wrapf(center_progress + 2.0, 0.0, total_length)
|
||||||
|
var forward_position: Vector3 = to_global(curve.sample_baked(forward_progress, true))
|
||||||
|
var forward_direction: Vector3 = forward_position - center_position
|
||||||
|
if forward_direction.length_squared() <= 0.0001 or is_zero_approx(forward_direction.x):
|
||||||
|
return
|
||||||
|
|
||||||
|
var desired_x_sign: float = -1.0 if train_facing_direction == TrainFacingDirection.LOCOMOTIVE_LEFT else 1.0
|
||||||
|
if forward_direction.x * desired_x_sign < 0.0:
|
||||||
|
_train_direction_sign = -1.0
|
||||||
|
|
||||||
|
if not is_zero_approx(train_speed):
|
||||||
|
train_speed = absf(train_speed) * _train_direction_sign
|
||||||
|
|
||||||
|
func _apply_train_start_delay() -> void:
|
||||||
|
if not _initial_is_inmotion:
|
||||||
|
return
|
||||||
|
|
||||||
|
var should_start_after_delay: bool = train_start_after_delay
|
||||||
|
var start_delay_seconds: float = maxf(train_start_delay_seconds, 0.0)
|
||||||
|
if start_delay_seconds <= 0.0:
|
||||||
|
if not should_start_after_delay:
|
||||||
|
is_inmotion = false
|
||||||
|
return
|
||||||
|
|
||||||
|
is_inmotion = false
|
||||||
|
await get_tree().create_timer(start_delay_seconds).timeout
|
||||||
|
if not is_inside_tree():
|
||||||
|
return
|
||||||
|
if not should_start_after_delay:
|
||||||
|
return
|
||||||
|
|
||||||
|
is_inmotion = _initial_is_inmotion
|
||||||
|
|
||||||
func _set_next_stop_index_from_current(current_stop_index: int) -> void:
|
func _set_next_stop_index_from_current(current_stop_index: int) -> void:
|
||||||
if stop_offset.is_empty():
|
if stop_offset.is_empty():
|
||||||
next_stop_index = 0
|
next_stop_index = 0
|
||||||
@@ -179,13 +239,9 @@ func _snap_train_to_progress() -> void:
|
|||||||
|
|
||||||
train_progress = wrapf(train_progress, 0.0, total_length)
|
train_progress = wrapf(train_progress, 0.0, total_length)
|
||||||
var center_position: Vector3 = to_global(curve.sample_baked(train_progress, true))
|
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
|
train_instance.global_position = center_position
|
||||||
if center_position.distance_to(forward_position) > 0.01:
|
_orient_vehicle_to_track(train_instance, train_progress, total_length)
|
||||||
train_instance.look_at(forward_position, Vector3.UP)
|
|
||||||
train_instance.rotate_y(PI)
|
|
||||||
|
|
||||||
if cameras:
|
if cameras:
|
||||||
cameras.global_position = center_position
|
cameras.global_position = center_position
|
||||||
@@ -207,6 +263,7 @@ func build_rails() -> void:
|
|||||||
|
|
||||||
var total_length = curve.get_baked_length()
|
var total_length = curve.get_baked_length()
|
||||||
var piece_number = int(total_length / sleepers_distance)
|
var piece_number = int(total_length / sleepers_distance)
|
||||||
|
var should_add_procedural_rails: bool = sleepers_model == null or add_procedural_rails_to_sleepers_model
|
||||||
|
|
||||||
for i in range(piece_number):
|
for i in range(piece_number):
|
||||||
var distance = i * sleepers_distance
|
var distance = i * sleepers_distance
|
||||||
@@ -238,19 +295,23 @@ func build_rails() -> void:
|
|||||||
sleeper.material_override = mat_wood
|
sleeper.material_override = mat_wood
|
||||||
rail_piece.add_child(sleeper)
|
rail_piece.add_child(sleeper)
|
||||||
|
|
||||||
var rail_sx = MeshInstance3D.new()
|
if should_add_procedural_rails:
|
||||||
rail_sx.mesh = mesh_rail
|
var rail_sx = MeshInstance3D.new()
|
||||||
rail_sx.material_override = mat_iron
|
rail_sx.mesh = mesh_rail
|
||||||
rail_sx.position = Vector3(-rail_distance / 2.0, 0.1, 0)
|
rail_sx.material_override = mat_iron
|
||||||
rail_piece.add_child(rail_sx)
|
rail_sx.position = Vector3(-rail_distance / 2.0, 0.1, 0)
|
||||||
|
rail_piece.add_child(rail_sx)
|
||||||
|
|
||||||
var rail_dx = MeshInstance3D.new()
|
var rail_dx = MeshInstance3D.new()
|
||||||
rail_dx.mesh = mesh_rail
|
rail_dx.mesh = mesh_rail
|
||||||
rail_dx.material_override = mat_iron
|
rail_dx.material_override = mat_iron
|
||||||
rail_dx.position = Vector3(rail_distance / 2.0, 0.1, 0)
|
rail_dx.position = Vector3(rail_distance / 2.0, 0.1, 0)
|
||||||
rail_piece.add_child(rail_dx)
|
rail_piece.add_child(rail_dx)
|
||||||
|
|
||||||
func _plan_stops() -> void:
|
func _plan_stops() -> void:
|
||||||
|
stop_offset.clear()
|
||||||
|
stops_position.clear()
|
||||||
|
|
||||||
var current_stops = []
|
var current_stops = []
|
||||||
for child in get_children():
|
for child in get_children():
|
||||||
if child is Marker3D:
|
if child is Marker3D:
|
||||||
@@ -264,10 +325,63 @@ func _plan_stops() -> void:
|
|||||||
stop_offset.append(data["offset"])
|
stop_offset.append(data["offset"])
|
||||||
stops_position.append(data["position"])
|
stops_position.append(data["position"])
|
||||||
|
|
||||||
|
func _advance_next_stop_index(go_forward: bool) -> void:
|
||||||
|
if stop_offset.is_empty():
|
||||||
|
next_stop_index = 0
|
||||||
|
return
|
||||||
|
|
||||||
|
if go_forward:
|
||||||
|
next_stop_index += 1
|
||||||
|
if next_stop_index >= stop_offset.size():
|
||||||
|
next_stop_index = 0
|
||||||
|
else:
|
||||||
|
next_stop_index -= 1
|
||||||
|
if next_stop_index < 0:
|
||||||
|
next_stop_index = stop_offset.size() - 1
|
||||||
|
|
||||||
|
func _find_next_valid_stop(go_forward: bool) -> bool:
|
||||||
|
if stop_offset.is_empty():
|
||||||
|
return false
|
||||||
|
|
||||||
|
for i in range(stop_offset.size()):
|
||||||
|
if _is_stop_valid(next_stop_index):
|
||||||
|
return true
|
||||||
|
_advance_next_stop_index(go_forward)
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
func _is_stop_valid(stop_index: int) -> bool:
|
||||||
|
if not require_station_for_stop:
|
||||||
|
return true
|
||||||
|
if stop_index < 0 or stop_index >= stops_position.size():
|
||||||
|
return false
|
||||||
|
|
||||||
|
return _has_station_near_position(stops_position[stop_index])
|
||||||
|
|
||||||
|
func _has_station_near_position(stop_position: Vector3) -> bool:
|
||||||
|
var radius_sq := station_stop_detection_radius * station_stop_detection_radius
|
||||||
|
for station in get_tree().get_nodes_in_group(STATION_STOP_GROUP):
|
||||||
|
var station_node := station as Node3D
|
||||||
|
if station_node == null or not is_instance_valid(station_node):
|
||||||
|
continue
|
||||||
|
|
||||||
|
var delta := station_node.global_position - stop_position
|
||||||
|
var horizontal_dist_sq := delta.x * delta.x + delta.z * delta.z
|
||||||
|
if horizontal_dist_sq <= radius_sq:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
|
if _is_photo_mode_active: return
|
||||||
|
if EnvironmentManagerRoot.is_keyboard_input_blocked(get_tree()):
|
||||||
|
return
|
||||||
|
|
||||||
if event is InputEventKey and event.pressed and not event.echo:
|
if event is InputEventKey and event.pressed and not event.echo:
|
||||||
if event.keycode == KEY_T:
|
if event.keycode == KEY_T:
|
||||||
_goto_next_stop()
|
_goto_next_stop()
|
||||||
|
if event.is_action_pressed("train_horn"):
|
||||||
|
UIEvents.toot_toot.emit(true)
|
||||||
#elif event.keycode == KEY_F:
|
#elif event.keycode == KEY_F:
|
||||||
#_test_manual_fireworks()
|
#_test_manual_fireworks()
|
||||||
|
|
||||||
@@ -293,6 +407,9 @@ func _test_manual_fireworks() -> void:
|
|||||||
func _goto_next_stop() -> void:
|
func _goto_next_stop() -> void:
|
||||||
if stop_offset.size() == 0 or stop_ongoing or is_restarting:
|
if stop_offset.size() == 0 or stop_ongoing or is_restarting:
|
||||||
return
|
return
|
||||||
|
if not _find_next_valid_stop(train_speed >= 0.0):
|
||||||
|
return
|
||||||
|
|
||||||
var target_offset = stop_offset[next_stop_index]
|
var target_offset = stop_offset[next_stop_index]
|
||||||
train_progress = target_offset
|
train_progress = target_offset
|
||||||
_execute_stop(train_speed >= 0)
|
_execute_stop(train_speed >= 0)
|
||||||
@@ -302,12 +419,13 @@ func _physics_process(delta: float) -> void:
|
|||||||
train_move(delta)
|
train_move(delta)
|
||||||
|
|
||||||
func input_controls_management(delta: float) -> void:
|
func input_controls_management(delta: float) -> void:
|
||||||
if not is_inmotion: return
|
if EnvironmentManagerRoot.is_keyboard_input_blocked(get_tree()): return
|
||||||
if Input.is_action_pressed("ui_up"):
|
if not is_inmotion or _is_photo_mode_active: return
|
||||||
|
if Input.is_action_pressed("train_speed_up"):
|
||||||
train_speed += manual_acceleration * delta
|
train_speed += manual_acceleration * delta
|
||||||
elif Input.is_action_pressed("ui_down"):
|
elif Input.is_action_pressed("train_speed_down"):
|
||||||
train_speed -= manual_acceleration * delta
|
train_speed -= manual_acceleration * delta
|
||||||
elif Input.is_action_pressed("ui_text_backspace"):
|
elif Input.is_action_pressed("train_stop"):
|
||||||
train_speed = 0
|
train_speed = 0
|
||||||
train_speed = clamp(train_speed, speed_min, speed_max)
|
train_speed = clamp(train_speed, speed_min, speed_max)
|
||||||
|
|
||||||
@@ -318,23 +436,31 @@ func train_move(delta: float) -> void:
|
|||||||
|
|
||||||
if not stop_ongoing:
|
if not stop_ongoing:
|
||||||
var last_progress = train_progress
|
var last_progress = train_progress
|
||||||
|
var has_valid_stop := false
|
||||||
|
|
||||||
if enable_stops and stop_offset.size() > 0 and not is_restarting:
|
if enable_stops and stop_offset.size() > 0 and not is_restarting:
|
||||||
var target_offset = stop_offset[next_stop_index]
|
has_valid_stop = _find_next_valid_stop(train_speed >= 0.0)
|
||||||
var dist = target_offset - train_progress
|
if has_valid_stop:
|
||||||
dist = wrapf(dist + total_length / 2.0, 0.0, total_length) - total_length / 2.0
|
var target_offset = stop_offset[next_stop_index]
|
||||||
|
var dist = target_offset - train_progress
|
||||||
|
dist = wrapf(dist + total_length / 2.0, 0.0, total_length) - total_length / 2.0
|
||||||
|
|
||||||
if abs(dist) < brake_distance and sign(dist) == sign(train_speed):
|
if abs(dist) < brake_distance and sign(dist) == sign(train_speed):
|
||||||
var t = abs(dist) / brake_distance
|
var t = abs(dist) / brake_distance
|
||||||
stop_multiply = max(smoothstep(0.0, 1.0, t), 0.05)
|
stop_multiply = max(smoothstep(0.0, 1.0, t), 0.05)
|
||||||
|
else:
|
||||||
|
stop_multiply = 1.0
|
||||||
else:
|
else:
|
||||||
stop_multiply = 1.0
|
stop_multiply = 1.0
|
||||||
|
|
||||||
var current_speed = train_speed * stop_multiply
|
var current_speed = train_speed * stop_multiply
|
||||||
train_progress += current_speed * delta
|
var next_progress: float = train_progress + current_speed * delta
|
||||||
|
var wrapped_forward: bool = current_speed > 0.0 and next_progress >= total_length
|
||||||
|
var wrapped_back: bool = current_speed < 0.0 and next_progress < 0.0
|
||||||
|
train_progress = wrapf(next_progress, 0.0, total_length)
|
||||||
_track_steam_distance(abs(current_speed) * delta)
|
_track_steam_distance(abs(current_speed) * delta)
|
||||||
|
|
||||||
if enable_stops and stop_offset.size() > 0:
|
if enable_stops and stop_offset.size() > 0 and has_valid_stop:
|
||||||
var target_offset = stop_offset[next_stop_index]
|
var target_offset = stop_offset[next_stop_index]
|
||||||
var exceeded_forward = (current_speed > 0 and last_progress < target_offset and train_progress >= target_offset)
|
var exceeded_forward = (current_speed > 0 and last_progress < target_offset and train_progress >= target_offset)
|
||||||
var exceeded_back = (current_speed < 0 and last_progress > target_offset and train_progress <= target_offset)
|
var exceeded_back = (current_speed < 0 and last_progress > target_offset and train_progress <= target_offset)
|
||||||
@@ -343,11 +469,9 @@ func train_move(delta: float) -> void:
|
|||||||
train_progress = target_offset
|
train_progress = target_offset
|
||||||
_execute_stop(current_speed > 0)
|
_execute_stop(current_speed > 0)
|
||||||
|
|
||||||
if train_progress > total_length:
|
if wrapped_forward:
|
||||||
train_progress -= total_length
|
|
||||||
if stop_offset.size() > 0: next_stop_index = 0
|
if stop_offset.size() > 0: next_stop_index = 0
|
||||||
elif train_progress < 0:
|
elif wrapped_back:
|
||||||
train_progress += total_length
|
|
||||||
if stop_offset.size() > 0: next_stop_index = stop_offset.size() - 1
|
if stop_offset.size() > 0: next_stop_index = stop_offset.size() - 1
|
||||||
|
|
||||||
var center_position = to_global(curve.sample_baked(train_progress, true))
|
var center_position = to_global(curve.sample_baked(train_progress, true))
|
||||||
@@ -358,8 +482,7 @@ func train_move(delta: float) -> void:
|
|||||||
|
|
||||||
if center_position.distance_to(forward_position) > 0.01:
|
if center_position.distance_to(forward_position) > 0.01:
|
||||||
train_instance.global_position = center_position
|
train_instance.global_position = center_position
|
||||||
train_instance.look_at(forward_position, Vector3.UP)
|
_orient_vehicle_to_track(train_instance, train_progress, total_length)
|
||||||
train_instance.rotate_y(PI)
|
|
||||||
|
|
||||||
if cameras:
|
if cameras:
|
||||||
cameras.global_position = center_position
|
cameras.global_position = center_position
|
||||||
@@ -396,44 +519,22 @@ func _track_steam_distance(distance_units: float) -> void:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Always track the global distance in our save file (for UI and persistency)
|
# Always track the global distance in our save file (for UI and persistency)
|
||||||
GameState.save_data.total_distance_km += distance_units * STEAM_DISTANCE_KM_PER_UNIT
|
GameState.save_data.total_distance_km += distance_units * DISTANCE_KM_PER_UNIT
|
||||||
|
StatsManager.add_distance_km(distance_units * DISTANCE_KM_PER_UNIT)
|
||||||
# Steam specific logic
|
|
||||||
if not SteamManager.is_on_steam:
|
|
||||||
return
|
|
||||||
|
|
||||||
_pending_steam_distance_km += distance_units * STEAM_DISTANCE_KM_PER_UNIT
|
|
||||||
var whole_km: int = int(floor(_pending_steam_distance_km))
|
|
||||||
if whole_km <= 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
_pending_steam_distance_km -= float(whole_km)
|
|
||||||
var total_distance_km: int = StatsManager.get_int(STEAM_DISTANCE_STAT) + whole_km
|
|
||||||
StatsManager.set_int(STEAM_DISTANCE_STAT, total_distance_km)
|
|
||||||
_check_distance_achievements(total_distance_km)
|
|
||||||
StatsManager.store()
|
|
||||||
|
|
||||||
#Check achievements
|
|
||||||
func _check_distance_achievements(total_distance_km: int) -> void:
|
|
||||||
if total_distance_km >= 100:
|
|
||||||
AchievementManager.unlock("ACH_DISTANCE_100")
|
|
||||||
if total_distance_km >= 200:
|
|
||||||
AchievementManager.unlock("ACH_DISTANCE_200")
|
|
||||||
|
|
||||||
func _execute_stop(go_forward: bool = true) -> void:
|
func _execute_stop(go_forward: bool = true) -> void:
|
||||||
|
if not _find_next_valid_stop(go_forward):
|
||||||
|
stop_multiply = 1.0
|
||||||
|
return
|
||||||
|
|
||||||
stop_ongoing = true
|
stop_ongoing = true
|
||||||
stop_multiply = 0.0
|
stop_multiply = 0.0
|
||||||
|
|
||||||
var current_stop_index = next_stop_index
|
var current_stop_index = next_stop_index
|
||||||
|
StatsManager.add_int("stat_stop_number", 1)
|
||||||
|
StatsManager.store()
|
||||||
|
|
||||||
if go_forward:
|
_advance_next_stop_index(go_forward)
|
||||||
next_stop_index += 1
|
|
||||||
if next_stop_index >= stop_offset.size():
|
|
||||||
next_stop_index = 0
|
|
||||||
else:
|
|
||||||
next_stop_index -= 1
|
|
||||||
if next_stop_index < 0:
|
|
||||||
next_stop_index = stop_offset.size() - 1
|
|
||||||
|
|
||||||
if fireworks_scene != null:
|
if fireworks_scene != null:
|
||||||
var marker_position = stops_position[current_stop_index]
|
var marker_position = stops_position[current_stop_index]
|
||||||
@@ -573,6 +674,10 @@ func _snap_wagons_to_progress(total_length: float = 0.0) -> void:
|
|||||||
if total_length <= 0.0:
|
if total_length <= 0.0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
var previous_progress: float = wrapf(train_progress, 0.0, total_length)
|
||||||
|
var previous_position: Vector3 = to_global(curve.sample_baked(previous_progress, true))
|
||||||
|
var previous_wagon_offset: float = 0.0
|
||||||
|
|
||||||
for i in range(wagon_instances.size()):
|
for i in range(wagon_instances.size()):
|
||||||
var wagon: Node3D = wagon_instances[i]
|
var wagon: Node3D = wagon_instances[i]
|
||||||
if not is_instance_valid(wagon):
|
if not is_instance_valid(wagon):
|
||||||
@@ -582,17 +687,101 @@ func _snap_wagons_to_progress(total_length: float = 0.0) -> void:
|
|||||||
if i < wagon_progress_offsets.size():
|
if i < wagon_progress_offsets.size():
|
||||||
wagon_offset = wagon_progress_offsets[i]
|
wagon_offset = wagon_progress_offsets[i]
|
||||||
|
|
||||||
var wagon_progress: float = train_progress - wagon_offset
|
var wagon_progress: float = train_progress - wagon_offset * _train_direction_sign
|
||||||
|
var segment_spacing: float = maxf(wagon_offset - previous_wagon_offset, 0.1)
|
||||||
|
|
||||||
var vehicle_progress: float = wrapf(wagon_progress, 0.0, total_length)
|
var vehicle_progress: float = wrapf(wagon_progress, 0.0, total_length)
|
||||||
var center_position: Vector3 = to_global(curve.sample_baked(vehicle_progress, true))
|
var center_position: Vector3 = to_global(curve.sample_baked(vehicle_progress, true))
|
||||||
var prog_forward: float = wrapf(vehicle_progress + 2.0, 0.0, total_length)
|
var zero_spacing_fix_weight: float = _get_zero_spacing_fix_weight(previous_progress, vehicle_progress, total_length)
|
||||||
var forward_position: Vector3 = to_global(curve.sample_baked(prog_forward, true))
|
if zero_spacing_fix_weight > 0.0:
|
||||||
|
var target_distance: float = _get_zero_spacing_target_distance(segment_spacing)
|
||||||
|
var corrected_progress: float = _find_zero_spacing_progress(previous_progress, previous_position, segment_spacing, target_distance, total_length)
|
||||||
|
vehicle_progress = _lerp_progress_on_track(vehicle_progress, corrected_progress, zero_spacing_fix_weight, total_length)
|
||||||
|
center_position = to_global(curve.sample_baked(vehicle_progress, true))
|
||||||
|
|
||||||
wagon.global_position = center_position
|
wagon.global_position = center_position
|
||||||
if center_position.distance_to(forward_position) > 0.01:
|
_orient_vehicle_to_track(wagon, vehicle_progress, total_length)
|
||||||
wagon.look_at(forward_position, Vector3.UP)
|
|
||||||
wagon.rotate_y(PI)
|
previous_progress = vehicle_progress
|
||||||
|
previous_position = center_position
|
||||||
|
previous_wagon_offset = wagon_offset
|
||||||
|
|
||||||
|
func _orient_vehicle_to_track(vehicle: Node3D, progress: float, total_length: float) -> void:
|
||||||
|
if vehicle == null or curve == null or total_length <= 0.0:
|
||||||
|
return
|
||||||
|
|
||||||
|
var center_progress: float = wrapf(progress, 0.0, total_length)
|
||||||
|
var center_position: Vector3 = to_global(curve.sample_baked(center_progress, true))
|
||||||
|
var forward_progress: float = wrapf(center_progress + 2.0 * _train_direction_sign, 0.0, total_length)
|
||||||
|
var forward_position: Vector3 = to_global(curve.sample_baked(forward_progress, true))
|
||||||
|
if center_position.distance_to(forward_position) <= 0.01:
|
||||||
|
return
|
||||||
|
|
||||||
|
vehicle.look_at(forward_position, Vector3.UP)
|
||||||
|
vehicle.rotate_y(PI)
|
||||||
|
|
||||||
|
func _get_zero_spacing_fix_weight(previous_progress: float, vehicle_progress: float, total_length: float) -> float:
|
||||||
|
var distance_to_zero: float = minf(
|
||||||
|
_get_progress_distance_to_zero(previous_progress, total_length),
|
||||||
|
_get_progress_distance_to_zero(vehicle_progress, total_length)
|
||||||
|
)
|
||||||
|
var full_fix_distance: float = maxf(TRACK_ZERO_SPACING_FIX_DISTANCE - TRACK_ZERO_SPACING_BLEND_DISTANCE, 0.0)
|
||||||
|
return 1.0 - smoothstep(full_fix_distance, TRACK_ZERO_SPACING_FIX_DISTANCE, distance_to_zero)
|
||||||
|
|
||||||
|
func _get_progress_distance_to_zero(progress: float, total_length: float) -> float:
|
||||||
|
var wrapped_progress: float = wrapf(progress, 0.0, total_length)
|
||||||
|
return minf(wrapped_progress, total_length - wrapped_progress)
|
||||||
|
|
||||||
|
func _get_zero_spacing_target_distance(segment_spacing: float) -> float:
|
||||||
|
return minf(maxf(segment_spacing * TRACK_ZERO_SPACING_TARGET_RATIO, TRACK_ZERO_SPACING_MIN_DISTANCE), segment_spacing)
|
||||||
|
|
||||||
|
func _find_zero_spacing_progress(previous_progress: float, previous_position: Vector3, segment_spacing: float, minimum_distance: float, total_length: float) -> float:
|
||||||
|
var start_distance: float = minf(minimum_distance, segment_spacing)
|
||||||
|
var best_progress: float = wrapf(previous_progress - start_distance * _train_direction_sign, 0.0, total_length)
|
||||||
|
var search_limit: float = minf(maxf(segment_spacing, minimum_distance) + TRACK_ZERO_SPACING_SEARCH_EXTRA, total_length)
|
||||||
|
var search_distance: float = start_distance
|
||||||
|
var previous_search_distance: float = start_distance
|
||||||
|
|
||||||
|
while search_distance <= search_limit:
|
||||||
|
var candidate_progress: float = wrapf(previous_progress - search_distance * _train_direction_sign, 0.0, total_length)
|
||||||
|
var candidate_position: Vector3 = to_global(curve.sample_baked(candidate_progress, true))
|
||||||
|
best_progress = candidate_progress
|
||||||
|
if previous_position.distance_to(candidate_position) >= minimum_distance:
|
||||||
|
if is_equal_approx(search_distance, start_distance):
|
||||||
|
return candidate_progress
|
||||||
|
|
||||||
|
return _refine_zero_spacing_progress(
|
||||||
|
previous_progress,
|
||||||
|
previous_position,
|
||||||
|
previous_search_distance,
|
||||||
|
search_distance,
|
||||||
|
minimum_distance,
|
||||||
|
total_length
|
||||||
|
)
|
||||||
|
|
||||||
|
previous_search_distance = search_distance
|
||||||
|
search_distance += TRACK_ZERO_SPACING_SEARCH_STEP
|
||||||
|
|
||||||
|
return best_progress
|
||||||
|
|
||||||
|
func _refine_zero_spacing_progress(previous_progress: float, previous_position: Vector3, min_search_distance: float, max_search_distance: float, minimum_distance: float, total_length: float) -> float:
|
||||||
|
var low_distance: float = min_search_distance
|
||||||
|
var high_distance: float = max_search_distance
|
||||||
|
|
||||||
|
for i in range(8):
|
||||||
|
var mid_distance: float = (low_distance + high_distance) * 0.5
|
||||||
|
var mid_progress: float = wrapf(previous_progress - mid_distance, 0.0, total_length)
|
||||||
|
var mid_position: Vector3 = to_global(curve.sample_baked(mid_progress, true))
|
||||||
|
if previous_position.distance_to(mid_position) >= minimum_distance:
|
||||||
|
high_distance = mid_distance
|
||||||
|
else:
|
||||||
|
low_distance = mid_distance
|
||||||
|
|
||||||
|
return wrapf(previous_progress - high_distance, 0.0, total_length)
|
||||||
|
|
||||||
|
func _lerp_progress_on_track(from_progress: float, to_progress: float, weight: float, total_length: float) -> float:
|
||||||
|
var wrapped_delta: float = wrapf(to_progress - from_progress + total_length * 0.5, 0.0, total_length) - total_length * 0.5
|
||||||
|
return wrapf(from_progress + wrapped_delta * clampf(weight, 0.0, 1.0), 0.0, total_length)
|
||||||
|
|
||||||
func _get_vehicle_length(vehicle: Node3D) -> float:
|
func _get_vehicle_length(vehicle: Node3D) -> float:
|
||||||
var bounds: AABB = _get_node_local_bounds(vehicle, vehicle)
|
var bounds: AABB = _get_node_local_bounds(vehicle, vehicle)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
shader_type spatial;
|
shader_type spatial;
|
||||||
render_mode blend_mix, depth_draw_opaque, cull_disabled;
|
render_mode blend_mix, depth_draw_opaque, cull_disabled, shadows_disabled;
|
||||||
|
|
||||||
//Wire color
|
//Wire color
|
||||||
uniform vec4 albedo_color : source_color = vec4(0.1, 0.1, 0.1, 1.0);
|
uniform vec4 albedo_color : source_color = vec4(0.1, 0.1, 0.1, 1.0);
|
||||||
|
|||||||
@@ -5,3 +5,4 @@
|
|||||||
[resource]
|
[resource]
|
||||||
render_priority = 0
|
render_priority = 0
|
||||||
shader = ExtResource("1_6saq1")
|
shader = ExtResource("1_6saq1")
|
||||||
|
shader_parameter/albedo_color = Color(0.1, 0.1, 0.1, 1)
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ signal camera_changed(camera: Camera3D)
|
|||||||
var is_zoomed : bool = false
|
var is_zoomed : bool = false
|
||||||
var zoom_tween : Tween
|
var zoom_tween : Tween
|
||||||
|
|
||||||
var is_moving_enabled : bool = true
|
|
||||||
|
|
||||||
var is_cinematic_mode : bool = false
|
var is_cinematic_mode : bool = false
|
||||||
var cinematic_timer : float = 0.0
|
var cinematic_timer : float = 0.0
|
||||||
var cinematic_index : int = 0
|
var cinematic_index : int = 0
|
||||||
@@ -34,6 +32,8 @@ var array_camera : Array[Camera3D] = []
|
|||||||
var initial_pivot_transformation : Transform3D
|
var initial_pivot_transformation : Transform3D
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
UIEvents.change_camera_request.connect(on_change_camera_request)
|
||||||
|
|
||||||
if pivot:
|
if pivot:
|
||||||
initial_pivot_transformation = pivot.transform
|
initial_pivot_transformation = pivot.transform
|
||||||
|
|
||||||
@@ -46,111 +46,90 @@ func _ready():
|
|||||||
if camera_iso:
|
if camera_iso:
|
||||||
set_camera(camera_iso)
|
set_camera(camera_iso)
|
||||||
|
|
||||||
|
func sync_zoom(new_size: float, new_fov: float, photo_is_zoomed: bool = false) -> void:
|
||||||
|
if camera_iso:
|
||||||
|
if zoom_tween and zoom_tween.is_valid():
|
||||||
|
zoom_tween.kill()
|
||||||
|
|
||||||
|
camera_iso.size = new_size
|
||||||
|
camera_iso.fov = new_fov
|
||||||
|
|
||||||
|
is_zoomed = photo_is_zoomed
|
||||||
|
|
||||||
|
var target_size = zoom_size if is_zoomed else normal_size
|
||||||
|
if not is_equal_approx(camera_iso.size, target_size):
|
||||||
|
zoom_tween = create_tween()
|
||||||
|
zoom_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||||
|
zoom_tween.tween_property(camera_iso, "size", target_size, zoom_time)
|
||||||
|
zoom_tween.parallel().tween_property(camera_iso, "fov", target_size, zoom_time)
|
||||||
|
|
||||||
func set_camera(camera: Camera3D):
|
func set_camera(camera: Camera3D):
|
||||||
camera.make_current()
|
camera.make_current()
|
||||||
emit_signal("camera_changed", camera)
|
emit_signal("camera_changed", camera)
|
||||||
|
|
||||||
func _input(event):
|
func _process(delta: float) -> void:
|
||||||
if event is InputEventKey and event.pressed and not event.echo:
|
var current_cam = get_viewport().get_camera_3d()
|
||||||
|
var is_photo_mode = current_cam != null and not current_cam in array_camera
|
||||||
|
|
||||||
#R-> Reset isometric camera
|
if not is_photo_mode:
|
||||||
if event.keycode == KEY_R and is_moving_enabled and pivot:
|
if is_cinematic_mode and array_camera.size() > 0:
|
||||||
pivot.transform = initial_pivot_transformation
|
cinematic_timer += delta
|
||||||
print("Isometric camera resetted!")
|
if cinematic_timer >= change_camera_time:
|
||||||
return
|
|
||||||
|
|
||||||
#C-> Toggle cinematic mode
|
|
||||||
if event.keycode == KEY_C:
|
|
||||||
is_cinematic_mode = !is_cinematic_mode
|
|
||||||
if is_cinematic_mode:
|
|
||||||
cinematic_timer = 0.0
|
cinematic_timer = 0.0
|
||||||
for i in range(array_camera.size()):
|
cinematic_index = (cinematic_index + 1) % array_camera.size()
|
||||||
if array_camera[i].current:
|
var next_cam = array_camera[cinematic_index]
|
||||||
cinematic_index = i
|
set_camera(next_cam)
|
||||||
break
|
|
||||||
print("Cinematic Mode: enabled")
|
|
||||||
else:
|
|
||||||
print("Cinematic Mode: disabled")
|
|
||||||
return
|
|
||||||
|
|
||||||
#Z-> soft zoom
|
if camera_iso == null or current_cam != camera_iso:
|
||||||
if event.keycode == KEY_Z and camera_iso:
|
|
||||||
is_zoomed = !is_zoomed
|
|
||||||
var target_size = zoom_size if is_zoomed else normal_size
|
|
||||||
|
|
||||||
if zoom_tween and zoom_tween.is_valid():
|
|
||||||
zoom_tween.kill()
|
|
||||||
|
|
||||||
zoom_tween = create_tween()
|
|
||||||
zoom_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
||||||
zoom_tween.tween_property(camera_iso, "size", target_size, zoom_time)
|
|
||||||
return
|
|
||||||
|
|
||||||
#manual controls (1-5)
|
|
||||||
var manual_action = false
|
|
||||||
|
|
||||||
if event.keycode == KEY_1 and camera_iso:
|
|
||||||
set_camera(camera_iso)
|
|
||||||
is_moving_enabled = true
|
|
||||||
manual_action = true
|
|
||||||
|
|
||||||
elif event.keycode == KEY_2 and camera_front:
|
|
||||||
set_camera(camera_front)
|
|
||||||
is_moving_enabled = false
|
|
||||||
manual_action = true
|
|
||||||
|
|
||||||
elif event.keycode == KEY_3 and camera_side1:
|
|
||||||
set_camera(camera_side1)
|
|
||||||
is_moving_enabled = false
|
|
||||||
manual_action = true
|
|
||||||
|
|
||||||
elif event.keycode == KEY_4 and camera_side2:
|
|
||||||
set_camera(camera_side2)
|
|
||||||
is_moving_enabled = false
|
|
||||||
manual_action = true
|
|
||||||
|
|
||||||
elif event.keycode == KEY_5 and camera_top:
|
|
||||||
set_camera(camera_top)
|
|
||||||
is_moving_enabled = false
|
|
||||||
manual_action = true
|
|
||||||
|
|
||||||
if manual_action and is_cinematic_mode:
|
|
||||||
is_cinematic_mode = false
|
|
||||||
print("Cinematic Mode: Disabled")
|
|
||||||
|
|
||||||
if is_moving_enabled and pivot:
|
|
||||||
if event is InputEventMouseMotion and Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
|
|
||||||
pivot.rotate_y(deg_to_rad(-event.relative.x * rotation_speed))
|
|
||||||
|
|
||||||
func _process(delta):
|
|
||||||
if is_cinematic_mode and array_camera.size() > 0:
|
|
||||||
cinematic_timer += delta
|
|
||||||
if cinematic_timer >= change_camera_time:
|
|
||||||
cinematic_timer = 0.0
|
|
||||||
cinematic_index = (cinematic_index + 1) % array_camera.size()
|
|
||||||
var next_cam = array_camera[cinematic_index]
|
|
||||||
set_camera(next_cam)
|
|
||||||
is_moving_enabled = (next_cam == camera_iso)
|
|
||||||
|
|
||||||
if not is_moving_enabled or pivot == null:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
var input_dir = Vector3.ZERO
|
if Input.is_action_just_pressed("zoom_in"):
|
||||||
if Input.is_key_pressed(KEY_W): input_dir.z -= 1
|
if is_zoomed == false:
|
||||||
if Input.is_key_pressed(KEY_S): input_dir.z += 1
|
return
|
||||||
if Input.is_key_pressed(KEY_A): input_dir.x -= 1
|
is_zoomed = false
|
||||||
if Input.is_key_pressed(KEY_D): input_dir.x += 1
|
var target_size = normal_size
|
||||||
|
|
||||||
if input_dir != Vector3.ZERO:
|
if zoom_tween and zoom_tween.is_valid():
|
||||||
input_dir = input_dir.normalized()
|
zoom_tween.kill()
|
||||||
|
|
||||||
var forward = pivot.transform.basis.z
|
zoom_tween = create_tween()
|
||||||
var right = pivot.transform.basis.x
|
zoom_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||||
forward.y = 0
|
zoom_tween.tween_property(camera_iso, "size", target_size, zoom_time)
|
||||||
right.y = 0
|
zoom_tween.parallel().tween_property(camera_iso, "fov", target_size, zoom_time)
|
||||||
|
|
||||||
forward = forward.normalized()
|
elif Input.is_action_just_pressed("zoom_out"):
|
||||||
right = right.normalized()
|
if is_zoomed == true:
|
||||||
|
return
|
||||||
|
is_zoomed = true
|
||||||
|
var target_size = zoom_size
|
||||||
|
|
||||||
var motion = (forward * input_dir.z + right * input_dir.x)
|
if zoom_tween and zoom_tween.is_valid():
|
||||||
pivot.position += motion * move_speed * delta
|
zoom_tween.kill()
|
||||||
|
|
||||||
|
zoom_tween = create_tween()
|
||||||
|
zoom_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||||
|
zoom_tween.tween_property(camera_iso, "size", target_size, zoom_time)
|
||||||
|
zoom_tween.parallel().tween_property(camera_iso, "fov", target_size, zoom_time)
|
||||||
|
|
||||||
|
func on_change_camera_request(camera_index: int) -> void:
|
||||||
|
var next_cam = array_camera[camera_index]
|
||||||
|
set_camera(next_cam)
|
||||||
|
|
||||||
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
|
var current_cam = get_viewport().get_camera_3d()
|
||||||
|
var is_photo_mode = current_cam != null and not current_cam in array_camera
|
||||||
|
if is_photo_mode:
|
||||||
|
return
|
||||||
|
|
||||||
|
if event is InputEventKey and event.pressed and not event.echo:
|
||||||
|
match event.keycode:
|
||||||
|
KEY_1:
|
||||||
|
if array_camera.size() > 0: set_camera(array_camera[0])
|
||||||
|
KEY_2:
|
||||||
|
if array_camera.size() > 1: set_camera(array_camera[1])
|
||||||
|
KEY_3:
|
||||||
|
if array_camera.size() > 2: set_camera(array_camera[2])
|
||||||
|
KEY_4:
|
||||||
|
if array_camera.size() > 3: set_camera(array_camera[3])
|
||||||
|
KEY_5:
|
||||||
|
if array_camera.size() > 4: set_camera(array_camera[4])
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ var environment_config: EnvironmentConfig
|
|||||||
@export var time_scale: float = 1.0
|
@export var time_scale: float = 1.0
|
||||||
@export var paused: bool = false
|
@export var paused: bool = false
|
||||||
|
|
||||||
|
var _time_tween: Tween
|
||||||
|
|
||||||
|
var time_transition_duration: float = 2
|
||||||
|
|
||||||
signal time_changed(time: float)
|
signal time_changed(time: float)
|
||||||
|
|
||||||
var current_time: float = 0.0
|
var current_time: float = 0.0
|
||||||
@@ -26,6 +30,7 @@ func _ready() -> void:
|
|||||||
UIEvents.toggle_pause_daytime.connect(_on_toggle_pause_daytime)
|
UIEvents.toggle_pause_daytime.connect(_on_toggle_pause_daytime)
|
||||||
UIEvents.time_option_item_changed.connect(_time_option_changed)
|
UIEvents.time_option_item_changed.connect(_time_option_changed)
|
||||||
|
|
||||||
|
paused = environment_config.start_day_time_paused
|
||||||
current_time = environment_config.start_time
|
current_time = environment_config.start_time
|
||||||
update_time(current_time)
|
update_time(current_time)
|
||||||
|
|
||||||
@@ -67,16 +72,49 @@ func get_time_option_index(currtime: float) -> int:
|
|||||||
return TIME_OPTION_DAY
|
return TIME_OPTION_DAY
|
||||||
return TIME_OPTION_SUNSET
|
return TIME_OPTION_SUNSET
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func _time_option_changed(index: int) -> void:
|
func _time_option_changed(index: int) -> void:
|
||||||
|
var target_time: float = 0.0
|
||||||
match index:
|
match index:
|
||||||
TIME_OPTION_SUNRISE:
|
TIME_OPTION_SUNRISE:
|
||||||
set_time(environment_config.sunrise)
|
target_time = environment_config.sunrise
|
||||||
TIME_OPTION_DAY:
|
TIME_OPTION_DAY:
|
||||||
set_time(environment_config.day)
|
target_time = environment_config.day
|
||||||
TIME_OPTION_SUNSET:
|
TIME_OPTION_SUNSET:
|
||||||
set_time(environment_config.sunset)
|
target_time = environment_config.sunset
|
||||||
TIME_OPTION_NIGHT:
|
TIME_OPTION_NIGHT:
|
||||||
set_time(environment_config.night)
|
target_time = environment_config.night
|
||||||
|
|
||||||
|
if _time_tween:
|
||||||
|
_time_tween.kill()
|
||||||
|
|
||||||
|
_time_tween = create_tween()
|
||||||
|
|
||||||
|
var start_time = current_time
|
||||||
|
|
||||||
|
# Gestione logica lineare: Notte(0.0) - Mattina(0.25) - Giorno(0.45) - Tramonto(0.8) - Notte(1.0)
|
||||||
|
# Per evitare di attraversare fasi intermedie opposte, forziamo il percorso su questo segmento.
|
||||||
|
|
||||||
|
if index == TIME_OPTION_NIGHT:
|
||||||
|
# Da Mattina a Notte -> andiamo a 0.0. Dal Giorno in poi -> andiamo a 1.0.
|
||||||
|
if start_time < environment_config.day:
|
||||||
|
target_time = 0.0
|
||||||
|
else:
|
||||||
|
target_time = 1.0
|
||||||
|
else:
|
||||||
|
# Se partiamo da Notte (valori vicini a 0.0 o 1.0)
|
||||||
|
if start_time < 0.1 or start_time > 0.9:
|
||||||
|
if index == TIME_OPTION_SUNRISE:
|
||||||
|
# Per andare a Mattina, partiamo dal lato 0.0
|
||||||
|
if start_time > 0.9:
|
||||||
|
start_time -= 1.0
|
||||||
|
else:
|
||||||
|
# Per andare a Giorno o Tramonto, partiamo dal lato 1.0
|
||||||
|
if start_time < 0.1:
|
||||||
|
start_time += 1.0
|
||||||
|
|
||||||
|
_time_tween.tween_method(set_time, start_time, target_time, time_transition_duration)
|
||||||
|
|
||||||
func _on_set_day_time(value: float) -> void:
|
func _on_set_day_time(value: float) -> void:
|
||||||
set_time(value)
|
set_time(value)
|
||||||
|
|||||||
@@ -4,9 +4,26 @@ extends Node3D
|
|||||||
const NOISE_TEXTURE: Texture2D = preload("res://core/daynight/noise.tres")
|
const NOISE_TEXTURE: Texture2D = preload("res://core/daynight/noise.tres")
|
||||||
const WEATHER_SHADER: Material = preload("res://core/daynight/weather_overlay.tres")
|
const WEATHER_SHADER: Material = preload("res://core/daynight/weather_overlay.tres")
|
||||||
const DYNAMIC_ENVIRONMENT_UPDATES_PER_FRAME: int = 32 #how many update of the environment (apply materials) will be done per frame
|
const DYNAMIC_ENVIRONMENT_UPDATES_PER_FRAME: int = 32 #how many update of the environment (apply materials) will be done per frame
|
||||||
|
const FORCE_WEATHER_MATERIAL_PATH_PARTS: PackedStringArray = [
|
||||||
|
"/chunk/material/field.tres",
|
||||||
|
"/chunk/material/grassflat_chunk.tres",
|
||||||
|
"/chunk/material/path_chunk.tres",
|
||||||
|
"/chunk/material/railway_chunk.tres",
|
||||||
|
"/chunk/material/terrain.tres",
|
||||||
|
"/chunk/prop/house/house.tres",
|
||||||
|
"/chunk/prop/house/house_emissiv.tres",
|
||||||
|
]
|
||||||
|
|
||||||
@export var environment_config: EnvironmentConfig
|
@export var environment_config: EnvironmentConfig
|
||||||
|
|
||||||
|
@export_group("Input")
|
||||||
|
@export var block_keyboard_input: bool = false:
|
||||||
|
set(value):
|
||||||
|
if block_keyboard_input == value:
|
||||||
|
return
|
||||||
|
block_keyboard_input = value
|
||||||
|
_apply_keyboard_input_block()
|
||||||
|
|
||||||
@export_group("Camera")
|
@export_group("Camera")
|
||||||
@export var camera: Camera3D
|
@export var camera: Camera3D
|
||||||
@export var camera_pivot: Node3D
|
@export var camera_pivot: Node3D
|
||||||
@@ -39,8 +56,25 @@ var weather_controller: WeatherController
|
|||||||
var day_tween: Tween
|
var day_tween: Tween
|
||||||
var day_time: float = 0.0
|
var day_time: float = 0.0
|
||||||
var pending_environment_nodes: Dictionary = {}
|
var pending_environment_nodes: Dictionary = {}
|
||||||
|
var weather_shader_no_noise: Material
|
||||||
|
var _blocked_keyboard_events_by_action: Dictionary = {}
|
||||||
|
|
||||||
|
static func is_keyboard_input_blocked(tree: SceneTree) -> bool:
|
||||||
|
if tree == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
for node in tree.get_nodes_in_group("keyboard_input_blocker"):
|
||||||
|
var environment_manager := node as EnvironmentManagerRoot
|
||||||
|
if environment_manager != null and environment_manager.block_keyboard_input:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
func _enter_tree() -> void:
|
||||||
|
add_to_group("keyboard_input_blocker")
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
_apply_keyboard_input_block()
|
||||||
|
|
||||||
#connect shader when new node is added
|
#connect shader when new node is added
|
||||||
get_tree().node_added.connect(_on_tree_node_added)
|
get_tree().node_added.connect(_on_tree_node_added)
|
||||||
@@ -80,16 +114,69 @@ func _ready() -> void:
|
|||||||
weather_controller.name = "WeatherController"
|
weather_controller.name = "WeatherController"
|
||||||
add_child(weather_controller)
|
add_child(weather_controller)
|
||||||
|
|
||||||
|
if day_night_controller != null:
|
||||||
|
select_day_time(day_night_controller.current_time)
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
_process_pending_environment_nodes()
|
_process_pending_environment_nodes()
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if block_keyboard_input and event is InputEventKey:
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
_restore_keyboard_action_events()
|
||||||
|
remove_from_group("keyboard_input_blocker")
|
||||||
|
|
||||||
func flush_pending_environment_nodes() -> void:
|
func flush_pending_environment_nodes() -> void:
|
||||||
_process_pending_environment_nodes(-1)
|
_process_pending_environment_nodes(-1)
|
||||||
|
|
||||||
|
func _apply_keyboard_input_block() -> void:
|
||||||
|
if block_keyboard_input:
|
||||||
|
_remove_keyboard_action_events()
|
||||||
|
else:
|
||||||
|
_restore_keyboard_action_events()
|
||||||
|
|
||||||
|
func _remove_keyboard_action_events() -> void:
|
||||||
|
if not _blocked_keyboard_events_by_action.is_empty():
|
||||||
|
return
|
||||||
|
|
||||||
|
for action in InputMap.get_actions():
|
||||||
|
var keyboard_events: Array[InputEvent] = []
|
||||||
|
for event in InputMap.action_get_events(action):
|
||||||
|
if event is InputEventKey:
|
||||||
|
keyboard_events.append(event)
|
||||||
|
|
||||||
|
if keyboard_events.is_empty():
|
||||||
|
continue
|
||||||
|
|
||||||
|
_blocked_keyboard_events_by_action[action] = keyboard_events
|
||||||
|
Input.action_release(action)
|
||||||
|
for keyboard_event in keyboard_events:
|
||||||
|
InputMap.action_erase_event(action, keyboard_event)
|
||||||
|
|
||||||
|
func _restore_keyboard_action_events() -> void:
|
||||||
|
for action in _blocked_keyboard_events_by_action.keys():
|
||||||
|
if not InputMap.has_action(action):
|
||||||
|
continue
|
||||||
|
|
||||||
|
for keyboard_event in _blocked_keyboard_events_by_action[action]:
|
||||||
|
InputMap.action_add_event(action, keyboard_event)
|
||||||
|
|
||||||
|
_blocked_keyboard_events_by_action.clear()
|
||||||
|
|
||||||
func _on_tree_node_added(node: Node) -> void:
|
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"):
|
if _should_queue_dynamic_environment_node(node):
|
||||||
_queue_dynamic_environment_node(node)
|
_queue_dynamic_environment_node(node)
|
||||||
|
|
||||||
|
func _should_queue_dynamic_environment_node(node: Node) -> bool:
|
||||||
|
if node.is_in_group("wind_node"):
|
||||||
|
return true
|
||||||
|
if node.is_in_group("weather_vegetables_node"):
|
||||||
|
return true
|
||||||
|
|
||||||
|
return node is Node3D
|
||||||
|
|
||||||
func _queue_dynamic_environment_node(node: Node) -> void:
|
func _queue_dynamic_environment_node(node: Node) -> void:
|
||||||
if not is_instance_valid(node):
|
if not is_instance_valid(node):
|
||||||
return
|
return
|
||||||
@@ -135,10 +222,10 @@ func _apply_dynamic_environment_materials(node: Node) -> void:
|
|||||||
if node.is_in_group("wind_node"):
|
if node.is_in_group("wind_node"):
|
||||||
_apply_wind_noise_to_node(node, NOISE_TEXTURE)
|
_apply_wind_noise_to_node(node, NOISE_TEXTURE)
|
||||||
|
|
||||||
if node.is_in_group("weather_node"):
|
if node is Node3D:
|
||||||
_apply_weather_overlay_to_node(node, WEATHER_SHADER)
|
_apply_weather_overlay_to_node(node, _get_weather_overlay_material(node))
|
||||||
|
|
||||||
if _should_clear_weather_overlay(node):
|
if _should_ignore_weather_overlay(node):
|
||||||
_clear_weather_overlay_from_node(node)
|
_clear_weather_overlay_from_node(node)
|
||||||
|
|
||||||
func ApplyWindNoiseToMaterials():
|
func ApplyWindNoiseToMaterials():
|
||||||
@@ -161,26 +248,43 @@ func _apply_wind_noise_to_node(node: Node, noise_tex: Texture2D) -> void:
|
|||||||
_apply_wind_noise_to_node(child, noise_tex)
|
_apply_wind_noise_to_node(child, noise_tex)
|
||||||
|
|
||||||
func ApplyWeatherShaderToMaterials():
|
func ApplyWeatherShaderToMaterials():
|
||||||
for node in get_tree().get_nodes_in_group("weather_node"):
|
var current_scene := get_tree().current_scene
|
||||||
_apply_weather_overlay_to_node(node, WEATHER_SHADER)
|
if current_scene != null:
|
||||||
|
_apply_weather_overlay_to_node(current_scene, _get_weather_overlay_material(current_scene))
|
||||||
|
|
||||||
for node in get_tree().get_nodes_in_group("weather_vegetables_node"):
|
func _get_weather_overlay_material(node: Node) -> Material:
|
||||||
if _should_clear_weather_overlay(node):
|
if not node.is_in_group("weather_overlay_no_noise"):
|
||||||
_clear_weather_overlay_from_node(node)
|
return WEATHER_SHADER
|
||||||
|
|
||||||
|
if weather_shader_no_noise == null:
|
||||||
|
weather_shader_no_noise = WEATHER_SHADER.duplicate() as Material
|
||||||
|
var shader_material := weather_shader_no_noise as ShaderMaterial
|
||||||
|
if shader_material:
|
||||||
|
shader_material.set_shader_parameter("snow_noise_enabled", false)
|
||||||
|
|
||||||
|
return weather_shader_no_noise
|
||||||
|
|
||||||
func _apply_weather_overlay_to_node(node: Node, material: Material) -> void:
|
func _apply_weather_overlay_to_node(node: Node, material: Material) -> void:
|
||||||
if _should_clear_weather_overlay(node):
|
if _should_ignore_weather_overlay(node):
|
||||||
_clear_weather_overlay_from_node(node)
|
_clear_weather_overlay_from_node(node)
|
||||||
return
|
return
|
||||||
|
|
||||||
if node is GeometryInstance3D:
|
if node is GeometryInstance3D:
|
||||||
if _geometry_uses_alpha_texture(node):
|
if _geometry_should_never_receive_weather_overlay(node):
|
||||||
|
node.material_overlay = null
|
||||||
|
elif _should_clear_weather_overlay(node) and not _geometry_forces_weather_overlay(node):
|
||||||
|
node.material_overlay = null
|
||||||
|
elif _geometry_uses_alpha_texture(node) or _geometry_should_skip_weather_overlay(node):
|
||||||
node.material_overlay = null
|
node.material_overlay = null
|
||||||
else:
|
else:
|
||||||
node.material_overlay = material
|
node.material_overlay = material
|
||||||
|
|
||||||
|
var child_material := _get_weather_overlay_material(node)
|
||||||
|
if child_material == WEATHER_SHADER and material == weather_shader_no_noise:
|
||||||
|
child_material = material
|
||||||
|
|
||||||
for child in node.get_children():
|
for child in node.get_children():
|
||||||
_apply_weather_overlay_to_node(child, material)
|
_apply_weather_overlay_to_node(child, child_material)
|
||||||
|
|
||||||
func _clear_weather_overlay_from_node(node: Node) -> void:
|
func _clear_weather_overlay_from_node(node: Node) -> void:
|
||||||
if node is GeometryInstance3D:
|
if node is GeometryInstance3D:
|
||||||
@@ -208,11 +312,102 @@ func _geometry_uses_alpha_texture(node: GeometryInstance3D) -> bool:
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
func _geometry_should_never_receive_weather_overlay(node: GeometryInstance3D) -> bool:
|
||||||
|
if node is GPUParticles3D:
|
||||||
|
return true
|
||||||
|
|
||||||
|
var node_name := String(node.name).to_lower()
|
||||||
|
return node_name.contains("cloud") or node_name.contains("fog") or node_name.contains("godray")
|
||||||
|
|
||||||
|
func _geometry_should_skip_weather_overlay(node: GeometryInstance3D) -> bool:
|
||||||
|
if _geometry_forces_weather_overlay(node):
|
||||||
|
return false
|
||||||
|
if _node_name_indicates_water(node):
|
||||||
|
return true
|
||||||
|
|
||||||
|
var has_material: bool = false
|
||||||
|
var all_materials_ignored: bool = true
|
||||||
|
var material_override := node.material_override
|
||||||
|
if material_override != null:
|
||||||
|
has_material = true
|
||||||
|
all_materials_ignored = all_materials_ignored and _material_ignores_weather_overlay(material_override)
|
||||||
|
|
||||||
|
if node is MeshInstance3D:
|
||||||
|
for surface_index in node.get_surface_override_material_count():
|
||||||
|
var surface_material: Material = node.get_surface_override_material(surface_index)
|
||||||
|
if surface_material == null:
|
||||||
|
continue
|
||||||
|
|
||||||
|
has_material = true
|
||||||
|
all_materials_ignored = all_materials_ignored and _material_ignores_weather_overlay(surface_material)
|
||||||
|
|
||||||
|
if node.mesh:
|
||||||
|
for surface_index in node.mesh.get_surface_count():
|
||||||
|
var mesh_material: Material = node.mesh.surface_get_material(surface_index)
|
||||||
|
if mesh_material == null:
|
||||||
|
continue
|
||||||
|
|
||||||
|
has_material = true
|
||||||
|
all_materials_ignored = all_materials_ignored and _material_ignores_weather_overlay(mesh_material)
|
||||||
|
|
||||||
|
return has_material and all_materials_ignored
|
||||||
|
|
||||||
|
func _geometry_forces_weather_overlay(node: GeometryInstance3D) -> bool:
|
||||||
|
if _material_forces_weather_overlay(node.material_override):
|
||||||
|
return true
|
||||||
|
|
||||||
|
if node is MeshInstance3D:
|
||||||
|
for surface_index in node.get_surface_override_material_count():
|
||||||
|
if _material_forces_weather_overlay(node.get_surface_override_material(surface_index)):
|
||||||
|
return true
|
||||||
|
|
||||||
|
if node.mesh:
|
||||||
|
for surface_index in node.mesh.get_surface_count():
|
||||||
|
if _material_forces_weather_overlay(node.mesh.surface_get_material(surface_index)):
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
func _material_forces_weather_overlay(material: Material) -> bool:
|
||||||
|
if material == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var resource_path := material.resource_path.to_lower()
|
||||||
|
for path_part in FORCE_WEATHER_MATERIAL_PATH_PARTS:
|
||||||
|
if resource_path.contains(path_part):
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
func _node_name_indicates_water(node: Node) -> bool:
|
||||||
|
var node_name := String(node.name).to_lower()
|
||||||
|
return node_name.begins_with("water") or node_name.contains("/water")
|
||||||
|
|
||||||
|
func _material_ignores_weather_overlay(material: Material) -> bool:
|
||||||
|
if material == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var resource_path := material.resource_path.to_lower()
|
||||||
|
if resource_path.contains("/water") or resource_path.contains("water_"):
|
||||||
|
return true
|
||||||
|
if resource_path.contains("/cloud/") or resource_path.contains("cloud"):
|
||||||
|
return true
|
||||||
|
|
||||||
|
var base_material := material as BaseMaterial3D
|
||||||
|
if base_material != null:
|
||||||
|
return base_material.transparency != BaseMaterial3D.TRANSPARENCY_DISABLED or base_material.albedo_color.a < 0.99
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
func _shader_material_uses_alpha_texture(material: ShaderMaterial) -> bool:
|
func _shader_material_uses_alpha_texture(material: ShaderMaterial) -> bool:
|
||||||
if material == null or material.shader == null:
|
if material == null or material.shader == null:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
return material.shader.code.find("alpha_texture") != -1
|
var shader_code := material.shader.code
|
||||||
|
return shader_code.find("alpha_texture") != -1 or shader_code.find("ALPHA") != -1 or shader_code.find("opacity") != -1
|
||||||
|
|
||||||
|
func _should_ignore_weather_overlay(node: Node) -> bool:
|
||||||
|
return node.is_in_group("weather_overlay_ignore")
|
||||||
|
|
||||||
func _should_clear_weather_overlay(node: Node) -> bool:
|
func _should_clear_weather_overlay(node: Node) -> bool:
|
||||||
return node.is_in_group("weather_vegetables_node")
|
return node.is_in_group("weather_vegetables_node")
|
||||||
@@ -233,7 +428,7 @@ func select_day_time(normalized_time: float) -> void:
|
|||||||
|
|
||||||
if normalized_time < environment_config.sunrise:
|
if normalized_time < environment_config.sunrise:
|
||||||
#Sunrise: night → morning
|
#Sunrise: night → morning
|
||||||
day_time = 3.0 - (normalized_time / environment_config.sunrise) * 2.0 # 3.0 → 1.0
|
day_time = 3.0 + (normalized_time / environment_config.sunrise) * 1.0 # 3.0 → 4.0
|
||||||
elif normalized_time < environment_config.day:
|
elif normalized_time < environment_config.day:
|
||||||
#Morning: morning → afternoon
|
#Morning: morning → afternoon
|
||||||
var t: float = (normalized_time - environment_config.sunrise) / (environment_config.day - environment_config.sunrise)
|
var t: float = (normalized_time - environment_config.sunrise) / (environment_config.day - environment_config.sunrise)
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ render_priority = 0
|
|||||||
shader = ExtResource("1_qxph4")
|
shader = ExtResource("1_qxph4")
|
||||||
shader_parameter/noise_texture = SubResource("NoiseTexture2D_kavln")
|
shader_parameter/noise_texture = SubResource("NoiseTexture2D_kavln")
|
||||||
shader_parameter/ray_color = Color(1, 0.78431374, 0.27058825, 1)
|
shader_parameter/ray_color = Color(1, 0.78431374, 0.27058825, 1)
|
||||||
shader_parameter/base_alpha = 1.0
|
shader_parameter/base_alpha = 0.150000007125
|
||||||
shader_parameter/noise_stretching = 3.17200010317
|
shader_parameter/noise_stretching = 3.000000095
|
||||||
shader_parameter/noise_scale = 1.0
|
shader_parameter/noise_scale = 1.0
|
||||||
shader_parameter/scrolling_speed = Vector2(0, 0.15)
|
shader_parameter/scrolling_speed = Vector2(0, 0.15)
|
||||||
shader_parameter/depth_softness = 1.0
|
shader_parameter/depth_softness = 1.0
|
||||||
shader_parameter/edge_fade_power = 2.0
|
shader_parameter/edge_fade_power = 2.0
|
||||||
shader_parameter/intensity_multiplier = 0.5330000253175
|
shader_parameter/intensity_multiplier = 0.50000002375
|
||||||
|
|
||||||
[node name="GodRay" type="Node3D" unique_id=1679441931]
|
[node name="GodRay" type="Node3D" unique_id=1679441931]
|
||||||
script = ExtResource("2_v3yr3")
|
script = ExtResource("2_v3yr3")
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ global uniform float global_snow_accumulation_speed = 0.005;
|
|||||||
global uniform float global_snow_melt_time = -1.0;
|
global uniform float global_snow_melt_time = -1.0;
|
||||||
global uniform float global_snow_melt_speed = 0.1;
|
global uniform float global_snow_melt_speed = 0.1;
|
||||||
global uniform float global_snow_amount = 0.0;
|
global uniform float global_snow_amount = 0.0;
|
||||||
|
global uniform vec4 global_snow_color = vec4(0.92, 0.96, 1.0, 1.0);
|
||||||
global uniform float global_rain_intensity;
|
global uniform float global_rain_intensity;
|
||||||
|
const float GRASS_FULL_SNOW_START = 0.58;
|
||||||
|
const float GRASS_FULL_SNOW_END = 0.86;
|
||||||
|
|
||||||
// --- PARAMETRI ESTETICI ---
|
// --- PARAMETRI ESTETICI ---
|
||||||
uniform bool billboard_enabled = true;
|
uniform bool billboard_enabled = true;
|
||||||
@@ -133,19 +136,22 @@ void fragment() {
|
|||||||
// Applichiamo la variazione al colore finale dell'erba
|
// Applichiamo la variazione al colore finale dell'erba
|
||||||
vec3 varied_grass_color = mix(v_final_color, variance_color.rgb, noise_sample * variance_intensity);
|
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 snow_amount = pow(get_snow_progress(), 0.55);
|
||||||
|
|
||||||
float top_mask = 1.0 - shifted_uv.y;
|
float top_mask = 1.0 - shifted_uv.y;
|
||||||
float snow_start = 1.0 - clamp(snow_coverage, 0.05, 1.0);
|
float snow_start = 1.0 - clamp(snow_coverage, 0.05, 1.0);
|
||||||
float snow_mask = smoothstep(snow_start, 1.0, top_mask) * snow_amount * snow_visibility;
|
float snow_mask = smoothstep(snow_start, 1.0, top_mask) * snow_amount * snow_visibility;
|
||||||
snow_mask *= step(0.01, snow_amount);
|
snow_mask *= step(0.01, snow_amount);
|
||||||
snow_mask = clamp(snow_mask, 0.0, 1.0);
|
snow_mask = clamp(snow_mask, 0.0, 1.0);
|
||||||
|
float full_snow_mask = smoothstep(GRASS_FULL_SNOW_START, GRASS_FULL_SNOW_END, snow_amount) * step(0.01, snow_visibility);
|
||||||
|
float final_snow_mask = max(snow_mask, full_snow_mask);
|
||||||
|
|
||||||
vec3 dark_snow = snow_color.rgb * (1.0 - shadow_intensity);
|
vec3 snow_base_color = max(snow_color.rgb, global_snow_color.rgb);
|
||||||
vec3 shaded_snow = mix(dark_snow, snow_color.rgb, v_shade_factor);
|
float snow_light = mix(0.82, 1.0, max(v_shade_factor, 0.35));
|
||||||
|
vec3 shaded_snow = clamp(snow_base_color * snow_light, 0.0, 1.0);
|
||||||
|
|
||||||
// Mescoliamo il colore variato con la neve
|
// Mescoliamo il colore variato con la neve
|
||||||
vec3 final_albedo = mix(varied_grass_color, shaded_snow, snow_mask);
|
vec3 final_albedo = mix(varied_grass_color, shaded_snow, final_snow_mask);
|
||||||
float rain_int = clamp(global_rain_intensity, 0.0, 1.0);
|
float rain_int = clamp(global_rain_intensity, 0.0, 1.0);
|
||||||
final_albedo *= mix(1.0, 1.0 - wetness_darkening, rain_int);
|
final_albedo *= mix(1.0, 1.0 - wetness_darkening, rain_int);
|
||||||
float final_roughness = mix(0.02, 0.005, rain_int);
|
float final_roughness = mix(0.02, 0.005, rain_int);
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ global uniform float global_snow_melt_speed = 0.1;
|
|||||||
global uniform float global_snow_amount = 0.0;
|
global uniform float global_snow_amount = 0.0;
|
||||||
global uniform vec4 global_snow_color;
|
global uniform vec4 global_snow_color;
|
||||||
global uniform float global_rain_intensity;
|
global uniform float global_rain_intensity;
|
||||||
|
const float LEAVES_FULL_SNOW_START = 0.96;
|
||||||
|
const float LEAVES_FULL_SNOW_END = 1.0;
|
||||||
|
const float LEAVES_TOP_SNOW_COVERAGE = 0.75;
|
||||||
|
|
||||||
uniform sampler2D wind_noise : filter_linear_mipmap;
|
uniform sampler2D wind_noise : filter_linear_mipmap;
|
||||||
uniform bool billboard_enabled = true;
|
uniform bool billboard_enabled = true;
|
||||||
@@ -108,12 +111,14 @@ void fragment() {
|
|||||||
float snow_amount = pow(get_snow_progress(), 0.55);
|
float snow_amount = pow(get_snow_progress(), 0.55);
|
||||||
|
|
||||||
float top_mask = 1.0 - shifted_uv.y;
|
float top_mask = 1.0 - shifted_uv.y;
|
||||||
float snow_mask = smoothstep(1.0 - snow_amount * 1.35, 1.08 - snow_amount * 1.35, top_mask) * snow_visibility;
|
float snow_mask = smoothstep(1.0 - snow_amount * LEAVES_TOP_SNOW_COVERAGE, 1.08 - snow_amount * LEAVES_TOP_SNOW_COVERAGE, top_mask) * snow_visibility;
|
||||||
snow_mask *= step(0.01, snow_amount);
|
snow_mask *= step(0.01, snow_amount);
|
||||||
|
float full_snow_mask = smoothstep(LEAVES_FULL_SNOW_START, LEAVES_FULL_SNOW_END, snow_amount) * clamp(snow_visibility, 0.0, 1.0);
|
||||||
|
float final_snow_mask = max(snow_mask, full_snow_mask);
|
||||||
|
|
||||||
vec3 dark_snow = global_snow_color.rgb * (1.0 - shadow_intensity);
|
float snow_light = mix(0.82, 1.0, max(v_shade_factor, 0.35));
|
||||||
vec3 shaded_snow = mix(dark_snow, global_snow_color.rgb, v_shade_factor);
|
vec3 shaded_snow = clamp(global_snow_color.rgb * snow_light, 0.0, 1.0);
|
||||||
vec3 final_albedo = mix(v_final_color, shaded_snow, snow_mask);
|
vec3 final_albedo = mix(v_final_color, shaded_snow, final_snow_mask);
|
||||||
|
|
||||||
// Rain wetness: darken and make shinier
|
// Rain wetness: darken and make shinier
|
||||||
float rain_int = clamp(global_rain_intensity, 0.0, 1.0);
|
float rain_int = clamp(global_rain_intensity, 0.0, 1.0);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ var is_storm: bool = false
|
|||||||
var cold_tween: Tween
|
var cold_tween: Tween
|
||||||
var wind_tween: Tween
|
var wind_tween: Tween
|
||||||
var is_windy: bool = false
|
var is_windy: bool = false
|
||||||
var thereare_fireflies: bool = false
|
var thereare_fireflies: bool = true
|
||||||
var current_wind_speed: float = 0.0
|
var current_wind_speed: float = 0.0
|
||||||
var current_wind_strength: float = 0.0
|
var current_wind_strength: float = 0.0
|
||||||
var current_wind_fade: float = 0.0
|
var current_wind_fade: float = 0.0
|
||||||
@@ -128,9 +128,7 @@ func _process(delta: float) -> void:
|
|||||||
|
|
||||||
_follow_camera()
|
_follow_camera()
|
||||||
|
|
||||||
var is_night = day_time >= 2.5
|
_update_fireflies_visibility()
|
||||||
if particles_fireflies:
|
|
||||||
particles_fireflies.emitting = thereare_fireflies and is_night and not is_raining and not is_snowing and not is_storm
|
|
||||||
|
|
||||||
var base_tint: Color
|
var base_tint: Color
|
||||||
var base_sky_top: Color
|
var base_sky_top: Color
|
||||||
@@ -172,7 +170,7 @@ func _process(delta: float) -> void:
|
|||||||
base_grad_intensity = lerp(environment_config.grad_intensity_morning, environment_config.grad_intensity_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_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)
|
base_rotation_sun = environment_config.sun_rotation_morning.lerp(environment_config.sun_rotation_afternoon, t)
|
||||||
else:
|
elif day_time <= 3.0:
|
||||||
var t = day_time - 2.0
|
var t = day_time - 2.0
|
||||||
base_tint = environment_config.afternoon_color.lerp(environment_config.night_color, t)
|
base_tint = environment_config.afternoon_color.lerp(environment_config.night_color, t)
|
||||||
base_sky_top = environment_config.sky_top_afternoon.lerp(environment_config.sky_top_night, t)
|
base_sky_top = environment_config.sky_top_afternoon.lerp(environment_config.sky_top_night, t)
|
||||||
@@ -186,6 +184,20 @@ func _process(delta: float) -> void:
|
|||||||
base_grad_intensity = lerp(environment_config.grad_intensity_afternoon, environment_config.grad_intensity_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_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)
|
base_rotation_sun = environment_config.sun_rotation_afternoon.lerp(environment_config.sun_rotation_night, t)
|
||||||
|
else:
|
||||||
|
var t = day_time - 3.0
|
||||||
|
base_tint = environment_config.night_color.lerp(environment_config.morning_color, t)
|
||||||
|
base_sky_top = environment_config.sky_top_night.lerp(environment_config.sky_top_morning, t)
|
||||||
|
base_sky_horizon = environment_config.sky_horizon_night.lerp(environment_config.sky_horizon_morning, t)
|
||||||
|
base_fog_color = environment_config.fog_color_night.lerp(environment_config.fog_color_morning, t)
|
||||||
|
base_fog_density = lerp(environment_config.fog_density_night, environment_config.fog_density_morning, t)
|
||||||
|
base_bloom = lerp(environment_config.glow_night, environment_config.glow_morning, t)
|
||||||
|
base_exposure = lerp(environment_config.exposure_night, environment_config.exposure_morning, t)
|
||||||
|
base_grad_top = environment_config.grad_top_night.lerp(environment_config.grad_top_morning, t)
|
||||||
|
base_grad_bot = environment_config.grad_bot_night.lerp(environment_config.grad_bot_morning, t)
|
||||||
|
base_grad_intensity = lerp(environment_config.grad_intensity_night, environment_config.grad_intensity_morning, t)
|
||||||
|
base_water_color = environment_config.water_color_night.lerp(environment_config.water_color_morning, t)
|
||||||
|
base_rotation_sun = environment_config.sun_rotation_night.lerp(environment_config.sun_rotation_morning, t)
|
||||||
|
|
||||||
var weather_color = environment_config.rain_mode_color
|
var weather_color = environment_config.rain_mode_color
|
||||||
if is_storm:
|
if is_storm:
|
||||||
@@ -197,11 +209,9 @@ func _process(delta: float) -> void:
|
|||||||
var final_fog_color = base_fog_color.lerp(base_fog_color * 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_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))
|
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))
|
||||||
var rain_weather_amount: float = 0.0
|
var rain_weather_amount: float = clamp(rain_intensity, 0.0, 1.0)
|
||||||
if is_raining:
|
|
||||||
rain_weather_amount = clamp(rain_intensity, 0.0, 1.0)
|
|
||||||
var storm_weather_amount: float = 0.0
|
var storm_weather_amount: float = 0.0
|
||||||
if is_raining and is_storm:
|
if is_storm:
|
||||||
if environment_config.storm_rain_intensity_multiplier > 1.0:
|
if environment_config.storm_rain_intensity_multiplier > 1.0:
|
||||||
storm_weather_amount = clamp((rain_intensity - 1.0) / (environment_config.storm_rain_intensity_multiplier - 1.0), 0.0, 1.0)
|
storm_weather_amount = clamp((rain_intensity - 1.0) / (environment_config.storm_rain_intensity_multiplier - 1.0), 0.0, 1.0)
|
||||||
else:
|
else:
|
||||||
@@ -316,26 +326,30 @@ func create_sound_players():
|
|||||||
func _get_rain_day_color() -> Color:
|
func _get_rain_day_color() -> Color:
|
||||||
if day_time <= 2.0:
|
if day_time <= 2.0:
|
||||||
return environment_config.rain_morning_color.lerp(environment_config.rain_afternoon_color, day_time - 1.0)
|
return environment_config.rain_morning_color.lerp(environment_config.rain_afternoon_color, day_time - 1.0)
|
||||||
|
elif day_time <= 3.0:
|
||||||
return environment_config.rain_afternoon_color.lerp(environment_config.rain_night_color, day_time - 2.0)
|
return environment_config.rain_afternoon_color.lerp(environment_config.rain_night_color, day_time - 2.0)
|
||||||
|
return environment_config.rain_night_color.lerp(environment_config.rain_morning_color, day_time - 3.0)
|
||||||
|
|
||||||
func _get_storm_day_color() -> Color:
|
func _get_storm_day_color() -> Color:
|
||||||
if day_time <= 2.0:
|
if day_time <= 2.0:
|
||||||
return environment_config.storm_morning_color.lerp(environment_config.storm_afternoon_color, day_time - 1.0)
|
return environment_config.storm_morning_color.lerp(environment_config.storm_afternoon_color, day_time - 1.0)
|
||||||
|
elif day_time <= 3.0:
|
||||||
return environment_config.storm_afternoon_color.lerp(environment_config.storm_night_color, day_time - 2.0)
|
return environment_config.storm_afternoon_color.lerp(environment_config.storm_night_color, day_time - 2.0)
|
||||||
|
return environment_config.storm_night_color.lerp(environment_config.storm_morning_color, day_time - 3.0)
|
||||||
|
|
||||||
func _get_rain_day_exposure() -> float:
|
func _get_rain_day_exposure() -> float:
|
||||||
if day_time <= 2.0:
|
if day_time <= 2.0:
|
||||||
return lerp(environment_config.rain_exposure_morning, environment_config.rain_exposure_afternoon, day_time - 1.0)
|
return lerp(environment_config.rain_exposure_morning, environment_config.rain_exposure_afternoon, day_time - 1.0)
|
||||||
|
elif day_time <= 3.0:
|
||||||
return lerp(environment_config.rain_exposure_afternoon, environment_config.rain_exposure_night, day_time - 2.0)
|
return lerp(environment_config.rain_exposure_afternoon, environment_config.rain_exposure_night, day_time - 2.0)
|
||||||
|
return lerp(environment_config.rain_exposure_night, environment_config.rain_exposure_morning, day_time - 3.0)
|
||||||
|
|
||||||
func _get_storm_day_exposure() -> float:
|
func _get_storm_day_exposure() -> float:
|
||||||
if day_time <= 2.0:
|
if day_time <= 2.0:
|
||||||
return lerp(environment_config.storm_exposure_morning, environment_config.storm_exposure_afternoon, day_time - 1.0)
|
return lerp(environment_config.storm_exposure_morning, environment_config.storm_exposure_afternoon, day_time - 1.0)
|
||||||
|
elif day_time <= 3.0:
|
||||||
return lerp(environment_config.storm_exposure_afternoon, environment_config.storm_exposure_night, day_time - 2.0)
|
return lerp(environment_config.storm_exposure_afternoon, environment_config.storm_exposure_night, day_time - 2.0)
|
||||||
|
return lerp(environment_config.storm_exposure_night, environment_config.storm_exposure_morning, day_time - 3.0)
|
||||||
|
|
||||||
#region camera
|
#region camera
|
||||||
func _set_camera(curr_camera: Camera3D):
|
func _set_camera(curr_camera: Camera3D):
|
||||||
@@ -389,9 +403,7 @@ func _follow_camera() -> void:
|
|||||||
|
|
||||||
func toggle_fireflies(value: bool):
|
func toggle_fireflies(value: bool):
|
||||||
thereare_fireflies = value
|
thereare_fireflies = value
|
||||||
var is_night = day_time >= 2.5
|
_update_fireflies_visibility()
|
||||||
if particles_fireflies:
|
|
||||||
particles_fireflies.emitting = thereare_fireflies and is_night and not is_raining
|
|
||||||
|
|
||||||
#disable fireflies and set default values and materials
|
#disable fireflies and set default values and materials
|
||||||
func init_fireflies():
|
func init_fireflies():
|
||||||
@@ -402,6 +414,15 @@ func init_fireflies():
|
|||||||
var proc_mat = particles_fireflies.process_material as ParticleProcessMaterial
|
var proc_mat = particles_fireflies.process_material as ParticleProcessMaterial
|
||||||
if proc_mat:
|
if proc_mat:
|
||||||
proc_mat.emission_box_extents = Vector3(environment_config.fireflies_spawn_ray, environment_config.fireflies_spawn_height, environment_config.fireflies_spawn_ray)
|
proc_mat.emission_box_extents = Vector3(environment_config.fireflies_spawn_ray, environment_config.fireflies_spawn_height, environment_config.fireflies_spawn_ray)
|
||||||
|
_update_fireflies_visibility()
|
||||||
|
|
||||||
|
func _should_show_fireflies() -> bool:
|
||||||
|
var is_night: bool = day_time >= 2.5
|
||||||
|
return thereare_fireflies and is_night and not is_raining and not is_snowing and not is_storm
|
||||||
|
|
||||||
|
func _update_fireflies_visibility() -> void:
|
||||||
|
if particles_fireflies:
|
||||||
|
particles_fireflies.emitting = _should_show_fireflies()
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -410,7 +431,7 @@ func init_fireflies():
|
|||||||
func toggle_wind(value: bool):
|
func toggle_wind(value: bool):
|
||||||
is_windy = value
|
is_windy = value
|
||||||
if particles_wind:
|
if particles_wind:
|
||||||
particles_wind.emitting = is_windy
|
particles_wind.emitting = true
|
||||||
|
|
||||||
_apply_wind_state()
|
_apply_wind_state()
|
||||||
_emit_weather_event_label()
|
_emit_weather_event_label()
|
||||||
@@ -420,7 +441,7 @@ func init_wind():
|
|||||||
_update_wind_amount_from_strength(environment_config.wind_strength)
|
_update_wind_amount_from_strength(environment_config.wind_strength)
|
||||||
if particles_wind:
|
if particles_wind:
|
||||||
particles_wind.visible = true
|
particles_wind.visible = true
|
||||||
particles_wind.emitting = false
|
particles_wind.emitting = true
|
||||||
particles_wind.amount = environment_config.wind_amount
|
particles_wind.amount = environment_config.wind_amount
|
||||||
var proc_mat_wind = particles_wind.process_material as ParticleProcessMaterial
|
var proc_mat_wind = particles_wind.process_material as ParticleProcessMaterial
|
||||||
if proc_mat_wind:
|
if proc_mat_wind:
|
||||||
@@ -451,6 +472,11 @@ func _apply_wind_config() -> void:
|
|||||||
|
|
||||||
_apply_wind_state(true)
|
_apply_wind_state(true)
|
||||||
|
|
||||||
|
func _get_target_wind_strength() -> float:
|
||||||
|
if is_windy:
|
||||||
|
return environment_config.wind_boost_strength
|
||||||
|
return environment_config.wind_strength
|
||||||
|
|
||||||
func _apply_wind_state(immediate: bool = false) -> void:
|
func _apply_wind_state(immediate: bool = false) -> void:
|
||||||
if environment_config == null:
|
if environment_config == null:
|
||||||
return
|
return
|
||||||
@@ -458,20 +484,23 @@ func _apply_wind_state(immediate: bool = false) -> void:
|
|||||||
if wind_tween and wind_tween.is_valid():
|
if wind_tween and wind_tween.is_valid():
|
||||||
wind_tween.kill()
|
wind_tween.kill()
|
||||||
|
|
||||||
var active_wind_speed := environment_config.wind_speed if is_windy else 0.0
|
if particles_wind:
|
||||||
var active_wind_strength := environment_config.wind_strength if is_windy else 0.0
|
particles_wind.emitting = true
|
||||||
var active_wind_fade := 1.0 if is_windy else 0.0
|
|
||||||
|
var active_wind_speed := environment_config.wind_speed
|
||||||
|
var active_wind_strength := _get_target_wind_strength()
|
||||||
|
var active_wind_fade := 1.0
|
||||||
if immediate:
|
if immediate:
|
||||||
_set_current_wind_speed(active_wind_speed)
|
_set_current_wind_speed(active_wind_speed)
|
||||||
_set_current_wind_strength(active_wind_strength)
|
_set_current_wind_strength(active_wind_strength)
|
||||||
_set_current_wind_fade(active_wind_fade)
|
_set_current_wind_fade(active_wind_fade)
|
||||||
return
|
return
|
||||||
|
|
||||||
_set_current_wind_speed(environment_config.wind_speed)
|
_set_current_wind_speed(active_wind_speed)
|
||||||
_set_current_wind_strength(environment_config.wind_strength)
|
_set_current_wind_fade(active_wind_fade)
|
||||||
wind_tween = create_tween()
|
wind_tween = create_tween()
|
||||||
wind_tween.tween_method(_set_current_wind_fade, current_wind_fade, active_wind_fade, environment_config.wind_fade_in_out_time)
|
wind_tween.tween_method(_set_current_wind_strength, current_wind_strength, active_wind_strength, environment_config.wind_fade_in_out_time)
|
||||||
wind_tween.tween_callback(_finish_wind_fade_out)
|
wind_tween.tween_callback(_finish_wind_transition)
|
||||||
|
|
||||||
func _set_current_wind_speed(value: float) -> void:
|
func _set_current_wind_speed(value: float) -> void:
|
||||||
current_wind_speed = value
|
current_wind_speed = value
|
||||||
@@ -481,6 +510,7 @@ func _set_current_wind_speed(value: float) -> void:
|
|||||||
func _set_current_wind_strength(value: float) -> void:
|
func _set_current_wind_strength(value: float) -> void:
|
||||||
current_wind_strength = value
|
current_wind_strength = value
|
||||||
RenderingServer.global_shader_parameter_set("global_wind_strength", value)
|
RenderingServer.global_shader_parameter_set("global_wind_strength", value)
|
||||||
|
_set_wind_particles_amount_from_strength(value)
|
||||||
_sync_wind_event_state()
|
_sync_wind_event_state()
|
||||||
|
|
||||||
func _set_current_wind_fade(value: float) -> void:
|
func _set_current_wind_fade(value: float) -> void:
|
||||||
@@ -494,13 +524,10 @@ func _sync_wind_event_state() -> void:
|
|||||||
#used to notify wind_decoration when wind change state
|
#used to notify wind_decoration when wind change state
|
||||||
wind_parameters_changed.emit(current_wind_speed, current_wind_strength, current_wind_fade, environment_config.wind_direction)
|
wind_parameters_changed.emit(current_wind_speed, current_wind_strength, current_wind_fade, environment_config.wind_direction)
|
||||||
|
|
||||||
func _finish_wind_fade_out() -> void:
|
func _finish_wind_transition() -> void:
|
||||||
if is_windy:
|
_set_current_wind_speed(environment_config.wind_speed)
|
||||||
return
|
_set_current_wind_strength(_get_target_wind_strength())
|
||||||
|
_set_current_wind_fade(1.0)
|
||||||
_set_current_wind_speed(0.0)
|
|
||||||
_set_current_wind_strength(0.0)
|
|
||||||
_set_current_wind_fade(0.0)
|
|
||||||
|
|
||||||
func change_wind_strength(value: float) -> void:
|
func change_wind_strength(value: float) -> void:
|
||||||
if environment_config == null:
|
if environment_config == null:
|
||||||
@@ -508,10 +535,11 @@ func change_wind_strength(value: float) -> void:
|
|||||||
|
|
||||||
environment_config.wind_strength = value
|
environment_config.wind_strength = value
|
||||||
_update_wind_amount_from_strength(value)
|
_update_wind_amount_from_strength(value)
|
||||||
if is_windy or current_wind_fade > 0.0:
|
if current_wind_fade > 0.0 and not is_windy:
|
||||||
_set_current_wind_strength(value)
|
_set_current_wind_strength(value)
|
||||||
if particles_wind:
|
if particles_wind:
|
||||||
particles_wind.amount = environment_config.wind_amount
|
particles_wind.amount = environment_config.wind_amount
|
||||||
|
_apply_wind_state()
|
||||||
|
|
||||||
func _update_wind_amount_from_strength(value: float) -> void:
|
func _update_wind_amount_from_strength(value: float) -> void:
|
||||||
if environment_config == null:
|
if environment_config == null:
|
||||||
@@ -519,12 +547,22 @@ func _update_wind_amount_from_strength(value: float) -> void:
|
|||||||
if max_wind_amount <= 0:
|
if max_wind_amount <= 0:
|
||||||
max_wind_amount = max(environment_config.wind_amount, 1)
|
max_wind_amount = max(environment_config.wind_amount, 1)
|
||||||
|
|
||||||
|
environment_config.wind_amount = _get_wind_particles_amount_from_strength(value)
|
||||||
|
|
||||||
|
func _get_wind_particles_amount_from_strength(value: float) -> int:
|
||||||
|
if max_wind_amount <= 0:
|
||||||
|
max_wind_amount = 1
|
||||||
|
|
||||||
var normalized_strength: float = clamp(value, 0.0, 1.0)
|
var normalized_strength: float = clamp(value, 0.0, 1.0)
|
||||||
var amount_ratio: float = normalized_strength
|
var amount_ratio: float = normalized_strength
|
||||||
if normalized_strength > 0.0 and normalized_strength < 0.3:
|
if normalized_strength > 0.0 and normalized_strength < 0.3:
|
||||||
amount_ratio = lerp(0.08, 0.25, normalized_strength / 0.3)
|
amount_ratio = lerp(0.08, 0.25, normalized_strength / 0.3)
|
||||||
|
|
||||||
environment_config.wind_amount = roundi(max_wind_amount * amount_ratio)
|
return roundi(max_wind_amount * amount_ratio)
|
||||||
|
|
||||||
|
func _set_wind_particles_amount_from_strength(value: float) -> void:
|
||||||
|
if particles_wind:
|
||||||
|
particles_wind.amount = _get_wind_particles_amount_from_strength(value)
|
||||||
|
|
||||||
func trigger_random_weather_event(duration: float = 0.0) -> void:
|
func trigger_random_weather_event(duration: float = 0.0) -> void:
|
||||||
if random_weather_restore_tween and random_weather_restore_tween.is_valid():
|
if random_weather_restore_tween and random_weather_restore_tween.is_valid():
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ 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_scale = 0.22;
|
||||||
global uniform float global_snow_cap_noise_strength = 0.18;
|
global uniform float global_snow_cap_noise_strength = 0.18;
|
||||||
const float SNOW_VISUAL_RESPONSE = 0.55;
|
const float SNOW_VISUAL_RESPONSE = 0.55;
|
||||||
|
const float FULL_SNOW_VISUAL_START = 0.58;
|
||||||
|
const float FULL_SNOW_VISUAL_END = 0.86;
|
||||||
|
uniform bool snow_noise_enabled = true;
|
||||||
uniform float snow_noise_scale : hint_range(0.01, 1.0) = 0.15;
|
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_edge_softness : hint_range(0.01, 0.5) = 0.2;
|
||||||
uniform float snow_roughness_variation : hint_range(0.0, 0.3) = 0.15;
|
uniform float snow_roughness_variation : hint_range(0.0, 0.3) = 0.15;
|
||||||
@@ -92,6 +95,10 @@ float get_visual_snow_progress(float snow_progress) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float get_snow_noise_mask(vec3 world_pos, float snow_progress) {
|
float get_snow_noise_mask(vec3 world_pos, float snow_progress) {
|
||||||
|
if (!snow_noise_enabled) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
float detail_noise = texture(noise_texture, world_pos.xz * snow_noise_scale).r;
|
float detail_noise = texture(noise_texture, world_pos.xz * snow_noise_scale).r;
|
||||||
float broad_noise = texture(noise_texture, world_pos.xz * snow_noise_scale * 0.35 + vec2(19.1, 7.4)).r;
|
float broad_noise = texture(noise_texture, world_pos.xz * snow_noise_scale * 0.35 + vec2(19.1, 7.4)).r;
|
||||||
float combined_noise = mix(detail_noise, broad_noise, 0.35);
|
float combined_noise = mix(detail_noise, broad_noise, 0.35);
|
||||||
@@ -118,7 +125,7 @@ void vertex() {
|
|||||||
world_normal.y
|
world_normal.y
|
||||||
);
|
);
|
||||||
float accumulation_growth = smoothstep(0.02, 0.45, snow_progress);
|
float accumulation_growth = smoothstep(0.02, 0.45, snow_progress);
|
||||||
float cap_noise = fbm(world_pos.xz * global_snow_cap_noise_scale + vec2(11.3, 4.7));
|
float cap_noise = snow_noise_enabled ? fbm(world_pos.xz * global_snow_cap_noise_scale + vec2(11.3, 4.7)) : 0.5;
|
||||||
float cap_variation = mix(1.0 - global_snow_cap_noise_strength, 1.0 + global_snow_cap_noise_strength, cap_noise);
|
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_amount = snow_progress;
|
||||||
@@ -133,7 +140,7 @@ void fragment() {
|
|||||||
float facing_up = clamp(world_normal.y, 0.0, 1.0);
|
float facing_up = clamp(world_normal.y, 0.0, 1.0);
|
||||||
vec3 geometric_normal = normalize(cross(dFdx(world_pos), dFdy(world_pos)));
|
vec3 geometric_normal = normalize(cross(dFdx(world_pos), dFdy(world_pos)));
|
||||||
float snow_facing_up = max(facing_up, abs(geometric_normal.y));
|
float snow_facing_up = max(facing_up, abs(geometric_normal.y));
|
||||||
float noise_val = texture(noise_texture, world_pos.xz * snow_noise_scale).r;
|
float noise_val = snow_noise_enabled ? texture(noise_texture, world_pos.xz * snow_noise_scale).r : 0.5;
|
||||||
|
|
||||||
//Snow
|
//Snow
|
||||||
float snow_progress = v_snow_amount;
|
float snow_progress = v_snow_amount;
|
||||||
@@ -147,10 +154,12 @@ void fragment() {
|
|||||||
float flat_accumulation = smoothstep(0.0, 0.35, v_snow_cap_mask) * snow_accumulation;
|
float flat_accumulation = smoothstep(0.0, 0.35, v_snow_cap_mask) * snow_accumulation;
|
||||||
float snow_noise_mask = get_snow_noise_mask(world_pos, snow_progress);
|
float snow_noise_mask = get_snow_noise_mask(world_pos, snow_progress);
|
||||||
float snow_variation = mix(0.75, 1.15, noise_val);
|
float snow_variation = mix(0.75, 1.15, noise_val);
|
||||||
float snow_coverage = clamp(mix(snow_progress * 0.35, snow_progress * snow_variation, snow_noise_mask) + flat_accumulation * 0.15, 0.0, 1.0);
|
float snow_coverage = clamp(mix(snow_progress * 0.55, snow_progress * snow_variation, snow_noise_mask) + flat_accumulation * 0.2, 0.0, 1.0);
|
||||||
float snow_factor = max(snow_edge * snow_coverage, flat_accumulation);
|
float snow_factor = max(snow_edge * snow_coverage, flat_accumulation);
|
||||||
float snow_opacity = clamp(snow_factor, 0.0, 1.0);
|
float snow_opacity = clamp(snow_factor, 0.0, 1.0);
|
||||||
snow_opacity = max(snow_opacity, flat_accumulation * mix(0.45, 0.9, snow_noise_mask));
|
snow_opacity = max(snow_opacity, flat_accumulation * mix(0.45, 0.9, snow_noise_mask));
|
||||||
|
float full_snow_opacity = snow_edge * smoothstep(FULL_SNOW_VISUAL_START, FULL_SNOW_VISUAL_END, snow_progress);
|
||||||
|
snow_opacity = max(snow_opacity, full_snow_opacity);
|
||||||
|
|
||||||
float shade = mix(-snow_color_variation, snow_color_variation, noise_val);
|
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);
|
vec3 snow_albedo = clamp(global_snow_color.rgb + vec3(shade), 0.0, 1.0);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ extends Resource
|
|||||||
@export_group("Day")
|
@export_group("Day")
|
||||||
@export var start_time: float = 0.0 #start time of the day
|
@export var start_time: float = 0.0 #start time of the day
|
||||||
@export var day_duration: float = 300.0 #Duration of a full day cycle in seconds
|
@export var day_duration: float = 300.0 #Duration of a full day cycle in seconds
|
||||||
|
@export var start_day_time_paused: bool = false #Start with day time paused, without advancing the day cycle
|
||||||
@export var sunrise: float = 0.25 #day_time 1.0→2.0 (night colors → morning colors)
|
@export var sunrise: float = 0.25 #day_time 1.0→2.0 (night colors → morning colors)
|
||||||
@export var day: float = 0.45 #day_time 2.0→2.0 (stays morning/afternoon)
|
@export var day: float = 0.45 #day_time 2.0→2.0 (stays morning/afternoon)
|
||||||
@export var sunset: float = 0.80 #day_time 2.0→3.0 (afternoon colors → night colors)
|
@export var sunset: float = 0.80 #day_time 2.0→3.0 (afternoon colors → night colors)
|
||||||
@@ -154,11 +155,6 @@ extends Resource
|
|||||||
@export var godray_spawn_height: float = 5.0 #Y world position where god rays spawn
|
@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
|
@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
|
#Snow settings
|
||||||
@export_group("Snow")
|
@export_group("Snow")
|
||||||
@export var snow_amount: float = 0.0 #Initial snow coverage amount (0.0 = none, 1.0 = full)
|
@export var snow_amount: float = 0.0 #Initial snow coverage amount (0.0 = none, 1.0 = full)
|
||||||
@@ -185,7 +181,8 @@ extends Resource
|
|||||||
@export var wind_direction: Vector2 = Vector2(0.5, 0.4) #Global wind direction (XZ)
|
@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_speed: float = 0.2 #Global wind animation speed
|
||||||
@export var wind_scale: float = 0.025 #Global wind noise scale
|
@export var wind_scale: float = 0.025 #Global wind noise scale
|
||||||
@export var wind_strength: float = 0.3 #Global wind displacement strength
|
@export var wind_strength: float = 0.12 #Low ambient wind displacement strength
|
||||||
|
@export var wind_boost_strength: float = 1.0 #Wind displacement strength while wind event is active
|
||||||
@export var cloud_scale: float = 0.5 #Cloud noise scale in sky shader
|
@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 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
|
@export_range(0.01, 1.0) var cloud_scale_envshadows_rate: float = 0.01 #Cloud scale multiplier for environment shadows
|
||||||
|
|||||||
BIN
core/font/LTMakeup-Regular.otf
Normal file
36
core/font/LTMakeup-Regular.otf.import
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="font_data_dynamic"
|
||||||
|
type="FontFile"
|
||||||
|
uid="uid://d2vn50t5k7xbk"
|
||||||
|
path="res://.godot/imported/LTMakeup-Regular.otf-3aec1bac2ab646ee8d91792b37ffa3b8.fontdata"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/font/LTMakeup-Regular.otf"
|
||||||
|
dest_files=["res://.godot/imported/LTMakeup-Regular.otf-3aec1bac2ab646ee8d91792b37ffa3b8.fontdata"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
Rendering=null
|
||||||
|
antialiasing=1
|
||||||
|
generate_mipmaps=false
|
||||||
|
disable_embedded_bitmaps=true
|
||||||
|
multichannel_signed_distance_field=false
|
||||||
|
msdf_pixel_range=8
|
||||||
|
msdf_size=48
|
||||||
|
allow_system_fallback=true
|
||||||
|
force_autohinter=false
|
||||||
|
modulate_color_glyphs=false
|
||||||
|
hinting=1
|
||||||
|
subpixel_positioning=4
|
||||||
|
keep_rounding_remainders=true
|
||||||
|
oversampling=0.0
|
||||||
|
Fallbacks=null
|
||||||
|
fallbacks=[]
|
||||||
|
Compress=null
|
||||||
|
compress=true
|
||||||
|
preload=[]
|
||||||
|
language_support={}
|
||||||
|
script_support={}
|
||||||
|
opentype_features={}
|
||||||
6
core/font/tooltip_theme.tres
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[gd_resource type="Theme" format=3 uid="uid://cwqdjc6t31a8i"]
|
||||||
|
|
||||||
|
[ext_resource type="FontFile" uid="uid://d2vn50t5k7xbk" path="res://core/font/LTMakeup-Regular.otf" id="1_itbcg"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
default_font = ExtResource("1_itbcg")
|
||||||
BIN
core/game_menu/assets/page.png
Normal file
|
After Width: | Height: | Size: 583 KiB |
40
core/game_menu/assets/page.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://kvn5l0n11a3q"
|
||||||
|
path="res://.godot/imported/page.png-6c8405fbc970a849a62cab2019c549ca.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page.png"
|
||||||
|
dest_files=["res://.godot/imported/page.png-6c8405fbc970a849a62cab2019c549ca.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_1/1_dx.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
40
core/game_menu/assets/page_1/1_dx.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dyvwywse5raqt"
|
||||||
|
path="res://.godot/imported/1_dx.png-72d52e4e36c6fed20f7226414ff0abd6.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_1/1_dx.png"
|
||||||
|
dest_files=["res://.godot/imported/1_dx.png-72d52e4e36c6fed20f7226414ff0abd6.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_1/1_sx.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
40
core/game_menu/assets/page_1/1_sx.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dbpfa1xgxxn8s"
|
||||||
|
path="res://.godot/imported/1_sx.png-f04965d63490ae436799d59856936013.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_1/1_sx.png"
|
||||||
|
dest_files=["res://.godot/imported/1_sx.png-f04965d63490ae436799d59856936013.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
BIN
core/game_menu/assets/page_1/rings.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
40
core/game_menu/assets/page_1/rings.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://clvc6nlbgyvp1"
|
||||||
|
path="res://.godot/imported/rings.png-21117a64acb1855b44e3ce941dd421bd.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_1/rings.png"
|
||||||
|
dest_files=["res://.godot/imported/rings.png-21117a64acb1855b44e3ce941dd421bd.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_2/2_dx.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
40
core/game_menu/assets/page_2/2_dx.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://drwe2xcnp8vs1"
|
||||||
|
path="res://.godot/imported/2_dx.png-1c9a95db875176635d05c1084fc67cb1.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_2/2_dx.png"
|
||||||
|
dest_files=["res://.godot/imported/2_dx.png-1c9a95db875176635d05c1084fc67cb1.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_2/2_sx.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
40
core/game_menu/assets/page_2/2_sx.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cgllgxrstcoy7"
|
||||||
|
path="res://.godot/imported/2_sx.png-396d61afe82f03c22efe87c2626240ae.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_2/2_sx.png"
|
||||||
|
dest_files=["res://.godot/imported/2_sx.png-396d61afe82f03c22efe87c2626240ae.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_3/3_dx.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
40
core/game_menu/assets/page_3/3_dx.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b6ae0kp1m0x74"
|
||||||
|
path="res://.godot/imported/3_dx.png-c85d01f060ffa9993163c423a29a54fc.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_3/3_dx.png"
|
||||||
|
dest_files=["res://.godot/imported/3_dx.png-c85d01f060ffa9993163c423a29a54fc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_3/3_sx.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
40
core/game_menu/assets/page_3/3_sx.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://diuai5vtjx23l"
|
||||||
|
path="res://.godot/imported/3_sx.png-537fe112f093326f28478c136b142922.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_3/3_sx.png"
|
||||||
|
dest_files=["res://.godot/imported/3_sx.png-537fe112f093326f28478c136b142922.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_3/coming_soon_01.png
Normal file
|
After Width: | Height: | Size: 143 KiB |
40
core/game_menu/assets/page_3/coming_soon_01.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://vg5y4qgc26ko"
|
||||||
|
path="res://.godot/imported/coming_soon_01.png-25c9049aaff7242878eb6f73c7097db7.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_3/coming_soon_01.png"
|
||||||
|
dest_files=["res://.godot/imported/coming_soon_01.png-25c9049aaff7242878eb6f73c7097db7.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_3/coming_soon_02.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
40
core/game_menu/assets/page_3/coming_soon_02.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bmatlcouch6jm"
|
||||||
|
path="res://.godot/imported/coming_soon_02.png-3a0cf6d408d00a891bcda63b2e091de2.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_3/coming_soon_02.png"
|
||||||
|
dest_files=["res://.godot/imported/coming_soon_02.png-3a0cf6d408d00a891bcda63b2e091de2.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_3/coming_soon_03.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
40
core/game_menu/assets/page_3/coming_soon_03.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bm55xiv8jgnni"
|
||||||
|
path="res://.godot/imported/coming_soon_03.png-57925084415215faf6655b4afb668ed8.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_3/coming_soon_03.png"
|
||||||
|
dest_files=["res://.godot/imported/coming_soon_03.png-57925084415215faf6655b4afb668ed8.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_3/coming_soon_04.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
40
core/game_menu/assets/page_3/coming_soon_04.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cj81qdlrulal2"
|
||||||
|
path="res://.godot/imported/coming_soon_04.png-8f40cb1c6984110b3c2b3d9c3c9c8658.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_3/coming_soon_04.png"
|
||||||
|
dest_files=["res://.godot/imported/coming_soon_04.png-8f40cb1c6984110b3c2b3d9c3c9c8658.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
core/game_menu/assets/page_3/coming_soon_05.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
40
core/game_menu/assets/page_3/coming_soon_05.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://biu4i40ck272q"
|
||||||
|
path="res://.godot/imported/coming_soon_05.png-ac2ff567503cb2e314861075714ac8fb.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://core/game_menu/assets/page_3/coming_soon_05.png"
|
||||||
|
dest_files=["res://.godot/imported/coming_soon_05.png-ac2ff567503cb2e314861075714ac8fb.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
Before Width: | Height: | Size: 1.7 MiB |
@@ -19,6 +19,8 @@ func setup() -> void:
|
|||||||
_add_collectible(collectible)
|
_add_collectible(collectible)
|
||||||
if unlocked_collectible_ids.has(collectible.id):
|
if unlocked_collectible_ids.has(collectible.id):
|
||||||
_unlock_collectible(collectible.id)
|
_unlock_collectible(collectible.id)
|
||||||
|
if is_inside_tree():
|
||||||
|
await get_tree().process_frame
|
||||||
|
|
||||||
func on_collectible_unlocked(collectible_id: StringName) -> void:
|
func on_collectible_unlocked(collectible_id: StringName) -> void:
|
||||||
_unlock_collectible(collectible_id)
|
_unlock_collectible(collectible_id)
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
@onready var tabs: Array[GameMenuTab] = [$%Tab1, $%Tab2, $%Tab3]
|
@onready var tabs: Array[GameMenuTab] = [$%Tab1, $%Tab2, $%Tab3]
|
||||||
@onready var pages: Array[GameMenuPage] = [$%Page1, $%Page2, $%Page3]
|
|
||||||
var current_index = 0
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
for tab: GameMenuTab in tabs:
|
for tab: GameMenuTab in tabs:
|
||||||
@@ -10,9 +8,11 @@ func _ready() -> void:
|
|||||||
|
|
||||||
on_tab_pressed(0, true)
|
on_tab_pressed(0, true)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func on_tab_pressed(index: int, disable_animation: bool = false) -> void:
|
func on_tab_pressed(index: int, disable_animation: bool = false) -> void:
|
||||||
for page in pages:
|
if BookAPI.is_busy() and not disable_animation:
|
||||||
page.hide_page()
|
return
|
||||||
|
|
||||||
for tab in tabs:
|
for tab in tabs:
|
||||||
if tab.index == index:
|
if tab.index == index:
|
||||||
@@ -26,8 +26,4 @@ func on_tab_pressed(index: int, disable_animation: bool = false) -> void:
|
|||||||
else:
|
else:
|
||||||
tab.deselect()
|
tab.deselect()
|
||||||
|
|
||||||
pages[index].show_page()
|
BookAPI.go_to_page((index + 1) * 2)
|
||||||
current_index = index
|
|
||||||
|
|
||||||
func open() -> void:
|
|
||||||
pages[current_index].show_page()
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ extends Control
|
|||||||
@onready var photo: Photo = $%Photo
|
@onready var photo: Photo = $%Photo
|
||||||
@onready var panel: Panel = $%Panel
|
@onready var panel: Panel = $%Panel
|
||||||
|
|
||||||
|
var is_closing: bool = false
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
GameState.on_photo_highlighted.connect(_on_photo_highlighted)
|
GameState.on_photo_highlighted.connect(_on_photo_highlighted)
|
||||||
@@ -15,10 +15,13 @@ func _on_photo_highlighted(texture: Texture) -> void:
|
|||||||
TweenFX.fold_in(photo)
|
TweenFX.fold_in(photo)
|
||||||
photo.setup(texture)
|
photo.setup(texture)
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if not visible:
|
||||||
|
return
|
||||||
|
|
||||||
var is_closing: bool = false
|
if event is InputEventMouse:
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
func _on_panel_gui_input(event: InputEvent) -> void:
|
|
||||||
if is_closing:
|
if is_closing:
|
||||||
return
|
return
|
||||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||||
|
|||||||
@@ -45,6 +45,4 @@ theme_override_constants/margin_top = 30
|
|||||||
theme_override_constants/margin_right = 20
|
theme_override_constants/margin_right = 20
|
||||||
theme_override_constants/margin_bottom = 185
|
theme_override_constants/margin_bottom = 185
|
||||||
|
|
||||||
[connection signal="gui_input" from="Panel" to="." method="_on_panel_gui_input"]
|
|
||||||
|
|
||||||
[editable path="Photo"]
|
[editable path="Photo"]
|
||||||
|
|||||||
@@ -1,20 +1,51 @@
|
|||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
|
signal settings_requested
|
||||||
|
|
||||||
|
const TRAIN_WHISTLE_STREAM: AudioStream = preload("res://tgcc/train/sounds/train_whistle2.mp3")
|
||||||
|
|
||||||
@onready var play_button: Button = $%PlayButton
|
@onready var play_button: Button = $%PlayButton
|
||||||
@onready var resume_button: Button = $%ResumeButton
|
@onready var resume_button: Button = $%ResumeButton
|
||||||
@onready var settings_button: Button = $%SettingsButton
|
@onready var settings_button: Button = $%SettingsButton
|
||||||
@onready var save_button: Button = $%SaveButton
|
@onready var save_button: Button = $%SaveButton
|
||||||
@onready var quit_button: Button = $%QuitButton
|
@onready var quit_button: Button = $%QuitButton
|
||||||
|
@export var fade_time: float = 0.5
|
||||||
|
@export var menu_whistle_volume_db: float = -12.0
|
||||||
|
|
||||||
|
var _fallback_whistle_player: AudioStreamPlayer
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
_create_fallback_whistle_player()
|
||||||
play_button.pressed.connect(_on_play_button_pressed)
|
play_button.pressed.connect(_on_play_button_pressed)
|
||||||
save_button.pressed.connect(_on_save_button_pressed)
|
save_button.pressed.connect(_on_save_button_pressed)
|
||||||
quit_button.pressed.connect(_on_quit_button_pressed)
|
quit_button.pressed.connect(_on_quit_button_pressed)
|
||||||
|
resume_button.pressed.connect(func(): UIEvents.resume_game_requested.emit())
|
||||||
|
settings_button.pressed.connect(func(): settings_requested.emit())
|
||||||
|
|
||||||
func _on_play_button_pressed() -> void:
|
func _on_play_button_pressed() -> void:
|
||||||
|
_play_train_horn()
|
||||||
|
await get_tree().create_timer(fade_time).timeout
|
||||||
SceneManager.change_scene(SceneConfig.SceneName.GAME)
|
SceneManager.change_scene(SceneConfig.SceneName.GAME)
|
||||||
|
|
||||||
|
func _create_fallback_whistle_player() -> void:
|
||||||
|
_fallback_whistle_player = AudioStreamPlayer.new()
|
||||||
|
_fallback_whistle_player.name = "FallbackTrainWhistlePlayer"
|
||||||
|
_fallback_whistle_player.bus = &"SFX"
|
||||||
|
_fallback_whistle_player.volume_db = menu_whistle_volume_db
|
||||||
|
_fallback_whistle_player.stream = TRAIN_WHISTLE_STREAM
|
||||||
|
add_child(_fallback_whistle_player)
|
||||||
|
|
||||||
|
func _play_train_horn() -> void:
|
||||||
|
if not UIEvents.toot_toot.get_connections().is_empty():
|
||||||
|
UIEvents.toot_toot.emit(false)
|
||||||
|
return
|
||||||
|
|
||||||
|
if _fallback_whistle_player == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
_fallback_whistle_player.stop()
|
||||||
|
_fallback_whistle_player.play()
|
||||||
|
|
||||||
func _on_save_button_pressed() -> void:
|
func _on_save_button_pressed() -> void:
|
||||||
GameState.save_game()
|
GameState.save_game()
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://id854u4gh12f" path="res://core/game_menu/page.tscn" id="1_uxih6"]
|
[ext_resource type="PackedScene" uid="uid://id854u4gh12f" path="res://core/game_menu/page.tscn" id="1_uxih6"]
|
||||||
[ext_resource type="Texture2D" uid="uid://bd80xo2id3xdm" path="res://core/game_menu/assets/decoration_page1.png" id="2_3i50p"]
|
[ext_resource type="Texture2D" uid="uid://bd80xo2id3xdm" path="res://core/game_menu/assets/decoration_page1.png" id="2_3i50p"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bh1kxsp5jyxfx" path="res://core/game_menu/train_selector.tscn" id="3_ijm38"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://bc60gon7fmvbr" path="res://core/game_menu/biome_selector.tscn" id="12_7yi8x"]
|
|
||||||
|
|
||||||
[node name="Page1" unique_id=2037261222 instance=ExtResource("1_uxih6")]
|
[node name="Page1" unique_id=2037261222 instance=ExtResource("1_uxih6")]
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
@@ -16,6 +14,7 @@ grow_vertical = 2
|
|||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
|
|
||||||
[node name="Background" parent="." index="0" unique_id=1203110384]
|
[node name="Background" parent="." index="0" unique_id=1203110384]
|
||||||
|
visible = true
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
@@ -36,17 +35,3 @@ pivot_offset_ratio = Vector2(0.5, 0.5)
|
|||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
texture = ExtResource("2_3i50p")
|
texture = ExtResource("2_3i50p")
|
||||||
expand_mode = 1
|
expand_mode = 1
|
||||||
|
|
||||||
[node name="TrainSelector" parent="." index="2" unique_id=1039769375 instance=ExtResource("3_ijm38")]
|
|
||||||
layout_mode = 0
|
|
||||||
offset_left = 211.0
|
|
||||||
offset_top = 365.0
|
|
||||||
offset_right = 871.0
|
|
||||||
offset_bottom = 758.0
|
|
||||||
|
|
||||||
[node name="BiomeSelector" parent="." index="3" unique_id=1957954960 instance=ExtResource("12_7yi8x")]
|
|
||||||
layout_mode = 0
|
|
||||||
offset_left = 1038.0
|
|
||||||
offset_top = 365.0
|
|
||||||
offset_right = 1738.0
|
|
||||||
offset_bottom = 783.0
|
|
||||||
|
|||||||
52
core/game_menu/page_1_left.tscn
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
[gd_scene format=3 uid="uid://h8hbdm1uno7q"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://0yyr318wl7co" path="res://core/game_menu/page_interactive.gd" id="1_script"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bh1kxsp5jyxfx" path="res://core/game_menu/train_selector.tscn" id="2_train"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://kvn5l0n11a3q" path="res://core/game_menu/assets/page.png" id="3_page_bg"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dbpfa1xgxxn8s" path="res://core/game_menu/assets/page_1/1_sx.png" id="3_sm65a"]
|
||||||
|
|
||||||
|
[node name="Page1Left" type="Control" unique_id=1881242741]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = -1160.0
|
||||||
|
offset_bottom = -506.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
|
[node name="PageBackground" type="TextureRect" parent="." unique_id=820333238]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 9
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = 760.0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_page_bg")
|
||||||
|
expand_mode = 1
|
||||||
|
flip_h = true
|
||||||
|
|
||||||
|
[node name="PageBackground2" type="TextureRect" parent="." unique_id=1693418558]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 9
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = 760.0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_sm65a")
|
||||||
|
|
||||||
|
[node name="TrainSelector" parent="." unique_id=2114418660 instance=ExtResource("2_train")]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -346.0
|
||||||
|
offset_top = -154.5
|
||||||
|
offset_right = 279.0
|
||||||
|
offset_bottom = 154.5
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
58
core/game_menu/page_1_right.tscn
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
[gd_scene format=3 uid="uid://d06kfd33fgepf"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://0yyr318wl7co" path="res://core/game_menu/page_interactive.gd" id="1_script"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bc60gon7fmvbr" path="res://core/game_menu/biome_selector.tscn" id="2_biome"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dyvwywse5raqt" path="res://core/game_menu/assets/page_1/1_dx.png" id="3_6tfbo"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://kvn5l0n11a3q" path="res://core/game_menu/assets/page.png" id="3_page_bg"]
|
||||||
|
|
||||||
|
[node name="Page1Right" type="Control" unique_id=2093612752]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = -1160.0
|
||||||
|
offset_bottom = -506.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
|
[node name="PageBackground" type="TextureRect" parent="." unique_id=1432670109]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 11
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -760.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_page_bg")
|
||||||
|
expand_mode = 1
|
||||||
|
|
||||||
|
[node name="PageBackground2" type="TextureRect" parent="." unique_id=1327230938]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 11
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -760.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_6tfbo")
|
||||||
|
stretch_mode = 2
|
||||||
|
|
||||||
|
[node name="BiomeSelector" parent="." unique_id=679177937 instance=ExtResource("2_biome")]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -301.0
|
||||||
|
offset_top = -158.0
|
||||||
|
offset_right = 368.0
|
||||||
|
offset_bottom = 158.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
[gd_scene format=3 uid="uid://4bamw5busnvc"]
|
[gd_scene format=3 uid="uid://4bamw5busnvc"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://id854u4gh12f" path="res://core/game_menu/page.tscn" id="1_ka6tx"]
|
[ext_resource type="PackedScene" uid="uid://id854u4gh12f" path="res://core/game_menu/page.tscn" id="1_ka6tx"]
|
||||||
[ext_resource type="Texture2D" uid="uid://xgx0211eo8hi" path="res://core/game_menu/assets/page_2/photocollections_text.png" id="2_4uwhd"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://d3qq4ho24phyw" path="res://core/game_menu/assets/decoration_page2.png" id="2_xqdsh"]
|
[ext_resource type="Texture2D" uid="uid://d3qq4ho24phyw" path="res://core/game_menu/assets/decoration_page2.png" id="2_xqdsh"]
|
||||||
[ext_resource type="Texture2D" uid="uid://xfl3ks4dbfaw" path="res://core/game_menu/assets/page_2/achievements_text.png" id="3_4uwhd"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://b6r787sik5yil" path="res://core/game_menu/photo_gallery.tscn" id="4_3tfra"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://bvw086glfpcba" path="res://core/game_menu/collectible_gallery.tscn" id="5_w0bcg"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://c7t3p8sp54apy" path="res://core/game_menu/highlighted_photo.tscn" id="6_2273a"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://bx6gsh5uchkge" path="res://core/game_menu/assets/lock.png" id="8_48jk0"]
|
|
||||||
|
|
||||||
[node name="Page2" unique_id=2037261222 instance=ExtResource("1_ka6tx")]
|
[node name="Page2" unique_id=2037261222 instance=ExtResource("1_ka6tx")]
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
@@ -31,49 +25,4 @@ mouse_filter = 2
|
|||||||
texture = ExtResource("2_xqdsh")
|
texture = ExtResource("2_xqdsh")
|
||||||
expand_mode = 1
|
expand_mode = 1
|
||||||
|
|
||||||
[node name="PhotoCollectionText" type="TextureRect" parent="." index="2" unique_id=240014191]
|
|
||||||
layout_mode = 0
|
|
||||||
offset_left = 260.0
|
|
||||||
offset_top = 330.0
|
|
||||||
offset_right = 827.0
|
|
||||||
offset_bottom = 415.0
|
|
||||||
texture = ExtResource("2_4uwhd")
|
|
||||||
stretch_mode = 3
|
|
||||||
|
|
||||||
[node name="AchievementsText" type="TextureRect" parent="." index="3" unique_id=1478291812]
|
|
||||||
layout_mode = 0
|
|
||||||
offset_left = 1184.0
|
|
||||||
offset_top = 330.0
|
|
||||||
offset_right = 1579.0
|
|
||||||
offset_bottom = 401.0
|
|
||||||
texture = ExtResource("3_4uwhd")
|
|
||||||
stretch_mode = 3
|
|
||||||
|
|
||||||
[node name="PhotoGallery" parent="." index="4" unique_id=354419843 instance=ExtResource("4_3tfra")]
|
|
||||||
layout_mode = 1
|
|
||||||
offset_left = 244.0
|
|
||||||
offset_top = 410.0
|
|
||||||
offset_right = 844.0
|
|
||||||
offset_bottom = 850.0
|
|
||||||
|
|
||||||
[node name="CollectibleGallery" parent="." index="5" unique_id=708445500 instance=ExtResource("5_w0bcg")]
|
|
||||||
visible = false
|
|
||||||
layout_mode = 1
|
|
||||||
offset_left = 1050.0
|
|
||||||
offset_top = 425.0
|
|
||||||
offset_right = 1690.0
|
|
||||||
offset_bottom = 835.0011
|
|
||||||
|
|
||||||
[node name="HighlightedPhoto" parent="." index="6" unique_id=888465569 instance=ExtResource("6_2273a")]
|
|
||||||
visible = false
|
|
||||||
layout_mode = 1
|
|
||||||
|
|
||||||
[node name="LockTexture" type="TextureRect" parent="." index="7" unique_id=1369712242]
|
|
||||||
layout_mode = 0
|
|
||||||
offset_left = 1265.0
|
|
||||||
offset_top = 449.0
|
|
||||||
offset_right = 1547.0
|
|
||||||
offset_bottom = 809.6977
|
|
||||||
texture = ExtResource("8_48jk0")
|
|
||||||
expand_mode = 1
|
|
||||||
stretch_mode = 5
|
|
||||||
|
|||||||
57
core/game_menu/page_2_left.tscn
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
[gd_scene format=3 uid="uid://ct0pbs1nyi3d1"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://0yyr318wl7co" path="res://core/game_menu/page_interactive.gd" id="1_script"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://xgx0211eo8hi" path="res://core/game_menu/assets/page_2/photocollections_text.png" id="2_text"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b6r787sik5yil" path="res://core/game_menu/photo_gallery.tscn" id="3_gallery"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://cgllgxrstcoy7" path="res://core/game_menu/assets/page_2/2_sx.png" id="3_sfb5f"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://kvn5l0n11a3q" path="res://core/game_menu/assets/page.png" id="4_page_bg"]
|
||||||
|
|
||||||
|
[node name="Page2Left" type="Control" unique_id=922443654]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = -1160.0
|
||||||
|
offset_bottom = -506.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
|
[node name="PageBackground" type="TextureRect" parent="." unique_id=1425677666]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 9
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = 760.0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("4_page_bg")
|
||||||
|
expand_mode = 1
|
||||||
|
flip_h = true
|
||||||
|
|
||||||
|
[node name="PageBackground2" type="TextureRect" parent="." unique_id=1817272886]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 9
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = 760.0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_sfb5f")
|
||||||
|
stretch_mode = 2
|
||||||
|
|
||||||
|
[node name="PhotoCollectionText" type="TextureRect" parent="." unique_id=643307421]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 96.0
|
||||||
|
offset_top = 27.0
|
||||||
|
offset_right = 663.0
|
||||||
|
offset_bottom = 112.0
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("2_text")
|
||||||
|
stretch_mode = 3
|
||||||
|
|
||||||
|
[node name="PhotoGallery" parent="." unique_id=1823812771 instance=ExtResource("3_gallery")]
|
||||||
|
layout_mode = 1
|
||||||
|
offset_left = 80.0
|
||||||
|
offset_top = 107.0
|
||||||
|
offset_right = 680.0
|
||||||
|
offset_bottom = 547.0
|
||||||
103
core/game_menu/page_2_right.tscn
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
[gd_scene format=3 uid="uid://clno1ahv2byng"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://0yyr318wl7co" path="res://core/game_menu/page_interactive.gd" id="1_script"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://xfl3ks4dbfaw" path="res://core/game_menu/assets/page_2/achievements_text.png" id="3_4uwhd"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://drwe2xcnp8vs1" path="res://core/game_menu/assets/page_2/2_dx.png" id="3_msl27"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bvw086glfpcba" path="res://core/game_menu/collectible_gallery.tscn" id="5_w0bcg"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bx6gsh5uchkge" path="res://core/game_menu/assets/lock.png" id="8_48jk0"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://vg5y4qgc26ko" path="res://core/game_menu/assets/page_3/coming_soon_01.png" id="8_71v1w"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bmatlcouch6jm" path="res://core/game_menu/assets/page_3/coming_soon_02.png" id="9_g7kul"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://kvn5l0n11a3q" path="res://core/game_menu/assets/page.png" id="9_page_bg"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bm55xiv8jgnni" path="res://core/game_menu/assets/page_3/coming_soon_03.png" id="10_h6k52"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://cj81qdlrulal2" path="res://core/game_menu/assets/page_3/coming_soon_04.png" id="11_7aajs"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://biu4i40ck272q" path="res://core/game_menu/assets/page_3/coming_soon_05.png" id="12_dn2di"]
|
||||||
|
|
||||||
|
[sub_resource type="AnimatedTexture" id="AnimatedTexture_0x02d"]
|
||||||
|
frames = 5
|
||||||
|
frame_0/texture = ExtResource("8_71v1w")
|
||||||
|
frame_0/duration = 0.15
|
||||||
|
frame_1/texture = ExtResource("9_g7kul")
|
||||||
|
frame_1/duration = 0.15
|
||||||
|
frame_2/texture = ExtResource("10_h6k52")
|
||||||
|
frame_2/duration = 0.15
|
||||||
|
frame_3/texture = ExtResource("11_7aajs")
|
||||||
|
frame_3/duration = 0.15
|
||||||
|
frame_4/texture = ExtResource("12_dn2di")
|
||||||
|
frame_4/duration = 0.15
|
||||||
|
|
||||||
|
[node name="Page2Right" type="Control" unique_id=944053844]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = -1160.0
|
||||||
|
offset_bottom = -506.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
|
[node name="PageBackground" type="TextureRect" parent="." unique_id=1387851649]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 11
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -760.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("9_page_bg")
|
||||||
|
expand_mode = 1
|
||||||
|
|
||||||
|
[node name="PageBackground2" type="TextureRect" parent="." unique_id=1602066462]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 11
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -760.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_msl27")
|
||||||
|
stretch_mode = 2
|
||||||
|
|
||||||
|
[node name="AchievementsText" type="TextureRect" parent="." unique_id=766898810]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 261.0
|
||||||
|
offset_top = 34.5
|
||||||
|
offset_right = 656.0
|
||||||
|
offset_bottom = 105.5
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_4uwhd")
|
||||||
|
stretch_mode = 3
|
||||||
|
|
||||||
|
[node name="CollectibleGallery" parent="." unique_id=916407522 instance=ExtResource("5_w0bcg")]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 1
|
||||||
|
offset_left = 127.0
|
||||||
|
offset_top = 129.5
|
||||||
|
offset_right = 767.0
|
||||||
|
offset_bottom = 539.5
|
||||||
|
|
||||||
|
[node name="LockTexture" type="TextureRect" parent="." unique_id=535700029]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 342.0
|
||||||
|
offset_top = 153.5
|
||||||
|
offset_right = 624.0
|
||||||
|
offset_bottom = 514.2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("8_48jk0")
|
||||||
|
expand_mode = 1
|
||||||
|
stretch_mode = 5
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="." unique_id=1942592146]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 596.0
|
||||||
|
offset_top = 86.0
|
||||||
|
offset_right = 743.0
|
||||||
|
offset_bottom = 233.0
|
||||||
|
texture = SubResource("AnimatedTexture_0x02d")
|
||||||
|
expand_mode = 1
|
||||||
|
stretch_mode = 5
|
||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://id854u4gh12f" path="res://core/game_menu/page.tscn" id="1_ua8fn"]
|
[ext_resource type="PackedScene" uid="uid://id854u4gh12f" path="res://core/game_menu/page.tscn" id="1_ua8fn"]
|
||||||
[ext_resource type="Texture2D" uid="uid://fpnhk1ygcrmu" path="res://core/game_menu/assets/page_3/bg_timbri.png" id="2_ckqcb"]
|
[ext_resource type="Texture2D" uid="uid://fpnhk1ygcrmu" path="res://core/game_menu/assets/page_3/bg_timbri.png" id="2_ckqcb"]
|
||||||
[ext_resource type="PackedScene" uid="uid://cxf8s80ivwj1p" path="res://core/game_menu/option_menu.tscn" id="2_tymfi"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://db2yhovnqq4cp" path="res://core/game_menu/assets/decoration_page3.png" id="2_yx6jr"]
|
[ext_resource type="Texture2D" uid="uid://db2yhovnqq4cp" path="res://core/game_menu/assets/decoration_page3.png" id="2_yx6jr"]
|
||||||
[ext_resource type="PackedScene" uid="uid://caqf471x0kttc" path="res://core/game_menu/settings_menu.tscn" id="4_twko7"]
|
|
||||||
|
|
||||||
[node name="Page3" unique_id=2037261222 instance=ExtResource("1_ua8fn")]
|
[node name="Page3" unique_id=2037261222 instance=ExtResource("1_ua8fn")]
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
@@ -37,33 +35,4 @@ anchor_bottom = 0.7740741
|
|||||||
texture = ExtResource("2_ckqcb")
|
texture = ExtResource("2_ckqcb")
|
||||||
metadata/_edit_use_anchors_ = true
|
metadata/_edit_use_anchors_ = true
|
||||||
|
|
||||||
[node name="OptionMenu" parent="." index="3" unique_id=1055457221 instance=ExtResource("2_tymfi")]
|
|
||||||
layout_mode = 0
|
|
||||||
anchors_preset = 0
|
|
||||||
anchor_left = 0.7213542
|
|
||||||
anchor_top = 0.5212963
|
|
||||||
anchor_right = 0.7213542
|
|
||||||
anchor_bottom = 0.5212963
|
|
||||||
grow_horizontal = 1
|
|
||||||
grow_vertical = 1
|
|
||||||
metadata/_edit_use_anchors_ = true
|
|
||||||
|
|
||||||
[node name="PlayButton" parent="OptionMenu/VBoxContainer" parent_id_path=PackedInt32Array(1055457221, 508055524) index="0" unique_id=1990327704]
|
|
||||||
visible = false
|
|
||||||
|
|
||||||
[node name="SettingsButton" parent="OptionMenu/VBoxContainer" parent_id_path=PackedInt32Array(1055457221, 508055524) index="2" unique_id=1931222561]
|
|
||||||
visible = false
|
|
||||||
|
|
||||||
[node name="SettingsMenu" parent="." index="4" unique_id=1639777294 instance=ExtResource("4_twko7")]
|
|
||||||
layout_mode = 1
|
|
||||||
anchor_top = 0.5388889
|
|
||||||
anchor_right = 0.0
|
|
||||||
anchor_bottom = 0.5388889
|
|
||||||
offset_left = 278.11
|
|
||||||
offset_top = -240.0
|
|
||||||
offset_right = 902.11
|
|
||||||
offset_bottom = 285.0
|
|
||||||
grow_vertical = 2
|
|
||||||
size_flags_horizontal = 6
|
|
||||||
|
|
||||||
[editable path="OptionMenu"]
|
|
||||||
|
|||||||
50
core/game_menu/page_3_left.tscn
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
[gd_scene format=3 uid="uid://dhjocrxlqosyr"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://0yyr318wl7co" path="res://core/game_menu/page_interactive.gd" id="1_script"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://caqf471x0kttc" path="res://core/game_menu/settings_menu.tscn" id="2_settings"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://diuai5vtjx23l" path="res://core/game_menu/assets/page_3/3_sx.png" id="3_jdqrf"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://kvn5l0n11a3q" path="res://core/game_menu/assets/page.png" id="3_page_bg"]
|
||||||
|
|
||||||
|
[node name="Page3Left" type="Control" unique_id=1419303211]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = -1160.0
|
||||||
|
offset_bottom = -506.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
|
[node name="PageBackground" type="TextureRect" parent="." unique_id=1048771500]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 9
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = 760.0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_page_bg")
|
||||||
|
expand_mode = 1
|
||||||
|
flip_h = true
|
||||||
|
|
||||||
|
[node name="PageBackground2" type="TextureRect" parent="." unique_id=1869708897]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 9
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = 760.0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_jdqrf")
|
||||||
|
stretch_mode = 2
|
||||||
|
|
||||||
|
[node name="SettingsMenu" parent="." unique_id=1402645746 instance=ExtResource("2_settings")]
|
||||||
|
layout_mode = 0
|
||||||
|
anchors_preset = 0
|
||||||
|
anchor_right = 0.0
|
||||||
|
anchor_bottom = 0.0
|
||||||
|
offset_left = 68.0
|
||||||
|
offset_top = 24.5
|
||||||
|
offset_right = 692.0
|
||||||
|
offset_bottom = 549.5
|
||||||
|
size_flags_horizontal = 6
|
||||||
64
core/game_menu/page_3_right.tscn
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
[gd_scene format=3 uid="uid://cnaguqaowck52"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://0yyr318wl7co" path="res://core/game_menu/page_interactive.gd" id="1_script"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cxf8s80ivwj1p" path="res://core/game_menu/option_menu.tscn" id="2_option"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b6ae0kp1m0x74" path="res://core/game_menu/assets/page_3/3_dx.png" id="3_j1o0h"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://kvn5l0n11a3q" path="res://core/game_menu/assets/page.png" id="3_page_bg"]
|
||||||
|
|
||||||
|
[node name="Page3Right" type="Control" unique_id=1670917406]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = -1160.0
|
||||||
|
offset_bottom = -506.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
|
[node name="PageBackground" type="TextureRect" parent="." unique_id=957141621]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 11
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -760.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_page_bg")
|
||||||
|
expand_mode = 1
|
||||||
|
|
||||||
|
[node name="PageBackground2" type="TextureRect" parent="." unique_id=409054633]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 11
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -760.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
texture = ExtResource("3_j1o0h")
|
||||||
|
stretch_mode = 2
|
||||||
|
|
||||||
|
[node name="OptionMenu" parent="." unique_id=537721615 instance=ExtResource("2_option")]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -181.5
|
||||||
|
offset_top = -212.0
|
||||||
|
offset_right = 248.5
|
||||||
|
offset_bottom = 212.0
|
||||||
|
|
||||||
|
[node name="PlayButton" parent="OptionMenu/VBoxContainer" parent_id_path=PackedInt32Array(537721615, 508055524) index="0" unique_id=1990327704]
|
||||||
|
visible = false
|
||||||
|
|
||||||
|
[node name="SettingsButton" parent="OptionMenu/VBoxContainer" parent_id_path=PackedInt32Array(537721615, 508055524) index="2" unique_id=1931222561]
|
||||||
|
visible = false
|
||||||
|
|
||||||
|
[editable path="OptionMenu"]
|
||||||
14
core/game_menu/page_interactive.gd
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
extends Control
|
||||||
|
|
||||||
|
signal manage_pageflip(give_control: bool)
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
manage_pageflip.connect(_on_manage_pageflip)
|
||||||
|
|
||||||
|
func _on_manage_pageflip(give_control: bool) -> void:
|
||||||
|
var tweens = find_children("*", "ContainerTween", true, false)
|
||||||
|
for tween in tweens:
|
||||||
|
if give_control:
|
||||||
|
tween.start_tween()
|
||||||
|
else:
|
||||||
|
tween.stop_tween()
|
||||||
1
core/game_menu/page_interactive.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://0yyr318wl7co
|
||||||
@@ -17,6 +17,8 @@ func load_gallery() -> void:
|
|||||||
for file_path in saved_photos:
|
for file_path in saved_photos:
|
||||||
if FileAccess.file_exists(file_path):
|
if FileAccess.file_exists(file_path):
|
||||||
_create_photo_thumbnail(file_path)
|
_create_photo_thumbnail(file_path)
|
||||||
|
if is_inside_tree():
|
||||||
|
await get_tree().process_frame
|
||||||
|
|
||||||
func _create_photo_thumbnail(file_path: String) -> void:
|
func _create_photo_thumbnail(file_path: String) -> void:
|
||||||
var image = Image.load_from_file(file_path)
|
var image = Image.load_from_file(file_path)
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ func _on_color_selected(selected_color_picker: TrainColorPicker, material_index:
|
|||||||
for i in range(color_pickers.size()):
|
for i in range(color_pickers.size()):
|
||||||
if color_pickers[i] == selected_color_picker:
|
if color_pickers[i] == selected_color_picker:
|
||||||
_set_selected_color(i, material_index)
|
_set_selected_color(i, material_index)
|
||||||
|
AchievementManager.unlock("ACH_CHANGE_TRAIN_COLOR")
|
||||||
break
|
break
|
||||||
|
|
||||||
func _apply_train_color(color: Color, material_index: int, color_index: int = 0) -> void:
|
func _apply_train_color(color: Color, material_index: int, color_index: int = 0) -> void:
|
||||||
|
|||||||
@@ -68,36 +68,37 @@ alignment = 1
|
|||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
color = Color(0.7432059, 0.5684909, 1, 1)
|
color = Color(0.72156864, 0.3529412, 0.32156864, 1)
|
||||||
|
|
||||||
[node name="ColorPicker2" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=584054476 instance=ExtResource("3_i2rs1")]
|
[node name="ColorPicker2" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=584054476 instance=ExtResource("3_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
color = Color(0.47657922, 0.8792635, 0.8823829, 1)
|
color = Color(0.83137256, 0.61960787, 0.3254902, 1)
|
||||||
|
|
||||||
[node name="ColorPicker3" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=889429149 instance=ExtResource("3_i2rs1")]
|
[node name="ColorPicker3" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=889429149 instance=ExtResource("3_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
color = Color(0.54004896, 0.5516501, 0.45533067, 1)
|
color = Color(0.35686275, 0.54901963, 0.5803922, 1)
|
||||||
|
|
||||||
[node name="ColorPicker4" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=466686411 instance=ExtResource("3_i2rs1")]
|
[node name="ColorPicker4" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=466686411 instance=ExtResource("3_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
color = Color(0.40863404, 1, 0.47657922, 1)
|
color = Color(0.43137255, 0.52156866, 0.40784314, 1)
|
||||||
|
|
||||||
[node name="ColorPicker5" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=675841518 instance=ExtResource("3_i2rs1")]
|
[node name="ColorPicker5" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=675841518 instance=ExtResource("3_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
color = Color(1, 0, 0, 1)
|
color = Color(0.5568628, 0.49803922, 0.65882355, 1)
|
||||||
|
|
||||||
[node name="ColorPicker6" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=1698412911 instance=ExtResource("3_i2rs1")]
|
[node name="ColorPicker6" parent="TextureRect/MarginContainer/HBoxContainer" unique_id=1698412911 instance=ExtResource("3_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
|
color = Color(0.32156864, 0.43137255, 0.49019608, 1)
|
||||||
|
|
||||||
[node name="TextureRect2" type="TextureRect" parent="." unique_id=1180760395]
|
[node name="TextureRect2" type="TextureRect" parent="." unique_id=1180760395]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -123,13 +124,13 @@ alignment = 1
|
|||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
color = Color(0.7432059, 0.5684909, 1, 1)
|
color = Color(0.92156863, 0.7529412, 0.40784314, 1)
|
||||||
|
|
||||||
[node name="ColorPicker8" parent="TextureRect2/MarginContainer/HBoxContainer2" unique_id=396324135 instance=ExtResource("3_i2rs1")]
|
[node name="ColorPicker8" parent="TextureRect2/MarginContainer/HBoxContainer2" unique_id=396324135 instance=ExtResource("3_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
color = Color(0.47657922, 0.8792635, 0.8823829, 1)
|
color = Color(0.9529412, 0.91764706, 0.827451, 1)
|
||||||
|
|
||||||
[node name="ColorPicker9" parent="TextureRect2/MarginContainer/HBoxContainer2" unique_id=1233950004 instance=ExtResource("3_i2rs1")]
|
[node name="ColorPicker9" parent="TextureRect2/MarginContainer/HBoxContainer2" unique_id=1233950004 instance=ExtResource("3_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
@@ -141,19 +142,20 @@ color = Color(0.54004896, 0.5516501, 0.45533067, 1)
|
|||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
color = Color(0.40863404, 1, 0.47657922, 1)
|
color = Color(0.33591065, 0.42461705, 0.38732395, 1)
|
||||||
|
|
||||||
[node name="ColorPicker11" parent="TextureRect2/MarginContainer/HBoxContainer2" unique_id=831493295 instance=ExtResource("3_i2rs1")]
|
[node name="ColorPicker11" parent="TextureRect2/MarginContainer/HBoxContainer2" unique_id=831493295 instance=ExtResource("3_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
color = Color(1, 0, 0, 1)
|
color = Color(0.44940293, 0.7807106, 0.89803255, 1)
|
||||||
|
|
||||||
[node name="ColorPicker12" parent="TextureRect2/MarginContainer/HBoxContainer2" unique_id=170636843 instance=ExtResource("3_i2rs1")]
|
[node name="ColorPicker12" parent="TextureRect2/MarginContainer/HBoxContainer2" unique_id=170636843 instance=ExtResource("3_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
|
color = Color(0.9098039, 0.6, 0.5529412, 1)
|
||||||
|
|
||||||
[node name="ContainerTween" parent="." unique_id=160227524 node_paths=PackedStringArray("targets") instance=ExtResource("10_i2rs1")]
|
[node name="ContainerTween" parent="." unique_id=160227524 node_paths=PackedStringArray("targets") instance=ExtResource("10_i2rs1")]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
targets = [NodePath(""), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker1"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker2"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker3"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker4"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker5"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker6"), NodePath("")]
|
targets = [null, NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker1"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker2"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker3"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker4"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker5"), NodePath("../TextureRect/MarginContainer/HBoxContainer/ColorPicker6"), null]
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ func _ready() -> void:
|
|||||||
deselect()
|
deselect()
|
||||||
disable()
|
disable()
|
||||||
|
|
||||||
func set_option_text(text: String) -> void:
|
func set_option_text(_text: String) -> void:
|
||||||
if label == null:
|
if label == null:
|
||||||
label = $%Label
|
label = $%Label
|
||||||
label.text = text
|
label.text = _text
|
||||||
|
|
||||||
func select() -> void:
|
func select() -> void:
|
||||||
if checkbox == null:
|
if checkbox == null:
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ signal on_photo_taken_finished
|
|||||||
signal on_photo_mode_black_screen_disappeared
|
signal on_photo_mode_black_screen_disappeared
|
||||||
|
|
||||||
const SAVE_PATH: String = "user://savegame.json"
|
const SAVE_PATH: String = "user://savegame.json"
|
||||||
|
const SAVE_PASSWORD: String = "jmpgames_megapwd"
|
||||||
|
|
||||||
var save_data: SaveGameData = SaveGameData.new()
|
var save_data: SaveGameData = SaveGameData.new()
|
||||||
var is_loaded: bool = false
|
var is_loaded: bool = false
|
||||||
@@ -39,17 +40,7 @@ func apply_video_settings() -> void:
|
|||||||
get_viewport().use_taa = save_data.anti_aliasing
|
get_viewport().use_taa = save_data.anti_aliasing
|
||||||
var msaa_mode = Viewport.MSAA_2X if save_data.anti_aliasing else Viewport.MSAA_DISABLED
|
var msaa_mode = Viewport.MSAA_2X if save_data.anti_aliasing else Viewport.MSAA_DISABLED
|
||||||
get_viewport().msaa_3d = msaa_mode
|
get_viewport().msaa_3d = msaa_mode
|
||||||
get_viewport().msaa_2d = msaa_mode
|
get_viewport().msaa_2d = Viewport.MSAA_DISABLED
|
||||||
|
|
||||||
func apply_train_colors() -> void:
|
|
||||||
if not save_data.train_color_1.is_empty():
|
|
||||||
var mat1: ShaderMaterial = load("res://tgcc/train/Color1_train.tres")
|
|
||||||
if mat1:
|
|
||||||
mat1.set_shader_parameter("albedo_color", Color(save_data.train_color_1))
|
|
||||||
if not save_data.train_color_2.is_empty():
|
|
||||||
var mat2: ShaderMaterial = load("res://tgcc/train/Color2_train.tres")
|
|
||||||
if mat2:
|
|
||||||
mat2.set_shader_parameter("albedo_color", Color(save_data.train_color_2))
|
|
||||||
|
|
||||||
match save_data.window_mode_index:
|
match save_data.window_mode_index:
|
||||||
0:
|
0:
|
||||||
@@ -64,34 +55,79 @@ func apply_train_colors() -> void:
|
|||||||
DisplayServer.window_set_size(Vector2i(save_data.resolution_x, save_data.resolution_y))
|
DisplayServer.window_set_size(Vector2i(save_data.resolution_x, save_data.resolution_y))
|
||||||
|
|
||||||
# Optional: center the window if resized
|
# Optional: center the window if resized
|
||||||
var screen_center = DisplayServer.screen_get_position() + DisplayServer.screen_get_size() / 2
|
var screen_position: Vector2i = DisplayServer.screen_get_position()
|
||||||
var window_size = DisplayServer.window_get_size()
|
var screen_size: Vector2i = DisplayServer.screen_get_size()
|
||||||
DisplayServer.window_set_position(screen_center - window_size / 2)
|
var window_size: Vector2i = DisplayServer.window_get_size()
|
||||||
|
var screen_center := screen_position + Vector2i(
|
||||||
|
floori(float(screen_size.x) / 2.0),
|
||||||
|
floori(float(screen_size.y) / 2.0)
|
||||||
|
)
|
||||||
|
var window_half_size := Vector2i(
|
||||||
|
floori(float(window_size.x) / 2.0),
|
||||||
|
floori(float(window_size.y) / 2.0)
|
||||||
|
)
|
||||||
|
DisplayServer.window_set_position(screen_center - window_half_size)
|
||||||
|
|
||||||
|
func apply_train_colors() -> void:
|
||||||
|
if not save_data.train_color_1.is_empty():
|
||||||
|
var mat1: ShaderMaterial = load("res://tgcc/train/Color1_train.tres")
|
||||||
|
if mat1:
|
||||||
|
mat1.set_shader_parameter("albedo_color", Color(save_data.train_color_1))
|
||||||
|
if not save_data.train_color_2.is_empty():
|
||||||
|
var mat2: ShaderMaterial = load("res://tgcc/train/Color2_train.tres")
|
||||||
|
if mat2:
|
||||||
|
mat2.set_shader_parameter("albedo_color", Color(save_data.train_color_2))
|
||||||
|
|
||||||
func save_game() -> void:
|
#encrypted file + binary serialization (standard)
|
||||||
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:
|
func load_game() -> void:
|
||||||
if not FileAccess.file_exists(SAVE_PATH):
|
if not FileAccess.file_exists(SAVE_PATH):
|
||||||
save_data = SaveGameData.new()
|
save_data = SaveGameData.new()
|
||||||
is_loaded = true
|
is_loaded = true
|
||||||
return
|
return
|
||||||
|
|
||||||
var file = FileAccess.open(SAVE_PATH, FileAccess.READ)
|
var file := FileAccess.open_encrypted_with_pass(SAVE_PATH, FileAccess.READ, SAVE_PASSWORD)
|
||||||
var json_string = file.get_as_text()
|
|
||||||
|
|
||||||
var json = JSON.new()
|
|
||||||
var parse_result = json.parse(json_string)
|
|
||||||
|
|
||||||
save_data = SaveGameData.new()
|
save_data = SaveGameData.new()
|
||||||
if parse_result == OK and json.data is Dictionary:
|
if file != null:
|
||||||
save_data = SaveGameData.from_dictionary(json.data)
|
var data = file.get_var()
|
||||||
|
file.close()
|
||||||
|
if data is Dictionary:
|
||||||
|
save_data = SaveGameData.from_dictionary(data)
|
||||||
|
#if file == null the key or the file is corrupted -> start with new data
|
||||||
is_loaded = true
|
is_loaded = true
|
||||||
|
|
||||||
|
|
||||||
|
func save_game() -> void:
|
||||||
|
var file := FileAccess.open_encrypted_with_pass(SAVE_PATH, FileAccess.WRITE, SAVE_PASSWORD)
|
||||||
|
if file == null:
|
||||||
|
push_error("Salvataggio fallito: %s" % FileAccess.get_open_error())
|
||||||
|
return
|
||||||
|
file.store_var(save_data.to_dictionary()) # binario, non JSON
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
#JSon Version
|
||||||
|
#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
|
||||||
|
|
||||||
func pause_game() -> void:
|
func pause_game() -> void:
|
||||||
get_tree().paused = true
|
get_tree().paused = true
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ extends Control
|
|||||||
@onready var option_menu: Control = $%OptionMenu
|
@onready var option_menu: Control = $%OptionMenu
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
option_menu.settings_button.pressed.connect(_on_settings_button_pressed)
|
option_menu.settings_requested.connect(_on_settings_button_pressed)
|
||||||
|
|
||||||
|
|
||||||
func _on_settings_button_pressed() -> void:
|
func _on_settings_button_pressed() -> void:
|
||||||
|
|||||||
47
core/main_menu/sign.gd
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
extends Node3D
|
||||||
|
|
||||||
|
@export var sub_viewport: SubViewport
|
||||||
|
@onready var quad_cartello = $%QuadSign
|
||||||
|
@onready var coll_shape = $%CollisionShape3D
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
sub_viewport.transparent_bg = true
|
||||||
|
|
||||||
|
var mat = StandardMaterial3D.new()
|
||||||
|
mat.albedo_texture = sub_viewport.get_texture()
|
||||||
|
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
|
||||||
|
#fix color
|
||||||
|
sub_viewport.use_hdr_2d = false
|
||||||
|
mat.albedo_texture_force_srgb = true
|
||||||
|
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
mat.albedo_color = Color(0.85, 0.85, 0.85, 1.0)
|
||||||
|
|
||||||
|
quad_cartello.material_override = mat
|
||||||
|
|
||||||
|
coll_shape.shape.size = Vector3(2.0, 2.0, 2.0)
|
||||||
|
coll_shape.position.z = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
func _on_area_3d_input_event(camera: Node, event: InputEvent, event_position: Vector3, normal: Vector3, shape_idx: int) -> void:
|
||||||
|
if event is InputEventMouse:
|
||||||
|
var local_cam = quad_cartello.to_local(camera.global_position)
|
||||||
|
var local_hit = quad_cartello.to_local(event_position)
|
||||||
|
var ray_dir = (local_hit - local_cam).normalized()
|
||||||
|
|
||||||
|
if abs(ray_dir.z) < 0.0001: return
|
||||||
|
|
||||||
|
var t = -local_cam.z / ray_dir.z
|
||||||
|
var exact_pos = local_cam + ray_dir * t
|
||||||
|
|
||||||
|
var uv_x = exact_pos.x + 0.5
|
||||||
|
var uv_y = 0.5 - exact_pos.y
|
||||||
|
if uv_x < 0 or uv_x > 1 or uv_y < 0 or uv_y > 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
var viewport_pos = Vector2(uv_x * sub_viewport.size.x, uv_y * sub_viewport.size.y)
|
||||||
|
var ev2d = event.duplicate()
|
||||||
|
ev2d.position = viewport_pos
|
||||||
|
ev2d.global_position = viewport_pos
|
||||||
|
|
||||||
|
sub_viewport.push_input(ev2d)
|
||||||