awacke1 commited on
Commit
78c0cdd
·
verified ·
1 Parent(s): 64b1b25

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +745 -298
index.html CHANGED
@@ -5,24 +5,18 @@
5
  <style>
6
  body { margin: 0; overflow: hidden; }
7
  canvas { display: block; }
8
- .info-panel {
9
- position: absolute; bottom: 10px; left: 10px;
10
- background: rgba(0,0,0,0.6); color: white;
11
- padding: 10px; border-radius: 5px;
12
- font-family: Arial, sans-serif; font-size: 14px;
13
- pointer-events: none;
14
- }
15
  </style>
 
16
  <script>
 
17
  function pollGameState() {
18
  console.log("Polling updated game state:", window.GAME_STATE);
 
19
  }
20
  setInterval(pollGameState, 5000);
21
  </script>
22
  </head>
23
  <body>
24
- <div class="info-panel">WASD or Arrow Keys to move | Click to place objects</div>
25
-
26
  <script type="importmap">
27
  {
28
  "imports": {
@@ -39,43 +33,28 @@
39
  let raycaster, mouse;
40
  const keysPressed = {};
41
  const playerSpeed = 0.15;
42
- let newlyPlacedObjects = [];
43
  const placeholderPlots = new Set();
44
- const groundMeshes = {};
 
 
45
  const SESSION_STORAGE_KEY = 'unsavedInfiniteWorldState';
46
-
 
47
  const allInitialObjects = window.ALL_INITIAL_OBJECTS || [];
48
  const plotsMetadata = window.PLOTS_METADATA || [];
49
  const selectedObjectType = window.SELECTED_OBJECT_TYPE || "None";
50
  const customScale = window.CUSTOM_SCALE || 1.0;
51
- const customRotationY = window.CUSTOM_ROTATION_Y || 0;
52
  const plotWidth = window.PLOT_WIDTH || 50.0;
53
  const plotDepth = window.PLOT_DEPTH || 50.0;
54
 
55
- const materials = {
56
- ground: new THREE.MeshStandardMaterial({color: 0x55aa55, roughness: 0.9, metalness: 0.1, side: THREE.DoubleSide}),
57
- placeholderGround: new THREE.MeshStandardMaterial({color: 0x448844, roughness: 0.95, metalness: 0.1, side: THREE.DoubleSide}),
58
- wood: new THREE.MeshStandardMaterial({color: 0x8B4513, roughness: 0.9, metalness: 0.1}),
59
- darkWood: new THREE.MeshStandardMaterial({color: 0x5D2906, roughness: 0.85, metalness: 0.15}),
60
- redBrick: new THREE.MeshStandardMaterial({color: 0xA03C28, roughness: 0.9, metalness: 0.05}),
61
- metal: new THREE.MeshStandardMaterial({color: 0x888888, roughness: 0.4, metalness: 0.8}),
62
- rustedMetal: new THREE.MeshStandardMaterial({color: 0x964B00, roughness: 0.7, metalness: 0.6}),
63
- glass: new THREE.MeshStandardMaterial({color: 0x88CCFF, roughness: 0.1, metalness: 0.9, transparent: true, opacity: 0.4}),
64
- concrete: new THREE.MeshStandardMaterial({color: 0x888888, roughness: 0.9, metalness: 0.1}),
65
- damagedConcrete: new THREE.MeshStandardMaterial({color: 0x777777, roughness: 0.95, metalness: 0.05}),
66
- darkGreen: new THREE.MeshStandardMaterial({color: 0x225522, roughness: 0.8, metalness: 0.1}),
67
- skin: new THREE.MeshStandardMaterial({color: 0xE0AC69, roughness: 0.7, metalness: 0.1}),
68
- cloth: new THREE.MeshStandardMaterial({color: 0xCCAA88, roughness: 0.9, metalness: 0.0}),
69
- neonGlow: new THREE.MeshStandardMaterial({color: 0x00FFFF, roughness: 0.2, metalness: 0.8, emissive: 0x00FFFF, emissiveIntensity: 1.0}),
70
- goldMetal: new THREE.MeshStandardMaterial({color: 0xFFD700, roughness: 0.3, metalness: 1.0}),
71
- leather: new THREE.MeshStandardMaterial({color: 0x8B4513, roughness: 0.9, metalness: 0.1}),
72
- stone: new THREE.MeshStandardMaterial({color: 0x777777, roughness: 0.9, metalness: 0.1}),
73
- bone: new THREE.MeshStandardMaterial({color: 0xE3DAC9, roughness: 0.7, metalness: 0.1}),
74
- zombie: new THREE.MeshStandardMaterial({color: 0x7D9F85, roughness: 0.8, metalness: 0.1}),
75
- lava: new THREE.MeshStandardMaterial({color: 0xFF4500, roughness: 0.7, metalness: 0.3, emissive: 0xFF4500, emissiveIntensity: 0.8}),
76
- crystal: new THREE.MeshStandardMaterial({color: 0x88CCFF, roughness: 0.1, metalness: 0.9, transparent: true, opacity: 0.7}),
77
- energyBeam: new THREE.MeshStandardMaterial({color: 0x88FFFF, roughness: 0.1, metalness: 0.5, emissive: 0x88FFFF, emissiveIntensity: 1.0, transparent: true, opacity: 0.7})
78
- };
79
 
80
  function init() {
81
  scene = new THREE.Scene();
@@ -102,12 +81,14 @@
102
  loadInitialObjects();
103
  restoreUnsavedState();
104
 
 
105
  document.addEventListener('mousemove', onMouseMove, false);
106
  document.addEventListener('click', onDocumentClick, false);
107
  window.addEventListener('resize', onWindowResize, false);
108
  document.addEventListener('keydown', onKeyDown);
109
  document.addEventListener('keyup', onKeyUp);
110
 
 
111
  window.teleportPlayer = teleportPlayer;
112
  window.getSaveDataAndPosition = getSaveDataAndPosition;
113
  window.getSaveDataForNamedPlot = getSaveDataForNamedPlot;
@@ -150,7 +131,7 @@
150
  if (groundMeshes[gridKey]) return;
151
  console.log(`Creating ${isPlaceholder ? 'placeholder' : 'initial'} ground at ${gridX}, ${gridZ}`);
152
  const groundGeometry = new THREE.PlaneGeometry(plotWidth, plotDepth);
153
- const material = isPlaceholder ? materials.placeholderGround : materials.ground;
154
  const groundMesh = new THREE.Mesh(groundGeometry, material);
155
  groundMesh.rotation.x = -Math.PI / 2;
156
  groundMesh.position.y = -0.05;
@@ -179,61 +160,10 @@
179
  console.log("Finished loading initial objects.");
180
  }
181
 
182
- function saveUnsavedState() {
183
- try {
184
- const stateToSave = newlyPlacedObjects.map(obj => ({
185
- obj_id: obj.userData.obj_id,
186
- type: obj.userData.type,
187
- position: { x: obj.position.x, y: obj.position.y, z: obj.position.z },
188
- rotation: { _x: obj.rotation.x, _y: obj.rotation.y, _z: obj.rotation.z, _order: obj.rotation.order }
189
- }));
190
- sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(stateToSave));
191
- console.log(`Saved ${stateToSave.length} unsaved objects to sessionStorage.`);
192
- } catch (e) {
193
- console.error("Error saving state to sessionStorage:", e);
194
- }
195
- }
196
-
197
- function restoreUnsavedState() {
198
- try {
199
- const savedState = sessionStorage.getItem(SESSION_STORAGE_KEY);
200
- if (savedState) {
201
- console.log("Found unsaved state in sessionStorage. Restoring...");
202
- const objectsToRestore = JSON.parse(savedState);
203
- if (Array.isArray(objectsToRestore)) {
204
- newlyPlacedObjects = [];
205
- let count = 0;
206
- objectsToRestore.forEach(objData => {
207
- if(createAndPlaceObject(objData, true)) { count++; }
208
- });
209
- console.log(`Restored ${count} objects.`);
210
- }
211
- } else {
212
- console.log("No unsaved state found in sessionStorage.");
213
- }
214
- } catch (e) {
215
- console.error("Error restoring state from sessionStorage:", e);
216
- sessionStorage.removeItem(SESSION_STORAGE_KEY);
217
- }
218
- }
219
-
220
- function clearUnsavedState() {
221
- sessionStorage.removeItem(SESSION_STORAGE_KEY);
222
- newlyPlacedObjects = [];
223
- console.log("Cleared unsaved state from memory and sessionStorage.");
224
- }
225
-
226
- function createObjectBase(type) {
227
- return { userData: { type: type, obj_id: THREE.MathUtils.generateUUID() } };
228
- }
229
-
230
  function createAndPlaceObject(objData, isNewObject) {
231
  let loadedObject = null;
232
  switch (objData.type) {
233
  case "Simple House": loadedObject = createSimpleHouse(); break;
234
- case "Tree": loadedObject = createTree(); break;
235
- case "Rock": loadedObject = createRock(); break;
236
- case "Fence Post": loadedObject = createFence động viên Post(); break;
237
  case "Cyberpunk Wall Panel": loadedObject = createCyberpunkWallPanel(); break;
238
  case "Modular Hab Block": loadedObject = createModularHabBlock(); break;
239
  case "MegaCorp Skyscraper": loadedObject = createMegaCorpSkyscraper(); break;
@@ -242,11 +172,14 @@
242
  case "House Roof Section": loadedObject = createHouseRoofSection(); break;
243
  case "Concrete Bunker Wall": loadedObject = createConcreteBunkerWall(); break;
244
  case "Damaged House Facade": loadedObject = createDamagedHouseFacade(); break;
 
 
245
  case "Pine Tree": loadedObject = createPineTree(); break;
246
  case "Boulder": loadedObject = createBoulder(); break;
247
  case "Alien Plant": loadedObject = createAlienPlant(); break;
248
  case "Floating Rock Platform": loadedObject = createFloatingRockPlatform(); break;
249
  case "Rubble Pile": loadedObject = createRubblePile(); break;
 
250
  case "Rooftop AC Unit": loadedObject = createRooftopACUnit(); break;
251
  case "Holographic Window Display": loadedObject = createHolographicWindowDisplay(); break;
252
  case "Jersey Barrier": loadedObject = createJerseyBarrier(); break;
@@ -288,28 +221,17 @@
288
  case "Steam Vent": loadedObject = createSteamVent(); break;
289
  default: console.warn("Unknown object type in data:", objData.type); break;
290
  }
291
-
292
  if (loadedObject) {
293
  if (objData.position && objData.position.x !== undefined) {
294
  loadedObject.position.set(objData.position.x, objData.position.y, objData.position.z);
295
  } else if (objData.pos_x !== undefined) {
296
  loadedObject.position.set(objData.pos_x, objData.pos_y, objData.pos_z);
297
  }
298
-
299
  if (objData.rotation) {
300
  loadedObject.rotation.set(objData.rotation._x, objData.rotation._y, objData.rotation._z, objData.rotation._order || 'XYZ');
301
  } else if (objData.rot_x !== undefined) {
302
  loadedObject.rotation.set(objData.rot_x, objData.rot_y, objData.rot_z, objData.rot_order || 'XYZ');
303
  }
304
-
305
- if (isNewObject && window.CUSTOM_SCALE && window.CUSTOM_SCALE !== 1.0) {
306
- loadedObject.scale.set(window.CUSTOM_SCALE, window.CUSTOM_SCALE, window.CUSTOM_SCALE);
307
- }
308
-
309
- if (isNewObject && window.CUSTOM_ROTATION_Y) {
310
- loadedObject.rotation.y = window.CUSTOM_ROTATION_Y;
311
- }
312
-
313
  loadedObject.userData.obj_id = objData.obj_id || loadedObject.userData.obj_id;
314
  scene.add(loadedObject);
315
  if (isNewObject) newlyPlacedObjects.push(loadedObject);
@@ -318,6 +240,55 @@
318
  return null;
319
  }
320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  function createSimpleHouse() {
322
  const base = createObjectBase("Simple House");
323
  const group = new THREE.Group();
@@ -380,211 +351,642 @@
380
  return post;
381
  }
382
 
 
 
383
  function createCyberpunkWallPanel() {
384
  const base = createObjectBase("Cyberpunk Wall Panel");
385
- const group = new THREE.Group();
386
- Object.assign(group, base);
387
- const panelGeo = new THREE.BoxGeometry(3, 4, 0.3);
388
- const panel = new THREE.Mesh(panelGeo, materials.metal);
389
  panel.castShadow = true;
390
  panel.receiveShadow = true;
391
- group.add(panel);
392
- const trimGeo = new THREE.BoxGeometry(3.2, 0.1, 0.4);
393
- const topTrim = new THREE.Mesh(trimGeo, materials.neonGlow);
394
- topTrim.position.y = 2;
395
- group.add(topTrim);
396
- const bottomTrim = new THREE.Mesh(trimGeo, materials.neonGlow);
397
- bottomTrim.position.y = -2;
398
- group.add(bottomTrim);
399
- const detailGeo = new THREE.BoxGeometry(2.5, 0.1, 0.1);
400
- for (let i = 0; i < 4; i++) {
401
- const detail = new THREE.Mesh(detailGeo, materials.metal);
402
- detail.position.y = -1.5 + i;
403
- detail.position.z = 0.2;
404
- group.add(detail);
405
- }
406
- group.position.y = 2;
407
- return group;
408
  }
409
 
410
  function createModularHabBlock() {
411
  const base = createObjectBase("Modular Hab Block");
412
  const group = new THREE.Group();
413
  Object.assign(group, base);
414
- const buildingGeo = new THREE.BoxGeometry(5, 5, 5);
415
- const building = new THREE.Mesh(buildingGeo, materials.concrete);
416
- building.castShadow = true;
417
- building.receiveShadow = true;
418
- group.add(building);
419
- const windowGeo = new THREE.BoxGeometry(1, 1, 0.1);
420
- const windowPositions = [
421
- [-1.5, 1.5, 2.51], [0, 1.5, 2.51], [1.5, 1.5, 2.51],
422
- [-1.5, 0, 2.51], [0, 0, 2.51], [1.5, 0, 2.51],
423
- [-1.5, -1.5, 2.51], [0, -1.5, 2.51], [1.5, -1.5, 2.51]
424
- ];
425
- windowPositions.forEach(pos => {
426
- const window = new THREE.Mesh(windowGeo, materials.glass);
427
- window.position.set(...pos);
428
- group.add(window);
429
- });
430
- const acGeo = new THREE.BoxGeometry(1.5, 0.8, 1.2);
431
- const ac = new THREE.Mesh(acGeo, materials.metal);
432
- ac.position.set(1.5, 2.9, 1.5);
433
- group.add(ac);
434
- const roofAccessGeo = new THREE.BoxGeometry(1.2, 1.5, 1.2);
435
- const roofAccess = new THREE.Mesh(roofAccessGeo, materials.concrete);
436
- roofAccess.position.set(-1.5, 3.25, 1.5);
437
- group.add(roofAccess);
438
- group.position.y = 2.5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  return group;
440
  }
441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  function createMagicStaff() {
443
  const base = createObjectBase("Magic Staff");
444
  const group = new THREE.Group();
445
  Object.assign(group, base);
446
- const poleGeo = new THREE.CylinderGeometry(0.05, 0.06, 1.8, 8);
447
- const pole = new THREE.Mesh(poleGeo, materials.wood);
448
- pole.castShadow = true;
449
- pole.receiveShadow = true;
450
- group.add(pole);
451
- const orbGeo = new THREE.IcosahedronGeometry(0.15, 1);
452
- const orbMat = new THREE.MeshStandardMaterial({
453
- color: 0x9900FF,
454
- roughness: 0.2,
455
- metalness: 0.5,
456
- emissive: 0x6600CC,
457
- emissiveIntensity: 0.5,
458
- transparent: true,
459
- opacity: 0.7
460
- });
461
- const orb = new THREE.Mesh(orbGeo, orbMat);
462
- orb.position.y = 0.9;
463
- orb.castShadow = true;
464
- orb.receiveShadow = true;
465
- group.add(orb);
466
- const numClaws = 4;
467
- for (let i = 0; i < numClaws; i++) {
468
- const angle = (i / numClaws) * Math.PI * 2;
469
- const clawGeo = new THREE.CylinderGeometry(0.02, 0.01, 0.2, 4);
470
- const claw = new THREE.Mesh(clawGeo, materials.metal);
471
- claw.position.set(
472
- Math.cos(angle) * 0.1,
473
- 0.82,
474
- Math.sin(angle) * 0.1
475
- );
476
- claw.rotation.x = Math.PI/6;
477
- claw.rotation.y = -angle;
478
- claw.castShadow = true;
479
- claw.receiveShadow = true;
480
- group.add(claw);
481
- }
482
- group.position.y = 0.5;
483
  return group;
484
  }
485
 
 
486
  function createCandleFlame() {
487
  const base = createObjectBase("Candle Flame");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  const group = new THREE.Group();
489
  Object.assign(group, base);
490
- const candleGeo = new THREE.CylinderGeometry(0.1, 0.1, 0.3, 8);
491
- const candle = new THREE.Mesh(candleGeo, new THREE.MeshStandardMaterial({
492
- color: 0xF8F8F0,
493
- roughness: 0.7
494
- }));
495
- candle.castShadow = true;
496
- candle.receiveShadow = true;
497
- group.add(candle);
498
- const flameGeo = new THREE.ConeGeometry(0.07, 0.2, 8);
499
- const flameMat = new THREE.MeshStandardMaterial({
500
- color: 0xFF9900,
501
- emissive: 0xFF6600,
502
- emissiveIntensity: 1.0,
503
- transparent: true,
504
- opacity: 0.9
505
- });
506
- const flame = new THREE.Mesh(flameGeo, flameMat);
507
- flame.position.y = 0.25;
508
- group.add(flame);
509
- const innerFlameGeo = new THREE.ConeGeometry(0.03, 0.1, 8);
510
- const innerFlameMat = new THREE.MeshStandardMaterial({
511
- color: 0xFFFF66,
512
- emissive: 0xFFFF00,
513
- emissiveIntensity: 1.0,
514
- transparent: true,
515
- opacity: 0.9
516
- });
517
- const innerFlame = new THREE.Mesh(innerFlameGeo, innerFlameMat);
518
- innerFlame.position.y = 0.05;
519
- flame.add(innerFlame);
520
- const light = new THREE.PointLight(0xFF9933, 0.7, 1.5);
521
- light.position.y = 0.3;
522
- group.add(light);
523
- group.position.y = 0.15;
524
  return group;
525
  }
526
 
527
- function createPlaceholderObject(type) {
528
- console.log(`Placeholder for ${type}: Implementation pending`);
529
- const base = createObjectBase(type);
530
- const geometry = new THREE.BoxGeometry(1, 1, 1);
531
- const material = new THREE.MeshStandardMaterial({ color: 0xff0000, roughness: 0.8 });
532
- const mesh = new THREE.Mesh(geometry, material);
533
- Object.assign(mesh, base);
534
- mesh.position.y = 0.5;
535
- mesh.castShadow = true;
536
- mesh.receiveShadow = true;
537
- return mesh;
538
- }
539
-
540
- function createMegaCorpSkyscraper() { return createPlaceholderObject("MegaCorp Skyscraper"); }
541
- function createCastleWallSection() { return createPlaceholderObject("Castle Wall Section"); }
542
- function createWoodenDoor() { return createPlaceholderObject("Wooden Door"); }
543
- function createHouseRoofSection() { return createPlaceholderObject("House Roof Section"); }
544
- function createConcreteBunkerWall() { return createPlaceholderObject("Concrete Bunker Wall"); }
545
- function createDamagedHouseFacade() { return createPlaceholderObject("Damaged House Facade"); }
546
- function createPineTree() { return createPlaceholderObject("Pine Tree"); }
547
- function createBoulder() { return createPlaceholderObject("Boulder"); }
548
- function createAlienPlant() { return createPlaceholderObject("Alien Plant"); }
549
- function createFloatingRockPlatform() { return createPlaceholderObject("Floating Rock Platform"); }
550
- function createRubblePile() { return createPlaceholderObject("Rubble Pile"); }
551
- function createRooftopACUnit() { return createPlaceholderObject("Rooftop AC Unit"); }
552
- function createHolographicWindowDisplay() { return createPlaceholderObject("Holographic Window Display"); }
553
- function createJerseyBarrier() { return createPlaceholderObject("Jersey Barrier"); }
554
- function createOilDrum() { return createPlaceholderObject("Oil Drum"); }
555
- function createCannedFood() { return createPlaceholderObject("Canned Food"); }
556
- function createTreasureChest() { return createPlaceholderObject("Treasure Chest"); }
557
- function createWallTorch() { return createPlaceholderObject("Wall Torch"); }
558
- function createBonePile() { return createPlaceholderObject("Bone Pile"); }
559
- function createKingFigure() { return createPlaceholderObject("King Figure"); }
560
- function createSoldierFigure() { return createPlaceholderObject("Soldier Figure"); }
561
- function createMageFigure() { return createPlaceholderObject("Mage Figure"); }
562
- function createZombieFigure() { return createPlaceholderObject("Zombie Figure"); }
563
- function createSurvivorFigure() { return createPlaceholderObject("Survivor Figure"); }
564
- function createDwarfMinerFigure() { return createPlaceholderObject("Dwarf Miner Figure"); }
565
- function createUndeadKnightFigure() { return createPlaceholderObject("Undead Knight Figure"); }
566
- function createHeroFigure() { return createPlaceholderObject("Hero Figure"); }
567
- function createWoodenCart() { return createPlaceholderObject("Wooden Cart"); }
568
- function createBallista() { return createPlaceholderObject("Ballista"); }
569
- function createSiegeTower() { return createPlaceholderObject("Siege Tower"); }
570
- function createBuggyFrame() { return createPlaceholderObject("Buggy Frame"); }
571
- function createMotorbike() { return createPlaceholderObject("Motorbike"); }
572
- function createHoverBike() { return createPlaceholderObject("Hover Bike"); }
573
- function createAPC() { return createPlaceholderObject("APC"); }
574
- function createSandBoat() { return createPlaceholderObject("Sand Boat"); }
575
- function createMakeshiftMachete() { return createPlaceholderObject("Makeshift Machete"); }
576
- function createPistolBody() { return createPlaceholderObject("Pistol Body"); }
577
- function createScopeAttachment() { return createPlaceholderObject("Scope Attachment"); }
578
- function createLaserPistol() { return createPlaceholderObject("Laser Pistol"); }
579
- function createEnergySword() { return createPlaceholderObject("Energy Sword"); }
580
- function createDwarvenAxe() { return createPlaceholderObject("Dwarven Axe"); }
581
- function createDustCloud() { return createPlaceholderObject("Dust Cloud"); }
582
- function createBloodSplatDecal() { return createPlaceholderObject("Blood Splat Decal"); }
583
- function createBurningBarrelFire() { return createPlaceholderObject("Burning Barrel Fire"); }
584
- function createWarpTunnelEffect() { return createPlaceholderObject("Warp Tunnel Effect"); }
585
- function createLaserBeam() { return createPlaceholderObject("Laser Beam"); }
586
- function createGoldSparkle() { return createPlaceholderObject("Gold Sparkle"); }
587
- function createSteamVent() { return createPlaceholderObject("Steam Vent"); }
588
 
589
  function onMouseMove(event) {
590
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
@@ -600,12 +1002,8 @@
600
  if (intersects.length > 0) {
601
  const intersectPoint = intersects[0].point;
602
  let newObjectToPlace = null;
603
-
604
  switch (selectedObjectType) {
605
  case "Simple House": newObjectToPlace = createSimpleHouse(); break;
606
- case "Tree": newObjectToPlace = createTree(); break;
607
- case "Rock": newObjectToPlace = createRock(); break;
608
- case "Fence Post": newObjectToPlace = createFencePost(); break;
609
  case "Cyberpunk Wall Panel": newObjectToPlace = createCyberpunkWallPanel(); break;
610
  case "Modular Hab Block": newObjectToPlace = createModularHabBlock(); break;
611
  case "MegaCorp Skyscraper": newObjectToPlace = createMegaCorpSkyscraper(); break;
@@ -614,22 +1012,60 @@
614
  case "House Roof Section": newObjectToPlace = createHouseRoofSection(); break;
615
  case "Concrete Bunker Wall": newObjectToPlace = createConcreteBunkerWall(); break;
616
  case "Damaged House Facade": newObjectToPlace = createDamagedHouseFacade(); break;
 
 
617
  case "Pine Tree": newObjectToPlace = createPineTree(); break;
618
  case "Boulder": newObjectToPlace = createBoulder(); break;
619
  case "Alien Plant": newObjectToPlace = createAlienPlant(); break;
620
  case "Floating Rock Platform": newObjectToPlace = createFloatingRockPlatform(); break;
621
  case "Rubble Pile": newObjectToPlace = createRubblePile(); break;
 
622
  case "Rooftop AC Unit": newObjectToPlace = createRooftopACUnit(); break;
623
- case "Holographic Window Display": newObjectხ4>0.5, 0.5)).toFixed(2);
624
- const newObjectToPlace = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshStandardMaterial({color: 0xff0000}));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  newObjectToPlace.position.copy(intersectPoint);
626
  newObjectToPlace.position.y += 0.01;
627
- if (window.CUSTOM_SCALE && window.CUSTOM_SCALE !== 1.0) {
628
- newObjectToPlace.scale.set(window.CUSTOM_SCALE, window.CUSTOM_SCALE, window.CUSTOM_SCALE);
629
- }
630
- if (window.CUSTOM_ROTATION_Y) {
631
- newObjectToPlace.rotation.y = window.CUSTOM_ROTATION_Y;
632
- }
633
  scene.add(newObjectToPlace);
634
  newlyPlacedObjects.push(newObjectToPlace);
635
  saveUnsavedState();
@@ -638,13 +1074,8 @@
638
  }
639
  }
