Auto-resume flight across chunk unload/reload

When the ship's chunk unloads the CC computer powers off and its redstone
outputs reset to 0, so on return the ship dropped and the pilot had to
re-ENGAGE every time. Flight state now survives the reboot:

- engaged.txt flag written while flying, cleared on pilot STOP.
- On boot, if engaged + profile present: drive the hover baseline
  immediately (closes the fall gap), then runStandby(true) re-enters the
  flight loop automatically with the persisted target -- no manual ENGAGE.
- runFly drives hover before its first sleep so no entry path leaves a
  0-output gap.
- Cleanup no longer lifts a PARKED ship: Ctrl+T out of standby holds hover
  only when engaged, else valves off.
- engaged.txt registered as per-ship state (gitignore, installer keep/reset,
  menu reset, build excludes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-25 17:55:15 +02:00
parent a94eff9283
commit 3e27b3f52b
5 changed files with 84 additions and 17 deletions

View File

@@ -97,6 +97,20 @@ local function profileComplete()
return loadCalib().signMeasured == true
end
-- ENGASJERT-flagg: finnes filen <=> autopiloten FLYR aktivt (etter ENGAGE/FLY, foer STOP).
-- Dette er kjernen i restart-overlevelsen: en chunk-unload slaar av datamaskinen, og reboot
-- nullstiller redstone -> skipet ville falle. Ligger flagget der ved boot, gjenopptar vi flukten
-- automatisk (og holder hover umiddelbart), i stedet for aa vente i standby paa manuell ENGAGE.
local ENGAGED_FILE = "engaged.txt"
local function setEngaged(on)
if on then
local f = fs.open(ENGAGED_FILE, "w"); f.write("1"); f.close()
elseif fs.exists(ENGAGED_FILE) then
fs.delete(ENGAGED_FILE)
end
end
local function isEngaged() return fs.exists(ENGAGED_FILE) end
-- live brukerinnstillinger (justeres fra pilot-konsollen), persistert. Syntetiske noekler -> cfg.
local SETTINGS_FILE = "settings.txt"
local function saveSettings(t) local f = fs.open(SETTINGS_FILE, "w"); f.write(textutils.serialise(t)); f.close() end
@@ -713,6 +727,11 @@ local function runFly()
print(""); print("[Enter]"); read(); return
end
-- driv hover FOERST -- lukker fall-gapet (spesielt paa auto-resume etter reboot der
-- redstone kom tilbake paa 0) foer loopen og status-broadcasten kommer i gang.
driveValves(calib.uHoverFront, calib.uHoverRear)
setEngaged(true) -- markér at vi FLYR -> overlever chunk-reload (auto-resume ved boot)
local launchAlt = sensors.readAltitude() or 0
local target = loadTarget() or (launchAlt + cfg.takeoffOffset)
local est = Estimator.new(cfg)
@@ -739,6 +758,8 @@ local function runFly()
elseif msg.cmd == "set" then applySetting(msg.key, msg.value, true)
elseif msg.cmd == "stop" then stopReq = true end
end
-- pilot-STOP er en eksplisitt "slutt aa fly" -> ryd engasjert-flagget saa en senere reload
-- IKKE gjenopptar av seg selv. (Ctrl+T beholder flagget: da vil du typisk fortsette senere.)
-- vent ~dt sekunder MEN behandle innkommende kommandoer underveis (ikke-blokkerende styring)
local function serve(secs)
local timer = os.startTimer(secs)
@@ -823,7 +844,10 @@ local function runFly()
})
serve(cfg.dt) -- vent dt + behandle pilot-kommandoer (erstatter sleep)
if stopReq then print("Pilot STOP -- holding hover, back to standby."); break end
if stopReq then
setEngaged(false) -- eksplisitt stopp -> ikke auto-resume etter neste reload
print("Pilot STOP -- holding hover, back to standby."); break
end
end
end
@@ -898,7 +922,21 @@ local function awaitCmd(secs)
end
end
local function runStandby()
local function runStandby(autoResume)
link.open()
-- AUTO-RESUME: fl...y da chunken unloadet -> gjenoppta UMIDDELBART (ingen fall, ingen manuell
-- ENGAGE). Bare paa boot-inngangen; manuell "standby" fra menyen gjor det ikke.
if autoResume and isEngaged() and fs.exists(CALIB_FILE) then
term.clear(); term.setCursorPos(1, 1)
print("==== RESUMING FLIGHT ====")
print("Autopilot was flying before the chunk")
print("reloaded -- re-engaging automatically.")
print("(Pilot STOP to stop; it will NOT resume")
print(" after a stop.)")
sleep(0.6)
runFly() -- blokkerer til STOP / Ctrl+T
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear)
end
while true do
-- re-proev modem hver runde: fanger et modem som plugges til mens vi staar i standby
local hasModem = link.open()
@@ -939,6 +977,11 @@ end
-- Foerste boot uten komplett profil -> tilby wizarden. Timeout -> standby, slik at en ubemannet
-- reboot (chunk-reload) aldri blir staaende og vente paa tastetrykk.
local function runBoot()
-- ALLER FOERST: fl...y vi da chunken unloadet? Driv hover UMIDDELBART saa skipet ikke rekker
-- aa falle mens vi tegner skjerm / venter. runStandby(true) gjenopptar selve reguleringen.
if isEngaged() and fs.exists(CALIB_FILE) then
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear)
end
if not profileComplete() then
term.clear(); term.setCursorPos(1, 1)
print("==== AIRSHIP AUTOPILOT ====")
@@ -959,7 +1002,7 @@ local function runBoot()
end
end
end
runStandby()
runStandby(true) -- true = auto-resume flukt hvis vi fløy da chunken unloadet
end
-- ============================ PROFIL / MENY ============================
@@ -1015,7 +1058,7 @@ local function menu()
term.clear(); term.setCursorPos(1, 1); showProfile()
print("\nR = reset SHIP PROFILE (new ship), Enter = back"); local a = read():lower()
if a == "r" then
for _, f in ipairs({ CALIB_FILE, SIDES_FILE, TARGET_FILE, SETTINGS_FILE }) do
for _, f in ipairs({ CALIB_FILE, SIDES_FILE, TARGET_FILE, SETTINGS_FILE, ENGAGED_FILE }) do
if fs.exists(f) then fs.delete(f) end
end
print("Ship profile reset -- setup wizard on next boot."); print("[Enter]"); read()
@@ -1060,20 +1103,24 @@ local function runMode(mode, a2, a3)
end)
-- opprydding: aktive driv-modus -> hold hover (aldri etterlat drivende uten beskjed);
-- monitor styrer ingenting (la utgangene staa).
local function holdHoverOut()
if fs.exists(CALIB_FILE) then
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear) -- hold, ikke null (aldri stup)
else
allOff()
end
end
if mode == "manual" or mode == "wiring" then
allOff()
elseif mode == "signtest" or mode == "setup" then
-- Ctrl+T midt i sign-testen: luftbaaren -> hold hover (ALDRI frittfall); paa bakken -> alt av
if signtestAirborne then
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear)
else
allOff()
end
elseif mode == "cal" or mode == "fly" or mode == "observe" or mode == "standby" or mode == "boot" then
if fs.exists(CALIB_FILE) then
local c = loadCalib(); driveValves(c.uHoverFront, c.uHoverRear) -- hold, ikke null (aldri stup)
else
allOff() -- aldri floeyet under vaar kontroll -> skipet staar paa bakken; ikke varm brennere
if signtestAirborne then holdHoverOut() else allOff() end
elseif mode == "cal" or mode == "fly" or mode == "observe" then
holdHoverOut() -- disse fl...yr aktivt -> hold hover ved avslutning
elseif mode == "boot" or mode == "standby" then
-- Ctrl+T ut av standby: hold hover KUN hvis vi faktisk fløy (engasjert). Et parkert skip
-- paa bakken skal IKKE begynne aa varme brennere og lette av seg selv.
if isEngaged() then holdHoverOut() else allOff()
end
end
if not ok and err ~= "Terminated" then print("ERROR: " .. tostring(err)); print("[Enter]"); read() end