awacke1 commited on
Commit
9cd2956
·
verified ·
1 Parent(s): 429d45a

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +928 -19
index.html CHANGED
@@ -1,19 +1,928 @@
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>3D Slithering</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ padding: 0;
11
+ background: #000;
12
+ overflow: hidden;
13
+ font-family: 'Arial', sans-serif;
14
+ cursor: none;
15
+ }
16
+
17
+ #gameContainer {
18
+ position: relative;
19
+ width: 100vw;
20
+ height: 100vh;
21
+ }
22
+
23
+ #ui {
24
+ position: absolute;
25
+ top: 20px;
26
+ left: 20px;
27
+ color: #fff;
28
+ z-index: 100;
29
+ font-size: 18px;
30
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
31
+ }
32
+
33
+ #powers {
34
+ position: absolute;
35
+ top: 20px;
36
+ right: 20px;
37
+ color: #fff;
38
+ z-index: 100;
39
+ font-size: 14px;
40
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
41
+ }
42
+
43
+ #instructions {
44
+ position: absolute;
45
+ top: 50%;
46
+ left: 50%;
47
+ transform: translate(-50%, -50%);
48
+ color: #fff;
49
+ text-align: center;
50
+ z-index: 200;
51
+ background: rgba(0,0,0,0.8);
52
+ padding: 30px;
53
+ border-radius: 15px;
54
+ display: block;
55
+ border: 2px solid #00ffff;
56
+ }
57
+
58
+ #instructions.hidden {
59
+ display: none;
60
+ }
61
+
62
+ .glow {
63
+ text-shadow: 0 0 10px #00ffff, 0 0 20px #00ffff, 0 0 30px #00ffff;
64
+ }
65
+
66
+ .power-active {
67
+ color: #00ff00;
68
+ text-shadow: 0 0 10px #00ff00;
69
+ }
70
+
71
+ .power-cooldown {
72
+ color: #ff6600;
73
+ text-shadow: 0 0 5px #ff6600;
74
+ }
75
+ </style>
76
+ </head>
77
+ <body>
78
+ <div id="gameContainer">
79
+ <div id="ui">
80
+ <div>Length: <span id="length" class="glow">5</span></div>
81
+ <div>Score: <span id="score" class="glow">0</span></div>
82
+ <div>Speed: <span id="speed" class="glow">1.0x</span></div>
83
+ </div>
84
+
85
+ <div id="powers">
86
+ <div><strong>POWERS:</strong></div>
87
+ <div>🛡️ Armor: <span id="armor">0</span></div>
88
+ <div>⚡ Boost: <span id="boost">Ready</span></div>
89
+ <div>👻 Phase: <span id="phase">Ready</span></div>
90
+ <div>💥 Blast: <span id="blast">Ready</span></div>
91
+ </div>
92
+
93
+ <div id="instructions">
94
+ <h2 class="glow">3D SLITHERING</h2>
95
+ <p><strong>CONTROLS:</strong></p>
96
+ <p>🖱️ Mouse movement OR WASD keys to control</p>
97
+ <p>🔵 <strong>Blue Spheres:</strong> Basic food pellets</p>
98
+ <p>🟦 <strong>Cubes:</strong> Armor segments (protection)</p>
99
+ <p>🔺 <strong>Pyramids:</strong> Speed boost power</p>
100
+ <p>⭕ <strong>Rings:</strong> Phase through ability</p>
101
+ <p>💎 <strong>Diamonds:</strong> Explosive blast power</p>
102
+ <br>
103
+ <p><strong>POWERS:</strong></p>
104
+ <p>Press SPACE to use Speed Boost</p>
105
+ <p>Press Q to use Phase Through</p>
106
+ <p>Press E to use Explosive Blast</p>
107
+ <br>
108
+ <p>Avoid crashing into other worms!</p>
109
+ <p>Make others crash to consume their remains!</p>
110
+ <p><strong>Click anywhere to start!</strong></p>
111
+ </div>
112
+ </div>
113
+
114
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
115
+ <script>
116
+ class SlitherGame {
117
+ constructor() {
118
+ this.scene = new THREE.Scene();
119
+ this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
120
+ this.renderer = new THREE.WebGLRenderer({ antialias: true });
121
+ this.mouse = new THREE.Vector2();
122
+ this.keys = {};
123
+ this.gameStarted = false;
124
+ this.score = 0;
125
+ this.useMouseControl = true;
126
+
127
+ this.playerWorm = null;
128
+ this.aiWorms = [];
129
+ this.pellets = [];
130
+ this.remains = [];
131
+
132
+ this.worldSize = 200;
133
+
134
+ // Power system
135
+ this.powers = {
136
+ armor: { count: 0 },
137
+ speed: { ready: true, cooldown: 0, duration: 0 },
138
+ phase: { ready: true, cooldown: 0, duration: 0 },
139
+ blast: { ready: true, cooldown: 0 }
140
+ };
141
+
142
+ this.init();
143
+ this.setupLighting();
144
+ this.createEnvironment();
145
+ this.setupEventListeners();
146
+ this.animate();
147
+ }
148
+
149
+ init() {
150
+ this.renderer.setSize(window.innerWidth, window.innerHeight);
151
+ this.renderer.shadowMap.enabled = true;
152
+ this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
153
+ this.renderer.setClearColor(0x000510);
154
+ document.getElementById('gameContainer').appendChild(this.renderer.domElement);
155
+
156
+ this.camera.position.set(0, 50, 30);
157
+ this.camera.lookAt(0, 0, 0);
158
+ }
159
+
160
+ setupLighting() {
161
+ const ambientLight = new THREE.AmbientLight(0x404040, 0.3);
162
+ this.scene.add(ambientLight);
163
+
164
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
165
+ directionalLight.position.set(50, 100, 50);
166
+ directionalLight.castShadow = true;
167
+ directionalLight.shadow.mapSize.width = 2048;
168
+ directionalLight.shadow.mapSize.height = 2048;
169
+ this.scene.add(directionalLight);
170
+
171
+ for (let i = 0; i < 5; i++) {
172
+ const light = new THREE.PointLight(0x00ffff, 0.5, 100);
173
+ light.position.set(
174
+ (Math.random() - 0.5) * this.worldSize,
175
+ 20 + Math.random() * 30,
176
+ (Math.random() - 0.5) * this.worldSize
177
+ );
178
+ this.scene.add(light);
179
+ }
180
+ }
181
+
182
+ createEnvironment() {
183
+ const canvas = document.createElement('canvas');
184
+ canvas.width = 512;
185
+ canvas.height = 512;
186
+ const ctx = canvas.getContext('2d');
187
+
188
+ const imageData = ctx.createImageData(512, 512);
189
+ for (let i = 0; i < imageData.data.length; i += 4) {
190
+ const noise = Math.random() * 255;
191
+ imageData.data[i] = noise;
192
+ imageData.data[i + 1] = noise;
193
+ imageData.data[i + 2] = noise;
194
+ imageData.data[i + 3] = 255;
195
+ }
196
+ ctx.putImageData(imageData, 0, 0);
197
+
198
+ const bumpTexture = new THREE.CanvasTexture(canvas);
199
+ bumpTexture.wrapS = THREE.RepeatWrapping;
200
+ bumpTexture.wrapT = THREE.RepeatWrapping;
201
+ bumpTexture.repeat.set(8, 8);
202
+
203
+ const groundGeometry = new THREE.PlaneGeometry(this.worldSize * 2, this.worldSize * 2);
204
+ const groundMaterial = new THREE.MeshPhongMaterial({
205
+ color: 0x001122,
206
+ bumpMap: bumpTexture,
207
+ bumpScale: 2
208
+ });
209
+
210
+ const ground = new THREE.Mesh(groundGeometry, groundMaterial);
211
+ ground.rotation.x = -Math.PI / 2;
212
+ ground.receiveShadow = true;
213
+ this.scene.add(ground);
214
+
215
+ this.createBoundaries();
216
+ this.spawnPellets(30);
217
+ this.spawnPowerUps(20);
218
+ }
219
+
220
+ createBoundaries() {
221
+ const boundaryGeometry = new THREE.BoxGeometry(4, 20, 4);
222
+ const boundaryMaterial = new THREE.MeshPhongMaterial({
223
+ color: 0xff0000,
224
+ emissive: 0x330000
225
+ });
226
+
227
+ const positions = [
228
+ [-this.worldSize, 10, 0], [this.worldSize, 10, 0],
229
+ [0, 10, -this.worldSize], [0, 10, this.worldSize]
230
+ ];
231
+
232
+ positions.forEach(pos => {
233
+ const boundary = new THREE.Mesh(boundaryGeometry, boundaryMaterial);
234
+ boundary.position.set(...pos);
235
+ boundary.castShadow = true;
236
+ this.scene.add(boundary);
237
+ });
238
+ }
239
+
240
+ spawnPellets(count) {
241
+ for (let i = 0; i < count; i++) {
242
+ this.createPellet();
243
+ }
244
+ }
245
+
246
+ spawnPowerUps(count) {
247
+ const types = ['cube', 'pyramid', 'ring', 'diamond'];
248
+ for (let i = 0; i < count; i++) {
249
+ const type = types[Math.floor(Math.random() * types.length)];
250
+ this.createPowerUp(type);
251
+ }
252
+ }
253
+
254
+ createPellet() {
255
+ const geometry = new THREE.SphereGeometry(1, 8, 8);
256
+ const material = new THREE.MeshPhongMaterial({
257
+ color: new THREE.Color().setHSL(Math.random(), 1, 0.5),
258
+ emissive: new THREE.Color().setHSL(Math.random(), 0.5, 0.1)
259
+ });
260
+
261
+ const pellet = new THREE.Mesh(geometry, material);
262
+ pellet.position.set(
263
+ (Math.random() - 0.5) * this.worldSize * 1.8,
264
+ 2,
265
+ (Math.random() - 0.5) * this.worldSize * 1.8
266
+ );
267
+ pellet.castShadow = true;
268
+ pellet.userData = { type: 'pellet', value: 10 };
269
+
270
+ this.scene.add(pellet);
271
+ this.pellets.push(pellet);
272
+ }
273
+
274
+ createPowerUp(type) {
275
+ let geometry, material, color, emissive;
276
+
277
+ switch(type) {
278
+ case 'cube':
279
+ geometry = new THREE.BoxGeometry(2, 2, 2);
280
+ color = 0x4169E1;
281
+ emissive = 0x000066;
282
+ break;
283
+ case 'pyramid':
284
+ geometry = new THREE.ConeGeometry(1.5, 3, 4);
285
+ color = 0xFF6347;
286
+ emissive = 0x330000;
287
+ break;
288
+ case 'ring':
289
+ geometry = new THREE.TorusGeometry(1.5, 0.5, 8, 16);
290
+ color = 0x9932CC;
291
+ emissive = 0x330033;
292
+ break;
293
+ case 'diamond':
294
+ geometry = new THREE.OctahedronGeometry(1.5);
295
+ color = 0xFFD700;
296
+ emissive = 0x333300;
297
+ break;
298
+ }
299
+
300
+ material = new THREE.MeshPhongMaterial({
301
+ color: color,
302
+ emissive: emissive,
303
+ shininess: 100
304
+ });
305
+
306
+ const powerUp = new THREE.Mesh(geometry, material);
307
+ powerUp.position.set(
308
+ (Math.random() - 0.5) * this.worldSize * 1.8,
309
+ 3,
310
+ (Math.random() - 0.5) * this.worldSize * 1.8
311
+ );
312
+ powerUp.castShadow = true;
313
+ powerUp.userData = { type: 'powerup', subtype: type, value: 25 };
314
+
315
+ this.scene.add(powerUp);
316
+ this.pellets.push(powerUp);
317
+ }
318
+
319
+ createWorm(isPlayer = false, color = 0x00ff00) {
320
+ const worm = {
321
+ segments: [],
322
+ positions: [],
323
+ direction: new THREE.Vector3(1, 0, 0),
324
+ speed: isPlayer ? 0.5 : 0.3,
325
+ baseSpeed: isPlayer ? 0.5 : 0.3,
326
+ length: 5,
327
+ isPlayer: isPlayer,
328
+ color: color,
329
+ isDead: false,
330
+ armor: 0,
331
+ isPhasing: false
332
+ };
333
+
334
+ for (let i = 0; i < worm.length; i++) {
335
+ const segment = this.createWormSegment(color, i === 0);
336
+ segment.position.set(-i * 3, 2, 0);
337
+ worm.segments.push(segment);
338
+ worm.positions.push(segment.position.clone());
339
+ this.scene.add(segment);
340
+ }
341
+
342
+ return worm;
343
+ }
344
+
345
+ createWormSegment(color, isHead = false, isArmor = false) {
346
+ let geometry;
347
+ if (isArmor) {
348
+ geometry = new THREE.BoxGeometry(2.5, 2.5, 2.5);
349
+ } else {
350
+ geometry = new THREE.CylinderGeometry(
351
+ isHead ? 2 : 1.5,
352
+ isHead ? 2 : 1.5,
353
+ 3,
354
+ 8
355
+ );
356
+ }
357
+
358
+ const canvas = document.createElement('canvas');
359
+ canvas.width = 256;
360
+ canvas.height = 256;
361
+ const ctx = canvas.getContext('2d');
362
+
363
+ for (let y = 0; y < 256; y += 16) {
364
+ for (let x = 0; x < 256; x += 16) {
365
+ const intensity = Math.sin(x * 0.1) * Math.sin(y * 0.1) * 127 + 128;
366
+ ctx.fillStyle = `rgb(${intensity},${intensity},${intensity})`;
367
+ ctx.fillRect(x, y, 16, 16);
368
+ }
369
+ }
370
+
371
+ const bumpTexture = new THREE.CanvasTexture(canvas);
372
+
373
+ let segmentColor = color;
374
+ if (isArmor) {
375
+ segmentColor = 0x4169E1; // Blue for armor
376
+ }
377
+
378
+ const material = new THREE.MeshPhongMaterial({
379
+ color: segmentColor,
380
+ emissive: new THREE.Color(segmentColor).multiplyScalar(0.1),
381
+ bumpMap: bumpTexture,
382
+ bumpScale: 0.5,
383
+ shininess: 100,
384
+ transparent: false,
385
+ opacity: 1
386
+ });
387
+
388
+ const segment = new THREE.Mesh(geometry, material);
389
+ segment.castShadow = true;
390
+ segment.userData = { isArmor: isArmor };
391
+ return segment;
392
+ }
393
+
394
+ startGame() {
395
+ if (this.gameStarted) return;
396
+
397
+ this.gameStarted = true;
398
+ document.getElementById('instructions').classList.add('hidden');
399
+
400
+ this.playerWorm = this.createWorm(true, 0x00ff00);
401
+
402
+ for (let i = 0; i < 5; i++) {
403
+ const aiWorm = this.createWorm(false, new THREE.Color().setHSL(Math.random(), 1, 0.5).getHex());
404
+ const startPos = new THREE.Vector3(
405
+ (Math.random() - 0.5) * this.worldSize,
406
+ 2,
407
+ (Math.random() - 0.5) * this.worldSize
408
+ );
409
+ aiWorm.segments.forEach((segment, idx) => {
410
+ segment.position.copy(startPos);
411
+ segment.position.x -= idx * 3;
412
+ aiWorm.positions[idx].copy(segment.position);
413
+ });
414
+ this.aiWorms.push(aiWorm);
415
+ }
416
+ }
417
+
418
+ updateWorm(worm, targetDirection) {
419
+ if (worm.isDead) return;
420
+
421
+ if (targetDirection) {
422
+ worm.direction.lerp(targetDirection.normalize(), 0.1);
423
+ worm.direction.normalize();
424
+ }
425
+
426
+ const head = worm.segments[0];
427
+ const newPosition = head.position.clone();
428
+ newPosition.add(worm.direction.clone().multiplyScalar(worm.speed));
429
+
430
+ if (Math.abs(newPosition.x) > this.worldSize || Math.abs(newPosition.z) > this.worldSize) {
431
+ if (worm.isPlayer && !worm.isPhasing) {
432
+ this.gameOver();
433
+ return;
434
+ } else if (!worm.isPlayer) {
435
+ worm.direction.multiplyScalar(-1);
436
+ return;
437
+ }
438
+ }
439
+
440
+ worm.positions.unshift(newPosition.clone());
441
+ if (worm.positions.length > worm.length) {
442
+ worm.positions.pop();
443
+ }
444
+
445
+ worm.segments.forEach((segment, index) => {
446
+ if (index < worm.positions.length) {
447
+ segment.position.copy(worm.positions[index]);
448
+
449
+ if (index < worm.positions.length - 1) {
450
+ const direction = new THREE.Vector3()
451
+ .subVectors(worm.positions[index], worm.positions[index + 1])
452
+ .normalize();
453
+ segment.lookAt(segment.position.clone().add(direction));
454
+ if (!segment.userData.isArmor) {
455
+ segment.rotateX(Math.PI / 2);
456
+ }
457
+ }
458
+
459
+ // Phase effect
460
+ if (worm.isPhasing) {
461
+ segment.material.transparent = true;
462
+ segment.material.opacity = 0.3;
463
+ } else {
464
+ segment.material.transparent = false;
465
+ segment.material.opacity = 1;
466
+ }
467
+ }
468
+ });
469
+ }
470
+
471
+ updateAI() {
472
+ this.aiWorms.forEach(worm => {
473
+ if (worm.isDead) return;
474
+
475
+ let target = null;
476
+ let minDistance = Infinity;
477
+
478
+ this.pellets.forEach(pellet => {
479
+ const distance = worm.segments[0].position.distanceTo(pellet.position);
480
+ if (distance < minDistance) {
481
+ minDistance = distance;
482
+ target = pellet.position;
483
+ }
484
+ });
485
+
486
+ if (target) {
487
+ const targetDirection = new THREE.Vector3()
488
+ .subVectors(target, worm.segments[0].position)
489
+ .normalize();
490
+
491
+ targetDirection.add(new THREE.Vector3(
492
+ (Math.random() - 0.5) * 0.3,
493
+ 0,
494
+ (Math.random() - 0.5) * 0.3
495
+ ));
496
+
497
+ this.updateWorm(worm, targetDirection);
498
+ }
499
+ });
500
+ }
501
+
502
+ checkCollisions() {
503
+ if (!this.playerWorm || this.playerWorm.isDead) return;
504
+
505
+ const playerHead = this.playerWorm.segments[0];
506
+
507
+ for (let i = this.pellets.length - 1; i >= 0; i--) {
508
+ const pellet = this.pellets[i];
509
+ if (playerHead.position.distanceTo(pellet.position) < 3) {
510
+ this.scene.remove(pellet);
511
+ this.pellets.splice(i, 1);
512
+
513
+ if (pellet.userData.type === 'powerup') {
514
+ this.handlePowerUpConsumption(pellet.userData.subtype);
515
+ } else {
516
+ this.growWorm(this.playerWorm);
517
+ }
518
+
519
+ this.score += pellet.userData.value;
520
+ this.updateUI();
521
+
522
+ if (pellet.userData.type === 'pellet') {
523
+ this.createPellet();
524
+ } else {
525
+ setTimeout(() => {
526
+ const types = ['cube', 'pyramid', 'ring', 'diamond'];
527
+ const type = types[Math.floor(Math.random() * types.length)];
528
+ this.createPowerUp(type);
529
+ }, 5000);
530
+ }
531
+ }
532
+ }
533
+
534
+ this.aiWorms.forEach(worm => {
535
+ if (worm.isDead) return;
536
+
537
+ for (let i = this.pellets.length - 1; i >= 0; i--) {
538
+ const pellet = this.pellets[i];
539
+ if (worm.segments[0].position.distanceTo(pellet.position) < 3) {
540
+ this.scene.remove(pellet);
541
+ this.pellets.splice(i, 1);
542
+ this.growWorm(worm);
543
+ if (pellet.userData.type === 'pellet') {
544
+ this.createPellet();
545
+ }
546
+ }
547
+ }
548
+ });
549
+
550
+ this.checkWormCollisions();
551
+ }
552
+
553
+ handlePowerUpConsumption(type) {
554
+ switch(type) {
555
+ case 'cube':
556
+ this.powers.armor.count++;
557
+ this.growWorm(this.playerWorm, true); // Grow with armor segment
558
+ break;
559
+ case 'pyramid':
560
+ this.powers.speed.ready = true;
561
+ this.powers.speed.cooldown = 0;
562
+ break;
563
+ case 'ring':
564
+ this.powers.phase.ready = true;
565
+ this.powers.phase.cooldown = 0;
566
+ break;
567
+ case 'diamond':
568
+ this.powers.blast.ready = true;
569
+ this.powers.blast.cooldown = 0;
570
+ break;
571
+ }
572
+ this.growWorm(this.playerWorm);
573
+ }
574
+
575
+ usePower(powerType) {
576
+ if (!this.gameStarted || !this.playerWorm) return;
577
+
578
+ switch(powerType) {
579
+ case 'speed':
580
+ if (this.powers.speed.ready) {
581
+ this.powers.speed.ready = false;
582
+ this.powers.speed.duration = 3000; // 3 seconds
583
+ this.powers.speed.cooldown = 10000; // 10 second cooldown
584
+ this.playerWorm.speed = this.playerWorm.baseSpeed * 2;
585
+ }
586
+ break;
587
+ case 'phase':
588
+ if (this.powers.phase.ready) {
589
+ this.powers.phase.ready = false;
590
+ this.powers.phase.duration = 2000; // 2 seconds
591
+ this.powers.phase.cooldown = 15000; // 15 second cooldown
592
+ this.playerWorm.isPhasing = true;
593
+ }
594
+ break;
595
+ case 'blast':
596
+ if (this.powers.blast.ready) {
597
+ this.powers.blast.ready = false;
598
+ this.powers.blast.cooldown = 8000; // 8 second cooldown
599
+ this.createBlast();
600
+ }
601
+ break;
602
+ }
603
+ }
604
+
605
+ createBlast() {
606
+ const blastGeometry = new THREE.SphereGeometry(15, 16, 16);
607
+ const blastMaterial = new THREE.MeshPhongMaterial({
608
+ color: 0xFFD700,
609
+ emissive: 0xFFD700,
610
+ transparent: true,
611
+ opacity: 0.6
612
+ });
613
+
614
+ const blast = new THREE.Mesh(blastGeometry, blastMaterial);
615
+ blast.position.copy(this.playerWorm.segments[0].position);
616
+ this.scene.add(blast);
617
+
618
+ // Check for AI worms in blast radius
619
+ this.aiWorms.forEach(worm => {
620
+ if (!worm.isDead) {
621
+ const distance = worm.segments[0].position.distanceTo(blast.position);
622
+ if (distance < 15) {
623
+ this.killWorm(worm);
624
+ }
625
+ }
626
+ });
627
+
628
+ // Animate blast
629
+ let scale = 0;
630
+ const animate = () => {
631
+ scale += 0.1;
632
+ blast.scale.setScalar(scale);
633
+ blast.material.opacity = Math.max(0, 0.6 - scale * 0.3);
634
+
635
+ if (scale < 2) {
636
+ requestAnimationFrame(animate);
637
+ } else {
638
+ this.scene.remove(blast);
639
+ }
640
+ };
641
+ animate();
642
+ }
643
+
644
+ updatePowers() {
645
+ const now = Date.now();
646
+
647
+ // Speed power
648
+ if (this.powers.speed.duration > 0) {
649
+ this.powers.speed.duration -= 16;
650
+ if (this.powers.speed.duration <= 0) {
651
+ this.playerWorm.speed = this.playerWorm.baseSpeed;
652
+ }
653
+ } else if (this.powers.speed.cooldown > 0) {
654
+ this.powers.speed.cooldown -= 16;
655
+ if (this.powers.speed.cooldown <= 0) {
656
+ this.powers.speed.ready = true;
657
+ }
658
+ }
659
+
660
+ // Phase power
661
+ if (this.powers.phase.duration > 0) {
662
+ this.powers.phase.duration -= 16;
663
+ if (this.powers.phase.duration <= 0) {
664
+ this.playerWorm.isPhasing = false;
665
+ }
666
+ } else if (this.powers.phase.cooldown > 0) {
667
+ this.powers.phase.cooldown -= 16;
668
+ if (this.powers.phase.cooldown <= 0) {
669
+ this.powers.phase.ready = true;
670
+ }
671
+ }
672
+
673
+ // Blast power
674
+ if (this.powers.blast.cooldown > 0) {
675
+ this.powers.blast.cooldown -= 16;
676
+ if (this.powers.blast.cooldown <= 0) {
677
+ this.powers.blast.ready = true;
678
+ }
679
+ }
680
+ }
681
+
682
+ checkWormCollisions() {
683
+ if (this.playerWorm.isPhasing) return; // Skip collision during phase
684
+
685
+ const allWorms = [this.playerWorm, ...this.aiWorms];
686
+
687
+ allWorms.forEach((worm1, i) => {
688
+ if (worm1.isDead) return;
689
+
690
+ allWorms.forEach((worm2, j) => {
691
+ if (i === j || worm2.isDead) return;
692
+
693
+ for (let k = 1; k < worm2.segments.length; k++) {
694
+ if (worm1.segments[0].position.distanceTo(worm2.segments[k].position) < 3) {
695
+ // Check for armor protection
696
+ if (worm1.isPlayer && worm1.armor > 0) {
697
+ worm1.armor--;
698
+ this.powers.armor.count--;
699
+ // Remove an armor segment
700
+ for (let s = worm1.segments.length - 1; s >= 0; s--) {
701
+ if (worm1.segments[s].userData.isArmor) {
702
+ this.scene.remove(worm1.segments[s]);
703
+ worm1.segments.splice(s, 1);
704
+ worm1.positions.splice(s, 1);
705
+ worm1.length--;
706
+ break;
707
+ }
708
+ }
709
+ return; // Armor absorbed the hit
710
+ }
711
+
712
+ this.killWorm(worm1);
713
+ if (worm1.isPlayer) {
714
+ this.gameOver();
715
+ }
716
+ break;
717
+ }
718
+ }
719
+ });
720
+ });
721
+ }
722
+
723
+ growWorm(worm, isArmor = false) {
724
+ worm.length++;
725
+ const newSegment = this.createWormSegment(worm.color, false, isArmor);
726
+
727
+ if (worm.positions.length > 0) {
728
+ const lastPos = worm.positions[worm.positions.length - 1];
729
+ newSegment.position.copy(lastPos);
730
+ } else {
731
+ const lastSegment = worm.segments[worm.segments.length - 1];
732
+ newSegment.position.copy(lastSegment.position);
733
+ newSegment.position.x -= 3;
734
+ }
735
+
736
+ worm.segments.push(newSegment);
737
+ worm.positions.push(newSegment.position.clone());
738
+ this.scene.add(newSegment);
739
+
740
+ if (isArmor && worm.isPlayer) {
741
+ worm.armor++;
742
+ }
743
+ }
744
+
745
+ killWorm(worm) {
746
+ worm.isDead = true;
747
+
748
+ worm.segments.forEach(segment => {
749
+ this.scene.remove(segment);
750
+
751
+ const remain = new THREE.Mesh(
752
+ new THREE.SphereGeometry(1.5, 8, 8),
753
+ new THREE.MeshPhongMaterial({
754
+ color: 0xffff00,
755
+ emissive: 0x333300
756
+ })
757
+ );
758
+ remain.position.copy(segment.position);
759
+ remain.castShadow = true;
760
+ remain.userData = { type: 'pellet', value: 15 };
761
+ this.scene.add(remain);
762
+ this.pellets.push(remain);
763
+ });
764
+
765
+ const index = this.aiWorms.indexOf(worm);
766
+ if (index > -1) {
767
+ this.aiWorms.splice(index, 1);
768
+ setTimeout(() => this.spawnNewAIWorm(), 3000);
769
+ }
770
+ }
771
+
772
+ spawnNewAIWorm() {
773
+ const aiWorm = this.createWorm(false, new THREE.Color().setHSL(Math.random(), 1, 0.5).getHex());
774
+ const startPos = new THREE.Vector3(
775
+ (Math.random() - 0.5) * this.worldSize,
776
+ 2,
777
+ (Math.random() - 0.5) * this.worldSize
778
+ );
779
+ aiWorm.segments.forEach((segment, idx) => {
780
+ segment.position.copy(startPos);
781
+ segment.position.x -= idx * 3;
782
+ aiWorm.positions[idx].copy(segment.position);
783
+ });
784
+ this.aiWorms.push(aiWorm);
785
+ }
786
+
787
+ gameOver() {
788
+ this.gameStarted = false;
789
+ alert(`Game Over! Final Score: ${this.score}, Length: ${this.playerWorm ? this.playerWorm.length : 0}`);
790
+ location.reload();
791
+ }
792
+
793
+ updateUI() {
794
+ document.getElementById('length').textContent = this.playerWorm ? this.playerWorm.length : 0;
795
+ document.getElementById('score').textContent = this.score;
796
+ document.getElementById('speed').textContent = this.playerWorm ? (this.playerWorm.speed / this.playerWorm.baseSpeed).toFixed(1) + 'x' : '1.0x';
797
+ document.getElementById('armor').textContent = this.powers.armor.count;
798
+
799
+ // Update power status
800
+ document.getElementById('boost').textContent = this.powers.speed.ready ? 'Ready' :
801
+ this.powers.speed.duration > 0 ? 'Active' : Math.ceil(this.powers.speed.cooldown / 1000) + 's';
802
+ document.getElementById('boost').className = this.powers.speed.ready ? '' :
803
+ this.powers.speed.duration > 0 ? 'power-active' : 'power-cooldown';
804
+
805
+ document.getElementById('phase').textContent = this.powers.phase.ready ? 'Ready' :
806
+ this.powers.phase.duration > 0 ? 'Active' : Math.ceil(this.powers.phase.cooldown / 1000) + 's';
807
+ document.getElementById('phase').className = this.powers.phase.ready ? '' :
808
+ this.powers.phase.duration > 0 ? 'power-active' : 'power-cooldown';
809
+
810
+ document.getElementById('blast').textContent = this.powers.blast.ready ? 'Ready' :
811
+ Math.ceil(this.powers.blast.cooldown / 1000) + 's';
812
+ document.getElementById('blast').className = this.powers.blast.ready ? '' : 'power-cooldown';
813
+ }
814
+
815
+ getMovementDirection() {
816
+ if (this.useMouseControl) {
817
+ const raycaster = new THREE.Raycaster();
818
+ raycaster.setFromCamera(this.mouse, this.camera);
819
+
820
+ const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), -2);
821
+ const target = new THREE.Vector3();
822
+ raycaster.ray.intersectPlane(plane, target);
823
+
824
+ if (target && this.playerWorm.segments[0]) {
825
+ const direction = new THREE.Vector3()
826
+ .subVectors(target, this.playerWorm.segments[0].position)
827
+ .normalize();
828
+ direction.y = 0;
829
+ return direction;
830
+ }
831
+ } else {
832
+ // WASD controls
833
+ const direction = new THREE.Vector3();
834
+ if (this.keys['w'] || this.keys['W']) direction.z -= 1;
835
+ if (this.keys['s'] || this.keys['S']) direction.z += 1;
836
+ if (this.keys['a'] || this.keys['A']) direction.x -= 1;
837
+ if (this.keys['d'] || this.keys['D']) direction.x += 1;
838
+
839
+ if (direction.length() > 0) {
840
+ return direction.normalize();
841
+ }
842
+ }
843
+ return null;
844
+ }
845
+
846
+ setupEventListeners() {
847
+ window.addEventListener('resize', () => {
848
+ this.camera.aspect = window.innerWidth / window.innerHeight;
849
+ this.camera.updateProjectionMatrix();
850
+ this.renderer.setSize(window.innerWidth, window.innerHeight);
851
+ });
852
+
853
+ document.addEventListener('mousemove', (event) => {
854
+ this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
855
+ this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
856
+ this.useMouseControl = true;
857
+ });
858
+
859
+ document.addEventListener('keydown', (event) => {
860
+ this.keys[event.key] = true;
861
+
862
+ if (['w', 'a', 's', 'd', 'W', 'A', 'S', 'D'].includes(event.key)) {
863
+ this.useMouseControl = false;
864
+ }
865
+
866
+ if (event.key === ' ') {
867
+ event.preventDefault();
868
+ this.usePower('speed');
869
+ } else if (event.key === 'q' || event.key === 'Q') {
870
+ this.usePower('phase');
871
+ } else if (event.key === 'e' || event.key === 'E') {
872
+ this.usePower('blast');
873
+ }
874
+ });
875
+
876
+ document.addEventListener('keyup', (event) => {
877
+ this.keys[event.key] = false;
878
+ });
879
+
880
+ document.addEventListener('click', () => {
881
+ if (!this.gameStarted) {
882
+ this.startGame();
883
+ }
884
+ });
885
+ }
886
+
887
+ animate() {
888
+ requestAnimationFrame(() => this.animate());
889
+
890
+ if (this.gameStarted && this.playerWorm && !this.playerWorm.isDead) {
891
+ const direction = this.getMovementDirection();
892
+ if (direction) {
893
+ this.updateWorm(this.playerWorm, direction);
894
+ }
895
+
896
+ this.updateAI();
897
+ this.updatePowers();
898
+ this.checkCollisions();
899
+
900
+ if (this.playerWorm.segments[0]) {
901
+ const playerPos = this.playerWorm.segments[0].position;
902
+ this.camera.position.lerp(
903
+ new THREE.Vector3(playerPos.x, playerPos.y + 50, playerPos.z + 30),
904
+ 0.05
905
+ );
906
+ this.camera.lookAt(playerPos);
907
+ }
908
+ }
909
+
910
+ this.pellets.forEach(pellet => {
911
+ pellet.rotation.y += 0.02;
912
+ if (pellet.userData.type === 'powerup') {
913
+ pellet.rotation.x += 0.01;
914
+ pellet.position.y += Math.sin(Date.now() * 0.005 + pellet.position.x) * 0.02;
915
+ } else {
916
+ pellet.position.y += Math.sin(Date.now() * 0.003 + pellet.position.x) * 0.01;
917
+ }
918
+ });
919
+
920
+ this.updateUI();
921
+ this.renderer.render(this.scene, this.camera);
922
+ }
923
+ }
924
+
925
+ new SlitherGame();
926
+ </script>
927
+ </body>
928
+ </html>