Expand Glitch operations hub, cabinet authoring, deployment, and RFID support

Operator hub
- Introduce Escape Gallery branding and Glitch: the Dark Machine room views
- Add cabinet illustrations, Pac-Man marquee/gameplay artwork, and waiting states
- Add device feedback panels with RFID readings, input/output states, and stale status
- Add logged operator hardware interventions with request deduplication

Room and cabinet authoring
- Add a visual custom-PCB connection editor with per-cabinet device inventory
- Support device IDs, bindings, wiring notes, input polarity, and validation
- Add physical-room label/dimension toggles and editable doorway annotations
- Preserve doorway changes through undo, save, and revert
- Expand cabinet profiles, room layout, and Glitch scenario resources

Scenarios and gameplay
- Add per-assignment game configuration and runtime feature validation
- Add timed test wins and Frogger individual-home objectives
- Add the three-cabinet timed/score/Frogger test scenario
- Add Cabinet 1 Pac-Man RFID credits, credit sound, and CREDIT 0/1 display
- Require physical Start after an accepted scan and prevent duplicate credits

Pi hardware and deployment
- Add GPIO feedback sampling and a separate multiplexed RFID helper
- Validate reader pin ownership and input/output conflicts
- Add SSH cabinet startup and Pi desktop-session service support
- Add master packaging, site setup, SSH enrollment, and staged cabinet releases
- Verify deployment manifests and support explicit activation/status/log commands

Documentation and verification
- Consolidate the root README around the current architecture and setup workflow
- Add the complete master/Pi/RFID export and deployment walkthrough
- Document network isolation, credentials, card identification, and rollback
- Add PCB, room-editor, feedback, credit, deployment, and controller tests
- Isolate protocol tests from designer-edited cabinet configurations

Known limitations
- RFID helper compilation and physical Pi commissioning remain unverified
- Fleet provisioning does not yet configure RFID-only installations completely
- Broader Pac-Man tests retain a ghost-capture assertion failure
- Staged exports must be rebuilt and checked before deploying runtime 2.3
This commit is contained in:
Jeff Luke committed 2026-10-17 14:53:54 -07:00
1 parent 705ec31e0b
commit c1b2195834
90 files changed
+4969 -456

No files matched your search

+80 -1
View File
@@ -231,7 +231,12 @@ var simulation_driver := EditorSimulationDriver.new()
var editor_selection := EscapeRoomEditorSelection.new()
var doorway_tool: Button
var doorway_check: CheckBox
var swing_check: CheckBox
func _ready() -> void:
_setup_view_controls()
select_tool_button.button_pressed = true
@@ -1503,6 +1508,9 @@ func _on_show_connections_check_box_toggled(
)
func _clear_property_panel() -> void:
if doorway_check != null:
doorway_check.disabled = true
swing_check.disabled = true
selected_label.text = (
"Nothing selected"
@@ -1668,6 +1676,7 @@ func _on_wall_creation_requested(
wall.start = start
wall.end = end
wall.thickness = 0.10
wall.is_doorway = doorway_tool.button_pressed
if undo_redo == null:
_add_wall(
@@ -1828,10 +1837,15 @@ func _refresh_wall_property_panel(
return
selected_label.text = (
"Wall: %s"
("Doorway: %s" if wall.is_doorway else "Wall: %s")
% wall.wall_id
)
doorway_check.disabled = false
doorway_check.set_pressed_no_signal(wall.is_doorway)
swing_check.set_pressed_no_signal(wall.swing_reversed)
swing_check.disabled = not wall.is_doorway
wall_start_x_spin_box.set_value_no_signal(
wall.start.x * METERS_TO_FEET
)
@@ -3001,3 +3015,68 @@ func _on_room_layout_refresh_requested() -> void:
status_label.text = (
"Physical room refreshed."
)
func _setup_view_controls() -> void:
var toolbar := draw_wall_button.get_parent()
doorway_tool = Button.new()
doorway_tool.text = "DRAW DOORWAY"
doorway_tool.toggle_mode = true
doorway_tool.button_group = draw_wall_button.button_group
doorway_tool.tooltip_text = "Drag from hinge to opposite jamb. Align with a wall to create an opening."
toolbar.add_child(doorway_tool)
toolbar.move_child(doorway_tool, draw_wall_button.get_index() + 1)
doorway_tool.pressed.connect(func(): room_layout.set_edit_mode(RoomLayout.EditMode.DRAW_WALL))
var view_bar := HBoxContainer.new()
toolbar.get_parent().add_child(view_bar)
toolbar.get_parent().move_child(view_bar, toolbar.get_index() + 1)
for title in ["Labels", "Dimensions"]:
var toggle := CheckBox.new()
toggle.text = title
toggle.button_pressed = true
view_bar.add_child(toggle)
if title == "Labels":
toggle.toggled.connect(room_layout.set_labels_visible)
else:
toggle.toggled.connect(room_layout.set_dimensions_visible)
doorway_check = CheckBox.new()
doorway_check.text = "Doorway opening"
doorway_check.disabled = true
doorway_check.toggled.connect(func(value: bool): _commit_door_setting("is_doorway", value))
swing_check = CheckBox.new()
swing_check.text = "Reverse door swing"
swing_check.disabled = true
swing_check.toggled.connect(func(value: bool): _commit_door_setting("swing_reversed", value))
var properties := delete_wall_button.get_parent()
properties.add_child(doorway_check)
properties.add_child(swing_check)
properties.move_child(doorway_check, delete_wall_button.get_index())
properties.move_child(swing_check, delete_wall_button.get_index())
func _commit_door_setting(property: String, value: bool) -> void:
if context == null:
return
var target := context.get_room()
var wall := target.get_wall(room_layout.selected_wall_id)
if wall == null:
doorway_check.set_pressed_no_signal(false)
swing_check.set_pressed_no_signal(false)
return
var old: bool = wall.get(property)
if old == value:
return
if undo_redo == null:
_apply_door_setting(target, wall, property, value)
return
undo_redo.create_action("Edit doorway", UndoRedo.MERGE_DISABLE, target)
undo_redo.add_do_method(self, "_apply_door_setting", target, wall, property, value)
undo_redo.add_undo_method(self, "_apply_door_setting", target, wall, property, old)
undo_redo.commit_action()
func _apply_door_setting(target: RoomDefinition, wall: RoomWallDefinition, property: String, value: bool) -> void:
if not target.walls.has(wall):
return
wall.set(property, value)
context.room_edit_session.mark_dirty()
room_layout.queue_redraw()
_refresh_wall_property_panel(wall.wall_id)