@agent-os-lab/agent-game-sdk 0.1.3 → 0.1.4

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.
@@ -1,5 +1,9 @@
1
1
  import * as THREE from "three";
2
2
  import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils.js";
3
+ import type {
4
+ ResolvedOfficeComponentInstance,
5
+ ResolvedOfficeLayout,
6
+ } from "../../layout";
3
7
 
4
8
  export const OFFICE_LAYOUT_SCALE = 1.2;
5
9
  const OFFICE_FLOOR_CENTER_X = 16;
@@ -19,12 +23,15 @@ const DESK_TEMPLATE = [
19
23
  { seat: { x: scaleOfficeX(-12), z: scaleOfficeZ(-6.2) }, rotation: Math.PI, desk: { x: scaleOfficeX(-12), z: scaleOfficeZ(-8) } },
20
24
  { seat: { x: scaleOfficeX(-7), z: scaleOfficeZ(-6.2) }, rotation: Math.PI, desk: { x: scaleOfficeX(-7), z: scaleOfficeZ(-8) } },
21
25
  { seat: { x: scaleOfficeX(-2), z: scaleOfficeZ(-6.2) }, rotation: Math.PI, desk: { x: scaleOfficeX(-2), z: scaleOfficeZ(-8) } },
26
+ { seat: { x: scaleOfficeX(3), z: scaleOfficeZ(-6.2) }, rotation: Math.PI, desk: { x: scaleOfficeX(3), z: scaleOfficeZ(-8) } },
22
27
  { seat: { x: scaleOfficeX(-12), z: scaleOfficeZ(-1.2) }, rotation: Math.PI, desk: { x: scaleOfficeX(-12), z: scaleOfficeZ(-3) } },
23
28
  { seat: { x: scaleOfficeX(-7), z: scaleOfficeZ(-1.2) }, rotation: Math.PI, desk: { x: scaleOfficeX(-7), z: scaleOfficeZ(-3) } },
24
29
  { seat: { x: scaleOfficeX(-2), z: scaleOfficeZ(-1.2) }, rotation: Math.PI, desk: { x: scaleOfficeX(-2), z: scaleOfficeZ(-3) } },
30
+ { seat: { x: scaleOfficeX(3), z: scaleOfficeZ(-1.2) }, rotation: Math.PI, desk: { x: scaleOfficeX(3), z: scaleOfficeZ(-3) } },
25
31
  { seat: { x: scaleOfficeX(-12), z: scaleOfficeZ(3.8) }, rotation: Math.PI, desk: { x: scaleOfficeX(-12), z: scaleOfficeZ(2) } },
26
32
  { seat: { x: scaleOfficeX(-7), z: scaleOfficeZ(3.8) }, rotation: Math.PI, desk: { x: scaleOfficeX(-7), z: scaleOfficeZ(2) } },
27
33
  { seat: { x: scaleOfficeX(-2), z: scaleOfficeZ(3.8) }, rotation: Math.PI, desk: { x: scaleOfficeX(-2), z: scaleOfficeZ(2) } },
34
+ { seat: { x: scaleOfficeX(3), z: scaleOfficeZ(3.8) }, rotation: Math.PI, desk: { x: scaleOfficeX(3), z: scaleOfficeZ(2) } },
28
35
  ] as const;
29
36
  export const DESK_SEATS = DESK_TEMPLATE.map((slot) => slot.seat) as ReadonlyArray<{ x: number; z: number }>;
