awacke1 commited on
Commit
6e7a654
·
verified ·
1 Parent(s): d1bc083

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1173 -19
index.html CHANGED
@@ -1,19 +1,1173 @@
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>Lake Minnetonka: Sailing Tower Defense</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
8
+ <style>
9
+ body {
10
+ margin: 0;
11
+ padding: 0;
12
+ background: #1a1a2e;
13
+ overflow: hidden;
14
+ font-family: 'Georgia', serif;
15
+ user-select: none;
16
+ }
17
+
18
+ canvas {
19
+ display: block;
20
+ cursor: crosshair;
21
+ }
22
+
23
+ .ui-container {
24
+ position: absolute;
25
+ top: 0;
26
+ left: 0;
27
+ width: 100%;
28
+ height: 100%;
29
+ pointer-events: none;
30
+ z-index: 100;
31
+ }
32
+
33
+ .ui-panel {
34
+ pointer-events: auto;
35
+ }
36
+
37
+ .controls {
38
+ position: absolute;
39
+ top: 20px;
40
+ left: 20px;
41
+ color: #d4af37;
42
+ background: rgba(0, 0, 0, 0.8);
43
+ padding: 15px;
44
+ border-radius: 10px;
45
+ border: 2px solid #8B4513;
46
+ min-width: 200px;
47
+ }
48
+
49
+ .controls h3 {
50
+ margin: 0 0 10px 0;
51
+ color: #daa520;
52
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
53
+ }
54
+
55
+ .controls p {
56
+ margin: 5px 0;
57
+ font-size: 12px;
58
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
59
+ }
60
+
61
+ .resource-panel {
62
+ position: absolute;
63
+ top: 20px;
64
+ right: 20px;
65
+ color: #d4af37;
66
+ background: rgba(0, 0, 0, 0.8);
67
+ padding: 15px;
68
+ border-radius: 10px;
69
+ border: 2px solid #8B4513;
70
+ min-width: 200px;
71
+ }
72
+
73
+ .tower-shop {
74
+ position: absolute;
75
+ bottom: 20px;
76
+ left: 20px;
77
+ color: #d4af37;
78
+ background: rgba(0, 0, 0, 0.8);
79
+ padding: 15px;
80
+ border-radius: 10px;
81
+ border: 2px solid #8B4513;
82
+ display: flex;
83
+ gap: 10px;
84
+ }
85
+
86
+ .tower-button {
87
+ background: rgba(139, 69, 19, 0.8);
88
+ border: 2px solid #d4af37;
89
+ color: #d4af37;
90
+ padding: 10px;
91
+ border-radius: 5px;
92
+ cursor: pointer;
93
+ font-family: Georgia, serif;
94
+ font-size: 12px;
95
+ transition: all 0.3s;
96
+ }
97
+
98
+ .tower-button:hover {
99
+ background: rgba(218, 165, 32, 0.3);
100
+ border-color: #daa520;
101
+ }
102
+
103
+ .tower-button.selected {
104
+ background: rgba(218, 165, 32, 0.6);
105
+ border-color: #FFD700;
106
+ }
107
+
108
+ .tower-button.disabled {
109
+ opacity: 0.5;
110
+ cursor: not-allowed;
111
+ }
112
+
113
+ .wave-info {
114
+ position: absolute;
115
+ bottom: 20px;
116
+ right: 20px;
117
+ color: #d4af37;
118
+ background: rgba(0, 0, 0, 0.8);
119
+ padding: 15px;
120
+ border-radius: 10px;
121
+ border: 2px solid #8B4513;
122
+ }
123
+
124
+ .sailing-controls {
125
+ position: absolute;
126
+ bottom: 140px;
127
+ left: 20px;
128
+ color: #d4af37;
129
+ background: rgba(0, 0, 0, 0.8);
130
+ padding: 15px;
131
+ border-radius: 10px;
132
+ border: 2px solid #8B4513;
133
+ }
134
+
135
+ .wind-indicator {
136
+ position: absolute;
137
+ top: 20px;
138
+ left: 50%;
139
+ transform: translateX(-50%);
140
+ color: #87CEEB;
141
+ background: rgba(0, 0, 0, 0.8);
142
+ padding: 10px;
143
+ border-radius: 50%;
144
+ border: 2px solid #87CEEB;
145
+ width: 60px;
146
+ height: 60px;
147
+ text-align: center;
148
+ font-size: 24px;
149
+ }
150
+
151
+ .health-bar {
152
+ position: absolute;
153
+ top: 50%;
154
+ left: 50%;
155
+ transform: translate(-50%, -50%);
156
+ width: 300px;
157
+ height: 20px;
158
+ background: rgba(139, 0, 0, 0.8);
159
+ border: 2px solid #8B4513;
160
+ border-radius: 10px;
161
+ overflow: hidden;
162
+ }
163
+
164
+ .health-fill {
165
+ height: 100%;
166
+ background: linear-gradient(90deg, #FF6B35, #DAA520);
167
+ transition: width 0.5s;
168
+ }
169
+
170
+ .game-over {
171
+ position: absolute;
172
+ top: 50%;
173
+ left: 50%;
174
+ transform: translate(-50%, -50%);
175
+ color: #d4af37;
176
+ background: rgba(0, 0, 0, 0.9);
177
+ padding: 30px;
178
+ border-radius: 15px;
179
+ border: 3px solid #8B4513;
180
+ text-align: center;
181
+ display: none;
182
+ }
183
+
184
+ .restart-button {
185
+ background: rgba(139, 69, 19, 0.8);
186
+ border: 2px solid #d4af37;
187
+ color: #d4af37;
188
+ padding: 15px 30px;
189
+ border-radius: 10px;
190
+ cursor: pointer;
191
+ font-family: Georgia, serif;
192
+ font-size: 16px;
193
+ margin-top: 20px;
194
+ }
195
+ </style>
196
+ </head>
197
+ <body>
198
+ <div class="ui-container">
199
+ <div class="controls ui-panel">
200
+ <h3>🏴‍☠️ Captain's Orders</h3>
201
+ <p>⚓ WASD: Sail your ship</p>
202
+ <p>🌊 Space: Adjust sails</p>
203
+ <p>🎯 Click: Place towers</p>
204
+ <p>🔄 R: Rotate ship</p>
205
+ <p>⚡ Tab: Quick towers</p>
206
+ </div>
207
+
208
+ <div class="resource-panel ui-panel">
209
+ <h3>⚓ Ship Status</h3>
210
+ <p>💰 Gold: <span id="gold">500</span></p>
211
+ <p>🏴‍☠️ Lives: <span id="lives">3</span></p>
212
+ <p>⚔️ Wave: <span id="wave">1</span></p>
213
+ <p>🎯 Score: <span id="score">0</span></p>
214
+ </div>
215
+
216
+ <div class="wind-indicator ui-panel">
217
+ <div id="wind-arrow">💨</div>
218
+ </div>
219
+
220
+ <div class="tower-shop ui-panel">
221
+ <button class="tower-button" data-tower="cannon" data-cost="100">
222
+ 🏰 Cannon<br/>$100
223
+ </button>
224
+ <button class="tower-button" data-tower="harpoon" data-cost="150">
225
+ 🎯 Harpoon<br/>$150
226
+ </button>
227
+ <button class="tower-button" data-tower="net" data-cost="200">
228
+ 🕸️ Net Trap<br/>$200
229
+ </button>
230
+ <button class="tower-button" data-tower="lighthouse" data-cost="300">
231
+ 🗼 Lighthouse<br/>$300
232
+ </button>
233
+ </div>
234
+
235
+ <div class="sailing-controls ui-panel">
236
+ <h4>⛵ Sailing</h4>
237
+ <p>Wind Speed: <span id="wind-speed">5</span> kts</p>
238
+ <p>Ship Speed: <span id="ship-speed">0</span> kts</p>
239
+ <p>Heading: <span id="heading">N</span></p>
240
+ </div>
241
+
242
+ <div class="wave-info ui-panel">
243
+ <h4>🌊 Wave Status</h4>
244
+ <p>Enemies: <span id="enemies-left">0</span></p>
245
+ <p>Next Wave: <span id="wave-timer">30</span>s</p>
246
+ <button id="start-wave" class="tower-button">Start Wave</button>
247
+ </div>
248
+
249
+ <div class="health-bar ui-panel">
250
+ <div class="health-fill" id="health-fill" style="width: 100%"></div>
251
+ </div>
252
+
253
+ <div class="game-over ui-panel" id="game-over">
254
+ <h2>⚓ Game Over ⚓</h2>
255
+ <p>Your fleet has been defeated!</p>
256
+ <p>Final Score: <span id="final-score">0</span></p>
257
+ <button class="restart-button" onclick="restartGame()">Set Sail Again</button>
258
+ </div>
259
+ </div>
260
+
261
+ <script>
262
+ // Game state
263
+ let gameState = {
264
+ gold: 500,
265
+ lives: 3,
266
+ wave: 1,
267
+ score: 0,
268
+ health: 100,
269
+ selectedTower: null,
270
+ gameRunning: true,
271
+ waveActive: false,
272
+ enemies: [],
273
+ towers: [],
274
+ projectiles: [],
275
+ particles: [],
276
+ windDirection: 0,
277
+ windSpeed: 5
278
+ };
279
+
280
+ // Scene setup
281
+ const scene = new THREE.Scene();
282
+ const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
283
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
284
+ renderer.setSize(window.innerWidth, window.innerHeight);
285
+ renderer.shadowMap.enabled = true;
286
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
287
+ document.body.appendChild(renderer.domElement);
288
+
289
+ // Camera controls
290
+ let cameraOffset = new THREE.Vector3(0, 15, 20);
291
+
292
+ // Player ship
293
+ let playerShip;
294
+ let shipRotation = 0;
295
+ let shipSpeed = 0;
296
+ let sailsOut = 0.5;
297
+ const keys = {};
298
+
299
+ // Atmospheric fog
300
+ scene.fog = new THREE.Fog(0x2d1810, 50, 1000);
301
+
302
+ // Create atmospheric sky
303
+ const skyGeometry = new THREE.SphereGeometry(1000, 32, 32);
304
+ const skyMaterial = new THREE.ShaderMaterial({
305
+ uniforms: {
306
+ time: { value: 0 }
307
+ },
308
+ vertexShader: `
309
+ varying vec3 vWorldPosition;
310
+ void main() {
311
+ vec4 worldPosition = modelMatrix * vec4(position, 1.0);
312
+ vWorldPosition = worldPosition.xyz;
313
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
314
+ }
315
+ `,
316
+ fragmentShader: `
317
+ uniform float time;
318
+ varying vec3 vWorldPosition;
319
+ void main() {
320
+ vec3 direction = normalize(vWorldPosition);
321
+ float elevation = direction.y;
322
+
323
+ vec3 dayBlue = vec3(0.3, 0.6, 0.9);
324
+ vec3 horizonGold = vec3(0.9, 0.7, 0.3);
325
+ vec3 deepBlue = vec3(0.1, 0.2, 0.4);
326
+
327
+ float horizonGlow = exp(-abs(elevation) * 1.5);
328
+ vec3 color = mix(deepBlue, dayBlue, elevation + 0.3);
329
+ color = mix(color, horizonGold, horizonGlow * 0.6);
330
+
331
+ gl_FragColor = vec4(color, 1.0);
332
+ }
333
+ `,
334
+ side: THREE.BackSide
335
+ });
336
+ const sky = new THREE.Mesh(skyGeometry, skyMaterial);
337
+ scene.add(sky);
338
+
339
+ // Create Lake Minnetonka water system
340
+ const lakeGeometry = new THREE.PlaneGeometry(2000, 2000, 200, 200);
341
+ const lakeMaterial = new THREE.ShaderMaterial({
342
+ uniforms: {
343
+ time: { value: 0 }
344
+ },
345
+ vertexShader: `
346
+ uniform float time;
347
+ varying vec2 vUv;
348
+ varying vec3 vPosition;
349
+
350
+ void main() {
351
+ vUv = uv;
352
+ vec3 pos = position;
353
+
354
+ // Multiple wave patterns for realistic lake movement
355
+ float wave1 = sin(pos.x * 0.02 + time * 0.5) * 0.2;
356
+ float wave2 = sin(pos.y * 0.015 + time * 0.7) * 0.15;
357
+ float wave3 = sin((pos.x + pos.y) * 0.01 + time * 0.3) * 0.25;
358
+ pos.z = wave1 + wave2 + wave3;
359
+
360
+ vPosition = pos;
361
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
362
+ }
363
+ `,
364
+ fragmentShader: `
365
+ uniform float time;
366
+ varying vec2 vUv;
367
+ varying vec3 vPosition;
368
+
369
+ void main() {
370
+ vec3 deepWater = vec3(0.1, 0.3, 0.5);
371
+ vec3 shallowWater = vec3(0.3, 0.5, 0.7);
372
+
373
+ float wave = sin(vPosition.x * 0.1 + time) * sin(vPosition.y * 0.08 + time);
374
+ vec3 waterColor = mix(deepWater, shallowWater, wave * 0.5 + 0.5);
375
+
376
+ gl_FragColor = vec4(waterColor, 0.8);
377
+ }
378
+ `,
379
+ transparent: true
380
+ });
381
+ lakeGeometry.rotateX(-Math.PI / 2);
382
+ const lake = new THREE.Mesh(lakeGeometry, lakeMaterial);
383
+ scene.add(lake);
384
+
385
+ // Create multiple lake sections representing different areas of Minnetonka
386
+ function createLakeSection(x, z, size, name) {
387
+ const sectionGeometry = new THREE.RingGeometry(size * 0.8, size, 32);
388
+ const sectionMaterial = new THREE.MeshBasicMaterial({
389
+ color: 0x87CEEB,
390
+ transparent: true,
391
+ opacity: 0.1,
392
+ side: THREE.DoubleSide
393
+ });
394
+ const section = new THREE.Mesh(sectionGeometry, sectionMaterial);
395
+ section.rotation.x = -Math.PI / 2;
396
+ section.position.set(x, 0.1, z);
397
+ section.userData = { name: name };
398
+ scene.add(section);
399
+ }
400
+
401
+ // Create the different bays of Lake Minnetonka
402
+ createLakeSection(0, 0, 50, "Upper Lake");
403
+ createLakeSection(-80, -60, 40, "Wayzata Bay");
404
+ createLakeSection(70, -50, 35, "Crystal Bay");
405
+ createLakeSection(-50, 80, 30, "Carmans Bay");
406
+ createLakeSection(60, 70, 32, "Smithtown Bay");
407
+
408
+ // Create islands and shoreline features
409
+ function createIsland(x, z, size) {
410
+ const islandGeometry = new THREE.CylinderGeometry(size, size * 1.2, 2, 16);
411
+ const islandMaterial = new THREE.MeshLambertMaterial({ color: 0x4d5d3d });
412
+ const island = new THREE.Mesh(islandGeometry, islandMaterial);
413
+ island.position.set(x, 0, z);
414
+ island.castShadow = true;
415
+ island.userData = { type: 'buildable' };
416
+ scene.add(island);
417
+
418
+ // Add some trees
419
+ for (let i = 0; i < 3; i++) {
420
+ const treeGeometry = new THREE.ConeGeometry(1, 4, 8);
421
+ const treeMaterial = new THREE.MeshLambertMaterial({ color: 0x2d4d1d });
422
+ const tree = new THREE.Mesh(treeGeometry, treeMaterial);
423
+ tree.position.set(
424
+ x + (Math.random() - 0.5) * size * 0.8,
425
+ 2,
426
+ z + (Math.random() - 0.5) * size * 0.8
427
+ );
428
+ scene.add(tree);
429
+ }
430
+
431
+ return island;
432
+ }
433
+
434
+ // Create strategic islands for tower placement
435
+ const islands = [
436
+ createIsland(-20, -30, 8),
437
+ createIsland(30, -20, 6),
438
+ createIsland(-40, 20, 7),
439
+ createIsland(25, 35, 5),
440
+ createIsland(0, -50, 9),
441
+ createIsland(-60, -10, 6),
442
+ createIsland(45, 10, 7)
443
+ ];
444
+
445
+ // Create player ship
446
+ function createPlayerShip() {
447
+ const shipGroup = new THREE.Group();
448
+
449
+ // Hull
450
+ const hullGeometry = new THREE.BoxGeometry(2, 0.5, 6);
451
+ const hullMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 });
452
+ const hull = new THREE.Mesh(hullGeometry, hullMaterial);
453
+ hull.position.y = 0.25;
454
+ shipGroup.add(hull);
455
+
456
+ // Mast
457
+ const mastGeometry = new THREE.CylinderGeometry(0.1, 0.1, 8);
458
+ const mastMaterial = new THREE.MeshLambertMaterial({ color: 0x654321 });
459
+ const mast = new THREE.Mesh(mastGeometry, mastMaterial);
460
+ mast.position.y = 4;
461
+ shipGroup.add(mast);
462
+
463
+ // Sail
464
+ const sailGeometry = new THREE.PlaneGeometry(3, 5);
465
+ const sailMaterial = new THREE.MeshLambertMaterial({
466
+ color: 0xffffff,
467
+ transparent: true,
468
+ opacity: 0.9,
469
+ side: THREE.DoubleSide
470
+ });
471
+ const sail = new THREE.Mesh(sailGeometry, sailMaterial);
472
+ sail.position.set(1.5, 4, 0);
473
+ sail.userData = { type: 'sail' };
474
+ shipGroup.add(sail);
475
+
476
+ shipGroup.position.set(0, 1, 0);
477
+ return shipGroup;
478
+ }
479
+
480
+ playerShip = createPlayerShip();
481
+ scene.add(playerShip);
482
+
483
+ // Enemy ship creation
484
+ function createEnemyShip() {
485
+ const shipGroup = new THREE.Group();
486
+
487
+ // Hull (smaller and darker)
488
+ const hullGeometry = new THREE.BoxGeometry(1.5, 0.4, 4);
489
+ const hullMaterial = new THREE.MeshLambertMaterial({ color: 0x2c1810 });
490
+ const hull = new THREE.Mesh(hullGeometry, hullMaterial);
491
+ hull.position.y = 0.2;
492
+ shipGroup.add(hull);
493
+
494
+ // Pirate flag
495
+ const flagGeometry = new THREE.PlaneGeometry(1, 1);
496
+ const flagMaterial = new THREE.MeshLambertMaterial({
497
+ color: 0x000000,
498
+ transparent: true,
499
+ opacity: 0.8
500
+ });
501
+ const flag = new THREE.Mesh(flagGeometry, flagMaterial);
502
+ flag.position.set(0, 3, 0);
503
+ shipGroup.add(flag);
504
+
505
+ return shipGroup;
506
+ }
507
+
508
+ // Tower creation functions
509
+ function createCannonTower(position) {
510
+ const towerGroup = new THREE.Group();
511
+
512
+ // Base
513
+ const baseGeometry = new THREE.CylinderGeometry(1.5, 2, 2);
514
+ const baseMaterial = new THREE.MeshLambertMaterial({ color: 0x654321 });
515
+ const base = new THREE.Mesh(baseGeometry, baseMaterial);
516
+ towerGroup.add(base);
517
+
518
+ // Cannon
519
+ const cannonGeometry = new THREE.CylinderGeometry(0.2, 0.3, 2);
520
+ const cannonMaterial = new THREE.MeshLambertMaterial({ color: 0x2c2c2c });
521
+ const cannon = new THREE.Mesh(cannonGeometry, cannonMaterial);
522
+ cannon.rotation.z = Math.PI / 2;
523
+ cannon.position.y = 1.5;
524
+ towerGroup.add(cannon);
525
+
526
+ towerGroup.position.copy(position);
527
+ towerGroup.userData = {
528
+ type: 'cannon',
529
+ range: 30,
530
+ damage: 25,
531
+ fireRate: 1000,
532
+ lastFire: 0,
533
+ cost: 100
534
+ };
535
+
536
+ return towerGroup;
537
+ }
538
+
539
+ function createHarpoonTower(position) {
540
+ const towerGroup = new THREE.Group();
541
+
542
+ // Base
543
+ const baseGeometry = new THREE.BoxGeometry(2, 1, 2);
544
+ const baseMaterial = new THREE.MeshLambertMaterial({ color: 0x8B7355 });
545
+ const base = new THREE.Mesh(baseGeometry, baseMaterial);
546
+ towerGroup.add(base);
547
+
548
+ // Harpoon launcher
549
+ const launcherGeometry = new THREE.BoxGeometry(0.5, 0.5, 3);
550
+ const launcherMaterial = new THREE.MeshLambertMaterial({ color: 0x2c2c2c });
551
+ const launcher = new THREE.Mesh(launcherGeometry, launcherMaterial);
552
+ launcher.position.y = 1;
553
+ towerGroup.add(launcher);
554
+
555
+ towerGroup.position.copy(position);
556
+ towerGroup.userData = {
557
+ type: 'harpoon',
558
+ range: 40,
559
+ damage: 40,
560
+ fireRate: 1500,
561
+ lastFire: 0,
562
+ cost: 150
563
+ };
564
+
565
+ return towerGroup;
566
+ }
567
+
568
+ function createNetTower(position) {
569
+ const towerGroup = new THREE.Group();
570
+
571
+ // Base
572
+ const baseGeometry = new THREE.CylinderGeometry(1, 1.5, 1.5);
573
+ const baseMaterial = new THREE.MeshLambertMaterial({ color: 0x4d5d3d });
574
+ const base = new THREE.Mesh(baseGeometry, baseMaterial);
575
+ towerGroup.add(base);
576
+
577
+ // Net mechanism
578
+ const netGeometry = new THREE.TorusGeometry(1, 0.1, 8, 16);
579
+ const netMaterial = new THREE.MeshLambertMaterial({ color: 0x8B8B00 });
580
+ const net = new THREE.Mesh(netGeometry, netMaterial);
581
+ net.position.y = 2;
582
+ towerGroup.add(net);
583
+
584
+ towerGroup.position.copy(position);
585
+ towerGroup.userData = {
586
+ type: 'net',
587
+ range: 25,
588
+ damage: 15,
589
+ fireRate: 2000,
590
+ lastFire: 0,
591
+ slow: 0.5,
592
+ cost: 200
593
+ };
594
+
595
+ return towerGroup;
596
+ }
597
+
598
+ function createLighthouseTower(position) {
599
+ const towerGroup = new THREE.Group();
600
+
601
+ // Base
602
+ const baseGeometry = new THREE.CylinderGeometry(1.5, 2, 8);
603
+ const baseMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
604
+ const base = new THREE.Mesh(baseGeometry, baseMaterial);
605
+ base.position.y = 4;
606
+ towerGroup.add(base);
607
+
608
+ // Light
609
+ const lightGeometry = new THREE.SphereGeometry(0.5);
610
+ const lightMaterial = new THREE.MeshBasicMaterial({ color: 0xFFFF00 });
611
+ const light = new THREE.Mesh(lightGeometry, lightMaterial);
612
+ light.position.y = 8.5;
613
+ towerGroup.add(light);
614
+
615
+ // Area light effect
616
+ const areaLight = new THREE.PointLight(0xFFFF00, 1, 60);
617
+ areaLight.position.y = 8.5;
618
+ towerGroup.add(areaLight);
619
+
620
+ towerGroup.position.copy(position);
621
+ towerGroup.userData = {
622
+ type: 'lighthouse',
623
+ range: 50,
624
+ damage: 10,
625
+ fireRate: 500,
626
+ lastFire: 0,
627
+ areaEffect: true,
628
+ cost: 300
629
+ };
630
+
631
+ return towerGroup;
632
+ }
633
+
634
+ // Projectile creation
635
+ function createProjectile(start, target, type) {
636
+ let projectileGeometry, projectileMaterial;
637
+
638
+ switch(type) {
639
+ case 'cannonball':
640
+ projectileGeometry = new THREE.SphereGeometry(0.2);
641
+ projectileMaterial = new THREE.MeshLambertMaterial({ color: 0x2c2c2c });
642
+ break;
643
+ case 'harpoon':
644
+ projectileGeometry = new THREE.CylinderGeometry(0.05, 0.05, 1);
645
+ projectileMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 });
646
+ break;
647
+ case 'net':
648
+ projectileGeometry = new THREE.PlaneGeometry(1, 1);
649
+ projectileMaterial = new THREE.MeshLambertMaterial({
650
+ color: 0x8B8B00,
651
+ transparent: true,
652
+ opacity: 0.7
653
+ });
654
+ break;
655
+ case 'lighthouse':
656
+ projectileGeometry = new THREE.SphereGeometry(0.1);
657
+ projectileMaterial = new THREE.MeshBasicMaterial({ color: 0xFFFF00 });
658
+ break;
659
+ }
660
+
661
+ const projectile = new THREE.Mesh(projectileGeometry, projectileMaterial);
662
+ projectile.position.copy(start);
663
+
664
+ const direction = new THREE.Vector3().subVectors(target, start).normalize();
665
+ const distance = start.distanceTo(target);
666
+
667
+ projectile.userData = {
668
+ type: type,
669
+ target: target.clone(),
670
+ direction: direction,
671
+ speed: 0.5,
672
+ life: distance / 0.5
673
+ };
674
+
675
+ return projectile;
676
+ }
677
+
678
+ // Enemy spawning
679
+ function spawnEnemyWave() {
680
+ const waveSize = gameState.wave * 3 + 2;
681
+ const spawnPoints = [
682
+ new THREE.Vector3(-100, 1, -100),
683
+ new THREE.Vector3(100, 1, -100),
684
+ new THREE.Vector3(-100, 1, 100),
685
+ new THREE.Vector3(100, 1, 100)
686
+ ];
687
+
688
+ for (let i = 0; i < waveSize; i++) {
689
+ setTimeout(() => {
690
+ const spawnPoint = spawnPoints[i % spawnPoints.length];
691
+ const enemy = createEnemyShip();
692
+ enemy.position.copy(spawnPoint);
693
+
694
+ const health = 50 + gameState.wave * 10;
695
+ const speed = 0.1 + gameState.wave * 0.02;
696
+
697
+ enemy.userData = {
698
+ type: 'enemy',
699
+ health: health,
700
+ maxHealth: health,
701
+ speed: speed,
702
+ value: 25 + gameState.wave * 5,
703
+ slowFactor: 1,
704
+ slowTime: 0
705
+ };
706
+
707
+ gameState.enemies.push(enemy);
708
+ scene.add(enemy);
709
+ }, i * 1000);
710
+ }
711
+ }
712
+
713
+ // Combat system
714
+ function updateTowers() {
715
+ const currentTime = Date.now();
716
+
717
+ gameState.towers.forEach(tower => {
718
+ if (currentTime - tower.userData.lastFire < tower.userData.fireRate) return;
719
+
720
+ // Find nearest enemy in range
721
+ let nearestEnemy = null;
722
+ let nearestDistance = Infinity;
723
+
724
+ gameState.enemies.forEach(enemy => {
725
+ const distance = tower.position.distanceTo(enemy.position);
726
+ if (distance <= tower.userData.range && distance < nearestDistance) {
727
+ nearestEnemy = enemy;
728
+ nearestDistance = distance;
729
+ }
730
+ });
731
+
732
+ if (nearestEnemy) {
733
+ tower.userData.lastFire = currentTime;
734
+
735
+ // Create projectile
736
+ const projectileType = {
737
+ 'cannon': 'cannonball',
738
+ 'harpoon': 'harpoon',
739
+ 'net': 'net',
740
+ 'lighthouse': 'lighthouse'
741
+ }[tower.userData.type];
742
+
743
+ const startPos = tower.position.clone();
744
+ startPos.y += 2;
745
+
746
+ const projectile = createProjectile(startPos, nearestEnemy.position, projectileType);
747
+ gameState.projectiles.push(projectile);
748
+ scene.add(projectile);
749
+
750
+ // Point tower at target
751
+ tower.lookAt(nearestEnemy.position);
752
+ }
753
+ });
754
+ }
755
+
756
+ function updateProjectiles() {
757
+ gameState.projectiles.forEach((projectile, index) => {
758
+ projectile.userData.life -= 1;
759
+
760
+ if (projectile.userData.life <= 0) {
761
+ scene.remove(projectile);
762
+ gameState.projectiles.splice(index, 1);
763
+ return;
764
+ }
765
+
766
+ // Move projectile
767
+ const movement = projectile.userData.direction.clone().multiplyScalar(projectile.userData.speed);
768
+ projectile.position.add(movement);
769
+
770
+ // Check collision with enemies
771
+ gameState.enemies.forEach((enemy, enemyIndex) => {
772
+ if (projectile.position.distanceTo(enemy.position) < 2) {
773
+ // Hit!
774
+ const tower = gameState.towers.find(t =>
775
+ t.userData.type === projectile.userData.type.replace('ball', '').replace('lighthouse', 'lighthouse')
776
+ );
777
+
778
+ if (tower) {
779
+ enemy.userData.health -= tower.userData.damage;
780
+
781
+ // Special effects
782
+ if (tower.userData.type === 'net') {
783
+ enemy.userData.slowFactor = tower.userData.slow;
784
+ enemy.userData.slowTime = 3000;
785
+ }
786
+
787
+ // Create hit particle effect
788
+ createHitEffect(enemy.position);
789
+ }
790
+
791
+ scene.remove(projectile);
792
+ gameState.projectiles.splice(index, 1);
793
+ }
794
+ });
795
+ });
796
+ }
797
+
798
+ function updateEnemies() {
799
+ gameState.enemies.forEach((enemy, index) => {
800
+ // Remove dead enemies
801
+ if (enemy.userData.health <= 0) {
802
+ gameState.gold += enemy.userData.value;
803
+ gameState.score += enemy.userData.value;
804
+ scene.remove(enemy);
805
+ gameState.enemies.splice(index, 1);
806
+ return;
807
+ }
808
+
809
+ // Update slow effect
810
+ if (enemy.userData.slowTime > 0) {
811
+ enemy.userData.slowTime -= 16;
812
+ if (enemy.userData.slowTime <= 0) {
813
+ enemy.userData.slowFactor = 1;
814
+ }
815
+ }
816
+
817
+ // Move towards player ship
818
+ const direction = new THREE.Vector3().subVectors(playerShip.position, enemy.position).normalize();
819
+ const speed = enemy.userData.speed * enemy.userData.slowFactor;
820
+ enemy.position.add(direction.multiplyScalar(speed));
821
+ enemy.lookAt(playerShip.position);
822
+
823
+ // Check if reached player
824
+ if (enemy.position.distanceTo(playerShip.position) < 3) {
825
+ gameState.health -= 20;
826
+ gameState.lives--;
827
+ scene.remove(enemy);
828
+ gameState.enemies.splice(index, 1);
829
+
830
+ if (gameState.health <= 0 || gameState.lives <= 0) {
831
+ endGame();
832
+ }
833
+ }
834
+ });
835
+ }
836
+
837
+ // Visual effects
838
+ function createHitEffect(position) {
839
+ for (let i = 0; i < 10; i++) {
840
+ const particleGeometry = new THREE.SphereGeometry(0.1);
841
+ const particleMaterial = new THREE.MeshBasicMaterial({
842
+ color: Math.random() < 0.5 ? 0xFF6B35 : 0xFFFF00
843
+ });
844
+ const particle = new THREE.Mesh(particleGeometry, particleMaterial);
845
+ particle.position.copy(position);
846
+
847
+ particle.userData = {
848
+ velocity: new THREE.Vector3(
849
+ (Math.random() - 0.5) * 0.2,
850
+ Math.random() * 0.2,
851
+ (Math.random() - 0.5) * 0.2
852
+ ),
853
+ life: 60
854
+ };
855
+
856
+ gameState.particles.push(particle);
857
+ scene.add(particle);
858
+ }
859
+ }
860
+
861
+ function updateParticles() {
862
+ gameState.particles.forEach((particle, index) => {
863
+ particle.userData.life--;
864
+
865
+ if (particle.userData.life <= 0) {
866
+ scene.remove(particle);
867
+ gameState.particles.splice(index, 1);
868
+ return;
869
+ }
870
+
871
+ particle.position.add(particle.userData.velocity);
872
+ particle.userData.velocity.y -= 0.005; // Gravity
873
+ particle.material.opacity = particle.userData.life / 60;
874
+ });
875
+ }
876
+
877
+ // Sailing physics
878
+ function updateShipPhysics() {
879
+ // Wind effect on sailing
880
+ const windAngle = gameState.windDirection;
881
+ const windEffect = Math.cos(shipRotation - windAngle) * gameState.windSpeed * sailsOut;
882
+
883
+ // Calculate ship speed based on wind
884
+ shipSpeed = Math.max(0, windEffect * 0.1);
885
+
886
+ // Move ship
887
+ const movement = new THREE.Vector3(
888
+ Math.sin(shipRotation) * shipSpeed,
889
+ 0,
890
+ Math.cos(shipRotation) * shipSpeed
891
+ );
892
+
893
+ playerShip.position.add(movement);
894
+
895
+ // Constrain to lake bounds
896
+ playerShip.position.x = Math.max(-90, Math.min(90, playerShip.position.x));
897
+ playerShip.position.z = Math.max(-90, Math.min(90, playerShip.position.z));
898
+
899
+ // Update sail animation based on wind
900
+ const sail = playerShip.children.find(child => child.userData.type === 'sail');
901
+ if (sail) {
902
+ sail.rotation.y = Math.sin(Date.now() * 0.003) * 0.1 * sailsOut;
903
+ }
904
+ }
905
+
906
+ // Input handling
907
+ document.addEventListener('keydown', (event) => {
908
+ keys[event.code] = true;
909
+
910
+ if (event.code === 'Space') {
911
+ event.preventDefault();
912
+ sailsOut = sailsOut === 1 ? 0.3 : 1;
913
+ }
914
+
915
+ if (event.code === 'KeyR') {
916
+ shipRotation += Math.PI / 4;
917
+ }
918
+
919
+ // Quick tower selection
920
+ if (event.code === 'Tab') {
921
+ event.preventDefault();
922
+ cycleTowerSelection();
923
+ }
924
+ });
925
+
926
+ document.addEventListener('keyup', (event) => {
927
+ keys[event.code] = false;
928
+ });
929
+
930
+ // Ship movement
931
+ function updateShipMovement() {
932
+ const turnSpeed = 0.05;
933
+ const acceleration = 0.01;
934
+
935
+ if (keys['KeyA']) shipRotation -= turnSpeed;
936
+ if (keys['KeyD']) shipRotation += turnSpeed;
937
+ if (keys['KeyW']) sailsOut = Math.min(1, sailsOut + acceleration);
938
+ if (keys['KeyS']) sailsOut = Math.max(0, sailsOut - acceleration);
939
+
940
+ playerShip.rotation.y = shipRotation;
941
+ }
942
+
943
+ // Camera following
944
+ function updateCamera() {
945
+ const targetPosition = playerShip.position.clone().add(cameraOffset);
946
+ camera.position.lerp(targetPosition, 0.05);
947
+ camera.lookAt(playerShip.position);
948
+ }
949
+
950
+ // Tower placement
951
+ let raycaster = new THREE.Raycaster();
952
+ let mouse = new THREE.Vector2();
953
+
954
+ renderer.domElement.addEventListener('click', (event) => {
955
+ if (!gameState.selectedTower) return;
956
+
957
+ mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
958
+ mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
959
+
960
+ raycaster.setFromCamera(mouse, camera);
961
+ const intersects = raycaster.intersectObjects(islands);
962
+
963
+ if (intersects.length > 0) {
964
+ const position = intersects[0].point;
965
+ position.y = 2;
966
+
967
+ const cost = parseInt(document.querySelector(`[data-tower="${gameState.selectedTower}"]`).dataset.cost);
968
+
969
+ if (gameState.gold >= cost) {
970
+ let tower;
971
+ switch(gameState.selectedTower) {
972
+ case 'cannon':
973
+ tower = createCannonTower(position);
974
+ break;
975
+ case 'harpoon':
976
+ tower = createHarpoonTower(position);
977
+ break;
978
+ case 'net':
979
+ tower = createNetTower(position);
980
+ break;
981
+ case 'lighthouse':
982
+ tower = createLighthouseTower(position);
983
+ break;
984
+ }
985
+
986
+ if (tower) {
987
+ gameState.towers.push(tower);
988
+ scene.add(tower);
989
+ gameState.gold -= cost;
990
+ updateUI();
991
+ }
992
+ }
993
+ }
994
+ });
995
+
996
+ // UI event handlers
997
+ document.querySelectorAll('.tower-button').forEach(button => {
998
+ if (button.dataset.tower) {
999
+ button.addEventListener('click', (e) => {
1000
+ e.stopPropagation();
1001
+ const towerType = button.dataset.tower;
1002
+ const cost = parseInt(button.dataset.cost);
1003
+
1004
+ if (gameState.gold >= cost) {
1005
+ gameState.selectedTower = towerType;
1006
+ document.querySelectorAll('.tower-button').forEach(b => b.classList.remove('selected'));
1007
+ button.classList.add('selected');
1008
+ }
1009
+ });
1010
+ }
1011
+ });
1012
+
1013
+ document.getElementById('start-wave').addEventListener('click', () => {
1014
+ if (!gameState.waveActive) {
1015
+ startWave();
1016
+ }
1017
+ });
1018
+
1019
+ // Game management
1020
+ function startWave() {
1021
+ gameState.waveActive = true;
1022
+ spawnEnemyWave();
1023
+ document.getElementById('start-wave').style.display = 'none';
1024
+ }
1025
+
1026
+ function checkWaveComplete() {
1027
+ if (gameState.waveActive && gameState.enemies.length === 0) {
1028
+ gameState.waveActive = false;
1029
+ gameState.wave++;
1030
+ gameState.gold += 100;
1031
+ document.getElementById('start-wave').style.display = 'block';
1032
+ updateUI();
1033
+ }
1034
+ }
1035
+
1036
+ function updateUI() {
1037
+ document.getElementById('gold').textContent = gameState.gold;
1038
+ document.getElementById('lives').textContent = gameState.lives;
1039
+ document.getElementById('wave').textContent = gameState.wave;
1040
+ document.getElementById('score').textContent = gameState.score;
1041
+ document.getElementById('enemies-left').textContent = gameState.enemies.length;
1042
+ document.getElementById('health-fill').style.width = `${gameState.health}%`;
1043
+ document.getElementById('wind-speed').textContent = gameState.windSpeed;
1044
+ document.getElementById('ship-speed').textContent = shipSpeed.toFixed(1);
1045
+
1046
+ const headings = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
1047
+ const headingIndex = Math.round((shipRotation + Math.PI) / (Math.PI / 4)) % 8;
1048
+ document.getElementById('heading').textContent = headings[headingIndex];
1049
+
1050
+ // Update wind indicator
1051
+ const windArrow = document.getElementById('wind-arrow');
1052
+ windArrow.style.transform = `rotate(${gameState.windDirection * 180 / Math.PI}deg)`;
1053
+
1054
+ // Update tower button states
1055
+ document.querySelectorAll('.tower-button').forEach(button => {
1056
+ if (button.dataset.cost) {
1057
+ const cost = parseInt(button.dataset.cost);
1058
+ button.classList.toggle('disabled', gameState.gold < cost);
1059
+ }
1060
+ });
1061
+ }
1062
+
1063
+ function cycleTowerSelection() {
1064
+ const towers = ['cannon', 'harpoon', 'net', 'lighthouse'];
1065
+ const currentIndex = towers.indexOf(gameState.selectedTower);
1066
+ const nextIndex = (currentIndex + 1) % towers.length;
1067
+
1068
+ const button = document.querySelector(`[data-tower="${towers[nextIndex]}"]`);
1069
+ if (button && !button.classList.contains('disabled')) {
1070
+ button.click();
1071
+ }
1072
+ }
1073
+
1074
+ function endGame() {
1075
+ gameState.gameRunning = false;
1076
+ document.getElementById('final-score').textContent = gameState.score;
1077
+ document.getElementById('game-over').style.display = 'block';
1078
+ }
1079
+
1080
+ function restartGame() {
1081
+ // Reset game state
1082
+ gameState = {
1083
+ gold: 500,
1084
+ lives: 3,
1085
+ wave: 1,
1086
+ score: 0,
1087
+ health: 100,
1088
+ selectedTower: null,
1089
+ gameRunning: true,
1090
+ waveActive: false,
1091
+ enemies: [],
1092
+ towers: [],
1093
+ projectiles: [],
1094
+ particles: [],
1095
+ windDirection: 0,
1096
+ windSpeed: 5
1097
+ };
1098
+
1099
+ // Clear scene
1100
+ [...gameState.enemies, ...gameState.towers, ...gameState.projectiles, ...gameState.particles]
1101
+ .forEach(obj => scene.remove(obj));
1102
+
1103
+ // Reset ship position
1104
+ playerShip.position.set(0, 1, 0);
1105
+ shipRotation = 0;
1106
+ shipSpeed = 0;
1107
+ sailsOut = 0.5;
1108
+
1109
+ document.getElementById('game-over').style.display = 'none';
1110
+ document.getElementById('start-wave').style.display = 'block';
1111
+
1112
+ updateUI();
1113
+ }
1114
+
1115
+ // Environmental effects
1116
+ function updateEnvironment() {
1117
+ // Change wind periodically
1118
+ if (Math.random() < 0.001) {
1119
+ gameState.windDirection += (Math.random() - 0.5) * 0.5;
1120
+ gameState.windSpeed = 3 + Math.random() * 7;
1121
+ }
1122
+ }
1123
+
1124
+ // Lighting
1125
+ const ambientLight = new THREE.AmbientLight(0x404070, 0.4);
1126
+ scene.add(ambientLight);
1127
+
1128
+ const sunLight = new THREE.DirectionalLight(0xFFE4B5, 0.8);
1129
+ sunLight.position.set(100, 100, 50);
1130
+ sunLight.castShadow = true;
1131
+ sunLight.shadow.mapSize.width = 2048;
1132
+ sunLight.shadow.mapSize.height = 2048;
1133
+ scene.add(sunLight);
1134
+
1135
+ // Animation loop
1136
+ let time = 0;
1137
+ function animate() {
1138
+ requestAnimationFrame(animate);
1139
+ time += 0.01;
1140
+
1141
+ if (gameState.gameRunning) {
1142
+ updateShipMovement();
1143
+ updateShipPhysics();
1144
+ updateCamera();
1145
+ updateTowers();
1146
+ updateProjectiles();
1147
+ updateEnemies();
1148
+ updateParticles();
1149
+ updateEnvironment();
1150
+ checkWaveComplete();
1151
+ updateUI();
1152
+ }
1153
+
1154
+ // Update water animation
1155
+ lakeMaterial.uniforms.time.value = time;
1156
+ skyMaterial.uniforms.time.value = time;
1157
+
1158
+ renderer.render(scene, camera);
1159
+ }
1160
+
1161
+ // Window resize handler
1162
+ window.addEventListener('resize', () => {
1163
+ camera.aspect = window.innerWidth / window.innerHeight;
1164
+ camera.updateProjectionMatrix();
1165
+ renderer.setSize(window.innerWidth, window.innerHeight);
1166
+ });
1167
+
1168
+ // Initialize
1169
+ updateUI();
1170
+ animate();
1171
+ </script>
1172
+ </body>
1173
+ </html>