awacke1 commited on
Commit
64b1b25
·
verified ·
1 Parent(s): 0c62314

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +476 -1405
index.html CHANGED
@@ -1,1295 +1,4 @@
1
- const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
2
- const innerFlameMat = new THREE.MeshStandardMaterial({
3
- color: 0xFFFF00,
4
- roughness: 0.2,
5
- metalness: 0.0,
6
- emissive: 0xFFFF00,
7
- emissiveIntensity: 1.0
8
- });
9
- const innerFlame = new THREE.Mesh(innerFlameGeo, innerFlameMat);
10
- innerFlame.position.set(0, 0.04, 0);
11
- flame.add(innerFlame);
12
-
13
- group.position.y = 1.5; // Mount height
14
- return group;
15
- }
16
-
17
- function createBonePile() {
18
- const base = createObjectBase("Bone Pile");
19
- const group = new THREE.Group();
20
- Object.assign(group, base);
21
-
22
- // Create various bones
23
- for (let i = 0; i < 8; i++) {
24
- let bone;
25
- // Choose bone type
26
- const boneType = Math.floor(Math.random() * 3);
27
-
28
- if (boneType === 0) { // Long bone
29
- const boneGeo = new THREE.CylinderGeometry(0.06, 0.08, 0.5 + Math.random() * 0.3, 8);
30
- bone = new THREE.Mesh(boneGeo, materials.bone);
31
- } else if (boneType === 1) { // Skull-like
32
- const boneGeo = new THREE.SphereGeometry(0.15 + Math.random() * 0.1, 8, 8);
33
- bone = new THREE.Mesh(boneGeo, materials.bone);
34
- } else { // Smaller bone fragment
35
- const boneGeo = new THREE.BoxGeometry(
36
- 0.1 + Math.random() * 0.1,
37
- 0.05 + Math.random() * 0.05,
38
- 0.2 + Math.random() * 0.1
39
- );
40
- bone = new THREE.Mesh(boneGeo, materials.bone);
41
- }
42
-
43
- // Position randomly within a circle
44
- const angle = Math.random() * Math.PI * 2;
45
- const distance = Math.random() * 0.6;
46
- bone.position.set(
47
- Math.sin(angle) * distance,
48
- Math.random() * 0.2,
49
- Math.cos(angle) * distance
50
- );
51
-
52
- // Random rotation
53
- bone.rotation.set(
54
- Math.random() * Math.PI,
55
- Math.random() * Math.PI,
56
- Math.random() * Math.PI
57
- );
58
-
59
- bone.castShadow = true;
60
- bone.receiveShadow = true;
61
- group.add(bone);
62
- }
63
-
64
- // Add a skull as centerpiece
65
- const skullGeo = new THREE.SphereGeometry(0.2, 8, 8);
66
- const skull = new THREE.Mesh(skullGeo, materials.bone);
67
- skull.scale.set(1, 0.8, 1.2);
68
- skull.position.set(0, 0.15, 0);
69
- skull.rotation.x = Math.PI/6;
70
- skull.castShadow = true;
71
- skull.receiveShadow = true;
72
- group.add(skull);
73
-
74
- // Eye sockets
75
- const socketGeo = new THREE.SphereGeometry(0.06, 8, 8);
76
- const blackMat = new THREE.MeshBasicMaterial({color: 0x000000});
77
-
78
- const leftEye = new THREE.Mesh(socketGeo, blackMat);
79
- leftEye.position.set(-0.07, 0.05, 0.15);
80
- skull.add(leftEye);
81
-
82
- const rightEye = new THREE.Mesh(socketGeo, blackMat);
83
- rightEye.position.set(0.07, 0.05, 0.15);
84
- skull.add(rightEye);
85
-
86
- group.position.y = 0.05; // Slightly above ground
87
- return group;
88
- }
89
-
90
- // =================== CHARACTERS CATEGORY ===================
91
-
92
- function createKingFigure() {
93
- const base = createObjectBase("King Figure");
94
- const group = new THREE.Group();
95
- Object.assign(group, base);
96
-
97
- // Body
98
- const bodyGeo = new THREE.CylinderGeometry(0.35, 0.25, 1.2, 8);
99
- const body = new THREE.Mesh(bodyGeo, materials.cloth);
100
- body.position.y = 0.6;
101
- body.castShadow = true;
102
- body.receiveShadow = true;
103
- group.add(body);
104
-
105
- // Head
106
- const headGeo = new THREE.SphereGeometry(0.25, 8, 8);
107
- const head = new THREE.Mesh(headGeo, materials.skin);
108
- head.position.y = 1.35;
109
- head.castShadow = true;
110
- head.receiveShadow = true;
111
- group.add(head);
112
-
113
- // Crown
114
- const crownBaseGeo = new THREE.CylinderGeometry(0.28, 0.28, 0.15, 8);
115
- const crownBase = new THREE.Mesh(crownBaseGeo, materials.goldMetal);
116
- crownBase.position.y = 1.5;
117
- crownBase.castShadow = true;
118
- crownBase.receiveShadow = true;
119
- group.add(crownBase);
120
-
121
- // Crown spikes
122
- for (let i = 0; i < 4; i++) {
123
- const spikeGeo = new THREE.ConeGeometry(0.06, 0.15, 4);
124
- const spike = new THREE.Mesh(spikeGeo, materials.goldMetal);
125
-
126
- const angle = i * Math.PI / 2;
127
- spike.position.set(
128
- Math.sin(angle) * 0.2,
129
- 1.65,
130
- Math.cos(angle) * 0.2
131
- );
132
-
133
- spike.castShadow = true;
134
- spike.receiveShadow = true;
135
- group.add(spike);
136
- }
137
-
138
- // Arms
139
- const armGeo = new THREE.CylinderGeometry(0.08, 0.08, 0.6, 8);
140
-
141
- const leftArm = new THREE.Mesh(armGeo, materials.cloth);
142
- leftArm.position.set(-0.4, 0.9, 0);
143
- leftArm.rotation.z = Math.PI / 4;
144
- leftArm.castShadow = true;
145
- leftArm.receiveShadow = true;
146
- group.add(leftArm);
147
-
148
- const rightArm = new THREE.Mesh(armGeo, materials.cloth);
149
- rightArm.position.set(0.4, 0.9, 0);
150
- rightArm.rotation.z = -Math.PI / 4;
151
- rightArm.castShadow = true;
152
- rightArm.receiveShadow = true;
153
- group.add(rightArm);
154
-
155
- // Cape
156
- const capeGeo = new THREE.BoxGeometry(0.7, 1, 0.1);
157
- const cape = new THREE.Mesh(capeGeo, new THREE.MeshStandardMaterial({
158
- color: 0x880000,
159
- roughness: 0.8
160
- }));
161
- cape.position.set(0, 0.7, -0.2);
162
- cape.castShadow = true;
163
- cape.receiveShadow = true;
164
- group.add(cape);
165
-
166
- // Scepter
167
- const scepterGeo = new THREE.CylinderGeometry(0.03, 0.05, 0.8, 8);
168
- const scepter = new THREE.Mesh(scepterGeo, materials.goldMetal);
169
- scepter.position.set(0.65, 0.9, 0);
170
- scepter.rotation.z = -Math.PI / 4;
171
- scepter.castShadow = true;
172
- scepter.receiveShadow = true;
173
- group.add(scepter);
174
-
175
- // Scepter orb
176
- const orbGeo = new THREE.SphereGeometry(0.08, 8, 8);
177
- const orb = new THREE.Mesh(orbGeo, materials.goldMetal);
178
- orb.position.set(0, 0.45, 0);
179
- orb.castShadow = true;
180
- orb.receiveShadow = true;
181
- scepter.add(orb);
182
-
183
- return group;
184
- }
185
-
186
- function createSoldierFigure() {
187
- const base = createObjectBase("Soldier Figure");
188
- const group = new THREE.Group();
189
- Object.assign(group, base);
190
-
191
- // Body with armor
192
- const bodyGeo = new THREE.CylinderGeometry(0.3, 0.25, 1, 8);
193
- const body = new THREE.Mesh(bodyGeo, materials.metal);
194
- body.position.y = 0.5;
195
- body.castShadow = true;
196
- body.receiveShadow = true;
197
- group.add(body);
198
-
199
- // Head
200
- const headGeo = new THREE.SphereGeometry(0.2, 8, 8);
201
- const head = new THREE.Mesh(headGeo, materials.skin);
202
- head.position.y = 1.1;
203
- head.castShadow = true;
204
- head.receiveShadow = true;
205
- group.add(head);
206
-
207
- // Helmet
208
- const helmetGeo = new THREE.CylinderGeometry(0.22, 0.22, 0.2, 8);
209
- const helmet = new THREE.Mesh(helmetGeo, materials.metal);
210
- helmet.position.y = 1.2;
211
- helmet.castShadow = true;
212
- helmet.receiveShadow = true;
213
- group.add(helmet);
214
-
215
- const helmetTopGeo = new THREE.SphereGeometry(0.22, 8, 8);
216
- const helmetTop = new THREE.Mesh(helmetTopGeo, materials.metal);
217
- helmetTop.position.y = 1.3;
218
- helmetTop.scale.y = 0.5;
219
- helmetTop.castShadow = true;
220
- helmetTop.receiveShadow = true;
221
- group.add(helmetTop);
222
-
223
- // Arms
224
- const armGeo = new THREE.CylinderGeometry(0.07, 0.07, 0.5, 8);
225
-
226
- const leftArm = new THREE.Mesh(armGeo, materials.metal);
227
- leftArm.position.set(-0.3, 0.7, 0);
228
- leftArm.rotation.z = Math.PI / 6;
229
- leftArm.castShadow = true;
230
- leftArm.receiveShadow = true;
231
- group.add(leftArm);
232
-
233
- const rightArm = new THREE.Mesh(armGeo, materials.metal);
234
- rightArm.position.set(0.3, 0.7, 0);
235
- rightArm.rotation.z = -Math.PI / 6;
236
- rightArm.castShadow = true;
237
- rightArm.receiveShadow = true;
238
- group.add(rightArm);
239
-
240
- // Shield
241
- const shieldGeo = new THREE.BoxGeometry(0.4, 0.6, 0.1);
242
- const shield = new THREE.Mesh(shieldGeo, materials.metal);
243
- shield.position.set(-0.45, 0.6, 0.1);
244
- shield.rotation.y = Math.PI / 10;
245
- shield.castShadow = true;
246
- shield.receiveShadow = true;
247
- group.add(shield);
248
-
249
- // Sword
250
- const swordHandleGeo = new THREE.CylinderGeometry(0.03, 0.03, 0.2, 8);
251
- const swordHandle = new THREE.Mesh(swordHandleGeo, materials.leather);
252
- swordHandle.position.set(0.4, 0.6, 0.2);
253
- swordHandle.rotation.x = Math.PI / 2;
254
- swordHandle.castShadow = true;
255
- swordHandle.receiveShadow = true;
256
- group.add(swordHandle);
257
-
258
- const swordBladeGeo = new THREE.BoxGeometry(0.05, 0.6, 0.01);
259
- const swordBlade = new THREE.Mesh(swordBladeGeo, materials.metal);
260
- swordBlade.position.set(0, -0.3, 0);
261
- swordBlade.castShadow = true;
262
- swordBlade.receiveShadow = true;
263
- swordHandle.add(swordBlade);
264
-
265
- // Legs
266
- const legGeo = new THREE.CylinderGeometry(0.09, 0.07, 0.5, 8);
267
-
268
- const leftLeg = new THREE.Mesh(legGeo, materials.metal);
269
- leftLeg.position.set(-0.1, 0, 0);
270
- leftLeg.castShadow = true;
271
- leftLeg.receiveShadow = true;
272
- group.add(leftLeg);
273
-
274
- const rightLeg = new THREE.Mesh(legGeo, materials.metal);
275
- rightLeg.position.set(0.1, 0, 0);
276
- rightLeg.castShadow = true;
277
- rightLeg.receiveShadow = true;
278
- group.add(rightLeg);
279
-
280
- group.position.y = 0.5;
281
- return group;
282
- }
283
-
284
- function createMageFigure() {
285
- const base = createObjectBase("Mage Figure");
286
- const group = new THREE.Group();
287
- Object.assign(group, base);
288
-
289
- // Body (robe)
290
- const bodyGeo = new THREE.CylinderGeometry(0.4, 0.5, 1.3, 8);
291
- const bodyMat = new THREE.MeshStandardMaterial({
292
- color: 0x5522AA,
293
- roughness: 0.8,
294
- metalness: 0.1
295
- });
296
- const body = new THREE.Mesh(bodyGeo, bodyMat);
297
- body.position.y = 0.65;
298
- body.castShadow = true;
299
- body.receiveShadow = true;
300
- group.add(body);
301
-
302
- // Head
303
- const headGeo = new THREE.SphereGeometry(0.2, 8, 8);
304
- const head = new THREE.Mesh(headGeo, materials.skin);
305
- head.position.y = 1.4;
306
- head.castShadow = true;
307
- head.receiveShadow = true;
308
- group.add(head);
309
-
310
- // Wizard hat
311
- const hatBaseGeo = new THREE.CylinderGeometry(0.25, 0.25, 0.05, 8);
312
- const hatBase = new THREE.Mesh(hatBaseGeo, bodyMat);
313
- hatBase.position.y = 1.55;
314
- hatBase.castShadow = true;
315
- hatBase.receiveShadow = true;
316
- group.add(hatBase);
317
-
318
- const hatTopGeo = new THREE.ConeGeometry(0.2, 0.5, 8);
319
- const hatTop = new THREE.Mesh(hatTopGeo, bodyMat);
320
- hatTop.position.y = 1.8;
321
- hatTop.castShadow = true;
322
- hatTop.receiveShadow = true;
323
- group.add(hatTop);
324
-
325
- // Arms
326
- const armGeo = new THREE.CylinderGeometry(0.1, 0.1, 0.6, 8);
327
-
328
- const leftArm = new THREE.Mesh(armGeo, bodyMat);
329
- leftArm.position.set(-0.4, 0.9, 0);
330
- leftArm.rotation.z = Math.PI / 4;
331
- leftArm.castShadow = true;
332
- leftArm.receiveShadow = true;
333
- group.add(leftArm);
334
-
335
- const rightArm = new THREE.Mesh(armGeo, bodyMat);
336
- rightArm.position.set(0.4, 0.9, 0);
337
- rightArm.rotation.z = -Math.PI / 4;
338
- rightArm.castShadow = true;
339
- rightArm.receiveShadow = true;
340
- group.add(rightArm);
341
-
342
- // Staff
343
- const staffGeo = new THREE.CylinderGeometry(0.03, 0.05, 1.8, 8);
344
- const staffMat = new THREE.MeshStandardMaterial({
345
- color: 0x663300,
346
- roughness: 0.9
347
- });
348
- const staff = new THREE.Mesh(staffGeo, staffMat);
349
- staff.position.set(0.7, 0.9, 0);
350
- staff.rotation.z = -0.2;
351
- staff.castShadow = true;
352
- staff.receiveShadow = true;
353
- group.add(staff);
354
-
355
- // Orb on staff
356
- const orbGeo = new THREE.SphereGeometry(0.12, 8, 8);
357
- const orbMat = new THREE.MeshStandardMaterial({
358
- color: 0x00CCFF,
359
- roughness: 0.2,
360
- metalness: 0.5,
361
- emissive: 0x0088FF,
362
- emissiveIntensity: 0.8
363
- });
364
- const orb = new THREE.Mesh(orbGeo, orbMat);
365
- orb.position.y = 0.9;
366
- orb.castShadow = true;
367
- orb.receiveShadow = true;
368
- staff.add(orb);
369
-
370
- // Cloak/cape
371
- const cloakGeo = new THREE.BoxGeometry(0.8, 1.2, 0.1);
372
- const cloak = new THREE.Mesh(cloakGeo, bodyMat);
373
- cloak.position.set(0, 0.7, -0.25);
374
- cloak.castShadow = true;
375
- cloak.receiveShadow = true;
376
- group.add(cloak);
377
-
378
- group.position.y = 0.5;
379
- return group;
380
- }
381
-
382
- function createZombieFigure() {
383
- const base = createObjectBase("Zombie Figure");
384
- const group = new THREE.Group();
385
- Object.assign(group, base);
386
-
387
- // Body
388
- const bodyGeo = new THREE.CylinderGeometry(0.25, 0.25, 1, 8);
389
- const body = new THREE.Mesh(bodyGeo, materials.zombie);
390
- body.position.y = 0.5;
391
- body.rotation.x = 0.2; // Leaning forward
392
- body.castShadow = true;
393
- body.receiveShadow = true;
394
- group.add(body);
395
-
396
- // Head
397
- const headGeo = new THREE.SphereGeometry(0.2, 8, 8);
398
- const head = new THREE.Mesh(headGeo, materials.zombie);
399
- head.position.set(0, 1.1, 0.1); // Pushed forward due to leaning
400
- head.castShadow = true;
401
- head.receiveShadow = true;
402
- group.add(head);
403
-
404
- // Arms - asymmetrical for zombie look
405
- const armGeo1 = new THREE.CylinderGeometry(0.08, 0.06, 0.6, 8);
406
- const leftArm = new THREE.Mesh(armGeo1, materials.zombie);
407
- leftArm.position.set(-0.3, 0.7, 0);
408
- leftArm.rotation.set(0.1, 0, Math.PI / 2.5); // Extended forward
409
- leftArm.castShadow = true;
410
- leftArm.receiveShadow = true;
411
- group.add(leftArm);
412
-
413
- const armGeo2 = new THREE.CylinderGeometry(0.07, 0.07, 0.55, 8);
414
- const rightArm = new THREE.Mesh(armGeo2, materials.zombie);
415
- rightArm.position.set(0.3, 0.7, 0);
416
- rightArm.rotation.set(-0.3, 0, -Math.PI / 3); // Hanging down
417
- rightArm.castShadow = true;
418
- rightArm.receiveShadow = true;
419
- group.add(rightArm);
420
-
421
- // Legs - asymmetrical stride
422
- const legGeo1 = new THREE.CylinderGeometry(0.09, 0.07, 0.5, 8);
423
- const leftLeg = new THREE.Mesh(legGeo1, materials.zombie);
424
- leftLeg.position.set(-0.1, 0, 0);
425
- leftLeg.rotation.x = -0.2; // Stepping forward
426
- leftLeg.castShadow = true;
427
- leftLeg.receiveShadow = true;
428
- group.add(leftLeg);
429
-
430
- const legGeo2 = new THREE.CylinderGeometry(0.08, 0.07, 0.5, 8);
431
- const rightLeg = new THREE.Mesh(legGeo2, materials.zombie);
432
- rightLeg.position.set(0.1, 0, -0.1);
433
- rightLeg.castShadow = true;
434
- rightLeg.receiveShadow = true;
435
- group.add(rightLeg);
436
-
437
- // Tattered clothes
438
- const clothesGeo = new THREE.CylinderGeometry(0.26, 0.28, 0.7, 8);
439
- const clothes = new THREE.Mesh(clothesGeo, new THREE.MeshStandardMaterial({
440
- color: 0x445566,
441
- roughness: 0.9,
442
- metalness: 0.0
443
- }));
444
- clothes.position.y = 0.4;
445
- clothes.castShadow = true;
446
- clothes.receiveShadow = true;
447
- body.add(clothes);
448
-
449
- // Gore detail (blood)
450
- const goreGeo = new THREE.SphereGeometry(0.1, 8, 8);
451
- const gore = new THREE.Mesh(goreGeo, new THREE.MeshStandardMaterial({
452
- color: 0x880000,
453
- roughness: 0.8,
454
- metalness: 0.2
455
- }));
456
- gore.position.set(0.15, 0.2, 0.1);
457
- gore.scale.set(1, 0.3, 1);
458
- body.add(gore);
459
-
460
- group.position.y = 0.5;
461
- return group;
462
- }
463
-
464
- function createSurvivorFigure() {
465
- const base = createObjectBase("Survivor Figure");
466
- const group = new THREE.Group();
467
- Object.assign(group, base);
468
-
469
- // Body
470
- const bodyGeo = new THREE.CylinderGeometry(0.25, 0.25, 1, 8);
471
- const body = new THREE.Mesh(bodyGeo, materials.leather);
472
- body.position.y = 0.5;
473
- body.castShadow = true;
474
- body.receiveShadow = true;
475
- group.add(body);
476
-
477
- // Head
478
- const headGeo = new THREE.SphereGeometry(0.2, 8, 8);
479
- const head = new THREE.Mesh(headGeo, materials.skin);
480
- head.position.y = 1.1;
481
- head.castShadow = true;
482
- head.receiveShadow = true;
483
- group.add(head);
484
-
485
- // Arms
486
- const armGeo = new THREE.CylinderGeometry(0.07, 0.07, 0.5, 8);
487
-
488
- const leftArm = new THREE.Mesh(armGeo, materials.leather);
489
- leftArm.position.set(-0.3, 0.7, 0);
490
- leftArm.rotation.z = Math.PI / 6;
491
- leftArm.castShadow = true;
492
- leftArm.receiveShadow = true;
493
- group.add(leftArm);
494
-
495
- const rightArm = new THREE.Mesh(armGeo, materials.leather);
496
- rightArm.position.set(0.3, 0.7, 0);
497
- rightArm.rotation.z = -Math.PI / 6;
498
- rightArm.castShadow = true;
499
- rightArm.receiveShadow = true;
500
- group.add(rightArm);
501
-
502
- // Legs
503
- const legGeo = new THREE.CylinderGeometry(0.08, 0.07, 0.5, 8);
504
-
505
- const leftLeg = new THREE.Mesh(legGeo, new THREE.MeshStandardMaterial({
506
- color: 0x223355, // Denim
507
- roughness: 0.8,
508
- }));
509
- leftLeg.position.set(-0.1, 0, 0);
510
- leftLeg.castShadow = true;
511
- leftLeg.receiveShadow = true;
512
- group.add(leftLeg);
513
-
514
- const rightLeg = new THREE.Mesh(legGeo, new THREE.MeshStandardMaterial({
515
- color: 0x223355, // Denim
516
- roughness: 0.8
517
- }));
518
- rightLeg.position.set(0.1, 0, 0);
519
- rightLeg.castShadow = true;
520
- rightLeg.receiveShadow = true;
521
- group.add(rightLeg);
522
-
523
- // Backpack
524
- const backpackGeo = new THREE.BoxGeometry(0.3, 0.4, 0.2);
525
- const backpack = new THREE.Mesh(backpackGeo, new THREE.MeshStandardMaterial({
526
- color: 0x556B2F, // Olive green
527
- roughness: 0.9
528
- }));
529
- backpack.position.set(0, 0.6, -0.2);
530
- backpack.castShadow = true;
531
- backpack.receiveShadow = true;
532
- group.add(backpack);
533
-
534
- // Baseball bat or weapon
535
- const batGeo = new THREE.CylinderGeometry(0.03, 0.05, 0.8, 8);
536
- const bat = new THREE.Mesh(batGeo, materials.wood);
537
- bat.position.set(0.45, 0.4, 0.2);
538
- bat.rotation.set(Math.PI/6, 0, Math.PI/6);
539
- bat.castShadow = true;
540
- bat.receiveShadow = true;
541
- group.add(bat);
542
-
543
- group.position.y = 0.5;
544
- return group;
545
- }
546
-
547
- function createDwarfMinerFigure() {
548
- const base = createObjectBase("Dwarf Miner Figure");
549
- const group = new THREE.Group();
550
- Object.assign(group, base);
551
-
552
- // Body - shorter, stockier
553
- const bodyGeo = new THREE.CylinderGeometry(0.3, 0.3, 0.7, 8);
554
- const body = new THREE.Mesh(bodyGeo, new THREE.MeshStandardMaterial({
555
- color: 0x8B4513, // Brown leather
556
- roughness: 0.8
557
- }));
558
- body.position.y = 0.35;
559
- body.castShadow = true;
560
- body.receiveShadow = true;
561
- group.add(body);
562
-
563
- // Head - larger in proportion
564
- const headGeo = new THREE.SphereGeometry(0.22, 8, 8);
565
- const head = new THREE.Mesh(headGeo, materials.skin);
566
- head.position.y = 0.8;
567
- head.castShadow = true;
568
- head.receiveShadow = true;
569
- group.add(head);
570
-
571
- // Beard
572
- const beardGeo = new THREE.ConeGeometry(0.2, 0.3, 8);
573
- const beard = new THREE.Mesh(beardGeo, new THREE.MeshStandardMaterial({
574
- color: 0xDD9933, // Golden beard
575
- roughness: 0.9
576
- }));
577
- beard.position.set(0, 0.7, 0.1);
578
- beard.rotation.x = -Math.PI/2;
579
- beard.castShadow = true;
580
- beard.receiveShadow = true;
581
- group.add(beard);
582
-
583
- // Miner's helmet
584
- const helmetGeo = new THREE.CylinderGeometry(0.23, 0.23, 0.15, 8);
585
- const helmet = new THREE.Mesh(helmetGeo, materials.metal);
586
- helmet.position.y = 0.95;
587
- helmet.castShadow = true;
588
- helmet.receiveShadow = true;
589
- group.add(helmet);
590
-
591
- // Helmet light
592
- const lightGeo = new THREE.CylinderGeometry(0.05, 0.05, 0.07, 8);
593
- const light = new THREE.Mesh(lightGeo, new THREE.MeshStandardMaterial({
594
- color: 0xFFFF00,
595
- emissive: 0xFFFF00,
596
- emissiveIntensity: 0.8
597
- }));
598
- light.position.set(0, 0, 0.25);
599
- helmet.add(light);
600
-
601
- // Arms
602
- const armGeo = new THREE.CylinderGeometry(0.08, 0.08, 0.4, 8);
603
-
604
- const leftArm = new THREE.Mesh(armGeo, materials.leather);
605
- leftArm.position.set(-0.35, 0.5, 0);
606
- leftArm.rotation.z = Math.PI / 4;
607
- leftArm.castShadow = true;
608
- leftArm.receiveShadow = true;
609
- group.add(leftArm);
610
-
611
- const rightArm = new THREE.Mesh(armGeo, materials.leather);
612
- rightArm.position.set(0.35, 0.5, 0);
613
- rightArm.rotation.z = -Math.PI / 4;
614
- rightArm.castShadow = true;
615
- rightArm.receiveShadow = true;
616
- group.add(rightArm);
617
-
618
- // Legs - Short, sturdy
619
- const legGeo = new THREE.CylinderGeometry(0.1, 0.08, 0.3, 8);
620
-
621
- const leftLeg = new THREE.Mesh(legGeo, materials.leather);
622
- leftLeg.position.set(-0.15, 0, 0);
623
- leftLeg.castShadow = true;
624
- leftLeg.receiveShadow = true;
625
- group.add(leftLeg);
626
-
627
- const rightLeg = new THREE.Mesh(legGeo, materials.leather);
628
- rightLeg.position.set(0.15, 0 const wallGeo = new THREE.BoxGeometry(5, 3, 0.5);
629
- const wall = new THREE.Mesh(wallGeo, materials.redBrick);
630
- wall.castShadow = true;
631
- wall.receiveShadow = true;
632
- group.add(wall);
633
-
634
- // Broken window
635
- const windowGeo = new THREE.BoxGeometry(1.2, 1.2, 0.1);
636
- const window = new THREE.Mesh(windowGeo, materials.glass);
637
- window.position.set(-1.2, 0.5, 0.3);
638
- window.material.opacity = 0.3; // Broken glass is more transparent
639
- group.add(window);
640
-
641
- // Door frame
642
- const doorFrameGeo = new THREE.BoxGeometry(1.1, 2.2, 0.1);
643
- const doorFrame = new THREE.Mesh(doorFrameGeo, materials.darkWood);
644
- doorFrame.position.set(1.2, -0.4, 0.3);
645
- group.add(doorFrame);
646
-
647
- // Damage details
648
- const damageGeo = new THREE.BoxGeometry(1.5, 1.2, 0.6);
649
- const damage = new THREE.Mesh(damageGeo, materials.damagedConcrete);
650
- damage.position.set(0.5, 1, 0.2);
651
- group.add(damage);
652
-
653
- // Add some rubble at the base
654
- for (let i = 0; i < 5; i++) {
655
- const rubbleGeo = new THREE.IcosahedronGeometry(0.3 * Math.random() + 0.1, 0);
656
- const rubble = new THREE.Mesh(rubbleGeo, materials.redBrick);
657
- rubble.position.set(
658
- Math.random() * 4 - 2,
659
- -1.5 + Math.random() * 0.3,
660
- 0.3 + Math.random() * 0.5
661
- );
662
- rubble.rotation.set(
663
- Math.random() * Math.PI,
664
- Math.random() * Math.PI,
665
- Math.random() * Math.PI
666
- );
667
- rubble.castShadow = true;
668
- rubble.receiveShadow = true;
669
- group.add(rubble);
670
- }
671
-
672
- group.position.y = 1.5; // Raise to ground level
673
- return group;
674
- }
675
-
676
- // =================== NATURE CATEGORY ===================
677
-
678
- function createPineTree() {
679
- const base = createObjectBase("Pine Tree");
680
- const group = new THREE.Group();
681
- Object.assign(group, base);
682
-
683
- // Trunk
684
- const trunkGeo = new THREE.CylinderGeometry(0.2, 0.3, 3, 8);
685
- const trunk = new THREE.Mesh(trunkGeo, materials.wood);
686
- trunk.position.y = 1.5;
687
- trunk.castShadow = true;
688
- trunk.receiveShadow = true;
689
- group.add(trunk);
690
-
691
- // Pine layers - cones stacked
692
- const colors = [0x005500, 0x006600, 0x007700];
693
- for (let i = 0; i < 3; i++) {
694
- const coneGeo = new THREE.ConeGeometry(1.2 - i * 0.3, 1.5, 8);
695
- const leafMat = new THREE.MeshStandardMaterial({
696
- color: colors[i],
697
- roughness: 0.8
698
- });
699
- const foliage = new THREE.Mesh(coneGeo, leafMat);
700
- foliage.position.y = 2 + i*1.2;
701
- foliage.castShadow = true;
702
- foliage.receiveShadow = true;
703
- group.add(foliage);
704
- }
705
-
706
- return group;
707
- }
708
-
709
- function createBoulder() {
710
- const base = createObjectBase("Boulder");
711
- const group = new THREE.Group();
712
- Object.assign(group, base);
713
-
714
- // Main boulder
715
- const boulderGeo = new THREE.IcosahedronGeometry(1.2, 1);
716
- const boulder = new THREE.Mesh(boulderGeo, materials.stone);
717
- boulder.castShadow = true;
718
- boulder.receiveShadow = true;
719
- boulder.scale.y = 0.8;
720
- boulder.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI);
721
- group.add(boulder);
722
-
723
- // Add some smaller stones nearby
724
- for (let i = 0; i < 3; i++) {
725
- const smallStoneGeo = new THREE.IcosahedronGeometry(0.3 * Math.random() + 0.2, 0);
726
- const smallStone = new THREE.Mesh(smallStoneGeo, materials.stone);
727
- smallStone.position.set(
728
- Math.random() * 2 - 1,
729
- Math.random() * 0.3,
730
- Math.random() * 2 - 1
731
- );
732
- smallStone.rotation.set(
733
- Math.random() * Math.PI,
734
- Math.random() * Math.PI,
735
- Math.random() * Math.PI
736
- );
737
- smallStone.castShadow = true;
738
- smallStone.receiveShadow = true;
739
- group.add(smallStone);
740
- }
741
-
742
- group.position.y = 0.6; // Raise to ground level
743
- return group;
744
- }
745
-
746
- function createAlienPlant() {
747
- const base = createObjectBase("Alien Plant");
748
- const group = new THREE.Group();
749
- Object.assign(group, base);
750
-
751
- // Plant base
752
- const baseGeo = new THREE.CylinderGeometry(0.3, 0.5, 0.5, 6);
753
- const plantBase = new THREE.Mesh(baseGeo, materials.darkGreen);
754
- plantBase.position.y = 0.25;
755
- plantBase.castShadow = true;
756
- plantBase.receiveShadow = true;
757
- group.add(plantBase);
758
-
759
- // Strange tentacle-like growths
760
- for (let i = 0; i < 5; i++) {
761
- const tentacleGeo = new THREE.CylinderGeometry(0.1, 0.05, 1.5, 6);
762
- // Bend the geometry
763
- const tentacleMat = new THREE.MeshStandardMaterial({
764
- color: 0x8800AA,
765
- roughness: 0.6,
766
- emissive: 0x330033,
767
- emissiveIntensity: 0.3
768
- });
769
- const tentacle = new THREE.Mesh(tentacleGeo, tentacleMat);
770
-
771
- tentacle.position.set(
772
- Math.sin(i * Math.PI * 2 / 5) * 0.3,
773
- 1,
774
- Math.cos(i * Math.PI * 2 / 5) * 0.3
775
- );
776
-
777
- tentacle.rotation.set(
778
- Math.random() * 0.5 - 0.25 + 0.3,
779
- 0,
780
- Math.random() * 0.5 - 0.25 + (i * Math.PI * 2 / 5)
781
- );
782
-
783
- tentacle.castShadow = true;
784
- tentacle.receiveShadow = true;
785
- group.add(tentacle);
786
-
787
- // Add glowing bulbs at the end of tentacles
788
- const bulbGeo = new THREE.SphereGeometry(0.15, 8, 8);
789
- const bulbMat = new THREE.MeshStandardMaterial({
790
- color: 0xAA00FF,
791
- roughness: 0.4,
792
- emissive: 0xAA00FF,
793
- emissiveIntensity: 0.8
794
- });
795
- const bulb = new THREE.Mesh(bulbGeo, bulbMat);
796
- bulb.position.y = 0.8;
797
- tentacle.add(bulb);
798
- }
799
-
800
- return group;
801
- }
802
-
803
- function createFloatingRockPlatform() {
804
- const base = createObjectBase("Floating Rock Platform");
805
- const group = new THREE.Group();
806
- Object.assign(group, base);
807
-
808
- // Main floating rock
809
- const rockGeo = new THREE.CylinderGeometry(1.5, 2, 1, 8);
810
- const rock = new THREE.Mesh(rockGeo, materials.stone);
811
- rock.castShadow = true;
812
- rock.receiveShadow = true;
813
- rock.position.y = 1.5;
814
- group.add(rock);
815
-
816
- // Top surface - grass
817
- const topGeo = new THREE.CylinderGeometry(1.5, 1.5, 0.1, 8);
818
- const top = new THREE.Mesh(topGeo, materials.ground);
819
- top.position.y = 2.05;
820
- top.receiveShadow = true;
821
- group.add(top);
822
-
823
- // Bottom details - crystals
824
- for (let i = 0; i < 6; i++) {
825
- const crystalGeo = new THREE.ConeGeometry(0.2, 0.6, 5);
826
- const crystal = new THREE.Mesh(crystalGeo, materials.crystal);
827
-
828
- const angle = i * Math.PI * 2 / 6;
829
- crystal.position.set(
830
- Math.sin(angle) * 1.7,
831
- 1,
832
- Math.cos(angle) * 1.7
833
- );
834
-
835
- crystal.rotation.x = Math.PI;
836
- crystal.rotation.z = Math.random() * 0.3 - 0.15;
837
-
838
- crystal.castShadow = true;
839
- crystal.receiveShadow = true;
840
- group.add(crystal);
841
- }
842
-
843
- // Anti-gravity effect (particles) - simulated with small meshes
844
- for (let i = 0; i < 8; i++) {
845
- const particleGeo = new THREE.SphereGeometry(0.1, 4, 4);
846
- const particle = new THREE.Mesh(particleGeo, materials.energyBeam);
847
-
848
- const angle = i * Math.PI * 2 / 8;
849
- const distance = 1 + Math.random() * 0.5;
850
- particle.position.set(
851
- Math.sin(angle) * distance,
852
- Math.random() * 1.5,
853
- Math.cos(angle) * distance
854
- );
855
-
856
- group.add(particle);
857
- }
858
-
859
- group.position.y = 0.8; // Hover above ground
860
- return group;
861
- }
862
-
863
- function createRubblePile() {
864
- const base = createObjectBase("Rubble Pile");
865
- const group = new THREE.Group();
866
- Object.assign(group, base);
867
-
868
- // Create multiple rubble pieces
869
- const materials = [
870
- new THREE.MeshStandardMaterial({color: 0x888888, roughness: 0.9}), // Concrete
871
- new THREE.MeshStandardMaterial({color: 0xA03C28, roughness: 0.9}), // Brick
872
- new THREE.MeshStandardMaterial({color: 0x777777, roughness: 0.8}) // Stone
873
- ];
874
-
875
- // Add chunks of various sizes
876
- for (let i = 0; i < 15; i++) {
877
- // Choose random geometry
878
- let geo;
879
- const geoType = Math.floor(Math.random() * 3);
880
- if (geoType === 0) {
881
- geo = new THREE.BoxGeometry(
882
- 0.3 + Math.random() * 0.7,
883
- 0.2 + Math.random() * 0.3,
884
- 0.3 + Math.random() * 0.7
885
- );
886
- } else if (geoType === 1) {
887
- geo = new THREE.IcosahedronGeometry(0.2 + Math.random() * 0.4, 0);
888
- } else {
889
- geo = new THREE.TetrahedronGeometry(0.3 + Math.random() * 0.4, 0);
890
- }
891
-
892
- // Choose random material
893
- const material = materials[Math.floor(Math.random() * materials.length)];
894
-
895
- const chunk = new THREE.Mesh(geo, material);
896
- chunk.position.set(
897
- Math.random() * 2 - 1,
898
- Math.random() * 0.8,
899
- Math.random() * 2 - 1
900
- );
901
- chunk.rotation.set(
902
- Math.random() * Math.PI,
903
- Math.random() * Math.PI,
904
- Math.random() * Math.PI
905
- );
906
- chunk.castShadow = true;
907
- chunk.receiveShadow = true;
908
- group.add(chunk);
909
- }
910
-
911
- // Add some rebar sticking out
912
- for (let i = 0; i < 5; i++) {
913
- const rebarGeo = new THREE.CylinderGeometry(0.03, 0.03, 0.8 + Math.random() * 0.8, 6);
914
- const rebar = new THREE.Mesh(rebarGeo, materials.rustedMetal);
915
- rebar.position.set(
916
- Math.random() * 1.8 - 0.9,
917
- 0.3 + Math.random() * 0.3,
918
- Math.random() * 1.8 - 0.9
919
- );
920
- rebar.rotation.set(
921
- Math.random() * 0.8 - 0.4,
922
- Math.random() * Math.PI,
923
- Math.random() * 0.8 - 0.4
924
- );
925
- rebar.castShadow = true;
926
- rebar.receiveShadow = true;
927
- group.add(rebar);
928
- }
929
-
930
- // Add dusty material on ground
931
- const dustGeo = new THREE.CircleGeometry(1.5, 16);
932
- const dustMat = new THREE.MeshStandardMaterial({
933
- color: 0xCCBBAA,
934
- roughness: 1.0,
935
- metalness: 0.0
936
- });
937
- const dust = new THREE.Mesh(dustGeo, dustMat);
938
- dust.rotation.x = -Math.PI/2;
939
- dust.position.y = 0.01;
940
- dust.receiveShadow = true;
941
- group.add(dust);
942
-
943
- group.position.y = 0.4; // Raise to ground level
944
- return group;
945
- }
946
-
947
- // =================== PROPS CATEGORY ===================
948
-
949
- function createRooftopACUnit() {
950
- const base = createObjectBase("Rooftop AC Unit");
951
- const group = new THREE.Group();
952
- Object.assign(group, base);
953
-
954
- // Main AC unit body
955
- const acGeo = new THREE.BoxGeometry(1.5, 0.8, 1.2);
956
- const ac = new THREE.Mesh(acGeo, materials.metal);
957
- ac.castShadow = true;
958
- ac.receiveShadow = true;
959
- group.add(ac);
960
-
961
- // Fan grille on top
962
- const fanGeo = new THREE.CylinderGeometry(0.4, 0.4, 0.1, 8);
963
- const fan = new THREE.Mesh(fanGeo, materials.rustedMetal);
964
- fan.rotation.x = Math.PI/2;
965
- fan.position.set(0, 0.45, 0);
966
- group.add(fan);
967
-
968
- // Vents on sides
969
- const ventGeo = new THREE.BoxGeometry(1.2, 0.4, 0.1);
970
-
971
- const vent1 = new THREE.Mesh(ventGeo, materials.rustedMetal);
972
- vent1.position.set(0, 0, 0.65);
973
- group.add(vent1);
974
-
975
- const vent2 = new THREE.Mesh(ventGeo, materials.rustedMetal);
976
- vent2.position.set(0, 0, -0.65);
977
- group.add(vent2);
978
-
979
- // Pipes
980
- const pipeGeo = new THREE.CylinderGeometry(0.1, 0.1, 0.6, 8);
981
- const pipe = new THREE.Mesh(pipeGeo, materials.metal);
982
- pipe.rotation.x = Math.PI/2;
983
- pipe.position.set(-0.6, -0.25, 0);
984
- group.add(pipe);
985
-
986
- group.position.y = 0.4; // Raise to ground level
987
- return group;
988
- }
989
-
990
- function createHolographicWindowDisplay() {
991
- const base = createObjectBase("Holographic Window Display");
992
- const group = new THREE.Group();
993
- Object.assign(group, base);
994
-
995
- // Frame
996
- const frameGeo = new THREE.BoxGeometry(2, 1.5, 0.1);
997
- const frame = new THREE.Mesh(frameGeo, materials.metal);
998
- frame.castShadow = true;
999
- frame.receiveShadow = true;
1000
- group.add(frame);
1001
-
1002
- // Holographic display
1003
- const displayGeo = new THREE.PlaneGeometry(1.8, 1.3);
1004
- const displayMat = new THREE.MeshStandardMaterial({
1005
- color: 0x00AAFF,
1006
- roughness: 0.2,
1007
- metalness: 0.8,
1008
- emissive: 0x0066FF,
1009
- emissiveIntensity: 0.8,
1010
- transparent: true,
1011
- opacity: 0.7
1012
- });
1013
- const display = new THREE.Mesh(displayGeo, displayMat);
1014
- display.position.z = 0.06;
1015
- group.add(display);
1016
-
1017
- // Data lines on the display
1018
- for (let i = 0; i < 5; i++) {
1019
- const lineGeo = new THREE.BoxGeometry(1.4, 0.04, 0.01);
1020
- const line = new THREE.Mesh(lineGeo, materials.energyBeam);
1021
- line.position.set(0, -0.4 + i * 0.2, 0.07);
1022
- group.add(line);
1023
- }
1024
-
1025
- group.position.y = 1.5; // Raise to wall mount height
1026
- return group;
1027
- }
1028
-
1029
- function createJerseyBarrier() {
1030
- const base = createObjectBase("Jersey Barrier");
1031
- const group = new THREE.Group();
1032
- Object.assign(group, base);
1033
-
1034
- // Create the barrier shape - custom geometry
1035
- const shape = new THREE.Shape();
1036
- shape.moveTo(-0.5, 0);
1037
- shape.lineTo(-0.4, 0.8);
1038
- shape.lineTo(0.4, 0.8);
1039
- shape.lineTo(0.5, 0);
1040
- shape.lineTo(-0.5, 0);
1041
-
1042
- const extrudeSettings = {
1043
- steps: 1,
1044
- depth: 2,
1045
- bevelEnabled: false
1046
- };
1047
-
1048
- const barrierGeo = new THREE.ExtrudeGeometry(shape, extrudeSettings);
1049
- const barrier = new THREE.Mesh(barrierGeo, materials.concrete);
1050
- barrier.castShadow = true;
1051
- barrier.receiveShadow = true;
1052
- barrier.rotation.y = Math.PI/2;
1053
- group.add(barrier);
1054
-
1055
- // Add some damage/weathering details
1056
- const damageGeo = new THREE.BoxGeometry(0.2, 0.1, 0.3);
1057
- const damage1 = new THREE.Mesh(damageGeo, materials.damagedConcrete);
1058
- damage1.position.set(0.51, 0.4, 0.7);
1059
- group.add(damage1);
1060
-
1061
- const damage2 = new THREE.Mesh(damageGeo, materials.damagedConcrete);
1062
- damage2.position.set(0.51, 0.3, -0.5);
1063
- group.add(damage2);
1064
-
1065
- // Optional graffiti - represented as colored patches
1066
- const graffitiGeo = new THREE.PlaneGeometry(0.8, 0.3);
1067
- const graffitiMat = new THREE.MeshStandardMaterial({
1068
- color: 0xFF3300,
1069
- roughness: 0.9
1070
- });
1071
- const graffiti = new THREE.Mesh(graffitiGeo, graffitiMat);
1072
- graffiti.rotation.y = Math.PI/2;
1073
- graffiti.position.set(0.51, 0.5, 0);
1074
- group.add(graffiti);
1075
-
1076
- group.position.y = 0.4; // Raise to ground level
1077
- return group;
1078
- }
1079
-
1080
- function createOilDrum() {
1081
- const base = createObjectBase("Oil Drum");
1082
- const group = new THREE.Group();
1083
- Object.assign(group, base);
1084
-
1085
- // Main drum body
1086
- const drumGeo = new THREE.CylinderGeometry(0.4, 0.4, 1.2, 16);
1087
- const drum = new THREE.Mesh(drumGeo, materials.rustedMetal);
1088
- drum.castShadow = true;
1089
- drum.receiveShadow = true;
1090
- group.add(drum);
1091
-
1092
- // Top and bottom rims
1093
- const rimGeo = new THREE.TorusGeometry(0.4, 0.05, 8, 24);
1094
-
1095
- const topRim = new THREE.Mesh(rimGeo, materials.metal);
1096
- topRim.rotation.x = Math.PI/2;
1097
- topRim.position.y = 0.6;
1098
- group.add(topRim);
1099
-
1100
- const bottomRim = new THREE.Mesh(rimGeo, materials.metal);
1101
- bottomRim.rotation.x = Math.PI/2;
1102
- bottomRim.position.y = -0.6;
1103
- group.add(bottomRim);
1104
-
1105
- // Add some bullet holes
1106
- for (let i = 0; i < 3; i++) {
1107
- const holeGeo = new THREE.CircleGeometry(0.05 + Math.random() * 0.05, 8);
1108
- const hole = new THREE.Mesh(holeGeo, new THREE.MeshBasicMaterial({color: 0x000000}));
1109
-
1110
- // Position on the curved surface of the drum
1111
- const angle = Math.random() * Math.PI * 2;
1112
- hole.position.set(
1113
- Math.sin(angle) * 0.41,
1114
- Math.random() * 0.8 - 0.4,
1115
- Math.cos(angle) * 0.41
1116
- );
1117
-
1118
- // Rotate to face outward
1119
- hole.rotation.y = angle;
1120
- hole.rotation.x = Math.PI/2;
1121
-
1122
- group.add(hole);
1123
- }
1124
-
1125
- // Add a lid cap
1126
- const lidGeo = new THREE.CylinderGeometry(0.4, 0.4, 0.05, 16);
1127
- const lid = new THREE.Mesh(lidGeo, materials.metal);
1128
- lid.position.y = 0.625;
1129
- group.add(lid);
1130
-
1131
- // Add cap in center
1132
- const capGeo = new THREE.CylinderGeometry(0.1, 0.1, 0.03, 16);
1133
- const cap = new THREE.Mesh(capGeo, materials.metal);
1134
- cap.position.y = 0.65;
1135
- group.add(cap);
1136
-
1137
- group.position.y = 0.6; // Raise to ground level
1138
- return group;
1139
- }
1140
-
1141
- function createCannedFood() {
1142
- const base = createObjectBase("Canned Food");
1143
- const group = new THREE.Group();
1144
- Object.assign(group, base);
1145
-
1146
- // Main can body
1147
- const canGeo = new THREE.CylinderGeometry(0.15, 0.15, 0.25, 16);
1148
- const can = new THREE.Mesh(canGeo, materials.metal);
1149
- can.castShadow = true;
1150
- can.receiveShadow = true;
1151
- group.add(can);
1152
-
1153
- // Top and bottom rims
1154
- const rimGeo = new THREE.TorusGeometry(0.15, 0.02, 8, 24);
1155
-
1156
- const topRim = new THREE.Mesh(rimGeo, materials.metal);
1157
- topRim.rotation.x = Math.PI/2;
1158
- topRim.position.y = 0.125;
1159
- group.add(topRim);
1160
-
1161
- const bottomRim = new THREE.Mesh(rimGeo, materials.metal);
1162
- bottomRim.rotation.x = Math.PI/2;
1163
- bottomRim.position.y = -0.125;
1164
- group.add(bottomRim);
1165
-
1166
- // Label - simulated with a colored band
1167
- const labelGeo = new THREE.CylinderGeometry(0.151, 0.151, 0.2, 16);
1168
- const labelMat = new THREE.MeshStandardMaterial({
1169
- color: 0x2244AA,
1170
- roughness: 0.9,
1171
- metalness: 0.0
1172
- });
1173
- const label = new THREE.Mesh(labelGeo, labelMat);
1174
- group.add(label);
1175
-
1176
- // Add a dent
1177
- const dentGeo = new THREE.SphereGeometry(0.1, 8, 8);
1178
- const dent = new THREE.Mesh(dentGeo, materials.metal);
1179
- dent.position.set(0.08, 0, 0.12);
1180
- dent.scale.set(0.5, 0.5, 0.2);
1181
- group.add(dent);
1182
-
1183
- group.position.y = 0.125; // Raise to ground level
1184
- group.scale.set(0.7, 0.7, 0.7); // Make the can a reasonable size
1185
- return group;
1186
- }
1187
-
1188
- function createTreasureChest() {
1189
- const base = createObjectBase("Treasure Chest");
1190
- const group = new THREE.Group();
1191
- Object.assign(group, base);
1192
-
1193
- // Chest base
1194
- const chestBaseGeo = new THREE.BoxGeometry(1, 0.6, 0.7);
1195
- const chestBase = new THREE.Mesh(chestBaseGeo, materials.wood);
1196
- chestBase.position.y = 0.3;
1197
- chestBase.castShadow = true;
1198
- chestBase.receiveShadow = true;
1199
- group.add(chestBase);
1200
-
1201
- // Chest lid
1202
- const chestLidGeo = new THREE.BoxGeometry(1, 0.2, 0.7);
1203
- const chestLid = new THREE.Mesh(chestLidGeo, materials.wood);
1204
- chestLid.position.set(0, 0.7, -0.2);
1205
- chestLid.rotation.x = Math.PI/4; // Opened
1206
- chestLid.castShadow = true;
1207
- chestLid.receiveShadow = true;
1208
- group.add(chestLid);
1209
-
1210
- // Metal bands
1211
- const bandGeo = new THREE.BoxGeometry(1.02, 0.1, 0.72);
1212
-
1213
- const band1 = new THREE.Mesh(bandGeo, materials.metal);
1214
- band1.position.y = 0.15;
1215
- group.add(band1);
1216
-
1217
- const band2 = new THREE.Mesh(bandGeo, materials.metal);
1218
- band2.position.y = 0.45;
1219
- group.add(band2);
1220
-
1221
- // Lid band
1222
- const lidBandGeo = new THREE.BoxGeometry(1.02, 0.1, 0.72);
1223
- const lidBand = new THREE.Mesh(lidBandGeo, materials.metal);
1224
- lidBand.position.y = 0.05;
1225
- chestLid.add(lidBand);
1226
-
1227
- // Lock
1228
- const lockGeo = new THREE.BoxGeometry(0.25, 0.2, 0.1);
1229
- const lock = new THREE.Mesh(lockGeo, materials.goldMetal);
1230
- lock.position.set(0, 0.6, 0.35);
1231
- group.add(lock);
1232
-
1233
- // Glow effect inside (treasure!)
1234
- const glowGeo = new THREE.IcosahedronGeometry(0.3, 0);
1235
- const glowMat = new THREE.MeshStandardMaterial({
1236
- color: 0xFFD700,
1237
- roughness: 0.2,
1238
- metalness: 1.0,
1239
- emissive: 0xFFD700,
1240
- emissiveIntensity: 0.5
1241
- });
1242
- const glow = new THREE.Mesh(glowGeo, glowMat);
1243
- glow.position.set(0, 0.3, 0);
1244
- group.add(glow);
1245
-
1246
- return group;
1247
- }
1248
-
1249
- function createWallTorch() {
1250
- const base = createObjectBase("Wall Torch");
1251
- const group = new THREE.Group();
1252
- Object.assign(group, base);
1253
-
1254
- // Torch handle
1255
- const handleGeo = new THREE.CylinderGeometry(0.03, 0.03, 0.5, 8);
1256
- const handle = new THREE.Mesh(handleGeo, materials.wood);
1257
- handle.rotation.x = Math.PI/4;
1258
- handle.position.y = 0.2;
1259
- handle.castShadow = true;
1260
- handle.receiveShadow = true;
1261
- group.add(handle);
1262
-
1263
- // Torch holder
1264
- const holderGeo = new THREE.CylinderGeometry(0.08, 0.12, 0.15, 8);
1265
- const holder = new THREE.Mesh(holderGeo, materials.metal);
1266
- holder.position.set(0, 0.48, 0.17);
1267
- holder.rotation.x = Math.PI/4;
1268
- group.add(holder);
1269
-
1270
- // Wall mount
1271
- const mountGeo = new THREE.BoxGeometry(0.25, 0.25, 0.1);
1272
- const mount = new THREE.Mesh(mountGeo, materials.metal);
1273
- mount.position.z = -0.1;
1274
- group.add(mount);
1275
-
1276
- // Flame effect (simplified)
1277
- const flameGeo = new THREE.ConeGeometry(0.1, 0.25, 8);
1278
- const flameMat = new THREE.MeshStandardMaterial({
1279
- color: 0xFF6600,
1280
- roughness: 0.3,
1281
- metalness: 0.0,
1282
- emissive: 0xFF3300,
1283
- emissiveIntensity: 0.8
1284
- });
1285
- const flame = new THREE.Mesh(flameGeo, flameMat);
1286
- flame.position.set(0, 0.58, 0.26);
1287
- flame.rotation.x = Math.PI/4;
1288
- group.add(flame);
1289
-
1290
- // Inner flame
1291
- const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1292
- const innerFlameM<!DOCTYPE html>
1293
  <html>
1294
  <head>
1295
  <title>Three.js Infinite World</title>
@@ -1297,32 +6,22 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1297
  body { margin: 0; overflow: hidden; }
1298
  canvas { display: block; }
1299
  .info-panel {
1300
- position: absolute;
1301
- bottom: 10px;
1302
- left: 10px;
1303
- background: rgba(0,0,0,0.6);
1304
- color: white;
1305
- padding: 10px;
1306
- border-radius: 5px;
1307
- font-family: Arial, sans-serif;
1308
- font-size: 14px;
1309
  pointer-events: none;
1310
  }
1311
  </style>
1312
- <!-- New: Polling function for game state -->
1313
  <script>
1314
- // Poll the shared game state every 5 seconds (for demonstration)
1315
  function pollGameState() {
1316
  console.log("Polling updated game state:", window.GAME_STATE);
1317
- // Here you could update the scene based on the new state.
1318
  }
1319
  setInterval(pollGameState, 5000);
1320
  </script>
1321
  </head>
1322
  <body>
1323
- <div class="info-panel">
1324
- WASD or Arrow Keys to move | Click to place objects
1325
- </div>
1326
 
1327
  <script type="importmap">
1328
  {
@@ -1340,14 +39,11 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1340
  let raycaster, mouse;
1341
  const keysPressed = {};
1342
  const playerSpeed = 0.15;
1343
- let newlyPlacedObjects = []; // Track objects added THIS session for saving
1344
  const placeholderPlots = new Set();
1345
- const groundMeshes = {}; // Store ground mesh references
1346
-
1347
- // --- Session Storage Key ---
1348
  const SESSION_STORAGE_KEY = 'unsavedInfiniteWorldState';
1349
-
1350
- // --- Injected State from Streamlit ---
1351
  const allInitialObjects = window.ALL_INITIAL_OBJECTS || [];
1352
  const plotsMetadata = window.PLOTS_METADATA || [];
1353
  const selectedObjectType = window.SELECTED_OBJECT_TYPE || "None";
@@ -1356,75 +52,29 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1356
  const plotWidth = window.PLOT_WIDTH || 50.0;
1357
  const plotDepth = window.PLOT_DEPTH || 50.0;
1358
 
1359
- // Materials Library - Centralized for reuse
1360
  const materials = {
1361
- // Common materials
1362
- ground: new THREE.MeshStandardMaterial({
1363
- color: 0x55aa55, roughness: 0.9, metalness: 0.1, side: THREE.DoubleSide
1364
- }),
1365
- placeholderGround: new THREE.MeshStandardMaterial({
1366
- color: 0x448844, roughness: 0.95, metalness: 0.1, side: THREE.DoubleSide
1367
- }),
1368
- wood: new THREE.MeshStandardMaterial({
1369
- color: 0x8B4513, roughness: 0.9, metalness: 0.1
1370
- }),
1371
- darkWood: new THREE.MeshStandardMaterial({
1372
- color: 0x5D2906, roughness: 0.85, metalness: 0.15
1373
- }),
1374
- redBrick: new THREE.MeshStandardMaterial({
1375
- color: 0xA03C28, roughness: 0.9, metalness: 0.05
1376
- }),
1377
- metal: new THREE.MeshStandardMaterial({
1378
- color: 0x888888, roughness: 0.4, metalness: 0.8
1379
- }),
1380
- rustedMetal: new THREE.MeshStandardMaterial({
1381
- color: 0x964B00, roughness: 0.7, metalness: 0.6
1382
- }),
1383
- glass: new THREE.MeshStandardMaterial({
1384
- color: 0x88CCFF, roughness: 0.1, metalness: 0.9, transparent: true, opacity: 0.4
1385
- }),
1386
- concrete: new THREE.MeshStandardMaterial({
1387
- color: 0x888888, roughness: 0.9, metalness: 0.1
1388
- }),
1389
- damagedConcrete: new THREE.MeshStandardMaterial({
1390
- color: 0x777777, roughness: 0.95, metalness: 0.05
1391
- }),
1392
- darkGreen: new THREE.MeshStandardMaterial({
1393
- color: 0x225522, roughness: 0.8, metalness: 0.1
1394
- }),
1395
- skin: new THREE.MeshStandardMaterial({
1396
- color: 0xE0AC69, roughness: 0.7, metalness: 0.1
1397
- }),
1398
- cloth: new THREE.MeshStandardMaterial({
1399
- color: 0xCCAA88, roughness: 0.9, metalness: 0.0
1400
- }),
1401
- neonGlow: new THREE.MeshStandardMaterial({
1402
- color: 0x00FFFF, roughness: 0.2, metalness: 0.8, emissive: 0x00FFFF, emissiveIntensity: 1.0
1403
- }),
1404
- goldMetal: new THREE.MeshStandardMaterial({
1405
- color: 0xFFD700, roughness: 0.3, metalness: 1.0
1406
- }),
1407
- leather: new THREE.MeshStandardMaterial({
1408
- color: 0x8B4513, roughness: 0.9, metalness: 0.1
1409
- }),
1410
- stone: new THREE.MeshStandardMaterial({
1411
- color: 0x777777, roughness: 0.9, metalness: 0.1
1412
- }),
1413
- bone: new THREE.MeshStandardMaterial({
1414
- color: 0xE3DAC9, roughness: 0.7, metalness: 0.1
1415
- }),
1416
- zombie: new THREE.MeshStandardMaterial({
1417
- color: 0x7D9F85, roughness: 0.8, metalness: 0.1
1418
- }),
1419
- lava: new THREE.MeshStandardMaterial({
1420
- color: 0xFF4500, roughness: 0.7, metalness: 0.3, emissive: 0xFF4500, emissiveIntensity: 0.8
1421
- }),
1422
- crystal: new THREE.MeshStandardMaterial({
1423
- color: 0x88CCFF, roughness: 0.1, metalness: 0.9, transparent: true, opacity: 0.7
1424
- }),
1425
- energyBeam: new THREE.MeshStandardMaterial({
1426
- color: 0x88FFFF, roughness: 0.1, metalness: 0.5, emissive: 0x88FFFF, emissiveIntensity: 1.0, transparent: true, opacity: 0.7
1427
- })
1428
  };
1429
 
1430
  function init() {
@@ -1452,14 +102,12 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1452
  loadInitialObjects();
1453
  restoreUnsavedState();
1454
 
1455
- // Event Listeners
1456
  document.addEventListener('mousemove', onMouseMove, false);
1457
  document.addEventListener('click', onDocumentClick, false);
1458
  window.addEventListener('resize', onWindowResize, false);
1459
  document.addEventListener('keydown', onKeyDown);
1460
  document.addEventListener('keyup', onKeyUp);
1461
 
1462
- // Define functions callable by Streamlit
1463
  window.teleportPlayer = teleportPlayer;
1464
  window.getSaveDataAndPosition = getSaveDataAndPosition;
1465
  window.getSaveDataForNamedPlot = getSaveDataForNamedPlot;
@@ -1582,13 +230,10 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1582
  function createAndPlaceObject(objData, isNewObject) {
1583
  let loadedObject = null;
1584
  switch (objData.type) {
1585
- // Original objects
1586
  case "Simple House": loadedObject = createSimpleHouse(); break;
1587
  case "Tree": loadedObject = createTree(); break;
1588
  case "Rock": loadedObject = createRock(); break;
1589
- case "Fence Post": loadedObject = createFencePost(); break;
1590
-
1591
- // Building category
1592
  case "Cyberpunk Wall Panel": loadedObject = createCyberpunkWallPanel(); break;
1593
  case "Modular Hab Block": loadedObject = createModularHabBlock(); break;
1594
  case "MegaCorp Skyscraper": loadedObject = createMegaCorpSkyscraper(); break;
@@ -1597,15 +242,11 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1597
  case "House Roof Section": loadedObject = createHouseRoofSection(); break;
1598
  case "Concrete Bunker Wall": loadedObject = createConcreteBunkerWall(); break;
1599
  case "Damaged House Facade": loadedObject = createDamagedHouseFacade(); break;
1600
-
1601
- // Nature category
1602
  case "Pine Tree": loadedObject = createPineTree(); break;
1603
  case "Boulder": loadedObject = createBoulder(); break;
1604
  case "Alien Plant": loadedObject = createAlienPlant(); break;
1605
  case "Floating Rock Platform": loadedObject = createFloatingRockPlatform(); break;
1606
  case "Rubble Pile": loadedObject = createRubblePile(); break;
1607
-
1608
- // Props category
1609
  case "Rooftop AC Unit": loadedObject = createRooftopACUnit(); break;
1610
  case "Holographic Window Display": loadedObject = createHolographicWindowDisplay(); break;
1611
  case "Jersey Barrier": loadedObject = createJerseyBarrier(); break;
@@ -1614,8 +255,6 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1614
  case "Treasure Chest": loadedObject = createTreasureChest(); break;
1615
  case "Wall Torch": loadedObject = createWallTorch(); break;
1616
  case "Bone Pile": loadedObject = createBonePile(); break;
1617
-
1618
- // Characters category
1619
  case "King Figure": loadedObject = createKingFigure(); break;
1620
  case "Soldier Figure": loadedObject = createSoldierFigure(); break;
1621
  case "Mage Figure": loadedObject = createMageFigure(); break;
@@ -1624,8 +263,6 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1624
  case "Dwarf Miner Figure": loadedObject = createDwarfMinerFigure(); break;
1625
  case "Undead Knight Figure": loadedObject = createUndeadKnightFigure(); break;
1626
  case "Hero Figure": loadedObject = createHeroFigure(); break;
1627
-
1628
- // Vehicles category
1629
  case "Wooden Cart": loadedObject = createWoodenCart(); break;
1630
  case "Ballista": loadedObject = createBallista(); break;
1631
  case "Siege Tower": loadedObject = createSiegeTower(); break;
@@ -1634,8 +271,6 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1634
  case "Hover Bike": loadedObject = createHoverBike(); break;
1635
  case "APC": loadedObject = createAPC(); break;
1636
  case "Sand Boat": loadedObject = createSandBoat(); break;
1637
-
1638
- // Weapons category
1639
  case "Makeshift Machete": loadedObject = createMakeshiftMachete(); break;
1640
  case "Pistol Body": loadedObject = createPistolBody(); break;
1641
  case "Scope Attachment": loadedObject = createScopeAttachment(); break;
@@ -1643,8 +278,6 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1643
  case "Energy Sword": loadedObject = createEnergySword(); break;
1644
  case "Dwarven Axe": loadedObject = createDwarvenAxe(); break;
1645
  case "Magic Staff": loadedObject = createMagicStaff(); break;
1646
-
1647
- // Effects category
1648
  case "Candle Flame": loadedObject = createCandleFlame(); break;
1649
  case "Dust Cloud": loadedObject = createDustCloud(); break;
1650
  case "Blood Splat Decal": loadedObject = createBloodSplatDecal(); break;
@@ -1653,30 +286,26 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1653
  case "Laser Beam": loadedObject = createLaserBeam(); break;
1654
  case "Gold Sparkle": loadedObject = createGoldSparkle(); break;
1655
  case "Steam Vent": loadedObject = createSteamVent(); break;
1656
-
1657
  default: console.warn("Unknown object type in data:", objData.type); break;
1658
  }
 
1659
  if (loadedObject) {
1660
- // Apply stored position
1661
  if (objData.position && objData.position.x !== undefined) {
1662
  loadedObject.position.set(objData.position.x, objData.position.y, objData.position.z);
1663
  } else if (objData.pos_x !== undefined) {
1664
  loadedObject.position.set(objData.pos_x, objData.pos_y, objData.pos_z);
1665
  }
1666
 
1667
- // Apply stored rotation
1668
  if (objData.rotation) {
1669
  loadedObject.rotation.set(objData.rotation._x, objData.rotation._y, objData.rotation._z, objData.rotation._order || 'XYZ');
1670
  } else if (objData.rot_x !== undefined) {
1671
  loadedObject.rotation.set(objData.rot_x, objData.rot_y, objData.rot_z, objData.rot_order || 'XYZ');
1672
  }
1673
 
1674
- // Apply custom scale if it's a new object (not loading from save)
1675
  if (isNewObject && window.CUSTOM_SCALE && window.CUSTOM_SCALE !== 1.0) {
1676
  loadedObject.scale.set(window.CUSTOM_SCALE, window.CUSTOM_SCALE, window.CUSTOM_SCALE);
1677
  }
1678
 
1679
- // Apply custom rotation if it's a new object
1680
  if (isNewObject && window.CUSTOM_ROTATION_Y) {
1681
  loadedObject.rotation.y = window.CUSTOM_ROTATION_Y;
1682
  }
@@ -1687,4 +316,446 @@ const innerFlameGeo = new THREE.ConeGeometry(0.05, 0.15, 8);
1687
  return loadedObject;
1688
  }
1689
  return null;
1690
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  <html>
3
  <head>
4
  <title>Three.js Infinite World</title>
 
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
  {
 
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";
 
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() {
 
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;
 
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
  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;
 
255
  case "Treasure Chest": loadedObject = createTreasureChest(); break;
256
  case "Wall Torch": loadedObject = createWallTorch(); break;
257
  case "Bone Pile": loadedObject = createBonePile(); break;
 
 
258
  case "King Figure": loadedObject = createKingFigure(); break;
259
  case "Soldier Figure": loadedObject = createSoldierFigure(); break;
260
  case "Mage Figure": loadedObject = createMageFigure(); break;
 
263
  case "Dwarf Miner Figure": loadedObject = createDwarfMinerFigure(); break;
264
  case "Undead Knight Figure": loadedObject = createUndeadKnightFigure(); break;
265
  case "Hero Figure": loadedObject = createHeroFigure(); break;
 
 
266
  case "Wooden Cart": loadedObject = createWoodenCart(); break;
267
  case "Ballista": loadedObject = createBallista(); break;
268
  case "Siege Tower": loadedObject = createSiegeTower(); break;
 
271
  case "Hover Bike": loadedObject = createHoverBike(); break;
272
  case "APC": loadedObject = createAPC(); break;
273
  case "Sand Boat": loadedObject = createSandBoat(); break;
 
 
274
  case "Makeshift Machete": loadedObject = createMakeshiftMachete(); break;
275
  case "Pistol Body": loadedObject = createPistolBody(); break;
276
  case "Scope Attachment": loadedObject = createScopeAttachment(); break;
 
278
  case "Energy Sword": loadedObject = createEnergySword(); break;
279
  case "Dwarven Axe": loadedObject = createDwarvenAxe(); break;
280
  case "Magic Staff": loadedObject = createMagicStaff(); break;
 
 
281
  case "Candle Flame": loadedObject = createCandleFlame(); break;
282
  case "Dust Cloud": loadedObject = createDustCloud(); break;
283
  case "Blood Splat Decal": loadedObject = createBloodSplatDecal(); break;
 
286
  case "Laser Beam": loadedObject = createLaserBeam(); break;
287
  case "Gold Sparkle": loadedObject = createGoldSparkle(); break;
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
  }
 
316
  return loadedObject;
317
  }
318
  return null;
319
+ }
320
+
321
+ function createSimpleHouse() {
322
+ const base = createObjectBase("Simple House");
323
+ const group = new THREE.Group();
324
+ Object.assign(group, base);
325
+ const mat1 = new THREE.MeshStandardMaterial({color:0xffccaa, roughness:0.8});
326
+ const mat2 = new THREE.MeshStandardMaterial({color:0xaa5533, roughness:0.7});
327
+ const m1 = new THREE.Mesh(new THREE.BoxGeometry(2,1.5,2.5), mat1);
328
+ m1.position.y = 1.5/2;
329
+ m1.castShadow = true;
330
+ m1.receiveShadow = true;
331
+ group.add(m1);
332
+ const m2 = new THREE.Mesh(new THREE.ConeGeometry(1.8,1,4), mat2);
333
+ m2.position.y = 1.5+1/2;
334
+ m2.rotation.y = Math.PI/4;
335
+ m2.castShadow = true;
336
+ m2.receiveShadow = true;
337
+ group.add(m2);
338
+ return group;
339
+ }
340
+
341
+ function createTree() {
342
+ const base = createObjectBase("Tree");
343
+ const group = new THREE.Group();
344
+ Object.assign(group, base);
345
+ const mat1 = new THREE.MeshStandardMaterial({color:0x8B4513, roughness:0.9});
346
+ const mat2 = new THREE.MeshStandardMaterial({color:0x228B22, roughness:0.8});
347
+ const m1 = new THREE.Mesh(new THREE.CylinderGeometry(0.3,0.4,2,8), mat1);
348
+ m1.position.y = 1;
349
+ m1.castShadow = true;
350
+ m1.receiveShadow = true;
351
+ group.add(m1);
352
+ const m2 = new THREE.Mesh(new THREE.IcosahedronGeometry(1.2,0), mat2);
353
+ m2.position.y = 2.8;
354
+ m2.castShadow = true;
355
+ m2.receiveShadow = true;
356
+ group.add(m2);
357
+ return group;
358
+ }
359
+
360
+ function createRock() {
361
+ const base = createObjectBase("Rock");
362
+ const mat = new THREE.MeshStandardMaterial({color:0xaaaaaa, roughness:0.8, metalness:0.1});
363
+ const rock = new THREE.Mesh(new THREE.IcosahedronGeometry(0.7,0), mat);
364
+ Object.assign(rock, base);
365
+ rock.position.y = 0.35;
366
+ rock.rotation.set(Math.random()*Math.PI, Math.random()*Math.PI, 0);
367
+ rock.castShadow = true;
368
+ rock.receiveShadow = true;
369
+ return rock;
370
+ }
371
+
372
+ function createFencePost() {
373
+ const base = createObjectBase("Fence Post");
374
+ const mat = new THREE.MeshStandardMaterial({color:0xdeb887, roughness:0.9});
375
+ const post = new THREE.Mesh(new THREE.BoxGeometry(0.2,1.5,0.2), mat);
376
+ Object.assign(post, base);
377
+ post.position.y = 0.75;
378
+ post.castShadow = true;
379
+ post.receiveShadow = true;
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;
591
+ mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
592
+ }
593
+
594
+ function onDocumentClick(event) {
595
+ if (selectedObjectType === "None") return;
596
+ const groundCandidates = Object.values(groundMeshes);
597
+ if (groundCandidates.length === 0) return;
598
+ raycaster.setFromCamera(mouse, camera);
599
+ const intersects = raycaster.intersectObjects(groundCandidates);
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;
612
+ case "Castle Wall Section": newObjectToPlace = createCastleWallSection(); break;
613
+ case "Wooden Door": newObjectToPlace = createWoodenDoor(); break;
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();
636
+ console.log(`Placed new ${selectedObjectType}. Total unsaved: ${newlyPlacedObjects.length}`);
637
+ }
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}`);
651
+ if (playerMesh) {
652
+ playerMesh.position.x = targetX;
653
+ playerMesh.position.z = targetZ;
654
+ const offset = new THREE.Vector3(0, 15, 20);
655
+ const targetPosition = playerMesh.position.clone().add(offset);
656
+ camera.position.copy(targetPosition);
657
+ camera.lookAt(playerMesh.position);
658
+ console.log("Player teleported to:", playerMesh.position);
659
+ } else {
660
+ console.error("Player mesh not found for teleport.");
661
+ }
662
+ }
663
+
664
+ function getSaveDataAndPosition() {
665
+ console.log(`JS getSaveDataAndPosition called. Found ${newlyPlacedObjects.length} new objects.`);
666
+ const objectsToSave = newlyPlacedObjects.map(obj => {
667
+ if (!obj.userData || !obj.userData.type) { return null; }
668
+ const rotation = { _x: obj.rotation.x, _y: obj.rotation.y, _z: obj.rotation.z, _order: obj.rotation.order };
669
+ return {
670
+ obj_id: obj.userData.obj_id, type: obj.userData.type,
671
+ position: { x: obj.position.x, y: obj.position.y, z: obj.position.z },
672
+ rotation: rotation
673
+ };
674
+ }).filter(obj => obj !== null);
675
+ const playerPos = playerMesh ? { x: playerMesh.position.x, y: playerMesh.position.y, z: playerMesh.position.z } : {x:0, y:0, z:0};
676
+ const payload = {
677
+ playerPosition: playerPos,
678
+ objectsToSave: objectsToSave
679
+ };
680
+ console.log("Prepared payload for saving:", payload);
681
+ return JSON.stringify(payload);
682
+ }
683
+
684
+ function getSaveDataForNamedPlot() {
685
+ return getSaveDataAndPosition();
686
+ }
687
+
688
+ function resetNewlyPlacedObjects() {
689
+ console.log(`JS resetNewlyPlacedObjects called.`);
690
+ clearUnsavedState();
691
+ }
692
+
693
+ function updatePlayerMovement() {
694
+ if (!playerMesh) return;
695
+ const moveDirection = new THREE.Vector3(0, 0, 0);
696
+ if (keysPressed['KeyW'] || keysPressed['ArrowUp']) moveDirection.z -= 1;
697
+ if (keysPressed['KeyS'] || keysPressed['ArrowDown']) moveDirection.z += 1;
698
+ if (keysPressed['KeyA'] || keysPressed['ArrowLeft']) moveDirection.x -= 1;
699
+ if (keysPressed['KeyD'] || keysPressed['ArrowRight']) moveDirection.x += 1;
700
+ if (moveDirection.lengthSq() > 0) {
701
+ moveDirection.normalize().multiplyScalar(playerSpeed);
702
+ const forward = new THREE.Vector3();
703
+ camera.getWorldDirection(forward);
704
+ forward.y = 0;
705
+ forward.normalize();
706
+ const right = new THREE.Vector3().crossVectors(camera.up, forward).normalize();
707
+ const worldMove = new THREE.Vector3();
708
+ worldMove.add(forward.multiplyScalar(-moveDirection.z));
709
+ worldMove.add(right.multiplyScalar(-moveDirection.x));
710
+ worldMove.normalize().multiplyScalar(playerSpeed);
711
+ playerMesh.position.add(worldMove);
712
+ playerMesh.position.y = Math.max(playerMesh.position.y, 0.4 + 0.8/2);
713
+ checkAndExpandGround();
714
+ }
715
+ }
716
+
717
+ function checkAndExpandGround() {
718
+ if (!playerMesh) return;
719
+ const currentGridX = Math.floor(playerMesh.position.x / plotWidth);
720
+ const currentGridZ = Math.floor(playerMesh.position.z / plotDepth);
721
+ for (let dx = -1; dx <= 1; dx++) {
722
+ for (let dz = -1; dz <= 1; dz++) {
723
+ if (dx === 0 && dz === 0) continue;
724
+ const checkX = currentGridX + dx;
725
+ const checkZ = currentGridZ + dz;
726
+ const gridKey = `${checkX}_${checkZ}`;
727
+ if (!groundMeshes[gridKey]) {
728
+ const isSavedPlot = plotsMetadata.some(plot => plot.grid_x === checkX && plot.grid_z === checkZ);
729
+ if (!isSavedPlot) {
730
+ createGroundPlane(checkX, checkZ, true);
731
+ }
732
+ }
733
+ }
734
+ }
735
+ }
736
+
737
+ function updateCamera() {
738
+ if (!playerMesh) return;
739
+ const offset = new THREE.Vector3(0, 15, 20);
740
+ const targetPosition = playerMesh.position.clone().add(offset);
741
+ camera.position.lerp(targetPosition, 0.08);
742
+ camera.lookAt(playerMesh.position);
743
+ }
744
+
745
+ function onWindowResize() {
746
+ camera.aspect = window.innerWidth / window.innerHeight;
747
+ camera.updateProjectionMatrix();
748
+ renderer.setSize(window.innerWidth, window.innerHeight);
749
+ }
750
+
751
+ function animate() {
752
+ requestAnimationFrame(animate);
753
+ updatePlayerMovement();
754
+ updateCamera();
755
+ renderer.render(scene, camera);
756
+ }
757
+
758
+ init();
759
+ </script>
760
+ </body>
761
+ </html>