30
37
  export const MEETING_ROOMS = [
@@ -101,6 +108,12 @@ export type OfficeFurniturePlan = {
101
108
  waterDispensers: number;
102
109
  teaBars: number;
103
110
  sofas: number;
111
+ treadmills: number;
112
+ stationaryBikes: number;
113
+ dumbbellRacks: number;
114
+ weightBenches: number;
115
+ yogaMats: number;
116
+ gymMirrors: number;
104
117
  coffeeTables: number;
105
118
  reviewStations: number;
106
119
  zonePads: number;
@@ -108,11 +121,11 @@ export type OfficeFurniturePlan = {
108
121
 
109
122
  export function createOfficeFurniturePlan(): OfficeFurniturePlan {
110
123
  return {
111
- desks: 9,
112
- deskChairs: 9,
113
- deskChairBases: 9,
114
- deskChairCasters: 45,
115
- monitors: 9,
124
+ desks: 12,
125
+ deskChairs: 12,
126
+ deskChairBases: 12,
127
+ deskChairCasters: 60,
128
+ monitors: 12,
116
129
  meetingTables: 3,
117
130
  meetingTableLegs: 18,
118
131
  meetingChairs: 29,
@@ -128,6 +141,12 @@ export function createOfficeFurniturePlan(): OfficeFurniturePlan {
128
141
  waterDispensers: 1,
129
142
  teaBars: 1,
130
143
  sofas: 1,
144
+ treadmills: 2,
145
+ stationaryBikes: 1,
146
+ dumbbellRacks: 1,
147
+ weightBenches: 1,
148
+ yogaMats: 2,
149
+ gymMirrors: 1,
131
150
  coffeeTables: 0,
132
151
  reviewStations: 0,
133
152
  zonePads: 2,
@@ -142,21 +161,16 @@ export function addLights(scene: THREE.Scene): void {
142
161
  scene.add(directional);
143
162
  }
144
163
 
145
- export function buildOfficeScene(scene: THREE.Scene): void {
146
- buildOfficeBaseFloors(scene);
147
- buildOfficeWalls(scene);
164
+ export function buildOfficeScene(scene: THREE.Scene, layout: ResolvedOfficeLayout): void {
165
+ buildOfficeBaseFloors(scene, layout);
166
+ buildOfficeWalls(scene, layout);
148
167
 
149
168
  const batcher = new BoxInstanceBatcher(scene);
150
169
  const transparentCache = new TransparentBoxCache();
151
170
  activeBoxBatcher = batcher;
152
171
  activeTransparentBoxCache = transparentCache;
153
172
  try {
154
- buildWallDecorations(scene);
155
- buildDeskCluster(scene);
156
- buildMeetingRoom(scene);
157
- buildLargeMeetingRoom(scene);
158
- buildLounge(scene);
159
- buildTeaBar(scene);
173
+ layout.components.forEach((component) => renderOfficeComponent(scene, component));
160
174
  } finally {
161
175
  activeBoxBatcher = undefined;
162
176
  activeTransparentBoxCache = undefined;
@@ -280,32 +294,30 @@ class TransparentBoxCache {
280
294
 
281
295
  let activeTransparentBoxCache: TransparentBoxCache | undefined;
282
296
 
283
- function buildOfficeBaseFloors(scene: THREE.Scene) {
284
- const floorMinX = OFFICE_FLOOR_CENTER_X - OFFICE_FLOOR_WIDTH / 2;
285
- const floorMaxX = OFFICE_FLOOR_CENTER_X + OFFICE_FLOOR_WIDTH / 2;
286
- const officeSplitX = scaleOfficeX(24);
287
- addBaseFloor(scene, 0xe5e7eb, (floorMinX + officeSplitX) / 2, officeSplitX - floorMinX, OFFICE_FLOOR_DEPTH);
288
- addBaseFloor(scene, 0xdbe4ec, (officeSplitX + floorMaxX) / 2, floorMaxX - officeSplitX, OFFICE_FLOOR_DEPTH);
297
+ function buildOfficeBaseFloors(scene: THREE.Scene, layout: ResolvedOfficeLayout) {
298
+ layout.scene.floorTiles.forEach((tile) => {
299
+ addBaseFloor(scene, tile.color, tile.x, tile.z, tile.width, tile.depth);
300
+ });
289
301
  }
290
302
 
291
- function addBaseFloor(scene: THREE.Scene, color: number, x: number, width: number, depth: number) {
303
+ function addBaseFloor(scene: THREE.Scene, color: number, x: number, z: number, width: number, depth: number) {
292
304
  const floor = new THREE.Mesh(
293
305
  new THREE.BoxGeometry(width, 0.2, depth),
294
306
  new THREE.MeshLambertMaterial({ color }),
295
307
  );
296
308
  floor.receiveShadow = true;
297
- floor.position.set(x, -0.1, 0);
309
+ floor.position.set(x, -0.1, z);
298
310
  scene.add(floor);
299
- addGridFloorLines(scene, x, width, depth);
311
+ addGridFloorLines(scene, x, z, width, depth);
300
312
  }
301
313
 
302
- function addGridFloorLines(scene: THREE.Scene, x: number, width: number, depth: number) {
314
+ function addGridFloorLines(scene: THREE.Scene, x: number, z: number, width: number, depth: number) {
303
315
  const geometry = new THREE.BufferGeometry();
304
316
  const positions: number[] = [];
305
317
  const minX = x - width / 2;
306
318
  const maxX = x + width / 2;
307
- const minZ = -depth / 2;
308
- const maxZ = depth / 2;
319
+ const minZ = z - depth / 2;
320
+ const maxZ = z + depth / 2;
309
321
 
310
322
  for (let lineX = minX; lineX <= maxX + 0.001; lineX += OFFICE_FLOOR_TILE_SIZE) {
311
323
  positions.push(lineX, 0.012, minZ, lineX, 0.012, maxZ);
@@ -323,17 +335,25 @@ function addGridFloorLines(scene: THREE.Scene, x: number, width: number, depth:
323
335
  scene.add(lines);
324
336
  }
325
337
 
326
- function buildOfficeWalls(scene: THREE.Scene) {
327
- const wallHeight = 2.5;
328
- const wallThickness = 0.5;
329
- const wallMaterial = new THREE.MeshLambertMaterial({ color: 0xe8e0d0 });
330
- const floorMinX = OFFICE_FLOOR_CENTER_X - OFFICE_FLOOR_WIDTH / 2;
331
- const floorMaxX = OFFICE_FLOOR_CENTER_X + OFFICE_FLOOR_WIDTH / 2;
332
- const floorMinZ = -OFFICE_FLOOR_DEPTH / 2;
333
-
334
- addOfficeWallBox(scene, OFFICE_FLOOR_WIDTH, wallHeight, wallThickness, OFFICE_FLOOR_CENTER_X, wallHeight / 2, floorMinZ, wallMaterial);
335
- addOfficeWallBox(scene, wallThickness, wallHeight, OFFICE_FLOOR_DEPTH, floorMinX, wallHeight / 2, 0, wallMaterial);
336
- addOfficeWallBox(scene, wallThickness, wallHeight, OFFICE_FLOOR_DEPTH, floorMaxX, wallHeight / 2, 0, wallMaterial);
338
+ function buildOfficeWalls(scene: THREE.Scene, layout: ResolvedOfficeLayout) {
339
+ const materialCache = new Map<number, THREE.MeshLambertMaterial>();
340
+ layout.scene.walls.forEach((wall) => {
341
+ const material = materialCache.get(wall.color) ?? (() => {
342
+ const created = new THREE.MeshLambertMaterial({ color: wall.color });
343
+ materialCache.set(wall.color, created);
344
+ return created;
345
+ })();
346
+ addOfficeWallBox(
347
+ scene,
348
+ wall.width,
349
+ wall.height,
350
+ wall.depth,
351
+ wall.x,
352
+ wall.height / 2,
353
+ wall.z,
354
+ material,
355
+ );
356
+ });
337
357
  }
338
358
 
339
359
  function addOfficeWallBox(
@@ -378,6 +398,23 @@ function addWallWhiteboard(scene: THREE.Scene, x: number, wallZ: number, rotatio
378
398
  addLocalBox(scene, 0x64748b, 1.2, 0.06, 0.05, x, wallZ, -0.5, 1.02, offsets.trayZ, rotation);
379
399
  }
380
400
 
401
+ function addWallWindow(
402
+ scene: THREE.Scene,
403
+ x: number,
404
+ wallZ: number,
405
+ rotation: number,
406
+ faceDirection: -1 | 1,
407
+ width = 3.1,
408
+ height = 1.25,
409
+ ) {
410
+ const frameZ = 0.08 * faceDirection;
411
+ const paneZ = 0.13 * faceDirection;
412
+ addLocalBox(scene, 0x64748b, width + 0.18, height + 0.18, 0.06, x, wallZ, 0, 1.65, frameZ, rotation);
413
+ addLocalBox(scene, 0x9bdaf2, width, height, 0.04, x, wallZ, 0, 1.65, paneZ, rotation);
414
+ addLocalBox(scene, 0xe0f2fe, 0.08, height, 0.05, x, wallZ, 0, 1.65, paneZ + 0.01 * faceDirection, rotation);
415
+ addLocalBox(scene, 0xe0f2fe, width, 0.08, 0.05, x, wallZ, 0, 1.65, paneZ + 0.012 * faceDirection, rotation);
416
+ }
417
+
381
418
  function addStickyNoteBoard(scene: THREE.Scene, x: number, z: number) {
382
419
  addBox(scene, 0xb87333, 3, 1.5, 0.08, x, 1.5, z - 0.04);
383
420
  addBox(scene, 0xd4a76a, 2.8, 1.3, 0.05, x, 1.5, z + 0.02);
@@ -414,7 +451,7 @@ function buildDeskCluster(scene: THREE.Scene) {
414
451
 
415
452
  function buildMeetingRoom(scene: THREE.Scene) {
416
453
  MEETING_ROOMS.forEach((room, roomIndex) => {
417
- addMeetingGlassRoom(scene, roomIndex);
454
+ addMeetingGlassRoom(scene, roomIndex, room.center.x, room.center.z, 8 * OFFICE_LAYOUT_SCALE);
418
455
  addMeetingTable(scene, room.center.x, room.center.z, room.topColor, room.legColor, room.tableDepth);
419
456
  room.seats.forEach((seat) => addOfficeChair(scene, seat.x, seat.z, seat.rotation));
420
457
  });
@@ -450,6 +487,106 @@ function buildTeaBar(scene: THREE.Scene) {
450
487
  addWaterDispenser(scene, scaleOfficeX(-16.9), scaleOfficeZ(10.4));
451
488
  }
452
489
 
490
+ function renderOfficeComponent(scene: THREE.Scene, component: ResolvedOfficeComponentInstance) {
491
+ const { x, z } = component.position;
492
+ const rotation = component.rotation ?? 0;
493
+ switch (component.kind) {
494
+ case "desk":
495
+ addDesk(scene, x, z);
496
+ return;
497
+ case "officeChair":
498
+ addOfficeChair(scene, x, z, rotation);
499
+ return;
500
+ case "monitor":
501
+ addMonitor(scene, x, z);
502
+ return;
503
+ case "meetingTable":
504
+ addMeetingTable(
505
+ scene,
506
+ x,
507
+ z,
508
+ component.props?.topColor,
509
+ component.props?.legColor,
510
+ component.props?.depth,
511
+ );
512
+ return;
513
+ case "meetingGlassRoom":
514
+ addMeetingGlassRoom(
515
+ scene,
516
+ component.props?.roomIndex ?? 0,
517
+ x,
518
+ z,
519
+ component.props?.depth ?? 8 * OFFICE_LAYOUT_SCALE,
520
+ component.props?.omitGlassSide ?? component.props?.wallSide,
521
+ component.props?.whiteboardSide ?? component.props?.wallSide,
522
+ );
523
+ return;
524
+ case "largeMeetingTable":
525
+ addLargeMeetingTable(scene, x, z);
526
+ return;
527
+ case "tableCup":
528
+ addTableCup(scene, x, z);
529
+ return;
530
+ case "dividerGlass":
531
+ addOfficeDividerGlass(scene);
532
+ return;
533
+ case "wallWhiteboard":
534
+ addWallWhiteboard(scene, x, z, rotation, component.props?.faceDirection ?? 1);
535
+ return;
536
+ case "wallWindow":
537
+ addWallWindow(scene, x, z, rotation, component.props?.faceDirection ?? 1, component.props?.width, component.props?.height);
538
+ return;
539
+ case "stickyNoteBoard":
540
+ addStickyNoteBoard(scene, x, z);
541
+ return;
542
+ case "wallClock":
543
+ addWallClock(scene, x, z);
544
+ return;
545
+ case "sofa":
546
+ addSofa(scene, x, z);
547
+ return;
548
+ case "loungeSideTable":
549
+ addLoungeSideTable(scene, x, z);
550
+ return;
551
+ case "bookcase":
552
+ addBookcase(scene, x, z);
553
+ return;
554
+ case "teaCounter":
555
+ addTeaCounter(scene, x, z);
556
+ return;
557
+ case "waterDispenser":
558
+ addWaterDispenser(scene, x, z);
559
+ return;
560
+ case "presentationBoard":
561
+ addPresentationBoard(scene, x, z, component.props?.screenColor, rotation);
562
+ return;
563
+ case "mobileDisplayStand":
564
+ addMobileDisplayStand(scene, x, z, rotation);
565
+ return;
566
+ case "treadmill":
567
+ addTreadmill(scene, x, z, rotation);
568
+ return;
569
+ case "stationaryBike":
570
+ addStationaryBike(scene, x, z, rotation);
571
+ return;
572
+ case "dumbbellRack":
573
+ addDumbbellRack(scene, x, z, rotation);
574
+ return;
575
+ case "weightBench":
576
+ addWeightBench(scene, x, z, rotation);
577
+ return;
578
+ case "yogaMat":
579
+ addYogaMat(scene, x, z, rotation);
580
+ return;
581
+ case "gymMirror":
582
+ addGymMirror(scene, x, z, rotation, component.props?.width, component.props?.faceDirection ?? 1);
583
+ return;
584
+ case "glassWall":
585
+ case "glassDoor":
586
+ return;
587
+ }
588
+ }
589
+
453
590
  function addDesk(scene: THREE.Scene, x: number, z: number) {
454
591
  addBox(scene, 0xd2b48c, 3, 0.15, 1.8, x, 1.5, z);
455
592
  addBox(scene, 0x8b7355, 0.15, 1.5, 0.15, x - 1.3, 0.75, z - 0.7);
@@ -506,55 +643,39 @@ function addMeetingTable(
506
643
  addBox(scene, legColor, 0.2, 1.5, 0.2, x + 2.2, 0.75, z + legZ);
507
644
  }
508
645
 
509
- function addMeetingGlassRoom(scene: THREE.Scene, roomIndex: number) {
510
- const sharedWallZ = -2.75;
511
- const northWallFaceZ = -OFFICE_FLOOR_DEPTH / 2 + 0.28;
512
- if (roomIndex === 0) {
513
- addGlassWallWithDoor(
514
- scene,
515
- roomIndex,
516
- scaleOfficeX(10.5),
517
- scaleOfficeZ((sharedWallZ + 9.6) / 2),
518
- (9.6 - sharedWallZ) * OFFICE_LAYOUT_SCALE,
519
- 1.9,
520
- 0.25,
521
- );
522
- addTransparentBox(scene, 0x88ccee, 11.7 * OFFICE_LAYOUT_SCALE, 2.5, 0.08, scaleOfficeX(16.35), 1.25, scaleOfficeZ(sharedWallZ), 0.2);
523
- addTransparentBox(scene, 0x88ccee, 11.7 * OFFICE_LAYOUT_SCALE, 2.5, 0.08, scaleOfficeX(16.35), 1.25, scaleOfficeZ(9.6), 0.2);
524
- addMeetingWhiteboard(scene, scaleOfficeX(16.35), scaleOfficeZ(sharedWallZ), Math.PI);
525
- addBox(scene, 0xcccccc, 0.15, 2.5, 0.15, scaleOfficeX(10.5), 1.25, scaleOfficeZ(sharedWallZ));
526
- addBox(scene, 0xcccccc, 0.15, 2.5, 0.15, scaleOfficeX(10.5), 1.25, scaleOfficeZ(9.6));
527
- return;
646
+ function addMeetingGlassRoom(
647
+ scene: THREE.Scene,
648
+ roomIndex: number,
649
+ x: number,
650
+ z: number,
651
+ depth: number,
652
+ omitGlassSide?: "north" | "south",
653
+ whiteboardSide?: "north" | "south",
654
+ ) {
655
+ const width = 11.7 * OFFICE_LAYOUT_SCALE;
656
+ const westX = x - width / 2;
657
+ const eastX = x + width / 2;
658
+ const northZ = z - depth / 2;
659
+ const southZ = z + depth / 2;
660
+
661
+ addGlassWallWithDoor(scene, roomIndex, westX, z, depth, roomIndex === 0 ? 1.9 : 1.6, 0.25);
662
+ addTransparentBox(scene, 0x88ccee, 0.08, 2.5, depth, eastX, 1.25, z, 0.15, `meetingGlassRoom:${roomIndex}:east`);
663
+ if (omitGlassSide !== "north") {
664
+ addTransparentBox(scene, 0x88ccee, width, 2.5, 0.08, x, 1.25, northZ, 0.2, `meetingGlassRoom:${roomIndex}:north`);
665
+ }
666
+ if (omitGlassSide !== "south") {
667
+ addTransparentBox(scene, 0x88ccee, width, 2.5, 0.08, x, 1.25, southZ, 0.2, `meetingGlassRoom:${roomIndex}:south`);
528
668
  }
529
669
 
530
- addGlassWallWithDoor(
531
- scene,
532
- roomIndex,
533
- scaleOfficeX(10.5),
534
- (northWallFaceZ + scaleOfficeZ(sharedWallZ)) / 2,
535
- scaleOfficeZ(sharedWallZ) - northWallFaceZ,
536
- 1.6,
537
- 0.25,
538
- );
539
- addTransparentBox(scene, 0x88ccee, 11.7 * OFFICE_LAYOUT_SCALE, 2.5, 0.08, scaleOfficeX(16.35), 1.25, northWallFaceZ, 0.2);
540
- addTransparentBox(
541
- scene,
542
- 0x88ccee,
543
- 0.08,
544
- 2.5,
545
- scaleOfficeZ(-9.3) - northWallFaceZ,
546
- scaleOfficeX(22.2),
547
- 1.25,
548
- (northWallFaceZ + scaleOfficeZ(-9.3)) / 2,
549
- 0.15,
550
- );
551
- addMeetingWhiteboard(scene, scaleOfficeX(16.35), northWallFaceZ, 0);
670
+ const whiteboardWallSide = whiteboardSide ?? "north";
671
+ const whiteboardZ = whiteboardWallSide === "south" ? southZ - 0.28 : northZ + 0.28;
672
+ addMeetingWhiteboard(scene, x, whiteboardZ, whiteboardWallSide === "south" ? Math.PI : 0);
552
673
  [
553
- [scaleOfficeX(10.5), northWallFaceZ],
554
- [scaleOfficeX(10.5), scaleOfficeZ(sharedWallZ)],
555
- [scaleOfficeX(22.2), northWallFaceZ],
556
- [scaleOfficeX(22.2), scaleOfficeZ(sharedWallZ)],
557
- ].forEach(([x, z]) => addBox(scene, 0xcccccc, 0.15, 2.5, 0.15, x, 1.25, z));
674
+ [westX, northZ],
675
+ [westX, southZ],
676
+ [eastX, northZ],
677
+ [eastX, southZ],
678
+ ].forEach(([postX, postZ]) => addBox(scene, 0xcccccc, 0.15, 2.5, 0.15, postX, 1.25, postZ));
558
679
  }
559
680
 
560
681
  function addOfficeDividerGlass(scene: THREE.Scene) {
@@ -704,6 +825,57 @@ function addWaterDispenser(scene: THREE.Scene, x: number, z: number) {
704
825
  addBox(scene, 0xf8fafc, 0.2, 0.22, 0.2, x + 0.25, 0.42, z - 0.16);
705
826
  }
706
827
 
828
+ function addTreadmill(scene: THREE.Scene, x: number, z: number, rotation = 0) {
829
+ addLocalBox(scene, 0x111827, 1.45, 0.18, 3.4, x, z, 0, 0.18, 0, rotation);
830
+ addLocalBox(scene, 0x475569, 1.18, 0.06, 2.85, x, z, 0, 0.32, 0.06, rotation);
831
+ addLocalBox(scene, 0x64748b, 0.12, 1.35, 0.12, x, z, -0.62, 0.92, -1.42, rotation);
832
+ addLocalBox(scene, 0x64748b, 0.12, 1.35, 0.12, x, z, 0.62, 0.92, -1.42, rotation);
833
+ addLocalBox(scene, 0x0f172a, 1.1, 0.55, 0.08, x, z, 0, 1.5, -1.55, rotation);
834
+ addLocalBox(scene, 0x38bdf8, 0.82, 0.36, 0.04, x, z, 0, 1.5, -1.61, rotation);
835
+ }
836
+
837
+ function addStationaryBike(scene: THREE.Scene, x: number, z: number, rotation = 0) {
838
+ addLocalBox(scene, 0x334155, 1.1, 0.1, 1.25, x, z, 0, 0.28, 0, rotation);
839
+ addLocalBox(scene, 0x64748b, 0.12, 1.1, 0.12, x, z, 0, 0.85, 0.15, rotation);
840
+ addLocalBox(scene, 0x111827, 0.68, 0.16, 0.55, x, z, 0, 1.38, 0.22, rotation);
841
+ addLocalBox(scene, 0x64748b, 1.1, 0.08, 0.08, x, z, 0, 1.38, -0.42, rotation);
842
+ addLocalBox(scene, 0x0f172a, 0.92, 0.92, 0.08, x, z, 0, 0.62, -0.22, rotation);
843
+ addLocalBox(scene, 0x0f172a, 0.32, 0.32, 0.12, x, z, 0, 0.62, -0.22, rotation);
844
+ }
845
+
846
+ function addDumbbellRack(scene: THREE.Scene, x: number, z: number, rotation = 0) {
847
+ addLocalBox(scene, 0x475569, 3.2, 0.12, 0.22, x, z, 0, 0.75, 0, rotation);
848
+ addLocalBox(scene, 0x334155, 3.2, 0.12, 0.22, x, z, 0, 0.38, 0.18, rotation);
849
+ for (let index = 0; index < 6; index += 1) {
850
+ const localX = -1.25 + index * 0.5;
851
+ addLocalBox(scene, 0x111827, 0.18, 0.18, 0.18, x, z, localX - 0.08, 0.9, -0.06, rotation);
852
+ addLocalBox(scene, 0x111827, 0.18, 0.18, 0.18, x, z, localX + 0.08, 0.9, -0.06, rotation);
853
+ addLocalBox(scene, 0xe5e7eb, 0.18, 0.06, 0.06, x, z, localX, 0.9, -0.06, rotation);
854
+ }
855
+ }
856
+
857
+ function addWeightBench(scene: THREE.Scene, x: number, z: number, rotation = 0) {
858
+ addLocalBox(scene, 0x111827, 2.25, 0.18, 0.62, x, z, 0, 0.55, 0, rotation);
859
+ addLocalBox(scene, 0x64748b, 0.12, 0.65, 0.12, x, z, -0.85, 0.26, -0.22, rotation);
860
+ addLocalBox(scene, 0x64748b, 0.12, 0.65, 0.12, x, z, 0.85, 0.26, 0.22, rotation);
861
+ addLocalBox(scene, 0x475569, 2.7, 0.08, 0.08, x, z, 0, 1.25, -0.56, rotation);
862
+ addLocalBox(scene, 0x111827, 0.28, 0.5, 0.5, x, z, -1.48, 1.25, -0.56, rotation);
863
+ addLocalBox(scene, 0x111827, 0.28, 0.5, 0.5, x, z, 1.48, 1.25, -0.56, rotation);
864
+ }
865
+
866
+ function addYogaMat(scene: THREE.Scene, x: number, z: number, rotation = 0) {
867
+ addLocalBox(scene, 0x0ea5e9, 1.35, 0.04, 3.05, x, z, 0, 0.04, 0, rotation);
868
+ addLocalBox(scene, 0xbae6fd, 1.05, 0.02, 0.18, x, z, 0, 0.08, -1.18, rotation);
869
+ addLocalBox(scene, 0xbae6fd, 1.05, 0.02, 0.18, x, z, 0, 0.08, 1.18, rotation);
870
+ }
871
+
872
+ function addGymMirror(scene: THREE.Scene, x: number, z: number, rotation = 0, width = 5.2, faceDirection: -1 | 1 = 1) {
873
+ addLocalBox(scene, 0x64748b, width + 0.18, 1.8, 0.06, x, z, 0, 1.45, 0.07 * faceDirection, rotation);
874
+ addLocalBox(scene, 0xbfefff, width, 1.62, 0.04, x, z, 0, 1.45, 0.12 * faceDirection, rotation);
875
+ addLocalBox(scene, 0xe0f2fe, 0.08, 1.42, 0.05, x, z, -width * 0.28, 1.45, 0.14 * faceDirection, rotation);
876
+ addLocalBox(scene, 0xe0f2fe, 0.08, 1.42, 0.05, x, z, width * 0.18, 1.45, 0.14 * faceDirection, rotation);
877
+ }
878
+
707
879
  function addBox(
708
880
  scene: THREE.Scene,
709
881
  color: number,
@@ -59,6 +59,7 @@ export type GameRuntimeConnectionState = "connecting" | "online" | "closed";
59
59
  export type GameRuntimeSubscription = {
60
60
  close: () => void;
61
61
  getSocket: () => WebSocket | null;
62
+ refresh: () => void;
62
63
  };
63
64
 
64
65
  export type AgentGameRuntimeBrowserClientOptions = {
@@ -310,6 +311,15 @@ export function subscribeGameRuntime(
310
311
  }
311
312
 
312
313
  return {
314
+ refresh: () => {
315
+ if (closed || socket.readyState !== WebSocketCtor.OPEN) {
316
+ return;
317
+ }
318
+ socket.send(JSON.stringify({
319
+ type: "refresh",
320
+ tenantId: options.tenantId,
321
+ }));
322
+ },
313
323
  close: () => {
314
324
  closed = true;
315
325
  clearPingInterval();