Der Effektort spots.archiv lag auf Arne (x 130, y 231) statt auf einem gezeichneten Schrank; er sitzt jetzt in der Mitte der obersten Schublade des ersten Aktenschranks (x 148). Graue Räume behalten den Teamfarbton (Alpha 0,10) unter der Grautönung; das Schild ersetzt dort weiter den Titel (Platz). Neue Tests: Zeichnung trifft den Spot für pult, tresor, archiv; grauer Raum mit Tint ohne Titel. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
135 lines
5.6 KiB
JavaScript
135 lines
5.6 KiB
JavaScript
// Hochhaus-Layout als Daten: Etagen, Räume und Heimatplätze der Figuren in der Welt 320 × 256 (Übersicht §3).
|
||
// Zeichnen (rooms, building) und Regie (director) lesen nur diese Tabelle; ein neuer Raum ist eine Zeile in
|
||
// ROOM_META plus eine Rolle. Kein React, kein Appwrite, kein window/document.
|
||
import { ROLES, FLOORS, FLOOR_TITLES, ROOM_TITLES } from '../../../lib/workersRoster.js'
|
||
|
||
export const WORLD = Object.freeze({ w: 320, h: 256 })
|
||
export const FLOOR_PITCH = 23 // Etage 22 hoch plus eine Bodenlinie
|
||
export const BRIDGE_H = 34 // die Kommandobrücke (Etage 0) ist höher
|
||
|
||
const TOP0 = 6 // Oberkante der Brücke
|
||
const FLOOR_H = 22
|
||
const ROOM_X0 = 42 // Räume stehen zwischen x 42 und 228 (Schacht und Etagenschilder liegen daneben)
|
||
const ROOM_W = 186
|
||
const HOME_DY = 14 // Figurenhöhe: Platz y = Boden - 14
|
||
const OTTO_DY = 27 // Otto steht höher (Kommandomütze, 24 breit)
|
||
const OTTO_W = 24
|
||
const WORKER_W = 12
|
||
const CAB_H = 21 // Kabinenhöhe des Aufzugs (lift.js importiert layout.js, nicht umgekehrt)
|
||
|
||
const meta = (kind, team) => Object.freeze({ kind, team })
|
||
|
||
// Raum -> Art der Zeichnung und Teamfarbe (Schlüssel aus TEAM in sprites.js)
|
||
export const ROOM_META = Object.freeze({
|
||
bruecke: meta('bruecke', 'freigabe'),
|
||
netz: meta('radar', 'radar'),
|
||
kleinanzeigen: meta('inbox', 'radar'),
|
||
portale: meta('feed', 'radar'),
|
||
foren: meta('board', 'radar'),
|
||
aerzte: meta('scout', 'aerzte'),
|
||
handwerk: meta('scout', 'handwerk'),
|
||
selbst: meta('scout', 'selbst'),
|
||
laden: meta('scout', 'laden'),
|
||
waage: meta('waage', 'bewertung'),
|
||
mail: meta('mail', 'freigabe'),
|
||
forum: meta('schreiber', 'freigabe'),
|
||
posteingang: meta('sortier', 'freigabe'),
|
||
abschluss: meta('vertrag', 'freigabe'),
|
||
tisch1: meta('tisch', 'werkstatt'),
|
||
tisch2: meta('tisch', 'werkstatt'),
|
||
tisch3: meta('tisch', 'werkstatt'),
|
||
pruefung: meta('pruefung', 'pruefung'),
|
||
pult: meta('pult', 'freigabe'),
|
||
tresor: meta('tresor', 'tresor'),
|
||
archiv: meta('archiv', 'archiv'),
|
||
})
|
||
|
||
// Räume ohne Worker: stehen in ihrer Etage hinter den Räumen der Rollen
|
||
const WORKERLESS = Object.freeze({ freigabe: ['pult'], tresor: ['tresor'] })
|
||
|
||
// Etage i: Brücke oben, danach im Raster 23 (Oberkante 41, 64, 87, ...); floorY ist die Bodenlinie
|
||
function floorBox(i) {
|
||
const h = i === 0 ? BRIDGE_H : FLOOR_H
|
||
const top = i === 0 ? TOP0 : TOP0 + BRIDGE_H + 1 + FLOOR_PITCH * (i - 1)
|
||
return { top, floorY: top + h, h }
|
||
}
|
||
|
||
// n Räume teilen die Breite gleichmäßig; der letzte nimmt den Rest (4 Räume: 46, 46, 46, 48)
|
||
function splitRooms(n) {
|
||
const w = Math.floor(ROOM_W / n)
|
||
return Array.from({ length: n }, (_, i) => ({ x: ROOM_X0 + i * w, w: i === n - 1 ? ROOM_W - w * (n - 1) : w }))
|
||
}
|
||
|
||
// Orte für Effekte: Briefkasten am Pult (Spitze), Tresor (Mitte der Tür), Schublade im Archiv (erster Aktenschrank, rechts neben
|
||
// Arne; rooms.js zeichnet die Schränke ab Raum + 100) und ein Punkt rechts außerhalb des Turms. Der Test in workersRooms.test.mjs
|
||
// prüft, dass die Zeichnung jeden dieser Orte trifft. Fehlt die Etage im Layout (nur bei eigenen Etagenlisten), ist der Ort null.
|
||
function buildSpots(floors) {
|
||
const at = (floorId, dx, dy) => {
|
||
const f = floors.find((fl) => fl.id === floorId)
|
||
return f ? { x: ROOM_X0 + dx, y: f.floorY + dy } : null
|
||
}
|
||
const tresor = at('tresor', 121, -11)
|
||
return {
|
||
pult: at('freigabe', 93, -15),
|
||
tresor,
|
||
aussen: tresor && { x: WORLD.w + 4, y: tresor.y - 1 },
|
||
archiv: at('keller', 106, -16),
|
||
}
|
||
}
|
||
|
||
// Layout aus Rollen und Etagenliste (Standard: das Rollen-Register). Wirft bei unbekanntem Raum oder unbekannter Etage.
|
||
export function buildLayout(roles = ROLES, floors = FLOORS) {
|
||
for (const r of roles) {
|
||
if (!Object.hasOwn(ROOM_META, r.room)) throw new Error(`Unbekannter Raum: ${r.room}`)
|
||
if (!floors.includes(r.floor)) throw new Error(`Unbekannte Etage: ${r.floor}`)
|
||
}
|
||
const homes = {}
|
||
const list = floors.map((id, index) => {
|
||
const box = floorBox(index)
|
||
const entries = roles.filter((r) => r.floor === id).map((r) => ({ id: r.room, workerId: r.workerId }))
|
||
for (const room of WORKERLESS[id] ?? []) entries.push({ id: room, workerId: null })
|
||
const slots = splitRooms(entries.length)
|
||
const rooms = entries.map((e, k) => {
|
||
const { kind, team } = ROOM_META[e.id]
|
||
return { id: e.id, kind, title: ROOM_TITLES[e.id], team, x: slots[k].x, w: slots[k].w, workerId: e.workerId }
|
||
})
|
||
for (const room of rooms) {
|
||
if (!room.workerId) continue
|
||
const otto = room.workerId === 'otto'
|
||
const x = room.x + Math.floor((room.w - (otto ? OTTO_W : WORKER_W)) / 2)
|
||
homes[room.workerId] = { x, y: box.floorY - (otto ? OTTO_DY : HOME_DY), floor: index, room: room.id }
|
||
}
|
||
return { id, index, title: FLOOR_TITLES[id] ?? id, top: box.top, floorY: box.floorY, h: box.h, rooms }
|
||
})
|
||
return { floors: list, homes, spots: buildSpots(list) }
|
||
}
|
||
|
||
// Etagenindex zur Kennung; unbekannt -> -1
|
||
export function floorIndex(layout, id) {
|
||
return layout.floors.findIndex((f) => f.id === id)
|
||
}
|
||
|
||
// Raum zur Kennung; unbekannt -> null
|
||
export function roomById(layout, roomId) {
|
||
for (const f of layout.floors) {
|
||
const room = f.rooms.find((r) => r.id === roomId)
|
||
if (room) return room
|
||
}
|
||
return null
|
||
}
|
||
|
||
// Heimatplatz des Workers; unbekannte Kennung (auch Prototyp-Namen) -> null
|
||
export function homeOf(layout, workerId) {
|
||
return typeof workerId === 'string' && Object.hasOwn(layout.homes, workerId) ? layout.homes[workerId] : null
|
||
}
|
||
|
||
// Standhöhe einer Figur auf Etage i (obere Kante des 14 hohen Körpers)
|
||
export function fy(layout, i) {
|
||
return layout.floors[i].floorY - HOME_DY
|
||
}
|
||
|
||
// Oberkante der Aufzugkabine, wenn sie auf Etage i hält
|
||
export function cabTop(layout, i) {
|
||
return layout.floors[i].floorY - CAB_H
|
||
}
|