21 Commits

Author SHA1 Message Date
e8c6c70fe7 fix ui event 2026-06-30 21:47:39 +02:00
5214f87e94 Merge branch 'main' into game_menu 2026-06-30 21:47:32 +02:00
9eebc75fa3 add tween to time transition 2026-06-30 21:35:25 +02:00
8198f7df1f fix snow and exposure 2026-06-30 17:04:40 +02:00
952058b0e0 fix snow for train and wagons 2026-06-30 11:08:37 +02:00
475749c4df fix wires, fix exposition for wheater change, add base wind, add fireflies when is night 2026-06-30 10:08:18 +02:00
1b5b364313 fix game menu 2026-06-29 23:56:37 +02:00
c6893b7ee8 update game menu 2026-06-29 23:34:55 +02:00
fc887c2519 fix page flip plugin file saved 2026-06-29 21:11:23 +02:00
086167a5a6 fix transition from splash 2026-06-29 18:16:30 +02:00
885bac9a4a Merge branch 'main' of https://gitea.jmpgames.it/jmp-games/tgcc 2026-06-29 18:10:58 +02:00
496a271946 fix transition 2026-06-29 18:10:38 +02:00
9da7b074d4 add page flip 2026-06-29 15:55:01 +02:00
d0d052f65b polish 8 2026-06-29 13:33:30 +00:00
67a99051bd update save mode 2026-06-29 15:23:44 +02:00
fb848a9848 add new transaction and add splash screen 2026-06-29 14:59:58 +02:00
bc30a169dc fix stats manager 2026-06-29 10:48:58 +02:00
f5576959b7 add achievement for train change 2026-06-29 10:21:56 +02:00
b8ab8f4971 Merge pull request 'add whistle to train' (#38) from whitleandstats into main
Reviewed-on: #38
2026-06-29 08:20:37 +00:00
22653d62bc fix train_selector 2026-06-29 10:20:11 +02:00
6a2a2d2e8a add whistle to train 2026-06-29 08:35:14 +02:00
119 changed files with 9793 additions and 261 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 KiB

View File

@@ -2,16 +2,16 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://d4e1lbng4uqk"
path="res://.godot/imported/quad_main.png-af26b29d5c3350fc4d75e08ad350ccc7.ctex"
uid="uid://bjowpx1ap4uxt"
path="res://.godot/imported/back.png-bddbf3418b746eb53bc79fd4d0989d41.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://core/game_menu/assets/page_3/quad_main.png"
dest_files=["res://.godot/imported/quad_main.png-af26b29d5c3350fc4d75e08ad350ccc7.ctex"]
source_file="res://addons/PageFlip/Assets/back.png"
dest_files=["res://.godot/imported/back.png-bddbf3418b746eb53bc79fd4d0989d41.ctex"]
[params]

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

View 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

Binary file not shown.

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 KiB

View 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

Binary file not shown.

View 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

View 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);
}

View File

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

View 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

View 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")

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View 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
View 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)

View File

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

1416
addons/PageFlip/page_flip.gd Normal file

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because one or more lines are too long

View 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("")

View File

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

View File

