Files
Trinittu/scripts/train.gd
2026-02-17 10:03:32 +01:00

249 lines
6.4 KiB
GDScript

extends Node3D
class_name Train
signal speed_changed(new_speed: float)
@export_group("Train")
@export var base_speed: float = 15.0
@export var max_speed: float = 50.0
@export var min_speed: float = 3.0
@export var acceleration: float = 12.0
@export var wagon_spacing: float = 5.5
var num_wagons: int
var group_name: String = ""
var locomotive_model_resources: Array[String] = [
"train-diesel-a.obj",
"train-diesel-b.obj",
"train-diesel-box-a.obj",
"train-diesel-c.obj"
]
var wagon_model_resources: Array[String] = [
"train-carriage-container-blue.obj",
"train-carriage-flatbed-wood.obj",
"train-carriage-lumber.obj",
"train-carriage-tank-large.obj",
"train-diesel-box-b.obj",
"train-diesel-box-c.obj"
]
# Riferimento al generatore (settato da main.gd)
var track_generator: TrackGenerator
# Stato
var current_speed: float = 15.0
var distance_traveled: float = 0.0
var locomotive: Node3D
var wagons: Array = []
var wagon_distances: Array = []
var _horn_player: AudioStreamPlayer
var _horn_playing: bool = false
func _ready() -> void:
# Aspetta un frame per assicurarsi che track_generator sia settato
await get_tree().process_frame
await get_tree().process_frame
_create_train()
current_speed = base_speed
_create_horn()
func _remove_train():
for node in get_tree().get_nodes_in_group(group_name):
node.queue_free()
func _create_train() -> void:
_remove_train()
_create_locomotive()
wagons = []
wagon_distances = []
num_wagons = randi_range(1, 10)
_create_wagons()
func _create_locomotive() -> void:
var locomotive_model = locomotive_model_resources.pick_random()
locomotive = load_model("res://resources/train kit/" + locomotive_model, "Locomotive")
locomotive.transform.rotated(Vector3(1, 0, 0), 180.0)
group_name = "locomotive" + str(Time.get_unix_time_from_system())
locomotive.add_to_group(group_name)
add_child(locomotive)
func _create_wagons() -> void:
var loco_length = 0
for child in locomotive.get_children():
if child is MeshInstance3D:
var aabb = child.get_aabb()
loco_length = aabb.size.z
for i in range(num_wagons):
var wagon_model = wagon_model_resources.pick_random()
var wagon = load_model("res://resources/train kit/" + wagon_model, "Wagon_%d" % i)
wagon.transform.rotated(Vector3(1, 0, 0), 180.0)
wagon.add_to_group(group_name)
add_child(wagon)
wagons.append(wagon)
wagon_distances.append(loco_length if num_wagons == 1 else 0 * 0.5 + (i + 1) * wagon_spacing)
_position_wagons_initial()
func _position_wagons_initial() -> void:
# Posiziona i vagoni sul tracciato all'avvio
if not track_generator:
return
for i in range(wagons.size()):
var wagon_dist = distance_traveled - wagon_distances[i]
# All'inizio i vagoni potrebbero essere "prima" della partenza
# quindi li posizioniamo dove possibile
if wagon_dist < 0:
wagon_dist = 0
var wagon_data = track_generator.get_position_on_track(wagon_dist)
if wagon_data["valid"]:
_apply_transform(wagons[i], wagon_data["position"], wagon_data["direction"])
func load_model(path: String, node_name: String) -> Node3D:
var mesh = OBJLoader.load_obj(path)
if mesh == null:
return null
# Crea un nodo contenitore
var newobj = Node3D.new()
newobj.name = node_name
# Crea il mesh instance come figlio
var mesh_instance = MeshInstance3D.new()
mesh_instance.mesh = mesh
newobj.add_child(mesh_instance)
return newobj
func _process(delta: float) -> void:
if not track_generator or not locomotive:
return
_handle_input(delta)
_update_movement(delta)
_update_track()
func _handle_input(delta: float) -> void:
if Input.is_action_pressed("speed_up"):
current_speed = min(current_speed + acceleration * delta, max_speed)
speed_changed.emit(current_speed)
elif Input.is_action_pressed("speed_down"):
current_speed = max(current_speed - acceleration * delta, min_speed)
speed_changed.emit(current_speed)
elif Input.is_action_pressed("change_train"):
_create_train()
current_speed = base_speed
if Input.is_action_just_pressed("horn") and not _horn_playing:
_play_horn()
func _update_movement(delta: float) -> void:
distance_traveled += current_speed * delta
# Locomotiva
var loco_data = track_generator.get_position_on_track(distance_traveled)
if loco_data["valid"]:
_apply_transform(locomotive, loco_data["position"], loco_data["direction"])
# Vagoni
for i in range(wagons.size()):
var wagon_dist = distance_traveled - wagon_distances[i]
if wagon_dist < 0:
wagon_dist = 0
var wagon_data = track_generator.get_position_on_track(wagon_dist)
if wagon_data["valid"]:
_apply_transform(wagons[i], wagon_data["position"], wagon_data["direction"])
func _apply_transform(node: Node3D, pos: Vector3, direction: Vector3) -> void:
node.global_position = pos
if direction.length_squared() > 0.001:
var target = pos - direction
var up = Vector3.UP
if abs(direction.dot(up)) > 0.99:
up = Vector3.RIGHT
node.look_at(target, up)
func _update_track() -> void:
track_generator.update_track(distance_traveled)
func get_locomotive_position() -> Vector3:
if locomotive:
return locomotive.global_position
return Vector3.ZERO
func get_speed() -> float:
return current_speed
func _create_horn() -> void:
_horn_player = AudioStreamPlayer.new()
_horn_player.name = "HornPlayer"
_horn_player.volume_db = -6.0
_horn_player.bus = "Master"
add_child(_horn_player)
var sample_rate: int = 22050
var duration: float = 1.8
var num_samples: int = int(sample_rate * duration)
var fade_in: float = 0.15
var fade_out: float = 0.4
var freqs: Array[float] = [277.18, 369.99, 311.13]
var data := PackedByteArray()
data.resize(num_samples * 2)
for i in range(num_samples):
var t: float = float(i) / sample_rate
var sample: float = 0.0
for freq in freqs:
sample += sin(TAU * freq * t)
sample /= freqs.size()
var envelope: float = 1.0
if t < fade_in:
envelope = t / fade_in
elif t > duration - fade_out:
envelope = (duration - t) / fade_out
sample *= envelope
var value: int = clampi(int(sample * 16000), -32768, 32767)
data[i * 2] = value & 0xFF
data[i * 2 + 1] = (value >> 8) & 0xFF
var stream := AudioStreamWAV.new()
stream.format = AudioStreamWAV.FORMAT_16_BITS
stream.mix_rate = sample_rate
stream.data = data
_horn_player.stream = stream
func _play_horn() -> void:
_horn_playing = true
_horn_player.play()
_horn_player.finished.connect(_on_horn_finished, CONNECT_ONE_SHOT)
func _on_horn_finished() -> void:
_horn_playing = false