extends PanelContainer ## Always-on compact panel listing the current controls. ## Reads bindings straight from the InputMap so it stays accurate if keys are rebound. # Each row is a friendly label plus the input action(s) that feed it. # Grouped actions (like movement) show their keys joined together. const ROWS := [ { "label": "Move", "actions": ["move_forward", "move_left", "move_backward", "move_right"] }, { "label": "Jump", "actions": ["jump"] }, { "label": "Sprint", "actions": ["shift"] }, { "label": "Dash", "actions": ["dash"] }, { "label": "Attack", "actions": ["attack"] }, { "label": "Block", "actions": ["block"] }, { "label": "Pick Up / Drop", "actions": ["pickup"] }, { "label": "Character Sheet", "actions": ["toggle_character_sheet"] }, { "label": "Chat", "actions": ["toggle_chat"] }, { "label": "Menu", "actions": ["quit"] }, ] func _ready() -> void: _build() func _build() -> void: var content: VBoxContainer = $Margin/Content var title := Label.new() title.text = "CONTROLS" title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title.add_theme_font_size_override("font_size", 16) title.add_theme_color_override("font_color", Color(1, 0.8, 0, 1)) title.add_theme_color_override("font_outline_color", Color.BLACK) title.add_theme_constant_override("outline_size", 2) content.add_child(title) var grid := GridContainer.new() grid.columns = 2 grid.add_theme_constant_override("h_separation", 12) grid.add_theme_constant_override("v_separation", 3) content.add_child(grid) for row in ROWS: var keys: Array[String] = [] for action in row["actions"]: var k := _key_text(action) if k != "": keys.append(k) if keys.is_empty(): continue grid.add_child(_make_label(" ".join(keys), Color(1, 0.82, 0.2, 1))) grid.add_child(_make_label(row["label"], Color.WHITE)) func _make_label(text: String, color: Color) -> Label: var l := Label.new() l.text = text l.add_theme_font_size_override("font_size", 13) l.add_theme_color_override("font_color", color) l.add_theme_color_override("font_outline_color", Color.BLACK) l.add_theme_constant_override("outline_size", 1) return l ## Returns a human-readable name for the first event bound to an action. func _key_text(action: String) -> String: if not InputMap.has_action(action): return "" for ev in InputMap.action_get_events(action): if ev is InputEventKey: var code: int = ev.physical_keycode if ev.physical_keycode != 0 else ev.keycode return OS.get_keycode_string(code) elif ev is InputEventMouseButton: match ev.button_index: MOUSE_BUTTON_LEFT: return "LMB" MOUSE_BUTTON_RIGHT: return "RMB" MOUSE_BUTTON_MIDDLE: return "MMB" _: return "Mouse %d" % ev.button_index return ""