@blkc-code/render-canvas 0.1.0 → 0.6.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.
Files changed (53) hide show
  1. package/dist/draw.d.ts +22 -12
  2. package/dist/draw.d.ts.map +1 -1
  3. package/dist/draw.js +374 -273
  4. package/dist/draw.js.map +1 -1
  5. package/dist/index.d.ts +4 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +4 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/links.d.ts +16 -0
  10. package/dist/links.d.ts.map +1 -0
  11. package/dist/links.js +75 -0
  12. package/dist/links.js.map +1 -0
  13. package/dist/office.d.ts +25 -2
  14. package/dist/office.d.ts.map +1 -1
  15. package/dist/office.js +134 -20
  16. package/dist/office.js.map +1 -1
  17. package/dist/pack.d.ts +31 -0
  18. package/dist/pack.d.ts.map +1 -0
  19. package/dist/pack.js +10 -0
  20. package/dist/pack.js.map +1 -0
  21. package/dist/paint.d.ts +15 -0
  22. package/dist/paint.d.ts.map +1 -0
  23. package/dist/paint.js +18 -0
  24. package/dist/paint.js.map +1 -0
  25. package/dist/runtime.js +1 -1
  26. package/dist/runtime.js.map +1 -1
  27. package/dist/runtime.txt +1 -1
  28. package/dist/types.d.ts +20 -3
  29. package/dist/types.d.ts.map +1 -1
  30. package/dist/world.d.ts +29 -13
  31. package/dist/world.d.ts.map +1 -1
  32. package/dist/world.js +150 -72
  33. package/dist/world.js.map +1 -1
  34. package/dist/worlds/office-data.d.ts +14 -0
  35. package/dist/worlds/office-data.d.ts.map +1 -0
  36. package/dist/worlds/office-data.js +52 -0
  37. package/dist/worlds/office-data.js.map +1 -0
  38. package/dist/worlds/office-paint.d.ts +13 -0
  39. package/dist/worlds/office-paint.d.ts.map +1 -0
  40. package/dist/worlds/office-paint.js +292 -0
  41. package/dist/worlds/office-paint.js.map +1 -0
  42. package/package.json +7 -2
  43. package/src/draw.ts +352 -272
  44. package/src/index.ts +4 -1
  45. package/src/links.ts +76 -0
  46. package/src/office.ts +142 -21
  47. package/src/pack.ts +36 -0
  48. package/src/paint.ts +24 -0
  49. package/src/runtime.ts +4 -1
  50. package/src/types.ts +31 -5
  51. package/src/world.ts +161 -77
  52. package/src/worlds/office-data.ts +57 -0
  53. package/src/worlds/office-paint.ts +276 -0
