@blkc-code/render-canvas 0.3.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/draw.d.ts +9 -9
- package/dist/draw.d.ts.map +1 -1
- package/dist/draw.js +82 -306
- package/dist/draw.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/links.d.ts +16 -0
- package/dist/links.d.ts.map +1 -0
- package/dist/links.js +75 -0
- package/dist/links.js.map +1 -0
- package/dist/office.d.ts +2 -0
- package/dist/office.d.ts.map +1 -1
- package/dist/office.js +7 -3
- package/dist/office.js.map +1 -1
- package/dist/pack.d.ts +31 -0
- package/dist/pack.d.ts.map +1 -0
- package/dist/pack.js +10 -0
- package/dist/pack.js.map +1 -0
- package/dist/paint.d.ts +15 -0
- package/dist/paint.d.ts.map +1 -0
- package/dist/paint.js +18 -0
- package/dist/paint.js.map +1 -0
- package/dist/runtime.js +1 -1
- package/dist/runtime.txt +1 -1
- package/dist/types.d.ts +14 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/world.d.ts +18 -13
- package/dist/world.d.ts.map +1 -1
- package/dist/world.js +42 -64
- package/dist/world.js.map +1 -1
- package/dist/worlds/office-data.d.ts +14 -0
- package/dist/worlds/office-data.d.ts.map +1 -0
- package/dist/worlds/office-data.js +53 -0
- package/dist/worlds/office-data.js.map +1 -0
- package/dist/worlds/office-paint.d.ts +13 -0
- package/dist/worlds/office-paint.d.ts.map +1 -0
- package/dist/worlds/office-paint.js +300 -0
- package/dist/worlds/office-paint.js.map +1 -0
- package/package.json +7 -2
- package/src/draw.ts +80 -289
- package/src/index.ts +3 -0
- package/src/links.ts +76 -0
- package/src/office.ts +8 -3
- package/src/pack.ts +36 -0
- package/src/paint.ts +24 -0
- package/src/types.ts +13 -3
- package/src/world.ts +53 -66
- package/src/worlds/office-data.ts +58 -0
- package/src/worlds/office-paint.ts +284 -0
package/dist/draw.d.ts
CHANGED
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
* 16 pixels a tile. The building is drawn once to a layer (drawBuilding); people, screens
|
|
4
4
|
* and anything else that moves are drawn on top each frame (drawWorld).
|
|
5
5
|
*/
|
|
6
|
-
import { type
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
import { type Ctx, type Palette } from "./paint.js";
|
|
7
|
+
import { type Item, type World } from "./world.js";
|
|
8
|
+
export { TILE, type Palette } from "./paint.js";
|
|
9
|
+
/** How one world looks. The engine draws the building, characters and labels around it. */
|
|
10
|
+
export interface Painter {
|
|
11
|
+
furniture(c: Ctx, item: Item, world: World, time: number, palette: Palette): void;
|
|
12
|
+
/** What lights up at night. Call `glow` for anything that does. */
|
|
13
|
+
glow(item: Item, world: World, glow: (x: number, y: number, radius: number, color: string) => void): void;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
+
export declare function registerPainter(id: string, painter: Painter): void;
|
|
15
16
|
export interface Daylight {
|
|
16
17
|
sky: string;
|
|
17
18
|
sun: string;
|
|
@@ -22,5 +23,4 @@ export declare function daylightAt(hour: number): Daylight;
|
|
|
22
23
|
export declare function drawBuilding(c: Ctx, world: World, palette: Palette, daylight: Daylight): void;
|
|
23
24
|
/** Everything that moves. `building` is the layer from drawBuilding. */
|
|
24
25
|
export declare function drawWorld(c: Ctx, world: World, time: number, palette: Palette, daylight: Daylight, building: CanvasImageSource | undefined, selected?: string): void;
|
|
25
|
-
export {};
|
|
26
26
|
//# sourceMappingURL=draw.d.ts.map
|
package/dist/draw.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draw.d.ts","sourceRoot":"","sources":["../src/draw.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"draw.d.ts","sourceRoot":"","sources":["../src/draw.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAqD,KAAK,GAAG,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACvG,OAAO,EAAgC,KAAK,IAAI,EAAa,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAG5F,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAIhD,2FAA2F;AAC3F,MAAM,WAAW,OAAO;IACtB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAClF,mEAAmE;IACnE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;CAC3G;AAID,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAElE;AAMD,MAAM,WAAW,QAAQ;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAErE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAKjD;AA8UD,wFAAwF;AACxF,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAgB7F;AAoED,wEAAwE;AACxE,wBAAgB,SAAS,CACvB,CAAC,EAAE,GAAG,EACN,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,iBAAiB,GAAG,SAAS,EACvC,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,CAgDN"}
|
package/dist/draw.js
CHANGED
|
@@ -3,23 +3,16 @@
|
|
|
3
3
|
* 16 pixels a tile. The building is drawn once to a layer (drawBuilding); people, screens
|
|
4
4
|
* and anything else that moves are drawn on top each frame (drawWorld).
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
};
|
|
15
|
-
function shade(color, amount) {
|
|
16
|
-
if (!color.startsWith("#"))
|
|
17
|
-
return color;
|
|
18
|
-
const hex = color.length === 4 ? `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}` : color;
|
|
19
|
-
const n = parseInt(hex.slice(1, 7), 16);
|
|
20
|
-
const channel = (shift) => Math.max(0, Math.min(255, ((n >> shift) & 255) + amount));
|
|
21
|
-
return `rgb(${channel(16)},${channel(8)},${channel(0)})`;
|
|
6
|
+
import { drawLinks, linkRoutes } from "./links.js";
|
|
7
|
+
import { BODY_FONT, LABEL_FONT, OUTLINE, TILE, rect, shade } from "./paint.js";
|
|
8
|
+
import { isSeated, specOf } from "./world.js";
|
|
9
|
+
import { OFFICE_PAINTER } from "./worlds/office-paint.js";
|
|
10
|
+
export { TILE } from "./paint.js";
|
|
11
|
+
const painters = new Map([["office", OFFICE_PAINTER]]);
|
|
12
|
+
export function registerPainter(id, painter) {
|
|
13
|
+
painters.set(id, painter);
|
|
22
14
|
}
|
|
15
|
+
const painterFor = (world) => painters.get(world.pack.id) ?? OFFICE_PAINTER;
|
|
23
16
|
export function daylightAt(hour) {
|
|
24
17
|
if (hour >= 7 && hour < 17)
|
|
25
18
|
return { sky: "#8ed0ff", sun: "rgba(255,244,214,0.10)", night: 0 };
|
|
@@ -240,6 +233,24 @@ function drawFloor(c, room) {
|
|
|
240
233
|
if (noise === 3)
|
|
241
234
|
rect(c, "#596070", x + 3, y + 8, 8, 1);
|
|
242
235
|
break;
|
|
236
|
+
case "hall": {
|
|
237
|
+
// Polished floor, with a runner down the middle of a corridor's length.
|
|
238
|
+
rect(c, "#cfc8b8", x, y, TILE, TILE);
|
|
239
|
+
rect(c, "#bdb6a6", x, y + 15, TILE, 1);
|
|
240
|
+
rect(c, "#bdb6a6", x + 15, y, 1, TILE);
|
|
241
|
+
const along = room.w >= room.h;
|
|
242
|
+
const offset = along ? ty - room.y : tx - room.x;
|
|
243
|
+
const size = along ? room.h : room.w;
|
|
244
|
+
if (along && offset === 0)
|
|
245
|
+
rect(c, "#7f6a55", x, y + 12, TILE, 4);
|
|
246
|
+
if (along && offset === size - 1)
|
|
247
|
+
rect(c, "#7f6a55", x, y, TILE, 4);
|
|
248
|
+
if (!along && offset === 0)
|
|
249
|
+
rect(c, "#7f6a55", x + 12, y, 4, TILE);
|
|
250
|
+
if (!along && offset === size - 1)
|
|
251
|
+
rect(c, "#7f6a55", x, y, 4, TILE);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
243
254
|
case "grass":
|
|
244
255
|
rect(c, "#3f8f4f", x, y, TILE, TILE);
|
|
245
256
|
for (let i = 0; i < 7; i++)
|
|
@@ -263,6 +274,24 @@ const WALL_TOP = "#2a2d48";
|
|
|
263
274
|
const WALL_FACE = "#5c628f";
|
|
264
275
|
const WALL_DARK = "#4a4f78";
|
|
265
276
|
const WALL_LIGHT = "#6d74a4";
|
|
277
|
+
/** Outside the building: lawn, with the odd shrub and tree. */
|
|
278
|
+
function drawGrounds(c, tx, ty) {
|
|
279
|
+
const x = tx * TILE;
|
|
280
|
+
const y = ty * TILE;
|
|
281
|
+
rect(c, (tx + ty) % 2 === 0 ? "#4b8a4f" : "#478549", x, y, TILE, TILE);
|
|
282
|
+
const noise = (tx * 73 + ty * 151) % 29;
|
|
283
|
+
for (let i = 0; i < 4; i++)
|
|
284
|
+
rect(c, "#5a9b5c", x + ((tx * 5 + i * 7) % 15), y + ((ty * 3 + i * 5) % 15), 1, 2);
|
|
285
|
+
if (noise === 4) {
|
|
286
|
+
rect(c, "#2f6a38", x + 3, y + 7, 10, 7);
|
|
287
|
+
rect(c, "#3c7f45", x + 5, y + 8, 5, 3);
|
|
288
|
+
}
|
|
289
|
+
else if (noise === 11) {
|
|
290
|
+
rect(c, "#5b3a22", x + 7, y + 10, 2, 5);
|
|
291
|
+
rect(c, "#24592e", x + 2, y + 1, 12, 10);
|
|
292
|
+
rect(c, "#2f6f3a", x + 4, y + 2, 7, 5);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
266
295
|
function drawWalls(c, world, palette, daylight) {
|
|
267
296
|
const open = new Set();
|
|
268
297
|
for (const room of world.rooms) {
|
|
@@ -278,12 +307,25 @@ function drawWalls(c, world, palette, daylight) {
|
|
|
278
307
|
continue;
|
|
279
308
|
const x = tx * TILE;
|
|
280
309
|
const y = ty * TILE;
|
|
310
|
+
const nearBuilding = (cx, cy) => {
|
|
311
|
+
for (let ay = cy - 1; ay <= cy + 1; ay++) {
|
|
312
|
+
for (let ax = cx - 1; ax <= cx + 1; ax++)
|
|
313
|
+
if (open.has(`${ax},${ay}`) || doors.has(`${ax},${ay}`))
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
return false;
|
|
317
|
+
};
|
|
318
|
+
if (!nearBuilding(tx, ty)) {
|
|
319
|
+
drawGrounds(c, tx, ty);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
281
322
|
if (open.has(`${tx},${ty + 1}`)) {
|
|
282
323
|
rect(c, WALL_FACE, x, y, TILE, TILE);
|
|
283
324
|
rect(c, WALL_LIGHT, x, y, TILE, 1);
|
|
284
325
|
rect(c, WALL_DARK, x, y + 12, TILE, 4);
|
|
285
326
|
rect(c, palette.primary, x, y + 11, TILE, 1);
|
|
286
|
-
|
|
327
|
+
// Windows look outside, so only an outer wall has them.
|
|
328
|
+
if (tx % 4 === 3 && !nearBuilding(tx, ty - 1) && !world.furniture.some((item) => specOf(world, item).wall && item.x === tx && item.y === ty)) {
|
|
287
329
|
// A window, in the colour of the sky right now.
|
|
288
330
|
rect(c, "#1f2937", x + 1, y + 1, 14, 9);
|
|
289
331
|
rect(c, daylight.sky, x + 2, y + 2, 12, 7);
|
|
@@ -326,6 +368,14 @@ function drawWalls(c, world, palette, daylight) {
|
|
|
326
368
|
function drawLabels(c, world) {
|
|
327
369
|
c.font = LABEL_FONT;
|
|
328
370
|
c.textBaseline = "top";
|
|
371
|
+
for (const item of world.furniture) {
|
|
372
|
+
if (!item.label)
|
|
373
|
+
continue;
|
|
374
|
+
const width = Math.ceil(c.measureText(item.label).width) + 4;
|
|
375
|
+
rect(c, "#f4ecd8", item.x * TILE + 17, item.y * TILE + 3, width, 10);
|
|
376
|
+
c.fillStyle = "#1c1408";
|
|
377
|
+
c.fillText(item.label, item.x * TILE + 19, item.y * TILE + 4.5);
|
|
378
|
+
}
|
|
329
379
|
for (const room of world.rooms) {
|
|
330
380
|
if (!room.name)
|
|
331
381
|
continue;
|
|
@@ -346,11 +396,11 @@ export function drawBuilding(c, world, palette, daylight) {
|
|
|
346
396
|
drawFloor(c, room);
|
|
347
397
|
drawWalls(c, world, palette, daylight);
|
|
348
398
|
for (const item of world.furniture)
|
|
349
|
-
if (item.type === "rug")
|
|
350
|
-
|
|
399
|
+
if (item.type === "rug" || item.type === "pile")
|
|
400
|
+
painterFor(world).furniture(c, item, world, 0, palette);
|
|
351
401
|
for (const item of world.furniture)
|
|
352
|
-
if (
|
|
353
|
-
|
|
402
|
+
if (specOf(world, item).wall)
|
|
403
|
+
painterFor(world).furniture(c, item, world, 0, palette);
|
|
354
404
|
// Light from the windows falling across the floor.
|
|
355
405
|
if (daylight.sun !== "rgba(0,0,0,0)") {
|
|
356
406
|
c.fillStyle = daylight.sun;
|
|
@@ -363,279 +413,6 @@ export function drawBuilding(c, world, palette, daylight) {
|
|
|
363
413
|
}
|
|
364
414
|
}
|
|
365
415
|
}
|
|
366
|
-
function drawDesk(c, x, y, on, time, seed, palette, blocked) {
|
|
367
|
-
rect(c, "#55351d", x + 1, y + 22, 46, 8);
|
|
368
|
-
rect(c, "#6b4427", x + 1, y + 13, 46, 10);
|
|
369
|
-
rect(c, "#8a5a33", x + 1, y + 10, 46, 12);
|
|
370
|
-
rect(c, "#a87545", x + 1, y + 10, 46, 2);
|
|
371
|
-
rect(c, "#3b2413", x + 3, y + 22, 3, 9);
|
|
372
|
-
rect(c, "#3b2413", x + 42, y + 22, 3, 9);
|
|
373
|
-
rect(c, "#55351d", x + 30, y + 23, 12, 7);
|
|
374
|
-
rect(c, "#7c5637", x + 31, y + 24, 10, 2);
|
|
375
|
-
rect(c, "#d4a24e", x + 35, y + 24, 2, 1);
|
|
376
|
-
rect(c, "#7c5637", x + 31, y + 27, 10, 2);
|
|
377
|
-
rect(c, "#d4a24e", x + 35, y + 27, 2, 1);
|
|
378
|
-
// Monitor
|
|
379
|
-
rect(c, OUTLINE, x + 14, y - 3, 20, 15);
|
|
380
|
-
rect(c, "#374151", x + 22, y + 12, 4, 2);
|
|
381
|
-
rect(c, "#4b5563", x + 19, y + 14, 10, 1);
|
|
382
|
-
if (blocked) {
|
|
383
|
-
rect(c, "#3f0f14", x + 15, y - 2, 18, 13);
|
|
384
|
-
if (Math.floor(time * 2) % 2 === 0) {
|
|
385
|
-
rect(c, "#ef4444", x + 23, y, 2, 6);
|
|
386
|
-
rect(c, "#ef4444", x + 23, y + 7, 2, 2);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
else if (on) {
|
|
390
|
-
rect(c, "#0f2a2e", x + 15, y - 2, 18, 13);
|
|
391
|
-
const scroll = Math.floor(time * 3 + (seed % 13));
|
|
392
|
-
for (let line = 0; line < 5; line++) {
|
|
393
|
-
const width = 3 + (((seed >> line) & 7) + (scroll + line) * 7) % 12;
|
|
394
|
-
rect(c, line % 3 === 0 ? palette.primary : line % 3 === 1 ? "#7dd3fc" : "#fcd34d", x + 16 + (line % 2) * 2, y - 1 + line * 2.2, width, 1);
|
|
395
|
-
}
|
|
396
|
-
c.fillStyle = "rgba(125,211,252,0.12)";
|
|
397
|
-
c.fillRect(x + 10, y + 11, 28, 8);
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
rect(c, "#1f2937", x + 15, y - 2, 18, 13);
|
|
401
|
-
rect(c, "#374151", x + 17, y, 3, 1);
|
|
402
|
-
}
|
|
403
|
-
rect(c, "#d1d5db", x + 16, y + 15, 16, 5);
|
|
404
|
-
for (let row = 0; row < 2; row++)
|
|
405
|
-
for (let key = 0; key < 7; key++)
|
|
406
|
-
rect(c, "#9ca3af", x + 17 + key * 2, y + 16 + row * 2, 1, 1);
|
|
407
|
-
rect(c, "#e5e7eb", x + 34, y + 16, 3, 4);
|
|
408
|
-
rect(c, "#f3f4f6", x + 40, y + 13, 5, 5);
|
|
409
|
-
rect(c, palette.primary, x + 40, y + 15, 5, 1);
|
|
410
|
-
rect(c, "#fde68a", x + 4, y + 13, 7, 6);
|
|
411
|
-
rect(c, "#fbbf24", x + 5, y + 15, 5, 1);
|
|
412
|
-
}
|
|
413
|
-
function drawBoard(c, x, y, world) {
|
|
414
|
-
rect(c, "#3a2c1e", x, y - 12, 64, 27);
|
|
415
|
-
rect(c, "#d9c29c", x + 2, y - 10, 60, 23);
|
|
416
|
-
for (let i = 0; i < 40; i++)
|
|
417
|
-
rect(c, "#c9b08a", x + 3 + (i * 37) % 58, y - 9 + (i * 13) % 21, 1, 1);
|
|
418
|
-
const colors = { todo: "#fde68a", doing: "#93c5fd", done: "#86efac" };
|
|
419
|
-
const per = 8;
|
|
420
|
-
world.tasks.slice(0, per * 2).forEach((task, index) => {
|
|
421
|
-
const col = index % per;
|
|
422
|
-
const row = Math.floor(index / per);
|
|
423
|
-
rect(c, "#00000033", x + 5 + col * 7, y - 7 + row * 10, 6, 8);
|
|
424
|
-
rect(c, colors[task.state], x + 4 + col * 7, y - 8 + row * 10, 6, 8);
|
|
425
|
-
rect(c, "#ef4444", x + 6 + col * 7, y - 8 + row * 10, 2, 1);
|
|
426
|
-
rect(c, "#00000040", x + 5 + col * 7, y - 5 + row * 10, 4, 1);
|
|
427
|
-
rect(c, "#00000040", x + 5 + col * 7, y - 3 + row * 10, 3, 1);
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
function drawFurniture(c, item, world, time, palette) {
|
|
431
|
-
const x = item.x * TILE;
|
|
432
|
-
const y = item.y * TILE;
|
|
433
|
-
switch (item.type) {
|
|
434
|
-
case "desk": {
|
|
435
|
-
const seatX = item.x + 1;
|
|
436
|
-
const seatY = item.y + 2;
|
|
437
|
-
const user = world.agents.find((a) => a.seat?.x === seatX && a.seat?.y === seatY && isSeated(a));
|
|
438
|
-
drawDesk(c, x, y, Boolean(user), time, user?.seed ?? 0, palette, user?.state === "blocked");
|
|
439
|
-
break;
|
|
440
|
-
}
|
|
441
|
-
case "coffee":
|
|
442
|
-
rect(c, "#374151", x, y + 2, 32, 14);
|
|
443
|
-
rect(c, "#6b7280", x, y + 2, 32, 2);
|
|
444
|
-
rect(c, OUTLINE, x + 4, y - 9, 15, 15);
|
|
445
|
-
rect(c, "#27272a", x + 5, y - 8, 13, 13);
|
|
446
|
-
rect(c, Math.floor(time * 2) % 2 ? "#ef4444" : "#7f1d1d", x + 15, y - 6, 2, 2);
|
|
447
|
-
rect(c, "#52525b", x + 7, y - 6, 6, 4);
|
|
448
|
-
rect(c, "#f3f4f6", x + 8, y + 3, 5, 4);
|
|
449
|
-
rect(c, "#f3f4f6", x + 23, y - 1, 5, 5);
|
|
450
|
-
rect(c, "#92400e", x + 24, y, 3, 1);
|
|
451
|
-
for (let i = 0; i < 3; i++) {
|
|
452
|
-
c.fillStyle = "rgba(255,255,255,0.5)";
|
|
453
|
-
c.fillRect(x + 9 + Math.round(Math.sin(time * 2 + i) * 1.5), y - 1 - ((time * 8 + i * 4) % 9), 1, 2);
|
|
454
|
-
}
|
|
455
|
-
break;
|
|
456
|
-
case "water":
|
|
457
|
-
rect(c, "#d1d5db", x + 3, y + 3, 10, 13);
|
|
458
|
-
rect(c, "#9ca3af", x + 3, y + 14, 10, 2);
|
|
459
|
-
rect(c, "#60a5fa", x + 4, y - 8, 8, 11);
|
|
460
|
-
rect(c, "#bae6fd", x + 5, y - 7, 2, 9);
|
|
461
|
-
rect(c, "#3b82f6", x + 5, y + 6, 2, 2);
|
|
462
|
-
rect(c, "#ef4444", x + 9, y + 6, 2, 2);
|
|
463
|
-
if (Math.floor(time * 0.4 + item.x) % 5 === 0)
|
|
464
|
-
rect(c, "#e0f2fe", x + 8, y - 2 - ((time * 6) % 7), 1, 1);
|
|
465
|
-
break;
|
|
466
|
-
case "sofa":
|
|
467
|
-
rect(c, shade(palette.primary, -60), x + 1, y - 7, 46, 11);
|
|
468
|
-
rect(c, shade(palette.primary, -30), x + 2, y - 6, 44, 3);
|
|
469
|
-
rect(c, "#4b5563", x, y + 3, 48, 11);
|
|
470
|
-
rect(c, "#6b7280", x + 3, y + 3, 20, 7);
|
|
471
|
-
rect(c, "#6b7280", x + 25, y + 3, 20, 7);
|
|
472
|
-
rect(c, "#374151", x, y + 12, 48, 3);
|
|
473
|
-
rect(c, "#374151", x - 1, y - 2, 4, 16);
|
|
474
|
-
rect(c, "#374151", x + 45, y - 2, 4, 16);
|
|
475
|
-
break;
|
|
476
|
-
case "plant":
|
|
477
|
-
rect(c, "#92400e", x + 4, y + 9, 8, 7);
|
|
478
|
-
rect(c, "#b45309", x + 3, y + 8, 10, 2);
|
|
479
|
-
for (const [lx, ly, lw, lh] of [[6, -3, 4, 11], [2, 0, 5, 6], [9, 1, 5, 6], [4, -6, 3, 6], [9, -5, 3, 5]])
|
|
480
|
-
rect(c, "#15803d", x + lx, y + ly, lw, lh);
|
|
481
|
-
rect(c, "#22c55e", x + 7, y - 3, 2, 8);
|
|
482
|
-
rect(c, "#22c55e", x + 3, y + 1, 2, 3);
|
|
483
|
-
rect(c, "#4ade80", x + 10, y - 4, 1, 3);
|
|
484
|
-
break;
|
|
485
|
-
case "fern":
|
|
486
|
-
rect(c, "#57534e", x + 3, y + 10, 10, 6);
|
|
487
|
-
rect(c, "#78716c", x + 2, y + 9, 12, 2);
|
|
488
|
-
for (let i = 0; i < 7; i++) {
|
|
489
|
-
const lean = i - 3;
|
|
490
|
-
for (let j = 0; j < 5; j++)
|
|
491
|
-
rect(c, j % 2 ? "#16a34a" : "#15803d", x + 7 + lean * (j / 2 + 1), y + 8 - j * 2 + Math.abs(lean), 2, 2);
|
|
492
|
-
}
|
|
493
|
-
break;
|
|
494
|
-
case "board":
|
|
495
|
-
drawBoard(c, x, y, world);
|
|
496
|
-
break;
|
|
497
|
-
case "whiteboard":
|
|
498
|
-
rect(c, "#9ca3af", x, y - 10, 48, 22);
|
|
499
|
-
rect(c, "#f9fafb", x + 2, y - 8, 44, 18);
|
|
500
|
-
rect(c, "#3b82f6", x + 5, y - 5, 14, 1);
|
|
501
|
-
rect(c, "#3b82f6", x + 5, y - 2, 9, 1);
|
|
502
|
-
rect(c, "#ef4444", x + 24, y - 6, 8, 6);
|
|
503
|
-
rect(c, "#f9fafb", x + 25, y - 5, 6, 4);
|
|
504
|
-
rect(c, "#10b981", x + 8, y + 3, 20, 1);
|
|
505
|
-
rect(c, "#10b981", x + 27, y + 2, 1, 3);
|
|
506
|
-
rect(c, "#6b7280", x + 2, y + 10, 44, 2);
|
|
507
|
-
break;
|
|
508
|
-
case "shelf":
|
|
509
|
-
rect(c, "#5b3a21", x, y - 12, 32, 28);
|
|
510
|
-
for (let shelf = 0; shelf < 2; shelf++) {
|
|
511
|
-
rect(c, "#3b2413", x + 2, y - 10 + shelf * 13, 28, 11);
|
|
512
|
-
for (let book = 0; book < 8; book++) {
|
|
513
|
-
const color = ["#ef4444", "#3b82f6", "#10b981", "#f59e0b", "#8b5cf6"][(book * 3 + shelf + item.x) % 5];
|
|
514
|
-
rect(c, color, x + 3 + book * 3, y - 8 + shelf * 13 + (book % 3), 2, 9 - (book % 3));
|
|
515
|
-
rect(c, shade(color, 40), x + 3 + book * 3, y - 6 + shelf * 13, 2, 1);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
break;
|
|
519
|
-
case "server":
|
|
520
|
-
rect(c, OUTLINE, x, y - 13, 16, 29);
|
|
521
|
-
rect(c, "#1f2937", x + 1, y - 12, 14, 27);
|
|
522
|
-
for (let i = 0; i < 5; i++) {
|
|
523
|
-
rect(c, "#111827", x + 2, y - 11 + i * 5, 12, 4);
|
|
524
|
-
const on = Math.floor(time * 3 + i * 2 + item.x) % 3 !== 0;
|
|
525
|
-
rect(c, on ? "#22c55e" : "#14532d", x + 3, y - 10 + i * 5, 1, 1);
|
|
526
|
-
rect(c, i % 2 === 0 ? "#f59e0b" : "#3b82f6", x + 5, y - 10 + i * 5, 1, 1);
|
|
527
|
-
rect(c, "#374151", x + 8, y - 10 + i * 5, 5, 1);
|
|
528
|
-
}
|
|
529
|
-
break;
|
|
530
|
-
case "table":
|
|
531
|
-
rect(c, "#44403c", x + 2, y + 16, 44, 12);
|
|
532
|
-
rect(c, "#78716c", x + 2, y + 3, 44, 14);
|
|
533
|
-
rect(c, "#a8a29e", x + 2, y + 3, 44, 2);
|
|
534
|
-
rect(c, "#f9fafb", x + 10, y + 6, 8, 6);
|
|
535
|
-
rect(c, "#f9fafb", x + 28, y + 8, 8, 6);
|
|
536
|
-
rect(c, "#1f2937", x + 12, y + 8, 4, 1);
|
|
537
|
-
rect(c, "#3b82f6", x + 30, y + 10, 4, 1);
|
|
538
|
-
break;
|
|
539
|
-
case "rug":
|
|
540
|
-
rect(c, palette.primary, x, y, 64, 48);
|
|
541
|
-
c.fillStyle = "rgba(0,0,0,0.42)";
|
|
542
|
-
c.fillRect(x + 3, y + 3, 58, 42);
|
|
543
|
-
rect(c, palette.primary, x + 6, y + 6, 52, 1);
|
|
544
|
-
rect(c, palette.primary, x + 6, y + 41, 52, 1);
|
|
545
|
-
rect(c, palette.primary, x + 6, y + 6, 1, 36);
|
|
546
|
-
rect(c, palette.primary, x + 57, y + 6, 1, 36);
|
|
547
|
-
for (let i = 0; i < 6; i++)
|
|
548
|
-
rect(c, palette.primary, x + 12 + i * 8, y + 22, 4, 4);
|
|
549
|
-
break;
|
|
550
|
-
case "arcade":
|
|
551
|
-
rect(c, OUTLINE, x, y - 14, 16, 30);
|
|
552
|
-
rect(c, "#4c1d95", x + 1, y - 13, 14, 28);
|
|
553
|
-
rect(c, "#111827", x + 3, y - 10, 10, 9);
|
|
554
|
-
rect(c, ["#22c55e", "#ec4899", "#38bdf8"][Math.floor(time * 2) % 3], x + 4 + (Math.floor(time * 4) % 7), y - 8 + (Math.floor(time * 3) % 5), 2, 2);
|
|
555
|
-
rect(c, "#f59e0b", x + 3, y - 13, 10, 2);
|
|
556
|
-
rect(c, "#27272a", x + 2, y + 1, 12, 4);
|
|
557
|
-
rect(c, "#ef4444", x + 4, y + 2, 2, 2);
|
|
558
|
-
rect(c, "#3b82f6", x + 9, y + 2, 2, 2);
|
|
559
|
-
break;
|
|
560
|
-
case "fridge":
|
|
561
|
-
rect(c, OUTLINE, x + 1, y - 12, 14, 28);
|
|
562
|
-
rect(c, "#e5e7eb", x + 2, y - 11, 12, 26);
|
|
563
|
-
rect(c, "#9ca3af", x + 2, y - 3, 12, 1);
|
|
564
|
-
rect(c, "#6b7280", x + 11, y - 8, 1, 3);
|
|
565
|
-
rect(c, "#6b7280", x + 11, y, 1, 5);
|
|
566
|
-
rect(c, "#fde68a", x + 4, y - 9, 3, 3);
|
|
567
|
-
rect(c, "#fca5a5", x + 5, y + 3, 3, 3);
|
|
568
|
-
break;
|
|
569
|
-
case "printer":
|
|
570
|
-
rect(c, "#4b5563", x + 2, y + 8, 12, 8);
|
|
571
|
-
rect(c, OUTLINE, x + 1, y - 2, 14, 11);
|
|
572
|
-
rect(c, "#d1d5db", x + 2, y - 1, 12, 9);
|
|
573
|
-
rect(c, "#f9fafb", x + 4, y - 5 - (Math.floor(time) % 4 === 0 ? 1 : 0), 8, 5);
|
|
574
|
-
rect(c, "#22c55e", x + 11, y + 1, 2, 1);
|
|
575
|
-
rect(c, "#9ca3af", x + 3, y + 5, 10, 1);
|
|
576
|
-
break;
|
|
577
|
-
case "lamp":
|
|
578
|
-
rect(c, "#374151", x + 5, y + 13, 6, 3);
|
|
579
|
-
rect(c, "#4b5563", x + 7, y - 4, 2, 17);
|
|
580
|
-
rect(c, OUTLINE, x + 2, y - 10, 12, 8);
|
|
581
|
-
rect(c, "#fde68a", x + 3, y - 9, 10, 6);
|
|
582
|
-
rect(c, "#fef3c7", x + 4, y - 8, 4, 2);
|
|
583
|
-
break;
|
|
584
|
-
case "beanbag":
|
|
585
|
-
rect(c, OUTLINE, x, y + 3, 16, 13);
|
|
586
|
-
rect(c, shade(palette.primary, -50), x + 1, y + 4, 14, 11);
|
|
587
|
-
rect(c, shade(palette.primary, -20), x + 3, y + 5, 8, 4);
|
|
588
|
-
rect(c, "#00000040", x + 1, y + 13, 14, 2);
|
|
589
|
-
break;
|
|
590
|
-
case "tv":
|
|
591
|
-
rect(c, "#3b2413", x + 2, y + 8, 44, 8);
|
|
592
|
-
rect(c, "#5b3a21", x + 2, y + 8, 44, 2);
|
|
593
|
-
rect(c, OUTLINE, x + 4, y - 14, 40, 22);
|
|
594
|
-
rect(c, "#0b1120", x + 5, y - 13, 38, 20);
|
|
595
|
-
for (let i = 0; i < 9; i++) {
|
|
596
|
-
const h = 2 + ((i * 5 + Math.floor(time * 3)) % 12);
|
|
597
|
-
rect(c, i % 3 === 0 ? palette.primary : i % 3 === 1 ? "#38bdf8" : "#f472b6", x + 8 + i * 4, y + 5 - h, 3, h);
|
|
598
|
-
}
|
|
599
|
-
break;
|
|
600
|
-
case "vending":
|
|
601
|
-
rect(c, OUTLINE, x, y - 14, 16, 30);
|
|
602
|
-
rect(c, "#b91c1c", x + 1, y - 13, 14, 28);
|
|
603
|
-
rect(c, "#0f172a", x + 3, y - 11, 8, 16);
|
|
604
|
-
for (let row = 0; row < 3; row++)
|
|
605
|
-
for (let col = 0; col < 2; col++)
|
|
606
|
-
rect(c, ["#fbbf24", "#34d399", "#60a5fa", "#f472b6"][(row + col * 2) % 4], x + 4 + col * 3, y - 10 + row * 5, 2, 4);
|
|
607
|
-
rect(c, "#d1d5db", x + 12, y - 9, 2, 6);
|
|
608
|
-
rect(c, "#111827", x + 3, y + 8, 10, 4);
|
|
609
|
-
break;
|
|
610
|
-
case "trophy":
|
|
611
|
-
rect(c, "#6b4427", x + 2, y + 8, 12, 8);
|
|
612
|
-
rect(c, "#8a5a33", x + 2, y + 8, 12, 2);
|
|
613
|
-
rect(c, "#78350f", x + 5, y + 5, 6, 3);
|
|
614
|
-
rect(c, "#f59e0b", x + 7, y + 1, 2, 4);
|
|
615
|
-
rect(c, OUTLINE, x + 3, y - 8, 10, 10);
|
|
616
|
-
rect(c, "#fbbf24", x + 4, y - 7, 8, 8);
|
|
617
|
-
rect(c, "#fde68a", x + 5, y - 6, 2, 5);
|
|
618
|
-
rect(c, "#fbbf24", x + 1, y - 6, 3, 1);
|
|
619
|
-
rect(c, "#fbbf24", x + 12, y - 6, 3, 1);
|
|
620
|
-
break;
|
|
621
|
-
case "clock": {
|
|
622
|
-
rect(c, OUTLINE, x + 3, y + 1, 10, 10);
|
|
623
|
-
rect(c, "#f9fafb", x + 4, y + 2, 8, 8);
|
|
624
|
-
rect(c, "#1f2937", x + 8, y + 3, 1, 3);
|
|
625
|
-
rect(c, "#1f2937", x + 8, y + 6, 3, 1);
|
|
626
|
-
break;
|
|
627
|
-
}
|
|
628
|
-
case "poster": {
|
|
629
|
-
const colors = ["#7c3aed", "#0ea5e9", "#db2777", "#059669"];
|
|
630
|
-
const color = colors[(item.x + item.y) % colors.length];
|
|
631
|
-
rect(c, OUTLINE, x + 2, y, 12, 11);
|
|
632
|
-
rect(c, color, x + 3, y + 1, 10, 9);
|
|
633
|
-
rect(c, shade(color, 60), x + 5, y + 3, 6, 3);
|
|
634
|
-
rect(c, "#f9fafb", x + 5, y + 7, 6, 1);
|
|
635
|
-
break;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
416
|
function nameTag(c, agent, selected, width) {
|
|
640
417
|
c.font = LABEL_FONT;
|
|
641
418
|
c.textBaseline = "top";
|
|
@@ -696,7 +473,7 @@ function drawProgress(c, world, palette) {
|
|
|
696
473
|
const w = 96;
|
|
697
474
|
rect(c, "rgba(17,24,39,0.85)", 4, 4, w, 20);
|
|
698
475
|
c.fillStyle = "#ffffff";
|
|
699
|
-
c.fillText(
|
|
476
|
+
c.fillText(`${world.pack.words.quests} ${done}/${world.tasks.length}`, 8, 6.5);
|
|
700
477
|
rect(c, "#374151", 8, 16, w - 8, 4);
|
|
701
478
|
rect(c, palette.primary, 8, 16, Math.round(((w - 8) * done) / world.tasks.length), 4);
|
|
702
479
|
}
|
|
@@ -708,12 +485,18 @@ export function drawWorld(c, world, time, palette, daylight, building, selected)
|
|
|
708
485
|
c.drawImage(building, 0, 0, width, height);
|
|
709
486
|
else
|
|
710
487
|
drawBuilding(c, world, palette, daylight);
|
|
488
|
+
// On the floor, so people and furniture stay in front of the lines.
|
|
489
|
+
if (world.showLinks && world.links.length > 0) {
|
|
490
|
+
world.routes ??= linkRoutes(world);
|
|
491
|
+
drawLinks(c, world.routes, time);
|
|
492
|
+
}
|
|
493
|
+
const painter = painterFor(world);
|
|
711
494
|
const scene = [];
|
|
712
495
|
for (const item of world.furniture) {
|
|
713
|
-
const spec =
|
|
496
|
+
const spec = specOf(world, item);
|
|
714
497
|
if (spec.solid === false)
|
|
715
498
|
continue;
|
|
716
|
-
scene.push({ depth: (item.y + spec.h) * TILE - 2, draw: () =>
|
|
499
|
+
scene.push({ depth: (item.y + spec.h) * TILE - 2, draw: () => painterFor(world).furniture(c, item, world, time, palette) });
|
|
717
500
|
}
|
|
718
501
|
const shown = world.agents.filter((agent) => agent.state !== "away");
|
|
719
502
|
for (const agent of shown)
|
|
@@ -734,15 +517,8 @@ export function drawWorld(c, world, time, palette, daylight, building, selected)
|
|
|
734
517
|
c.fillStyle = gradient;
|
|
735
518
|
c.fillRect(x - radius, y - radius, radius * 2, radius * 2);
|
|
736
519
|
};
|
|
737
|
-
for (const item of world.furniture)
|
|
738
|
-
|
|
739
|
-
glow(item.x * TILE + 8, item.y * TILE, 36, "rgba(253,224,139,0.30)");
|
|
740
|
-
if (item.type === "arcade" || item.type === "vending" || item.type === "tv")
|
|
741
|
-
glow(item.x * TILE + 8, item.y * TILE, 26, "rgba(125,211,252,0.16)");
|
|
742
|
-
if (item.type === "desk" && world.agents.some((a) => a.seat?.x === item.x + 1 && a.seat?.y === item.y + 2 && isSeated(a))) {
|
|
743
|
-
glow(item.x * TILE + 24, item.y * TILE + 8, 30, "rgba(125,211,252,0.26)");
|
|
744
|
-
}
|
|
745
|
-
}
|
|
520
|
+
for (const item of world.furniture)
|
|
521
|
+
painter.glow(item, world, glow);
|
|
746
522
|
c.restore();
|
|
747
523
|
}
|
|
748
524
|
// On top, so tall furniture against a wall can't hide a room's name.
|