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:
1 parent
705ec31e0b
commit
c1b2195834
90 files changed
+4969
-456
No files matched your search
+79
-11
@@ -82,6 +82,8 @@ var highlighted_bindings: Dictionary = {}
|
||||
|
||||
var binding_labels: Array[Label] = []
|
||||
var show_connections: bool = true
|
||||
var show_labels: bool = true
|
||||
var show_dimensions: bool = true
|
||||
|
||||
var selected_binding_key: String = ""
|
||||
|
||||
@@ -178,6 +180,7 @@ func _rebuild() -> void:
|
||||
_build_cabinets()
|
||||
_build_devices()
|
||||
_build_binding_labels()
|
||||
set_labels_visible(show_labels)
|
||||
|
||||
queue_redraw()
|
||||
|
||||
@@ -976,7 +979,7 @@ func set_connections_visible(
|
||||
show_connections = visible
|
||||
|
||||
for label in binding_labels:
|
||||
label.visible = visible
|
||||
label.visible = visible and show_labels
|
||||
|
||||
queue_redraw()
|
||||
|
||||
@@ -1703,7 +1706,7 @@ func _draw_dimension_label(
|
||||
text: String
|
||||
) -> void:
|
||||
|
||||
if text.is_empty():
|
||||
if not show_dimensions or text.is_empty():
|
||||
return
|
||||
|
||||
var font := (
|
||||
@@ -2075,7 +2078,7 @@ func _draw_walls() -> void:
|
||||
if room == null:
|
||||
return
|
||||
|
||||
for wall in room.walls:
|
||||
for wall in _ordered_walls():
|
||||
if wall == null:
|
||||
continue
|
||||
|
||||
@@ -2111,13 +2114,7 @@ func _draw_walls() -> void:
|
||||
|
||||
width += 3.0
|
||||
|
||||
draw_line(
|
||||
start,
|
||||
end,
|
||||
wall_color,
|
||||
width,
|
||||
true
|
||||
)
|
||||
_draw_wall_or_door(wall, start, end, wall_color, width)
|
||||
|
||||
#
|
||||
# Wall dimension.
|
||||
@@ -2867,7 +2864,9 @@ func _find_wall_at_screen_position(
|
||||
|
||||
var hit_radius_pixels: float = 10.0
|
||||
|
||||
for wall in room.walls:
|
||||
var ordered := _ordered_walls()
|
||||
ordered.reverse()
|
||||
for wall in ordered:
|
||||
if wall == null:
|
||||
continue
|
||||
|
||||
@@ -3987,3 +3986,72 @@ func _get_cabinet_profile(
|
||||
return assignment.cabinet
|
||||
|
||||
return null
|
||||
|
||||
|
||||
func set_labels_visible(value: bool) -> void:
|
||||
show_labels = value
|
||||
for item in item_nodes.values():
|
||||
item.title_label.visible = value
|
||||
item.subtitle_label.visible = value
|
||||
item.tooltip_text = item.base_title + "\n" + item.base_subtitle
|
||||
for label in binding_labels:
|
||||
label.visible = value and show_connections
|
||||
queue_redraw()
|
||||
|
||||
func set_dimensions_visible(value: bool) -> void:
|
||||
show_dimensions = value
|
||||
queue_redraw()
|
||||
|
||||
func _ordered_walls() -> Array[RoomWallDefinition]:
|
||||
var ordered: Array[RoomWallDefinition] = []
|
||||
for wall in room.walls:
|
||||
if wall != null and not wall.is_doorway:
|
||||
ordered.append(wall)
|
||||
for wall in room.walls:
|
||||
if wall != null and wall.is_doorway:
|
||||
ordered.append(wall)
|
||||
return ordered
|
||||
|
||||
# Openings are projected onto collinear wall segments without destroying geometry.
|
||||
func _wall_solid_ranges(wall: RoomWallDefinition) -> Array[Vector2]:
|
||||
var ranges: Array[Vector2] = [Vector2(0, 1)]
|
||||
var delta := wall.end - wall.start
|
||||
if delta.length_squared() < 0.000001:
|
||||
return ranges
|
||||
for door in room.walls:
|
||||
if door == null or not door.is_doorway:
|
||||
continue
|
||||
var tolerance := maxf(wall.thickness * 0.5, 0.02)
|
||||
if absf(delta.cross(door.start - wall.start)) / delta.length() > tolerance or absf(delta.cross(door.end - wall.start)) / delta.length() > tolerance:
|
||||
continue
|
||||
var a := delta.dot(door.start - wall.start) / delta.length_squared()
|
||||
var b := delta.dot(door.end - wall.start) / delta.length_squared()
|
||||
var low := minf(a, b)
|
||||
var high := maxf(a, b)
|
||||
var next: Array[Vector2] = []
|
||||
for segment in ranges:
|
||||
if high <= segment.x or low >= segment.y:
|
||||
next.append(segment)
|
||||
else:
|
||||
if low > segment.x:
|
||||
next.append(Vector2(segment.x, low))
|
||||
if high < segment.y:
|
||||
next.append(Vector2(high, segment.y))
|
||||
ranges = next
|
||||
return ranges
|
||||
|
||||
func _draw_wall_or_door(wall: RoomWallDefinition, start: Vector2, end: Vector2, color: Color, width: float) -> void:
|
||||
if not wall.is_doorway:
|
||||
for segment in _wall_solid_ranges(wall):
|
||||
draw_line(start.lerp(end, segment.x), start.lerp(end, segment.y), color, width, true)
|
||||
return
|
||||
var direction := end - start
|
||||
if direction.length() < 1.0:
|
||||
return
|
||||
var angle := direction.angle()
|
||||
var swing := -PI / 2.0 if wall.swing_reversed else PI / 2.0
|
||||
var door_color := Color("67d5c5") if wall.wall_id != selected_wall_id else color
|
||||
draw_dashed_line(start, end, door_color, 1.5, 5.0)
|
||||
draw_line(start, start + direction.rotated(swing), door_color, 2.0, true)
|
||||
draw_arc(start, direction.length(), minf(angle, angle + swing), maxf(angle, angle + swing), 32, door_color, 1.5, true)
|
||||
draw_circle(start, 4.0, door_color)
|
||||
Reference in new issue
Block a user