Add Pac-Man power-on flow and document Odin integration
- Add a four-second arcade-style boot sequence followed by the Character / Nickname introduction. - Separate cabinet power-on from RFID credit and player Start. - Add operator Power on control and cabinet power-state reporting. - Prevent repeated power commands from restarting boot or gameplay. - Ignore RFID scans received before the cabinet is ready. - Add power-state, RFID, controller, and GPIO feedback regression tests. - Expand the master walkthrough with export, packaging, file transfer, fresh-Pi setup, RFID compilation, and end-to-end testing instructions. - Correct tester hardware documentation: BCM23/J20 drives an LED; physical buttons connect through a separate I2C1 input board. - Document operator-triggered power-on as the current test workflow and retain the temporary 2.4 configuration compatibility notes. - Record successful physical testing of power-on, RFID credit, and player Start. - Add an Odin integration README describing the proposed Rust/NATS bridge, shared messages, Flutter GM interface, autopilot sequencing, hardware ownership, and Starfish SSH-management role. - Define migration milestones and information needed from the framework owner; link the integration guide from the root README. Validation: 69 controller tests, 10 GPIO tests, Pac-Man power/RFID checks, managed-cabinet lifecycle checks, and visual screen inspection.
This commit is contained in:
1 parent
c1b2195834
commit
41ef12f954
27 files changed
+577
-48
No files matched your search
+50
-2
@@ -13,6 +13,10 @@ var model = Model.new()
|
||||
var view
|
||||
var audio
|
||||
var accumulator := 0.0
|
||||
signal power_state_changed(state: String)
|
||||
const BOOT_SECONDS := 4.0
|
||||
var power_state := "off"
|
||||
var boot_elapsed := 0.0
|
||||
var credits := 0
|
||||
var rfid_seen: Dictionary = {}
|
||||
var rfid_initialized := false
|
||||
@@ -30,7 +34,11 @@ func receive_device_feedback(feedback: Dictionary) -> void:
|
||||
var ident := str(entry.get("id", ""))
|
||||
if ident.is_empty() or rfid_seen.has(ident): continue
|
||||
rfid_seen[ident] = true
|
||||
if device_id.is_empty() or is_running or credits > 0 or model.state != "idle": continue
|
||||
if power_state == "off" and entry.get("device") == run_config.get("power_on_device", "") and entry.get("kind") == "input" and entry.get("value") == true and str(entry.get("error", "")).is_empty():
|
||||
for sample in feedback.get("devices", []):
|
||||
if sample.get("id") == entry.get("device") and int(entry.get("age_ms", 999999)) < 2000 and int(sample.get("age_ms", 999999)) < 2000 and str(sample.get("error", "")).is_empty():
|
||||
power_on()
|
||||
if power_state != "ready" or device_id.is_empty() or is_running or credits > 0 or model.state != "idle": continue
|
||||
if entry.get("device") != device_id or entry.get("kind") != "rfid" or not str(entry.get("error", "")).is_empty(): continue
|
||||
var uid := str(entry.get("value", "")).to_upper()
|
||||
if uid.is_empty(): continue
|
||||
@@ -57,13 +65,40 @@ func _ready() -> void:
|
||||
model.sound_requested.connect(audio.play_effect)
|
||||
model.finished.connect(_on_finished)
|
||||
_apply_config()
|
||||
if str(run_config.get("power_on_device", "")).is_empty(): power_on()
|
||||
|
||||
func configure(config: Dictionary = {}) -> void:
|
||||
power_off()
|
||||
super.configure(config)
|
||||
credits = 0
|
||||
rfid_seen.clear()
|
||||
rfid_initialized = false
|
||||
_apply_config()
|
||||
if str(run_config.get("power_on_device", "")).is_empty(): power_on()
|
||||
|
||||
func power_on() -> bool:
|
||||
if power_state != "off": return false
|
||||
boot_elapsed = 0.0
|
||||
power_state = "booting"
|
||||
view.power_state = power_state
|
||||
view.startup_time = 0.0
|
||||
audio.stop_all()
|
||||
power_state_changed.emit(power_state)
|
||||
return true
|
||||
|
||||
func power_off() -> void:
|
||||
is_running = false
|
||||
credits = 0
|
||||
boot_elapsed = 0.0
|
||||
power_state = "off"
|
||||
model.reset()
|
||||
accumulator = 0.0
|
||||
if audio != null: audio.stop_all()
|
||||
if view != null:
|
||||
view.power_state = power_state
|
||||
view.startup_time = 0.0
|
||||
view.credits = 0
|
||||
power_state_changed.emit(power_state)
|
||||
|
||||
func _apply_config() -> void:
|
||||
if view != null:
|
||||
@@ -75,7 +110,7 @@ func _apply_config() -> void:
|
||||
model.extra_life_score = maxi(0, int(run_config.get("extra_life_score", extra_life_score)))
|
||||
|
||||
func start_game() -> bool:
|
||||
if is_running: return false
|
||||
if is_running or power_state != "ready": return false
|
||||
if not str(run_config.get("rfid_credit_device", "")).is_empty():
|
||||
if credits < 1: return false
|
||||
credits -= 1
|
||||
@@ -117,6 +152,19 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed(pair[0]): model.requested = pair[1]
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if power_state == "off": return
|
||||
if power_state == "booting":
|
||||
boot_elapsed = minf(BOOT_SECONDS, boot_elapsed + delta)
|
||||
view.startup_time = boot_elapsed
|
||||
if boot_elapsed >= BOOT_SECONDS:
|
||||
power_state = "ready"
|
||||
view.power_state = power_state
|
||||
view.startup_time = 0.0
|
||||
# Ignore scans collected while the ROM check was running.
|
||||
rfid_initialized = false
|
||||
power_state_changed.emit(power_state)
|
||||
return
|
||||
if not is_running: view.startup_time += delta
|
||||
if not is_running:
|
||||
model.clock += delta
|
||||
if is_running:
|
||||
|
||||
Reference in new issue
Block a user