@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/src/types.ts
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
export type AgentState = "working" | "thinking" | "blocked" | "reviewing" | "idle" | "done" | "away";
|
|
4
4
|
export type TaskState = "todo" | "doing" | "done";
|
|
5
|
-
export type FloorStyle = "wood" | "carpet" | "tile" | "stone" | "concrete" | "grass";
|
|
6
|
-
export type
|
|
5
|
+
export type FloorStyle = "wood" | "carpet" | "tile" | "stone" | "concrete" | "grass" | "hall";
|
|
6
|
+
export type OfficeFurnitureType =
|
|
7
7
|
| "desk" | "coffee" | "sofa" | "plant" | "fern" | "board" | "whiteboard" | "shelf" | "server" | "table" | "rug"
|
|
8
8
|
| "water" | "arcade" | "fridge" | "printer" | "lamp" | "beanbag" | "tv" | "vending" | "clock" | "poster" | "trophy";
|
|
9
9
|
|
|
10
|
+
/** An office item, or whatever else the world defines. */
|
|
11
|
+
export type FurnitureType = OfficeFurnitureType | (string & {});
|
|
12
|
+
|
|
10
13
|
export interface OfficeRoom {
|
|
11
14
|
id?: string;
|
|
12
15
|
name?: string;
|
|
@@ -19,7 +22,7 @@ export interface OfficeRoom {
|
|
|
19
22
|
paths?: string[];
|
|
20
23
|
}
|
|
21
24
|
export interface OfficeDoor { x: number; y: number }
|
|
22
|
-
export interface OfficeFurniture { type: FurnitureType; x: number; y: number }
|
|
25
|
+
export interface OfficeFurniture { type: FurnitureType; x: number; y: number; label?: string }
|
|
23
26
|
export interface OfficeAgent {
|
|
24
27
|
id: string;
|
|
25
28
|
name?: string;
|
|
@@ -35,15 +38,22 @@ export interface OfficeAgent {
|
|
|
35
38
|
}
|
|
36
39
|
export interface OfficeTask { title: string; state?: TaskState; agent?: string }
|
|
37
40
|
|
|
41
|
+
/** `from` depends on `to`. */
|
|
42
|
+
export interface OfficeLink { from: string; to: string }
|
|
43
|
+
|
|
38
44
|
export interface OfficeLayout {
|
|
39
45
|
cols?: number;
|
|
40
46
|
rows?: number;
|
|
41
47
|
rooms?: OfficeRoom[];
|
|
42
48
|
doors?: OfficeDoor[];
|
|
43
49
|
furniture?: OfficeFurniture[];
|
|
50
|
+
/** How the rooms' code depends on each other. Drawn as lines between them. */
|
|
51
|
+
links?: OfficeLink[];
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
export interface OfficeScene extends OfficeLayout {
|
|
55
|
+
/** Which world to draw. The office if left out or unknown. */
|
|
56
|
+
world?: string;
|
|
47
57
|
agents?: OfficeAgent[];
|
|
48
58
|
tasks?: OfficeTask[];
|
|
49
59
|
}
|
package/src/world.ts
CHANGED
|
@@ -3,70 +3,36 @@
|
|
|
3
3
|
* heading. Nothing in here touches a canvas, so all of it can be tested.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { registerWorld, worldData, type ItemSpec, type WorldData } from "./pack.js";
|
|
7
|
+
import { OFFICE, OFFICE_ITEMS, OFFICE_LAYOUT } from "./worlds/office-data.js";
|
|
8
|
+
import type { LinkRoute } from "./links.js";
|
|
6
9
|
import type { AgentState, FloorStyle, FurnitureType, OfficeAgent, OfficeDoor, OfficeFurniture, OfficeLayout, OfficeRoom, OfficeScene, OfficeTask, TaskState } from "./types.js";
|
|
7
10
|
|
|
8
|
-
export const MAX_COLS =
|
|
9
|
-
export const MAX_ROWS =
|
|
11
|
+
export const MAX_COLS = 1024;
|
|
12
|
+
export const MAX_ROWS = 1024;
|
|
10
13
|
const MAX_AGENTS = 40;
|
|
11
|
-
const MAX_FURNITURE =
|
|
14
|
+
const MAX_FURNITURE = 60000;
|
|
15
|
+
const MAX_ROOMS = 8000;
|
|
16
|
+
const MAX_DOORS = 16000;
|
|
17
|
+
const MAX_LINKS = 4000;
|
|
12
18
|
const MAX_TASKS = 60;
|
|
13
19
|
export const WALK_SPEED = 4.5; // tiles a second
|
|
14
20
|
|
|
15
|
-
/**
|
|
16
|
-
export const FURNITURE
|
|
17
|
-
desk: { w: 3, h: 2, spot: { x: 1, y: 2 } },
|
|
18
|
-
coffee: { w: 2, h: 1, spot: { x: 0, y: 1 } },
|
|
19
|
-
water: { w: 1, h: 1, spot: { x: 0, y: 1 } },
|
|
20
|
-
sofa: { w: 3, h: 1, spot: { x: 1, y: 1 } },
|
|
21
|
-
plant: { w: 1, h: 1 },
|
|
22
|
-
fern: { w: 1, h: 1 },
|
|
23
|
-
board: { w: 4, h: 1, spot: { x: 1, y: 1 } },
|
|
24
|
-
whiteboard: { w: 3, h: 1, spot: { x: 1, y: 1 } },
|
|
25
|
-
shelf: { w: 2, h: 1, spot: { x: 0, y: 1 } },
|
|
26
|
-
server: { w: 1, h: 1 },
|
|
27
|
-
table: { w: 3, h: 2, spot: { x: 1, y: 2 } },
|
|
28
|
-
rug: { w: 4, h: 3, solid: false },
|
|
29
|
-
arcade: { w: 1, h: 1, spot: { x: 0, y: 1 } },
|
|
30
|
-
fridge: { w: 1, h: 1, spot: { x: 0, y: 1 } },
|
|
31
|
-
printer: { w: 1, h: 1, spot: { x: 0, y: 1 } },
|
|
32
|
-
lamp: { w: 1, h: 1 },
|
|
33
|
-
beanbag: { w: 1, h: 1, spot: { x: 0, y: 1 } },
|
|
34
|
-
tv: { w: 3, h: 1, spot: { x: 1, y: 2 } },
|
|
35
|
-
vending: { w: 1, h: 1, spot: { x: 0, y: 1 } },
|
|
36
|
-
trophy: { w: 1, h: 1 },
|
|
37
|
-
clock: { w: 1, h: 1, solid: false, wall: true },
|
|
38
|
-
poster: { w: 1, h: 1, solid: false, wall: true },
|
|
39
|
-
};
|
|
21
|
+
/** Footprints and uses, for the office. Other worlds bring their own. */
|
|
22
|
+
export const FURNITURE = OFFICE_ITEMS;
|
|
40
23
|
|
|
41
24
|
const AGENT_STATES: readonly string[] = ["working", "thinking", "blocked", "reviewing", "idle", "done", "away"];
|
|
42
25
|
const TASK_STATES: readonly string[] = ["todo", "doing", "done"];
|
|
43
|
-
const FLOORS: readonly string[] = ["wood", "carpet", "tile", "stone", "concrete", "grass"];
|
|
44
|
-
|
|
45
|
-
export const DEFAULT_LAYOUT =
|
|
46
|
-
cols: 26,
|
|
47
|
-
rows: 17,
|
|
48
|
-
rooms: [
|
|
49
|
-
{ id: "studio", name: "Studio", x: 1, y: 3, w: 15, h: 13, floor: "wood" },
|
|
50
|
-
{ id: "lounge", name: "Lounge", x: 17, y: 3, w: 8, h: 6, floor: "carpet" },
|
|
51
|
-
{ id: "quests", name: "Quests", x: 17, y: 10, w: 8, h: 6, floor: "tile" },
|
|
52
|
-
] as OfficeRoom[],
|
|
53
|
-
doors: [{ x: 16, y: 6 }, { x: 16, y: 13 }, { x: 23, y: 9 }] as OfficeDoor[],
|
|
54
|
-
furniture: [
|
|
55
|
-
{ type: "desk", x: 2, y: 4 }, { type: "desk", x: 7, y: 4 }, { type: "desk", x: 12, y: 4 },
|
|
56
|
-
{ type: "desk", x: 2, y: 10 }, { type: "desk", x: 7, y: 10 }, { type: "desk", x: 12, y: 10 },
|
|
57
|
-
{ type: "plant", x: 1, y: 8 }, { type: "plant", x: 15, y: 8 }, { type: "server", x: 15, y: 15 }, { type: "server", x: 14, y: 15 },
|
|
58
|
-
{ type: "shelf", x: 1, y: 15 },
|
|
59
|
-
{ type: "coffee", x: 19, y: 3 }, { type: "water", x: 21, y: 3 }, { type: "sofa", x: 22, y: 3 }, { type: "rug", x: 20, y: 5 }, { type: "plant", x: 24, y: 8 },
|
|
60
|
-
{ type: "board", x: 19, y: 10 }, { type: "table", x: 19, y: 13 }, { type: "plant", x: 24, y: 15 },
|
|
61
|
-
] as OfficeFurniture[],
|
|
62
|
-
};
|
|
26
|
+
const FLOORS: readonly string[] = ["wood", "carpet", "tile", "stone", "concrete", "grass", "hall"];
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_LAYOUT = OFFICE_LAYOUT;
|
|
63
29
|
|
|
64
30
|
const int = (value: unknown, fallback: number): number => (typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : fallback);
|
|
65
31
|
const str = (value: unknown, max = 120): string => (typeof value === "string" ? value.slice(0, max) : "");
|
|
66
32
|
const list = <T>(value: unknown, max: number): T[] => (Array.isArray(value) ? (value.slice(0, max) as T[]) : []);
|
|
67
33
|
|
|
68
34
|
export interface Room { id: string; name: string; x: number; y: number; w: number; h: number; floor: FloorStyle; paths: string[] }
|
|
69
|
-
export interface Item { type: FurnitureType; x: number; y: number }
|
|
35
|
+
export interface Item { type: FurnitureType; x: number; y: number; label?: string }
|
|
70
36
|
export interface Task { title: string; state: TaskState; agent: string }
|
|
71
37
|
|
|
72
38
|
export interface Agent {
|
|
@@ -94,6 +60,12 @@ export interface Agent {
|
|
|
94
60
|
}
|
|
95
61
|
|
|
96
62
|
export interface World {
|
|
63
|
+
/** What this world is made of. */
|
|
64
|
+
pack: WorldData;
|
|
65
|
+
links: { from: string; to: string }[];
|
|
66
|
+
/** Worked out when first drawn, and again when the building changes. */
|
|
67
|
+
routes?: LinkRoute[];
|
|
68
|
+
showLinks: boolean;
|
|
97
69
|
cols: number;
|
|
98
70
|
rows: number;
|
|
99
71
|
rooms: Room[];
|
|
@@ -187,13 +159,18 @@ function toTask(raw: OfficeTask): Task {
|
|
|
187
159
|
return { title: str(raw?.title, 80), state: TASK_STATES.includes(raw?.state as string) ? (raw.state as TaskState) : "todo", agent: str(raw?.agent, 60) };
|
|
188
160
|
}
|
|
189
161
|
|
|
190
|
-
|
|
162
|
+
registerWorld(OFFICE);
|
|
163
|
+
|
|
164
|
+
/** A piece of furniture's spec. Only items in the world's list get into a World. */
|
|
165
|
+
export const specOf = (world: Pick<World, "pack">, item: Item): ItemSpec => world.pack.items[item.type] as ItemSpec;
|
|
166
|
+
|
|
167
|
+
function buildLayout(scene: OfficeLayout, pack: WorldData): Pick<World, "links" | "cols" | "rows" | "rooms" | "doors" | "furniture" | "walkable" | "spots" | "seats"> {
|
|
191
168
|
const custom = Array.isArray(scene?.rooms) && scene.rooms.length > 0;
|
|
192
|
-
const cols = Math.min(Math.max(int(scene?.cols, custom ? 20 :
|
|
193
|
-
const rows = Math.min(Math.max(int(scene?.rows, custom ? 14 :
|
|
194
|
-
const rawRooms = custom ? list<OfficeRoom>(scene.rooms,
|
|
195
|
-
const rawDoors = custom ? list<OfficeDoor>(scene.doors,
|
|
196
|
-
const rawItems = Array.isArray(scene?.furniture) ? list<OfficeFurniture>(scene.furniture, MAX_FURNITURE) : custom ? [] :
|
|
169
|
+
const cols = Math.min(Math.max(int(scene?.cols, custom ? 20 : pack.layout.cols), 6), MAX_COLS);
|
|
170
|
+
const rows = Math.min(Math.max(int(scene?.rows, custom ? 14 : pack.layout.rows), 6), MAX_ROWS);
|
|
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;
|
|
173
|
+
const rawItems = Array.isArray(scene?.furniture) ? list<OfficeFurniture>(scene.furniture, MAX_FURNITURE) : custom ? [] : pack.layout.furniture;
|
|
197
174
|
|
|
198
175
|
// Clamped to the grid: everything later loops over these, so a huge room must not hang the page.
|
|
199
176
|
const rooms: Room[] = rawRooms.map((room, index) => {
|
|
@@ -201,7 +178,7 @@ function buildLayout(scene: OfficeLayout): Pick<World, "cols" | "rows" | "rooms"
|
|
|
201
178
|
const y = Math.min(Math.max(int(room?.y, 0), 0), rows);
|
|
202
179
|
return {
|
|
203
180
|
id: str(room?.id, 60) || `room-${index + 1}`,
|
|
204
|
-
name: str(room?.name,
|
|
181
|
+
name: str(room?.name, 60),
|
|
205
182
|
x, y,
|
|
206
183
|
w: Math.min(Math.max(int(room?.w, 0), 0), cols - x),
|
|
207
184
|
h: Math.min(Math.max(int(room?.h, 0), 0), rows - y),
|
|
@@ -220,36 +197,46 @@ function buildLayout(scene: OfficeLayout): Pick<World, "cols" | "rows" | "rooms"
|
|
|
220
197
|
for (const door of doors) mark(door.x, door.y, true);
|
|
221
198
|
|
|
222
199
|
const furniture: Item[] = rawItems
|
|
223
|
-
.filter((item) => item && Object.hasOwn(
|
|
224
|
-
.map((item) =>
|
|
200
|
+
.filter((item) => item && Object.hasOwn(pack.items, item.type))
|
|
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
|
+
});
|
|
225
205
|
for (const item of furniture) {
|
|
226
|
-
const spec =
|
|
206
|
+
const spec = pack.items[item.type] as ItemSpec;
|
|
227
207
|
if (spec.solid === false) continue;
|
|
228
208
|
for (let y = item.y; y < item.y + spec.h; y++) for (let x = item.x; x < item.x + spec.w; x++) mark(x, y, false);
|
|
229
209
|
}
|
|
230
210
|
|
|
231
211
|
const open = (x: number, y: number): boolean => x >= 0 && y >= 0 && x < cols && y < rows && walkable[y * cols + x] === true;
|
|
232
212
|
const spot = (item: Item): { x: number; y: number } | undefined => {
|
|
233
|
-
const offset =
|
|
213
|
+
const offset = (pack.items[item.type] as ItemSpec).spot;
|
|
234
214
|
if (!offset) return undefined;
|
|
235
215
|
const point = { x: item.x + offset.x, y: item.y + offset.y };
|
|
236
216
|
return open(point.x, point.y) ? point : undefined;
|
|
237
217
|
};
|
|
238
|
-
const seats = furniture.filter((item) => item.type === "
|
|
218
|
+
const seats = furniture.filter((item) => (pack.items[item.type] as ItemSpec).use === "seat").map(spot).filter((p): p is { x: number; y: number } => Boolean(p));
|
|
239
219
|
const spots: World["spots"] = { lounge: [], board: [] };
|
|
240
220
|
for (const item of furniture) {
|
|
241
221
|
const point = spot(item);
|
|
242
222
|
if (!point) continue;
|
|
243
|
-
|
|
244
|
-
if (
|
|
223
|
+
const use = (pack.items[item.type] as ItemSpec).use;
|
|
224
|
+
if (use === "lounge") spots.lounge.push(point);
|
|
225
|
+
if (use === "board") spots.board.push(point);
|
|
245
226
|
}
|
|
246
|
-
|
|
227
|
+
const links = list<{ from: string; to: string }>(scene?.links, MAX_LINKS)
|
|
228
|
+
.map((link) => ({ from: str(link?.from, 60), to: str(link?.to, 60) }))
|
|
229
|
+
.filter((link) => link.from && link.to);
|
|
230
|
+
return { cols, rows, rooms, doors, furniture, walkable, seats, spots, links };
|
|
247
231
|
}
|
|
248
232
|
|
|
249
233
|
/** Build a world from a scene. Bad values are dropped or clamped, so it never throws. */
|
|
250
234
|
export function createWorld(scene: OfficeScene = {}): World {
|
|
235
|
+
const pack = worldData(scene?.world, OFFICE);
|
|
251
236
|
const world: World = {
|
|
252
|
-
|
|
237
|
+
pack,
|
|
238
|
+
showLinks: true,
|
|
239
|
+
...buildLayout(scene, pack),
|
|
253
240
|
agents: list<OfficeAgent>(scene?.agents, MAX_AGENTS).map(toAgent),
|
|
254
241
|
tasks: list<OfficeTask>(scene?.tasks, MAX_TASKS).map(toTask),
|
|
255
242
|
};
|
|
@@ -259,7 +246,7 @@ export function createWorld(scene: OfficeScene = {}): World {
|
|
|
259
246
|
|
|
260
247
|
/** Change the building while everyone is in it. Anyone left inside a wall or a desk moves. */
|
|
261
248
|
export function applyLayout(world: World, layout: OfficeLayout): void {
|
|
262
|
-
Object.assign(world, buildLayout(layout));
|
|
249
|
+
Object.assign(world, buildLayout(layout, world.pack), { routes: undefined });
|
|
263
250
|
for (const agent of world.agents) {
|
|
264
251
|
agent.seat = undefined;
|
|
265
252
|
agent.path = [];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** The office as data: what furniture exists, how big it is, what it's for, and what things are called. */
|
|
2
|
+
|
|
3
|
+
import type { WorldData } from "../pack.js";
|
|
4
|
+
import type { OfficeDoor, OfficeFurniture, OfficeRoom } from "../types.js";
|
|
5
|
+
|
|
6
|
+
/** Footprint in tiles, and where someone stands to use it, relative to its corner. */
|
|
7
|
+
export const OFFICE_ITEMS: WorldData["items"] = {
|
|
8
|
+
desk: { w: 3, h: 2, spot: { x: 1, y: 2 }, use: "seat" },
|
|
9
|
+
coffee: { w: 2, h: 1, spot: { x: 0, y: 1 }, use: "lounge" },
|
|
10
|
+
water: { w: 1, h: 1, spot: { x: 0, y: 1 }, use: "lounge" },
|
|
11
|
+
sofa: { w: 3, h: 1, spot: { x: 1, y: 1 }, use: "lounge" },
|
|
12
|
+
plant: { w: 1, h: 1 },
|
|
13
|
+
fern: { w: 1, h: 1 },
|
|
14
|
+
board: { w: 4, h: 1, spot: { x: 1, y: 1 }, use: "board" },
|
|
15
|
+
whiteboard: { w: 3, h: 1, spot: { x: 1, y: 1 }, use: "board" },
|
|
16
|
+
shelf: { w: 2, h: 1, spot: { x: 0, y: 1 }, use: "lounge" },
|
|
17
|
+
server: { w: 1, h: 1 },
|
|
18
|
+
table: { w: 3, h: 2, spot: { x: 1, y: 2 }, use: "board" },
|
|
19
|
+
rug: { w: 4, h: 3, solid: false },
|
|
20
|
+
pile: { w: 1, h: 1, solid: false },
|
|
21
|
+
arcade: { w: 1, h: 1, spot: { x: 0, y: 1 }, use: "lounge" },
|
|
22
|
+
fridge: { w: 1, h: 1, spot: { x: 0, y: 1 }, use: "lounge" },
|
|
23
|
+
printer: { w: 1, h: 1, spot: { x: 0, y: 1 } },
|
|
24
|
+
lamp: { w: 1, h: 1 },
|
|
25
|
+
beanbag: { w: 1, h: 1, spot: { x: 0, y: 1 }, use: "lounge" },
|
|
26
|
+
tv: { w: 3, h: 1, spot: { x: 1, y: 2 }, use: "lounge" },
|
|
27
|
+
vending: { w: 1, h: 1, spot: { x: 0, y: 1 }, use: "lounge" },
|
|
28
|
+
trophy: { w: 1, h: 1 },
|
|
29
|
+
clock: { w: 1, h: 1, solid: false, wall: true },
|
|
30
|
+
poster: { w: 1, h: 1, solid: false, wall: true },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const OFFICE_LAYOUT = {
|
|
34
|
+
cols: 26,
|
|
35
|
+
rows: 17,
|
|
36
|
+
rooms: [
|
|
37
|
+
{ id: "studio", name: "Studio", x: 1, y: 3, w: 15, h: 13, floor: "wood" },
|
|
38
|
+
{ id: "lounge", name: "Lounge", x: 17, y: 3, w: 8, h: 6, floor: "carpet" },
|
|
39
|
+
{ id: "quests", name: "Quests", x: 17, y: 10, w: 8, h: 6, floor: "tile" },
|
|
40
|
+
] as OfficeRoom[],
|
|
41
|
+
doors: [{ x: 16, y: 6 }, { x: 16, y: 13 }, { x: 23, y: 9 }] as OfficeDoor[],
|
|
42
|
+
furniture: [
|
|
43
|
+
{ type: "desk", x: 2, y: 4 }, { type: "desk", x: 7, y: 4 }, { type: "desk", x: 12, y: 4 },
|
|
44
|
+
{ type: "desk", x: 2, y: 10 }, { type: "desk", x: 7, y: 10 }, { type: "desk", x: 12, y: 10 },
|
|
45
|
+
{ type: "plant", x: 1, y: 8 }, { type: "plant", x: 15, y: 8 }, { type: "server", x: 15, y: 15 }, { type: "server", x: 14, y: 15 },
|
|
46
|
+
{ type: "shelf", x: 1, y: 15 },
|
|
47
|
+
{ type: "coffee", x: 19, y: 3 }, { type: "water", x: 21, y: 3 }, { type: "sofa", x: 22, y: 3 }, { type: "rug", x: 20, y: 5 }, { type: "plant", x: 24, y: 8 },
|
|
48
|
+
{ type: "board", x: 19, y: 10 }, { type: "table", x: 19, y: 13 }, { type: "plant", x: 24, y: 15 },
|
|
49
|
+
] as OfficeFurniture[],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const OFFICE: WorldData = {
|
|
53
|
+
id: "office",
|
|
54
|
+
name: "Pixel Office",
|
|
55
|
+
words: { quests: "QUESTS" },
|
|
56
|
+
items: OFFICE_ITEMS,
|
|
57
|
+
layout: OFFICE_LAYOUT,
|
|
58
|
+
};
|
|
@@ -0,0 +1,284 @@
|
|
|
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 "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;
|
|
178
|
+
case "rug":
|
|
179
|
+
rect(c, palette.primary, x, y, 64, 48);
|
|
180
|
+
c.fillStyle = "rgba(0,0,0,0.42)";
|
|
181
|
+
c.fillRect(x + 3, y + 3, 58, 42);
|
|
182
|
+
rect(c, palette.primary, x + 6, y + 6, 52, 1);
|
|
183
|
+
rect(c, palette.primary, x + 6, y + 41, 52, 1);
|
|
184
|
+
rect(c, palette.primary, x + 6, y + 6, 1, 36);
|
|
185
|
+
rect(c, palette.primary, x + 57, y + 6, 1, 36);
|
|
186
|
+
for (let i = 0; i < 6; i++) rect(c, palette.primary, x + 12 + i * 8, y + 22, 4, 4);
|
|
187
|
+
break;
|
|
188
|
+
case "arcade":
|
|
189
|
+
rect(c, OUTLINE, x, y - 14, 16, 30);
|
|
190
|
+
rect(c, "#4c1d95", x + 1, y - 13, 14, 28);
|
|
191
|
+
rect(c, "#111827", x + 3, y - 10, 10, 9);
|
|
192
|
+
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);
|
|
193
|
+
rect(c, "#f59e0b", x + 3, y - 13, 10, 2);
|
|
194
|
+
rect(c, "#27272a", x + 2, y + 1, 12, 4);
|
|
195
|
+
rect(c, "#ef4444", x + 4, y + 2, 2, 2);
|
|
196
|
+
rect(c, "#3b82f6", x + 9, y + 2, 2, 2);
|
|
197
|
+
break;
|
|
198
|
+
case "fridge":
|
|
199
|
+
rect(c, OUTLINE, x + 1, y - 12, 14, 28);
|
|
200
|
+
rect(c, "#e5e7eb", x + 2, y - 11, 12, 26);
|
|
201
|
+
rect(c, "#9ca3af", x + 2, y - 3, 12, 1);
|
|
202
|
+
rect(c, "#6b7280", x + 11, y - 8, 1, 3);
|
|
203
|
+
rect(c, "#6b7280", x + 11, y, 1, 5);
|
|
204
|
+
rect(c, "#fde68a", x + 4, y - 9, 3, 3);
|
|
205
|
+
rect(c, "#fca5a5", x + 5, y + 3, 3, 3);
|
|
206
|
+
break;
|
|
207
|
+
case "printer":
|
|
208
|
+
rect(c, "#4b5563", x + 2, y + 8, 12, 8);
|
|
209
|
+
rect(c, OUTLINE, x + 1, y - 2, 14, 11);
|
|
210
|
+
rect(c, "#d1d5db", x + 2, y - 1, 12, 9);
|
|
211
|
+
rect(c, "#f9fafb", x + 4, y - 5 - (Math.floor(time) % 4 === 0 ? 1 : 0), 8, 5);
|
|
212
|
+
rect(c, "#22c55e", x + 11, y + 1, 2, 1);
|
|
213
|
+
rect(c, "#9ca3af", x + 3, y + 5, 10, 1);
|
|
214
|
+
break;
|
|
215
|
+
case "lamp":
|
|
216
|
+
rect(c, "#374151", x + 5, y + 13, 6, 3);
|
|
217
|
+
rect(c, "#4b5563", x + 7, y - 4, 2, 17);
|
|
218
|
+
rect(c, OUTLINE, x + 2, y - 10, 12, 8);
|
|
219
|
+
rect(c, "#fde68a", x + 3, y - 9, 10, 6);
|
|
220
|
+
rect(c, "#fef3c7", x + 4, y - 8, 4, 2);
|
|
221
|
+
break;
|
|
222
|
+
case "beanbag":
|
|
223
|
+
rect(c, OUTLINE, x, y + 3, 16, 13);
|
|
224
|
+
rect(c, shade(palette.primary, -50), x + 1, y + 4, 14, 11);
|
|
225
|
+
rect(c, shade(palette.primary, -20), x + 3, y + 5, 8, 4);
|
|
226
|
+
rect(c, "#00000040", x + 1, y + 13, 14, 2);
|
|
227
|
+
break;
|
|
228
|
+
case "tv":
|
|
229
|
+
rect(c, "#3b2413", x + 2, y + 8, 44, 8);
|
|
230
|
+
rect(c, "#5b3a21", x + 2, y + 8, 44, 2);
|
|
231
|
+
rect(c, OUTLINE, x + 4, y - 14, 40, 22);
|
|
232
|
+
rect(c, "#0b1120", x + 5, y - 13, 38, 20);
|
|
233
|
+
for (let i = 0; i < 9; i++) {
|
|
234
|
+
const h = 2 + ((i * 5 + Math.floor(time * 3)) % 12);
|
|
235
|
+
rect(c, i % 3 === 0 ? palette.primary : i % 3 === 1 ? "#38bdf8" : "#f472b6", x + 8 + i * 4, y + 5 - h, 3, h);
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case "vending":
|
|
239
|
+
rect(c, OUTLINE, x, y - 14, 16, 30);
|
|
240
|
+
rect(c, "#b91c1c", x + 1, y - 13, 14, 28);
|
|
241
|
+
rect(c, "#0f172a", x + 3, y - 11, 8, 16);
|
|
242
|
+
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);
|
|
243
|
+
rect(c, "#d1d5db", x + 12, y - 9, 2, 6);
|
|
244
|
+
rect(c, "#111827", x + 3, y + 8, 10, 4);
|
|
245
|
+
break;
|
|
246
|
+
case "trophy":
|
|
247
|
+
rect(c, "#6b4427", x + 2, y + 8, 12, 8);
|
|
248
|
+
rect(c, "#8a5a33", x + 2, y + 8, 12, 2);
|
|
249
|
+
rect(c, "#78350f", x + 5, y + 5, 6, 3);
|
|
250
|
+
rect(c, "#f59e0b", x + 7, y + 1, 2, 4);
|
|
251
|
+
rect(c, OUTLINE, x + 3, y - 8, 10, 10);
|
|
252
|
+
rect(c, "#fbbf24", x + 4, y - 7, 8, 8);
|
|
253
|
+
rect(c, "#fde68a", x + 5, y - 6, 2, 5);
|
|
254
|
+
rect(c, "#fbbf24", x + 1, y - 6, 3, 1); rect(c, "#fbbf24", x + 12, y - 6, 3, 1);
|
|
255
|
+
break;
|
|
256
|
+
case "clock": {
|
|
257
|
+
rect(c, OUTLINE, x + 3, y + 1, 10, 10);
|
|
258
|
+
rect(c, "#f9fafb", x + 4, y + 2, 8, 8);
|
|
259
|
+
rect(c, "#1f2937", x + 8, y + 3, 1, 3);
|
|
260
|
+
rect(c, "#1f2937", x + 8, y + 6, 3, 1);
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
case "poster": {
|
|
264
|
+
const colors = ["#7c3aed", "#0ea5e9", "#db2777", "#059669"];
|
|
265
|
+
const color = colors[(item.x + item.y) % colors.length] as string;
|
|
266
|
+
rect(c, OUTLINE, x + 2, y, 12, 11);
|
|
267
|
+
rect(c, color, x + 3, y + 1, 10, 9);
|
|
268
|
+
rect(c, shade(color, 60), x + 5, y + 3, 6, 3);
|
|
269
|
+
rect(c, "#f9fafb", x + 5, y + 7, 6, 1);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export const OFFICE_PAINTER = {
|
|
276
|
+
furniture: drawFurniture,
|
|
277
|
+
glow(item: Item, world: World, glow: (x: number, y: number, radius: number, color: string) => void): void {
|
|
278
|
+
if (item.type === "lamp") glow(item.x * TILE + 8, item.y * TILE, 36, "rgba(253,224,139,0.30)");
|
|
279
|
+
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)");
|
|
280
|
+
if (item.type === "desk" && world.agents.some((a) => a.seat?.x === item.x + 1 && a.seat?.y === item.y + 2 && isSeated(a))) {
|
|
281
|
+
glow(item.x * TILE + 24, item.y * TILE + 8, 30, "rgba(125,211,252,0.26)");
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
};
|