@@ -0,0 +1,276 @@
1
+ /**
2
+ * How the office looks. Every piece of art here is original to this project, made in code at
3
+ * 16 pixels a tile.
4
+ */
5
+
6
+ import { OUTLINE, TILE, rect, shade, type Ctx, type Palette } from "../paint.js";
7
+ import { isSeated, type Item, type World } from "../world.js";
8
+
9
+ function drawDesk(c: Ctx, x: number, y: number, on: boolean, time: number, seed: number, palette: Palette, blocked: boolean): void {
10
+ rect(c, "#55351d", x + 1, y + 22, 46, 8);
11
+ rect(c, "#6b4427", x + 1, y + 13, 46, 10);
12
+ rect(c, "#8a5a33", x + 1, y + 10, 46, 12);
13
+ rect(c, "#a87545", x + 1, y + 10, 46, 2);
14
+ rect(c, "#3b2413", x + 3, y + 22, 3, 9);
15
+ rect(c, "#3b2413", x + 42, y + 22, 3, 9);
16
+ rect(c, "#55351d", x + 30, y + 23, 12, 7);
17
+ rect(c, "#7c5637", x + 31, y + 24, 10, 2);
18
+ rect(c, "#d4a24e", x + 35, y + 24, 2, 1);
19
+ rect(c, "#7c5637", x + 31, y + 27, 10, 2);
20
+ rect(c, "#d4a24e", x + 35, y + 27, 2, 1);
21
+ // Monitor
22
+ rect(c, OUTLINE, x + 14, y - 3, 20, 15);
23
+ rect(c, "#374151", x + 22, y + 12, 4, 2);
24
+ rect(c, "#4b5563", x + 19, y + 14, 10, 1);
25
+ if (blocked) {
26
+ rect(c, "#3f0f14", x + 15, y - 2, 18, 13);
27
+ if (Math.floor(time * 2) % 2 === 0) { rect(c, "#ef4444", x + 23, y, 2, 6); rect(c, "#ef4444", x + 23, y + 7, 2, 2); }
28
+ } else if (on) {
29
+ rect(c, "#0f2a2e", x + 15, y - 2, 18, 13);
30
+ const scroll = Math.floor(time * 3 + (seed % 13));
31
+ for (let line = 0; line < 5; line++) {
32
+ const width = 3 + (((seed >> line) & 7) + (scroll + line) * 7) % 12;
33
+ rect(c, line % 3 === 0 ? palette.primary : line % 3 === 1 ? "#7dd3fc" : "#fcd34d", x + 16 + (line % 2) * 2, y - 1 + line * 2.2, width, 1);
34
+ }
35
+ c.fillStyle = "rgba(125,211,252,0.12)";
36
+ c.fillRect(x + 10, y + 11, 28, 8);
37
+ } else {
38
+ rect(c, "#1f2937", x + 15, y - 2, 18, 13);
39
+ rect(c, "#374151", x + 17, y, 3, 1);
40
+ }
41
+ rect(c, "#d1d5db", x + 16, y + 15, 16, 5);
42
+ for (let row = 0; row < 2; row++) for (let key = 0; key < 7; key++) rect(c, "#9ca3af", x + 17 + key * 2, y + 16 + row * 2, 1, 1);
43
+ rect(c, "#e5e7eb", x + 34, y + 16, 3, 4);
44
+ rect(c, "#f3f4f6", x + 40, y + 13, 5, 5);
45
+ rect(c, palette.primary, x + 40, y + 15, 5, 1);
46
+ rect(c, "#fde68a", x + 4, y + 13, 7, 6);
47
+ rect(c, "#fbbf24", x + 5, y + 15, 5, 1);
48
+ }
49
+
50
+ function drawBoard(c: Ctx, x: number, y: number, world: World): void {
51
+ rect(c, "#3a2c1e", x, y - 12, 64, 27);
52
+ rect(c, "#d9c29c", x + 2, y - 10, 60, 23);
53
+ for (let i = 0; i < 40; i++) rect(c, "#c9b08a", x + 3 + (i * 37) % 58, y - 9 + (i * 13) % 21, 1, 1);
54
+ const colors = { todo: "#fde68a", doing: "#93c5fd", done: "#86efac" };
55
+ const per = 8;
56
+ world.tasks.slice(0, per * 2).forEach((task, index) => {
57
+ const col = index % per;
58
+ const row = Math.floor(index / per);
59
+ rect(c, "#00000033", x + 5 + col * 7, y - 7 + row * 10, 6, 8);
60
+ rect(c, colors[task.state], x + 4 + col * 7, y - 8 + row * 10, 6, 8);
61
+ rect(c, "#ef4444", x + 6 + col * 7, y - 8 + row * 10, 2, 1);
62
+ rect(c, "#00000040", x + 5 + col * 7, y - 5 + row * 10, 4, 1);
63
+ rect(c, "#00000040", x + 5 + col * 7, y - 3 + row * 10, 3, 1);
64
+ });
65
+ }
66
+
67
+ function drawFurniture(c: Ctx, item: Item, world: World, time: number, palette: Palette): void {
68
+ const x = item.x * TILE;
69
+ const y = item.y * TILE;
70
+ switch (item.type) {
71
+ case "desk": {
72
+ const seatX = item.x + 1;
73
+ const seatY = item.y + 2;
74
+ const user = world.agents.find((a) => a.seat?.x === seatX && a.seat?.y === seatY && isSeated(a));
75
+ drawDesk(c, x, y, Boolean(user), time, user?.seed ?? 0, palette, user?.state === "blocked");
76
+ break;
77
+ }
78
+ case "coffee":
79
+ rect(c, "#374151", x, y + 2, 32, 14);
80
+ rect(c, "#6b7280", x, y + 2, 32, 2);
81
+ rect(c, OUTLINE, x + 4, y - 9, 15, 15);
82
+ rect(c, "#27272a", x + 5, y - 8, 13, 13);
83
+ rect(c, Math.floor(time * 2) % 2 ? "#ef4444" : "#7f1d1d", x + 15, y - 6, 2, 2);
84
+ rect(c, "#52525b", x + 7, y - 6, 6, 4);
85
+ rect(c, "#f3f4f6", x + 8, y + 3, 5, 4);
86
+ rect(c, "#f3f4f6", x + 23, y - 1, 5, 5);
87
+ rect(c, "#92400e", x + 24, y, 3, 1);
88
+ for (let i = 0; i < 3; i++) {
89
+ c.fillStyle = "rgba(255,255,255,0.5)";
90
+ c.fillRect(x + 9 + Math.round(Math.sin(time * 2 + i) * 1.5), y - 1 - ((time * 8 + i * 4) % 9), 1, 2);
91
+ }
92
+ break;
93
+ case "water":
94
+ rect(c, "#d1d5db", x + 3, y + 3, 10, 13);
95
+ rect(c, "#9ca3af", x + 3, y + 14, 10, 2);
96
+ rect(c, "#60a5fa", x + 4, y - 8, 8, 11);
97
+ rect(c, "#bae6fd", x + 5, y - 7, 2, 9);
98
+ rect(c, "#3b82f6", x + 5, y + 6, 2, 2);
99
+ rect(c, "#ef4444", x + 9, y + 6, 2, 2);
100
+ if (Math.floor(time * 0.4 + item.x) % 5 === 0) rect(c, "#e0f2fe", x + 8, y - 2 - ((time * 6) % 7), 1, 1);
101
+ break;
102
+ case "sofa":
103
+ rect(c, shade(palette.primary, -60), x + 1, y - 7, 46, 11);
104
+ rect(c, shade(palette.primary, -30), x + 2, y - 6, 44, 3);
105
+ rect(c, "#4b5563", x, y + 3, 48, 11);
106
+ rect(c, "#6b7280", x + 3, y + 3, 20, 7);
107
+ rect(c, "#6b7280", x + 25, y + 3, 20, 7);
108
+ rect(c, "#374151", x, y + 12, 48, 3);
109
+ rect(c, "#374151", x - 1, y - 2, 4, 16);
110
+ rect(c, "#374151", x + 45, y - 2, 4, 16);
111
+ break;
112
+ case "plant":
113
+ rect(c, "#92400e", x + 4, y + 9, 8, 7);
114
+ rect(c, "#b45309", x + 3, y + 8, 10, 2);
115
+ 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]] as const) rect(c, "#15803d", x + lx, y + ly, lw, lh);
116
+ rect(c, "#22c55e", x + 7, y - 3, 2, 8);
117
+ rect(c, "#22c55e", x + 3, y + 1, 2, 3);
118
+ rect(c, "#4ade80", x + 10, y - 4, 1, 3);
119
+ break;
120
+ case "fern":
121
+ rect(c, "#57534e", x + 3, y + 10, 10, 6);
122
+ rect(c, "#78716c", x + 2, y + 9, 12, 2);
123
+ for (let i = 0; i < 7; i++) {
124
+ const lean = i - 3;
125
+ for (let j = 0; j < 5; j++) rect(c, j % 2 ? "#16a34a" : "#15803d", x + 7 + lean * (j / 2 + 1), y + 8 - j * 2 + Math.abs(lean), 2, 2);
126
+ }
127
+ break;
128
+ case "board":
129
+ drawBoard(c, x, y, world);
130
+ break;
131
+ case "whiteboard":
132
+ rect(c, "#9ca3af", x, y - 10, 48, 22);
133
+ rect(c, "#f9fafb", x + 2, y - 8, 44, 18);
134
+ rect(c, "#3b82f6", x + 5, y - 5, 14, 1); rect(c, "#3b82f6", x + 5, y - 2, 9, 1);
135
+ rect(c, "#ef4444", x + 24, y - 6, 8, 6); rect(c, "#f9fafb", x + 25, y - 5, 6, 4);
136
+ rect(c, "#10b981", x + 8, y + 3, 20, 1); rect(c, "#10b981", x + 27, y + 2, 1, 3);
137
+ rect(c, "#6b7280", x + 2, y + 10, 44, 2);
138
+ break;
139
+ case "shelf":
140
+ rect(c, "#5b3a21", x, y - 12, 32, 28);
141
+ for (let shelf = 0; shelf < 2; shelf++) {
142
+ rect(c, "#3b2413", x + 2, y - 10 + shelf * 13, 28, 11);
143
+ for (let book = 0; book < 8; book++) {
144
+ const color = ["#ef4444", "#3b82f6", "#10b981", "#f59e0b", "#8b5cf6"][(book * 3 + shelf + item.x) % 5] as string;
145
+ rect(c, color, x + 3 + book * 3, y - 8 + shelf * 13 + (book % 3), 2, 9 - (book % 3));
146
+ rect(c, shade(color, 40), x + 3 + book * 3, y - 6 + shelf * 13, 2, 1);
147
+ }
148
+ }
149
+ break;
150
+ case "server":
151
+ rect(c, OUTLINE, x, y - 13, 16, 29);
152
+ rect(c, "#1f2937", x + 1, y - 12, 14, 27);
153
+ for (let i = 0; i < 5; i++) {
154
+ rect(c, "#111827", x + 2, y - 11 + i * 5, 12, 4);
155
+ const on = Math.floor(time * 3 + i * 2 + item.x) % 3 !== 0;
156
+ rect(c, on ? "#22c55e" : "#14532d", x + 3, y - 10 + i * 5, 1, 1);
157
+ rect(c, i % 2 === 0 ? "#f59e0b" : "#3b82f6", x + 5, y - 10 + i * 5, 1, 1);
158
+ rect(c, "#374151", x + 8, y - 10 + i * 5, 5, 1);
159
+ }
160
+ break;
161
+ case "table":
162
+ rect(c, "#44403c", x + 2, y + 16, 44, 12);
163
+ rect(c, "#78716c", x + 2, y + 3, 44, 14);
164
+ rect(c, "#a8a29e", x + 2, y + 3, 44, 2);
165
+ rect(c, "#f9fafb", x + 10, y + 6, 8, 6);
166
+ rect(c, "#f9fafb", x + 28, y + 8, 8, 6);
167
+ rect(c, "#1f2937", x + 12, y + 8, 4, 1);
168
+ rect(c, "#3b82f6", x + 30, y + 10, 4, 1);
169
+ break;
170
+ case "rug":
171
+ rect(c, palette.primary, x, y, 64, 48);
172
+ c.fillStyle = "rgba(0,0,0,0.42)";
173
+ c.fillRect(x + 3, y + 3, 58, 42);
174
+ rect(c, palette.primary, x + 6, y + 6, 52, 1);
175
+ rect(c, palette.primary, x + 6, y + 41, 52, 1);
176
+ rect(c, palette.primary, x + 6, y + 6, 1, 36);
177
+ rect(c, palette.primary, x + 57, y + 6, 1, 36);
178
+ for (let i = 0; i < 6; i++) rect(c, palette.primary, x + 12 + i * 8, y + 22, 4, 4);
179
+ break;
180
+ case "arcade":
181
+ rect(c, OUTLINE, x, y - 14, 16, 30);
182
+ rect(c, "#4c1d95", x + 1, y - 13, 14, 28);
183
+ rect(c, "#111827", x + 3, y - 10, 10, 9);
184
+ rect(c, ["#22c55e", "#ec4899", "#38bdf8"][Math.floor(time * 2) % 3] as string, x + 4 + (Math.floor(time * 4) % 7), y - 8 + (Math.floor(time * 3) % 5), 2, 2);
185
+ rect(c, "#f59e0b", x + 3, y - 13, 10, 2);
186
+ rect(c, "#27272a", x + 2, y + 1, 12, 4);
187
+ rect(c, "#ef4444", x + 4, y + 2, 2, 2);
188
+ rect(c, "#3b82f6", x + 9, y + 2, 2, 2);
189
+ break;
190
+ case "fridge":
191
+ rect(c, OUTLINE, x + 1, y - 12, 14, 28);
192
+ rect(c, "#e5e7eb", x + 2, y - 11, 12, 26);
193
+ rect(c, "#9ca3af", x + 2, y - 3, 12, 1);
194
+ rect(c, "#6b7280", x + 11, y - 8, 1, 3);
195
+ rect(c, "#6b7280", x + 11, y, 1, 5);
196
+ rect(c, "#fde68a", x + 4, y - 9, 3, 3);
197
+ rect(c, "#fca5a5", x + 5, y + 3, 3, 3);
198
+ break;
199
+ case "printer":
200
+ rect(c, "#4b5563", x + 2, y + 8, 12, 8);
201
+ rect(c, OUTLINE, x + 1, y - 2, 14, 11);
202
+ rect(c, "#d1d5db", x + 2, y - 1, 12, 9);
203
+ rect(c, "#f9fafb", x + 4, y - 5 - (Math.floor(time) % 4 === 0 ? 1 : 0), 8, 5);
204
+ rect(c, "#22c55e", x + 11, y + 1, 2, 1);
205
+ rect(c, "#9ca3af", x + 3, y + 5, 10, 1);
206
+ break;
207
+ case "lamp":
208
+ rect(c, "#374151", x + 5, y + 13, 6, 3);
209
+ rect(c, "#4b5563", x + 7, y - 4, 2, 17);
210
+ rect(c, OUTLINE, x + 2, y - 10, 12, 8);
211
+ rect(c, "#fde68a", x + 3, y - 9, 10, 6);
212
+ rect(c, "#fef3c7", x + 4, y - 8, 4, 2);
213
+ break;
214
+ case "beanbag":
215
+ rect(c, OUTLINE, x, y + 3, 16, 13);
216
+ rect(c, shade(palette.primary, -50), x + 1, y + 4, 14, 11);
217
+ rect(c, shade(palette.primary, -20), x + 3, y + 5, 8, 4);
218
+ rect(c, "#00000040", x + 1, y + 13, 14, 2);
219
+ break;
220
+ case "tv":
221
+ rect(c, "#3b2413", x + 2, y + 8, 44, 8);
222
+ rect(c, "#5b3a21", x + 2, y + 8, 44, 2);
223
+ rect(c, OUTLINE, x + 4, y - 14, 40, 22);
224
+ rect(c, "#0b1120", x + 5, y - 13, 38, 20);
225
+ for (let i = 0; i < 9; i++) {
226
+ const h = 2 + ((i * 5 + Math.floor(time * 3)) % 12);
227
+ rect(c, i % 3 === 0 ? palette.primary : i % 3 === 1 ? "#38bdf8" : "#f472b6", x + 8 + i * 4, y + 5 - h, 3, h);
228
+ }
229
+ break;
230
+ case "vending":
231
+ rect(c, OUTLINE, x, y - 14, 16, 30);
232
+ rect(c, "#b91c1c", x + 1, y - 13, 14, 28);
233
+ rect(c, "#0f172a", x + 3, y - 11, 8, 16);
234
+ for (let row = 0; row < 3; row++) for (let col = 0; col < 2; col++) rect(c, ["#fbbf24", "#34d399", "#60a5fa", "#f472b6"][(row + col * 2) % 4] as string, x + 4 + col * 3, y - 10 + row * 5, 2, 4);
235
+ rect(c, "#d1d5db", x + 12, y - 9, 2, 6);
236
+ rect(c, "#111827", x + 3, y + 8, 10, 4);
237
+ break;
238
+ case "trophy":
239
+ rect(c, "#6b4427", x + 2, y + 8, 12, 8);
240
+ rect(c, "#8a5a33", x + 2, y + 8, 12, 2);
241
+ rect(c, "#78350f", x + 5, y + 5, 6, 3);
242
+ rect(c, "#f59e0b", x + 7, y + 1, 2, 4);
243
+ rect(c, OUTLINE, x + 3, y - 8, 10, 10);
244
+ rect(c, "#fbbf24", x + 4, y - 7, 8, 8);
245
+ rect(c, "#fde68a", x + 5, y - 6, 2, 5);
246
+ rect(c, "#fbbf24", x + 1, y - 6, 3, 1); rect(c, "#fbbf24", x + 12, y - 6, 3, 1);
247
+ break;
248
+ case "clock": {
249
+ rect(c, OUTLINE, x + 3, y + 1, 10, 10);
250
+ rect(c, "#f9fafb", x + 4, y + 2, 8, 8);
251
+ rect(c, "#1f2937", x + 8, y + 3, 1, 3);
252
+ rect(c, "#1f2937", x + 8, y + 6, 3, 1);
253
+ break;
254
+ }
255
+ case "poster": {
256
+ const colors = ["#7c3aed", "#0ea5e9", "#db2777", "#059669"];
257
+ const color = colors[(item.x + item.y) % colors.length] as string;
258
+ rect(c, OUTLINE, x + 2, y, 12, 11);
259
+ rect(c, color, x + 3, y + 1, 10, 9);
260
+ rect(c, shade(color, 60), x + 5, y + 3, 6, 3);
261
+ rect(c, "#f9fafb", x + 5, y + 7, 6, 1);
262
+ break;
263
+ }
264
+ }
265
+ }
266
+
267
+ export const OFFICE_PAINTER = {
268
+ furniture: drawFurniture,
269
+ glow(item: Item, world: World, glow: (x: number, y: number, radius: number, color: string) => void): void {
270
+ if (item.type === "lamp") glow(item.x * TILE + 8, item.y * TILE, 36, "rgba(253,224,139,0.30)");
271
+ if (item.type === "arcade" || item.type === "vending" || item.type === "tv") glow(item.x * TILE + 8, item.y * TILE, 26, "rgba(125,211,252,0.16)");
272
+ if (item.type === "desk" && world.agents.some((a) => a.seat?.x === item.x + 1 && a.seat?.y === item.y + 2 && isSeated(a))) {
273
+ glow(item.x * TILE + 24, item.y * TILE + 8, 30, "rgba(125,211,252,0.26)");
274
+ }
275
+ },
276
+ };