@blkc-code/render-canvas 0.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blkc-code/render-canvas",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "description": "Canvas renderer for BLKC: draws canvas Blocks such as the Pixel Office",
6
6
  "main": "dist/index.js",
package/src/draw.ts CHANGED
@@ -351,6 +351,13 @@ function drawWalls(c: Ctx, world: World, palette: Palette, daylight: Daylight):
351
351
  function drawLabels(c: Ctx, world: World): void {
352
352
  c.font = LABEL_FONT;
353
353
  c.textBaseline = "top";
354
+ for (const item of world.furniture) {
355
+ if (!item.label) continue;
356
+ const width = Math.ceil(c.measureText(item.label).width) + 4;
357
+ rect(c, "#f4ecd8", item.x * TILE + 17, item.y * TILE + 3, width, 10);
358
+ c.fillStyle = "#1c1408";
359
+ c.fillText(item.label, item.x * TILE + 19, item.y * TILE + 4.5);
360
+ }
354
361
  for (const room of world.rooms) {
355
362
  if (!room.name) continue;
356
363
  const label = room.name.toUpperCase();
@@ -369,7 +376,7 @@ export function drawBuilding(c: Ctx, world: World, palette: Palette, daylight: D
369
376
  rect(c, WALL_TOP, 0, 0, world.cols * TILE, world.rows * TILE);
370
377
  for (const room of world.rooms) drawFloor(c, room);
371
378
  drawWalls(c, world, palette, daylight);
372
- for (const item of world.furniture) if (item.type === "rug") painterFor(world).furniture(c, item, world, 0, palette);
379
+ for (const item of world.furniture) if (item.type === "rug" || item.type === "pile") painterFor(world).furniture(c, item, world, 0, palette);
373
380
  for (const item of world.furniture) if (specOf(world, item).wall) painterFor(world).furniture(c, item, world, 0, palette);
374
381
  // Light from the windows falling across the floor.
375
382
  if (daylight.sun !== "rgba(0,0,0,0)") {
package/src/office.ts CHANGED
@@ -76,8 +76,10 @@ export function createOffice(canvas: HTMLCanvasElement, scene: OfficeScene = {},
76
76
  const width = (): number => world.cols * TILE;
77
77
  const height = (): number => world.rows * TILE;
78
78
 
79
+ // A big map starts small on screen, so it can be zoomed further: as far as 4x true size.
80
+ const zoomLimit = (): number => Math.max(MAX_ZOOM, scale > 0 ? 4 / scale : MAX_ZOOM);
79
81
  const clampView = (): void => {
80
- view.zoom = Math.min(MAX_ZOOM, Math.max(1, view.zoom));
82
+ view.zoom = Math.min(zoomLimit(), Math.max(1, view.zoom));
81
83
  view.x = Math.min(width() - width() / view.zoom, Math.max(0, view.x));
82
84
  view.y = Math.min(height() - height() / view.zoom, Math.max(0, view.y));
83
85
  };
@@ -192,7 +194,7 @@ export function createOffice(canvas: HTMLCanvasElement, scene: OfficeScene = {},
192
194
  return;
193
195
  }
194
196
  const before = event ? tileAt(event) : { x: view.x / TILE + width() / view.zoom / TILE / 2, y: view.y / TILE + height() / view.zoom / TILE / 2 };
195
- view.zoom = Math.min(MAX_ZOOM, Math.max(1, view.zoom * factor));
197
+ view.zoom = Math.min(zoomLimit(), Math.max(1, view.zoom * factor));
196
198
  if (event) {
197
199
  const after = tileAt(event);
198
200
  view.x += (before.x - after.x) * TILE;
@@ -233,7 +235,7 @@ export function createOffice(canvas: HTMLCanvasElement, scene: OfficeScene = {},
233
235
  setLayout(layout) {
234
236
  applyLayout(world, layout);
235
237
  buildingDirty = true;
236
- view.zoom = Math.min(view.zoom, MAX_ZOOM);
238
+ view.zoom = Math.min(view.zoom, zoomLimit());
237
239
  clampView();
238
240
  scale = 0;
239
241
  resize();
package/src/types.ts CHANGED
@@ -22,7 +22,7 @@ export interface OfficeRoom {
22
22
  paths?: string[];
23
23
  }
24
24
  export interface OfficeDoor { x: number; y: number }
25
- export interface OfficeFurniture { type: FurnitureType; x: number; y: number }
25
+ export interface OfficeFurniture { type: FurnitureType; x: number; y: number; label?: string }
26
26
  export interface OfficeAgent {
27
27
  id: string;
28
28
  name?: string;
package/src/world.ts CHANGED
@@ -8,10 +8,13 @@ import { OFFICE, OFFICE_ITEMS, OFFICE_LAYOUT } from "./worlds/office-data.js";
8
8
  import type { LinkRoute } from "./links.js";
9
9
  import type { AgentState, FloorStyle, FurnitureType, OfficeAgent, OfficeDoor, OfficeFurniture, OfficeLayout, OfficeRoom, OfficeScene, OfficeTask, TaskState } from "./types.js";
10
10
 
11
- export const MAX_COLS = 64;
12
- export const MAX_ROWS = 40;
11
+ export const MAX_COLS = 1024;
12
+ export const MAX_ROWS = 1024;
13
13
  const MAX_AGENTS = 40;
14
- const MAX_FURNITURE = 300;
14
+ const MAX_FURNITURE = 60000;
15
+ const MAX_ROOMS = 8000;
16
+ const MAX_DOORS = 16000;
17
+ const MAX_LINKS = 4000;
15
18
  const MAX_TASKS = 60;
16
19
  export const WALK_SPEED = 4.5; // tiles a second
17
20
 
@@ -29,7 +32,7 @@ const str = (value: unknown, max = 120): string => (typeof value === "string" ?
29
32
  const list = <T>(value: unknown, max: number): T[] => (Array.isArray(value) ? (value.slice(0, max) as T[]) : []);
30
33
 
31
34
  export interface Room { id: string; name: string; x: number; y: number; w: number; h: number; floor: FloorStyle; paths: string[] }
32
- export interface Item { type: FurnitureType; x: number; y: number }
35
+ export interface Item { type: FurnitureType; x: number; y: number; label?: string }
33
36
  export interface Task { title: string; state: TaskState; agent: string }
34
37
 
35
38
  export interface Agent {
@@ -165,8 +168,8 @@ function buildLayout(scene: OfficeLayout, pack: WorldData): Pick<World, "links"
165
168
  const custom = Array.isArray(scene?.rooms) && scene.rooms.length > 0;
166
169
  const cols = Math.min(Math.max(int(scene?.cols, custom ? 20 : pack.layout.cols), 6), MAX_COLS);
167
170
  const rows = Math.min(Math.max(int(scene?.rows, custom ? 14 : pack.layout.rows), 6), MAX_ROWS);
168
- const rawRooms = custom ? list<OfficeRoom>(scene.rooms, 40) : pack.layout.rooms;
169
- const rawDoors = custom ? list<OfficeDoor>(scene.doors, 60) : pack.layout.doors;
171
+ const rawRooms = custom ? list<OfficeRoom>(scene.rooms, MAX_ROOMS) : pack.layout.rooms;
172
+ const rawDoors = custom ? list<OfficeDoor>(scene.doors, MAX_DOORS) : pack.layout.doors;
170
173
  const rawItems = Array.isArray(scene?.furniture) ? list<OfficeFurniture>(scene.furniture, MAX_FURNITURE) : custom ? [] : pack.layout.furniture;
171
174
 
172
175
  // Clamped to the grid: everything later loops over these, so a huge room must not hang the page.
@@ -175,7 +178,7 @@ function buildLayout(scene: OfficeLayout, pack: WorldData): Pick<World, "links"
175
178
  const y = Math.min(Math.max(int(room?.y, 0), 0), rows);
176
179
  return {
177
180
  id: str(room?.id, 60) || `room-${index + 1}`,
178
- name: str(room?.name, 24),
181
+ name: str(room?.name, 60),
179
182
  x, y,
180
183
  w: Math.min(Math.max(int(room?.w, 0), 0), cols - x),
181
184
  h: Math.min(Math.max(int(room?.h, 0), 0), rows - y),
@@ -195,7 +198,10 @@ function buildLayout(scene: OfficeLayout, pack: WorldData): Pick<World, "links"
195
198
 
196
199
  const furniture: Item[] = rawItems
197
200
  .filter((item) => item && Object.hasOwn(pack.items, item.type))
198
- .map((item) => ({ type: item.type, x: int(item.x, -99), y: int(item.y, -99) }));
201
+ .map((item) => {
202
+ const label = str(item.label, 40);
203
+ return { type: item.type, x: int(item.x, -99), y: int(item.y, -99), ...(label ? { label } : {}) };
204
+ });
199
205
  for (const item of furniture) {
200
206
  const spec = pack.items[item.type] as ItemSpec;
201
207
  if (spec.solid === false) continue;
@@ -218,7 +224,7 @@ function buildLayout(scene: OfficeLayout, pack: WorldData): Pick<World, "links"
218
224
  if (use === "lounge") spots.lounge.push(point);
219
225
  if (use === "board") spots.board.push(point);
220
226
  }
221
- const links = list<{ from: string; to: string }>(scene?.links, 60)
227
+ const links = list<{ from: string; to: string }>(scene?.links, MAX_LINKS)
222
228
  .map((link) => ({ from: str(link?.from, 60), to: str(link?.to, 60) }))
223
229
  .filter((link) => link.from && link.to);
224
230
  return { cols, rows, rooms, doors, furniture, walkable, seats, spots, links };
@@ -17,6 +17,7 @@ export const OFFICE_ITEMS: WorldData["items"] = {
17
17
  server: { w: 1, h: 1 },
18
18
  table: { w: 3, h: 2, spot: { x: 1, y: 2 }, use: "board" },
19
19
  rug: { w: 4, h: 3, solid: false },
20
+ pile: { w: 1, h: 1, solid: false },
20
21
  arcade: { w: 1, h: 1, spot: { x: 0, y: 1 }, use: "lounge" },
21
22
  fridge: { w: 1, h: 1, spot: { x: 0, y: 1 }, use: "lounge" },
22
23
  printer: { w: 1, h: 1, spot: { x: 0, y: 1 } },
@@ -167,6 +167,14 @@ function drawFurniture(c: Ctx, item: Item, world: World, time: number, palette:
167
167
  rect(c, "#1f2937", x + 12, y + 8, 4, 1);
168
168
  rect(c, "#3b82f6", x + 30, y + 10, 4, 1);
169
169
  break;
170
+ case "pile":
171
+ // A stack of boxes, for a group of loose files.
172
+ rect(c, "#14141f", x + 1, y + 5, 14, 10);
173
+ rect(c, "#b98b55", x + 2, y + 6, 12, 8);
174
+ rect(c, "#d3a874", x + 2, y + 6, 12, 2);
175
+ rect(c, "#14141f", x + 4, y + 1, 8, 5);
176
+ rect(c, "#d3a874", x + 5, y + 2, 6, 3);
177
+ break;
170
178
  case "rug":
171
179
  rect(c, palette.primary, x, y, 64, 48);
172
180
  c.fillStyle = "rgba(0,0,0,0.42)";