@@ -86,7 +86,7 @@ const RIVER_NEIGHBOUR_OFFSETS: Dictionary = {
##max cells to search around a lamppost chunk
@export_range(1, 6, 1) var lamppost_search_radius_cells: int = 3
##max allowed distance between lamppost pairs
@export var lamppost_max_wire_distance: float = 60.0
@export var lamppost_max_wire_distance: float = 35.0
##if true wire connections blocked by colliders are discarded
@export var lamppost_obstacle_check_enabled: bool = true

View File

@@ -1,7 +1,7 @@
extends Path3D
const STEAM_DISTANCE_STAT: String = "stat_distance_km"
const STEAM_DISTANCE_KM_PER_UNIT: float = 1#0.001
const DISTANCE_KM_PER_UNIT: float = 0.01
@export_group("Train")
##train speed
@@ -83,17 +83,20 @@ var environment_config: EnvironmentConfig
var wagon_instances: Array[Node3D] = []
var wagon_progress_offsets: Array[float] = []
var _pending_steam_distance_km: float = 0.0
var _initial_is_inmotion: bool = true
func _ready() -> void:
randomize()
_cache_environment_config()
_initial_is_inmotion = is_inmotion
if curve != null and curve.get_baked_length() > 0:
build_rails()
build_train()
_plan_stops()
_apply_initial_train_start()
_apply_train_start_delay()
else:
print("WARNING: Draw Path3D for rails!")
@@ -138,6 +141,23 @@ func _apply_initial_train_start() -> void:
_snap_train_to_progress()
func _apply_train_start_delay() -> void:
if not _initial_is_inmotion:
return
var start_delay_seconds: float = 0.0
if environment_config != null:
start_delay_seconds = maxf(environment_config.train_start_delay_seconds, 0.0)
if start_delay_seconds <= 0.0:
return
is_inmotion = false
await get_tree().create_timer(start_delay_seconds).timeout
if not is_inside_tree():
return
is_inmotion = _initial_is_inmotion
func _set_next_stop_index_from_current(current_stop_index: int) -> void:
if stop_offset.is_empty():
next_stop_index = 0
@@ -396,13 +416,13 @@ func _track_steam_distance(distance_units: float) -> void:
return
# 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
# Steam specific logic
if not SteamManager.is_on_steam:
return
_pending_steam_distance_km += distance_units * STEAM_DISTANCE_KM_PER_UNIT
_pending_steam_distance_km += distance_units * DISTANCE_KM_PER_UNIT
var whole_km: int = int(floor(_pending_steam_distance_km))
if whole_km <= 0:
return

View File

@@ -12,6 +12,10 @@ var environment_config: EnvironmentConfig
@export var time_scale: float = 1.0
@export var paused: bool = false
var _time_tween: Tween
var time_transition_duration: float = 2
signal time_changed(time: float)
var current_time: float = 0.0
@@ -67,16 +71,30 @@ func get_time_option_index(currtime: float) -> int:
return TIME_OPTION_DAY
return TIME_OPTION_SUNSET
func _time_option_changed(index: int) -> void:
var target_time: float = 0.0
match index:
TIME_OPTION_SUNRISE:
set_time(environment_config.sunrise)
target_time = environment_config.sunrise
TIME_OPTION_DAY:
set_time(environment_config.day)
target_time = environment_config.day
TIME_OPTION_SUNSET:
set_time(environment_config.sunset)
target_time = environment_config.sunset
TIME_OPTION_NIGHT:
set_time(environment_config.night)
target_time = environment_config.night
if _time_tween:
_time_tween.kill()
_time_tween = create_tween()
# Always move forward in time for a natural transition
if target_time < current_time:
target_time += 1.0
_time_tween.tween_method(set_time, current_time, target_time, time_transition_duration)
func _on_set_day_time(value: float) -> void:
set_time(value)

View File

@@ -39,6 +39,7 @@ var weather_controller: WeatherController
var day_tween: Tween
var day_time: float = 0.0
var pending_environment_nodes: Dictionary = {}
var weather_shader_no_noise: Material
func _ready() -> void:
@@ -80,6 +81,9 @@ func _ready() -> void:
weather_controller.name = "WeatherController"
add_child(weather_controller)
if day_night_controller != null:
select_day_time(day_night_controller.current_time)
func _process(_delta: float) -> void:
_process_pending_environment_nodes()
@@ -135,10 +139,10 @@ func _apply_dynamic_environment_materials(node: Node) -> void:
if node.is_in_group("wind_node"):
_apply_wind_noise_to_node(node, NOISE_TEXTURE)
if node.is_in_group("weather_node"):
_apply_weather_overlay_to_node(node, WEATHER_SHADER)
if node.is_in_group("weather_node") or node.is_in_group("weather_vegetables_node"):
_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)
func ApplyWindNoiseToMaterials():
@@ -162,19 +166,30 @@ func _apply_wind_noise_to_node(node: Node, noise_tex: Texture2D) -> void:
func ApplyWeatherShaderToMaterials():
for node in get_tree().get_nodes_in_group("weather_node"):
_apply_weather_overlay_to_node(node, WEATHER_SHADER)
_apply_weather_overlay_to_node(node, _get_weather_overlay_material(node))
for node in get_tree().get_nodes_in_group("weather_vegetables_node"):
if _should_clear_weather_overlay(node):
_clear_weather_overlay_from_node(node)
_apply_weather_overlay_to_node(node, _get_weather_overlay_material(node))
func _get_weather_overlay_material(node: Node) -> Material:
if not node.is_in_group("weather_overlay_no_noise"):
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:
if _should_clear_weather_overlay(node):
if _should_ignore_weather_overlay(node):
_clear_weather_overlay_from_node(node)
return
if node is GeometryInstance3D:
if _geometry_uses_alpha_texture(node):
if _should_clear_weather_overlay(node) or _geometry_uses_alpha_texture(node):
node.material_overlay = null
else:
node.material_overlay = material
@@ -214,6 +229,9 @@ func _shader_material_uses_alpha_texture(material: ShaderMaterial) -> bool:
return material.shader.code.find("alpha_texture") != -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:
return node.is_in_group("weather_vegetables_node")

View File

