| |
| |
| |
| |
| |
| |
| export const W = 640, H = 480, WALL = 10; |
|
|
| export const ROOMS = { |
| boardroom: { x: WALL, y: WALL, w: 220, h: 160 }, |
| breakroom: { x: 450, y: WALL, w: 180, h: 140 }, |
| }; |
|
|
| export const SPOTS = { |
| playerDesk: { x: 150, y: 255 }, |
| inbox: { x: 205, y: 252 }, |
| coffee: { x: 590, y: 64 }, |
| plant: { x: 612, y: 446 }, |
| printer: { x: 420, y: 436 }, |
| clock: { x: 330, y: 40 }, |
| window: { x: 4, y: 250 }, |
| phone: { x: 185, y: 250 }, |
| boardroomDoor: { x: 236, y: 120 }, |
| newspaperLanding: { x: 330, y: 290 }, |
| reception: { x: 320, y: 446 }, |
| playerStart: { x: 320, y: 300 }, |
| }; |
|
|
| export const NPC_SPOTS = { |
| brad: { x: 310, y: 205 }, |
| stacey: { x: 470, y: 235 }, |
| kevin: { x: 560, y: 335 }, |
| janet: { x: 360, y: 385 }, |
| derek: { x: 175, y: 385 }, |
| }; |
|
|
| function deskCollider(s) { return { x: s.x - 26, y: s.y + 6, w: 52, h: 34 }; } |
|
|
| export const COLLIDERS = [ |
| |
| { x: 0, y: 0, w: W, h: 26 }, { x: 0, y: H - WALL, w: W, h: WALL }, |
| { x: 0, y: 0, w: WALL, h: H }, { x: W - WALL, y: 0, w: WALL, h: H }, |
| |
| { x: ROOMS.boardroom.x, y: ROOMS.boardroom.y, |
| w: ROOMS.boardroom.w, h: ROOMS.boardroom.h }, |
| |
| { x: 450, y: WALL, w: 6, h: 140 }, |
| { x: 450, y: 144, w: 50, h: 6 }, |
| { x: 560, y: 144, w: 70, h: 6 }, |
| |
| { x: SPOTS.coffee.x - 12, y: SPOTS.coffee.y - 10, w: 26, h: 24 }, |
| { x: SPOTS.plant.x - 8, y: SPOTS.plant.y - 6, w: 18, h: 20 }, |
| { x: SPOTS.printer.x - 14, y: SPOTS.printer.y - 8, w: 30, h: 22 }, |
| { x: SPOTS.reception.x - 30, y: SPOTS.reception.y - 6, w: 60, h: 18 }, |
| |
| deskCollider(SPOTS.playerDesk), |
| ...Object.values(NPC_SPOTS).map(deskCollider), |
| ]; |
|
|
| export function collides(px, py) { |
| |
| const box = { x: px - 8, y: py - 4, w: 16, h: 10 }; |
| return COLLIDERS.some((c) => |
| box.x < c.x + c.w && box.x + box.w > c.x && |
| box.y < c.y + c.h && box.y + box.h > c.y); |
| } |
|
|
| |
| |
| const P = { |
| ink: "#2e2a22", |
| tileA: "#8d8b82", tileB: "#878579", tileSeam: "#76746b", |
| wallCap: "#ece8dc", wallFace: "#d8d4c8", wallShade: "#bcb7a8", |
| baseboard: "#b4af9f", |
| glass: "rgba(140,200,235,.32)", glassFrame: "#8e94a6", |
| glassLine: "rgba(255,255,255,.65)", |
| sky: "#aedcf2", skyHi: "#cdeaf8", bldg: "#9fb2c4", bldgLit: "#f4f9fd", |
| bldgLit2: "#ffe9a8", |
| woodFloorA: "#b58a55", woodFloorB: "#a87c45", woodSeam: "#8a6230", |
| meetFloorA: "#878579", meetFloorB: "#807e72", |
| woodTop: "#c3a169", woodTopHi: "#dcbb82", woodFace: "#997b46", |
| deskTop: "#e9e4d3", deskTopHi: "#f4f0e2", deskFace: "#c6c2b6", |
| deskFaceLo: "#b3ab95", |
| partTop: "#ece8dc", partFace: "#bdb8a6", partCloth: "#a7a08c", |
| monitor: "#1c3a5a", monitorFrame: "#2b2e3a", monitorSide: "#23242c", |
| screenGlow: "#7fb2ec", |
| chairTop: "#3b3e46", chairHi: "#4a4e58", chairFace: "#2f323a", |
| paper: "#faf7ee", paperShadow: "#c7c0aa", |
| metalTop: "#cfccc4", metalHi: "#e6e2d6", metalFace: "#a8a392", |
| shadow: "rgba(46,42,34,.30)", shadowSoft: "rgba(46,42,34,.16)", |
| plantPot: "#b35c3a", plantPotHi: "#cf7450", leaf: "#3f9e52", |
| leafHi: "#5cc46e", leafLo: "#2c7a3d", |
| mug: "#e25b4a", book: "#8e6fd8", folder: "#3aa3e0", binder2: "#e6b33c", |
| binder3: "#d9534f", |
| lamp: "#5b5e6b", lampGlow: "#ffd98c", |
| case: "#2e2f37", caseHi: "#46474f", |
| label: "#6e6e7e", |
| }; |
|
|
| let staticScene = null; |
| let rainDrops = []; |
|
|
| |
| if (typeof document !== "undefined" && document.fonts) |
| document.fonts.ready.then(() => { staticScene = null; }); |
|
|
| |
| function r(c, x, y, w, h, color) { c.fillStyle = color; c.fillRect(x, y, w, h); } |
|
|
| function shade(c, x, y, w, soft) { |
| |
| r(c, x + 1, y, w - 2, 4, soft ? P.shadowSoft : P.shadow); |
| r(c, x + 3, y + 4, w - 6, 2, P.shadowSoft); |
| } |
|
|
| |
| function box3d(c, x, y, w, topH, faceH, top, face, hi, outline = P.ink) { |
| r(c, x - 1, y - 1, w + 2, topH + faceH + 2, outline); |
| r(c, x, y, w, topH, top); |
| if (hi) r(c, x, y, w, 2, hi); |
| r(c, x, y + topH, w, faceH, face); |
| r(c, x, y + topH, w, 1, "rgba(0,0,0,.14)"); |
| } |
|
|
| function plant(c, x, y, big) { |
| const s = big ? 1.4 : 1; |
| shade(c, x - 8 * s, y + 9 * s, 17 * s, true); |
| box3d(c, x - 6 * s, y, 12 * s, 3 * s, 8 * s, P.plantPotHi, P.plantPot); |
| r(c, x - 8 * s, y - 9 * s, 6 * s, 10 * s, P.leaf); |
| r(c, x + 1 * s, y - 12 * s, 6 * s, 13 * s, P.leafHi); |
| r(c, x - 3 * s, y - 7 * s, 5 * s, 8 * s, P.leafLo); |
| r(c, x - 1 * s, y - 15 * s, 3 * s, 7 * s, P.leaf); |
| r(c, x - 2 * s, y - 16 * s, 1, 1, "#7fe08f"); |
| } |
|
|
| function chair(c, x, y) { |
| shade(c, x - 7, y + 7, 15, true); |
| box3d(c, x - 5, y - 9, 11, 6, 3, P.chairTop, P.chairFace, P.chairHi); |
| box3d(c, x - 6, y - 1, 13, 5, 4, P.chairTop, P.chairFace, P.chairHi); |
| } |
|
|
| function monitor(c, x, y, lit = true) { |
| r(c, x - 9, y - 7, 19, 15, P.ink); |
| r(c, x - 8, y - 6, 17, 13, P.monitorFrame); |
| r(c, x + 7, y - 6, 2, 13, P.monitorSide); |
| r(c, x - 6, y - 4, 12, 8, P.monitor); |
| if (lit) { |
| r(c, x - 5, y - 3, 8, 1, P.screenGlow); |
| r(c, x - 5, y - 1, 5, 1, "#4d7fc0"); |
| r(c, x - 5, y + 1, 7, 1, "#4d7fc0"); |
| } |
| r(c, x - 2, y + 7, 5, 2, P.ink); |
| } |
|
|
| function papers(c, x, y) { |
| r(c, x, y, 8, 6, P.paperShadow); |
| r(c, x - 1, y - 1, 8, 6, P.paper); |
| r(c, x - 1, y - 1, 8, 1, "#f0f0f4"); |
| r(c, x + 1, y + 1, 5, 1, P.paperShadow); |
| } |
|
|
| function keyboard(c, x, y) { |
| r(c, x - 1, y - 1, 14, 7, P.ink); |
| r(c, x, y, 12, 5, "#a4a7b4"); |
| r(c, x + 1, y + 1, 10, 1, "#c9ccd6"); |
| } |
|
|
| function deskLamp(c, x, y) { |
| r(c, x, y + 4, 6, 2, P.lamp); |
| r(c, x + 2, y - 2, 2, 6, P.lamp); |
| r(c, x, y - 5, 7, 4, P.ink); |
| r(c, x + 1, y - 4, 5, 2, P.lamp); |
| r(c, x + 1, y - 1, 5, 1, P.lampGlow); |
| } |
|
|
| function landline(c, x, y) { |
| r(c, x - 1, y - 1, 12, 9, P.ink); |
| r(c, x, y, 10, 7, "#b9bcc8"); |
| r(c, x, y, 10, 2, "#d4d6de"); |
| r(c, x + 1, y - 3, 8, 3, "#8e919e"); |
| r(c, x + 6, y + 3, 3, 3, "#7d8290"); |
| } |
|
|
| function briefcase(c, x, y) { |
| shade(c, x - 1, y + 9, 14, true); |
| r(c, x - 1, y - 1, 14, 11, P.ink); |
| r(c, x, y, 12, 9, P.case); |
| r(c, x, y, 12, 2, P.caseHi); |
| r(c, x + 4, y - 3, 4, 3, P.ink); |
| r(c, x + 5, y - 2, 2, 1, P.case); |
| r(c, x + 5, y + 4, 2, 2, "#8c8f9b"); |
| } |
|
|
| function shelfUnit(c, x, y) { |
| box3d(c, x, y, 40, 16, 7, "#e8e8ed", "#c2c4ce", "#f6f6f9"); |
| r(c, x + 1, y + 7, 38, 1, "#b6b6c1"); |
| [[3, P.folder], [9, P.binder2], [15, P.binder3], [21, P.book], |
| [27, P.folder]].forEach(([off, col]) => { |
| r(c, x + off, y + 2, 5, 8, col); |
| r(c, x + off, y + 2, 5, 1, "rgba(255,255,255,.4)"); |
| }); |
| papers(c, x + 33, y + 3); |
| } |
|
|
| |
| function cubicle(c, s, accent, opts = {}) { |
| const dx = s.x - 26, dy = s.y + 6; |
| const py = s.y - 34; |
| |
| r(c, dx - 5, py - 1, 62, 30, P.ink); |
| r(c, dx - 4, py, 60, 4, P.partTop); |
| r(c, dx - 4, py + 4, 60, 18, P.partFace); |
| r(c, dx - 2, py + 6, 56, 14, P.partCloth); |
| r(c, dx - 2, py + 6, 56, 1, "rgba(255,255,255,.35)"); |
| r(c, dx - 4, py + 22, 60, 6, "#9da4b6"); |
| |
| r(c, dx + 4, py + 8, 6, 5, P.paper); |
| r(c, dx + 44, py + 8, 7, 5, accent); |
| r(c, dx + 44, py + 8, 7, 1, "rgba(255,255,255,.45)"); |
| |
| r(c, dx - 9, py + 3, 7, 58, P.ink); |
| r(c, dx - 8, py + 4, 5, 56, P.partFace); |
| r(c, dx - 8, py + 4, 5, 2, P.partTop); |
| r(c, dx + 54, py + 3, 7, 58, P.ink); |
| r(c, dx + 55, py + 4, 5, 56, P.partFace); |
| r(c, dx + 55, py + 4, 5, 2, P.partTop); |
| |
| shade(c, dx - 2, dy + 31, 58); |
| box3d(c, dx, dy, 52, 20, 10, P.deskTop, P.deskFace, P.deskTopHi); |
| r(c, dx, dy + 26, 52, 2, P.deskFaceLo); |
| |
| monitor(c, s.x, dy + 5, !opts.darkMonitor); |
| keyboard(c, s.x - 6, dy + 15); |
| papers(c, dx + 5, dy + 13); |
| deskLamp(c, dx + 41, dy + 6); |
| r(c, dx + 44, dy + 16, 5, 5, P.mug); |
| r(c, dx + 44, dy + 16, 5, 1, "#f2917f"); |
| if (opts.phone) landline(c, dx + 3, dy + 3); |
| if (opts.books) { r(c, dx + 3, dy + 14, 4, 7, P.book); |
| r(c, dx + 8, dy + 15, 4, 6, P.folder); } |
| } |
|
|
| |
| function buildStatic() { |
| const cv = document.createElement("canvas"); |
| cv.width = W; cv.height = H; |
| const c = cv.getContext("2d"); |
|
|
| |
| for (let ty = 0; ty < H / 32; ty++) |
| for (let tx = 0; tx < W / 32; tx++) |
| r(c, tx * 32, ty * 32, 32, 32, (tx + ty) % 2 ? P.tileA : P.tileB); |
| c.globalAlpha = 0.85; |
| for (let x = 0; x <= W; x += 32) r(c, x, 0, 1, H, P.tileSeam); |
| for (let y = 0; y <= H; y += 32) r(c, 0, y, W, 1, P.tileSeam); |
| c.globalAlpha = 1; |
| |
| c.globalAlpha = 0.10; |
| r(c, 244, 30, 92, 66, "#fff3c4"); |
| r(c, 340, 30, 92, 66, "#fff3c4"); |
| c.globalAlpha = 1; |
|
|
| |
| const br = ROOMS.boardroom; |
| for (let ty = 0; ty < br.h / 16; ty++) |
| for (let tx = 0; tx < br.w / 16; tx++) |
| r(c, br.x + tx * 16, br.y + ty * 16, 16, 16, |
| (tx + ty) % 2 ? P.meetFloorA : P.meetFloorB); |
| |
| r(c, br.x, br.y, br.w, 4, P.wallCap); |
| r(c, br.x, br.y + 4, br.w, 16, P.wallFace); |
| r(c, br.x, br.y + 18, br.w, 2, P.wallShade); |
| r(c, br.x, br.y + 20, br.w, 1, P.ink); |
| |
| r(c, br.x + 38, br.y + 52, 146, 74, "#b6543e"); |
| r(c, br.x + 42, br.y + 56, 138, 66, "#c4664e"); |
| |
| r(c, br.x + 63, br.y + 5, 92, 27, P.ink); |
| r(c, br.x + 64, br.y + 6, 90, 25, "#5d5d68"); |
| r(c, br.x + 67, br.y + 9, 84, 19, "#fdfdfe"); |
| r(c, br.x + 67, br.y + 9, 84, 4, "#e8e8ee"); |
| |
| shade(c, br.x + 46, br.y + 119, 130); |
| box3d(c, br.x + 46, br.y + 56, 128, 50, 12, P.woodTop, P.woodFace, |
| P.woodTopHi); |
| papers(c, br.x + 70, br.y + 74); papers(c, br.x + 130, br.y + 86); |
| r(c, br.x + 101, br.y + 78, 16, 11, P.ink); |
| r(c, br.x + 102, br.y + 79, 14, 9, P.metalTop); |
| r(c, br.x + 107, br.y + 82, 4, 3, "#6e7180"); |
| [[64, 44], [104, 44], [144, 44], [64, 128], [104, 128], [144, 128]] |
| .forEach(([x, y]) => chair(c, br.x + x, br.y + y)); |
| plant(c, br.x + 16, br.y + 140); |
| briefcase(c, br.x + 196, br.y + 132); |
| |
| r(c, br.x, br.y, 6, br.h, P.wallCap); |
| r(c, br.x + 5, br.y, 1, br.h, P.ink); |
| r(c, br.x, br.y + br.h - 6, br.w, 6, P.wallCap); |
| r(c, br.x, br.y + br.h, br.w - 6, 10, P.wallFace); |
| r(c, br.x, br.y + br.h + 8, br.w - 6, 2, P.wallShade); |
| r(c, br.x, br.y + br.h + 10, br.w - 6, 1, P.ink); |
| |
| r(c, br.x + br.w - 6, br.y, 6, 14, P.wallCap); |
| r(c, br.x + br.w - 6, br.y, 6, 1, P.ink); |
| r(c, br.x + br.w - 7, br.y + 14, 8, 70, P.glassFrame); |
| r(c, br.x + br.w - 5, br.y + 16, 4, 66, P.glass); |
| for (let i = 0; i < 4; i++) |
| r(c, br.x + br.w - 5, br.y + 18 + i * 17, 4, 1, P.glassLine); |
| r(c, br.x + br.w - 6, br.y + 84, 6, 6, P.wallCap); |
| r(c, br.x + br.w - 6, br.y + 140, 6, 30, P.wallCap); |
| r(c, br.x + br.w - 1, br.y, 1, br.h, P.ink); |
| |
| r(c, br.x + br.w - 7, br.y + 88, 8, 3, P.ink); |
| r(c, br.x + br.w - 7, br.y + 139, 8, 3, P.ink); |
| |
| r(c, br.x + 55, br.y + 5, 82, 13, P.ink); |
| r(c, br.x + 56, br.y + 6, 80, 11, "#fdfdfe"); |
| c.font = "7px 'Press Start 2P'"; |
| c.fillStyle = "#34343f"; |
| c.fillText("BOARDROOM", br.x + 61, br.y + 15); |
|
|
| |
| const bk = ROOMS.breakroom; |
| for (let py = 0; py < bk.h; py += 10) { |
| r(c, bk.x, bk.y + py, bk.w, 10, |
| (py / 10) % 2 ? P.woodFloorA : P.woodFloorB); |
| r(c, bk.x, bk.y + py, bk.w, 1, P.woodSeam); |
| } |
| |
| r(c, bk.x, bk.y, bk.w, 4, P.wallCap); |
| r(c, bk.x, bk.y + 4, bk.w, 14, P.wallFace); |
| r(c, bk.x, bk.y + 16, bk.w, 2, P.wallShade); |
| r(c, bk.x, bk.y + 18, bk.w, 1, P.ink); |
| |
| shade(c, bk.x + 6, bk.y + 39, 124); |
| box3d(c, bk.x + 6, bk.y + 10, 124, 18, 10, P.deskTop, P.deskFace, |
| P.deskTopHi); |
| r(c, bk.x + 16, bk.y + 14, 16, 10, P.ink); |
| r(c, bk.x + 17, bk.y + 15, 14, 8, "#a9adba"); |
| r(c, bk.x + 19, bk.y + 17, 10, 4, "#8a8e9c"); |
| r(c, bk.x + 44, bk.y + 14, 10, 8, P.mug); |
| r(c, bk.x + 44, bk.y + 14, 10, 2, "#f2917f"); |
| r(c, bk.x + 58, bk.y + 16, 8, 6, P.folder); |
| papers(c, bk.x + 76, bk.y + 15); |
| |
| shade(c, bk.x + 140, bk.y + 53, 28); |
| r(c, bk.x + 139, bk.y + 7, 30, 48, P.ink); |
| r(c, bk.x + 140, bk.y + 8, 28, 46, "#eceef2"); |
| r(c, bk.x + 140, bk.y + 8, 28, 3, "#fafbfd"); |
| r(c, bk.x + 140, bk.y + 26, 28, 2, "#c3c6cf"); |
| r(c, bk.x + 162, bk.y + 14, 3, 8, "#9da0ab"); |
| r(c, bk.x + 140, bk.y + 48, 28, 6, "#d4d6de"); |
| |
| shade(c, bk.x + 112, bk.y + 99, 22); |
| r(c, bk.x + 111, bk.y + 63, 24, 38, P.ink); |
| r(c, bk.x + 112, bk.y + 64, 22, 36, "#d9534f"); |
| r(c, bk.x + 112, bk.y + 64, 22, 3, "#e8736f"); |
| r(c, bk.x + 115, bk.y + 70, 12, 20, "#23232e"); |
| [[0, P.binder2], [6, P.folder], [12, "#5cc46e"]].forEach(([off, col]) => { |
| r(c, bk.x + 116, bk.y + 72 + off, 10, 3, col); |
| }); |
| r(c, bk.x + 129, bk.y + 72, 3, 10, "#f2f2f5"); |
| r(c, bk.x + 112, bk.y + 94, 22, 6, "#b23f3c"); |
| |
| const cf = SPOTS.coffee; |
| shade(c, cf.x - 11, cf.y + 11, 24); |
| r(c, cf.x - 11, cf.y - 11, 24, 24, P.ink); |
| r(c, cf.x - 10, cf.y - 10, 22, 22, "#3a3a45"); |
| r(c, cf.x - 10, cf.y - 10, 22, 3, "#52525e"); |
| r(c, cf.x - 6, cf.y - 5, 14, 7, "#1d1d26"); |
| r(c, cf.x - 4, cf.y + 4, 6, 5, P.mug); |
| r(c, cf.x + 5, cf.y - 3, 3, 2, "#7ddb6f"); |
| |
| shade(c, bk.x + 32, bk.y + 103, 48); |
| box3d(c, bk.x + 32, bk.y + 76, 48, 20, 8, P.woodTop, P.woodFace, |
| P.woodTopHi); |
| chair(c, bk.x + 20, bk.y + 84); chair(c, bk.x + 92, bk.y + 84); |
| r(c, bk.x + 48, bk.y + 82, 6, 6, P.mug); |
| |
| shade(c, bk.x + 56, bk.y + 132, 16, true); |
| r(c, bk.x + 56, bk.y + 111, 14, 24, P.ink); |
| r(c, bk.x + 57, bk.y + 116, 12, 15, "#dfe7f2"); |
| r(c, bk.x + 59, bk.y + 112, 8, 6, "#bcd8f0"); |
| r(c, bk.x + 57, bk.y + 128, 12, 3, P.metalFace); |
| |
| r(c, bk.x, bk.y, 6, bk.h, P.wallCap); |
| r(c, bk.x + 5, bk.y, 1, bk.h, P.ink); |
| r(c, bk.x, bk.y + bk.h - 6, 50, 6, P.wallCap); |
| r(c, bk.x, bk.y + bk.h, 50, 10, P.wallFace); |
| r(c, bk.x, bk.y + bk.h + 9, 50, 1, P.ink); |
| r(c, bk.x + 110, bk.y + bk.h - 6, 70, 6, P.wallCap); |
| r(c, bk.x + 110, bk.y + bk.h, 70, 10, P.wallFace); |
| r(c, bk.x + 110, bk.y + bk.h + 9, 70, 1, P.ink); |
| |
| r(c, bk.x + 29, bk.y + 3, 74, 13, P.ink); |
| r(c, bk.x + 30, bk.y + 4, 72, 11, "#fdfdfe"); |
| c.fillStyle = "#34343f"; |
| c.fillText("BREAK RM", bk.x + 35, bk.y + 13); |
|
|
| |
| r(c, 0, 0, W, 4, P.wallCap); |
| r(c, 0, 4, W, 18, P.wallFace); |
| r(c, 0, 4, W, 2, "#ffffff"); |
| r(c, 0, 20, W, 3, P.wallShade); |
| r(c, 0, 23, W, 1, P.ink); |
| r(c, 0, 24, W, 3, P.baseboard); |
| |
| [[250, 76], [346, 76]].forEach(([wx, ww]) => { |
| r(c, wx - 4, 2, ww + 8, 24, P.ink); |
| r(c, wx - 3, 3, ww + 6, 22, P.glassFrame); |
| r(c, wx, 5, ww, 18, P.sky); |
| r(c, wx, 5, ww, 6, P.skyHi); |
| r(c, wx + 8, 8, 14, 3, "#ffffff"); |
| r(c, wx + 40, 11, 18, 3, "#ffffff"); |
| for (let i = 0; i < ww / 12; i++) { |
| const bh = 6 + ((i * 37) % 7); |
| r(c, wx + i * 12 + 1, 23 - bh, 9, bh, P.bldg); |
| if (i % 2 === 0) r(c, wx + i * 12 + 3, 25 - bh + 2, 2, 2, P.bldgLit); |
| if (i % 3 === 0) r(c, wx + i * 12 + 6, 25 - bh + 4, 2, 2, P.bldgLit2); |
| } |
| r(c, wx + ww / 2 - 1, 5, 2, 18, P.glassFrame); |
| }); |
| |
| r(c, 25, 5, 36, 16, P.ink); |
| r(c, 26, 6, 34, 14, "#e6e6eb"); |
| r(c, 26, 6, 34, 3, "#f6f6f9"); |
| r(c, 30, 14, 26, 2, "#b6b6c1"); |
| r(c, 30, 17, 26, 1, "#b6b6c1"); |
| |
| shelfUnit(c, 380, 28); |
| |
| r(c, 564, 4, 60, 21, P.ink); |
| r(c, 565, 5, 58, 19, "#ffffff"); |
| r(c, 565, 5, 58, 2, "#e8e8ee"); |
| c.strokeStyle = "#d9534f"; c.lineWidth = 1; |
| c.beginPath(); c.moveTo(570, 19); c.lineTo(580, 12); c.lineTo(590, 16); |
| c.lineTo(602, 8); c.lineTo(616, 11); c.stroke(); |
| c.strokeStyle = "#3aa3e0"; |
| c.beginPath(); c.moveTo(570, 21); c.lineTo(585, 18); c.lineTo(600, 20); |
| c.lineTo(616, 15); c.stroke(); |
| |
| r(c, 0, H - WALL, W, WALL, P.wallFace); |
| r(c, 0, H - WALL, W, 2, P.ink); |
| r(c, 0, 0, WALL, H, P.wallFace); |
| r(c, WALL - 1, 0, 1, H, P.ink); |
| r(c, W - WALL, 0, WALL, H, P.wallFace); |
| r(c, W - WALL, 0, 1, H, P.ink); |
| |
| r(c, 0, 226, WALL, 78, P.ink); |
| r(c, 0, 228, WALL - 1, 74, P.glassFrame); |
| r(c, 1, 231, WALL - 3, 68, P.sky); |
| r(c, 1, 262, WALL - 3, 2, P.glassFrame); |
| |
| r(c, 299, H - WALL - 1, 62, WALL + 1, P.ink); |
| r(c, 300, H - WALL, 60, WALL, "#c2996a"); |
| r(c, 300, H - WALL, 60, 2, "#dab987"); |
| r(c, 329, H - WALL, 2, WALL, P.ink); |
| r(c, 306, H - 6, 4, 2, "#6b4d2a"); r(c, 350, H - 6, 4, 2, "#6b4d2a"); |
| |
| r(c, 313, H - 25, 34, 12, P.ink); |
| r(c, 314, H - 24, 32, 10, "#1f7a37"); |
| c.fillStyle = "#d8ffe2"; |
| c.font = "6px 'Press Start 2P'"; |
| c.fillText("EXIT", 318, H - 16); |
| |
| r(c, 302, H - 34, 56, 14, "#b6543e"); |
| r(c, 302, H - 34, 56, 1, "#8a3f2e"); r(c, 302, H - 21, 56, 1, "#8a3f2e"); |
|
|
| |
| cubicle(c, SPOTS.playerDesk, "#5b5b68", { books: true }); |
| cubicle(c, NPC_SPOTS.brad, "#ff5a3c", { phone: true }); |
| cubicle(c, NPC_SPOTS.stacey, "#1fd9c4", { books: true }); |
| cubicle(c, NPC_SPOTS.kevin, "#ffd23f"); |
| cubicle(c, NPC_SPOTS.janet, "#b95cff", { books: true }); |
| cubicle(c, NPC_SPOTS.derek, "#8fd4ff", { darkMonitor: true, phone: true }); |
| |
| c.fillStyle = P.label; |
| c.font = "6px 'Press Start 2P'"; |
| c.fillText("YOU", SPOTS.playerDesk.x - 9, SPOTS.playerDesk.y + 52); |
|
|
| |
| const j = NPC_SPOTS.janet; |
| r(c, j.x - 19, j.y - 31, 38, 18, P.ink); |
| r(c, j.x - 18, j.y - 30, 36, 16, "#ffffff"); |
| [["#ff36c0", 0], ["#3aa3e0", 9], ["#5cc46e", 18], ["#e6b33c", 27]] |
| .forEach(([col, off]) => r(c, j.x - 15 + off, j.y - 27, 6, 6, col)); |
| r(c, j.x - 15, j.y - 19, 26, 2, P.paperShadow); |
|
|
| |
| r(c, 605, 311, 26, 50, P.ink); |
| r(c, 606, 312, 24, 48, "#9a9aa8"); |
| r(c, 609, 315, 18, 42, "#ffffff"); |
| c.strokeStyle = "#d9534f"; c.lineWidth = 1; |
| c.beginPath(); c.moveTo(612, 350); c.lineTo(617, 326); |
| c.lineTo(621, 340); c.lineTo(625, 320); c.stroke(); |
| c.fillStyle = "#3aa3e0"; c.fillText("?", 612, 324); |
|
|
| |
| const d = NPC_SPOTS.derek; |
| r(c, d.x - 25, d.y + 9, 12, 14, P.ink); |
| r(c, d.x - 24, d.y + 10, 10, 12, "#d9d2b8"); |
| r(c, d.x - 22, d.y + 12, 6, 2, "#bfb89e"); |
| briefcase(c, d.x + 38, d.y + 24); |
|
|
| |
| |
| [320, 360].forEach((y) => { |
| shade(c, WALL + 2, y + 31, 26); |
| r(c, WALL + 1, y - 1, 26, 34, P.ink); |
| r(c, WALL + 2, y, 24, 32, P.metalTop); |
| r(c, WALL + 2, y, 24, 3, P.metalHi); |
| r(c, WALL + 2, y + 26, 24, 6, P.metalFace); |
| r(c, WALL + 4, y + 7, 20, 2, P.metalFace); |
| r(c, WALL + 4, y + 17, 20, 2, P.metalFace); |
| r(c, WALL + 12, y + 4, 5, 2, P.metalHi); |
| r(c, WALL + 12, y + 14, 5, 2, P.metalHi); |
| }); |
| papers(c, WALL + 8, 308); |
| briefcase(c, WALL + 30, 344); |
| |
| r(c, WALL + 3, 179, 28, 22, P.ink); |
| r(c, WALL + 4, 180, 26, 20, "#ffffff"); |
| r(c, WALL + 6, 182, 22, 16, "#2c2c38"); |
| [["#ff36c0", 0, 0], ["#3aa3e0", 11, 0], ["#5cc46e", 0, 8], |
| ["#e6b33c", 11, 8]].forEach(([col, ox, oy]) => { |
| r(c, WALL + 7 + ox, 183 + oy, 9, 7, col); |
| }); |
| |
| const pr = SPOTS.printer; |
| shade(c, pr.x - 13, pr.y + 9, 30); |
| r(c, pr.x - 14, pr.y - 9, 30, 20, P.ink); |
| r(c, pr.x - 13, pr.y - 8, 28, 14, "#e6e6eb"); |
| r(c, pr.x - 13, pr.y - 8, 28, 3, "#f6f6f9"); |
| r(c, pr.x - 13, pr.y + 6, 28, 4, "#c5c7d1"); |
| r(c, pr.x - 8, pr.y - 12, 18, 5, "#cfcfd8"); |
| r(c, pr.x - 8, pr.y - 12, 18, 1, P.ink); |
| r(c, pr.x - 5, pr.y - 2, 12, 4, "#3a3a45"); |
| r(c, pr.x + 9, pr.y - 5, 3, 2, "#5cc46e"); |
| |
| shade(c, SPOTS.reception.x - 30, SPOTS.reception.y + 9, 60); |
| box3d(c, SPOTS.reception.x - 30, SPOTS.reception.y - 8, 60, 14, 8, |
| P.woodTop, P.woodFace, P.woodTopHi); |
| monitor(c, SPOTS.reception.x - 14, SPOTS.reception.y - 2); |
| landline(c, SPOTS.reception.x + 6, SPOTS.reception.y - 4); |
| r(c, SPOTS.reception.x + 20, SPOTS.reception.y - 4, 8, 6, P.leaf); |
| |
| plant(c, SPOTS.plant.x, SPOTS.plant.y, true); |
| plant(c, 248, 188); |
| plant(c, 432, 188); |
| |
| r(c, 319, 6, 24, 18, P.ink); |
| r(c, 320, 7, 22, 16, "#ffffff"); |
| r(c, 322, 9, 18, 12, "#aedcf2"); |
| r(c, 324, 15, 6, 3, "#5cc46e"); r(c, 332, 11, 5, 5, "#e6b33c"); |
| |
| [[352, 250, -20], [368, 262, 35], [340, 270, 10]].forEach(([x, y, a]) => { |
| c.save(); c.translate(x, y); c.rotate((a * Math.PI) / 180); |
| r(c, -4, -3, 8, 6, P.paper); |
| r(c, -4, 2, 8, 1, P.paperShadow); |
| c.restore(); |
| }); |
| |
| const ib = SPOTS.inbox; |
| r(c, ib.x - 10, ib.y - 6, 22, 13, P.ink); |
| r(c, ib.x - 9, ib.y - 5, 20, 11, P.metalFace); |
| r(c, ib.x - 8, ib.y - 4, 18, 9, P.metalTop); |
| r(c, ib.x - 6, ib.y - 2, 14, 5, "#8a8e9c"); |
| r(c, ib.x - 6, ib.y - 2, 14, 1, P.paper); |
|
|
| return cv; |
| } |
|
|
| |
| const css = (name) => |
| getComputedStyle(document.documentElement).getPropertyValue(name).trim(); |
|
|
| export function drawFloor(ctx, opts = {}) { |
| if (!staticScene) staticScene = buildStatic(); |
| ctx.drawImage(staticScene, 0, 0); |
|
|
| if (opts.gloomy) { r(ctx, 0, 0, W, H, "rgba(54,60,92,.30)"); } |
|
|
| const br = ROOMS.boardroom; |
| |
| if (opts.doorGlow) { |
| const pulse = Math.floor(Date.now() / 280) % 2; |
| r(ctx, br.x + br.w - 6, br.y + 90, 6, 50, pulse ? "#ffc73b" : "#d9a82f"); |
| ctx.globalAlpha = pulse ? 0.35 : 0.2; |
| r(ctx, br.x + br.w - 16, br.y + 82, 26, 66, "#ffc73b"); |
| ctx.globalAlpha = 1; |
| } else { |
| r(ctx, br.x + br.w - 6, br.y + 90, 6, 50, "#c2996a"); |
| r(ctx, br.x + br.w - 6, br.y + 90, 2, 50, "#dab987"); |
| r(ctx, br.x + br.w - 4, br.y + 112, 2, 6, "#6b4d2a"); |
| } |
|
|
| |
| if (opts.gloomy) { |
| if (rainDrops.length === 0) |
| rainDrops = Array.from({ length: 7 }, () => |
| ({ x: 1 + Math.random() * 6, y: 232 + Math.random() * 64 })); |
| ctx.fillStyle = "#5a8ec4"; |
| rainDrops.forEach((d) => { |
| ctx.fillRect(d.x, d.y, 1, 4); |
| d.y += 2; if (d.y > 296) d.y = 232; |
| }); |
| |
| ctx.globalAlpha = 0.5; |
| for (let i = 0; i < 10; i++) { |
| const rx = 252 + ((i * 53 + Math.floor(Date.now() / 90) * 7) % 168); |
| ctx.fillRect(rx, 6 + (i * 31) % 12, 1, 4); |
| } |
| ctx.globalAlpha = 1; |
| } |
|
|
| |
| const ck = SPOTS.clock; |
| r(ctx, ck.x - 9, ck.y - 9, 18, 18, "#3a3d49"); |
| r(ctx, ck.x - 7, ck.y - 7, 14, 14, "#ffffff"); |
| r(ctx, ck.x - 7, ck.y - 7, 14, 2, "#e8e8ee"); |
| ctx.fillStyle = "#2c2c38"; |
| const sec = Math.floor(Date.now() / 1000) % 60; |
| const ang = (sec / 60) * Math.PI * 2 - Math.PI / 2; |
| ctx.fillRect(ck.x, ck.y, Math.max(1, Math.cos(ang) * 4), 1); |
| ctx.fillRect(ck.x, ck.y - 3, 1, 3); |
|
|
| |
| if (Math.floor(Date.now() / 800) % 5 === 0) { |
| ctx.globalAlpha = 0.15; |
| for (const s of [SPOTS.playerDesk, ...Object.values(NPC_SPOTS)]) |
| r(ctx, s.x - 6, s.y + 7, 12, 8, "#7fb2ec"); |
| ctx.globalAlpha = 1; |
| } |
|
|
| |
| if (opts.printerShake) { |
| const pj = Math.sin(Date.now() / 30) * 2; |
| const pr = SPOTS.printer; |
| r(ctx, pr.x - 13 + pj, pr.y - 8, 28, 17, "#e6e6eb"); |
| r(ctx, pr.x - 8 + pj, pr.y - 12, 18, 5, "#cfcfd8"); |
| r(ctx, pr.x - 2 + pj, pr.y - 14, 10, 6, "#ffffff"); |
| } |
|
|
| |
| const pd = SPOTS.playerDesk; |
| const ph = opts.phoneRing ? Math.sin(Date.now() / 25) * 2 : 0; |
| r(ctx, pd.x + 9 + ph, pd.y + 12, 16, 11, "#3a3d49"); |
| r(ctx, pd.x + 10 + ph, pd.y + 13, 14, 9, "#b9bcc8"); |
| r(ctx, pd.x + 11 + ph, pd.y + 14, 12, 2, "#d4d6de"); |
| r(ctx, pd.x + 12 + ph, pd.y + 18, 8, 2, "#7d8290"); |
| if (opts.phoneRing && Math.floor(Date.now() / 200) % 2) |
| r(ctx, pd.x + 14, pd.y + 6, 6, 4, css("--bds-neon-cyan") || "#2ff0ff"); |
|
|
| |
| if (opts.inboxLit) { |
| const ib = SPOTS.inbox; |
| const lit = Math.floor(Date.now() / 300) % 2; |
| r(ctx, ib.x - 6, ib.y - 2, 14, 5, lit ? "#8edb3a" : "#6fae2c"); |
| ctx.globalAlpha = 0.3; |
| r(ctx, ib.x - 12, ib.y - 8, 26, 17, "#8edb3a"); |
| ctx.globalAlpha = 1; |
| } |
| } |
|
|