Validate POC A and POC B foundations

This commit is contained in:
chindit committed 2026-08-07 13:31:21 +02:00
1 parent 44fd4b4b43
commit c38e09a663
47 files changed
+4990 -35

No files matched your search

+394
View File
@@ -0,0 +1,394 @@
extends Node3D
const MISSION_POINTS := [
{
"title": "Rejoignez la balise de navigation",
"position": Vector3(0.0, -22.0, -58.0),
"color": Color("5ddcff"),
"radius": 8.0,
},
{
"title": "Descendez au-delà de 60 m et inspectez la balise",
"position": Vector3(38.0, -66.0, -128.0),
"color": Color("ffbd59"),
"radius": 4.0,
},
{
"title": "Revenez dans la zone de récupération",
"position": Vector3(0.0, -12.0, 12.0),
"color": Color("7af0a5"),
"radius": 8.0,
},
]
var _submarine: SubmarineController
var _hud: PocHud
var _alarm_player: AlarmPlayer
var _mission_index := 0
var _mission_markers: Array[Node3D] = []
var _mission_guide_groups: Array[Node3D] = []
var _terminal := false
var _elapsed_time := 0.0
func _ready() -> void:
_ensure_input_actions()
_build_environment()
_build_course()
_hud = PocHud.new()
add_child(_hud)
_alarm_player = AlarmPlayer.new()
add_child(_alarm_player)
_submarine = SubmarineController.new()
_submarine.name = "Submarine"
_submarine.position = Vector3(0.0, -12.0, 12.0)
_submarine.mission_origin = _submarine.position
_submarine.telemetry_updated.connect(_on_telemetry_updated)
_submarine.loss_triggered.connect(_on_loss_triggered)
_submarine.abandon_progress_changed.connect(_hud.set_ejection_progress)
add_child(_submarine)
_update_mission_marker_visibility()
_hud.set_objective(MISSION_POINTS[0]["title"])
func _process(delta: float) -> void:
if _terminal:
return
_elapsed_time += delta
var target: Vector3 = MISSION_POINTS[_mission_index]["position"]
var distance := _submarine.global_position.distance_to(target)
_hud.set_objective(MISSION_POINTS[_mission_index]["title"], distance)
if distance <= float(MISSION_POINTS[_mission_index]["radius"]):
_mission_index += 1
if _mission_index >= MISSION_POINTS.size():
_complete_course()
else:
_update_mission_marker_visibility()
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("debug_critical") and not _terminal:
_submarine.force_critical_state()
if _terminal and event.is_action_pressed("restart_poc"):
get_tree().reload_current_scene()
func _on_telemetry_updated(snapshot: Dictionary) -> void:
if _hud == null:
return
_hud.update_telemetry(snapshot)
_alarm_player.update_alerts(
get_process_delta_time(),
[
int(snapshot["oxygen_alert"]),
int(snapshot["battery_alert"]),
int(snapshot["hull_alert"]),
int(snapshot["depth_alert"]),
]
)
func _on_loss_triggered(reason: StringName) -> void:
_terminal = true
_alarm_player.reset()
var message: String = {
&"hull_failure": "La coque a cédé sous la pression ou après un impact.",
&"oxygen_depleted": "L’oxygène est épuisé. La capsule a été récupérée.",
&"manual_ejection": "Vous avez abandonné volontairement le véhicule.",
}.get(reason, "Le véhicule a été perdu.")
_hud.show_terminal("SOUS-MARIN PERDU", message)
func _complete_course() -> void:
_terminal = true
_submarine.controls_enabled = false
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
_alarm_player.reset()
_hud.show_terminal(
"PARCOURS TERMINÉ",
"Temps : %02d:%02d\nOxygène, propulsion, pression et retour ont été exercés."
% [int(_elapsed_time) / 60, int(_elapsed_time) % 60]
)
func _update_mission_marker_visibility() -> void:
for marker_index in _mission_markers.size():
_mission_markers[marker_index].visible = marker_index == _mission_index
for guide_index in _mission_guide_groups.size():
_mission_guide_groups[guide_index].visible = guide_index == _mission_index
func _build_environment() -> void:
var world_environment := WorldEnvironment.new()
var environment := Environment.new()
environment.background_mode = Environment.BG_COLOR
environment.background_color = Color("031823")
environment.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
environment.ambient_light_color = Color("2b6575")
environment.ambient_light_energy = 0.46
environment.fog_enabled = true
environment.fog_light_color = Color("174553")
environment.fog_light_energy = 0.52
environment.fog_density = 0.013
environment.fog_height = -5.0
environment.fog_height_density = 0.03
world_environment.environment = environment
add_child(world_environment)
var sun := DirectionalLight3D.new()
sun.rotation_degrees = Vector3(-55.0, -28.0, 0.0)
sun.light_color = Color("82bdc9")
sun.light_energy = 0.35
sun.shadow_enabled = true
add_child(sun)
var surface := MeshInstance3D.new()
var surface_mesh := PlaneMesh.new()
surface_mesh.size = Vector2(320.0, 420.0)
surface.mesh = surface_mesh
surface.position = Vector3(0.0, 0.0, -80.0)
var surface_material := StandardMaterial3D.new()
surface_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
surface_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
surface_material.cull_mode = BaseMaterial3D.CULL_DISABLED
surface_material.albedo_color = Color(0.13, 0.55, 0.65, 0.13)
surface.material_override = surface_material
add_child(surface)
_add_water_particles()
func _build_course() -> void:
_add_static_box(
"Seafloor",
Vector3(0.0, -96.0, -80.0),
Vector3(300.0, 10.0, 360.0),
Color("183a3c")
)
_add_static_box(
"LeftWall",
Vector3(-75.0, -50.0, -90.0),
Vector3(22.0, 95.0, 260.0),
Color("24494b")
)
# Nearby formations make speed, distance and heading readable from the
# cockpit before the player reaches the first mission beacon.
var navigation_rock_specs := [
[Vector3(-10.0, -24.0, -12.0), Vector3(10.0, 24.0, 10.0), -14.0],
[Vector3(12.0, -30.0, -28.0), Vector3(11.0, 34.0, 12.0), 18.0],
[Vector3(-12.0, -38.0, -42.0), Vector3(9.0, 46.0, 10.0), 8.0],
[Vector3(11.0, -44.0, -50.0), Vector3(8.0, 52.0, 10.0), -10.0],
[Vector3(7.0, -55.0, -73.0), Vector3(10.0, 38.0, 12.0), -16.0],
[Vector3(25.0, -69.0, -96.0), Vector3(12.0, 45.0, 14.0), 13.0],
[Vector3(45.0, -72.0, -112.0), Vector3(13.0, 50.0, 15.0), -9.0],
]
for rock_index in navigation_rock_specs.size():
var spec: Array = navigation_rock_specs[rock_index]
var navigation_rock := _add_static_box(
"NavigationRock%02d" % rock_index,
spec[0],
spec[1],
Color("315553") if rock_index % 2 == 0 else Color("294947")
)
navigation_rock.rotation_degrees.y = spec[2]
_add_static_box(
"RightWall",
Vector3(75.0, -50.0, -90.0),
Vector3(22.0, 95.0, 260.0),
Color("24494b")
)
var rock_specs := [
[Vector3(-25.0, -70.0, -78.0), Vector3(24.0, 44.0, 28.0), -18.0],
[Vector3(28.0, -76.0, -92.0), Vector3(18.0, 34.0, 20.0), 16.0],
[Vector3(-12.0, -83.0, -145.0), Vector3(42.0, 22.0, 30.0), 8.0],
[Vector3(54.0, -63.0, -150.0), Vector3(20.0, 58.0, 24.0), -12.0],
]
for rock_index in rock_specs.size():
var spec: Array = rock_specs[rock_index]
var rock := _add_static_box(
"Rock%02d" % rock_index,
spec[0],
spec[1],
Color("2d5452")
)
rock.rotation_degrees.y = spec[2]
for mission_data in MISSION_POINTS:
_mission_markers.append(
_add_beacon(mission_data["position"], mission_data["color"])
)
# Intermediate lights reveal the direction of the next leg without drawing
# a HUD arrow. Each chain only appears when its objective becomes active.
_mission_guide_groups.append(_add_guide_chain([], Color("5ddcff")))
_mission_guide_groups.append(
_add_guide_chain(
[
Vector3(8.0, -28.0, -75.0),
Vector3(18.0, -38.0, -94.0),
Vector3(29.0, -51.0, -112.0),
],
Color("ffbd59")
)
)
_mission_guide_groups.append(
_add_guide_chain(
[
Vector3(30.0, -55.0, -100.0),
Vector3(23.0, -44.0, -72.0),
Vector3(15.0, -34.0, -44.0),
Vector3(8.0, -23.0, -16.0),
],
Color("7af0a5")
)
)
func _add_water_particles() -> void:
var particles := GPUParticles3D.new()
particles.name = "SuspendedWaterParticles"
particles.position = Vector3(0.0, -45.0, -65.0)
particles.amount = 520
particles.lifetime = 14.0
particles.preprocess = 14.0
particles.fixed_fps = 30
particles.visibility_aabb = AABB(
Vector3(-85.0, -55.0, -125.0),
Vector3(170.0, 110.0, 250.0)
)
var particle_motion := ParticleProcessMaterial.new()
particle_motion.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX
particle_motion.emission_box_extents = Vector3(78.0, 48.0, 118.0)
particle_motion.direction = Vector3(0.25, 0.12, 0.08)
particle_motion.spread = 35.0
particle_motion.initial_velocity_min = 0.025
particle_motion.initial_velocity_max = 0.11
particle_motion.gravity = Vector3.ZERO
particle_motion.scale_min = 0.035
particle_motion.scale_max = 0.11
particles.process_material = particle_motion
var mote_mesh := QuadMesh.new()
mote_mesh.size = Vector2(0.075, 0.075)
var mote_material := StandardMaterial3D.new()
mote_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
mote_material.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED
mote_material.albedo_color = Color(0.62, 0.88, 0.92, 0.42)
mote_material.roughness = 0.15
mote_mesh.material = mote_material
particles.draw_pass_1 = mote_mesh
add_child(particles)
func _add_static_box(node_name: String, box_position: Vector3, box_size: Vector3, color: Color) -> StaticBody3D:
var body := StaticBody3D.new()
body.name = node_name
body.position = box_position
add_child(body)
var mesh_instance := MeshInstance3D.new()
var mesh := BoxMesh.new()
mesh.size = box_size
mesh_instance.mesh = mesh
var material := StandardMaterial3D.new()
material.albedo_color = color
material.roughness = 0.9
mesh_instance.material_override = material
body.add_child(mesh_instance)
var collision_shape := CollisionShape3D.new()
var shape := BoxShape3D.new()
shape.size = box_size
collision_shape.shape = shape
body.add_child(collision_shape)
return body
func _add_beacon(beacon_position: Vector3, color: Color) -> Node3D:
var marker := Node3D.new()
marker.position = beacon_position
add_child(marker)
var sphere_instance := MeshInstance3D.new()
var sphere := SphereMesh.new()
sphere.radius = 2.2
sphere.height = 4.4
sphere_instance.mesh = sphere
var material := StandardMaterial3D.new()
material.albedo_color = color
material.emission_enabled = true
material.emission = color
material.emission_energy_multiplier = 4.0
sphere_instance.material_override = material
marker.add_child(sphere_instance)
var light := OmniLight3D.new()
light.omni_range = 18.0
light.light_energy = 5.0
light.light_color = color
marker.add_child(light)
return marker
func _add_guide_chain(guide_positions: Array, color: Color) -> Node3D:
var group := Node3D.new()
group.name = "MissionGuideChain"
add_child(group)
for guide_position in guide_positions:
var guide := Node3D.new()
guide.position = guide_position
group.add_child(guide)
var guide_mesh_instance := MeshInstance3D.new()
var guide_mesh := SphereMesh.new()
guide_mesh.radius = 0.38
guide_mesh.height = 0.76
guide_mesh_instance.mesh = guide_mesh
var guide_material := StandardMaterial3D.new()
guide_material.albedo_color = color
guide_material.emission_enabled = true
guide_material.emission = color
guide_material.emission_energy_multiplier = 1.5
guide_mesh_instance.material_override = guide_material
guide.add_child(guide_mesh_instance)
var guide_light := OmniLight3D.new()
guide_light.omni_range = 7.0
guide_light.light_energy = 1.8
guide_light.light_color = color
guide.add_child(guide_light)
return group
func _ensure_input_actions() -> void:
_register_key_action("move_forward", KEY_W)
_register_key_action("move_backward", KEY_S)
_register_key_action("move_left", KEY_A)
_register_key_action("move_right", KEY_D)
_register_key_action("ascend", KEY_SPACE)
_register_key_action("descend", KEY_CTRL)
_register_key_action("boost", KEY_SHIFT)
_register_key_action("abandon_vehicle", KEY_X)
_register_key_action("debug_critical", KEY_F9)
_register_key_action("restart_poc", KEY_ENTER)
_register_key_action("toggle_mouse_capture", KEY_ESCAPE)
func _register_key_action(action_name: StringName, physical_keycode: Key) -> void:
if not InputMap.has_action(action_name):
InputMap.add_action(action_name)
if InputMap.action_get_events(action_name).is_empty():
var key_event := InputEventKey.new()
key_event.physical_keycode = physical_keycode
InputMap.action_add_event(action_name, key_event)