@@ -53,7 +53,7 @@ var is_storm: bool = false
var cold_tween: Tween
var wind_tween: Tween
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_strength: float = 0.0
var current_wind_fade: float = 0.0
@@ -128,9 +128,7 @@ func _process(delta: float) -> void:
_follow_camera()
var is_night = day_time >= 2.5
if particles_fireflies:
particles_fireflies.emitting = thereare_fireflies and is_night and not is_raining and not is_snowing and not is_storm
_update_fireflies_visibility()
var base_tint: Color
var base_sky_top: Color
@@ -197,11 +195,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_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 rain_weather_amount: float = 0.0
if is_raining:
rain_weather_amount = clamp(rain_intensity, 0.0, 1.0)
var rain_weather_amount: float = clamp(rain_intensity, 0.0, 1.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:
storm_weather_amount = clamp((rain_intensity - 1.0) / (environment_config.storm_rain_intensity_multiplier - 1.0), 0.0, 1.0)
else:
@@ -389,9 +385,7 @@ func _follow_camera() -> void:
func toggle_fireflies(value: bool):
thereare_fireflies = value
var is_night = day_time >= 2.5
if particles_fireflies:
particles_fireflies.emitting = thereare_fireflies and is_night and not is_raining
_update_fireflies_visibility()
#disable fireflies and set default values and materials
func init_fireflies():
@@ -402,6 +396,15 @@ func init_fireflies():
var proc_mat = particles_fireflies.process_material as ParticleProcessMaterial
if proc_mat:
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
@@ -410,7 +413,7 @@ func init_fireflies():
func toggle_wind(value: bool):
is_windy = value
if particles_wind:
particles_wind.emitting = is_windy
particles_wind.emitting = true
_apply_wind_state()
_emit_weather_event_label()
@@ -420,7 +423,7 @@ func init_wind():
_update_wind_amount_from_strength(environment_config.wind_strength)
if particles_wind:
particles_wind.visible = true
particles_wind.emitting = false
particles_wind.emitting = true
particles_wind.amount = environment_config.wind_amount
var proc_mat_wind = particles_wind.process_material as ParticleProcessMaterial
if proc_mat_wind:
@@ -451,6 +454,11 @@ func _apply_wind_config() -> void:
_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:
if environment_config == null:
return
@@ -458,20 +466,23 @@ func _apply_wind_state(immediate: bool = false) -> void:
if wind_tween and wind_tween.is_valid():
wind_tween.kill()
var active_wind_speed := environment_config.wind_speed if is_windy else 0.0
var active_wind_strength := environment_config.wind_strength if is_windy else 0.0
var active_wind_fade := 1.0 if is_windy else 0.0
if particles_wind:
particles_wind.emitting = true
var active_wind_speed := environment_config.wind_speed
var active_wind_strength := _get_target_wind_strength()
var active_wind_fade := 1.0
if immediate:
_set_current_wind_speed(active_wind_speed)
_set_current_wind_strength(active_wind_strength)
_set_current_wind_fade(active_wind_fade)
return
_set_current_wind_speed(environment_config.wind_speed)
_set_current_wind_strength(environment_config.wind_strength)
_set_current_wind_speed(active_wind_speed)
_set_current_wind_fade(active_wind_fade)
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_callback(_finish_wind_fade_out)
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_transition)
func _set_current_wind_speed(value: float) -> void:
current_wind_speed = value
@@ -481,6 +492,7 @@ func _set_current_wind_speed(value: float) -> void:
func _set_current_wind_strength(value: float) -> void:
current_wind_strength = value
RenderingServer.global_shader_parameter_set("global_wind_strength", value)
_set_wind_particles_amount_from_strength(value)
_sync_wind_event_state()
func _set_current_wind_fade(value: float) -> void:
@@ -494,13 +506,10 @@ func _sync_wind_event_state() -> void:
#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)
func _finish_wind_fade_out() -> void:
if is_windy:
return
_set_current_wind_speed(0.0)
_set_current_wind_strength(0.0)
_set_current_wind_fade(0.0)
func _finish_wind_transition() -> void:
_set_current_wind_speed(environment_config.wind_speed)
_set_current_wind_strength(_get_target_wind_strength())
_set_current_wind_fade(1.0)
func change_wind_strength(value: float) -> void:
if environment_config == null:
@@ -508,10 +517,11 @@ func change_wind_strength(value: float) -> void:
environment_config.wind_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)
if particles_wind:
particles_wind.amount = environment_config.wind_amount
_apply_wind_state()
func _update_wind_amount_from_strength(value: float) -> void:
if environment_config == null:
@@ -519,12 +529,22 @@ func _update_wind_amount_from_strength(value: float) -> void:
if max_wind_amount <= 0:
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 amount_ratio: float = normalized_strength
if normalized_strength > 0.0 and 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:
if random_weather_restore_tween and random_weather_restore_tween.is_valid():

View File

