Workers P2: Räume nach kind, Pokal-Seiten, graue Räume, Gebäudehülle

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-18 02:29:57 +00:00
1 parent fb331de1a6
commit 8555453a74
3 files changed
+660

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
// Gebäudehülle des Hustler-Hochhauses: Boden und Erde, Dach, Aufzugschacht, Etagenböden und -wände, Schachttüren mit Lampen,
// Etagenschilder rechts außen und der Pausenschleier.
// Portiert aus entwuerfe/scene-turm.js (drawOutside Z. 99-111, drawBuilding Z. 118-172) auf 10 Etagen. Die Stadtsilhouette,
// das Rohrpost-Rohr und die Welle der Vorlage entfallen. Die Räume selbst zeichnet rooms.js (danach, über der Rückwand).
// Etagenmaße kommen aus dem Layout, die Hüllenmaße (Turm x 14..230, Räume x 42..228) stehen hier. Kein React, kein Appwrite,
// kein window/document.
import { px, tint, label, PAL, TEAM } from './sprites.js'
import { WORLD } from './layout.js'
const GROUND_Y = 224 // Bodenlinie = Boden der Tresor-Etage (Erdgeschoss); darunter liegt der Keller
const ROOF_Y = 4 // Oberkante der Dachlinie
const HULL_X0 = 14 // Turm: linke Außenkante
const HULL_W = 216 // Turm: Breite (x 14..230)
const WALL_W = 2 // Wandstärke
const SHAFT_X = 16 // Aufzugschacht x 16..40
const SHAFT_W = 24
const SHAFT_WALL_X = 40 // Trennwand zwischen Schacht und Räumen
const ROOMS_X0 = 42
const ROOMS_W = 186
const DOOR_H = 18 // Schachttür: Sturz 18 über dem Boden
const LAMP_X = 43
const SIGN_DOT_X = 230 // Etagenschild: Punkt rechts neben der Außenwand, Text bei x 234
const SIGN_X = 234
const SIDE_DOOR_X = 225
const C = {
wall: '#aab2c0',
back: '#f3f4f7',
strip: '#e6e9ef',
shaft: '#e4e8ef',
rail: '#d3d8e1',
frame: PAL.metal2,
mute: PAL.mute,
lampOff: '#c3cad6',
ground: PAL.metal,
earth: '#f7f4f0',
speck: '#e8e2da',
post: '#d5dae3',
doorWood: '#a8825f',
}
// Erdflecken: [x, Abstand unter der Bodenlinie]; die mittleren liegen unter dem Keller und sind verdeckt
const SPECKS = [[4, 7], [9, 15], [6, 25], [244, 9], [262, 17], [290, 8], [306, 19], [300, 26], [60, 28], [140, 30], [210, 27]]
// Boden, Erde, Dachlinie mit Fahne und eine Laterne links. Wird vor dem Turm gezeichnet.
export function drawOutside(ctx) {
px(ctx, 0, GROUND_Y + 1, WORLD.w, WORLD.h - GROUND_Y - 1, C.earth)
px(ctx, 0, GROUND_Y, WORLD.w, 1, C.ground)
for (const [x, dy] of SPECKS) px(ctx, x, GROUND_Y + dy, 2, 1, C.speck)
// Laterne
px(ctx, 6, GROUND_Y - 19, 1, 19, C.post)
px(ctx, 4, GROUND_Y - 21, 5, 2, C.post)
px(ctx, 5, GROUND_Y - 19, 3, 1, '#fff3c4')
// Dachlinie und Fahne
px(ctx, HULL_X0, ROOF_Y, HULL_W, 2, C.frame)
px(ctx, 60, 0, 1, 4, C.frame)
px(ctx, 61, 0, 4, 2, TEAM.freigabe)
}
// Hülle, Schacht, Rückwände, Böden, Schachttüren mit Lampen (grün: Kabine steht auf der Etage) und die Etagenschilder.
// t ist im Aufruf vorgesehen (Takt der Szene), die Hülle bewegt sich heute nicht.
export function drawTower(ctx, layout, t, lift) {
const floors = layout.floors
if (floors.length === 0) return
const topY = floors[0].top
const bottom = floors[floors.length - 1].floorY
// Schacht mit Schienen
px(ctx, SHAFT_X, topY, SHAFT_W, bottom - topY, C.shaft)
px(ctx, SHAFT_X, topY, 1, bottom - topY, C.rail)
px(ctx, SHAFT_X + SHAFT_W - 1, topY, 1, bottom - topY, C.rail)
// Rückwand und Bodenstreifen jeder Etage
for (const f of floors) {
px(ctx, ROOMS_X0, f.top, ROOMS_W, f.h, C.back)
px(ctx, ROOMS_X0, f.floorY - 3, ROOMS_W, 3, C.strip)
}
// Trennwand zum Schacht mit Türöffnung je Etage, Sturz und Etagenlampe
px(ctx, SHAFT_WALL_X, topY, WALL_W, bottom - topY, C.back)
floors.forEach((f, i) => {
px(ctx, SHAFT_WALL_X, f.top, WALL_W, f.h - DOOR_H, C.wall)
px(ctx, SHAFT_WALL_X, f.floorY - DOOR_H, WALL_W, 1, C.frame)
px(ctx, LAMP_X, f.floorY - 14, 2, 2, lift.at(i) ? PAL.green : C.lampOff)
})
// Etagenböden, Fundament und Seitenwände
for (const f of floors) px(ctx, HULL_X0, f.floorY, HULL_W, 1, C.wall)
px(ctx, HULL_X0, bottom, HULL_W, 2, C.frame)
px(ctx, HULL_X0, ROOF_Y, WALL_W, bottom + 2 - ROOF_Y, C.wall)
px(ctx, ROOMS_X0 + ROOMS_W, ROOF_Y, WALL_W, bottom + 2 - ROOF_Y, C.wall)
// Seitentür im Erdgeschoss (Tresor-Etage): hier gehen die Scouts in die Stadt
const ground = floors.find((f) => f.id === 'tresor')
if (ground) {
const g = ground.floorY
px(ctx, ROOMS_X0 + ROOMS_W, g - 14, WALL_W, 11, C.back)
px(ctx, ROOMS_X0 + ROOMS_W, g - 3, WALL_W, 3, C.strip)
px(ctx, SIDE_DOOR_X, g - 16, 8, 2, C.doorWood)
px(ctx, SIDE_DOOR_X, g - 14, 2, 14, C.doorWood)
px(ctx, SIDE_DOOR_X + 6, g - 14, 2, 14, C.doorWood)
}
// Schachtkopf mit Rolle
px(ctx, 23, topY, 11, 3, C.frame)
px(ctx, 24, topY + 1, 9, 1, C.ground)
// Etagenschilder rechts außen: Punkt in der Teamfarbe des ersten Raums, Titel aus dem Layout
for (const f of floors) {
px(ctx, SIGN_DOT_X, f.top + 2, 2, 2, TEAM[f.rooms[0]?.team] ?? C.wall)
label(ctx, f.title, SIGN_X, f.top + 1, { size: 11, color: C.mute, weight: 600 })
}
}
// Weißer Schleier mit „Pausiert“ über der ganzen Welt; nur bei look.paused
export function drawPauseVeil(ctx, look) {
if (!look.paused) return
tint(ctx, -2, -2, WORLD.w + 4, WORLD.h + 4, PAL.w, 0.45)
label(ctx, 'Pausiert', WORLD.w / 2, WORLD.h / 2 - 5, { size: 28, color: PAL.ink, weight: 700, align: 'center', bg: PAL.w })
}
+306
View File
@@ -0,0 +1,306 @@
// Räume des Hustler-Hochhauses: eine Zeichnung je Raumart (kind), Pokal-Seiten, graue Räume und die Anzeigewerte `look`.
// Portiert aus entwuerfe/scene-turm.js (drawBridge ... drawArchive, Z. 182-314) auf die Raumtabelle des Layouts: alle Maße
// relativ zu room.x und zum Boden der Etage, Schriften nur über label(). Rückwand und Boden zeichnet building.js davor.
// Kein React, kein Appwrite, kein window/document.
import { px, tint, label, drawPaper, PAL, TEAM } from './sprites.js'
import { drawDesk, drawMonitor, drawCabinet, drawPlant, drawTray, drawTrophy, drawStamp, drawRadar, drawMailbox, drawVault, drawScale, drawSign, drawIcon } from './props.js'
const TROPHIES_PER_PAGE = 8
const TINT_ALPHA = 0.10 // Teamfarbton über jedem Raum
const GREY_ALPHA = 0.45
const GREY_TINT = '#9aa3b2'
const DIVIDER = '#e1e5ec'
const SIGN_TEXT = 'noch nicht angeschlossen'
const SIGN_MAX_W = 60
const STEP_COLORS = [TEAM.radar, TEAM.bewertung, TEAM.werkstatt, TEAM.pruefung, TEAM.freigabe] // Stationen der Pipeline-Tafel
const FEED_ROWS = [8, 5, 9] // Breiten der durchlaufenden Zeilen im Feed-Schirm
const MAX_PAGE_DOTS = 19
// Anzeigewerte der Räume. Die Regie (director.js) schreibt sie, die Zeichnung liest sie nur.
// greyRooms: nicht angeschlossene Räume, busyRooms: Räume, deren Worker gerade arbeitet (dort laufen die Monitore).
export function createLook() {
return {
greyRooms: new Set(),
busyRooms: new Set(),
pultOpen: 0,
dealsMonth: 0,
botLeads: 0,
trophyPage: 0, // gezeigte Pokal-Seite
pageT: 0, // Zeit seit der letzten Blätterung (Sekunden)
trophyPop: 0, // Restzeit des Aufblinkens des neuesten Pokals
vaultFlash: 0, // Restzeit des Tresor-Blitzes
paused: false,
}
}
// Pokal-Seiten: perPage je Seite, die Seite wird auf 0..pages-1 geklemmt; ungültige Werte zählen als 0
export function trophyPaging(total, page, perPage = TROPHIES_PER_PAGE) {
const size = Number.isFinite(perPage) && perPage >= 1 ? Math.floor(perPage) : TROPHIES_PER_PAGE
const n = whole(total)
const pages = Math.max(1, Math.ceil(n / size))
const p = Number.isFinite(page) ? Math.min(pages - 1, Math.max(0, Math.floor(page))) : 0
const from = p * size
return { shown: Math.min(size, n - from), page: p, pages, from }
}
// Zähler als ganze Zahl >= 0
function whole(value) {
return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0
}
// ---------- Hilfen für die Raumzeichnungen ----------
// r = { room, x, w, top, g, tp, busy, grey, color, look }: g = Boden der Etage, tp = Zeit mit Versatz je Raum (Monitore blinken nicht im Gleichschritt)
const deskAt = (ctx, r, dx, w) => drawDesk(ctx, r.x + dx, r.g - 11, w, r.color)
const monitorAt = (ctx, r, dx, color = r.color) => drawMonitor(ctx, r.x + dx, r.g - 21, r.tp, color, r.busy)
const hop = (r) => (r.busy && Math.floor(r.tp * 2) % 2 === 0 ? 3 : 0) // Stempel springt im Takt, solange gearbeitet wird
// Monitor mit Schreib-Animation: erste Zeile wächst, dann die zweite, dazu eine blinkende Schreibmarke
function drawTypingMonitor(ctx, r, dx) {
const x = r.x + dx
const y = r.g - 21
drawMonitor(ctx, x, y, r.tp, r.color, false)
if (!r.busy) return
const n = Math.floor(r.tp * 4) % 9
const first = Math.min(6, n + 1)
const second = Math.max(0, n - 5)
px(ctx, x + 2, y + 2, first, 1, r.color)
if (second > 0) px(ctx, x + 2, y + 4, second, 1, r.color)
if (n % 2 === 0) px(ctx, x + 2 + (second > 0 ? second : first), y + (second > 0 ? 4 : 2), 1, 1, PAL.w)
}
// Pappkarton 15 × 8 mit Etikett
function drawBox(ctx, x, y) {
px(ctx, x, y, 15, 8, PAL.wood)
px(ctx, x, y, 15, 1, PAL.wood2)
px(ctx, x + 5, y + 3, 5, 2, '#efe3d2')
}
// ---------- Raumarten ----------
export const ROOM_DRAWERS = Object.freeze({
// Kommandobrücke: Konsole mit 2 Monitoren, Pipeline-Tafel, Fenster in die Nacht, Pflanze
bruecke(ctx, r) {
const { x, top, g, tp } = r
px(ctx, x + 62, top + 3, 104, 22, PAL.metal2)
px(ctx, x + 63, top + 4, 102, 20, '#e8ebf9')
px(ctx, x + 63, top + 16, 102, 8, '#dfe3f5')
for (const [dx, dy, w, h] of [[66, 18, 6, 6], [74, 14, 5, 10], [82, 17, 8, 7], [128, 15, 6, 9], [138, 18, 9, 6], [152, 13, 5, 11]]) px(ctx, x + dx, top + dy, w, h, '#cdd3ee')
const stars = [[70, 6], [86, 9], [99, 5], [130, 7], [144, 5], [156, 10], [108, 8], [78, 16]]
stars.forEach(([dx, dy], i) => {
if ((Math.floor(tp * 1.3) + i) % 3 !== 0) px(ctx, x + dx, top + dy, 1, 1, '#b3bbec')
})
px(ctx, x + 150, top + 7, 5, 5, '#f6dc7a')
px(ctx, x + 152, top + 6, 4, 4, '#e8ebf9')
px(ctx, x + 113, top + 4, 2, 20, PAL.metal2)
px(ctx, x + 60, top + 25, 108, 2, '#aab2c0')
// Konsole
drawDesk(ctx, x + 6, g - 11, 30, r.color)
drawMonitor(ctx, x + 10, g - 21, tp, '#b7a4f0', r.busy)
drawMonitor(ctx, x + 24, g - 21, tp + 1, '#7fb2ff', r.busy)
// Pipeline-Tafel
px(ctx, x + 42, top + 5, 18, 14, PAL.metal2)
px(ctx, x + 43, top + 6, 16, 12, PAL.w)
px(ctx, x + 45, top + 10, 12, 1, PAL.metal)
STEP_COLORS.forEach((color, i) => px(ctx, x + 44 + 3 * i, top + 9, 2, 2, color))
px(ctx, x + 45, top + 13, 3, 3, '#ffe28a')
px(ctx, x + 50, top + 13, 3, 3, '#bde5c9')
px(ctx, x + 55, top + 13, 3, 3, '#f6b9b9')
drawPlant(ctx, x + 174, g - 9)
},
// Netz-Radar: Radar auf dem Boden, Tisch daneben
radar(ctx, r) {
deskAt(ctx, r, 2, 14)
drawRadar(ctx, r.x + 31, r.g - 15, r.busy ? r.tp : 0)
},
// Kleinanzeigen: Tisch mit Monitor, Eingangskorb auf dem Wandbrett (voll, solange gearbeitet wird)
inbox(ctx, r) {
deskAt(ctx, r, 2, 14)
monitorAt(ctx, r, 4)
px(ctx, r.x + 31, r.g - 11, 12, 1, PAL.wood2)
drawTray(ctx, r.x + 32, r.g - 15, r.busy)
},
// Portale: Tisch mit Monitor, Wandschirm mit durchlaufenden Zeilen
feed(ctx, r) {
deskAt(ctx, r, 2, 14)
monitorAt(ctx, r, 4)
const sx = r.x + 31
const sy = r.g - 18
px(ctx, sx, sy, 14, 10, PAL.o)
px(ctx, sx + 1, sy + 1, 12, 8, PAL.screen)
if (!r.busy) return
const shift = Math.floor(r.tp * 3) % FEED_ROWS.length
for (let i = 0; i < FEED_ROWS.length; i++) px(ctx, sx + 2, sy + 2 + i * 2, FEED_ROWS[(i + shift) % FEED_ROWS.length], 1, r.color)
},
// Foren: Tisch mit Monitor, Tafel mit zwei Sprechblasen
board(ctx, r) {
deskAt(ctx, r, 2, 14)
monitorAt(ctx, r, 4)
const bx = r.x + 31
const by = r.g - 19
px(ctx, bx, by, 14, 11, PAL.wood2)
px(ctx, bx + 1, by + 1, 12, 9, '#eadfcf')
px(ctx, bx + 2, by + 2, 6, 3, PAL.w)
px(ctx, bx + 3, by + 5, 1, 1, PAL.w)
px(ctx, bx + 6, by + 6, 6, 3, r.color)
px(ctx, bx + 10, by + 9, 1, 1, r.color)
},
// Branchen-Scout: Tisch in der Teamfarbe, Monitor, Branchen-Icon an der Wand (Icon = Raumkennung)
scout(ctx, r) {
deskAt(ctx, r, 2, 14)
monitorAt(ctx, r, 4)
drawIcon(ctx, r.room.id, r.x + r.w - 10, r.top + 8, r.color)
},
// Waage: Waage links der Figur (schwankt beim Bewerten), Tisch mit Papieren rechts
waage(ctx, r) {
drawScale(ctx, r.x + 62, r.g - 16, r.busy ? Math.round(Math.sin(r.tp * 6) * 2.4) : 0)
deskAt(ctx, r, 104, 22)
drawPaper(ctx, r.x + 109, r.g - 18)
drawPaper(ctx, r.x + 117, r.g - 18)
},
// Mail-Stube: Tisch, Monitor, Briefstapel auf einem Beistelltisch
mail(ctx, r) {
deskAt(ctx, r, 2, 14)
monitorAt(ctx, r, 4)
px(ctx, r.x + 31, r.g - 7, 12, 1, PAL.wood2)
px(ctx, r.x + 32, r.g - 6, 1, 6, PAL.wood2)
px(ctx, r.x + 41, r.g - 6, 1, 6, PAL.wood2)
for (let i = 0; i < 3; i++) {
px(ctx, r.x + 32, r.g - 9 - i * 2, 10, 2, PAL.w)
px(ctx, r.x + 32, r.g - 9 - i * 2, 10, 1, PAL.g)
}
},
// Forum-Stube: Tisch, Monitor mit Schreib-Animation
schreiber(ctx, r) {
deskAt(ctx, r, 2, 14)
drawTypingMonitor(ctx, r, 4)
},
// Posteingang: Tisch, Regal mit drei Ablagekörben (Eingang voll, Mitte beim Sortieren, Ausgang leer)
sortier(ctx, r) {
deskAt(ctx, r, 2, 14)
const rx = r.x + 31
px(ctx, rx, r.g - 20, 1, 18, PAL.wood2)
px(ctx, rx + 11, r.g - 20, 1, 18, PAL.wood2)
const filled = [true, r.busy, false]
filled.forEach((full, i) => drawTray(ctx, rx + 1, r.g - 19 + i * 6, full))
},
// Abschluss: Tisch, Vertrag, Stempel (springt beim Arbeiten)
vertrag(ctx, r) {
deskAt(ctx, r, 2, 14)
drawPaper(ctx, r.x + 3, r.g - 18)
drawStamp(ctx, r.x + 10, r.g - 16 - hop(r), true)
},
// Werkbank: Tisch, Monitor
tisch(ctx, r) {
deskAt(ctx, r, 6, 18)
monitorAt(ctx, r, 10)
},
// Prüftisch: Tisch mit zwei Stempeln, Handy auf einem Sockel (zeigt beim Prüfen wechselnde Bilder)
pruefung(ctx, r) {
deskAt(ctx, r, 60, 22)
drawStamp(ctx, r.x + 66, r.g - 16, false)
drawStamp(ctx, r.x + 73, r.g - 16 - hop(r), true)
const sx = r.x + 108
px(ctx, sx, r.g - 5, 10, 5, PAL.wood)
px(ctx, sx, r.g - 5, 10, 1, PAL.wood2)
px(ctx, sx + 2, r.g - 15, 6, 10, PAL.o)
const shot = Math.floor(r.tp * 2) % 2 === 0 ? '#cfe3ff' : '#bde5c9'
px(ctx, sx + 3, r.g - 14, 4, 7, r.busy ? shot : PAL.screen)
px(ctx, sx + 4, r.g - 7, 2, 1, PAL.mute)
},
// Dein Pult: der Briefkasten in der Mitte; Zahl der offenen Einträge daneben (keine bei 0)
pult(ctx, r) {
const open = whole(r.look.pultOpen)
const cx = r.x + r.w / 2
drawMailbox(ctx, cx - 6, r.g - 15, open)
if (open > 0) label(ctx, open > 99 ? '99+' : String(open), cx + 15, r.g - 19, { size: 11, color: PAL.w, weight: 700, align: 'center', bg: PAL.red })
},
// Tresor: Pokalbrett (eine Seite zu 8), Tresortür, Zähler der Deals dieses Monats
tresor(ctx, r) {
const { look } = r
const total = whole(look.dealsMonth)
const pg = trophyPaging(total, look.trophyPage)
const shelf = r.g - 9
px(ctx, r.x + 24, shelf, 78, 2, PAL.wood2)
px(ctx, r.x + 26, shelf + 2, 2, 3, PAL.wood2)
px(ctx, r.x + 98, shelf + 2, 2, 3, PAL.wood2)
for (let i = 0; i < pg.shown; i++) {
const newest = pg.from + i === total - 1
if (newest && look.trophyPop > 0 && Math.floor(look.trophyPop * 10) % 2) continue
drawTrophy(ctx, r.x + 26 + 9 * i, shelf - 9)
}
// Seitenpunkte, wenn mehr als eine Seite da ist
for (let p = 0; p < Math.min(pg.pages, MAX_PAGE_DOTS); p++) px(ctx, r.x + 24 + p * 4, shelf + 6, 2, 2, p === pg.page ? TEAM.tresor : PAL.metal)
drawVault(ctx, r.x + 110, r.g - 21, r.tp)
if (look.vaultFlash > 0) tint(ctx, r.x + 110, r.g - 21, 22, 20, PAL.gold, 0.3 * Math.min(1, look.vaultFlash * 2))
label(ctx, `${total} Deals`, r.x + 138, r.g - 15, { size: 11, color: PAL.ink, weight: 600 })
},
// Archiv & Register: Kartons links, vier Aktenschränke rechts der Figur (beim Arbeiten springt je eine Schublade auf), Zähler der Bot-Leads
archiv(ctx, r) {
drawBox(ctx, r.x + 14, r.g - 9)
drawBox(ctx, r.x + 31, r.g - 9)
drawBox(ctx, r.x + 22, r.g - 17)
const open = Math.floor(r.tp * 1.5) % 4
for (let i = 0; i < 4; i++) {
const x = r.x + 100 + 14 * i
const y = r.g - 20
drawCabinet(ctx, x, y, 3)
if (r.busy && i === open) {
px(ctx, x + 1, y + 7, 10, 5, '#fafbfd')
px(ctx, x + 2, y + 9, 8, 2, PAL.p)
px(ctx, x + 3, y + 6, 6, 1, PAL.w)
}
}
label(ctx, `${whole(r.look.botLeads)} Bot-Leads`, r.x + r.w - 2, r.g - 15, { size: 11, color: PAL.ink, weight: 600, align: 'right' })
},
})
// ---------- Zeichnen ----------
function drawRoom(ctx, room, floor, index, t, look) {
// Räume ohne Worker (pult, tresor) werden nie grau
const grey = room.workerId != null && look.greyRooms.has(room.id)
const r = {
room,
x: room.x,
w: room.w,
top: floor.top,
g: floor.floorY,
tp: t + room.x * 0.05,
busy: !grey && look.busyRooms.has(room.id),
grey,
color: TEAM[room.team] ?? PAL.metal2,
look,
}
if (!grey) tint(ctx, room.x, floor.top, room.w, floor.h, r.color, TINT_ALPHA)
if (index > 0) px(ctx, room.x, floor.top, 1, floor.h, DIVIDER)
// Das Schild steht oben mittig und verdrängt den Titel; Name und Branche erkennt man an Möbeln und Icon
if (!grey) label(ctx, room.title, room.x + 2, floor.top + 1, { size: 10, color: PAL.mute, weight: 500 })
if (Object.hasOwn(ROOM_DRAWERS, room.kind)) ROOM_DRAWERS[room.kind](ctx, r)
if (grey) {
tint(ctx, room.x, floor.top, room.w, floor.h, GREY_TINT, GREY_ALPHA)
const w = Math.min(room.w - 2, SIGN_MAX_W)
drawSign(ctx, room.x + (room.w - w) / 2, floor.top + 1, w, SIGN_TEXT)
}
}
// Alle Räume aller Etagen. Vorher: drawOutside, drawTower. Nachher: Aufzug hinten, Figuren, Aufzug vorn.
export function drawRooms(ctx, layout, t, look) {
for (const floor of layout.floors) floor.rooms.forEach((room, index) => drawRoom(ctx, room, floor, index, t, look))
}