640
 
641
- function onKeyDown(event) {
642
- keysPressed[event.code] = true;
643
- }
644
-
645
- function onKeyUp(event) {
646
- keysPressed[event.code] = false;
647
- }
648
 
649
  function teleportPlayer(targetX, targetZ) {
650
  console.log(`JS teleportPlayer called with targetX: ${targetX}, targetZ: ${targetZ}`);
@@ -682,7 +1113,23 @@
682
  }
683
 
684
  function getSaveDataForNamedPlot() {
685
- return getSaveDataAndPosition();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
  }
687
 
688
  function resetNewlyPlacedObjects() {
 
5
  <style>
6
  body { margin: 0; overflow: hidden; }
7
  canvas { display: block; }
 
 
 
 
 
 
 
8
  </style>
9
+ <!-- New: Polling function for game state -->
10
  <script>
11
+ // Poll the shared game state every 5 seconds (for demonstration)
12
  function pollGameState() {
13
  console.log("Polling updated game state:", window.GAME_STATE);
14
+ // Here you could update the scene based on the new state.
15
  }
16
  setInterval(pollGameState, 5000);
17
  </script>
18
  </head>
19
  <body>
 
 
20
  <script type="importmap">
21
  {
22
  "imports": {
 
33
  let raycaster, mouse;
34
  const keysPressed = {};
35
  const playerSpeed = 0.15;
36
+ let newlyPlacedObjects = []; // Track objects added THIS session for saving
37
  const placeholderPlots = new Set();
38
+ const groundMeshes = {}; // Store ground mesh references
39
+
40
+ // --- Session Storage Key ---
41
  const SESSION_STORAGE_KEY = 'unsavedInfiniteWorldState';
42
+
43
+ // --- Injected State from Streamlit ---
44
  const allInitialObjects = window.ALL_INITIAL_OBJECTS || [];
45
  const plotsMetadata = window.PLOTS_METADATA || [];
46
  const selectedObjectType = window.SELECTED_OBJECT_TYPE || "None";
47
  const customScale = window.CUSTOM_SCALE || 1.0;
48
+ const customRotationY = window.CUSTOM_ROTATION_Y || 0.0;
49
  const plotWidth = window.PLOT_WIDTH || 50.0;
50
  const plotDepth = window.PLOT_DEPTH || 50.0;
51
 
52
+ const groundMaterial = new THREE.MeshStandardMaterial({
53
+ color: 0x55aa55, roughness: 0.9, metalness: 0.1, side: THREE.DoubleSide
54
+ });
55
+ const placeholderGroundMaterial = new THREE.MeshStandardMaterial({
56
+ color: 0x448844, roughness: 0.95, metalness: 0.1, side: THREE.DoubleSide
57
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
  function init() {
60
  scene = new THREE.Scene();
 
81
  loadInitialObjects();
82
  restoreUnsavedState();
83
 
84
+ // Event Listeners
85
  document.addEventListener('mousemove', onMouseMove, false);
86
  document.addEventListener('click', onDocumentClick, false);
87
  window.addEventListener('resize', onWindowResize, false);
88
  document.addEventListener('keydown', onKeyDown);
89
  document.addEventListener('keyup', onKeyUp);
90
 
91
+ // Define functions callable by Streamlit
92
  window.teleportPlayer = teleportPlayer;
93
  window.getSaveDataAndPosition = getSaveDataAndPosition;
94
  window.getSaveDataForNamedPlot = getSaveDataForNamedPlot;
 
131
  if (groundMeshes[gridKey]) return;
132
  console.log(`Creating ${isPlaceholder ? 'placeholder' : 'initial'} ground at ${gridX}, ${gridZ}`);
133
  const groundGeometry = new THREE.PlaneGeometry(plotWidth, plotDepth);
134
+ const material = isPlaceholder ? placeholderGroundMaterial : groundMaterial;
135
  const groundMesh = new THREE.Mesh(groundGeometry, material);
136
  groundMesh.rotation.x = -Math.PI / 2;
137
  groundMesh.position.y = -0.05;
 
160
  console.log("Finished loading initial objects.");
161
  }
162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  function createAndPlaceObject(objData, isNewObject) {
164
  let loadedObject = null;
165
  switch (objData.type) {
166
  case "Simple House": loadedObject = createSimpleHouse(); break;
 
 
 
167
  case "Cyberpunk Wall Panel": loadedObject = createCyberpunkWallPanel(); break;
168
  case "Modular Hab Block": loadedObject = createModularHabBlock(); break;
169
  case "MegaCorp Skyscraper": loadedObject = createMegaCorpSkyscraper(); break;
 
172
  case "House Roof Section": loadedObject = createHouseRoofSection(); break;
173
  case "Concrete Bunker Wall": loadedObject = createConcreteBunkerWall(); break;
174
  case "Damaged House Facade": loadedObject = createDamagedHouseFacade(); break;
175
+ case "Tree": loadedObject = createTree(); break;
176
+ case "Rock": loadedObject = createRock(); break;
177
  case "Pine Tree": loadedObject = createPineTree(); break;
178
  case "Boulder": loadedObject = createBoulder(); break;
179
  case "Alien Plant": loadedObject = createAlienPlant(); break;
180
  case "Floating Rock Platform": loadedObject = createFloatingRockPlatform(); break;
181
  case "Rubble Pile": loadedObject = createRubblePile(); break;
182
+ case "Fence Post": loadedObject = createFencePost(); break;
183
  case "Rooftop AC Unit": loadedObject = createRooftopACUnit(); break;
184
  case "Holographic Window Display": loadedObject = createHolographicWindowDisplay(); break;
185
  case "Jersey Barrier": loadedObject = createJerseyBarrier(); break;
 
221
  case "Steam Vent": loadedObject = createSteamVent(); break;
222
  default: console.warn("Unknown object type in data:", objData.type); break;
223
  }
 
224
  if (loadedObject) {
225
  if (objData.position && objData.position.x !== undefined) {
226
  loadedObject.position.set(objData.position.x, objData.position.y, objData.position.z);
227
  } else if (objData.pos_x !== undefined) {
228
  loadedObject.position.set(objData.pos_x, objData.pos_y, objData.pos_z);
229
  }
 
230
  if (objData.rotation) {
231
  loadedObject.rotation.set(objData.rotation._x, objData.rotation._y, objData.rotation._z, objData.rotation._order || 'XYZ');
232
  } else if (objData.rot_x !== undefined) {
233
  loadedObject.rotation.set(objData.rot_x, objData.rot_y, objData.rot_z, objData.rot_order || 'XYZ');
234
  }
 
 
 
 
 
 
 
 
 
235
  loadedObject.userData.obj_id = objData.obj_id || loadedObject.userData.obj_id;
236
  scene.add(loadedObject);
237
  if (isNewObject) newlyPlacedObjects.push(loadedObject);
 
240
  return null;
241
  }
242
 
243
+ function saveUnsavedState() {
244
+ try {
245
+ const stateToSave = newlyPlacedObjects.map(obj => ({
246
+ obj_id: obj.userData.obj_id,
247
+ type: obj.userData.type,
248
+ position: { x: obj.position.x, y: obj.position.y, z: obj.position.z },
249
+ rotation: { _x: obj.rotation.x, _y: obj.rotation.y, _z: obj.rotation.z, _order: obj.rotation.order }
250
+ }));
251
+ sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(stateToSave));
252
+ console.log(`Saved ${stateToSave.length} unsaved objects to sessionStorage.`);
253
+ } catch (e) {
254
+ console.error("Error saving state to sessionStorage:", e);
255
+ }
256
+ }
257
+
258
+ function restoreUnsavedState() {
259
+ try {
260
+ const savedState = sessionStorage.getItem(SESSION_STORAGE_KEY);
261
+ if (savedState) {
262
+ console.log("Found unsaved state in sessionStorage. Restoring...");
263
+ const objectsToRestore = JSON.parse(savedState);
264
+ if (Array.isArray(objectsToRestore)) {
265
+ newlyPlacedObjects = [];
266
+ let count = 0;
267
+ objectsToRestore.forEach(objData => {
268
+ if(createAndPlaceObject(objData, true)) { count++; }
269
+ });
270
+ console.log(`Restored ${count} objects.`);
271
+ }
272
+ } else {
273
+ console.log("No unsaved state found in sessionStorage.");
274
+ }
275
+ } catch (e) {
276
+ console.error("Error restoring state from sessionStorage:", e);
277
+ sessionStorage.removeItem(SESSION_STORAGE_KEY);
278
+ }
279
+ }
280
+
281
+ function clearUnsavedState() {
282
+ sessionStorage.removeItem(SESSION_STORAGE_KEY);
283
+ newlyPlacedObjects = [];
284
+ console.log("Cleared unsaved state from memory and sessionStorage.");
285
+ }
286
+
287
+ function createObjectBase(type) {
288
+ return { userData: { type: type, obj_id: THREE.MathUtils.generateUUID() } };
289
+ }
290
+
291
+ // --- Existing Object Creation Functions ---
292
  function createSimpleHouse() {
293
  const base = createObjectBase("Simple House");
294
  const group = new THREE.Group();
 
351
  return post;
352
  }
353
 
354
+ // --- New Object Creation Functions ---
355
+ // Buildings
356
  function createCyberpunkWallPanel() {
357
  const base = createObjectBase("Cyberpunk Wall Panel");
358
+ const mat = new THREE.MeshStandardMaterial({color:0x00b7eb, roughness:0.5, metalness:0.8});
359
+ const panel = new THREE.Mesh(new THREE.BoxGeometry(2,3,0.2), mat);
360
+ Object.assign(panel, base);
361
+ panel.position.y = 1.5;
362
  panel.castShadow = true;
363
  panel.receiveShadow = true;
364
+ return panel;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
  }
366
 
367
  function createModularHabBlock() {
368
  const base = createObjectBase("Modular Hab Block");
369
  const group = new THREE.Group();
370
  Object.assign(group, base);
371
+ const mat = new THREE.MeshStandardMaterial({color:0x666666, roughness:0.7});
372
+ const block = new THREE.Mesh(new THREE.BoxGeometry(3,2,3), mat);
373
+ block.position.y = 1;
374
+ block.castShadow = true;
375
+ block.receiveShadow = true;
376
+ group.add(block);
377
+ return group;
378
+ }
379
+
380
+ function createMegaCorpSkyscraper() {
381
+ const base = createObjectBase("MegaCorp Skyscraper");
382
+ const group = new THREE.Group();
383
+ Object.assign(group, base);
384
+ const mat = new THREE.MeshStandardMaterial({color:0x333399, roughness:0.4, metalness:0.9});
385
+ const tower = new THREE.Mesh(new THREE.BoxGeometry(4,10,4), mat);
386
+ tower.position.y = 5;
387
+ tower.castShadow = true;
388
+ tower.receiveShadow = true;
389
+ group.add(tower);
390
+ return group;
391
+ }
392
+
393
+ function createCastleWallSection() {
394
+ const base = createObjectBase("Castle Wall Section");
395
+ const mat = new THREE.MeshStandardMaterial({color:0x808080, roughness:0.8});
396
+ const wall = new THREE.Mesh(new THREE.BoxGeometry(5,3,1), mat);
397
+ Object.assign(wall, base);
398
+ wall.position.y = 1.5;
399
+ wall.castShadow = true;
400
+ wall.receiveShadow = true;
401
+ return wall;
402
+ }
403
+
404
+ function createWoodenDoor() {
405
+ const base = createObjectBase("Wooden Door");
406
+ const mat = new THREE.MeshStandardMaterial({color:0x8b4513, roughness:0.9});
407
+ const door = new THREE.Mesh(new THREE.BoxGeometry(1,2,0.2), mat);
408
+ Object.assign(door, base);
409
+ door.position.y = 1;
410
+ door.castShadow = true;
411
+ door.receiveShadow = true;
412
+ return door;
413
+ }
414
+
415
+ function createHouseRoofSection() {
416
+ const base = createObjectBase("House Roof Section");
417
+ const mat = new THREE.MeshStandardMaterial({color:0x800000, roughness:0.7});
418
+ const roof = new THREE.Mesh(new THREE.ConeGeometry(2,1,4), mat);
419
+ Object.assign(roof, base);
420
+ roof.position.y = 1;
421
+ roof.rotation.y = Math.PI/4;
422
+ roof.castShadow = true;
423
+ roof.receiveShadow = true;
424
+ return roof;
425
+ }
426
+
427
+ function createConcreteBunkerWall() {
428
+ const base = createObjectBase("Concrete Bunker Wall");
429
+ const mat = new THREE.MeshStandardMaterial({color:0x555555, roughness:0.9});
430
+ const wall = new THREE.Mesh(new THREE.BoxGeometry(4,2,1), mat);
431
+ Object.assign(wall, base);
432
+ wall.position.y = 1;
433
+ wall.castShadow = true;
434
+ wall.receiveShadow = true;
435
+ return wall;
436
+ }
437
+
438
+ function createDamagedHouseFacade() {
439
+ const base = createObjectBase("Damaged House Facade");
440
+ const mat = new THREE.MeshStandardMaterial({color:0xaaaaaa, roughness:0.9});
441
+ const facade = new THREE.Mesh(new THREE.BoxGeometry(3,2,0.3), mat);
442
+ Object.assign(facade, base);
443
+ facade.position.y = 1;
444
+ facade.castShadow = true;
445
+ facade.receiveShadow = true;
446
+ return facade;
447
+ }
448
+
449
+ // Nature
450
+ function createPineTree() {
451
+ const base = createObjectBase("Pine Tree");
452
+ const group = new THREE.Group();
453
+ Object.assign(group, base);
454
+ const mat1 = new THREE.MeshStandardMaterial({color:0x8b4513, roughness:0.9});
455
+ const mat2 = new THREE.MeshStandardMaterial({color:0x006400, roughness:0.8});
456
+ const trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.2,0.3,1.5,8), mat1);
457
+ trunk.position.y = 0.75;
458
+ trunk.castShadow = true;
459
+ trunk.receiveShadow = true;
460
+ group.add(trunk);
461
+ const foliage = new THREE.Mesh(new THREE.ConeGeometry(1,2,8), mat2);
462
+ foliage.position.y = 2;
463
+ foliage.castShadow = true;
464
+ foliage.receiveShadow = true;
465
+ group.add(foliage);
466
+ return group;
467
+ }
468
+
469
+ function createBoulder() {
470
+ const base = createObjectBase("Boulder");
471
+ const mat = new THREE.MeshStandardMaterial({color:0x666666, roughness:0.8});
472
+ const boulder = new THREE.Mesh(new THREE.IcosahedronGeometry(1,0), mat);
473
+ Object.assign(boulder, base);
474
+ boulder.position.y = 0.5;
475
+ boulder.rotation.set(Math.random()*Math.PI, Math.random()*Math.PI, 0);
476
+ boulder.castShadow = true;
477
+ boulder.receiveShadow = true;
478
+ return boulder;
479
+ }
480
+
481
+ function createAlienPlant() {
482
+ const base = createObjectBase("Alien Plant");
483
+ const group = new THREE.Group();
484
+ Object.assign(group, base);
485
+ const mat = new THREE.MeshStandardMaterial({color:0x00ff00, roughness:0.7});
486
+ const stem = new THREE.Mesh(new THREE.CylinderGeometry(0.1,0.1,1,8), mat);
487
+ stem.position.y = 0.5;
488
+ stem.castShadow = true;
489
+ stem.receiveShadow = true;
490
+ group.add(stem);
491
+ const head = new THREE.Mesh(new THREE.SphereGeometry(0.3,8,8), mat);
492
+ head.position.y = 1;
493
+ head.castShadow = true;
494
+ head.receiveShadow = true;
495
+ group.add(head);
496
+ return group;
497
+ }
498
+
499
+ function createFloatingRockPlatform() {
500
+ const base = createObjectBase("Floating Rock Platform");
501
+ const mat = new THREE.MeshStandardMaterial({color:0x555555, roughness:0.8});
502
+ const platform = new THREE.Mesh(new THREE.BoxGeometry(3,0.5,3), mat);
503
+ Object.assign(platform, base);
504
+ platform.position.y = 2;
505
+ platform.castShadow = true;
506
+ platform.receiveShadow = true;
507
+ return platform;
508
+ }
509
+
510
+ function createRubblePile() {
511
+ const base = createObjectBase("Rubble Pile");
512
+ const group = new THREE.Group();
513
+ Object.assign(group, base);
514
+ const mat = new THREE.MeshStandardMaterial({color:0x777777, roughness:0.9});
515
+ for (let i = 0; i < 5; i++) {
516
+ const rock = new THREE.Mesh(new THREE.IcosahedronGeometry(0.3,0), mat);
517
+ rock.position.set(Math.random()*0.5-0.25, Math.random()*0.3, Math.random()*0.5-0.25);
518
+ rock.castShadow = true;
519
+ rock.receiveShadow = true;
520
+ group.add(rock);
521
+ }
522
  return group;
523
  }
524
 
525
+ // Props
526
+ function createRooftopACUnit() {
527
+ const base = createObjectBase("Rooftop AC Unit");
528
+ const mat = new THREE.MeshStandardMaterial({color:0x666666, roughness:0.7, metalness:0.5});
529
+ const unit = new THREE.Mesh(new THREE.BoxGeometry(1,0.8,1), mat);
530
+ Object.assign(unit, base);
531
+ unit.position.y = 0.4;
532
+ unit.castShadow = true;
533
+ unit.receiveShadow = true;
534
+ return unit;
535
+ }
536
+
537
+ function createHolographicWindowDisplay() {
538
+ const base = createObjectBase("Holographic Window Display");
539
+ const mat = new THREE.MeshStandardMaterial({color:0x00b7eb, roughness:0.5, transparent: true, opacity: 0.7});
540
+ const display = new THREE.Mesh(new THREE.PlaneGeometry(1,1), mat);
541
+ Object.assign(display, base);
542
+ display.position.y = 1;
543
+ display.castShadow = false;
544
+ display.receiveShadow = false;
545
+ return display;
546
+ }
547
+
548
+ function createJerseyBarrier() {
549
+ const base = createObjectBase("Jersey Barrier");
550
+ const mat = new THREE.MeshStandardMaterial({color:0x888888, roughness:0.9});
551
+ const barrier = new THREE.Mesh(new THREE.BoxGeometry(2,0.8,0.5), mat);
552
+ Object.assign(barrier, base);
553
+ barrier.position.y = 0.4;
554
+ barrier.castShadow = true;
555
+ barrier.receiveShadow = true;
556
+ return barrier;
557
+ }
558
+
559
+ function createOilDrum() {
560
+ const base = createObjectBase("Oil Drum");
561
+ const mat = new THREE.MeshStandardMaterial({color:0x555555, roughness:0.7, metalness:0.3});
562
+ const drum = new THREE.Mesh(new THREE.CylinderGeometry(0.4,0.4,1,12), mat);
563
+ Object.assign(drum, base);
564
+ drum.position.y = 0.5;
565
+ drum.castShadow = true;
566
+ drum.receiveShadow = true;
567
+ return drum;
568
+ }
569
+
570
+ function createCannedFood() {
571
+ const base = createObjectBase("Canned Food");
572
+ const mat = new THREE.MeshStandardMaterial({color:0xaaaaaa, roughness:0.7, metalness:0.5});
573
+ const can = new THREE.Mesh(new THREE.CylinderGeometry(0.1,0.1,0.2,12), mat);
574
+ Object.assign(can, base);
575
+ can.position.y = 0.1;
576
+ can.castShadow = true;
577
+ can.receiveShadow = true;
578
+ return can;
579
+ }
580
+
581
+ function createTreasureChest() {
582
+ const base = createObjectBase("Treasure Chest");
583
+ const mat = new THREE.MeshStandardMaterial({color:0x8b4513, roughness:0.8});
584
+ const chest = new THREE.Mesh(new THREE.BoxGeometry(1,0.6,0.8), mat);
585
+ Object.assign(chest, base);
586
+ chest.position.y = 0.3;
587
+ chest.castShadow = true;
588
+ chest.receiveShadow = true;
589
+ return chest;
590
+ }
591
+
592
+ function createWallTorch() {
593
+ const base = createObjectBase("Wall Torch");
594
+ const group = new THREE.Group();
595
+ Object.assign(group, base);
596
+ const mat1 = new THREE.MeshStandardMaterial({color:0xdeb887, roughness:0.9});
597
+ const mat2 = new THREE.MeshStandardMaterial({color:0xff4500, roughness:0.5});
598
+ const holder = new THREE.Mesh(new THREE.BoxGeometry(0.1,0.5,0.1), mat1);
599
+ holder.position.y = 0.25;
600
+ holder.castShadow = true;
601
+ holder.receiveShadow = true;
602
+ group.add(holder);
603
+ const flame = new THREE.Mesh(new THREE.SphereGeometry(0.1,8,8), mat2);
604
+ flame.position.y = 0.5;
605
+ flame.castShadow = true;
606
+ flame.receiveShadow = false;
607
+ group.add(flame);
608
+ return group;
609
+ }
610
+
611
+ function createBonePile() {
612
+ const base = createObjectBase("Bone Pile");
613
+ const group = new THREE.Group();
614
+ Object.assign(group, base);
615
+ const mat = new THREE.MeshStandardMaterial({color:0xdddddd, roughness:0.9});
616
+ for (let i = 0; i < 3; i++) {
617
+ const bone = new THREE.Mesh(new THREE.CylinderGeometry(0.05,0.05,0.3,8), mat);
618
+ bone.position.set(Math.random()*0.2-0.1, Math.random()*0.1, Math.random()*0.2-0.1);
619
+ bone.rotation.set(Math.random()*Math.PI, Math.random()*Math.PI, Math.random()*Math.PI);
620
+ bone.castShadow = true;
621
+ bone.receiveShadow = true;
622
+ group.add(bone);
623
+ }
624
+ return group;
625
+ }
626
+
627
+ // Characters
628
+ function createKingFigure() {
629
+ const base = createObjectBase("King Figure");
630
+ const mat = new THREE.MeshStandardMaterial({color:0xffd700, roughness:0.6});
631
+ const figure = new THREE.Mesh(new THREE.CapsuleGeometry(0.2,0.6,4,8), mat);
632
+ Object.assign(figure, base);
633
+ figure.position.y = 0.4;
634
+ figure.castShadow = true;
635
+ figure.receiveShadow = true;
636
+ return figure;
637
+ }
638
+
639
+ function createSoldierFigure() {
640
+ const base = createObjectBase("Soldier Figure");
641
+ const mat = new THREE.MeshStandardMaterial({color:0x228b22, roughness:0.7});
642
+ const figure = new THREE.Mesh(new THREE.CapsuleGeometry(0.2,0.6,4,8), mat);
643
+ Object.assign(figure, base);
644
+ figure.position.y = 0.4;
645
+ figure.castShadow = true;
646
+ figure.receiveShadow = true;
647
+ return figure;
648
+ }
649
+
650
+ function createMageFigure() {
651
+ const base = createObjectBase("Mage Figure");
652
+ const mat = new THREE.MeshStandardMaterial({color:0x800080, roughness:0.7});
653
+ const figure = new THREE.Mesh(new THREE.CapsuleGeometry(0.2,0.6,4,8), mat);
654
+ Object.assign(figure, base);
655
+ figure.position.y = 0.4;
656
+ figure.castShadow = true;
657
+ figure.receiveShadow = true;
658
+ return figure;
659
+ }
660
+
661
+ function createZombieFigure() {
662
+ const base = createObjectBase("Zombie Figure");
663
+ const mat = new THREE.MeshStandardMaterial({color:0x90ee90, roughness:0.8});
664
+ const figure = new THREE.Mesh(new THREE.CapsuleGeometry(0.2,0.6,4,8), mat);
665
+ Object.assign(figure, base);
666
+ figure.position.y = 0.4;
667
+ figure.castShadow = true;
668
+ figure.receiveShadow = true;
669
+ return figure;
670
+ }
671
+
672
+ function createSurvivorFigure() {
673
+ const base = createObjectBase("Survivor Figure");
674
+ const mat = new THREE.MeshStandardMaterial({color:0x4682b4, roughness:0.7});
675
+ const figure = new THREE.Mesh(new THREE.CapsuleGeometry(0.2,0.6,4,8), mat);
676
+ Object.assign(figure, base);
677
+ figure.position.y = 0.4;
678
+ figure.castShadow = true;
679
+ figure.receiveShadow = true;
680
+ return figure;
681
+ }
682
+
683
+ function createDwarfMinerFigure() {
684
+ const base = createObjectBase("Dwarf Miner Figure");
685
+ const mat = new THREE.MeshStandardMaterial({color:0x8b4513, roughness:0.7});
686
+ const figure = new THREE.Mesh(new THREE.CapsuleGeometry(0.2,0.5,4,8), mat);
687
+ Object.assign(figure, base);
688
+ figure.position.y = 0.35;
689
+ figure.castShadow = true;
690
+ figure.receiveShadow = true;
691
+ return figure;
692
+ }
693
+
694
+ function createUndeadKnightFigure() {
695
+ const base = createObjectBase("Undead Knight Figure");
696
+ const mat = new THREE.MeshStandardMaterial({color:0x555555, roughness:0.8});
697
+ const figure = new THREE.Mesh(new THREE.CapsuleGeometry(0.2,0.6,4,8), mat);
698
+ Object.assign(figure, base);
699
+ figure.position.y = 0.4;
700
+ figure.castShadow = true;
701
+ figure.receiveShadow = true;
702
+ return figure;
703
+ }
704
+
705
+ function createHeroFigure() {
706
+ const base = createObjectBase("Hero Figure");
707
+ const mat = new THREE.MeshStandardMaterial({color:0xff0000, roughness:0.6});
708
+ const figure = new THREE.Mesh(new THREE.CapsuleGeometry(0.2,0.6,4,8), mat);
709
+ Object.assign(figure, base);
710
+ figure.position.y = 0.4;
711
+ figure.castShadow = true;
712
+ figure.receiveShadow = true;
713
+ return figure;
714
+ }
715
+
716
+ // Vehicles
717
+ function createWoodenCart() {
718
+ const base = createObjectBase("Wooden Cart");
719
+ const mat = new THREE.MeshStandardMaterial({color:0x8b4513, roughness:0.8});
720
+ const cart = new THREE.Mesh(new THREE.BoxGeometry(2,0.8,1), mat);
721
+ Object.assign(cart, base);
722
+ cart.position.y = 0.4;
723
+ cart.castShadow = true;
724
+ cart.receiveShadow = true;
725
+ return cart;
726
+ }
727
+
728
+ function createBallista() {
729
+ const base = createObjectBase("Ballista");
730
+ const mat = new THREE.MeshStandardMaterial({color:0xdeb887, roughness:0.8});
731
+ const ballista = new THREE.Mesh(new THREE.BoxGeometry(1.5,0.8,0.5), mat);
732
+ Object.assign(ballista, base);
733
+ ballista.position.y = 0.4;
734
+ ballista.castShadow = true;
735
+ ballista.receiveShadow = true;
736
+ return ballista;
737
+ }
738
+
739
+ function createSiegeTower() {
740
+ const base = createObjectBase("Siege Tower");
741
+ const mat = new THREE.MeshStandardMaterial({color:0x8b4513, roughness:0.8});
742
+ const tower = new THREE.Mesh(new THREE.BoxGeometry(2,4,2), mat);
743
+ Object.assign(tower, base);
744
+ tower.position.y = 2;
745
+ tower.castShadow = true;
746
+ tower.receiveShadow = true;
747
+ return tower;
748
+ }
749
+
750
+ function createBuggyFrame() {
751
+ const base = createObjectBase("Buggy Frame");
752
+ const mat = new THREE.MeshStandardMaterial({color:0x666666, roughness:0.7, metalness:0.5});
753
+ const frame = new THREE.Mesh(new THREE.BoxGeometry(2,0.5,1), mat);
754
+ Object.assign(frame, base);
755
+ frame.position.y = 0.25;
756
+ frame.castShadow = true;
757
+ frame.receiveShadow = true;
758
+ return frame;
759
+ }
760
+
761
+ function createMotorbike() {
762
+ const base = createObjectBase("Motorbike");
763
+ const mat = new THREE.MeshStandardMaterial({color:0x333333, roughness:0.7, metalness:0.5});
764
+ const bike = new THREE.Mesh(new THREE.BoxGeometry(1.5,0.6,0.4), mat);
765
+ Object.assign(bike, base);
766
+ bike.position.y = 0.3;
767
+ bike.castShadow = true;
768
+ bike.receiveShadow = true;
769
+ return bike;
770
+ }
771
+
772
+ function createHoverBike() {
773
+ const base = createObjectBase("Hover Bike");
774
+ const mat = new THREE.MeshStandardMaterial({color:0x00b7eb, roughness:0.5, metalness:0.8});
775
+ const bike = new THREE.Mesh(new THREE.BoxGeometry(1.5,0.4,0.4), mat);
776
+ Object.assign(bike, base);
777
+ bike.position.y = 0.5;
778
+ bike.castShadow = true;
779
+ bike.receiveShadow = true;
780
+ return bike;
781
+ }
782
+
783
+ function createAPC() {
784
+ const base = createObjectBase("APC");
785
+ const mat = new THREE.MeshStandardMaterial({color:0x2f4f4f, roughness:0.7, metalness:0.5});
786
+ const apc = new THREE.Mesh(new THREE.BoxGeometry(3,1.5,1.5), mat);
787
+ Object.assign(apc, base);
788
+ apc.position.y = 0.75;
789
+ apc.castShadow = true;
790
+ apc.receiveShadow = true;
791
+ return apc;
792
+ }
793
+
794
+ function createSandBoat() {
795
+ const base = createObjectBase("Sand Boat");
796
+ const mat = new THREE.MeshStandardMaterial({color:0xdeb887, roughness:0.8});
797
+ const boat = new THREE.Mesh(new THREE.BoxGeometry(2,0.5,1), mat);
798
+ Object.assign(boat, base);
799
+ boat.position.y = 0.25;
800
+ boat.castShadow = true;
801
+ boat.receiveShadow = true;
802
+ return boat;
803
+ }
804
+
805
+ // Weapons
806
+ function createMakeshiftMachete() {
807
+ const base = createObjectBase("Makeshift Machete");
808
+ const mat = new THREE.MeshStandardMaterial({color:0x777777, roughness:0.7, metalness:0.5});
809
+ const machete = new THREE.Mesh(new THREE.BoxGeometry(0.8,0.05,0.2), mat);
810
+ Object.assign(machete, base);
811
+ machete.position.y = 0.1;
812
+ machete.castShadow = true;
813
+ machete.receiveShadow = true;
814
+ return machete;
815
+ }
816
+
817
+ function createPistolBody() {
818
+ const base = createObjectBase("Pistol Body");
819
+ const mat = new THREE.MeshStandardMaterial({color:0x333333, roughness:0.7, metalness:0.5});
820
+ const pistol = new THREE.Mesh(new THREE.BoxGeometry(0.5,0.2,0.3), mat);
821
+ Object.assign(pistol, base);
822
+ pistol.position.y = 0.1;
823
+ pistol.castShadow = true;
824
+ pistol.receiveShadow = true;
825
+ return pistol;
826
+ }
827
+
828
+ function createScopeAttachment() {
829
+ const base = createObjectBase("Scope Attachment");
830
+ const mat = new THREE.MeshStandardMaterial({color:0x555555, roughness:0.7, metalness:0.5});
831
+ const scope = new THREE.Mesh(new THREE.CylinderGeometry(0.05,0.05,0.3,8), mat);
832
+ Object.assign(scope, base);
833
+ scope.position.y = 0.15;
834
+ scope.rotation.z = Math.PI/2;
835
+ scope.castShadow = true;
836
+ scope.receiveShadow = true;
837
+ return scope;
838
+ }
839
+
840
+ function createLaserPistol() {
841
+ const base = createObjectBase("Laser Pistol");
842
+ const mat = new THREE.MeshStandardMaterial({color:0x00b7eb, roughness:0.5, metalness:0.8});
843
+ const pistol = new THREE.Mesh(new THREE.BoxGeometry(0.5,0.2,0.3), mat);
844
+ Object.assign(pistol, base);
845
+ pistol.position.y = 0.1;
846
+ pistol.castShadow = true;
847
+ pistol.receiveShadow = true;
848
+ return pistol;
849
+ }
850
+
851
+ function createEnergySword() {
852
+ const base = createObjectBase("Energy Sword");
853
+ const mat = new THREE.MeshStandardMaterial({color:0x00ff00, roughness:0.5, transparent: true, opacity: 0.8});
854
+ const sword = new THREE.Mesh(new THREE.BoxGeometry(0.8,0.05,0.2), mat);
855
+ Object.assign(sword, base);
856
+ sword.position.y = 0.1;
857
+ sword.castShadow = true;
858
+ sword.receiveShadow = true;
859
+ return sword;
860
+ }
861
+
862
+ function createDwarvenAxe() {
863
+ const base = createObjectBase("Dwarven Axe");
864
+ const mat = new THREE.MeshStandardMaterial({color:0x555555, roughness:0.7, metalness:0.5});
865
+ const axe = new THREE.Mesh(new THREE.BoxGeometry(0.5,0.3,0.1), mat);
866
+ Object.assign(axe, base);
867
+ axe.position.y = 0.15;
868
+ axe.castShadow = true;
869
+ axe.receiveShadow = true;
870
+ return axe;
871
+ }
872
+
873
  function createMagicStaff() {
874
  const base = createObjectBase("Magic Staff");
875
  const group = new THREE.Group();
876
  Object.assign(group, base);
877
+ const mat1 = new THREE.MeshStandardMaterial({color:0x8b4513, roughness:0.8});
878
+ const mat2 = new THREE.MeshStandardMaterial({color:0xff00ff, roughness:0.5});
879
+ const shaft = new THREE.Mesh(new THREE.CylinderGeometry(0.05,0.05,1.5,8), mat1);
880
+ shaft.position.y = 0.75;
881
+ shaft.castShadow = true;
882
+ shaft.receiveShadow = true;
883
+ group.add(shaft);
884
+ const gem = new THREE.Mesh(new THREE.SphereGeometry(0.1,8,8), mat2);
885
+ gem.position.y = 1.5;
886
+ gem.castShadow = true;
887
+ gem.receiveShadow = true;
888
+ group.add(gem);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
889
  return group;
890
  }
891
 
892
+ // Effects
893
  function createCandleFlame() {
894
  const base = createObjectBase("Candle Flame");
895
+ const mat = new THREE.MeshStandardMaterial({color:0xff4500, roughness:0.5, transparent: true, opacity: 0.8});
896
+ const flame = new THREE.Mesh(new THREE.SphereGeometry(0.1,8,8), mat);
897
+ Object.assign(flame, base);
898
+ flame.position.y = 0.1;
899
+ flame.castShadow = true;
900
+ flame.receiveShadow = false;
901
+ return flame;
902
+ }
903
+
904
+ function createDustCloud() {
905
+ const base = createObjectBase("Dust Cloud");
906
+ const mat = new THREE.MeshStandardMaterial({color:0xaaaaaa, roughness:0.9, transparent: true, opacity: 0.5});
907
+ const cloud = new THREE.Mesh(new THREE.SphereGeometry(0.5,8,8), mat);
908
+ Object.assign(cloud, base);
909
+ cloud.position.y = 0.25;
910
+ cloud.castShadow = false;
911
+ cloud.receiveShadow = false;
912
+ return cloud;
913
+ }
914
+
915
+ function createBloodSplatDecal() {
916
+ const base = createObjectBase("Blood Splat Decal");
917
+ const mat = new THREE.MeshStandardMaterial({color:0x8b0000, roughness:0.9, transparent: true, opacity: 0.8});
918
+ const splat = new THREE.Mesh(new THREE.PlaneGeometry(0.5,0.5), mat);
919
+ Object.assign(splat, base);
920
+ splat.position.y = 0.01;
921
+ splat.rotation.x = -Math.PI/2;
922
+ splat.castShadow = false;
923
+ splat.receiveShadow = true;
924
+ return splat;
925
+ }
926
+
927
+ function createBurningBarrelFire() {
928
+ const base = createObjectBase("Burning Barrel Fire");
929
  const group = new THREE.Group();
930
  Object.assign(group, base);
931
+ const mat1 = new THREE.MeshStandardMaterial({color:0x555555, roughness:0.7, metalness:0.5});
932
+ const mat2 = new THREE.MeshStandardMaterial({color:0xff4500, roughness:0.5, transparent: true, opacity: 0.8});
933
+ const barrel = new THREE.Mesh(new THREE.CylinderGeometry(0.4,0.4,1,12), mat1);
934
+ barrel.position.y = 0.5;
935
+ barrel.castShadow = true;
936
+ barrel.receiveShadow = true;
937
+ group.add(barrel);
938
+ const fire = new THREE.Mesh(new THREE.SphereGeometry(0.3,8,8), mat2);
939
+ fire.position.y = 1;
940
+ fire.castShadow = true;
941
+ fire.receiveShadow = false;
942
+ group.add(fire);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
943
  return group;
944
  }
945
 
946
+ function createWarpTunnelEffect() {
947
+ const base = createObjectBase("Warp Tunnel Effect");
948
+ const mat = new THREE.MeshStandardMaterial({color:0x00b7eb, roughness:0.5, transparent: true, opacity: 0.7});
949
+ const tunnel = new THREE.Mesh(new THREE.CylinderGeometry(0.5,0.5,2,12), mat);
950
+ Object.assign(tunnel, base);
951
+ tunnel.position.y = 1;
952
+ tunnel.castShadow = false;
953
+ tunnel.receiveShadow = false;
954
+ return tunnel;
955
+ }
956
+
957
+ function createLaserBeam() {
958
+ const base = createObjectBase("Laser Beam");
959
+ const mat = new THREE.MeshStandardMaterial({color:0xff0000, roughness:0.5, transparent: true, opacity: 0.8});
960
+ const beam = new THREE.Mesh(new THREE.CylinderGeometry(0.05,0.05,1,8), mat);
961
+ Object.assign(beam, base);
962
+ beam.position.y = 0.5;
963
+ beam.rotation.z = Math.PI/2;
964
+ beam.castShadow = false;
965
+ beam.receiveShadow = false;
966
+ return beam;
967
+ }
968
+
969
+ function createGoldSparkle() {
970
+ const base = createObjectBase("Gold Sparkle");
971
+ const mat = new THREE.MeshStandardMaterial({color:0xffd700, roughness:0.5, transparent: true, opacity: 0.8});
972
+ const sparkle = new THREE.Mesh(new THREE.SphereGeometry(0.1,8,8), mat);
973
+ Object.assign(sparkle, base);
974
+ sparkle.position.y = 0.1;
975
+ sparkle.castShadow = false;
976
+ sparkle.receiveShadow = false;
977
+ return sparkle;
978
+ }
979
+
980
+ function createSteamVent() {
981
+ const base = createObjectBase("Steam Vent");
982
+ const mat = new THREE.MeshStandardMaterial({color:0xaaaaaa, roughness:0.9, transparent: true, opacity: 0.5});
983
+ const vent = new THREE.Mesh(new THREE.CylinderGeometry(0.1,0.1,0.5,8), mat);
984
+ Object.assign(vent, base);
985
+ vent.position.y = 0.25;
986
+ vent.castShadow = false;
987
+ vent.receiveShadow = false;
988
+ return vent;
989
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
990
 
991
  function onMouseMove(event) {
992
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
 
1002
  if (intersects.length > 0) {
1003
  const intersectPoint = intersects[0].point;
1004
  let newObjectToPlace = null;
 
1005
  switch (selectedObjectType) {
1006
  case "Simple House": newObjectToPlace = createSimpleHouse(); break;
 
 
 
1007
  case "Cyberpunk Wall Panel": newObjectToPlace = createCyberpunkWallPanel(); break;
1008
  case "Modular Hab Block": newObjectToPlace = createModularHabBlock(); break;
1009
  case "MegaCorp Skyscraper": newObjectToPlace = createMegaCorpSkyscraper(); break;
 
1012
  case "House Roof Section": newObjectToPlace = createHouseRoofSection(); break;
1013
  case "Concrete Bunker Wall": newObjectToPlace = createConcreteBunkerWall(); break;
1014
  case "Damaged House Facade": newObjectToPlace = createDamagedHouseFacade(); break;
1015
+ case "Tree": newObjectToPlace = createTree(); break;
1016
+ case "Rock": newObjectToPlace = createRock(); break;
1017
  case "Pine Tree": newObjectToPlace = createPineTree(); break;
1018
  case "Boulder": newObjectToPlace = createBoulder(); break;
1019
  case "Alien Plant": newObjectToPlace = createAlienPlant(); break;
1020
  case "Floating Rock Platform": newObjectToPlace = createFloatingRockPlatform(); break;
1021
  case "Rubble Pile": newObjectToPlace = createRubblePile(); break;
1022
+ case "Fence Post": newObjectToPlace = createFencePost(); break;
1023
  case "Rooftop AC Unit": newObjectToPlace = createRooftopACUnit(); break;
1024
+ case "Holographic Window Display": newObjectToPlace = createHolographicWindowDisplay(); break;
1025
+ case "Jersey Barrier": newObjectToPlace = createJerseyBarrier(); break;
1026
+ case "Oil Drum": newObjectToPlace = createOilDrum(); break;
1027
+ case "Canned Food": newObjectToPlace = createCannedFood(); break;
1028
+ case "Treasure Chest": newObjectToPlace = createTreasureChest(); break;
1029
+ case "Wall Torch": newObjectToPlace = createWallTorch(); break;
1030
+ case "Bone Pile": newObjectToPlace = createBonePile(); break;
1031
+ case "King Figure": newObjectToPlace = createKingFigure(); break;
1032
+ case "Soldier Figure": newObjectToPlace = createSoldierFigure(); break;
1033
+ case "Mage Figure": newObjectToPlace = createMageFigure(); break;
1034
+ case "Zombie Figure": newObjectToPlace = createZombieFigure(); break;
1035
+ case "Survivor Figure": newObjectToPlace = createSurvivorFigure(); break;
1036
+ case "Dwarf Miner Figure": newObjectToPlace = createDwarfMinerFigure(); break;
1037
+ case "Undead Knight Figure": newObjectToPlace = createUndeadKnightFigure(); break;
1038
+ case "Hero Figure": newObjectToPlace = createHeroFigure(); break;
1039
+ case "Wooden Cart": newObjectToPlace = createWoodenCart(); break;
1040
+ case "Ballista": newObjectToPlace = createBallista(); break;
1041
+ case "Siege Tower": newObjectToPlace = createSiegeTower(); break;
1042
+ case "Buggy Frame": newObjectToPlace = createBuggyFrame(); break;
1043
+ case "Motorbike": newObjectToPlace = createMotorbike(); break;
1044
+ case "Hover Bike": newObjectToPlace = createHoverBike(); break;
1045
+ case "APC": newObjectToPlace = createAPC(); break;
1046
+ case "Sand Boat": newObjectToPlace = createSandBoat(); break;
1047
+ case "Makeshift Machete": newObjectToPlace = createMakeshiftMachete(); break;
1048
+ case "Pistol Body": newObjectToPlace = createPistolBody(); break;
1049
+ case "Scope Attachment": newObjectToPlace = createScopeAttachment(); break;
1050
+ case "Laser Pistol": newObjectToPlace = createLaserPistol(); break;
1051
+ case "Energy Sword": newObjectToPlace = createEnergySword(); break;
1052
+ case "Dwarven Axe": newObjectToPlace = createDwarvenAxe(); break;
1053
+ case "Magic Staff": newObjectToPlace = createMagicStaff(); break;
1054
+ case "Candle Flame": newObjectToPlace = createCandleFlame(); break;
1055
+ case "Dust Cloud": newObjectToPlace = createDustCloud(); break;
1056
+ case "Blood Splat Decal": newObjectToPlace = createBloodSplatDecal(); break;
1057
+ case "Burning Barrel Fire": newObjectToPlace = createBurningBarrelFire(); break;
1058
+ case "Warp Tunnel Effect": newObjectToPlace = createWarpTunnelEffect(); break;
1059
+ case "Laser Beam": newObjectToPlace = createLaserBeam(); break;
1060
+ case "Gold Sparkle": newObjectToPlace = createGoldSparkle(); break;
1061
+ case "Steam Vent": newObjectToPlace = createSteamVent(); break;
1062
+ default: return;
1063
+ }
1064
+ if (newObjectToPlace) {
1065
  newObjectToPlace.position.copy(intersectPoint);
1066
  newObjectToPlace.position.y += 0.01;
1067
+ newObjectToPlace.scale.setScalar(customScale);
1068
+ newObjectToPlace.rotation.y = customRotationY;
 
 
 
 
1069
  scene.add(newObjectToPlace);
1070
  newlyPlacedObjects.push(newObjectToPlace);
1071
  saveUnsavedState();
 
1074
  }
1075
  }
1076
 
1077
+ function onKeyDown(event) { keysPressed[event.code] = true; }
1078
+ function onKeyUp(event) { keysPressed[event.code] = false; }
 
 
 
 
 
1079
 
1080
  function teleportPlayer(targetX, targetZ) {
1081
  console.log(`JS teleportPlayer called with targetX: ${targetX}, targetZ: ${targetZ}`);
 
1113
  }
1114
 
1115
  function getSaveDataForNamedPlot() {
1116
+ console.log(`JS getSaveDataForNamedPlot called. Found ${newlyPlacedObjects.length} new objects.`);
1117
+ const objectsToSave = newlyPlacedObjects.map(obj => {
1118
+ if (!obj.userData || !obj.userData.type) { return null; }
1119
+ const rotation = { _x: obj.rotation.x, _y: obj.rotation.y, _z: obj.rotation.z, _order: obj.rotation.order };
1120
+ return {
1121
+ obj_id: obj.userData.obj_id, type: obj.userData.type,
1122
+ position: { x: obj.position.x, y: obj.position.y, z: obj.position.z },
1123
+ rotation: rotation
1124
+ };
1125
+ }).filter(obj => obj !== null);
1126
+ const playerPos = playerMesh ? { x: playerMesh.position.x, y: playerMesh.position.y, z: playerMesh.position.z } : {x:0, y:0, z:0};
1127
+ const payload = {
1128
+ playerPosition: playerPos,
1129
+ objectsToSave: objectsToSave
1130
+ };
1131
+ console.log("Prepared payload for named plot:", payload);
1132
+ return JSON.stringify(payload);
1133
  }
1134
 
1135
  function resetNewlyPlacedObjects() {