@@ -17,6 +17,7 @@ global uniform float global_snow_cap_flatness_end = 0.96;
global uniform float global_snow_cap_noise_scale = 0.22;
global uniform float global_snow_cap_noise_strength = 0.18;
const float SNOW_VISUAL_RESPONSE = 0.55;
uniform bool snow_noise_enabled = true;
uniform float snow_noise_scale : hint_range(0.01, 1.0) = 0.15;
uniform float snow_edge_softness : hint_range(0.01, 0.5) = 0.2;
uniform float snow_roughness_variation : hint_range(0.0, 0.3) = 0.15;
@@ -92,6 +93,10 @@ float get_visual_snow_progress(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 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);
@@ -118,7 +123,7 @@ void vertex() {
world_normal.y
);
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);
v_snow_amount = snow_progress;
@@ -133,7 +138,7 @@ void fragment() {
float facing_up = clamp(world_normal.y, 0.0, 1.0);
vec3 geometric_normal = normalize(cross(dFdx(world_pos), dFdy(world_pos)));
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
float snow_progress = v_snow_amount;

View File

@@ -158,6 +158,8 @@ extends Resource
@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
@export var train_start_delay_seconds: float = 3.0 #Seconds the train waits before starting at game launch
@export var train_start_acceleration_seconds: float = 4.0 #Seconds used to progressively reach normal train speed after launch
#Snow settings
@export_group("Snow")
@@ -185,7 +187,8 @@ extends Resource
@export var wind_direction: Vector2 = Vector2(0.5, 0.4) #Global wind direction (XZ)
@export var wind_speed: float = 0.2 #Global wind animation speed
@export var wind_scale: float = 0.025 #Global wind noise scale
@export var wind_strength: float = 0.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_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

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -19,6 +19,8 @@ func setup() -> void:
_add_collectible(collectible)
if unlocked_collectible_ids.has(collectible.id):
_unlock_collectible(collectible.id)
if is_inside_tree():
await get_tree().process_frame
func on_collectible_unlocked(collectible_id: StringName) -> void:
_unlock_collectible(collectible_id)

View File

@@ -1,8 +1,6 @@
extends Control
@onready var tabs: Array[GameMenuTab] = [$%Tab1, $%Tab2, $%Tab3]
@onready var pages: Array[GameMenuPage] = [$%Page1, $%Page2, $%Page3]
var current_index = 0
func _ready() -> void:
for tab: GameMenuTab in tabs:
@@ -10,9 +8,11 @@ func _ready() -> void:
on_tab_pressed(0, true)
func on_tab_pressed(index: int, disable_animation: bool = false) -> void:
for page in pages:
page.hide_page()
if BookAPI.is_busy() and not disable_animation:
return
for tab in tabs:
if tab.index == index:
@@ -26,8 +26,4 @@ func on_tab_pressed(index: int, disable_animation: bool = false) -> void:
else:
tab.deselect()
pages[index].show_page()
current_index = index
func open() -> void:
pages[current_index].show_page()
BookAPI.go_to_page((index + 1) * 2)

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@ extends Control
@onready var photo: Photo = $%Photo
@onready var panel: Panel = $%Panel
var is_closing: bool = false
func _ready() -> void:
GameState.on_photo_highlighted.connect(_on_photo_highlighted)
@@ -15,10 +15,13 @@ func _on_photo_highlighted(texture: Texture) -> void:
TweenFX.fold_in(photo)
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:
return
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:

View File

@@ -45,6 +45,4 @@ theme_override_constants/margin_top = 30
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 185
[connection signal="gui_input" from="Panel" to="." method="_on_panel_gui_input"]
[editable path="Photo"]

View File

@@ -1,20 +1,48 @@
extends Control
const TRAIN_WHISTLE_STREAM: AudioStream = preload("res://tgcc/train/sounds/train_whistle2.mp3")
@onready var play_button: Button = $%PlayButton
@onready var resume_button: Button = $%ResumeButton
@onready var settings_button: Button = $%SettingsButton
@onready var save_button: Button = $%SaveButton
@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:
_create_fallback_whistle_player()
play_button.pressed.connect(_on_play_button_pressed)
save_button.pressed.connect(_on_save_button_pressed)
quit_button.pressed.connect(_on_quit_button_pressed)
resume_button.pressed.connect(func(): UIEvents.resume_game_requested.emit())
func _on_play_button_pressed() -> void:
_play_train_horn()
await get_tree().create_timer(fade_time).timeout
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:
GameState.save_game()

View File

@@ -2,8 +2,6 @@
[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="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")]
anchors_preset = 15
@@ -16,6 +14,7 @@ grow_vertical = 2
mouse_filter = 2
[node name="Background" parent="." index="0" unique_id=1203110384]
visible = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@@ -36,17 +35,3 @@ pivot_offset_ratio = Vector2(0.5, 0.5)
mouse_filter = 2
texture = ExtResource("2_3i50p")
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

View 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

View 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

View File

@@ -1,13 +1,7 @@
[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="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://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")]
anchors_preset = 15
@@ -31,49 +25,4 @@ mouse_filter = 2
texture = ExtResource("2_xqdsh")
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

View 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

View 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

View File

