Validate POC A and POC B foundations
This commit is contained in:
1 parent
44fd4b4b43
commit
c38e09a663
47 files changed
+4990
-35
No files matched your search
+394
@@ -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)
|
||||
Reference in new issue
Block a user