252 lines
8.6 KiB
GDScript
252 lines
8.6 KiB
GDScript
class_name SonarHud
|
||
extends CanvasLayer
|
||
|
||
const SonarModelScript = preload("res://src/sonar_model.gd")
|
||
const SonarReliefRendererScript = preload("res://src/sonar_relief_renderer.gd")
|
||
|
||
signal expanded_changed(is_expanded: bool)
|
||
|
||
var _renderer: SubViewport
|
||
var _telemetry_label: Label
|
||
var _compact_panel: PanelContainer
|
||
var _compact_status: Label
|
||
var _expanded_overlay: ColorRect
|
||
var _expanded_status: Label
|
||
var _help_label: Label
|
||
var _expanded := false
|
||
|
||
|
||
func _ready() -> void:
|
||
layer = 2
|
||
_renderer = SonarReliefRendererScript.new()
|
||
_renderer.name = "SonarReliefRenderer"
|
||
add_child(_renderer)
|
||
_build_hud()
|
||
|
||
|
||
func update_telemetry(snapshot: Dictionary) -> void:
|
||
_telemetry_label.text = "PROFONDEUR %.1f m\nVITESSE %.1f m/s\nPROPULSION %d%%" % [
|
||
float(snapshot.get("depth", 0.0)),
|
||
float(snapshot.get("speed", 0.0)),
|
||
int(round(float(snapshot.get("battery", 0.0)))),
|
||
]
|
||
|
||
|
||
func update_sonar_pose(new_transform: Transform3D) -> void:
|
||
_renderer.update_submarine_pose(new_transform)
|
||
|
||
|
||
func show_ping_started(_max_range: float) -> void:
|
||
_renderer.start_scan()
|
||
_set_status(
|
||
"SCAN 0% • PREMIERS RETOURS EN ATTENTE",
|
||
Color("9cf7f2"),
|
||
"SCAN 0%"
|
||
)
|
||
|
||
|
||
func show_ping_progress(
|
||
current_cells: Array[Dictionary],
|
||
previous_cells: Array[Dictionary],
|
||
_max_range: float,
|
||
progress: float
|
||
) -> void:
|
||
_renderer.update_scan(current_cells, previous_cells, progress)
|
||
var metal_detected := _metal_cell_count(current_cells) > 0
|
||
var color := Color("ffad52") if metal_detected else Color("9cf7f2")
|
||
var suffix := " • ANOMALIE" if metal_detected else ""
|
||
var status := "SCAN %d%%%s" % [int(round(progress * 100.0)), suffix]
|
||
_set_status(status, color, status)
|
||
|
||
|
||
func show_ping_results(
|
||
current_cells: Array[Dictionary],
|
||
_previous_cells: Array[Dictionary],
|
||
_max_range: float
|
||
) -> void:
|
||
_renderer.finish_scan(current_cells)
|
||
var metal_count := _metal_cell_count(current_cells)
|
||
var status := "SCAN TERMINÉ • RELIEF RECONSTRUIT"
|
||
var color := Color("9cf7f2")
|
||
if metal_count > 0:
|
||
status += " • ANOMALIE ORANGE"
|
||
color = Color("ffad52")
|
||
_set_status(
|
||
status,
|
||
color,
|
||
"SCAN TERMINÉ • ANOMALIE" if metal_count > 0 else "SCAN TERMINÉ"
|
||
)
|
||
|
||
|
||
func toggle_expanded() -> void:
|
||
set_expanded(not _expanded)
|
||
|
||
|
||
func set_expanded(is_expanded: bool) -> void:
|
||
if _expanded == is_expanded:
|
||
return
|
||
_expanded = is_expanded
|
||
_expanded_overlay.visible = is_expanded
|
||
_compact_panel.visible = not is_expanded
|
||
_help_label.visible = not is_expanded
|
||
expanded_changed.emit(is_expanded)
|
||
|
||
|
||
func is_expanded() -> bool:
|
||
return _expanded
|
||
|
||
|
||
func relief_renderer() -> SubViewport:
|
||
return _renderer
|
||
|
||
|
||
func _set_status(text: String, color: Color, compact_text: String = "") -> void:
|
||
_compact_status.text = text if compact_text.is_empty() else compact_text
|
||
_compact_status.modulate = color
|
||
_expanded_status.text = text
|
||
_expanded_status.modulate = color
|
||
|
||
|
||
func _metal_cell_count(cells: Array[Dictionary]) -> int:
|
||
var count := 0
|
||
for cell: Dictionary in cells:
|
||
if int(cell["material"]) == SonarModelScript.MaterialCategory.METAL:
|
||
count += 1
|
||
return count
|
||
|
||
|
||
func _build_hud() -> void:
|
||
var root := Control.new()
|
||
root.name = "SonarHudRoot"
|
||
root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||
add_child(root)
|
||
|
||
var objective := Label.new()
|
||
objective.set_anchors_preset(Control.PRESET_CENTER_TOP)
|
||
objective.position = Vector2(-390.0, 24.0)
|
||
objective.size = Vector2(780.0, 38.0)
|
||
objective.text = "LISEZ LE RELIEF • FRANCHISSEZ L'OUVERTURE • IDENTIFIEZ L'ANOMALIE"
|
||
objective.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
objective.add_theme_font_size_override("font_size", 18)
|
||
root.add_child(objective)
|
||
|
||
var telemetry_panel := PanelContainer.new()
|
||
telemetry_panel.position = Vector2(22.0, 82.0)
|
||
telemetry_panel.size = Vector2(280.0, 118.0)
|
||
root.add_child(telemetry_panel)
|
||
_telemetry_label = Label.new()
|
||
_telemetry_label.text = "PROFONDEUR\nVITESSE\nPROPULSION"
|
||
_telemetry_label.add_theme_font_size_override("font_size", 16)
|
||
telemetry_panel.add_child(_telemetry_label)
|
||
|
||
var instruction_panel := PanelContainer.new()
|
||
instruction_panel.position = Vector2(22.0, 214.0)
|
||
instruction_panel.size = Vector2(294.0, 112.0)
|
||
root.add_child(instruction_panel)
|
||
var instruction := Label.new()
|
||
instruction.text = (
|
||
"F ÉMETTRE UN SCAN MANUEL\n"
|
||
+ "Le relief apparaît du proche au lointain.\n"
|
||
+ "L'orange signale un retour anormal."
|
||
)
|
||
instruction.modulate = Color(0.78, 0.94, 0.95, 0.94)
|
||
instruction_panel.add_child(instruction)
|
||
|
||
_compact_panel = PanelContainer.new()
|
||
_compact_panel.set_anchors_preset(Control.PRESET_TOP_RIGHT)
|
||
_compact_panel.position = Vector2(-342.0, 82.0)
|
||
_compact_panel.size = Vector2(320.0, 276.0)
|
||
root.add_child(_compact_panel)
|
||
var compact_box := VBoxContainer.new()
|
||
compact_box.add_theme_constant_override("separation", 6)
|
||
_compact_panel.add_child(compact_box)
|
||
var compact_title := Label.new()
|
||
compact_title.text = "SONAR BATHYMÉTRIQUE • AGRANDIR"
|
||
compact_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
compact_title.add_theme_font_size_override("font_size", 14)
|
||
compact_box.add_child(compact_title)
|
||
var compact_view := _make_sonar_texture_rect(Vector2(298.0, 190.0))
|
||
compact_view.tooltip_text = "Cliquez pour ouvrir le grand écran sonar"
|
||
compact_view.mouse_filter = Control.MOUSE_FILTER_STOP
|
||
compact_view.gui_input.connect(_on_compact_view_input)
|
||
compact_box.add_child(compact_view)
|
||
_compact_status = Label.new()
|
||
_compact_status.text = "F • LANCER LE PREMIER SCAN"
|
||
_compact_status.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
compact_box.add_child(_compact_status)
|
||
var compact_reference := Label.new()
|
||
compact_reference.text = "PORTÉE 95 M • SOUS-MARIN EN BAS • CAP VERS LE HAUT"
|
||
compact_reference.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
compact_reference.modulate = Color(0.62, 0.82, 0.84, 0.86)
|
||
compact_reference.add_theme_font_size_override("font_size", 11)
|
||
compact_box.add_child(compact_reference)
|
||
|
||
_expanded_overlay = ColorRect.new()
|
||
_expanded_overlay.name = "ExpandedSonarOverlay"
|
||
_expanded_overlay.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||
_expanded_overlay.color = Color(0.0, 0.015, 0.02, 0.96)
|
||
_expanded_overlay.mouse_filter = Control.MOUSE_FILTER_STOP
|
||
_expanded_overlay.visible = false
|
||
root.add_child(_expanded_overlay)
|
||
|
||
var expanded_panel := PanelContainer.new()
|
||
expanded_panel.position = Vector2(110.0, 34.0)
|
||
expanded_panel.size = Vector2(1060.0, 652.0)
|
||
_expanded_overlay.add_child(expanded_panel)
|
||
var expanded_box := VBoxContainer.new()
|
||
expanded_box.add_theme_constant_override("separation", 7)
|
||
expanded_panel.add_child(expanded_box)
|
||
var expanded_title := Label.new()
|
||
expanded_title.text = "SONAR BATHYMÉTRIQUE • PORTÉE 95 M • CAP VERS LE HAUT"
|
||
expanded_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
expanded_title.add_theme_font_size_override("font_size", 19)
|
||
expanded_box.add_child(expanded_title)
|
||
var expanded_view := _make_sonar_texture_rect(Vector2(1038.0, 556.0))
|
||
expanded_box.add_child(expanded_view)
|
||
_expanded_status = Label.new()
|
||
_expanded_status.text = "F • LANCER LE PREMIER SCAN"
|
||
_expanded_status.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
_expanded_status.add_theme_font_size_override("font_size", 16)
|
||
expanded_box.add_child(_expanded_status)
|
||
var legend := Label.new()
|
||
legend.text = "BLEU PROFOND • VERT/JAUNE HAUTEUR • ORANGE CAISSE/ANOMALIE • NOIR INCONNU"
|
||
legend.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
legend.modulate = Color(0.68, 0.88, 0.90, 0.90)
|
||
expanded_box.add_child(legend)
|
||
|
||
var close_button := Button.new()
|
||
close_button.position = Vector2(1044.0, 46.0)
|
||
close_button.size = Vector2(108.0, 36.0)
|
||
close_button.text = "FERMER ×"
|
||
close_button.pressed.connect(func() -> void: set_expanded(false))
|
||
_expanded_overlay.add_child(close_button)
|
||
|
||
_help_label = Label.new()
|
||
_help_label.set_anchors_preset(Control.PRESET_BOTTOM_WIDE)
|
||
_help_label.offset_left = 20.0
|
||
_help_label.offset_top = -42.0
|
||
_help_label.offset_right = -20.0
|
||
_help_label.offset_bottom = -12.0
|
||
_help_label.text = "F scanner • Tab agrandir/fermer • W/S propulsion • A/D gouvernail • Espace/Ctrl profondeur • Échap souris"
|
||
_help_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
_help_label.modulate = Color(0.75, 0.90, 0.94, 0.9)
|
||
root.add_child(_help_label)
|
||
|
||
|
||
func _make_sonar_texture_rect(minimum_size: Vector2) -> TextureRect:
|
||
var view := TextureRect.new()
|
||
view.custom_minimum_size = minimum_size
|
||
view.texture = _renderer.get_texture()
|
||
view.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
view.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||
return view
|
||
|
||
|
||
func _on_compact_view_input(event: InputEvent) -> void:
|
||
if (
|
||
event is InputEventMouseButton
|
||
and event.button_index == MOUSE_BUTTON_LEFT
|
||
and event.pressed
|
||
):
|
||
set_expanded(true)
|