@@ -2,9 +2,7 @@
[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="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="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")]
anchors_preset = 15
@@ -37,33 +35,4 @@ anchor_bottom = 0.7740741
texture = ExtResource("2_ckqcb")
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"]

View 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

View 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"]

View 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()

View File

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

View File

@@ -17,6 +17,8 @@ func load_gallery() -> void:
for file_path in saved_photos:
if FileAccess.file_exists(file_path):
_create_photo_thumbnail(file_path)
if is_inside_tree():
await get_tree().process_frame
func _create_photo_thumbnail(file_path: String) -> void:
var image = Image.load_from_file(file_path)

View File

@@ -9,10 +9,10 @@ func _ready() -> void:
deselect()
disable()
func set_option_text(text: String) -> void:
func set_option_text(_text: String) -> void:
if label == null:
label = $%Label
label.text = text
label.text = _text
func select() -> void:
if checkbox == null:

View File

@@ -22,6 +22,7 @@ signal on_photo_taken_finished
signal on_photo_mode_black_screen_disappeared
const SAVE_PATH: String = "user://savegame.json"
const SAVE_PASSWORD: String = "jmpgames_megapwd"
var save_data: SaveGameData = SaveGameData.new()
var is_loaded: bool = false
@@ -39,7 +40,7 @@ func apply_video_settings() -> void:
get_viewport().use_taa = save_data.anti_aliasing
var msaa_mode = Viewport.MSAA_2X if save_data.anti_aliasing else Viewport.MSAA_DISABLED
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():
@@ -64,34 +65,69 @@ func apply_train_colors() -> void:
DisplayServer.window_set_size(Vector2i(save_data.resolution_x, save_data.resolution_y))
# Optional: center the window if resized
var screen_center = DisplayServer.screen_get_position() + DisplayServer.screen_get_size() / 2
var window_size = DisplayServer.window_get_size()
DisplayServer.window_set_position(screen_center - window_size / 2)
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)
var screen_position: Vector2i = DisplayServer.screen_get_position()
var screen_size: Vector2i = DisplayServer.screen_get_size()
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)
#encrypted file + binary serialization (standard)
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)
var file := FileAccess.open_encrypted_with_pass(SAVE_PATH, FileAccess.READ, SAVE_PASSWORD)
save_data = SaveGameData.new()
if parse_result == OK and json.data is Dictionary:
save_data = SaveGameData.from_dictionary(json.data)
if file != null:
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
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:
get_tree().paused = true

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 KiB

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://clmbvd7ilhra2"
path="res://.godot/imported/button_choochoo_on.png-e06955044ec386fd4f20a50236119841.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://core/main_scene_ui/assets/button_choochoo_on.png"
dest_files=["res://.godot/imported/button_choochoo_on.png-e06955044ec386fd4f20a50236119841.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

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 KiB

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://qq84r14df2ix"
path="res://.godot/imported/button_visibility_on.png-32e35f3ae7b88f431f03c78dc0b29e6c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://core/main_scene_ui/assets/button_visibility_on.png"
dest_files=["res://.godot/imported/button_visibility_on.png-32e35f3ae7b88f431f03c78dc0b29e6c.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

View File

@@ -1,6 +1,5 @@
extends Control
@onready var game_menu: Control = $%GameMenu
@onready var game_menu_panel: Control = $%GameMenuPanel
@onready var menu_icon_button: Button = $%MenuIconButton
@@ -9,7 +8,6 @@ extends Control
@onready var photo_icon_button: Button = $%PhotoIconButton
@onready var choo_choo_button: Button = $%ChooChooButton
@onready var collectible_icon_button: Button = $%CollectibleIconButton
@onready var resume_button: Button = $GameMenuPanel/GameMenu/Pages/Page3/OptionMenu/VBoxContainer/ResumeButton
@onready var weather_row: HBoxContainer = $%WeatherRow
@onready var time_of_day_row: HBoxContainer = $%TimeOfDayRow
@onready var photo_mode_texture_panel: Panel = $%PhotoModeTexturePanel
@@ -41,12 +39,14 @@ func _ready() -> void:
menu_icon_button.pressed.connect(_open_game_menu_on_page.bind(2))
photo_icon_button.pressed.connect(_on_photo_icon_button_pressed)
choo_choo_button.pressed.connect(_on_choo_choo_button_pressed)
resume_button.pressed.connect(_on_resume_button_pressed)
UIEvents.resume_game_requested.connect(_on_resume_button_pressed)
func _process(delta: float) -> void:
func _process(_delta: float) -> void:
if time_label and dist_label and GameState.is_loaded:
var time = GameState.save_data.total_time_played_seconds
@warning_ignore("integer_division")
var hours = int(time) / 3600
@warning_ignore("integer_division")
var minutes = (int(time) % 3600) / 60
var seconds = int(time) % 60
time_label.text = "Travel Time: %02d:%02d:%02d" % [hours, minutes, seconds]
@@ -113,6 +113,8 @@ func _on_weather_row_on_option_changed(data: String) -> void:
_:
pass
AchievementManager.is_unlocked("ACH_CHANGE_METEO")
func pick_random_weather() -> void:
var valid_buttons = []
for child in weather_row.options_icon_buttons:
@@ -189,10 +191,12 @@ func _on_photo_taken_finished() -> void:
await tween.finished
photo_mode_black_screen.hide()
GameState.on_photo_mode_black_screen_disappeared.emit()
StatsManager.add_int("stat_photo_number", 1)
StatsManager.store()
func _on_choo_choo_button_pressed() -> void:
#play sfx
pass
UIEvents.toot_toot.emit(true)
AchievementManager.is_unlocked("ACH_CHANGE_TRAIN_COLOR")
func _set_buttons_mouse_filter(ignore: bool) -> void:
var filter = Control.MOUSE_FILTER_IGNORE if ignore else Control.MOUSE_FILTER_STOP

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
## Updates total elapsed time and train distance labels.
class_name TotalStatsDisplay
extends Node
const RAILS_SCRIPT = preload("res://core/biome_generator/rails.gd")
const SECONDS_PER_MINUTE: int = 60
const MINUTES_PER_HOUR: int = 60
const HOURS_PER_DAY: int = 24
#DISTANCE_KM_PER_UNIT is taken from rails.gd constant
@export var time_label_path: NodePath
@export var distance_label_path: NodePath
@export var rail_path: NodePath
var _elapsed_seconds: float = 0.0
var _total_distance_km: float = 0.0
var _last_train_progress: float = -1.0
@onready var _time_label: Label = get_node_or_null(time_label_path) as Label
@onready var _distance_label: Label = get_node_or_null(distance_label_path) as Label
@onready var _rail_path: Path3D = get_node_or_null(rail_path) as Path3D
func _ready() -> void:
_update_time_label()
_update_distance_label()
func _process(delta: float) -> void:
_elapsed_seconds += delta
_update_total_distance()
_update_time_label()
_update_distance_label()
func _update_total_distance() -> void:
if _rail_path == null or _rail_path.curve == null:
return
var current_train_progress: float = float(_rail_path.get("train_progress"))
if _last_train_progress < 0.0:
_last_train_progress = current_train_progress
return
var total_length: float = _rail_path.curve.get_baked_length()
if total_length <= 0.0:
return
var progress_delta: float = absf(current_train_progress - _last_train_progress)
var wrapped_progress_delta: float = minf(progress_delta, total_length - progress_delta)
_total_distance_km += wrapped_progress_delta * RAILS_SCRIPT.DISTANCE_KM_PER_UNIT
_last_train_progress = current_train_progress
func _update_time_label() -> void:
if _time_label == null:
return
var total_minutes: int = int(floor(_elapsed_seconds / SECONDS_PER_MINUTE))
var days: int = floori(float(total_minutes) / float(MINUTES_PER_HOUR * HOURS_PER_DAY))
var hours: int = floori(float(total_minutes) / float(MINUTES_PER_HOUR)) % HOURS_PER_DAY
var minutes: int = total_minutes % MINUTES_PER_HOUR
_time_label.text = "Time %dd %02dh %02dm" % [days, hours, minutes]
func _update_distance_label() -> void:
if _distance_label == null:
return
_distance_label.text = "Distance %.1f km" % _total_distance_km

View File

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

View File

@@ -1,6 +1,6 @@
class_name SceneConfig
extends Resource
enum SceneName { MAIN_MENU, GAME }
enum SceneName { MAIN_MENU, GAME, SPLASH_SCREEN }
@export var scenes: Dictionary[SceneName, PackedScene]

View File

@@ -3,11 +3,13 @@
[ext_resource type="PackedScene" uid="uid://btcpge7cj2041" path="res://core/main_menu/main_menu.tscn" id="1_72aup"]
[ext_resource type="Script" uid="uid://bbgyhmb8a17i7" path="res://core/scene_manager/scene_config.gd" id="1_km45g"]
[ext_resource type="PackedScene" uid="uid://vjf4bdxd8saj" path="res://tgcc/main_scene.tscn" id="2_prsyo"]
[ext_resource type="PackedScene" uid="uid://ri5kxx4mipo" path="res://core/splash_screen/splash_screen.tscn" id="3_aasbo"]
[resource]
script = ExtResource("1_km45g")
scenes = Dictionary[int, PackedScene]({
0: ExtResource("1_72aup"),
1: ExtResource("2_prsyo")
1: ExtResource("2_prsyo"),
2: ExtResource("3_aasbo")
})
metadata/_custom_type_script = "uid://bbgyhmb8a17i7"

View File

@@ -3,42 +3,119 @@ extends CanvasLayer
@export var config: SceneConfig
@onready var color_rect = $ColorRect
@onready var loading_screen = $LoadingScreen
@export var transition_duration: float = 1
@export var transition_duration: float = 3
const SHADER_PATH := "res://core/transition.gdshader"
var transaction_material: ShaderMaterial
func _ready() -> void:
color_rect.color.a = 0.0
color_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
loading_screen.hide()
_build_material()
_update_resolution()
get_viewport().size_changed.connect(_update_resolution)
_set_factor(0.0)
color_rect.visible = false
func change_scene(scene_enum: SceneConfig.SceneName) -> void:
func _build_material() -> void:
transaction_material = ShaderMaterial.new()
transaction_material.shader = load(SHADER_PATH)
#directional gradient: bottom-left-> black; top-right -> white
var g := Gradient.new()
g.set_color(0, Color.BLACK)
g.set_color(1, Color.WHITE)
var grad_tex := GradientTexture2D.new()
grad_tex.gradient = g
grad_tex.fill = GradientTexture2D.FILL_LINEAR
grad_tex.fill_from = Vector2(0.0, 1.0) # bottom-left
grad_tex.fill_to = Vector2(1.0, 0.0) # top-right
grad_tex.width = 256
grad_tex.height = 256
#shape: sphere (white at the center, black around)
var sg := Gradient.new()
sg.set_color(0, Color.WHITE)
sg.set_color(1, Color.BLACK)
var shape_tex := GradientTexture2D.new()
shape_tex.gradient = sg
shape_tex.fill = GradientTexture2D.FILL_RADIAL
shape_tex.fill_from = Vector2(0.5, 0.5) # center
shape_tex.fill_to = Vector2(0.5, 0.0) # ray
shape_tex.width = 64
shape_tex.height = 64
transaction_material.set_shader_parameter("base_color", Color.BLACK)
transaction_material.set_shader_parameter("gradient_texture", grad_tex)
transaction_material.set_shader_parameter("gradient_fixed", false)
transaction_material.set_shader_parameter("shape_texture", shape_tex)
transaction_material.set_shader_parameter("shape_tiling", 16.0)
transaction_material.set_shader_parameter("shape_rotation", 0.0)
transaction_material.set_shader_parameter("shape_scroll", Vector2.ZERO)
transaction_material.set_shader_parameter("shape_feathering", 0.05)
transaction_material.set_shader_parameter("shape_treshold", 1.0) # >=1 -> full at the end of the transation
transaction_material.set_shader_parameter("width", 0.5)
color_rect.material = transaction_material
func _update_resolution() -> void:
var s := get_viewport().get_visible_rect().size
if color_rect.material:
color_rect.material.set_shader_parameter("node_resolution", s)
func _set_factor(v: float) -> void:
if color_rect.material:
color_rect.material.set_shader_parameter("factor", v)
func cover(duration := 0.5) -> void:
color_rect.visible = true
var t := create_tween()
t.tween_method(_set_factor, 0.0, 1.0, duration)
await t.finished
func reveal(duration := 0.5) -> void:
var t := create_tween()
t.tween_method(_set_factor, 1.0, 0.0, duration)
await t.finished
color_rect.visible = false
func change_scene_with_standard_fade(scene_enum: SceneConfig.SceneName, duration: float = transition_duration, show_loading_label: bool = false) -> void:
if not config or not config.scenes.has(scene_enum):
return
color_rect.material = null
color_rect.color = Color(0,0,0)
var target_scene: PackedScene = config.scenes[scene_enum]
color_rect.mouse_filter = Control.MOUSE_FILTER_STOP
if show_loading_label:
loading_screen.visible = true
var tween_in = create_tween()
tween_in.tween_property(color_rect, "color:a", 1.0, transition_duration)
await tween_in.finished
loading_screen.show()
await get_tree().process_frame
await cover(duration)
get_tree().change_scene_to_packed(target_scene)
await reveal(duration)
loading_screen.hide()
if show_loading_label:
loading_screen.visible = false
color_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
var tween_out = create_tween()
tween_out.tween_property(color_rect, "color:a", 0.0, transition_duration)
await tween_out.finished
func change_scene(scene_enum: SceneConfig.SceneName, duration: float = transition_duration, show_loading_label: bool = false) -> void:
if not config or not config.scenes.has(scene_enum):
return
color_rect.material = transaction_material
color_rect.color = Color(0,0,0,0)
var target_scene: PackedScene = config.scenes[scene_enum]
color_rect.mouse_filter = Control.MOUSE_FILTER_STOP
if show_loading_label:
loading_screen.visible = true
await cover(duration)
get_tree().change_scene_to_packed(target_scene)
await reveal(duration)
if show_loading_label:
loading_screen.visible = false
color_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
func quit_game() -> void:
color_rect.mouse_filter = Control.MOUSE_FILTER_STOP
var tween = create_tween()
tween.tween_property(color_rect, "color:a", 1.0, transition_duration)
await tween.finished
get_tree().quit()

View File

@@ -20,6 +20,7 @@ mouse_filter = 2
color = Color(0, 0, 0, 0)
[node name="LoadingScreen" type="Control" parent="." unique_id=1075176512]
visible = false
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dp3qxncy8u184"
path="res://.godot/imported/jmp_logo.png-c6094efb2c4c429c25ff79b5a4cee21c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://core/splash_screen/jmp_logo.png"
dest_files=["res://.godot/imported/jmp_logo.png-c6094efb2c4c429c25ff79b5a4cee21c.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

View File

@@ -0,0 +1,17 @@
extends Control
@export var main_menu_scene: PackedScene
@export var display_time: float = 2.0
@export var fade_in_time: float = 0.5
@onready var logo: Sprite2D = $ColorRect/JMPLogo
func _ready() -> void:
logo.modulate.a = 0.0
await get_tree().create_timer(fade_in_time).timeout
var tween := create_tween()
tween.tween_property(logo, "modulate:a", 1.0, fade_in_time).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
await get_tree().create_timer(display_time).timeout
#tween = create_tween()
#tween.tween_property(logo, "modulate:a", 0.0, fade_in_time).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
SceneManager.change_scene_with_standard_fade(SceneConfig.SceneName.MAIN_MENU, 0.5)

View File

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

View File

@@ -0,0 +1,27 @@
[gd_scene format=3 uid="uid://ri5kxx4mipo"]
[ext_resource type="Script" uid="uid://ckj2pcoxi5j3" path="res://core/splash_screen/splash_screen.gd" id="1_elsnp"]
[ext_resource type="Texture2D" uid="uid://dp3qxncy8u184" path="res://core/splash_screen/jmp_logo.png" id="1_jkpgl"]
[node name="Control" type="Control" unique_id=163470013]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_elsnp")
[node name="ColorRect" type="ColorRect" parent="." unique_id=1599828641]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0, 0, 0, 1)
[node name="JMPLogo" type="Sprite2D" parent="ColorRect" unique_id=1244888940]
position = Vector2(980.99994, 556)
scale = Vector2(0.37799045, 0.37799042)
texture = ExtResource("1_jkpgl")

View File

@@ -4,23 +4,22 @@ extends Node
#if you have an error setting achievements or stats in debug mode remember to open your steam client
#with jmp games account or another account who bought this game. If error persist try to clos and reopen steam client.
const KNOWN := [
"ACH_DISTANCE_100",
"ACH_DISTANCE_200",
"ACH_10_PHOTOS",
"ACH_CHANGE_METEO",
"ACH_CHANGE_TRAIN_COLOR",
]
#how it works:
#func _on_player_won() -> void:
# Achievements.unlock("ACH_DISTANCE_100") #unlock the achievement at first win
# AchievementManager.unlock("ACH_DISTANCE_100") #unlock the achievement at first win
#func _on_match_finished(coins_collected: int) -> void:
#update stats
#...
#check thresholds and unlock achievements
# if Stats.get_int("stat_distance_km") >= 100:
# Achievements.unlock("ACH_DISTANCE_100")
# if Stats.get_int("stat_distance_km") >= 200:
# Achievements.unlock("ACH_DISTANCE_200")
# if StatsManager.get_int("stat_distance_km") >= 100:
# AchievementManager.unlock("ACH_DISTANCE_100")
# if StatsManager.get_int("stat_distance_km") >= 200:
# AchievementManager.unlock("ACH_DISTANCE_200")
signal unlocked(api_name: String)
@@ -36,6 +35,8 @@ func _on_global_percentages_ready(_game: int, _result: int) -> void:
pass
func unlock(api_name: String) -> void:
if not SteamManager.is_on_steam:
return
if not KNOWN.has(api_name):
push_warning("Achievement unknown: %s" % api_name)
return
@@ -48,17 +49,25 @@ func unlock(api_name: String) -> void:
unlocked.emit(api_name)
func is_unlocked(api_name: String) -> bool:
if not SteamManager.is_on_steam:
return false
var data: Dictionary = Steam.getAchievement(api_name)
return data.get("achieved", false)
func get_display_name(api_name: String) -> String:
if not SteamManager.is_on_steam:
return ""
return Steam.getAchievementDisplayAttribute(api_name, "name")
func get_description(api_name: String) -> String:
if not SteamManager.is_on_steam:
return ""
return Steam.getAchievementDisplayAttribute(api_name, "desc")
#Global percentage of player who have unlocked the achievement
func get_global_percent(api_name: String) -> float:
if not SteamManager.is_on_steam:
return 0.0
var data: Dictionary = Steam.getAchievementAchievedPercent(api_name)
if not data.get("ret", false):
return -1.0 #global percentage not yet available
@@ -66,9 +75,10 @@ func get_global_percent(api_name: String) -> float:
#for development only: reset an achievement
func clear(api_name: String) -> void:
if not SteamManager.is_on_steam:
return
Steam.clearAchievement(api_name)
Steam.storeStats()
func _on_achievement_stored(_game: int, _group: bool, api_name: String,
current: int, maximum: int) -> void:
func _on_achievement_stored(_game: int, _group: bool, api_name: String, current: int, maximum: int) -> void:
print("Achievement confirmed by server: %s (%d/%d)" % [api_name, current, maximum])

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