From be398f6d93797d72adf474d14ec967e4d06a5fcc Mon Sep 17 00:00:00 2001 From: Matthew Polak Date: Sat, 18 Jul 2026 13:25:18 -0700 Subject: [PATCH 1/6] test: cover replay input display Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c779ef78-f1e2-4b31-8c60-1b7fc8811ccb --- scripts/test-godot.sh | 1 + tests/replay_input_display_test.gd | 91 ++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 tests/replay_input_display_test.gd diff --git a/scripts/test-godot.sh b/scripts/test-godot.sh index b003341..79e8445 100755 --- a/scripts/test-godot.sh +++ b/scripts/test-godot.sh @@ -9,6 +9,7 @@ GODOT_BIN="${GODOT_BIN:-godot}" TEST_SCRIPTS=( "res://tests/unit/runtime_options_test.gd" "res://tests/unit/bootstrap_test.gd" + "res://tests/replay_input_display_test.gd" "res://tests/world_record_announcement_test.gd" "res://tests/offline_playtest_smoke.gd" "res://tests/aim_menu_smoke.gd" diff --git a/tests/replay_input_display_test.gd b/tests/replay_input_display_test.gd new file mode 100644 index 0000000..08ac8d8 --- /dev/null +++ b/tests/replay_input_display_test.gd @@ -0,0 +1,91 @@ +extends SceneTree + +const TestCase = preload("res://tests/support/test_case.gd") +const SCENE_PATH := "res://src/maps/replay_input_display.tscn" + +func _init() -> void: + call_deferred("_run") + +func _run() -> void: + var t := TestCase.new() + + var replay_input_display_scene := load(SCENE_PATH) as PackedScene + t.check(replay_input_display_scene != null, "Expected %s to load" % SCENE_PATH) + if replay_input_display_scene != null: + var replay_input_display = replay_input_display_scene.instantiate() + t.check(replay_input_display != null, "Expected %s to instantiate" % SCENE_PATH) + if replay_input_display != null: + t.check( + replay_input_display.has_method("set_inputs"), + "Replay input display should expose set_inputs(bool, bool, bool, bool, bool, bool, bool)", + ) + t.check(replay_input_display.has_method("reset"), "Replay input display should expose reset()") + t.check( + replay_input_display.has_method("is_input_active"), + "Replay input display should expose is_input_active(StringName)", + ) + + root.add_child(replay_input_display) + await process_frame + + _check_label(t, replay_input_display, "Movement/W/Label", "W") + _check_label(t, replay_input_display, "Movement/A/Label", "A") + _check_label(t, replay_input_display, "Movement/S/Label", "S") + _check_label(t, replay_input_display, "Movement/D/Label", "D") + _check_label(t, replay_input_display, "Combat/Shoot/Label", "LMB") + _check_label(t, replay_input_display, "Combat/Ads/Label", "RMB") + _check_label(t, replay_input_display, "Combat/Reload/Label", "R") + + replay_input_display.set_inputs(true, false, true, false, true, false, true) + _check_input_state(t, replay_input_display, &"forward", true) + _check_input_state(t, replay_input_display, &"left", false) + _check_input_state(t, replay_input_display, &"back", true) + _check_input_state(t, replay_input_display, &"right", false) + _check_input_state(t, replay_input_display, &"shoot", true) + _check_input_state(t, replay_input_display, &"ads", false) + _check_input_state(t, replay_input_display, &"reload", true) + + var active_panel := _panel_stylebox(replay_input_display, "Movement/W/Label") + var inactive_panel := _panel_stylebox(replay_input_display, "Movement/A/Label") + t.check(active_panel != null, "Active keycap should expose a panel stylebox") + t.check(inactive_panel != null, "Inactive keycap should expose a panel stylebox") + if active_panel is StyleBoxFlat and inactive_panel is StyleBoxFlat: + t.check( + active_panel.bg_color != inactive_panel.bg_color, + "Active and inactive keycaps should use distinct panel background colors", + ) + + replay_input_display.reset() + _check_input_state(t, replay_input_display, &"forward", false) + _check_input_state(t, replay_input_display, &"left", false) + _check_input_state(t, replay_input_display, &"back", false) + _check_input_state(t, replay_input_display, &"right", false) + _check_input_state(t, replay_input_display, &"shoot", false) + _check_input_state(t, replay_input_display, &"ads", false) + _check_input_state(t, replay_input_display, &"reload", false) + + replay_input_display.queue_free() + + await process_frame + quit(t.finish()) + +func _check_label(t: TestCase, replay_input_display: Node, path: String, expected_text: String) -> void: + var label := replay_input_display.get_node_or_null(path) as Label + t.check(label != null, "Expected %s to exist" % path) + if label != null: + t.check_equal(label.text, expected_text, "%s should display %s" % [path, expected_text]) + +func _check_input_state(t: TestCase, replay_input_display: Node, input_name: StringName, expected_active: bool) -> void: + t.check_equal( + replay_input_display.is_input_active(input_name), + expected_active, + "Replay input %s should be %s" % [input_name, "active" if expected_active else "inactive"], + ) + +func _panel_stylebox(replay_input_display: Node, label_path: String) -> StyleBox: + var label := replay_input_display.get_node_or_null(label_path) as Label + if label == null: + return null + + var parent_control := label.get_parent() as Control + return parent_control.get_theme_stylebox("panel") if parent_control != null else null From 010b943f9c66a8b892881a06777dbf36342b430e Mon Sep 17 00:00:00 2001 From: Matthew Polak Date: Sat, 18 Jul 2026 13:27:53 -0700 Subject: [PATCH 2/6] test: correct replay input expectations Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c779ef78-f1e2-4b31-8c60-1b7fc8811ccb --- tests/replay_input_display_test.gd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/replay_input_display_test.gd b/tests/replay_input_display_test.gd index 08ac8d8..528ad41 100644 --- a/tests/replay_input_display_test.gd +++ b/tests/replay_input_display_test.gd @@ -38,15 +38,15 @@ func _run() -> void: replay_input_display.set_inputs(true, false, true, false, true, false, true) _check_input_state(t, replay_input_display, &"forward", true) - _check_input_state(t, replay_input_display, &"left", false) - _check_input_state(t, replay_input_display, &"back", true) + _check_input_state(t, replay_input_display, &"left", true) + _check_input_state(t, replay_input_display, &"back", false) _check_input_state(t, replay_input_display, &"right", false) _check_input_state(t, replay_input_display, &"shoot", true) _check_input_state(t, replay_input_display, &"ads", false) _check_input_state(t, replay_input_display, &"reload", true) var active_panel := _panel_stylebox(replay_input_display, "Movement/W/Label") - var inactive_panel := _panel_stylebox(replay_input_display, "Movement/A/Label") + var inactive_panel := _panel_stylebox(replay_input_display, "Movement/S/Label") t.check(active_panel != null, "Active keycap should expose a panel stylebox") t.check(inactive_panel != null, "Inactive keycap should expose a panel stylebox") if active_panel is StyleBoxFlat and inactive_panel is StyleBoxFlat: From c3fc172f2b3b6245237281583e1f3f6816dec22d Mon Sep 17 00:00:00 2001 From: Matthew Polak Date: Sat, 18 Jul 2026 13:33:15 -0700 Subject: [PATCH 3/6] feat: add replay input display Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c779ef78-f1e2-4b31-8c60-1b7fc8811ccb --- src/maps/replay_input_display.gd | 98 ++++++++++++++++++++++++++ src/maps/replay_input_display.tscn | 106 +++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 src/maps/replay_input_display.gd create mode 100644 src/maps/replay_input_display.tscn diff --git a/src/maps/replay_input_display.gd b/src/maps/replay_input_display.gd new file mode 100644 index 0000000..b9953f9 --- /dev/null +++ b/src/maps/replay_input_display.gd @@ -0,0 +1,98 @@ +class_name ReplayInputDisplay +extends HBoxContainer + +const ACTIVE_BG := Color(0.94, 0.93, 0.89, 1.0) +const ACTIVE_BORDER := Color(0.18, 0.18, 0.18, 1.0) +const ACTIVE_FONT := Color(0.10, 0.10, 0.10, 1.0) +const INACTIVE_BG := Color(0.12, 0.14, 0.17, 1.0) +const INACTIVE_BORDER := Color(0.30, 0.33, 0.37, 1.0) +const INACTIVE_FONT := Color(0.78, 0.81, 0.84, 1.0) + +var _input_states := { + &"forward": false, + &"back": false, + &"left": false, + &"right": false, + &"shoot": false, + &"ads": false, + &"reload": false, +} + +var _key_panels: Dictionary = {} +var _key_labels: Dictionary = {} +var _active_panel_style: StyleBoxFlat +var _inactive_panel_style: StyleBoxFlat + +func _ready() -> void: + _active_panel_style = _create_panel_style(ACTIVE_BG, ACTIVE_BORDER) + _inactive_panel_style = _create_panel_style(INACTIVE_BG, INACTIVE_BORDER) + + _register_decorative_panel($"Movement/TopLeftSpacer") + _register_key(&"forward", $"Movement/W") + _register_decorative_panel($"Movement/TopRightSpacer") + _register_key(&"back", $"Movement/S") + _register_key(&"left", $"Movement/A") + _register_key(&"right", $"Movement/D") + _register_key(&"shoot", $"Combat/Shoot") + _register_key(&"ads", $"Combat/Ads") + _register_key(&"reload", $"Combat/Reload") + + reset() + +func set_inputs( + forward_input: bool, + back_input: bool, + left_input: bool, + right_input: bool, + shoot_input: bool, + ads_input: bool, + reload_input: bool, + ) -> void: + _input_states[&"forward"] = forward_input + _input_states[&"back"] = back_input + _input_states[&"left"] = left_input + _input_states[&"right"] = right_input + _input_states[&"shoot"] = shoot_input + _input_states[&"ads"] = ads_input + _input_states[&"reload"] = reload_input + _apply_visual_state() + +func reset() -> void: + set_inputs(false, false, false, false, false, false, false) + +func is_input_active(input_name: StringName) -> bool: + return _input_states.get(input_name, false) + +func _register_key(action_name: StringName, panel: PanelContainer) -> void: + var label := panel.get_node_or_null("Label") as Label + _key_panels[action_name] = panel + _key_labels[action_name] = label + +func _register_decorative_panel(panel: PanelContainer) -> void: + if panel != null: + panel.add_theme_stylebox_override("panel", _inactive_panel_style) + +func _apply_visual_state() -> void: + for action_name in _key_panels.keys(): + var panel := _key_panels[action_name] as PanelContainer + var label := _key_labels.get(action_name) as Label + var active := _input_states.get(action_name, false) + + if panel != null: + panel.add_theme_stylebox_override("panel", _active_panel_style if active else _inactive_panel_style) + if label != null: + label.add_theme_color_override("font_color", ACTIVE_FONT if active else INACTIVE_FONT) + +func _create_panel_style(bg_color: Color, border_color: Color) -> StyleBoxFlat: + var style := StyleBoxFlat.new() + style.bg_color = bg_color + style.border_color = border_color + style.border_width_left = 1 + style.border_width_top = 1 + style.border_width_right = 1 + style.border_width_bottom = 1 + style.content_margin_left = 5 + style.content_margin_top = 3 + style.content_margin_right = 5 + style.content_margin_bottom = 3 + return style diff --git a/src/maps/replay_input_display.tscn b/src/maps/replay_input_display.tscn new file mode 100644 index 0000000..abdf39d --- /dev/null +++ b/src/maps/replay_input_display.tscn @@ -0,0 +1,106 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://src/maps/replay_input_display.gd" id="1_ry1dp"] + +[node name="ReplayInputDisplay" type="HBoxContainer"] +custom_minimum_size = Vector2(0, 64) +layout_mode = 2 +theme_override_constants/separation = 12 +alignment = 1 +script = ExtResource("1_ry1dp") + +[node name="Movement" type="GridContainer" parent="."] +layout_mode = 2 +theme_override_constants/h_separation = 4 +theme_override_constants/v_separation = 4 +columns = 3 + +[node name="TopLeftSpacer" type="PanelContainer" parent="Movement"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 + +[node name="W" type="PanelContainer" parent="Movement"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 + +[node name="Label" type="Label" parent="Movement/W"] +layout_mode = 2 +theme_override_font_sizes/font_size = 15 +text = "W" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TopRightSpacer" type="PanelContainer" parent="Movement"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 + +[node name="A" type="PanelContainer" parent="Movement"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 + +[node name="Label" type="Label" parent="Movement/A"] +layout_mode = 2 +theme_override_font_sizes/font_size = 15 +text = "A" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="S" type="PanelContainer" parent="Movement"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 + +[node name="Label" type="Label" parent="Movement/S"] +layout_mode = 2 +theme_override_font_sizes/font_size = 15 +text = "S" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="D" type="PanelContainer" parent="Movement"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 + +[node name="Label" type="Label" parent="Movement/D"] +layout_mode = 2 +theme_override_font_sizes/font_size = 15 +text = "D" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Combat" type="HBoxContainer" parent="."] +layout_mode = 2 +theme_override_constants/separation = 4 +alignment = 1 + +[node name="Shoot" type="PanelContainer" parent="Combat"] +custom_minimum_size = Vector2(46, 30) +layout_mode = 2 + +[node name="Label" type="Label" parent="Combat/Shoot"] +layout_mode = 2 +theme_override_font_sizes/font_size = 15 +text = "LMB" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Ads" type="PanelContainer" parent="Combat"] +custom_minimum_size = Vector2(46, 30) +layout_mode = 2 + +[node name="Label" type="Label" parent="Combat/Ads"] +layout_mode = 2 +theme_override_font_sizes/font_size = 15 +text = "RMB" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Reload" type="PanelContainer" parent="Combat"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 + +[node name="Label" type="Label" parent="Combat/Reload"] +layout_mode = 2 +theme_override_font_sizes/font_size = 15 +text = "R" +horizontal_alignment = 1 +vertical_alignment = 1 From 7d3f218f8975850735933f3712447e5dbf13a436 Mon Sep 17 00:00:00 2001 From: Matthew Polak Date: Sat, 18 Jul 2026 13:49:29 -0700 Subject: [PATCH 4/6] feat: show inputs during replay playback Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c779ef78-f1e2-4b31-8c60-1b7fc8811ccb --- src/maps/map_ui.gd | 19 ++++++++++-- src/maps/map_ui.tscn | 13 ++++++--- src/recorder.gd | 1 + tests/replay_input_display_test.gd | 46 ++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/maps/map_ui.gd b/src/maps/map_ui.gd index 19fa5c0..f5a49a0 100644 --- a/src/maps/map_ui.gd +++ b/src/maps/map_ui.gd @@ -15,6 +15,7 @@ signal return_control_to_player @export var replay_slider: HSlider @export var replay_container: Control @export var replay_v: VBoxContainer +@export var replay_input_display: ReplayInputDisplay @export var game_info: Container @export var ammo_label: Label @export var leaderboard: Container @@ -23,20 +24,34 @@ func on_shot(mag_ammo: int, _reserve_ammo := 0) -> void: ammo_label.text = "Ammo: %d / Inf" % [mag_ammo] func _ready() -> void: - keybind_info_label.text = ("Press Ctrl to toggle UI\nPress %s to restart" % Global.settings_manager.get_keybind_string("restart")) + var settings_manager = get_node("/root/Global").settings_manager + keybind_info_label.text = ("Press Ctrl to toggle UI\nPress %s to restart" % settings_manager.get_keybind_string("restart")) done_replay_btn.pressed.connect(func() -> void: return_control_to_player.emit()) - alt_speed_label.visible = Global.settings_manager.value("Display", "speed") + alt_speed_label.visible = settings_manager.value("Display", "speed") func set_frame(frame: int, total: int) -> void: tick_label.text = "Tick: %d / %d" % [frame + 1, total] replay_slider.value = frame replay_slider.max_value = total - 1 +func set_replay_inputs(frame) -> void: + replay_input_display.set_inputs( + frame.forward_input, + frame.back_input, + frame.left_input, + frame.right_input, + frame.shoot_input, + frame.ads_input, + frame.reload_input, + ) + func set_replay_visible(value: bool) -> void: replay_container.visible = value replay_v.visible = value map.recorder.controller.visible = value + if not value: + replay_input_display.reset() func is_replay_visible() -> bool: return replay_container.visible diff --git a/src/maps/map_ui.tscn b/src/maps/map_ui.tscn index 7a331f6..7a73444 100644 --- a/src/maps/map_ui.tscn +++ b/src/maps/map_ui.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=13 format=3 uid="uid://bnati2e7fo1g"] +[gd_scene load_steps=14 format=3 uid="uid://bnati2e7fo1g"] [ext_resource type="Script" uid="uid://vuacki0u3vnw" path="res://src/maps/map_ui.gd" id="1_jnrmt"] [ext_resource type="Theme" uid="uid://3as6f5nxctip" path="res://src/main_theme.tres" id="2_uralg"] @@ -10,6 +10,7 @@ [ext_resource type="Texture2D" uid="uid://dqvbiit5bj0rm" path="res://src/textures/plat_medal.png" id="9_k6ak5"] [ext_resource type="Texture2D" uid="uid://doueeo1np6if1" path="res://src/textures/author_medal.png" id="10_dxk01"] [ext_resource type="FontFile" uid="uid://reim7hyo6sqy" path="res://src/fonts/OpenSans-Regular.ttf" id="11_5vm7s"] +[ext_resource type="PackedScene" path="res://src/maps/replay_input_display.tscn" id="12_pfxlv"] [sub_resource type="Theme" id="Theme_of3v1"] default_font_size = 15 @@ -17,7 +18,7 @@ default_font_size = 15 [sub_resource type="Theme" id="Theme_nvgh5"] default_font = ExtResource("11_5vm7s") -[node name="MapUi" type="CanvasLayer" node_paths=PackedStringArray("keybind_info_label", "done_replay_btn", "tick_label", "replay_slider", "replay_container", "replay_v", "game_info", "ammo_label", "leaderboard")] +[node name="MapUi" type="CanvasLayer" node_paths=PackedStringArray("keybind_info_label", "done_replay_btn", "tick_label", "replay_slider", "replay_container", "replay_v", "replay_input_display", "game_info", "ammo_label", "leaderboard")] script = ExtResource("1_jnrmt") keybind_info_label = NodePath("ReplayContainer/V/KeybindInfo") done_replay_btn = NodePath("ReplayContainer/V/Done") @@ -25,6 +26,7 @@ tick_label = NodePath("ReplayContainer/V/Label") replay_slider = NodePath("ReplayContainer/V/Slider") replay_container = NodePath("ReplayContainer") replay_v = NodePath("ReplayContainer/V") +replay_input_display = NodePath("ReplayContainer/V/ReplayInputDisplay") game_info = NodePath("UiContainer/GameInfo") ammo_label = NodePath("UiContainer/BottomLeft/V/Ammo") leaderboard = NodePath("UiContainer/GameInfo/Leaderboard") @@ -327,7 +329,7 @@ anchor_top = 1.0 anchor_right = 0.5 anchor_bottom = 1.0 offset_left = -150.0 -offset_top = -147.0 +offset_top = -215.0 offset_right = 150.0 grow_horizontal = 2 grow_vertical = 0 @@ -336,12 +338,15 @@ grow_vertical = 0 custom_minimum_size = Vector2(300, 0) layout_mode = 2 offset_right = 300.0 -offset_bottom = 147.0 +offset_bottom = 215.0 grow_horizontal = 2 grow_vertical = 0 theme = ExtResource("2_uralg") alignment = 1 +[node name="ReplayInputDisplay" parent="ReplayContainer/V" instance=ExtResource("12_pfxlv")] +layout_mode = 2 + [node name="Label" type="Label" parent="ReplayContainer/V"] layout_mode = 2 text = "Frame: 0 / 999" diff --git a/src/recorder.gd b/src/recorder.gd index 24a5645..b994f09 100644 --- a/src/recorder.gd +++ b/src/recorder.gd @@ -103,6 +103,7 @@ func set_frame(value: int) -> void: else: map.map_ui.set_speed(speed) map.map_ui.set_timer(current_frame * dt) + map.map_ui.set_replay_inputs(frame) if prev_frame.weapon_index != frame.weapon_index: controller.weapon_handler.set_weapon(Global.game_manager.get_weapon_from_index(frame.weapon_index), is_ghost) diff --git a/tests/replay_input_display_test.gd b/tests/replay_input_display_test.gd index 528ad41..8b84a02 100644 --- a/tests/replay_input_display_test.gd +++ b/tests/replay_input_display_test.gd @@ -2,6 +2,7 @@ extends SceneTree const TestCase = preload("res://tests/support/test_case.gd") const SCENE_PATH := "res://src/maps/replay_input_display.tscn" +const MAP_UI_SCENE_PATH := "res://src/maps/map_ui.tscn" func _init() -> void: call_deferred("_run") @@ -66,6 +67,42 @@ func _run() -> void: replay_input_display.queue_free() + var map_ui_scene := load(MAP_UI_SCENE_PATH) as PackedScene + t.check(map_ui_scene != null, "Expected %s to load" % MAP_UI_SCENE_PATH) + if map_ui_scene != null: + var map_ui = map_ui_scene.instantiate() + t.check(map_ui != null, "Expected %s to instantiate" % MAP_UI_SCENE_PATH) + if map_ui != null: + var replay_input_display = map_ui.get_node_or_null("ReplayContainer/V/ReplayInputDisplay") + t.check( + replay_input_display != null, + "MapUi should own ReplayContainer/V/ReplayInputDisplay", + ) + t.check_equal( + map_ui.get("replay_input_display"), + replay_input_display, + "MapUi replay_input_display export should reference ReplayContainer/V/ReplayInputDisplay", + ) + + if t.check(map_ui.has_method("set_replay_inputs"), "MapUi should expose set_replay_inputs(Recorder.Frame)"): + var frame := _RecorderFrame.new() + frame.forward_input = true + frame.back_input = false + frame.left_input = false + frame.right_input = true + frame.shoot_input = false + frame.ads_input = true + frame.reload_input = false + + map_ui.set_replay_inputs(frame) + if replay_input_display != null: + _check_input_state(t, replay_input_display, &"forward", true) + _check_input_state(t, replay_input_display, &"right", true) + _check_input_state(t, replay_input_display, &"ads", true) + _check_input_state(t, replay_input_display, &"shoot", false) + + map_ui.queue_free() + await process_frame quit(t.finish()) @@ -89,3 +126,12 @@ func _panel_stylebox(replay_input_display: Node, label_path: String) -> StyleBox var parent_control := label.get_parent() as Control return parent_control.get_theme_stylebox("panel") if parent_control != null else null + +class _RecorderFrame: + var forward_input: bool + var back_input: bool + var left_input: bool + var right_input: bool + var shoot_input: bool + var ads_input: bool + var reload_input: bool From 2777c14928016f85f7376a032eb7dd2e5cc02842 Mon Sep 17 00:00:00 2001 From: Matthew Polak Date: Sat, 18 Jul 2026 14:01:47 -0700 Subject: [PATCH 5/6] refactor: type replay input wiring Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c779ef78-f1e2-4b31-8c60-1b7fc8811ccb --- src/maps/map_ui.gd | 29 ++++++++++++++++++----------- src/recorder.gd | 10 +++++++++- tests/replay_input_display_test.gd | 22 ++-------------------- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/maps/map_ui.gd b/src/maps/map_ui.gd index f5a49a0..ad5ec75 100644 --- a/src/maps/map_ui.gd +++ b/src/maps/map_ui.gd @@ -24,26 +24,33 @@ func on_shot(mag_ammo: int, _reserve_ammo := 0) -> void: ammo_label.text = "Ammo: %d / Inf" % [mag_ammo] func _ready() -> void: - var settings_manager = get_node("/root/Global").settings_manager - keybind_info_label.text = ("Press Ctrl to toggle UI\nPress %s to restart" % settings_manager.get_keybind_string("restart")) + keybind_info_label.text = ("Press Ctrl to toggle UI\nPress %s to restart" % Global.settings_manager.get_keybind_string("restart")) done_replay_btn.pressed.connect(func() -> void: return_control_to_player.emit()) - alt_speed_label.visible = settings_manager.value("Display", "speed") + alt_speed_label.visible = Global.settings_manager.value("Display", "speed") func set_frame(frame: int, total: int) -> void: tick_label.text = "Tick: %d / %d" % [frame + 1, total] replay_slider.value = frame replay_slider.max_value = total - 1 -func set_replay_inputs(frame) -> void: +func set_replay_inputs( + forward_input: bool, + back_input: bool, + left_input: bool, + right_input: bool, + shoot_input: bool, + ads_input: bool, + reload_input: bool, + ) -> void: replay_input_display.set_inputs( - frame.forward_input, - frame.back_input, - frame.left_input, - frame.right_input, - frame.shoot_input, - frame.ads_input, - frame.reload_input, + forward_input, + back_input, + left_input, + right_input, + shoot_input, + ads_input, + reload_input, ) func set_replay_visible(value: bool) -> void: diff --git a/src/recorder.gd b/src/recorder.gd index b994f09..5203e3a 100644 --- a/src/recorder.gd +++ b/src/recorder.gd @@ -103,7 +103,15 @@ func set_frame(value: int) -> void: else: map.map_ui.set_speed(speed) map.map_ui.set_timer(current_frame * dt) - map.map_ui.set_replay_inputs(frame) + map.map_ui.set_replay_inputs( + frame.forward_input, + frame.back_input, + frame.left_input, + frame.right_input, + frame.shoot_input, + frame.ads_input, + frame.reload_input, + ) if prev_frame.weapon_index != frame.weapon_index: controller.weapon_handler.set_weapon(Global.game_manager.get_weapon_from_index(frame.weapon_index), is_ghost) diff --git a/tests/replay_input_display_test.gd b/tests/replay_input_display_test.gd index 8b84a02..8b601aa 100644 --- a/tests/replay_input_display_test.gd +++ b/tests/replay_input_display_test.gd @@ -84,17 +84,8 @@ func _run() -> void: "MapUi replay_input_display export should reference ReplayContainer/V/ReplayInputDisplay", ) - if t.check(map_ui.has_method("set_replay_inputs"), "MapUi should expose set_replay_inputs(Recorder.Frame)"): - var frame := _RecorderFrame.new() - frame.forward_input = true - frame.back_input = false - frame.left_input = false - frame.right_input = true - frame.shoot_input = false - frame.ads_input = true - frame.reload_input = false - - map_ui.set_replay_inputs(frame) + if t.check(map_ui.has_method("set_replay_inputs"), "MapUi should expose set_replay_inputs(bool, bool, bool, bool, bool, bool, bool)"): + map_ui.set_replay_inputs(true, false, false, true, false, true, false) if replay_input_display != null: _check_input_state(t, replay_input_display, &"forward", true) _check_input_state(t, replay_input_display, &"right", true) @@ -126,12 +117,3 @@ func _panel_stylebox(replay_input_display: Node, label_path: String) -> StyleBox var parent_control := label.get_parent() as Control return parent_control.get_theme_stylebox("panel") if parent_control != null else null - -class _RecorderFrame: - var forward_input: bool - var back_input: bool - var left_input: bool - var right_input: bool - var shoot_input: bool - var ads_input: bool - var reload_input: bool From 965a9355c30f9c90d29feb923039829a0bce12ea Mon Sep 17 00:00:00 2001 From: Matthew Polak Date: Sat, 18 Jul 2026 14:18:19 -0700 Subject: [PATCH 6/6] fix: format replay input scripts Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c779ef78-f1e2-4b31-8c60-1b7fc8811ccb --- src/maps/map_ui.gd | 2 +- src/maps/replay_input_display.gd | 6 +++--- src/recorder.gd | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/maps/map_ui.gd b/src/maps/map_ui.gd index ad5ec75..04eb282 100644 --- a/src/maps/map_ui.gd +++ b/src/maps/map_ui.gd @@ -42,7 +42,7 @@ func set_replay_inputs( shoot_input: bool, ads_input: bool, reload_input: bool, - ) -> void: +) -> void: replay_input_display.set_inputs( forward_input, back_input, diff --git a/src/maps/replay_input_display.gd b/src/maps/replay_input_display.gd index b9953f9..f1c95b6 100644 --- a/src/maps/replay_input_display.gd +++ b/src/maps/replay_input_display.gd @@ -18,8 +18,8 @@ var _input_states := { &"reload": false, } -var _key_panels: Dictionary = {} -var _key_labels: Dictionary = {} +var _key_panels: Dictionary = { } +var _key_labels: Dictionary = { } var _active_panel_style: StyleBoxFlat var _inactive_panel_style: StyleBoxFlat @@ -47,7 +47,7 @@ func set_inputs( shoot_input: bool, ads_input: bool, reload_input: bool, - ) -> void: +) -> void: _input_states[&"forward"] = forward_input _input_states[&"back"] = back_input _input_states[&"left"] = left_input diff --git a/src/recorder.gd b/src/recorder.gd index 5203e3a..92a7971 100644 --- a/src/recorder.gd +++ b/src/recorder.gd @@ -103,7 +103,9 @@ func set_frame(value: int) -> void: else: map.map_ui.set_speed(speed) map.map_ui.set_timer(current_frame * dt) - map.map_ui.set_replay_inputs( + map \ + .map_ui \ + .set_replay_inputs( frame.forward_input, frame.back_input, frame.left_input,