Compare commits
17 Commits
24ffdaba5e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2358dc8b53 | |||
| 185b1b4744 | |||
| e86da0c2bc | |||
| 5341fdd3f5 | |||
|
|
d58c31ac65 | ||
|
|
48ba1761e0 | ||
|
|
0ce2cc6b04 | ||
| 8ff14f3455 | |||
| 6da7021a58 | |||
| b87be91521 | |||
|
|
9f3acbccf9 | ||
| 4aaf8e3fd3 | |||
| dc76181cc0 | |||
| 0f7bb6308a | |||
| a944492dbd | |||
| 15f38c096b | |||
|
|
cc7f819628 |
Binary file not shown.
@@ -87,6 +87,8 @@ const RIVER_NEIGHBOUR_OFFSETS: Dictionary = {
|
||||
@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 = 35.0
|
||||
##minimum horizontal distance between generated wires and the railway centerline
|
||||
@export var lamppost_rail_clearance_distance: float = 4.0
|
||||
##if true wire connections blocked by colliders are discarded
|
||||
@export var lamppost_obstacle_check_enabled: bool = true
|
||||
|
||||
@@ -210,6 +212,17 @@ func _physics_process(_delta: float) -> void:
|
||||
last_pos_train = current_pos
|
||||
_refresh_world(current_pos)
|
||||
|
||||
func generate_initial_world_now() -> void:
|
||||
if rail_path == null or rail_path.train_instance == null:
|
||||
return
|
||||
|
||||
var train_pos = rail_path.train_instance.global_position
|
||||
var current_pos = Vector2i(roundi(train_pos.x / chunk_size), roundi(train_pos.z / chunk_size))
|
||||
last_pos_train = current_pos
|
||||
_rebuild_generation_queue(current_pos)
|
||||
_drain_generation_queue_now()
|
||||
_drain_wire_queue_now()
|
||||
|
||||
func collect_all_chunkinfo(root: Node, list: Array[Node]) -> void:
|
||||
if root == null: return
|
||||
|
||||
@@ -533,6 +546,20 @@ func _drain_generation_queue() -> void:
|
||||
pending_generation_cells.clear()
|
||||
pending_generation_cursor = 0
|
||||
|
||||
func _drain_generation_queue_now() -> void:
|
||||
while pending_generation_cursor < pending_generation_cells.size():
|
||||
var grid_pos: Vector2i = pending_generation_cells[pending_generation_cursor]
|
||||
pending_generation_cursor += 1
|
||||
if board.has(grid_pos):
|
||||
continue
|
||||
|
||||
var is_obstacle = _register_cell_with_ray(grid_pos)
|
||||
if not is_obstacle:
|
||||
_add_compatible_biome(grid_pos)
|
||||
|
||||
pending_generation_cells.clear()
|
||||
pending_generation_cursor = 0
|
||||
|
||||
func _drain_cleanup_queue() -> void:
|
||||
var processed: int = 0
|
||||
var start_usec: int = Time.get_ticks_usec()
|
||||
@@ -593,6 +620,22 @@ func _drain_wire_queue() -> void:
|
||||
pending_wire_cells.clear()
|
||||
pending_wire_cursor = 0
|
||||
|
||||
func _drain_wire_queue_now() -> void:
|
||||
while pending_wire_cursor < pending_wire_cells.size():
|
||||
var grid_pos: Vector2i = pending_wire_cells[pending_wire_cursor]
|
||||
pending_wire_cursor += 1
|
||||
pending_wire_lookup.erase(grid_pos)
|
||||
|
||||
if not board.has(grid_pos):
|
||||
continue
|
||||
if not board[grid_pos].get("have_lamppost", false):
|
||||
continue
|
||||
|
||||
_connect_lamppost_wires(grid_pos)
|
||||
|
||||
pending_wire_cells.clear()
|
||||
pending_wire_cursor = 0
|
||||
|
||||
func _generate_pieces_around_train(center: Vector2i) -> void:
|
||||
for x in range(-eye_line, eye_line + 1):
|
||||
for z in range(-eye_line, eye_line + 1):
|
||||
@@ -1423,6 +1466,25 @@ func _get_wire_sample_point(p1: Vector3, p2: Vector3, t: float) -> Vector3:
|
||||
point.y -= gravity
|
||||
return point
|
||||
|
||||
func _is_wire_too_close_to_rail(p1: Vector3, p2: Vector3) -> bool:
|
||||
if rail_path == null or rail_path.curve == null:
|
||||
return false
|
||||
if lamppost_rail_clearance_distance <= 0.0:
|
||||
return false
|
||||
|
||||
var clearance_sq: float = lamppost_rail_clearance_distance * lamppost_rail_clearance_distance
|
||||
var samples: int = 10
|
||||
for i in range(samples + 1):
|
||||
var t: float = float(i) / float(samples)
|
||||
var wire_point: Vector3 = _get_wire_sample_point(p1, p2, t)
|
||||
var rail_local_point: Vector3 = rail_path.to_local(wire_point)
|
||||
var closest_rail_point: Vector3 = rail_path.to_global(rail_path.curve.get_closest_point(rail_local_point))
|
||||
var horizontal_delta := Vector2(wire_point.x - closest_rail_point.x, wire_point.z - closest_rail_point.z)
|
||||
if horizontal_delta.length_squared() < clearance_sq:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
func _collect_wire_excluded_rids(root: Node, excluded_rids: Array[RID]) -> void:
|
||||
if root == null:
|
||||
return
|
||||
@@ -1441,6 +1503,9 @@ func _get_wire_excluded_rids(excluded_roots: Array) -> Array[RID]:
|
||||
return excluded_rids
|
||||
|
||||
func _is_wire_path_blocked(p1: Vector3, p2: Vector3, excluded_roots: Array = []) -> bool:
|
||||
if _is_wire_too_close_to_rail(p1, p2):
|
||||
return true
|
||||
|
||||
if not lamppost_obstacle_check_enabled:
|
||||
return false
|
||||
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
extends Path3D
|
||||
|
||||
const STEAM_DISTANCE_STAT: String = "stat_distance_km"
|
||||
const DISTANCE_KM_PER_UNIT: float = 0.01
|
||||
const STATION_STOP_GROUP: StringName = &"railway_station"
|
||||
const TRACK_ZERO_SPACING_FIX_DISTANCE: float = 45.0
|
||||
const TRACK_ZERO_SPACING_BLEND_DISTANCE: float = 18.0
|
||||
const TRACK_ZERO_SPACING_SEARCH_EXTRA: float = 35.0
|
||||
const TRACK_ZERO_SPACING_SEARCH_STEP: float = 0.5
|
||||
const TRACK_ZERO_SPACING_TARGET_RATIO: float = 0.97
|
||||
const TRACK_ZERO_SPACING_MIN_DISTANCE: float = 1.0
|
||||
|
||||
enum TrainStartMode { RANDOM_POSITION, SPECIFIED_STATION, SPECIFIED_POSITION }
|
||||
enum TrainFacingDirection { KEEP_PATH_DIRECTION, LOCOMOTIVE_LEFT, LOCOMOTIVE_RIGHT }
|
||||
|
||||
@export_group("Train")
|
||||
##train speed
|
||||
@@ -32,11 +40,24 @@ const STATION_STOP_GROUP: StringName = &"railway_station"
|
||||
##swing of the train during the ran
|
||||
@export_range(0.0, 1.0) var basic_swing: float = 0.1
|
||||
|
||||
@export_group("Train Start")
|
||||
@export_enum("random_position", "specified_station", "specified_position") var train_start_mode: int = TrainStartMode.RANDOM_POSITION
|
||||
@export_range(0, 64, 1, "or_greater") var train_start_stop_index: int = 1
|
||||
@export var train_start_position: float = 0.0
|
||||
@export var train_start_delay_seconds: float = 0.0
|
||||
@export var train_start_after_delay: bool = true
|
||||
@export var train_start_acceleration_seconds: float = 4.0
|
||||
@export_enum("keep_path_direction", "locomotive_left", "locomotive_right") var train_facing_direction: int = TrainFacingDirection.KEEP_PATH_DIRECTION
|
||||
|
||||
@export_group("Rails Bulding")
|
||||
##if true builds visible rail pieces along the Path3D
|
||||
@export var build_track_visuals: bool = true
|
||||
##distance between sleepers of the rails
|
||||
@export var sleepers_distance: float = 1.0
|
||||
##nodel for the sleeper of the rail
|
||||
@export var sleepers_model: PackedScene
|
||||
##if true add procedural rail bars even when sleepers_model is assigned
|
||||
@export var add_procedural_rails_to_sleepers_model: bool = false
|
||||
|
||||
@export_group("Manual Controls")
|
||||
##max speed
|
||||
@@ -84,23 +105,22 @@ var next_stop_index: int = 0
|
||||
var stop_multiply: float = 1.0
|
||||
var is_restarting: bool = false
|
||||
var tween_restart: Tween
|
||||
var environment_config: EnvironmentConfig
|
||||
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
|
||||
var _is_photo_mode_active: bool = false
|
||||
var _train_direction_sign: float = 1.0
|
||||
|
||||
func _ready() -> void:
|
||||
randomize()
|
||||
GameState.on_enable_photo_mode_request.connect(func(): _is_photo_mode_active = true)
|
||||
GameState.on_disable_photo_mode_request.connect(func(): _is_photo_mode_active = false)
|
||||
|
||||
_cache_environment_config()
|
||||
_initial_is_inmotion = is_inmotion
|
||||
|
||||
if curve != null and curve.get_baked_length() > 0:
|
||||
build_rails()
|
||||
if build_track_visuals:
|
||||
build_rails()
|
||||
build_train()
|
||||
_plan_stops()
|
||||
_apply_initial_train_start()
|
||||
@@ -108,24 +128,6 @@ func _ready() -> void:
|
||||
else:
|
||||
print("WARNING: Draw Path3D for rails!")
|
||||
|
||||
func _cache_environment_config() -> void:
|
||||
var parent_node: Node = get_parent()
|
||||
if parent_node != null:
|
||||
var day_night_node := parent_node.get_node_or_null("DayNight") as EnvironmentManagerRoot
|
||||
if day_night_node != null:
|
||||
environment_config = day_night_node.environment_config
|
||||
return
|
||||
|
||||
var current_scene: Node = get_tree().current_scene
|
||||
if current_scene == null:
|
||||
return
|
||||
|
||||
for child in current_scene.get_children():
|
||||
var environment_manager := child as EnvironmentManagerRoot
|
||||
if environment_manager != null:
|
||||
environment_config = environment_manager.environment_config
|
||||
return
|
||||
|
||||
func _apply_initial_train_start() -> void:
|
||||
if train_instance == null or curve == null:
|
||||
return
|
||||
@@ -134,37 +136,54 @@ func _apply_initial_train_start() -> void:
|
||||
if total_length <= 0.0:
|
||||
return
|
||||
|
||||
if environment_config != null:
|
||||
match environment_config.train_start_from_random_position:
|
||||
EnvironmentConfig.TrainStartMode.RANDOM_POSITION:
|
||||
train_progress = randf() * total_length
|
||||
match train_start_mode:
|
||||
TrainStartMode.RANDOM_POSITION:
|
||||
train_progress = randf() * total_length
|
||||
_update_next_stop_index_from_progress()
|
||||
TrainStartMode.SPECIFIED_STATION:
|
||||
if stop_offset.is_empty():
|
||||
train_progress = wrapf(train_progress, 0.0, total_length)
|
||||
_update_next_stop_index_from_progress()
|
||||
EnvironmentConfig.TrainStartMode.SPECIFIED_STATION:
|
||||
if stop_offset.is_empty():
|
||||
train_progress = wrapf(train_progress, 0.0, total_length)
|
||||
_update_next_stop_index_from_progress()
|
||||
else:
|
||||
var stop_index: int = clampi(environment_config.train_start_stop_index, 0, stop_offset.size() - 1)
|
||||
train_progress = stop_offset[stop_index]
|
||||
_set_next_stop_index_from_current(stop_index)
|
||||
EnvironmentConfig.TrainStartMode.SPECIFIED_POSITION:
|
||||
train_progress = wrapf(environment_config.train_start_position, 0.0, total_length)
|
||||
_update_next_stop_index_from_progress()
|
||||
else:
|
||||
train_progress = wrapf(train_progress, 0.0, total_length)
|
||||
_update_next_stop_index_from_progress()
|
||||
else:
|
||||
var stop_index: int = clampi(train_start_stop_index, 0, stop_offset.size() - 1)
|
||||
train_progress = stop_offset[stop_index]
|
||||
_set_next_stop_index_from_current(stop_index)
|
||||
TrainStartMode.SPECIFIED_POSITION:
|
||||
train_progress = wrapf(train_start_position, 0.0, total_length)
|
||||
_update_next_stop_index_from_progress()
|
||||
|
||||
_apply_train_facing_direction(total_length)
|
||||
_update_next_stop_index_from_progress()
|
||||
_snap_train_to_progress()
|
||||
|
||||
func _apply_train_facing_direction(total_length: float) -> void:
|
||||
_train_direction_sign = 1.0
|
||||
if train_facing_direction == TrainFacingDirection.KEEP_PATH_DIRECTION or curve == null:
|
||||
return
|
||||
if total_length <= 0.0:
|
||||
return
|
||||
|
||||
var center_progress: float = wrapf(train_progress, 0.0, total_length)
|
||||
var center_position: Vector3 = to_global(curve.sample_baked(center_progress, true))
|
||||
var forward_progress: float = wrapf(center_progress + 2.0, 0.0, total_length)
|
||||
var forward_position: Vector3 = to_global(curve.sample_baked(forward_progress, true))
|
||||
var forward_direction: Vector3 = forward_position - center_position
|
||||
if forward_direction.length_squared() <= 0.0001 or is_zero_approx(forward_direction.x):
|
||||
return
|
||||
|
||||
var desired_x_sign: float = -1.0 if train_facing_direction == TrainFacingDirection.LOCOMOTIVE_LEFT else 1.0
|
||||
if forward_direction.x * desired_x_sign < 0.0:
|
||||
_train_direction_sign = -1.0
|
||||
|
||||
if not is_zero_approx(train_speed):
|
||||
train_speed = absf(train_speed) * _train_direction_sign
|
||||
|
||||
func _apply_train_start_delay() -> void:
|
||||
if not _initial_is_inmotion:
|
||||
return
|
||||
|
||||
var should_start_after_delay: bool = true
|
||||
var start_delay_seconds: float = 0.0
|
||||
if environment_config != null:
|
||||
should_start_after_delay = environment_config.train_start_after_delay
|
||||
start_delay_seconds = maxf(environment_config.train_start_delay_seconds, 0.0)
|
||||
var should_start_after_delay: bool = train_start_after_delay
|
||||
var start_delay_seconds: float = maxf(train_start_delay_seconds, 0.0)
|
||||
if start_delay_seconds <= 0.0:
|
||||
if not should_start_after_delay:
|
||||
is_inmotion = false
|
||||
@@ -220,13 +239,9 @@ func _snap_train_to_progress() -> void:
|
||||
|
||||
train_progress = wrapf(train_progress, 0.0, total_length)
|
||||
var center_position: Vector3 = to_global(curve.sample_baked(train_progress, true))
|
||||
var prog_forward: float = wrapf(train_progress + 2.0, 0.0, total_length)
|
||||
var forward_position: Vector3 = to_global(curve.sample_baked(prog_forward, true))
|
||||
|
||||
train_instance.global_position = center_position
|
||||
if center_position.distance_to(forward_position) > 0.01:
|
||||
train_instance.look_at(forward_position, Vector3.UP)
|
||||
train_instance.rotate_y(PI)
|
||||
_orient_vehicle_to_track(train_instance, train_progress, total_length)
|
||||
|
||||
if cameras:
|
||||
cameras.global_position = center_position
|
||||
@@ -248,6 +263,7 @@ func build_rails() -> void:
|
||||
|
||||
var total_length = curve.get_baked_length()
|
||||
var piece_number = int(total_length / sleepers_distance)
|
||||
var should_add_procedural_rails: bool = sleepers_model == null or add_procedural_rails_to_sleepers_model
|
||||
|
||||
for i in range(piece_number):
|
||||
var distance = i * sleepers_distance
|
||||
@@ -279,17 +295,18 @@ func build_rails() -> void:
|
||||
sleeper.material_override = mat_wood
|
||||
rail_piece.add_child(sleeper)
|
||||
|
||||
var rail_sx = MeshInstance3D.new()
|
||||
rail_sx.mesh = mesh_rail
|
||||
rail_sx.material_override = mat_iron
|
||||
rail_sx.position = Vector3(-rail_distance / 2.0, 0.1, 0)
|
||||
rail_piece.add_child(rail_sx)
|
||||
|
||||
var rail_dx = MeshInstance3D.new()
|
||||
rail_dx.mesh = mesh_rail
|
||||
rail_dx.material_override = mat_iron
|
||||
rail_dx.position = Vector3(rail_distance / 2.0, 0.1, 0)
|
||||
rail_piece.add_child(rail_dx)
|
||||
if should_add_procedural_rails:
|
||||
var rail_sx = MeshInstance3D.new()
|
||||
rail_sx.mesh = mesh_rail
|
||||
rail_sx.material_override = mat_iron
|
||||
rail_sx.position = Vector3(-rail_distance / 2.0, 0.1, 0)
|
||||
rail_piece.add_child(rail_sx)
|
||||
|
||||
var rail_dx = MeshInstance3D.new()
|
||||
rail_dx.mesh = mesh_rail
|
||||
rail_dx.material_override = mat_iron
|
||||
rail_dx.position = Vector3(rail_distance / 2.0, 0.1, 0)
|
||||
rail_piece.add_child(rail_dx)
|
||||
|
||||
func _plan_stops() -> void:
|
||||
stop_offset.clear()
|
||||
@@ -437,7 +454,10 @@ func train_move(delta: float) -> void:
|
||||
stop_multiply = 1.0
|
||||
|
||||
var current_speed = train_speed * stop_multiply
|
||||
train_progress += current_speed * delta
|
||||
var next_progress: float = train_progress + current_speed * delta
|
||||
var wrapped_forward: bool = current_speed > 0.0 and next_progress >= total_length
|
||||
var wrapped_back: bool = current_speed < 0.0 and next_progress < 0.0
|
||||
train_progress = wrapf(next_progress, 0.0, total_length)
|
||||
_track_steam_distance(abs(current_speed) * delta)
|
||||
|
||||
if enable_stops and stop_offset.size() > 0 and has_valid_stop:
|
||||
@@ -449,11 +469,9 @@ func train_move(delta: float) -> void:
|
||||
train_progress = target_offset
|
||||
_execute_stop(current_speed > 0)
|
||||
|
||||
if train_progress > total_length:
|
||||
train_progress -= total_length
|
||||
if wrapped_forward:
|
||||
if stop_offset.size() > 0: next_stop_index = 0
|
||||
elif train_progress < 0:
|
||||
train_progress += total_length
|
||||
elif wrapped_back:
|
||||
if stop_offset.size() > 0: next_stop_index = stop_offset.size() - 1
|
||||
|
||||
var center_position = to_global(curve.sample_baked(train_progress, true))
|
||||
@@ -464,8 +482,7 @@ func train_move(delta: float) -> void:
|
||||
|
||||
if center_position.distance_to(forward_position) > 0.01:
|
||||
train_instance.global_position = center_position
|
||||
train_instance.look_at(forward_position, Vector3.UP)
|
||||
train_instance.rotate_y(PI)
|
||||
_orient_vehicle_to_track(train_instance, train_progress, total_length)
|
||||
|
||||
if cameras:
|
||||
cameras.global_position = center_position
|
||||
@@ -503,28 +520,7 @@ func _track_steam_distance(distance_units: float) -> void:
|
||||
|
||||
# Always track the global distance in our save file (for UI and persistency)
|
||||
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 * DISTANCE_KM_PER_UNIT
|
||||
var whole_km: int = int(floor(_pending_steam_distance_km))
|
||||
if whole_km <= 0:
|
||||
return
|
||||
|
||||
_pending_steam_distance_km -= float(whole_km)
|
||||
var total_distance_km: int = StatsManager.get_int(STEAM_DISTANCE_STAT) + whole_km
|
||||
StatsManager.set_int(STEAM_DISTANCE_STAT, total_distance_km)
|
||||
_check_distance_achievements(total_distance_km)
|
||||
StatsManager.store()
|
||||
|
||||
#Check achievements
|
||||
func _check_distance_achievements(total_distance_km: int) -> void:
|
||||
if total_distance_km >= 100:
|
||||
AchievementManager.unlock("ACH_DISTANCE_100")
|
||||
if total_distance_km >= 200:
|
||||
AchievementManager.unlock("ACH_DISTANCE_200")
|
||||
StatsManager.add_distance_km(distance_units * DISTANCE_KM_PER_UNIT)
|
||||
|
||||
func _execute_stop(go_forward: bool = true) -> void:
|
||||
if not _find_next_valid_stop(go_forward):
|
||||
@@ -532,12 +528,14 @@ func _execute_stop(go_forward: bool = true) -> void:
|
||||
return
|
||||
|
||||
stop_ongoing = true
|
||||
stop_multiply = 0.0
|
||||
|
||||
stop_multiply = 0.0
|
||||
|
||||
var current_stop_index = next_stop_index
|
||||
|
||||
StatsManager.add_int("stat_stop_number", 1)
|
||||
StatsManager.store()
|
||||
|
||||
_advance_next_stop_index(go_forward)
|
||||
|
||||
|
||||
if fireworks_scene != null:
|
||||
var marker_position = stops_position[current_stop_index]
|
||||
var firework_number = randi_range(5, 8)
|
||||
@@ -676,6 +674,10 @@ func _snap_wagons_to_progress(total_length: float = 0.0) -> void:
|
||||
if total_length <= 0.0:
|
||||
return
|
||||
|
||||
var previous_progress: float = wrapf(train_progress, 0.0, total_length)
|
||||
var previous_position: Vector3 = to_global(curve.sample_baked(previous_progress, true))
|
||||
var previous_wagon_offset: float = 0.0
|
||||
|
||||
for i in range(wagon_instances.size()):
|
||||
var wagon: Node3D = wagon_instances[i]
|
||||
if not is_instance_valid(wagon):
|
||||
@@ -685,17 +687,101 @@ func _snap_wagons_to_progress(total_length: float = 0.0) -> void:
|
||||
if i < wagon_progress_offsets.size():
|
||||
wagon_offset = wagon_progress_offsets[i]
|
||||
|
||||
var wagon_progress: float = train_progress - wagon_offset
|
||||
var wagon_progress: float = train_progress - wagon_offset * _train_direction_sign
|
||||
var segment_spacing: float = maxf(wagon_offset - previous_wagon_offset, 0.1)
|
||||
|
||||
var vehicle_progress: float = wrapf(wagon_progress, 0.0, total_length)
|
||||
var center_position: Vector3 = to_global(curve.sample_baked(vehicle_progress, true))
|
||||
var prog_forward: float = wrapf(vehicle_progress + 2.0, 0.0, total_length)
|
||||
var forward_position: Vector3 = to_global(curve.sample_baked(prog_forward, true))
|
||||
var zero_spacing_fix_weight: float = _get_zero_spacing_fix_weight(previous_progress, vehicle_progress, total_length)
|
||||
if zero_spacing_fix_weight > 0.0:
|
||||
var target_distance: float = _get_zero_spacing_target_distance(segment_spacing)
|
||||
var corrected_progress: float = _find_zero_spacing_progress(previous_progress, previous_position, segment_spacing, target_distance, total_length)
|
||||
vehicle_progress = _lerp_progress_on_track(vehicle_progress, corrected_progress, zero_spacing_fix_weight, total_length)
|
||||
center_position = to_global(curve.sample_baked(vehicle_progress, true))
|
||||
|
||||
wagon.global_position = center_position
|
||||
if center_position.distance_to(forward_position) > 0.01:
|
||||
wagon.look_at(forward_position, Vector3.UP)
|
||||
wagon.rotate_y(PI)
|
||||
_orient_vehicle_to_track(wagon, vehicle_progress, total_length)
|
||||
|
||||
previous_progress = vehicle_progress
|
||||
previous_position = center_position
|
||||
previous_wagon_offset = wagon_offset
|
||||
|
||||
func _orient_vehicle_to_track(vehicle: Node3D, progress: float, total_length: float) -> void:
|
||||
if vehicle == null or curve == null or total_length <= 0.0:
|
||||
return
|
||||
|
||||
var center_progress: float = wrapf(progress, 0.0, total_length)
|
||||
var center_position: Vector3 = to_global(curve.sample_baked(center_progress, true))
|
||||
var forward_progress: float = wrapf(center_progress + 2.0 * _train_direction_sign, 0.0, total_length)
|
||||
var forward_position: Vector3 = to_global(curve.sample_baked(forward_progress, true))
|
||||
if center_position.distance_to(forward_position) <= 0.01:
|
||||
return
|
||||
|
||||
vehicle.look_at(forward_position, Vector3.UP)
|
||||
vehicle.rotate_y(PI)
|
||||
|
||||
func _get_zero_spacing_fix_weight(previous_progress: float, vehicle_progress: float, total_length: float) -> float:
|
||||
var distance_to_zero: float = minf(
|
||||
_get_progress_distance_to_zero(previous_progress, total_length),
|
||||
_get_progress_distance_to_zero(vehicle_progress, total_length)
|
||||
)
|
||||
var full_fix_distance: float = maxf(TRACK_ZERO_SPACING_FIX_DISTANCE - TRACK_ZERO_SPACING_BLEND_DISTANCE, 0.0)
|
||||
return 1.0 - smoothstep(full_fix_distance, TRACK_ZERO_SPACING_FIX_DISTANCE, distance_to_zero)
|
||||
|
||||
func _get_progress_distance_to_zero(progress: float, total_length: float) -> float:
|
||||
var wrapped_progress: float = wrapf(progress, 0.0, total_length)
|
||||
return minf(wrapped_progress, total_length - wrapped_progress)
|
||||
|
||||
func _get_zero_spacing_target_distance(segment_spacing: float) -> float:
|
||||
return minf(maxf(segment_spacing * TRACK_ZERO_SPACING_TARGET_RATIO, TRACK_ZERO_SPACING_MIN_DISTANCE), segment_spacing)
|
||||
|
||||
func _find_zero_spacing_progress(previous_progress: float, previous_position: Vector3, segment_spacing: float, minimum_distance: float, total_length: float) -> float:
|
||||
var start_distance: float = minf(minimum_distance, segment_spacing)
|
||||
var best_progress: float = wrapf(previous_progress - start_distance * _train_direction_sign, 0.0, total_length)
|
||||
var search_limit: float = minf(maxf(segment_spacing, minimum_distance) + TRACK_ZERO_SPACING_SEARCH_EXTRA, total_length)
|
||||
var search_distance: float = start_distance
|
||||
var previous_search_distance: float = start_distance
|
||||
|
||||
while search_distance <= search_limit:
|
||||
var candidate_progress: float = wrapf(previous_progress - search_distance * _train_direction_sign, 0.0, total_length)
|
||||
var candidate_position: Vector3 = to_global(curve.sample_baked(candidate_progress, true))
|
||||
best_progress = candidate_progress
|
||||
if previous_position.distance_to(candidate_position) >= minimum_distance:
|
||||
if is_equal_approx(search_distance, start_distance):
|
||||
return candidate_progress
|
||||
|
||||
return _refine_zero_spacing_progress(
|
||||
previous_progress,
|
||||
previous_position,
|
||||
previous_search_distance,
|
||||
search_distance,
|
||||
minimum_distance,
|
||||
total_length
|
||||
)
|
||||
|
||||
previous_search_distance = search_distance
|
||||
search_distance += TRACK_ZERO_SPACING_SEARCH_STEP
|
||||
|
||||
return best_progress
|
||||
|
||||
func _refine_zero_spacing_progress(previous_progress: float, previous_position: Vector3, min_search_distance: float, max_search_distance: float, minimum_distance: float, total_length: float) -> float:
|
||||
var low_distance: float = min_search_distance
|
||||
var high_distance: float = max_search_distance
|
||||
|
||||
for i in range(8):
|
||||
var mid_distance: float = (low_distance + high_distance) * 0.5
|
||||
var mid_progress: float = wrapf(previous_progress - mid_distance, 0.0, total_length)
|
||||
var mid_position: Vector3 = to_global(curve.sample_baked(mid_progress, true))
|
||||
if previous_position.distance_to(mid_position) >= minimum_distance:
|
||||
high_distance = mid_distance
|
||||
else:
|
||||
low_distance = mid_distance
|
||||
|
||||
return wrapf(previous_progress - high_distance, 0.0, total_length)
|
||||
|
||||
func _lerp_progress_on_track(from_progress: float, to_progress: float, weight: float, total_length: float) -> float:
|
||||
var wrapped_delta: float = wrapf(to_progress - from_progress + total_length * 0.5, 0.0, total_length) - total_length * 0.5
|
||||
return wrapf(from_progress + wrapped_delta * clampf(weight, 0.0, 1.0), 0.0, total_length)
|
||||
|
||||
func _get_vehicle_length(vehicle: Node3D) -> float:
|
||||
var bounds: AABB = _get_node_local_bounds(vehicle, vehicle)
|
||||
|
||||
@@ -4,6 +4,15 @@ extends Node3D
|
||||
const NOISE_TEXTURE: Texture2D = preload("res://core/daynight/noise.tres")
|
||||
const WEATHER_SHADER: Material = preload("res://core/daynight/weather_overlay.tres")
|
||||
const DYNAMIC_ENVIRONMENT_UPDATES_PER_FRAME: int = 32 #how many update of the environment (apply materials) will be done per frame
|
||||
const FORCE_WEATHER_MATERIAL_PATH_PARTS: PackedStringArray = [
|
||||
"/chunk/material/field.tres",
|
||||
"/chunk/material/grassflat_chunk.tres",
|
||||
"/chunk/material/path_chunk.tres",
|
||||
"/chunk/material/railway_chunk.tres",
|
||||
"/chunk/material/terrain.tres",
|
||||
"/chunk/prop/house/house.tres",
|
||||
"/chunk/prop/house/house_emissiv.tres",
|
||||
]
|
||||
|
||||
@export var environment_config: EnvironmentConfig
|
||||
|
||||
@@ -157,9 +166,17 @@ func _restore_keyboard_action_events() -> void:
|
||||
_blocked_keyboard_events_by_action.clear()
|
||||
|
||||
func _on_tree_node_added(node: Node) -> void:
|
||||
if node.is_in_group("wind_node") or node.is_in_group("weather_node") or node.is_in_group("weather_vegetables_node"):
|
||||
if _should_queue_dynamic_environment_node(node):
|
||||
_queue_dynamic_environment_node(node)
|
||||
|
||||
func _should_queue_dynamic_environment_node(node: Node) -> bool:
|
||||
if node.is_in_group("wind_node"):
|
||||
return true
|
||||
if node.is_in_group("weather_vegetables_node"):
|
||||
return true
|
||||
|
||||
return node is Node3D
|
||||
|
||||
func _queue_dynamic_environment_node(node: Node) -> void:
|
||||
if not is_instance_valid(node):
|
||||
return
|
||||
@@ -205,7 +222,7 @@ 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") or node.is_in_group("weather_vegetables_node"):
|
||||
if node is Node3D:
|
||||
_apply_weather_overlay_to_node(node, _get_weather_overlay_material(node))
|
||||
|
||||
if _should_ignore_weather_overlay(node):
|
||||
@@ -231,11 +248,9 @@ func _apply_wind_noise_to_node(node: Node, noise_tex: Texture2D) -> void:
|
||||
_apply_wind_noise_to_node(child, noise_tex)
|
||||
|
||||
func ApplyWeatherShaderToMaterials():
|
||||
for node in get_tree().get_nodes_in_group("weather_node"):
|
||||
_apply_weather_overlay_to_node(node, _get_weather_overlay_material(node))
|
||||
|
||||
for node in get_tree().get_nodes_in_group("weather_vegetables_node"):
|
||||
_apply_weather_overlay_to_node(node, _get_weather_overlay_material(node))
|
||||
var current_scene := get_tree().current_scene
|
||||
if current_scene != null:
|
||||
_apply_weather_overlay_to_node(current_scene, _get_weather_overlay_material(current_scene))
|
||||
|
||||
func _get_weather_overlay_material(node: Node) -> Material:
|
||||
if not node.is_in_group("weather_overlay_no_noise"):
|
||||
@@ -255,13 +270,21 @@ func _apply_weather_overlay_to_node(node: Node, material: Material) -> void:
|
||||
return
|
||||
|
||||
if node is GeometryInstance3D:
|
||||
if _should_clear_weather_overlay(node) or _geometry_uses_alpha_texture(node):
|
||||
if _geometry_should_never_receive_weather_overlay(node):
|
||||
node.material_overlay = null
|
||||
elif _should_clear_weather_overlay(node) and not _geometry_forces_weather_overlay(node):
|
||||
node.material_overlay = null
|
||||
elif _geometry_uses_alpha_texture(node) or _geometry_should_skip_weather_overlay(node):
|
||||
node.material_overlay = null
|
||||
else:
|
||||
node.material_overlay = material
|
||||
|
||||
var child_material := _get_weather_overlay_material(node)
|
||||
if child_material == WEATHER_SHADER and material == weather_shader_no_noise:
|
||||
child_material = material
|
||||
|
||||
for child in node.get_children():
|
||||
_apply_weather_overlay_to_node(child, material)
|
||||
_apply_weather_overlay_to_node(child, child_material)
|
||||
|
||||
func _clear_weather_overlay_from_node(node: Node) -> void:
|
||||
if node is GeometryInstance3D:
|
||||
@@ -289,11 +312,99 @@ func _geometry_uses_alpha_texture(node: GeometryInstance3D) -> bool:
|
||||
|
||||
return false
|
||||
|
||||
func _geometry_should_never_receive_weather_overlay(node: GeometryInstance3D) -> bool:
|
||||
if node is GPUParticles3D:
|
||||
return true
|
||||
|
||||
var node_name := String(node.name).to_lower()
|
||||
return node_name.contains("cloud") or node_name.contains("fog") or node_name.contains("godray")
|
||||
|
||||
func _geometry_should_skip_weather_overlay(node: GeometryInstance3D) -> bool:
|
||||
if _geometry_forces_weather_overlay(node):
|
||||
return false
|
||||
if _node_name_indicates_water(node):
|
||||
return true
|
||||
|
||||
var has_material: bool = false
|
||||
var all_materials_ignored: bool = true
|
||||
var material_override := node.material_override
|
||||
if material_override != null:
|
||||
has_material = true
|
||||
all_materials_ignored = all_materials_ignored and _material_ignores_weather_overlay(material_override)
|
||||
|
||||
if node is MeshInstance3D:
|
||||
for surface_index in node.get_surface_override_material_count():
|
||||
var surface_material: Material = node.get_surface_override_material(surface_index)
|
||||
if surface_material == null:
|
||||
continue
|
||||
|
||||
has_material = true
|
||||
all_materials_ignored = all_materials_ignored and _material_ignores_weather_overlay(surface_material)
|
||||
|
||||
if node.mesh:
|
||||
for surface_index in node.mesh.get_surface_count():
|
||||
var mesh_material: Material = node.mesh.surface_get_material(surface_index)
|
||||
if mesh_material == null:
|
||||
continue
|
||||
|
||||
has_material = true
|
||||
all_materials_ignored = all_materials_ignored and _material_ignores_weather_overlay(mesh_material)
|
||||
|
||||
return has_material and all_materials_ignored
|
||||
|
||||
func _geometry_forces_weather_overlay(node: GeometryInstance3D) -> bool:
|
||||
if _material_forces_weather_overlay(node.material_override):
|
||||
return true
|
||||
|
||||
if node is MeshInstance3D:
|
||||
for surface_index in node.get_surface_override_material_count():
|
||||
if _material_forces_weather_overlay(node.get_surface_override_material(surface_index)):
|
||||
return true
|
||||
|
||||
if node.mesh:
|
||||
for surface_index in node.mesh.get_surface_count():
|
||||
if _material_forces_weather_overlay(node.mesh.surface_get_material(surface_index)):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
func _material_forces_weather_overlay(material: Material) -> bool:
|
||||
if material == null:
|
||||
return false
|
||||
|
||||
var resource_path := material.resource_path.to_lower()
|
||||
for path_part in FORCE_WEATHER_MATERIAL_PATH_PARTS:
|
||||
if resource_path.contains(path_part):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
func _node_name_indicates_water(node: Node) -> bool:
|
||||
var node_name := String(node.name).to_lower()
|
||||
return node_name.begins_with("water") or node_name.contains("/water")
|
||||
|
||||
func _material_ignores_weather_overlay(material: Material) -> bool:
|
||||
if material == null:
|
||||
return false
|
||||
|
||||
var resource_path := material.resource_path.to_lower()
|
||||
if resource_path.contains("/water") or resource_path.contains("water_"):
|
||||
return true
|
||||
if resource_path.contains("/cloud/") or resource_path.contains("cloud"):
|
||||
return true
|
||||
|
||||
var base_material := material as BaseMaterial3D
|
||||
if base_material != null:
|
||||
return base_material.transparency != BaseMaterial3D.TRANSPARENCY_DISABLED or base_material.albedo_color.a < 0.99
|
||||
|
||||
return false
|
||||
|
||||
func _shader_material_uses_alpha_texture(material: ShaderMaterial) -> bool:
|
||||
if material == null or material.shader == null:
|
||||
return false
|
||||
|
||||
return material.shader.code.find("alpha_texture") != -1
|
||||
var shader_code := material.shader.code
|
||||
return shader_code.find("alpha_texture") != -1 or shader_code.find("ALPHA") != -1 or shader_code.find("opacity") != -1
|
||||
|
||||
func _should_ignore_weather_overlay(node: Node) -> bool:
|
||||
return node.is_in_group("weather_overlay_ignore")
|
||||
|
||||
@@ -15,6 +15,8 @@ global uniform float global_snow_melt_speed = 0.1;
|
||||
global uniform float global_snow_amount = 0.0;
|
||||
global uniform vec4 global_snow_color = vec4(0.92, 0.96, 1.0, 1.0);
|
||||
global uniform float global_rain_intensity;
|
||||
const float GRASS_FULL_SNOW_START = 0.58;
|
||||
const float GRASS_FULL_SNOW_END = 0.86;
|
||||
|
||||
// --- PARAMETRI ESTETICI ---
|
||||
uniform bool billboard_enabled = true;
|
||||
@@ -141,13 +143,15 @@ void fragment() {
|
||||
float snow_mask = smoothstep(snow_start, 1.0, top_mask) * snow_amount * snow_visibility;
|
||||
snow_mask *= step(0.01, snow_amount);
|
||||
snow_mask = clamp(snow_mask, 0.0, 1.0);
|
||||
float full_snow_mask = smoothstep(GRASS_FULL_SNOW_START, GRASS_FULL_SNOW_END, snow_amount) * step(0.01, snow_visibility);
|
||||
float final_snow_mask = max(snow_mask, full_snow_mask);
|
||||
|
||||
vec3 snow_base_color = max(snow_color.rgb, global_snow_color.rgb);
|
||||
float snow_light = mix(0.82, 1.0, max(v_shade_factor, 0.35));
|
||||
vec3 shaded_snow = clamp(snow_base_color * snow_light, 0.0, 1.0);
|
||||
|
||||
// Mescoliamo il colore variato con la neve
|
||||
vec3 final_albedo = mix(varied_grass_color, shaded_snow, snow_mask);
|
||||
vec3 final_albedo = mix(varied_grass_color, shaded_snow, final_snow_mask);
|
||||
float rain_int = clamp(global_rain_intensity, 0.0, 1.0);
|
||||
final_albedo *= mix(1.0, 1.0 - wetness_darkening, rain_int);
|
||||
float final_roughness = mix(0.02, 0.005, rain_int);
|
||||
|
||||
@@ -13,6 +13,9 @@ global uniform float global_snow_melt_speed = 0.1;
|
||||
global uniform float global_snow_amount = 0.0;
|
||||
global uniform vec4 global_snow_color;
|
||||
global uniform float global_rain_intensity;
|
||||
const float LEAVES_FULL_SNOW_START = 0.96;
|
||||
const float LEAVES_FULL_SNOW_END = 1.0;
|
||||
const float LEAVES_TOP_SNOW_COVERAGE = 0.75;
|
||||
|
||||
uniform sampler2D wind_noise : filter_linear_mipmap;
|
||||
uniform bool billboard_enabled = true;
|
||||
@@ -108,12 +111,14 @@ void fragment() {
|
||||
float snow_amount = pow(get_snow_progress(), 0.55);
|
||||
|
||||
float top_mask = 1.0 - shifted_uv.y;
|
||||
float snow_mask = smoothstep(1.0 - snow_amount * 1.35, 1.08 - snow_amount * 1.35, top_mask) * snow_visibility;
|
||||
float snow_mask = smoothstep(1.0 - snow_amount * LEAVES_TOP_SNOW_COVERAGE, 1.08 - snow_amount * LEAVES_TOP_SNOW_COVERAGE, top_mask) * snow_visibility;
|
||||
snow_mask *= step(0.01, snow_amount);
|
||||
float full_snow_mask = smoothstep(LEAVES_FULL_SNOW_START, LEAVES_FULL_SNOW_END, snow_amount) * clamp(snow_visibility, 0.0, 1.0);
|
||||
float final_snow_mask = max(snow_mask, full_snow_mask);
|
||||
|
||||
float snow_light = mix(0.82, 1.0, max(v_shade_factor, 0.35));
|
||||
vec3 shaded_snow = clamp(global_snow_color.rgb * snow_light, 0.0, 1.0);
|
||||
vec3 final_albedo = mix(v_final_color, shaded_snow, snow_mask);
|
||||
vec3 final_albedo = mix(v_final_color, shaded_snow, final_snow_mask);
|
||||
|
||||
// Rain wetness: darken and make shinier
|
||||
float rain_int = clamp(global_rain_intensity, 0.0, 1.0);
|
||||
|
||||
@@ -17,6 +17,8 @@ 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;
|
||||
const float FULL_SNOW_VISUAL_START = 0.58;
|
||||
const float FULL_SNOW_VISUAL_END = 0.86;
|
||||
uniform bool snow_noise_enabled = true;
|
||||
uniform float snow_noise_scale : hint_range(0.01, 1.0) = 0.15;
|
||||
uniform float snow_edge_softness : hint_range(0.01, 0.5) = 0.2;
|
||||
@@ -152,10 +154,12 @@ void fragment() {
|
||||
float flat_accumulation = smoothstep(0.0, 0.35, v_snow_cap_mask) * snow_accumulation;
|
||||
float snow_noise_mask = get_snow_noise_mask(world_pos, snow_progress);
|
||||
float snow_variation = mix(0.75, 1.15, noise_val);
|
||||
float snow_coverage = clamp(mix(snow_progress * 0.35, snow_progress * snow_variation, snow_noise_mask) + flat_accumulation * 0.15, 0.0, 1.0);
|
||||
float snow_coverage = clamp(mix(snow_progress * 0.55, snow_progress * snow_variation, snow_noise_mask) + flat_accumulation * 0.2, 0.0, 1.0);
|
||||
float snow_factor = max(snow_edge * snow_coverage, flat_accumulation);
|
||||
float snow_opacity = clamp(snow_factor, 0.0, 1.0);
|
||||
snow_opacity = max(snow_opacity, flat_accumulation * mix(0.45, 0.9, snow_noise_mask));
|
||||
float full_snow_opacity = snow_edge * smoothstep(FULL_SNOW_VISUAL_START, FULL_SNOW_VISUAL_END, snow_progress);
|
||||
snow_opacity = max(snow_opacity, full_snow_opacity);
|
||||
|
||||
float shade = mix(-snow_color_variation, snow_color_variation, noise_val);
|
||||
vec3 snow_albedo = clamp(global_snow_color.rgb + vec3(shade), 0.0, 1.0);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
class_name EnvironmentConfig
|
||||
extends Resource
|
||||
|
||||
enum TrainStartMode { RANDOM_POSITION, SPECIFIED_STATION, SPECIFIED_POSITION }
|
||||
|
||||
@export_group("Day")
|
||||
@export var start_time: float = 0.0 #start time of the day
|
||||
@export var day_duration: float = 300.0 #Duration of a full day cycle in seconds
|
||||
@@ -157,15 +155,6 @@ enum TrainStartMode { RANDOM_POSITION, SPECIFIED_STATION, SPECIFIED_POSITION }
|
||||
@export var godray_spawn_height: float = 5.0 #Y world position where god rays spawn
|
||||
@export var godray_scale: Vector3 = Vector3(2.0, 6.0, 2.0) #Scale of the god ray mesh
|
||||
|
||||
#Train start settings
|
||||
@export_group("Train Start")
|
||||
@export_enum("random_position", "specified_station", "specified_position") var train_start_from_random_position: int = TrainStartMode.RANDOM_POSITION #Train start mode
|
||||
@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_position: float = 0.0 #Offset on the rail curve used when train_start_from_random_position is specified_position
|
||||
@export var train_start_delay_seconds: float = 0.0 #Seconds the train waits before starting at game launch
|
||||
@export var train_start_after_delay: bool = true #When enabled the train starts after train_start_delay_seconds, otherwise it stays stopped
|
||||
@export var train_start_acceleration_seconds: float = 4.0 #Seconds used to progressively reach normal train speed after launch
|
||||
|
||||
#Snow settings
|
||||
@export_group("Snow")
|
||||
@export var snow_amount: float = 0.0 #Initial snow coverage amount (0.0 = none, 1.0 = full)
|
||||
|
||||
@@ -40,6 +40,7 @@ func _on_color_selected(selected_color_picker: TrainColorPicker, material_index:
|
||||
for i in range(color_pickers.size()):
|
||||
if color_pickers[i] == selected_color_picker:
|
||||
_set_selected_color(i, material_index)
|
||||
AchievementManager.unlock("ACH_CHANGE_TRAIN_COLOR")
|
||||
break
|
||||
|
||||
func _apply_train_color(color: Color, material_index: int, color_index: int = 0) -> void:
|
||||
|
||||
@@ -41,17 +41,7 @@ func apply_video_settings() -> void:
|
||||
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 = Viewport.MSAA_DISABLED
|
||||
|
||||
func apply_train_colors() -> void:
|
||||
if not save_data.train_color_1.is_empty():
|
||||
var mat1: ShaderMaterial = load("res://tgcc/train/Color1_train.tres")
|
||||
if mat1:
|
||||
mat1.set_shader_parameter("albedo_color", Color(save_data.train_color_1))
|
||||
if not save_data.train_color_2.is_empty():
|
||||
var mat2: ShaderMaterial = load("res://tgcc/train/Color2_train.tres")
|
||||
if mat2:
|
||||
mat2.set_shader_parameter("albedo_color", Color(save_data.train_color_2))
|
||||
|
||||
|
||||
match save_data.window_mode_index:
|
||||
0:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN)
|
||||
@@ -78,6 +68,16 @@ func apply_train_colors() -> void:
|
||||
)
|
||||
DisplayServer.window_set_position(screen_center - window_half_size)
|
||||
|
||||
func apply_train_colors() -> void:
|
||||
if not save_data.train_color_1.is_empty():
|
||||
var mat1: ShaderMaterial = load("res://tgcc/train/Color1_train.tres")
|
||||
if mat1:
|
||||
mat1.set_shader_parameter("albedo_color", Color(save_data.train_color_1))
|
||||
if not save_data.train_color_2.is_empty():
|
||||
var mat2: ShaderMaterial = load("res://tgcc/train/Color2_train.tres")
|
||||
if mat2:
|
||||
mat2.set_shader_parameter("albedo_color", Color(save_data.train_color_2))
|
||||
|
||||
#encrypted file + binary serialization (standard)
|
||||
func load_game() -> void:
|
||||
if not FileAccess.file_exists(SAVE_PATH):
|
||||
|
||||
@@ -113,6 +113,7 @@ func _on_time_of_day_row_on_option_changed(data: String) -> void:
|
||||
pass
|
||||
|
||||
func _on_weather_row_on_option_changed(data: String) -> void:
|
||||
var weather_changed: bool = true
|
||||
match data:
|
||||
"Snow":
|
||||
_snow = !_snow
|
||||
@@ -129,9 +130,10 @@ func _on_weather_row_on_option_changed(data: String) -> void:
|
||||
"Random":
|
||||
pick_random_weather()
|
||||
_:
|
||||
pass
|
||||
weather_changed = false
|
||||
|
||||
AchievementManager.is_unlocked("ACH_CHANGE_METEO")
|
||||
if weather_changed:
|
||||
AchievementManager.unlock("ACH_CHANGE_METEO")
|
||||
|
||||
func pick_random_weather() -> void:
|
||||
var valid_buttons = []
|
||||
@@ -214,7 +216,6 @@ func _on_photo_taken_finished() -> void:
|
||||
|
||||
func _on_choo_choo_button_pressed() -> void:
|
||||
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
|
||||
|
||||
@@ -6,6 +6,11 @@ extends Node
|
||||
const KNOWN := [
|
||||
"ACH_CHANGE_METEO",
|
||||
"ACH_CHANGE_TRAIN_COLOR",
|
||||
"ACH_DISTANCE_100",
|
||||
"ACH_DISTANCE_200",
|
||||
"ACH_10_PHOTOS",
|
||||
"ACH_10_TOOTTOOT",
|
||||
"ACH_DISTANCE_1000"
|
||||
]
|
||||
|
||||
#how it works:
|
||||
|
||||
@@ -13,6 +13,13 @@ const KNOWN := [
|
||||
"stat_photo_number",
|
||||
"stat_toottoot_number",
|
||||
]
|
||||
const TIME_PLAYED_STAT: String = "stat_time_played"
|
||||
const DISTANCE_KM_STAT: String = "stat_distance_km"
|
||||
const STATS_STORE_INTERVAL_SECONDS: int = 60
|
||||
|
||||
var _pending_time_played_seconds: float = 0.0
|
||||
var _pending_distance_km: float = 0.0
|
||||
var _stats_store_timer: float = 0.0
|
||||
|
||||
#how it works:
|
||||
#func _on_match_finished(score: int) -> void:
|
||||
@@ -25,10 +32,20 @@ const KNOWN := [
|
||||
|
||||
func _ready() -> void:
|
||||
if not SteamManager.is_on_steam:
|
||||
set_process(false)
|
||||
return
|
||||
|
||||
Steam.user_stats_stored.connect(_on_stats_stored)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_pending_time_played_seconds += delta
|
||||
_stats_store_timer += delta
|
||||
if _stats_store_timer >= STATS_STORE_INTERVAL_SECONDS:
|
||||
_flush_periodic_stats(false)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
_flush_periodic_stats(true)
|
||||
|
||||
func get_int(stat: String) -> int:
|
||||
if not SteamManager.is_on_steam:
|
||||
return 0
|
||||
@@ -62,6 +79,14 @@ func add_int(stat: String, amount: int = 1) -> void:
|
||||
return
|
||||
set_int(stat, get_int(stat) + amount)
|
||||
|
||||
func add_distance_km(distance_km: float) -> void:
|
||||
if not SteamManager.is_on_steam:
|
||||
return
|
||||
if distance_km <= 0.0:
|
||||
return
|
||||
|
||||
_pending_distance_km += distance_km
|
||||
|
||||
func update_avg_rate(stat: String, count_this_session: float, session_length: float) -> void:
|
||||
if not SteamManager.is_on_steam:
|
||||
return
|
||||
@@ -74,6 +99,32 @@ func store() -> void:
|
||||
if not Steam.storeStats():
|
||||
push_error("storeStats failed; data saved locally")
|
||||
|
||||
#periodic stats incrementation; store data every 60 secs
|
||||
func _flush_periodic_stats(force_store: bool) -> void:
|
||||
if not SteamManager.is_on_steam:
|
||||
_pending_time_played_seconds = 0.0
|
||||
_pending_distance_km = 0.0
|
||||
_stats_store_timer = 0.0
|
||||
return
|
||||
|
||||
var has_changes: bool = false
|
||||
|
||||
var whole_seconds: int = int(floor(_pending_time_played_seconds))
|
||||
if whole_seconds > 0:
|
||||
_pending_time_played_seconds -= float(whole_seconds)
|
||||
add_int(TIME_PLAYED_STAT, whole_seconds)
|
||||
has_changes = true
|
||||
|
||||
var whole_km: int = int(floor(_pending_distance_km))
|
||||
if whole_km > 0:
|
||||
_pending_distance_km -= float(whole_km)
|
||||
add_int(DISTANCE_KM_STAT, whole_km)
|
||||
has_changes = true
|
||||
|
||||
_stats_store_timer = 0.0
|
||||
if has_changes and (force_store or whole_seconds > 0 or whole_km > 0):
|
||||
store()
|
||||
|
||||
#Development only: reset stats
|
||||
func reset_all(include_achievements: bool = false) -> void:
|
||||
if not SteamManager.is_on_steam:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -154,8 +154,6 @@ buffer = PackedFloat32Array(0.09478149, -0.4699982, -0.8787032, -19.27537, -0.99
|
||||
[node name="chunk_railway_station_hero" unique_id=1726311377 node_paths=PackedStringArray("connection_left", "connection_right") groups=["railway_station", "weather_node"] instance=ExtResource("1_c8ljj")]
|
||||
script = ExtResource("9_5ow1i")
|
||||
chunk_type = 1
|
||||
est = true
|
||||
west = true
|
||||
have_lamppost = true
|
||||
connection_left = [NodePath("PaloLuce3/sx"), NodePath("PaloLuce2/sx"), NodePath("PaloLuce/sx")]
|
||||
connection_right = [NodePath("PaloLuce/dx"), NodePath("PaloLuce2/dx"), NodePath("PaloLuce3/dx")]
|
||||
|
||||
84
tgcc/main menu/distant_menu_world.tscn
Normal file
84
tgcc/main menu/distant_menu_world.tscn
Normal file
@@ -0,0 +1,84 @@
|
||||
[gd_scene format=3 uid="uid://c7shevn0javuv"]
|
||||
|
||||
[ext_resource type="Material" uid="uid://blqelpjvdv23j" path="res://tgcc/chunk/material/grassflat_chunk.tres" id="1_grass"]
|
||||
[ext_resource type="Material" uid="uid://c2x7xxweexmj" path="res://tgcc/chunk/material/field.tres" id="2_field"]
|
||||
[ext_resource type="Material" uid="uid://4xhpd6lust7w" path="res://tgcc/chunk/material/path_chunk.tres" id="3_path"]
|
||||
[ext_resource type="Material" uid="uid://cnu38m5dsy4uw" path="res://tgcc/chunk/material/terrain.tres" id="4_terrain"]
|
||||
[ext_resource type="Material" uid="uid://o31kp0jm07q3" path="res://tgcc/chunk/material/rocks.tres" id="7_rocks"]
|
||||
|
||||
[sub_resource type="PlaneMesh" id="PlaneMesh_ground"]
|
||||
size = Vector2(560, 360)
|
||||
|
||||
[sub_resource type="PlaneMesh" id="PlaneMesh_field_large"]
|
||||
size = Vector2(70, 95)
|
||||
|
||||
[sub_resource type="PlaneMesh" id="PlaneMesh_field_small"]
|
||||
size = Vector2(44, 62)
|
||||
|
||||
[sub_resource type="PlaneMesh" id="PlaneMesh_path_long"]
|
||||
size = Vector2(12, 340)
|
||||
|
||||
[sub_resource type="PlaneMesh" id="PlaneMesh_path_short"]
|
||||
size = Vector2(10, 120)
|
||||
|
||||
[sub_resource type="SphereMesh" id="SphereMesh_tree_crown"]
|
||||
radius = 4.0
|
||||
height = 5.0
|
||||
radial_segments = 8
|
||||
rings = 4
|
||||
|
||||
[node name="DistantMenuWorld" type="Node3D" unique_id=448788103]
|
||||
|
||||
[node name="Ground" type="MeshInstance3D" parent="." unique_id=1769769010]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 20, -0.12, 250)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("PlaneMesh_ground")
|
||||
surface_material_override/0 = ExtResource("1_grass")
|
||||
|
||||
[node name="BackDirt" type="MeshInstance3D" parent="." unique_id=441617212]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -155, -0.1, 235)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("PlaneMesh_field_large")
|
||||
surface_material_override/0 = ExtResource("4_terrain")
|
||||
|
||||
[node name="FieldA" type="MeshInstance3D" parent="." unique_id=1168162022]
|
||||
transform = Transform3D(0.9659258, 0, -0.25881904, 0, 1, 0, 0.25881904, 0, 0.9659258, -90, -0.08, 175)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("PlaneMesh_field_large")
|
||||
surface_material_override/0 = ExtResource("2_field")
|
||||
|
||||
[node name="FieldB" type="MeshInstance3D" parent="." unique_id=908374467]
|
||||
transform = Transform3D(0.8660254, 0, 0.5, 0, 1, 0, -0.5, 0, 0.8660254, 105, -0.08, 190)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("PlaneMesh_field_large")
|
||||
surface_material_override/0 = ExtResource("2_field")
|
||||
|
||||
[node name="FieldC" type="MeshInstance3D" parent="." unique_id=725405179]
|
||||
transform = Transform3D(0.9063078, 0, -0.42261827, 0, 1, 0, 0.42261827, 0, 0.9063078, 160, -0.08, 305)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("PlaneMesh_field_small")
|
||||
surface_material_override/0 = ExtResource("2_field")
|
||||
|
||||
[node name="FieldD" type="MeshInstance3D" parent="." unique_id=180749651]
|
||||
transform = Transform3D(0.9848077, 0, 0.17364818, 0, 1, 0, -0.17364818, 0, 0.9848077, -190, -0.08, 320)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("PlaneMesh_field_small")
|
||||
surface_material_override/0 = ExtResource("2_field")
|
||||
|
||||
[node name="DistantRoadMain" type="MeshInstance3D" parent="." unique_id=979021818]
|
||||
transform = Transform3D(0.70710677, 0, -0.70710677, 0, 1, 0, 0.70710677, 0, 0.70710677, 5, -0.06, 240)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("PlaneMesh_path_long")
|
||||
surface_material_override/0 = ExtResource("3_path")
|
||||
|
||||
[node name="DistantRoadCross" type="MeshInstance3D" parent="." unique_id=1994181739]
|
||||
transform = Transform3D(0.25881904, 0, 0.9659258, 0, 1, 0, -0.9659258, 0, 0.25881904, -20, -0.05, 220)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("PlaneMesh_path_short")
|
||||
surface_material_override/0 = ExtResource("3_path")
|
||||
|
||||
[node name="RockPatch" type="MeshInstance3D" parent="." unique_id=12563390]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.35, 0, 0, 0, 0.65, -15, 0.6, 350)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("SphereMesh_tree_crown")
|
||||
surface_material_override/0 = ExtResource("7_rocks")
|
||||
@@ -1,4 +1,84 @@
|
||||
extends Node3D
|
||||
|
||||
@export var play_departure_speed: float = -6.0
|
||||
@export var play_departure_delay_seconds: float = 1.0
|
||||
@export var play_move_before_transition_seconds: float = 3.0
|
||||
@export var menu_train_whistle_volume_db: float = -12.0
|
||||
@export var prewarm_chunks_before_show: bool = true
|
||||
|
||||
var _play_departure_started: bool = false
|
||||
|
||||
@onready var _rail_path: Node = $rail
|
||||
@onready var _biome_generator: Node = $biome_generator
|
||||
@onready var _environment_manager: Node = $EnvironmentManager
|
||||
@onready var _option_menu: Control = $MainMenuUI/SubViewport/OptionMenu
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
||||
if prewarm_chunks_before_show:
|
||||
visible = false
|
||||
await get_tree().physics_frame
|
||||
if not is_inside_tree():
|
||||
return
|
||||
if _biome_generator != null and _biome_generator.has_method("generate_initial_world_now"):
|
||||
_biome_generator.generate_initial_world_now()
|
||||
if _environment_manager != null and _environment_manager.has_method("flush_pending_environment_nodes"):
|
||||
_environment_manager.flush_pending_environment_nodes()
|
||||
visible = true
|
||||
|
||||
_stop_menu_train()
|
||||
_configure_menu_train_whistle_volume()
|
||||
_configure_play_transition_delay()
|
||||
_connect_play_button()
|
||||
|
||||
func _configure_menu_train_whistle_volume() -> void:
|
||||
if _rail_path == null or not "train_instance" in _rail_path:
|
||||
return
|
||||
|
||||
var train_instance := _rail_path.get("train_instance") as Node3D
|
||||
if train_instance == null:
|
||||
return
|
||||
|
||||
if "whistle_volume_db" in train_instance:
|
||||
train_instance.set("whistle_volume_db", menu_train_whistle_volume_db)
|
||||
|
||||
var whistle_player := train_instance.get_node_or_null("TrainWhistlePlayer") as AudioStreamPlayer3D
|
||||
if whistle_player != null:
|
||||
whistle_player.volume_db = menu_train_whistle_volume_db
|
||||
|
||||
func _configure_play_transition_delay() -> void:
|
||||
if _option_menu == null or not "fade_time" in _option_menu:
|
||||
return
|
||||
|
||||
_option_menu.set("fade_time", play_departure_delay_seconds + play_move_before_transition_seconds)
|
||||
|
||||
func _stop_menu_train() -> void:
|
||||
if _rail_path == null:
|
||||
return
|
||||
|
||||
_rail_path.set("is_inmotion", false)
|
||||
_rail_path.set("stop_multiply", 1.0)
|
||||
|
||||
func _connect_play_button() -> void:
|
||||
if _option_menu == null or not "play_button" in _option_menu:
|
||||
return
|
||||
|
||||
var play_button := _option_menu.get("play_button") as Button
|
||||
if play_button == null:
|
||||
return
|
||||
if not play_button.pressed.is_connected(_on_play_button_pressed):
|
||||
play_button.pressed.connect(_on_play_button_pressed)
|
||||
|
||||
func _on_play_button_pressed() -> void:
|
||||
if _rail_path == null or _play_departure_started:
|
||||
return
|
||||
|
||||
_play_departure_started = true
|
||||
await get_tree().create_timer(play_departure_delay_seconds).timeout
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
_rail_path.set("train_speed", play_departure_speed)
|
||||
_rail_path.set("stop_multiply", 1.0)
|
||||
_rail_path.set("is_restarting", false)
|
||||
_rail_path.set("stop_ongoing", false)
|
||||
_rail_path.set("is_inmotion", true)
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
[ext_resource type="Shader" uid="uid://bhqtjjs3emv7f" path="res://tgcc/main menu/godraysmenu.gdshader" id="103_g1vc5"]
|
||||
[ext_resource type="PackedScene" uid="uid://cp2vmysawnuql" path="res://tgcc/chunk/prop/sign/scene/signMenu.tscn" id="103_mrhmq"]
|
||||
[ext_resource type="Material" uid="uid://dljvvppsdbjd1" path="res://tgcc/chunk/material/glowplane.tres" id="104_mrhmq"]
|
||||
[ext_resource type="PackedScene" path="res://tgcc/main menu/distant_menu_world.tscn" id="105_background"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_00nq7"]
|
||||
render_priority = 0
|
||||
@@ -176,13 +177,12 @@ metadata/_custom_type_script = "uid://brcimd12tx3dm"
|
||||
compositor_effects = Array[CompositorEffect]([SubResource("CompositorEffect_q52t1")])
|
||||
|
||||
[sub_resource type="Curve3D" id="Curve3D_263ax"]
|
||||
closed = true
|
||||
bake_interval = 50.0
|
||||
_data = {
|
||||
"points": PackedVector3Array(0, 0, 0, 0, 0, 0, 3.5, 0, 0, 0, 0, 0, 0, 0, 0, 3.5, 0, 100),
|
||||
"tilts": PackedFloat32Array(0, 0)
|
||||
"points": PackedVector3Array(0, 0, 0, 0, 0, 0, 3.0078363, -1.8119812e-05, -12.587097, 0, 0, 0, 0, 0, 0, 3.360159, -1.8119812e-05, -79.54889, 0, 0, 0, 0, 0, 0, 2.826539, -1.8119812e-05, -159.47318, 0, 0, 0, 0, 0, 0, 3.0367248, -1.8119812e-05, -269.20667),
|
||||
"tilts": PackedFloat32Array(0, 0, 0, 0)
|
||||
}
|
||||
point_count = 2
|
||||
point_count = 4
|
||||
|
||||
[sub_resource type="PlaneMesh" id="PlaneMesh_00nq7"]
|
||||
|
||||
@@ -284,6 +284,8 @@ size = Vector2(600, 300)
|
||||
[node name="MainMenu" type="Node3D" unique_id=448381304]
|
||||
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0, 0)
|
||||
script = ExtResource("1_4j5xl")
|
||||
play_departure_speed = 6.0
|
||||
menu_train_whistle_volume_db = -14.0
|
||||
|
||||
[node name="EnvironmentManager" parent="." unique_id=1611939731 instance=ExtResource("1_2o8pc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.822516, 0, 1.9254212)
|
||||
@@ -311,20 +313,29 @@ directional_shadow_mode = 0
|
||||
directional_shadow_max_distance = 200.0
|
||||
|
||||
[node name="rail" type="Path3D" parent="." unique_id=28337754]
|
||||
transform = Transform3D(1.3113416e-07, 0, 1, 0, 1, 0, -1, 0, 1.3113416e-07, -47.25185, 1.7218199, 3.4257338)
|
||||
transform = Transform3D(1.3113416e-07, 0, 1, 0, 1, 0, -1, 0, 1.3113416e-07, 114.74815, 1.7218199, 3.4257479)
|
||||
curve = SubResource("Curve3D_263ax")
|
||||
script = ExtResource("7_5p27e")
|
||||
train_model = ExtResource("8_ljvyb")
|
||||
wagon_pool = ExtResource("9_wcae3")
|
||||
wagon_count = 5
|
||||
wagon_gap = 0.8
|
||||
wagon_random_number = false
|
||||
wagon_count = 4
|
||||
wagon_gap = 0.6
|
||||
wagon_spacing_scale = 0.9
|
||||
train_start_mode = 2
|
||||
train_start_stop_index = 0
|
||||
train_start_position = 87.0
|
||||
train_start_after_delay = false
|
||||
train_facing_direction = 2
|
||||
sleepers_model = ExtResource("10_00nq7")
|
||||
fireworks_scene = ExtResource("11_aylkj")
|
||||
|
||||
[node name="MainMap" parent="." unique_id=1895741703 instance=ExtResource("2_wbrya")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 63.069893, 0, -4.743943e-06)
|
||||
|
||||
[node name="DistantMenuWorld" parent="." unique_id=1852904287 instance=ExtResource("105_background")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -117.95)
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1523044435]
|
||||
transform = Transform3D(-80.07708, -1.0239479e-05, 4.277144e-13, 0, -2.9642006e-06, -64.80289, 1.2091303e-05, -67.813, 2.832624e-06, 308.45245, 59.279312, 332.5165)
|
||||
cast_shadow = 0
|
||||
@@ -355,9 +366,10 @@ railway_pool = SubResource("Resource_jrt8d")
|
||||
biome_list = Array[ExtResource("15_3blen")]([SubResource("Resource_n3osn")])
|
||||
entity_pool = ExtResource("78_xg6nk")
|
||||
entity_spawn_probability = 0.0
|
||||
max_entities_per_chunk = 5
|
||||
eye_line = 8
|
||||
max_entities_per_chunk = 0
|
||||
eye_line = 7
|
||||
lamppost_max_wire_distance = 0.0
|
||||
lamppost_rail_clearance_distance = 3.0
|
||||
|
||||
[node name="Fuji2" parent="." unique_id=842328745 instance=ExtResource("99_61psf")]
|
||||
transform = Transform3D(0.09337742, 0, 0.9715159, 0, 0.5685182, 0, -0.3828171, 0, 0.23697394, -236.37123, -4.8100815, 216.00096)
|
||||
@@ -368,14 +380,14 @@ transform = Transform3D(-0.09359112, 0, 0.9713834, 0, 0.56225973, 0, -0.38276488
|
||||
[node name="Fuji4" parent="." unique_id=930179001 instance=ExtResource("99_61psf")]
|
||||
transform = Transform3D(-0.09359112, 0, 0.9713834, 0, 0.56225973, 0, -0.38276488, 0, -0.23751618, 239.46135, 1.7057877, 224.6928)
|
||||
|
||||
[node name="Landscape" parent="Fuji4" index="0" unique_id=1897614485]
|
||||
[node name="Landscape" parent="Fuji4" index="0" unique_id=581950846]
|
||||
transform = Transform3D(28.638817, 1.7053026e-13, -7.6293945e-06, 0, 13.6219845, 2.1621709e-06, 0, -1.0284306e-06, 28.638834, -10.786804, 5.0069, 17.382736)
|
||||
surface_material_override/0 = SubResource("ShaderMaterial_g1vc5")
|
||||
|
||||
[node name="Fuji5" parent="." unique_id=1070037082 instance=ExtResource("99_61psf")]
|
||||
transform = Transform3D(0.0935979, 0, 0.9713791, 0, 0.56225973, 0, -0.38276324, 0, 0.23753339, -275.95566, -3.4616928, 232.02234)
|
||||
|
||||
[node name="Landscape" parent="Fuji5" index="0" unique_id=1897614485]
|
||||
[node name="Landscape" parent="Fuji5" index="0" unique_id=581950846]
|
||||
transform = Transform3D(28.638819, 1.7053026e-13, -7.6293945e-06, 0, 13.6219845, 2.1621709e-06, 0, -1.0284306e-06, 28.638836, -10.786743, 6.703195, 17.382767)
|
||||
surface_material_override/0 = SubResource("ShaderMaterial_g1vc5")
|
||||
|
||||
@@ -385,7 +397,7 @@ transform = Transform3D(-4.88843, -0.020050459, 0.86837775, -0.010355438, 4.9646
|
||||
[node name="Fuji6" parent="." unique_id=150126281 instance=ExtResource("100_0aqpp")]
|
||||
transform = Transform3D(-5.287449, -0.020231497, 0.22351237, -0.011200703, 5.009496, 0.014500697, -0.9394429, 0.05414178, -1.2581636, 32.446022, 15.848213, 525.54346)
|
||||
|
||||
[node name="Mt Fuji_simplified_3d_mesh" parent="Fuji6" index="0" unique_id=206531083]
|
||||
[node name="Mt Fuji_simplified_3d_mesh" parent="Fuji6" index="0" unique_id=65404928]
|
||||
transform = Transform3D(0.5151193, -0.011206273, -0.07264297, 0.077138014, 0.07483422, 0.48510167, 3.5762787e-07, -0.4938935, 0.07515043, 6.299696, 12.244916, 3.004303)
|
||||
surface_material_override/0 = SubResource("ShaderMaterial_vvb5q")
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ shader_parameter/sun_color = Color(0.2841828, 0.2023238, 0.27953526, 1)
|
||||
script = ExtResource("3_vg0pk")
|
||||
start_time = 0.5
|
||||
start_day_time_paused = true
|
||||
show_day_time_debug = false
|
||||
morning_color = Color(0.93333334, 0.78039217, 0.6039216, 1)
|
||||
afternoon_color = Color(0.9490196, 0.62352943, 0.38039216, 1)
|
||||
night_color = Color(0.18431373, 0.18431373, 0.4862745, 1)
|
||||
@@ -85,9 +86,6 @@ godray_spawn_radius = 100.0
|
||||
godray_spawn_offset = Vector3(20, 80, 20)
|
||||
godray_rotation_degrees = Vector3(50, 30, 0)
|
||||
godray_scale = Vector3(2, 25, 50)
|
||||
train_start_from_random_position = 2
|
||||
train_start_position = 99.0
|
||||
train_start_after_delay = false
|
||||
snow_amount = 2000.0
|
||||
wind_amount = 50
|
||||
cloud_speed = 0.01
|
||||
|
||||
@@ -190,7 +190,7 @@ data = PackedByteArray("//vQZAAOhpd2PgtGR7JsJPfgYSYSJ6387g9rgIGKF+EZgxxoAM4TYIWm
|
||||
closed = true
|
||||
bake_interval = 60.0
|
||||
_data = {
|
||||
"points": PackedVector3Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, -4.372, 0, 140, 0, 0, 0, 0, 0, 0, -20.963, 0, 160, 0, 0, 0, 0, 0, 0, -40.62518, 0, 168.52754, 0, 0, 0, 0, 0, 0, -60, 0, 170, 0, 0, 0, 0, 0, 0, -80, 0, 170, 0, 0, 0, 0, 0, 0, -100, 0, 170, 0, 0, 0, 0, 0, 0, -120.42937, 0, 171.50279, 0, 0, 0, 0, 0, 0, -141.41747, 7.6293945e-06, 180.5982, 0, 0, 0, 0, 0, 0, -155.77005, 7.6293945e-06, 198.35184, 0, 0, 0, 0, 0, 0, -160.14105, 7.6293945e-06, 219.80951, 0, 0, 0, 0, 0, 0, -163.99007, 7.6293945e-06, 239.36005, 0, 0, 0, 0, 0, 0, -175.77509, 7.6293945e-06, 256.16028, 0, 0, 0, 0, 0, 0, -195.62283, 7.6293945e-06, 267.46912, 0, 0, 0, 0, 0, 0, -219.9999, 7.6293945e-06, 270.11758, 0, 0, 0, 0, 0, 0, -240, 0, 270.118, 0, 0, 0, 0, 0, 0, -260, 0, 270.118, 0, 0, 0, 0, 0, 0, -275.19666, 7.6293945e-06, 270.55228, 0, 0, 0, 0, 0, 0, -294.2925, 7.6293945e-06, 276.05307, 0, 0, 0, 0, 0, 0, -307.19006, 7.6293945e-06, 286.9095, 0, 0, 0, 0, 0, 0, -317.32443, 7.6293945e-06, 303.14682, 0, 0, 0, 0, 0, 0, -320, 0, 320, 0, 0, 0, 0, 0, 0, -320, 0, 340, 0, 0, 0, 0, 0, 0, -320, 0, 360, 0, 0, 0, 0, 0, 0, -320, 0, 380, 0, 0, 0, 0, 0, 0, -316.78876, 7.6293945e-06, 396.91193, 0, 0, 0, 0, 0, 0, -307.68207, 7.6293945e-06, 412.70596, 0, 0, 0, 0, 0, 0, -291.08273, 7.6293945e-06, 424.7491, 0, 0, 0, 0, 0, 0, -270, 0, 429.994, 0, 0, 0, 0, 0, 0, -250, 0, 429.994, 0, 0, 0, 0, 0, 0, -229.90146, 7.6293945e-06, 433.90823, 0, 0, 0, 0, 0, 0, -217.15442, 7.6293945e-06, 442.98242, 0, 0, 0, 0, 0, 0, -204.60803, 7.6293945e-06, 459.93704, 0, 0, 0, 0, 0, 0, -200, 0, 480, 0, 0, 0, 0, 0, 0, -200, 0, 500, 0, 0, 0, 0, 0, 0, -195.5338, 7.6293945e-06, 520.0368, 0, 0, 0, 0, 0, 0, -184.9173, 7.6293945e-06, 535.07776, 0, 0, 0, 0, 0, 0, -170.36423, 7.6293945e-06, 545.0799, 0, 0, 0, 0, 0, 0, -150.56184, 0, 549.8127, 0, 0, 0, 0, 0, 0, -130, 0, 550, 0, 0, 0, 0, 0, 0, -110, 0, 550, 0, 0, 0, 0, 0, 0, -90, 0, 550, 0, 0, 0, 0, 0, 0, -70, 0, 550, 0, 0, 0, 0, 0, 0, -50, 0, 550, 0, 0, 0, 0, 0, 0, -29.862535, 7.6293945e-06, 545.3371, 0, 0, 0, 0, 0, 0, -13.215244, 7.6293945e-06, 533.0626, 0, 0, 0, 0, 0, 0, -4.098665, 7.6293945e-06, 519.6814, 0, 0, 0, 0, 0, 0, -0.048213482, 7.6293945e-06, 500.107, 0, 0, 0, 0, 0, 0, 3.7920675, 7.6293945e-06, 480.64902, 0, 0, 0, 0, 0, 0, 13.513225, 7.6293945e-06, 465.9133, 0, 0, 0, 0, 0, 0, 29.76715, 7.6293945e-06, 454.41486, 0, 0, 0, 0, 0, 0, 49.711926, 7.6293945e-06, 450.12488, 0, 0, 0, 0, 0, 0, 70, 0, 450, 0, 0, 0, 0, 0, 0, 90, 0, 450, 0, 0, 0, 0, 0, 0, 110.055405, 7.6293945e-06, 445.42627, 0, 0, 0, 0, 0, 0, 126.975784, 7.6293945e-06, 432.8683, 0, 0, 0, 0, 0, 0, 136.95703, 7.6293945e-06, 417.5506, 0, 0, 0, 0, 0, 0, 140, 0, 400, 0, 0, 0, 0, 0, 0, 140, 0, 380, 0, 0, 0, 0, 0, 0, 140, 0, 360, 0, 0, 0, 0, 0, 0, 140, 0, 340, 0, 0, 0, 0, 0, 0, 140, 0, 320, 0, 0, 0, 0, 0, 0, 140, 0, 300, 0, 0, 0, 0, 0, 0, 140, 0, 280, 0, 0, 0, 0, 0, 0, 140, 0, 260, 0, 0, 0, 0, 0, 0, 140, 0, 240, 0, 0, 0, 0, 0, 0, 140, 0, 220, 0, 0, 0, 0, 0, 0, 140, 0, 200, 0, 0, 0, 0, 0, 0, 140, 0, 180, 0, 0, 0, 0, 0, 0, 140, 0, 160, 0, 0, 0, 0, 0, 0, 140, 0, 140, 0, 0, 0, 0, 0, 0, 140, 0, 120, 0, 0, 0, 0, 0, 0, 140, 0, 100, 0, 0, 0, 0, 0, 0, 140, 0, 80, 0, 0, 0, 0, 0, 0, 140, 0, 60, 0, 0, 0, 0, 0, 0, 140, 0, 40, 0, 0, 0, 0, 0, 0, 140.26848, 0, 20, 0, 0, 0, 0, 0, 0, 140, 0, 0, 0, 0, 0, 0, 0, 0, 136.2151, 7.6293945e-06, -18.930761, 0, 0, 0, 0, 0, 0, 124.47424, 7.6293945e-06, -35.771675, 0, 0, 0, 0, 0, 0, 109.556854, 7.6293945e-06, -45.911987, 0, 0, 0, 0, 0, 0, 90, 0, -50, 0, 0, 0, 0, 0, 0, 70, 0, -50, 0, 0, 0, 0, 0, 0, 50, 0, -50, 0, 0, 0, 0, 0, 0, 30.626865, 7.6293945e-06, -46.35706, 0, 0, 0, 0, 0, 0, 14.519331, 7.6293945e-06, -35.468895, 0, 0, 0, 0, 0, 0, 3.52941, 7.6293945e-06, -19.590664),
|
||||
"points": PackedVector3Array(0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, -4.1409535, 0, 140, 0, 0, 0, 0, 0, 0, -20.385382, 0, 160.4621, 0, 0, 0, 0, 0, 0, -40.62518, 0, 168.52754, 0, 0, 0, 0, 0, 0, -60, 0, 170, 0, 0, 0, 0, 0, 0, -80, 0, 170, 0, 0, 0, 0, 0, 0, -100, 0, 170, 0, 0, 0, 0, 0, 0, -120.42937, 0, 171.50279, 0, 0, 0, 0, 0, 0, -141.41747, 7.6293945e-06, 180.5982, 0, 0, 0, 0, 0, 0, -155.77005, 7.6293945e-06, 198.35184, 0, 0, 0, 0, 0, 0, -160.14105, 7.6293945e-06, 219.80951, 0, 0, 0, 0, 0, 0, -163.99007, 7.6293945e-06, 239.36005, 0, 0, 0, 0, 0, 0, -175.77509, 7.6293945e-06, 256.16028, 0, 0, 0, 0, 0, 0, -195.62283, 7.6293945e-06, 267.46912, 0, 0, 0, 0, 0, 0, -219.9999, 7.6293945e-06, 270.11758, 0, 0, 0, 0, 0, 0, -240, 0, 270.118, 0, 0, 0, 0, 0, 0, -260, 0, 270.118, 0, 0, 0, 0, 0, 0, -275.19666, 7.6293945e-06, 270.55228, 0, 0, 0, 0, 0, 0, -294.2925, 7.6293945e-06, 276.05307, 0, 0, 0, 0, 0, 0, -307.19006, 7.6293945e-06, 286.9095, 0, 0, 0, 0, 0, 0, -317.32443, 7.6293945e-06, 303.14682, 0, 0, 0, 0, 0, 0, -320, 0, 320, 0, 0, 0, 0, 0, 0, -320, 0, 340, 0, 0, 0, 0, 0, 0, -320, 0, 360, 0, 0, 0, 0, 0, 0, -320, 0, 380, 0, 0, 0, 0, 0, 0, -316.78876, 7.6293945e-06, 396.91193, 0, 0, 0, 0, 0, 0, -307.68207, 7.6293945e-06, 412.70596, 0, 0, 0, 0, 0, 0, -291.08273, 7.6293945e-06, 424.7491, 0, 0, 0, 0, 0, 0, -270, 0, 429.994, 0, 0, 0, 0, 0, 0, -250, 0, 429.994, 0, 0, 0, 0, 0, 0, -229.90146, 7.6293945e-06, 433.90823, 0, 0, 0, 0, 0, 0, -217.15442, 7.6293945e-06, 442.98242, 0, 0, 0, 0, 0, 0, -203.97934, 7.6293945e-06, 459.7799, 0, 0, 0, 0, 0, 0, -200, 0, 480, 0, 0, 0, 0, 0, 0, -200, 0, 500, 0, 0, 0, 0, 0, 0, -195.5338, 7.6293945e-06, 520.0368, 0, 0, 0, 0, 0, 0, -184.9173, 7.6293945e-06, 535.07776, 0, 0, 0, 0, 0, 0, -170.36423, 7.6293945e-06, 545.0799, 0, 0, 0, 0, 0, 0, -150.56184, 0, 549.8127, 0, 0, 0, 0, 0, 0, -130, 0, 550, 0, 0, 0, 0, 0, 0, -110, 0, 550, 0, 0, 0, 0, 0, 0, -90, 0, 550, 0, 0, 0, 0, 0, 0, -70, 0, 550, 0, 0, 0, 0, 0, 0, -50, 0, 550, 0, 0, 0, 0, 0, 0, -29.862535, 7.6293945e-06, 545.3371, 0, 0, 0, 0, 0, 0, -12.945752, 7.6293945e-06, 534.1406, 0, 0, 0, 0, 0, 0, -4.098665, 7.6293945e-06, 519.6814, 0, 0, 0, 0, 0, 0, -0.048213482, 7.6293945e-06, 500.107, 0, 0, 0, 0, 0, 0, 3.7920675, 7.6293945e-06, 480.64902, 0, 0, 0, 0, 0, 0, 13.513225, 7.6293945e-06, 465.9133, 0, 0, 0, 0, 0, 0, 29.76715, 7.6293945e-06, 454.41486, 0, 0, 0, 0, 0, 0, 49.711926, 7.6293945e-06, 450.12488, 0, 0, 0, 0, 0, 0, 70, 0, 450, 0, 0, 0, 0, 0, 0, 90, 0, 450, 0, 0, 0, 0, 0, 0, 110.055405, 7.6293945e-06, 445.42627, 0, 0, 0, 0, 0, 0, 127.27293, 7.6293945e-06, 433.46255, 0, 0, 0, 0, 0, 0, 136.95703, 7.6293945e-06, 417.5506, 0, 0, 0, 0, 0, 0, 140, 0, 400, 0, 0, 0, 0, 0, 0, 140, 0, 380, 0, 0, 0, 0, 0, 0, 140, 0, 360, 0, 0, 0, 0, 0, 0, 140, 0, 340, 0, 0, 0, 0, 0, 0, 140, 0, 320, 0, 0, 0, 0, 0, 0, 140, 0, 300, 0, 0, 0, 0, 0, 0, 140, 0, 280, 0, 0, 0, 0, 0, 0, 140, 0, 260, 0, 0, 0, 0, 0, 0, 140, 0, 240, 0, 0, 0, 0, 0, 0, 140, 0, 220, 0, 0, 0, 0, 0, 0, 140, 0, 200, 0, 0, 0, 0, 0, 0, 140, 0, 180, 0, 0, 0, 0, 0, 0, 140, 0, 160, 0, 0, 0, 0, 0, 0, 140, 0, 140, 0, 0, 0, 0, 0, 0, 140, 0, 120, 0, 0, 0, 0, 0, 0, 140, 0, 100, 0, 0, 0, 0, 0, 0, 140, 0, 80, 0, 0, 0, 0, 0, 0, 140, 0, 60, 0, 0, 0, 0, 0, 0, 140, 0, 40, 0, 0, 0, 0, 0, 0, 140.26848, 0, 20, 0, 0, 0, 0, 0, 0, 140, 0, 0, 0, 0, 0, 0, 0, 0, 136.2151, 7.6293945e-06, -18.930761, 0, 0, 0, 0, 0, 0, 124.47424, 7.6293945e-06, -35.771675, 0, 0, 0, 0, 0, 0, 109.556854, 7.6293945e-06, -45.911987, 0, 0, 0, 0, 0, 0, 90, 0, -50, 0, 0, 0, 0, 0, 0, 70, 0, -50, 0, 0, 0, 0, 0, 0, 50, 0, -50, 0, 0, 0, 0, 0, 0, 30.626865, 7.6293945e-06, -46.35706, 0, 0, 0, 0, 0, 0, 14.519331, 7.6293945e-06, -35.468895, 0, 0, 0, 0, 0, 0, 3.5, 0, -17.9, 0, 0, 0, 0, 0, 0, 0.2006703, 0, 0.20440674, 0, 0, 0, 0, 0, 0, 0.06303978, 0, 20.431122),
|
||||
"tilts": PackedFloat32Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
}
|
||||
point_count = 93
|
||||
@@ -205,9 +205,9 @@ compositor = SubResource("Compositor_mb5yv")
|
||||
rail_path = NodePath("../rail")
|
||||
biome_list = Array[ExtResource("6_jfe74")]([SubResource("Resource_3qrd0")])
|
||||
entity_pool = ExtResource("34_a2cst")
|
||||
entity_spawn_probability = 1.0
|
||||
entity_spawn_probability = 0.4
|
||||
max_entities_per_chunk = 5
|
||||
eye_line = 4
|
||||
eye_line = 7
|
||||
|
||||
[node name="Terrain" type="MeshInstance3D" parent="." unique_id=219178561]
|
||||
visible = false
|
||||
@@ -253,18 +253,6 @@ cameras = NodePath("../cameras")
|
||||
sleepers_model = ExtResource("46_lbmv2")
|
||||
fireworks_scene = ExtResource("47_q7p65")
|
||||
|
||||
[node name="Marker3D" type="Marker3D" parent="rail" unique_id=1460318018]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.7581053, -1.6046681, 59.658325)
|
||||
|
||||
[node name="Marker3D2" type="Marker3D" parent="rail" unique_id=131965864]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 127.14737, 0, 169.44846)
|
||||
|
||||
[node name="Marker3D3" type="Marker3D" parent="rail" unique_id=492991547]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -91.191925, 0, 539.03174)
|
||||
|
||||
[node name="Marker3D4" type="Marker3D" parent="rail" unique_id=1550097358]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -312.19193, 0, 349.03174)
|
||||
|
||||
[node name="Control" type="Control" parent="." unique_id=630650980]
|
||||
visible = false
|
||||
layout_mode = 3
|
||||
|
||||
@@ -64,9 +64,9 @@ grad_intensity_night = 0.5
|
||||
fog_color_morning = Color(0.7490196, 0.8509804, 0.9490196, 1)
|
||||
fog_color_afternoon = Color(0.9823975, 0.8034449, 0.8778439, 1)
|
||||
fog_color_night = Color(0.18375358, 0.16627002, 0.3588226, 1)
|
||||
fog_density_morning = 0.01
|
||||
fog_density_afternoon = 0.01
|
||||
fog_density_night = 0.03
|
||||
fog_density_morning = 0.02
|
||||
fog_density_afternoon = 0.02
|
||||
fog_density_night = 0.02
|
||||
glow_morning = 0.4
|
||||
glow_night = 0.8
|
||||
enable_fog = true
|
||||
@@ -78,8 +78,8 @@ lightning_min = 2
|
||||
lightning_max = 4
|
||||
lightning_scale_min = 2.0
|
||||
lightning_scale_max = 5.0
|
||||
godray_max_rain = 30
|
||||
godray_spawn_radius = 100.0
|
||||
godray_max_rain = 15
|
||||
godray_spawn_radius = 200.0
|
||||
godray_spawn_offset = Vector3(20, 80, 20)
|
||||
godray_rotation_degrees = Vector3(50, 30, 0)
|
||||
godray_scale = Vector3(2, 25, 50)
|
||||
|
||||
Reference in New Issue
Block a user