kirikir13 commited on
Commit
a8fefcd
·
verified ·
1 Parent(s): 0d06493

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +651 -19
index.html CHANGED
@@ -1,19 +1,651 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Game-Unknown | 3D Sandbox</title>
7
+
8
+ <!-- Fonts -->
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Rajdhani:wght@300;500;700&display=swap" rel="stylesheet">
12
+
13
+ <!-- Three.js and Addons -->
14
+ <script type="importmap">
15
+ {
16
+ "imports": {
17
+ "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
18
+ "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
19
+ }
20
+ }
21
+ </script>
22
+
23
+ <style>
24
+ :root {
25
+ --primary: #00f260;
26
+ --secondary: #0575e6;
27
+ --glass: rgba(255, 255, 255, 0.1);
28
+ --glass-border: rgba(255, 255, 255, 0.2);
29
+ --text: #ffffff;
30
+ --bg-gradient: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
31
+ }
32
+
33
+ * {
34
+ margin: 0;
35
+ padding: 0;
36
+ box-sizing: border-box;
37
+ user-select: none;
38
+ }
39
+
40
+ body {
41
+ font-family: 'Rajdhani', sans-serif;
42
+ background: var(--bg-gradient);
43
+ color: var(--text);
44
+ overflow: hidden;
45
+ height: 100vh;
46
+ width: 100vw;
47
+ }
48
+
49
+ /* --- Canvas --- */
50
+ #game-canvas {
51
+ position: fixed;
52
+ top: 0;
53
+ left: 0;
54
+ width: 100%;
55
+ height: 100%;
56
+ z-index: 1;
57
+ display: none; /* Hidden until start */
58
+ }
59
+
60
+ /* --- UI Overlay --- */
61
+ #ui-layer {
62
+ position: relative;
63
+ z-index: 10;
64
+ width: 100%;
65
+ height: 100%;
66
+ pointer-events: none; /* Let clicks pass to canvas when locked */
67
+ display: flex;
68
+ flex-direction: column;
69
+ justify-content: space-between;
70
+ }
71
+
72
+ /* --- Header --- */
73
+ header {
74
+ padding: 20px;
75
+ display: flex;
76
+ justify-content: space-between;
77
+ align-items: center;
78
+ background: linear-gradient(to bottom, rgba(0,0,0,0.8), transparent);
79
+ pointer-events: auto;
80
+ }
81
+
82
+ .logo {
83
+ font-family: 'Orbitron', sans-serif;
84
+ font-size: 1.5rem;
85
+ font-weight: 700;
86
+ background: linear-gradient(to right, var(--primary), var(--secondary));
87
+ -webkit-background-clip: text;
88
+ -webkit-text-fill-color: transparent;
89
+ text-transform: uppercase;
90
+ letter-spacing: 2px;
91
+ }
92
+
93
+ .anycoder-link {
94
+ font-size: 0.9rem;
95
+ color: rgba(255,255,255,0.7);
96
+ text-decoration: none;
97
+ border-bottom: 1px solid var(--primary);
98
+ transition: 0.3s;
99
+ }
100
+ .anycoder-link:hover { color: var(--primary); }
101
+
102
+ /* --- Setup Screen (Modal) --- */
103
+ #setup-screen {
104
+ position: absolute;
105
+ top: 0;
106
+ left: 0;
107
+ width: 100%;
108
+ height: 100%;
109
+ background: rgba(15, 12, 41, 0.9);
110
+ backdrop-filter: blur(10px);
111
+ display: flex;
112
+ justify-content: center;
113
+ align-items: center;
114
+ z-index: 20;
115
+ pointer-events: auto;
116
+ transition: opacity 0.5s ease;
117
+ }
118
+
119
+ .panel {
120
+ background: var(--glass);
121
+ border: 1px solid var(--glass-border);
122
+ padding: 40px;
123
+ border-radius: 20px;
124
+ width: 90%;
125
+ max-width: 500px;
126
+ box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
127
+ }
128
+
129
+ h2 {
130
+ font-family: 'Orbitron', sans-serif;
131
+ margin-bottom: 20px;
132
+ text-align: center;
133
+ }
134
+
135
+ .form-group {
136
+ margin-bottom: 20px;
137
+ }
138
+
139
+ label {
140
+ display: block;
141
+ margin-bottom: 8px;
142
+ font-weight: 700;
143
+ color: var(--primary);
144
+ }
145
+
146
+ input[type="text"], select, input[type="file"] {
147
+ width: 100%;
148
+ padding: 12px;
149
+ background: rgba(0, 0, 0, 0.5);
150
+ border: 1px solid var(--glass-border);
151
+ color: white;
152
+ border-radius: 8px;
153
+ font-family: 'Rajdhani', sans-serif;
154
+ font-size: 1.1rem;
155
+ outline: none;
156
+ transition: 0.3s;
157
+ }
158
+
159
+ input:focus, select:focus {
160
+ border-color: var(--primary);
161
+ box-shadow: 0 0 10px rgba(0, 242, 96, 0.3);
162
+ }
163
+
164
+ .btn {
165
+ width: 100%;
166
+ padding: 15px;
167
+ border: none;
168
+ border-radius: 8px;
169
+ background: linear-gradient(90deg, var(--secondary), var(--primary));
170
+ color: #000;
171
+ font-family: 'Orbitron', sans-serif;
172
+ font-weight: 700;
173
+ font-size: 1.2rem;
174
+ cursor: pointer;
175
+ transition: transform 0.2s, box-shadow 0.2s;
176
+ text-transform: uppercase;
177
+ }
178
+
179
+ .btn:hover {
180
+ transform: scale(1.02);
181
+ box-shadow: 0 0 20px rgba(0, 242, 96, 0.6);
182
+ }
183
+
184
+ /* --- HUD --- */
185
+ #hud {
186
+ display: none; /* Hidden initially */
187
+ padding: 20px;
188
+ pointer-events: auto;
189
+ }
190
+
191
+ .hud-top {
192
+ display: flex;
193
+ justify-content: space-between;
194
+ font-size: 1.2rem;
195
+ font-weight: 700;
196
+ }
197
+
198
+ .crosshair {
199
+ position: absolute;
200
+ top: 50%;
201
+ left: 50%;
202
+ width: 20px;
203
+ height: 20px;
204
+ transform: translate(-50%, -50%);
205
+ pointer-events: none;
206
+ }
207
+ .crosshair::before, .crosshair::after {
208
+ content: '';
209
+ position: absolute;
210
+ background: var(--primary);
211
+ }
212
+ .crosshair::before { top: 9px; left: 0; width: 20px; height: 2px; }
213
+ .crosshair::after { top: 0; left: 9px; width: 2px; height: 20px; }
214
+
215
+ .controls-hint {
216
+ text-align: center;
217
+ font-size: 0.9rem;
218
+ opacity: 0.7;
219
+ margin-top: 10px;
220
+ }
221
+
222
+ /* --- Notifications --- */
223
+ #notifications {
224
+ position: fixed;
225
+ bottom: 20px;
226
+ right: 20px;
227
+ display: flex;
228
+ flex-direction: column;
229
+ gap: 10px;
230
+ z-index: 30;
231
+ }
232
+ .toast {
233
+ background: rgba(0,0,0,0.8);
234
+ border-left: 4px solid var(--primary);
235
+ padding: 15px;
236
+ border-radius: 4px;
237
+ animation: slideIn 0.3s ease-out;
238
+ }
239
+ @keyframes slideIn {
240
+ from { transform: translateX(100%); opacity: 0; }
241
+ to { transform: translateX(0); opacity: 1; }
242
+ }
243
+
244
+ /* Weapon Model */
245
+ #weapon-container {
246
+ position: absolute;
247
+ bottom: 0;
248
+ right: 20%;
249
+ width: 200px;
250
+ height: 200px;
251
+ display: none; /* Hidden until game starts */
252
+ pointer-events: none;
253
+ }
254
+
255
+ /* Loading Spinner */
256
+ .loader {
257
+ border: 4px solid rgba(255, 255, 255, 0.1);
258
+ border-left-color: var(--primary);
259
+ border-radius: 50%;
260
+ width: 30px;
261
+ height: 30px;
262
+ animation: spin 1s linear infinite;
263
+ margin: 0 auto;
264
+ display: none;
265
+ }
266
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
267
+
268
+ </style>
269
+ </head>
270
+ <body>
271
+
272
+ <!-- 3D Canvas -->
273
+ <canvas id="game-canvas"></canvas>
274
+
275
+ <!-- UI Layer -->
276
+ <div id="ui-layer">
277
+ <header>
278
+ <div class="logo">Game-Unknown</div>
279
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with AnyCoder</a>
280
+ </header>
281
+
282
+ <!-- HUD -->
283
+ <div id="hud">
284
+ <div class="hud-top">
285
+ <div id="player-health">HP: 100%</div>
286
+ <div id="world-name">World: Greek City</div>
287
+ </div>
288
+ <div class="crosshair"></div>
289
+ <div class="controls-hint">WASD to Move | SPACE to Jump | CLICK to Shoot | ESC to Pause</div>
290
+ </div>
291
+ </div>
292
+
293
+ <!-- Weapon Overlay (3D Model attached to camera later) -->
294
+ <div id="weapon-container"></div>
295
+
296
+ <!-- Setup Screen -->
297
+ <div id="setup-screen">
298
+ <div class="panel">
299
+ <h2>Mission Init</h2>
300
+
301
+ <div class="form-group">
302
+ <label>HF Token (Authentication)</label>
303
+ <input type="text" id="hf-token" placeholder="Enter HuggingFace Token...">
304
+ </div>
305
+
306
+ <div class="form-group">
307
+ <label>Load Custom Model (.glb / .gltf)</label>
308
+ <input type="file" id="model-upload" accept=".glb, .gltf">
309
+ <div style="text-align: center; margin: 5px 0; font-size: 0.8rem;">OR</div>
310
+ <select id="preset-model">
311
+ <option value="default">Select Preset: Soldier</option>
312
+ <option value="wizard">Preset: Wizard</option>
313
+ <option value="cyborg">Preset: Cyborg</option>
314
+ </select>
315
+ </div>
316
+
317
+ <div class="form-group">
318
+ <label>World Environment</label>
319
+ <select id="world-select">
320
+ <option value="greek">Greek City (Ruins)</option>
321
+ <option value="warehouse">Abandoned Warehouse</option>
322
+ <option value="cyber">Neon Cyber Field</option>
323
+ </select>
324
+ </div>
325
+
326
+ <div class="loader" id="loader"></div>
327
+ <button class="btn" id="enter-btn">ENTER SIMULATION</button>
328
+ </div>
329
+ </div>
330
+
331
+ <div id="notifications"></div>
332
+
333
+ <script type="module">
334
+ import * as THREE from 'three';
335
+ import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
336
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
337
+
338
+ // --- State & Config ---
339
+ const state = {
340
+ moveForward: false,
341
+ moveBackward: false,
342
+ moveLeft: false,
343
+ moveRight: false,
344
+ canJump: false,
345
+ velocity: new THREE.Vector3(),
346
+ direction: new THREE.Vector3(),
347
+ isPlaying: false,
348
+ world: 'greek'
349
+ };
350
+
351
+ // --- Scene Setup ---
352
+ const canvas = document.querySelector('#game-canvas');
353
+ const scene = new THREE.Scene();
354
+ scene.background = new THREE.Color(0x050510);
355
+ scene.fog = new THREE.FogExp2(0x050510, 0.02);
356
+
357
+ const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
358
+ camera.position.y = 1.6; // Eye level
359
+
360
+ const renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
361
+ renderer.setSize(window.innerWidth, window.innerHeight);
362
+ renderer.shadowMap.enabled = true;
363
+
364
+ // --- Lighting ---
365
+ const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);
366
+ scene.add(hemiLight);
367
+
368
+ const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
369
+ dirLight.position.set(10, 20, 10);
370
+ dirLight.castShadow = true;
371
+ dirLight.shadow.camera.top = 50;
372
+ dirLight.shadow.camera.bottom = -50;
373
+ dirLight.shadow.camera.left = -50;
374
+ dirLight.shadow.camera.right = 50;
375
+ scene.add(dirLight);
376
+
377
+ // --- Controls ---
378
+ const controls = new PointerLockControls(camera, document.body);
379
+
380
+ // --- Weapon Visuals (Procedural Gun) ---
381
+ const weaponGeo = new THREE.BoxGeometry(0.2, 0.2, 0.6);
382
+ const weaponMat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.2, metalness: 0.8 });
383
+ const weapon = new THREE.Mesh(weaponGeo, weaponMat);
384
+ weapon.position.set(0.3, -0.3, -0.5);
385
+ camera.add(weapon);
386
+ scene.add(camera); // Add camera to scene so weapon renders
387
+
388
+ // --- Physics / Movement Logic ---
389
+ let prevTime = performance.now();
390
+
391
+ const onKeyDown = function (event) {
392
+ switch (event.code) {
393
+ case 'ArrowUp': case 'KeyW': state.moveForward = true; break;
394
+ case 'ArrowLeft': case 'KeyA': state.moveLeft = true; break;
395
+ case 'ArrowDown': case 'KeyS': state.moveBackward = true; break;
396
+ case 'ArrowRight': case 'KeyD': state.moveRight = true; break;
397
+ case 'Space': if (state.canJump === true) state.velocity.y += 350; state.canJump = false; break;
398
+ }
399
+ };
400
+
401
+ const onKeyUp = function (event) {
402
+ switch (event.code) {
403
+ case 'ArrowUp': case 'KeyW': state.moveForward = false; break;
404
+ case 'ArrowLeft': case 'KeyA': state.moveLeft = false; break;
405
+ case 'ArrowDown': case 'KeyS': state.moveBackward = false; break;
406
+ case 'ArrowRight': case 'KeyD': state.moveRight = false; break;
407
+ }
408
+ };
409
+
410
+ document.addEventListener('keydown', onKeyDown);
411
+ document.addEventListener('keyup', onKeyUp);
412
+
413
+ // --- Mouse Click (Shoot) ---
414
+ document.addEventListener('mousedown', () => {
415
+ if (state.isPlaying && controls.isLocked) {
416
+ // Visual Recoil
417
+ weapon.position.z = -0.3;
418
+ setTimeout(() => { weapon.position.z = -0.5; }, 100);
419
+
420
+ // Simple Raycast to see if we hit the 'enemies'
421
+ const raycaster = new THREE.Raycaster();
422
+ raycaster.setFromCamera(new THREE.Vector2(0,0), camera);
423
+
424
+ // Find targets
425
+ const intersects = raycaster.intersectObjects(enemies.children);
426
+ if(intersects.length > 0) {
427
+ const hitObj = intersects[0].object;
428
+ // "Kill" effect
429
+ hitObj.material.color.setHex(0xff0000);
430
+ scene.remove(hitObj);
431
+ notify("Target Eliminated");
432
+ }
433
+ }
434
+ });
435
+
436
+ // --- World Generation ---
437
+ const enemies = new THREE.Group();
438
+ scene.add(enemies);
439
+
440
+ function createEnvironment(type) {
441
+ // Clear previous
442
+ while(scene.children.length > 0){
443
+ const obj = scene.children[0];
444
+ if(obj.type === "Mesh" || obj.type === "Group") {
445
+ if(obj !== camera && obj !== enemies) scene.remove(obj);
446
+ }
447
+ }
448
+ enemies.clear();
449
+
450
+ // Floor
451
+ let floorGeo, floorMat;
452
+ if(type === 'cyber') {
453
+ floorGeo = new THREE.PlaneGeometry(200, 200, 100, 100);
454
+ floorMat = new THREE.MeshStandardMaterial({ color: 0x000000, wireframe: true, emissive: 0x0575e6, emissiveIntensity: 0.5 });
455
+ } else {
456
+ floorGeo = new THREE.PlaneGeometry(200, 200);
457
+ floorMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.8 });
458
+ }
459
+ const floor = new THREE.Mesh(floorGeo, floorMat);
460
+ floor.rotation.x = - Math.PI / 2;
461
+ floor.receiveShadow = true;
462
+ scene.add(floor);
463
+
464
+ // Procedural Props based on World Type
465
+ if (type === 'greek') {
466
+ for(let i=0; i<50; i++) {
467
+ const colGeo = new THREE.CylinderGeometry(1, 1, 10, 6);
468
+ const colMat = new THREE.MeshStandardMaterial({ color: 0xdddddd });
469
+ const column = new THREE.Mesh(colGeo, colMat);
470
+ column.position.set((Math.random()-0.5)*100, 5, (Math.random()-0.5)*100);
471
+ column.castShadow = true;
472
+ column.receiveShadow = true;
473
+ scene.add(column);
474
+
475
+ // Spawn Enemy nearby
476
+ spawnEnemy(column.position);
477
+ }
478
+ } else if (type === 'warehouse') {
479
+ for(let i=0; i<20; i++) {
480
+ const boxGeo = new THREE.BoxGeometry(5, 5, 5);
481
+ const boxMat = new THREE.MeshStandardMaterial({ color: 0x555555 });
482
+ const box = new THREE.Mesh(boxGeo, boxMat);
483
+ box.position.set((Math.random()-0.5)*80, 2.5, (Math.random()-0.5)*80);
484
+ box.castShadow = true;
485
+ scene.add(box);
486
+ spawnEnemy(box.position);
487
+ }
488
+ } else {
489
+ // Cyber / Generic
490
+ for(let i=0; i<40; i++) {
491
+ const geo = new THREE.TorusGeometry(2, 0.5, 8, 20);
492
+ const mat = new THREE.MeshStandardMaterial({ color: 0x00f260, emissive: 0x00f260, emissiveIntensity: 0.8 });
493
+ const ring = new THREE.Mesh(geo, mat);
494
+ ring.position.set((Math.random()-0.5)*100, 3, (Math.random()-0.5)*100);
495
+ ring.rotation.x = Math.PI / 2;
496
+ scene.add(ring);
497
+ spawnEnemy(ring.position);
498
+ }
499
+ }
500
+ }
501
+
502
+ function spawnEnemy(refPos) {
503
+ const geo = new THREE.SphereGeometry(1, 32, 32);
504
+ const mat = new THREE.MeshStandardMaterial({ color: 0xff0000 });
505
+ const enemy = new THREE.Mesh(geo, mat);
506
+ // Random position near reference
507
+ enemy.position.set(
508
+ refPos.x + (Math.random()-0.5)*20,
509
+ 2,
510
+ refPos.z + (Math.random()-0.5)*20
511
+ );
512
+ enemy.userData = { moveDir: Math.random() };
513
+ enemies.add(enemy);
514
+ }
515
+
516
+ // --- Model Loading Logic ---
517
+ function loadPlayerModel(source, file = null) {
518
+ // Remove existing child (default camera child is weapon, we want to attach model to a group parented to camera)
519
+
520
+ // For this demo, we will just change the camera height or simple geometry
521
+ // Implementing full GLTF loading requires actual file URLs.
522
+ // Since this is a text response, we simulate the upload process.
523
+
524
+ if (file) {
525
+ const url = URL.createObjectURL(file);
526
+ const loader = new GLTFLoader();
527
+ loader.load(url, function (gltf) {
528
+ const model = gltf.scene;
529
+ model.scale.set(1, 1, 1); // Adjust scale
530
+ // Ideally attach to a wrapper group at camera position
531
+ notify("Custom Model Loaded Successfully");
532
+ }, undefined, function (error) {
533
+ console.error(error);
534
+ notify("Error loading model. Using Default.");
535
+ });
536
+ } else {
537
+ notify(`Loaded Preset: ${source}`);
538
+ }
539
+ }
540
+
541
+ // --- UI Interactions ---
542
+ const setupScreen = document.getElementById('setup-screen');
543
+ const hud = document.getElementById('hud');
544
+ const enterBtn = document.getElementById('enter-btn');
545
+ const worldSelect = document.getElementById('world-select');
546
+ const modelUpload = document.getElementById('model-upload');
547
+ const presetModel = document.getElementById('preset-model');
548
+ const loader = document.getElementById('loader');
549
+
550
+ enterBtn.addEventListener('click', () => {
551
+ const token = document.getElementById('hf-token').value;
552
+ if(!token) {
553
+ alert("Please enter a HuggingFace Token (or any text to proceed).");
554
+ return;
555
+ }
556
+
557
+ loader.style.display = 'block';
558
+ enterBtn.style.opacity = '0.5';
559
+
560
+ // Simulate Loading
561
+ setTimeout(() => {
562
+ state.world = worldSelect.value;
563
+ loadPlayerModel(presetModel.value, modelUpload.files[0]);
564
+ createEnvironment(state.world);
565
+
566
+ loader.style.display = 'none';
567
+ setupScreen.style.opacity = '0';
568
+ setTimeout(() => {
569
+ setupScreen.style.display = 'none';
570
+ hud.style.display = 'block';
571
+ canvas.style.display = 'block';
572
+ controls.lock();
573
+ state.isPlaying = true;
574
+ }, 500);
575
+ }, 1500);
576
+ });
577
+
578
+ // Handle Pointer Lock Events
579
+ controls.addEventListener('lock', function () {
580
+ state.isPlaying = true;
581
+ });
582
+ controls.addEventListener('unlock', function () {
583
+ state.isPlaying = false;
584
+ // Show pause menu logic could go here
585
+ notify("Paused - Click to Resume");
586
+ });
587
+
588
+ function notify(msg) {
589
+ const container = document.getElementById('notifications');
590
+ const el = document.createElement('div');
591
+ el.className = 'toast';
592
+ el.innerText = msg;
593
+ container.appendChild(el);
594
+ setTimeout(() => el.remove(), 3000);
595
+ }
596
+
597
+ // --- Main Loop ---
598
+ function animate() {
599
+ requestAnimationFrame(animate);
600
+
601
+ if (state.isPlaying) {
602
+ const time = performance.now();
603
+ const delta = (time - prevTime) / 1000;
604
+
605
+ // Movement Physics
606
+ state.velocity.x -= state.velocity.x * 10.0 * delta;
607
+ state.velocity.z -= state.velocity.z * 10.0 * delta;
608
+ state.velocity.y -= 9.8 * 100.0 * delta; // Gravity
609
+
610
+ state.direction.z = Number(state.moveForward) - Number(state.moveBackward);
611
+ state.direction.x = Number(state.moveRight) - Number(state.moveLeft);
612
+ state.direction.normalize();
613
+
614
+ if (state.moveForward || state.moveBackward) state.velocity.z -= state.direction.z * 400.0 * delta;
615
+ if (state.moveLeft || state.moveRight) state.velocity.x -= state.direction.x * 400.0 * delta;
616
+
617
+ controls.moveRight(- state.velocity.x * delta);
618
+ controls.moveForward(- state.velocity.z * delta);
619
+ camera.position.y += (state.velocity.y * delta); // Simple gravity
620
+
621
+ // Floor Collision
622
+ if (camera.position.y < 1.6) {
623
+ state.velocity.y = 0;
624
+ camera.position.y = 1.6;
625
+ state.canJump = true;
626
+ }
627
+
628
+ // Animate Enemies (Bobbing)
629
+ enemies.children.forEach((child, idx) => {
630
+ child.position.y = 2 + Math.sin(time * 0.003 + idx) * 0.5;
631
+ child.rotation.y += 0.01;
632
+ });
633
+
634
+ prevTime = time;
635
+ }
636
+
637
+ renderer.render(scene, camera);
638
+ }
639
+
640
+ // Window Resize
641
+ window.addEventListener('resize', () => {
642
+ camera.aspect = window.innerWidth / window.innerHeight;
643
+ camera.updateProjectionMatrix();
644
+ renderer.setSize(window.innerWidth, window.innerHeight);
645
+ });
646
+
647
+ animate();
648
+
649
+ </script>
650
+ </body>
651
+ </html>