Workers P2: Aufzug mit Abbruch und Reset

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-18 02:16:47 +00:00
1 parent 38261ecdd8
commit fb331de1a6
2 files changed
+397

No files matched your search

+169
View File
@@ -0,0 +1,169 @@
// Aufzug des Hustler-Hochhauses: Warteschlange, Zustandsautomat (Anfahren, Einsteigen, Fahren, Aussteigen) und Kabine.
// Portiert aus entwuerfe/scene-turm.js (Kabine Z. 316-330, Zustandsautomat Z. 466-492). Neu: cancel/reset geben die Kabine
// frei (die Regie bricht Gruppen ab, ohne dass der Aufzug hängen bleibt), ungültige Anfragen kommen abgebrochen zurück.
// Die Etagenlampen zeichnet building.js über at(i). Kein React, kein Appwrite, kein window/document.
import { cabTop } from './layout.js'
import { px, tint, PAL } from './sprites.js'
// Maße in Weltkoordinaten: cabWx = linke Kante des Fahrgasts in der Kabine (12 breiter Körper mittig in der 20 breiten Kabine),
// exitX = Ziel des ersten Schritts nach dem Aussteigen, slot0/slotStep = Wartepunkte vor dem Schacht (wer zuerst kommt, steht links),
// speed = Einheiten je Sekunde, cabH = Kabinenhöhe (gleich der in layout.js)
export const LIFT = Object.freeze({ cabWx: 22, exitX: 50, slot0: 52, slotStep: 14, speed: 110, cabH: 21 })
const CAB_X = 18 // linke Kante der Kabine (20 breit)
const CAB_W = 20
const ROPE_X = 28
const ROPE_TOP = 9 // das Seil hängt unter der Seilrolle am Schachtkopf
const RIDER_DY = 7 // Fahrgast: obere Kante des Körpers 7 unter der Kabinenoberkante (= Standhöhe auf der Etage)
const CLOSE_S = 0.3 // Türen schließen, bevor die Fahrt beginnt
const NEAR = 1.5 // at(i): Kabine gilt als auf der Etage, wenn sie höchstens so weit davon entfernt ist
const START_FLOOR = 2
const FRAME = PAL.metal2
const CAB_BACK = '#f1f3f8'
const CAB_RAIL = '#cfd5df'
const CAB_BASE = '#e1e5ec'
const CAB_LAMP = '#ffe9a8'
/* createLift(layout) → { y, state, request(agent, from, to), update(dt), cancel(agent), reset(), at(floorIndex), drawBack(ctx), drawFront(ctx) }
y = Oberkante der Kabine, state = idle | toPick | boarding | closing | riding | unloading.
request legt eine Anfrage { a, from, to, st } hinten an (FIFO); st läuft queued -> open -> boarded -> arrived -> out, außerdem cancelled.
boarded und out setzt der Fahrgast (die Regie), alle anderen Übergänge der Aufzug. Während der Fahrt (closing, riding) folgt der Agent
der Kabine: a.y = y + 7, a.x = cabWx, a.frame = 0.
Eine Anfrage mit ungültiger Etage (kein ganzer Index im Layout) oder ohne Agenten-Objekt kommt sofort als cancelled zurück und wird nie bedient. */
export function createLift(layout) {
const floorCount = layout.floors.length
const isFloor = (i) => Number.isInteger(i) && i >= 0 && i < floorCount
const lift = { y: cabTop(layout, Math.min(START_FLOOR, floorCount - 1)), state: 'idle' }
let queue = [] // wartende Anfragen, vorne die nächste
let req = null // laufende Anfrage
let closeT = 0
// Kabine frei: keine laufende Anfrage, Leerlauf (die Kabine bleibt, wo sie ist)
function release() {
req = null
lift.state = 'idle'
closeT = 0
}
// Fährt um höchstens speed * dt auf ty zu; true = angekommen
function cabMove(ty, dt) {
const d = ty - lift.y
const s = LIFT.speed * dt
if (Math.abs(d) <= s) {
lift.y = ty
return true
}
lift.y += d > 0 ? s : -s
return false
}
// Der Fahrgast steht in der Kabine
function follow() {
if (!req) return
req.a.y = lift.y + RIDER_DY
req.a.x = LIFT.cabWx
req.a.frame = 0
}
lift.request = function request(a, from, to) {
const r = { a, from, to, st: 'queued' }
if (!a || typeof a !== 'object' || !isFloor(from) || !isFloor(to)) {
r.st = 'cancelled'
return r
}
queue.push(r)
return r
}
lift.update = function update(dt) {
if (!Number.isFinite(dt) || dt <= 0) return
// von außen abgebrochene laufende Anfrage: Kabine frei, kein Festhängen in boarding
if (req && req.st === 'cancelled') release()
if (lift.state === 'idle') {
while (queue.length && queue[0].st === 'cancelled') queue.shift()
if (queue.length) {
req = queue.shift()
lift.state = 'toPick'
}
}
if (lift.state === 'toPick') {
if (cabMove(cabTop(layout, req.from), dt)) {
req.st = 'open'
lift.state = 'boarding'
}
} else if (lift.state === 'boarding') {
if (req.st === 'boarded') {
lift.state = 'closing'
closeT = 0
}
} else if (lift.state === 'closing') {
closeT += dt
follow()
if (closeT > CLOSE_S) lift.state = 'riding'
} else if (lift.state === 'riding') {
// erst fahren, dann mitnehmen: der Fahrgast hinkt der Kabine nicht um einen Schritt hinterher (die Vorlage tut es)
const arrived = cabMove(cabTop(layout, req.to), dt)
follow()
if (arrived) {
req.st = 'arrived'
lift.state = 'unloading'
}
} else if (lift.state === 'unloading') {
if (req.st === 'out') release()
}
}
// Alle Anfragen des Agenten abbrechen; steckt er in der Kabine, ist sie danach frei (der Agent wird nicht mehr bewegt).
// Ein bereits ausgestiegener Fahrgast (out) bleibt out.
lift.cancel = function cancel(agent) {
const keep = []
for (const r of queue) {
if (r.a === agent) r.st = 'cancelled'
else keep.push(r)
}
queue = keep
if (req && req.a === agent) {
if (req.st !== 'out') req.st = 'cancelled'
release()
}
}
// Alle wartenden und die laufende Anfrage abbrechen (Schnappschuss, Neuaufbau der Szene); die Kabine bleibt stehen
lift.reset = function reset() {
for (const r of queue) r.st = 'cancelled'
queue = []
if (req && req.st !== 'out') req.st = 'cancelled'
release()
}
// Steht die Kabine auf Etage i? (Etagenlampe grün); ungültiger Index -> false
lift.at = function at(i) {
return isFloor(i) && Math.abs(lift.y - cabTop(layout, i)) < NEAR
}
// Seil und Rückwand: vor den Figuren zeichnen
lift.drawBack = function drawBack(ctx) {
const cy = Math.round(lift.y)
px(ctx, ROPE_X, ROPE_TOP, 1, cy - ROPE_TOP, FRAME)
px(ctx, CAB_X, cy, CAB_W, LIFT.cabH, CAB_BACK)
px(ctx, CAB_X + 1, cy + 11, CAB_W - 2, 1, CAB_RAIL)
px(ctx, CAB_X + 1, cy + 18, CAB_W - 2, 2, CAB_BASE)
}
// Glasschleier, Rahmen, Lampe und Aufhängung: nach den Figuren zeichnen
lift.drawFront = function drawFront(ctx) {
const cy = Math.round(lift.y)
tint(ctx, CAB_X + 1, cy + 1, CAB_W - 2, LIFT.cabH - 2, '#ffffff', 0.18)
px(ctx, CAB_X, cy, CAB_W, 1, FRAME)
px(ctx, CAB_X, cy + LIFT.cabH - 1, CAB_W, 1, FRAME)
px(ctx, CAB_X, cy, 1, LIFT.cabH, FRAME)
px(ctx, CAB_X + CAB_W - 1, cy, 1, LIFT.cabH, FRAME)
px(ctx, 24, cy + 1, 8, 1, CAB_LAMP)
px(ctx, 26, cy - 2, 4, 2, FRAME)
}
return lift
}