crocro commited on
Commit
b808025
·
verified ·
1 Parent(s): b3d7cf1

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +1180 -19
index.html CHANGED
@@ -1,19 +1,1180 @@
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>Mattel Handheld Football Clone</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ font-family: 'Arial', sans-serif;
13
+ }
14
+
15
+ body {
16
+ background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%);
17
+ color: white;
18
+ min-height: 100vh;
19
+ display: flex;
20
+ flex-direction: column;
21
+ align-items: center;
22
+ justify-content: center;
23
+ overflow: hidden;
24
+ position: relative;
25
+ }
26
+
27
+ body::before {
28
+ content: '';
29
+ position: absolute;
30
+ top: 0;
31
+ left: 0;
32
+ right: 0;
33
+ bottom: 0;
34
+ background-image:
35
+ radial-gradient(circle at 10% 20%, rgba(255, 255, 255, 0.05) 0%, transparent 50%),
36
+ radial-gradient(circle at 90% 80%, rgba(255, 255, 255, 0.05) 0%, transparent 50%);
37
+ pointer-events: none;
38
+ }
39
+
40
+ .header {
41
+ position: absolute;
42
+ top: 20px;
43
+ left: 50%;
44
+ transform: translateX(-50%);
45
+ text-align: center;
46
+ z-index: 100;
47
+ }
48
+
49
+ .header h1 {
50
+ font-size: 2.5rem;
51
+ font-weight: 800;
52
+ text-shadow: 0 0 10px rgba(255, 0, 0, 0.5);
53
+ margin-bottom: 10px;
54
+ background: linear-gradient(90deg, #ff0000, #ff8c00);
55
+ -webkit-background-clip: text;
56
+ background-clip: text;
57
+ color: transparent;
58
+ }
59
+
60
+ .header .anycoder-link {
61
+ font-size: 0.9rem;
62
+ color: #ccc;
63
+ text-decoration: none;
64
+ transition: all 0.3s ease;
65
+ }
66
+
67
+ .header .anycoder-link:hover {
68
+ color: #fff;
69
+ text-shadow: 0 0 5px rgba(255, 255, 255, 0.5);
70
+ }
71
+
72
+ .container {
73
+ display: flex;
74
+ flex-direction: column;
75
+ align-items: center;
76
+ justify-content: center;
77
+ width: 100%;
78
+ max-width: 1200px;
79
+ padding: 20px;
80
+ }
81
+
82
+ .game-container {
83
+ perspective: 1000px;
84
+ width: 100%;
85
+ max-width: 800px;
86
+ height: 600px;
87
+ margin-bottom: 30px;
88
+ position: relative;
89
+ }
90
+
91
+ #game-canvas {
92
+ width: 100%;
93
+ height: 100%;
94
+ background: transparent;
95
+ border-radius: 10px;
96
+ box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
97
+ }
98
+
99
+ .controls {
100
+ display: flex;
101
+ flex-direction: column;
102
+ align-items: center;
103
+ gap: 20px;
104
+ margin-bottom: 20px;
105
+ }
106
+
107
+ .button-row {
108
+ display: flex;
109
+ gap: 20px;
110
+ }
111
+
112
+ .game-button {
113
+ width: 80px;
114
+ height: 80px;
115
+ border-radius: 50%;
116
+ background: linear-gradient(145deg, #333, #111);
117
+ border: none;
118
+ color: white;
119
+ font-size: 1.2rem;
120
+ font-weight: bold;
121
+ cursor: pointer;
122
+ box-shadow: 0 8px 15px rgba(0, 0, 0, 0.3);
123
+ transition: all 0.2s ease;
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: center;
127
+ position: relative;
128
+ overflow: hidden;
129
+ }
130
+
131
+ .game-button::before {
132
+ content: '';
133
+ position: absolute;
134
+ top: 0;
135
+ left: 0;
136
+ width: 100%;
137
+ height: 100%;
138
+ background: linear-gradient(45deg, rgba(255, 255, 255, 0.1), transparent);
139
+ transform: translate(-100%, -100%);
140
+ transition: transform 0.3s ease;
141
+ }
142
+
143
+ .game-button:active::before {
144
+ transform: translate(100%, 100%);
145
+ }
146
+
147
+ .game-button:active {
148
+ transform: translateY(2px);
149
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
150
+ }
151
+
152
+ .game-button.red {
153
+ background: linear-gradient(145deg, #ff0000, #cc0000);
154
+ }
155
+
156
+ .game-button.blue {
157
+ background: linear-gradient(145deg, #0066ff, #0033cc);
158
+ }
159
+
160
+ .game-button.green {
161
+ background: linear-gradient(145deg, #00ff00, #00cc00);
162
+ }
163
+
164
+ .game-button.yellow {
165
+ background: linear-gradient(145deg, #ffff00, #cccc00);
166
+ }
167
+
168
+ .game-info {
169
+ background: rgba(0, 0, 0, 0.7);
170
+ border-radius: 10px;
171
+ padding: 20px;
172
+ margin-top: 20px;
173
+ max-width: 600px;
174
+ backdrop-filter: blur(10px);
175
+ border: 1px solid rgba(255, 255, 255, 0.1);
176
+ }
177
+
178
+ .game-info h2 {
179
+ color: #ff0000;
180
+ margin-bottom: 15px;
181
+ text-align: center;
182
+ }
183
+
184
+ .game-info p {
185
+ margin-bottom: 10px;
186
+ line-height: 1.6;
187
+ }
188
+
189
+ .game-stats {
190
+ display: flex;
191
+ justify-content: space-around;
192
+ margin-top: 15px;
193
+ }
194
+
195
+ .stat {
196
+ text-align: center;
197
+ padding: 10px 20px;
198
+ border-radius: 5px;
199
+ background: rgba(255, 255, 255, 0.1);
200
+ }
201
+
202
+ .stat h3 {
203
+ color: #00ff00;
204
+ font-size: 1.5rem;
205
+ }
206
+
207
+ .stat p {
208
+ font-size: 0.9rem;
209
+ color: #ccc;
210
+ }
211
+
212
+ .instructions {
213
+ background: rgba(0, 0, 0, 0.7);
214
+ border-radius: 10px;
215
+ padding: 20px;
216
+ margin-top: 20px;
217
+ max-width: 600px;
218
+ backdrop-filter: blur(10px);
219
+ border: 1px solid rgba(255, 255, 255, 0.1);
220
+ }
221
+
222
+ .instructions h3 {
223
+ color: #00ffff;
224
+ margin-bottom: 15px;
225
+ }
226
+
227
+ .instructions ul {
228
+ list-style-type: none;
229
+ padding-left: 0;
230
+ }
231
+
232
+ .instructions li {
233
+ margin-bottom: 8px;
234
+ padding-left: 20px;
235
+ position: relative;
236
+ }
237
+
238
+ .instructions li::before {
239
+ content: "•";
240
+ color: #ff0000;
241
+ position: absolute;
242
+ left: 0;
243
+ }
244
+
245
+ .loading {
246
+ position: absolute;
247
+ top: 0;
248
+ left: 0;
249
+ width: 100%;
250
+ height: 100%;
251
+ display: flex;
252
+ flex-direction: column;
253
+ align-items: center;
254
+ justify-content: center;
255
+ background: rgba(0, 0, 0, 0.8);
256
+ z-index: 1000;
257
+ backdrop-filter: blur(5px);
258
+ }
259
+
260
+ .loading h2 {
261
+ color: #ff0000;
262
+ margin-bottom: 20px;
263
+ font-size: 2rem;
264
+ }
265
+
266
+ .loading .spinner {
267
+ width: 50px;
268
+ height: 50px;
269
+ border: 5px solid rgba(255, 255, 255, 0.3);
270
+ border-top-color: #ff0000;
271
+ border-radius: 50%;
272
+ animation: spin 1s linear infinite;
273
+ }
274
+
275
+ @keyframes spin {
276
+ to { transform: rotate(360deg); }
277
+ }
278
+
279
+ .sound-toggle {
280
+ position: absolute;
281
+ top: 20px;
282
+ right: 20px;
283
+ background: rgba(255, 255, 255, 0.1);
284
+ border: 1px solid rgba(255, 255, 255, 0.2);
285
+ color: white;
286
+ padding: 8px 15px;
287
+ border-radius: 20px;
288
+ cursor: pointer;
289
+ display: flex;
290
+ align-items: center;
291
+ gap: 8px;
292
+ transition: all 0.3s ease;
293
+ z-index: 100;
294
+ }
295
+
296
+ .sound-toggle:hover {
297
+ background: rgba(255, 255, 255, 0.2);
298
+ }
299
+
300
+ .sound-toggle i {
301
+ font-size: 1rem;
302
+ }
303
+
304
+ .sound-toggle.on i::before {
305
+ content: "🔊";
306
+ }
307
+
308
+ .sound-toggle.off i::before {
309
+ content: "🔇";
310
+ }
311
+
312
+ @media (max-width: 768px) {
313
+ .game-container {
314
+ height: 400px;
315
+ }
316
+
317
+ .button-row {
318
+ flex-direction: column;
319
+ gap: 10px;
320
+ }
321
+
322
+ .game-button {
323
+ width: 60px;
324
+ height: 60px;
325
+ font-size: 1rem;
326
+ }
327
+
328
+ .header h1 {
329
+ font-size: 1.8rem;
330
+ }
331
+
332
+ .game-info, .instructions {
333
+ padding: 15px;
334
+ }
335
+ }
336
+
337
+ @media (max-width: 480px) {
338
+ body {
339
+ padding: 10px;
340
+ }
341
+
342
+ .game-container {
343
+ height: 300px;
344
+ }
345
+
346
+ .game-button {
347
+ width: 50px;
348
+ height: 50px;
349
+ font-size: 0.9rem;
350
+ }
351
+
352
+ .header h1 {
353
+ font-size: 1.5rem;
354
+ }
355
+ }
356
+ </style>
357
+ </head>
358
+ <body>
359
+ <div class="header">
360
+ <h1>MATTEL HANDHELD FOOTBALL</h1>
361
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link" target="_blank">Built with anycoder</a>
362
+ </div>
363
+
364
+ <div class="sound-toggle" id="sound-toggle">
365
+ <i></i>
366
+ <span>Sound</span>
367
+ </div>
368
+
369
+ <div class="container">
370
+ <div class="game-container">
371
+ <canvas id="game-canvas"></canvas>
372
+ <div class="loading" id="loading">
373
+ <h2>Loading Game...</h2>
374
+ <div class="spinner"></div>
375
+ </div>
376
+ </div>
377
+
378
+ <div class="controls">
379
+ <div class="button-row">
380
+ <button class="game-button red" id="pass-button">PASS</button>
381
+ <button class="game-button blue" id="run-button">RUN</button>
382
+ <button class="game-button green" id="kick-button">KICK</button>
383
+ <button class="game-button yellow" id="reset-button">RESET</button>
384
+ </div>
385
+ </div>
386
+
387
+ <div class="game-info">
388
+ <h2>Game Status</h2>
389
+ <div class="game-stats">
390
+ <div class="stat">
391
+ <h3 id="score">0-0</h3>
392
+ <p>Score</p>
393
+ </div>
394
+ <div class="stat">
395
+ <h3 id="down">1st</h3>
396
+ <p>Down</p>
397
+ </div>
398
+ <div class="stat">
399
+ <h3 id="yards">10</h3>
400
+ <p>Yards to Go</p>
401
+ </div>
402
+ <div class="stat">
403
+ <h3 id="quarter">1</h3>
404
+ <p>Quarter</p>
405
+ </div>
406
+ </div>
407
+ </div>
408
+
409
+ <div class="instructions">
410
+ <h3>How to Play</h3>
411
+ <ul>
412
+ <li><strong>PASS:</strong> Attempt a forward pass (red button)</li>
413
+ <li><strong>RUN:</strong> Run with the ball (blue button)</li>
414
+ <li><strong>KICK:</strong> Kick the ball (green button)</li>
415
+ <li><strong>RESET:</strong> Reset the game (yellow button)</li>
416
+ <li>Get 10 yards in 4 downs to continue</li>
417
+ <li>Score touchdowns to win!</li>
418
+ </ul>
419
+ </div>
420
+ </div>
421
+
422
+ <script src="https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.min.js"></script>
423
+ <script src="https://cdn.jsdelivr.net/npm/three@0.182.0/examples/js/controls/OrbitControls.js"></script>
424
+ <script>
425
+ // Game state variables
426
+ let gameState = {
427
+ score: [0, 0],
428
+ down: 1,
429
+ yardsToGo: 10,
430
+ yardsGained: 0,
431
+ quarter: 1,
432
+ possession: 0,
433
+ gameOver: false,
434
+ playInProgress: false,
435
+ lastPlay: null,
436
+ fieldPosition: 50,
437
+ soundEnabled: true
438
+ };
439
+
440
+ // Three.js variables
441
+ let scene, camera, renderer, controls;
442
+ let device, screen, buttons = [];
443
+ let field, ball;
444
+ let clock = new THREE.Clock();
445
+ let mixer, animations = [];
446
+
447
+ // Sound effects
448
+ let soundEffects = {
449
+ pass: null,
450
+ run: null,
451
+ kick: null,
452
+ touchdown: null,
453
+ tackle: null
454
+ };
455
+
456
+ // Initialize the game
457
+ function init() {
458
+ // Set up the scene
459
+ scene = new THREE.Scene();
460
+ scene.background = new THREE.Color(0x0a0a0a);
461
+
462
+ // Set up the camera
463
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
464
+ camera.position.set(0, 10, 20);
465
+
466
+ // Set up the renderer
467
+ renderer = new THREE.WebGLRenderer({
468
+ canvas: document.getElementById('game-canvas'),
469
+ antialias: true,
470
+ alpha: true
471
+ });
472
+ renderer.setSize(window.innerWidth * 0.8, window.innerHeight * 0.6);
473
+ renderer.setPixelRatio(window.devicePixelRatio);
474
+ renderer.shadowMap.enabled = true;
475
+
476
+ // Add orbit controls
477
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
478
+ controls.enableDamping = true;
479
+ controls.dampingFactor = 0.05;
480
+ controls.screenSpacePanning = false;
481
+ controls.minDistance = 10;
482
+ controls.maxDistance = 50;
483
+ controls.maxPolarAngle = Math.PI / 2 - 0.1;
484
+
485
+ // Add lights
486
+ const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
487
+ scene.add(ambientLight);
488
+
489
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
490
+ directionalLight.position.set(10, 20, 10);
491
+ directionalLight.castShadow = true;
492
+ directionalLight.shadow.mapSize.width = 1024;
493
+ directionalLight.shadow.mapSize.height = 1024;
494
+ scene.add(directionalLight);
495
+
496
+ const hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 0.3);
497
+ scene.add(hemisphereLight);
498
+
499
+ // Create the device
500
+ createDevice();
501
+
502
+ // Create the field
503
+ createField();
504
+
505
+ // Create the ball
506
+ createBall();
507
+
508
+ // Load sounds
509
+ loadSounds();
510
+
511
+ // Set up event listeners
512
+ setupEventListeners();
513
+
514
+ // Start the game loop
515
+ animate();
516
+
517
+ // Hide loading screen
518
+ document.getElementById('loading').style.display = 'none';
519
+ }
520
+
521
+ // Create the handheld device
522
+ function createDevice() {
523
+ // Device base
524
+ const baseGeometry = new THREE.BoxGeometry(12, 8, 2);
525
+ const baseMaterial = new THREE.MeshPhongMaterial({
526
+ color: 0x333333,
527
+ shininess: 30
528
+ });
529
+ device = new THREE.Mesh(baseGeometry, baseMaterial);
530
+ device.position.y = 4;
531
+ device.castShadow = true;
532
+ device.receiveShadow = true;
533
+ scene.add(device);
534
+
535
+ // Device top (screen area)
536
+ const topGeometry = new THREE.BoxGeometry(10, 6, 0.5);
537
+ const topMaterial = new THREE.MeshPhongMaterial({
538
+ color: 0x111111,
539
+ shininess: 50
540
+ });
541
+ const top = new THREE.Mesh(topGeometry, topMaterial);
542
+ top.position.set(0, 0.5, 1.25);
543
+ device.add(top);
544
+
545
+ // Screen
546
+ const screenGeometry = new THREE.PlaneGeometry(8, 4.5);
547
+ const screenMaterial = new THREE.MeshBasicMaterial({
548
+ color: 0x00ff00,
549
+ transparent: true,
550
+ opacity: 0.8
551
+ });
552
+ screen = new THREE.Mesh(screenGeometry, screenMaterial);
553
+ screen.position.set(0, 0.5, 1.5);
554
+ device.add(screen);
555
+
556
+ // Screen border
557
+ const borderGeometry = new THREE.BoxGeometry(8.2, 4.7, 0.1);
558
+ const borderMaterial = new THREE.MeshPhongMaterial({
559
+ color: 0x000000,
560
+ shininess: 100
561
+ });
562
+ const border = new THREE.Mesh(borderGeometry, borderMaterial);
563
+ border.position.set(0, 0.5, 1.45);
564
+ device.add(border);
565
+
566
+ // Create buttons
567
+ createButtons();
568
+
569
+ // Add some details
570
+ addDeviceDetails();
571
+ }
572
+
573
+ // Create the buttons
574
+ function createButtons() {
575
+ const buttonPositions = [
576
+ { x: -3, y: -2, color: 0xff0000, label: 'PASS' },
577
+ { x: -1, y: -2, color: 0x0000ff, label: 'RUN' },
578
+ { x: 1, y: -2, color: 0x00ff00, label: 'KICK' },
579
+ { x: 3, y: -2, color: 0xffff00, label: 'RESET' }
580
+ ];
581
+
582
+ buttonPositions.forEach(pos => {
583
+ const buttonGroup = new THREE.Group();
584
+
585
+ // Button base
586
+ const buttonGeometry = new THREE.CylinderGeometry(0.8, 0.8, 0.3, 32);
587
+ const buttonMaterial = new THREE.MeshPhongMaterial({
588
+ color: pos.color,
589
+ shininess: 50
590
+ });
591
+ const button = new THREE.Mesh(buttonGeometry, buttonMaterial);
592
+ button.position.set(pos.x, pos.y, 1.5);
593
+ button.rotation.x = Math.PI / 2;
594
+ button.castShadow = true;
595
+ buttonGroup.add(button);
596
+
597
+ // Button label
598
+ const labelCanvas = document.createElement('canvas');
599
+ labelCanvas.width = 128;
600
+ labelCanvas.height = 128;
601
+ const labelContext = labelCanvas.getContext('2d');
602
+ labelContext.fillStyle = 'white';
603
+ labelContext.font = 'Bold 20px Arial';
604
+ labelContext.textAlign = 'center';
605
+ labelContext.textBaseline = 'middle';
606
+ labelContext.fillText(pos.label, 64, 64);
607
+
608
+ const labelTexture = new THREE.CanvasTexture(labelCanvas);
609
+ const labelMaterial = new THREE.SpriteMaterial({ map: labelTexture });
610
+ const labelSprite = new THREE.Sprite(labelMaterial);
611
+ labelSprite.position.set(pos.x, pos.y, 1.5);
612
+ labelSprite.scale.set(1, 1, 1);
613
+ buttonGroup.add(labelSprite);
614
+
615
+ buttons.push({
616
+ group: buttonGroup,
617
+ mesh: button,
618
+ label: pos.label,
619
+ originalColor: pos.color,
620
+ pressed: false
621
+ });
622
+
623
+ device.add(buttonGroup);
624
+ });
625
+ }
626
+
627
+ // Add device details
628
+ function addDeviceDetails() {
629
+ // Speaker grills
630
+ const grillGeometry = new THREE.BoxGeometry(1.5, 0.2, 0.1);
631
+ const grillMaterial = new THREE.MeshPhongMaterial({ color: 0x555555 });
632
+
633
+ for (let i = 0; i < 4; i++) {
634
+ const grill = new THREE.Mesh(grillGeometry, grillMaterial);
635
+ grill.position.set(-4.5 + i * 0.4, 3, 1.5);
636
+ device.add(grill);
637
+ }
638
+
639
+ // Brand logo
640
+ const logoGeometry = new THREE.BoxGeometry(2, 0.5, 0.1);
641
+ const logoMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000 });
642
+ const logo = new THREE.Mesh(logoGeometry, logoMaterial);
643
+ logo.position.set(0, 3.5, 1.5);
644
+ device.add(logo);
645
+
646
+ // Create a simple "MATTEL" text
647
+ const logoCanvas = document.createElement('canvas');
648
+ logoCanvas.width = 256;
649
+ logoCanvas.height = 64;
650
+ const logoContext = logoCanvas.getContext('2d');
651
+ logoContext.fillStyle = 'white';
652
+ logoContext.font = 'Bold 30px Arial';
653
+ logoContext.textAlign = 'center';
654
+ logoContext.textBaseline = 'middle';
655
+ logoContext.fillText('MATTEL', 128, 32);
656
+
657
+ const logoTexture = new THREE.CanvasTexture(logoCanvas);
658
+ const logoSpriteMaterial = new THREE.SpriteMaterial({ map: logoTexture });
659
+ const logoSprite = new THREE.Sprite(logoSpriteMaterial);
660
+ logoSprite.position.set(0, 3.5, 1.51);
661
+ logoSprite.scale.set(2, 0.5, 1);
662
+ device.add(logoSprite);
663
+ }
664
+
665
+ // Create the football field
666
+ function createField() {
667
+ // Field base
668
+ const fieldGeometry = new THREE.PlaneGeometry(30, 15);
669
+ const fieldMaterial = new THREE.MeshStandardMaterial({
670
+ color: 0x008000,
671
+ roughness: 0.8,
672
+ metalness: 0.2
673
+ });
674
+ field = new THREE.Mesh(fieldGeometry, fieldMaterial);
675
+ field.rotation.x = -Math.PI / 2;
676
+ field.position.y = 0;
677
+ field.receiveShadow = true;
678
+ scene.add(field);
679
+
680
+ // Field lines
681
+ const lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff });
682
+
683
+ // Sideline
684
+ const sidelineGeometry = new THREE.BufferGeometry();
685
+ const sidelineVertices = [];
686
+ sidelineVertices.push(-15, 0, -7.5);
687
+ sidelineVertices.push(15, 0, -7.5);
688
+ sidelineVertices.push(15, 0, 7.5);
689
+ sidelineVertices.push(-15, 0, 7.5);
690
+ sidelineVertices.push(-15, 0, -7.5);
691
+ sidelineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sidelineVertices, 3));
692
+ const sideline = new THREE.Line(sidelineGeometry, lineMaterial);
693
+ field.add(sideline);
694
+
695
+ // Yard lines
696
+ for (let i = -14; i <= 14; i += 2) {
697
+ const yardLineGeometry = new THREE.BufferGeometry();
698
+ const yardLineVertices = [];
699
+ yardLineVertices.push(i, 0, -7);
700
+ yardLineVertices.push(i, 0, 7);
701
+ yardLineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(yardLineVertices, 3));
702
+ const yardLine = new THREE.Line(yardLineGeometry, lineMaterial);
703
+ field.add(yardLine);
704
+ }
705
+
706
+ // End zones
707
+ const endZoneGeometry = new THREE.PlaneGeometry(3, 15);
708
+ const endZoneMaterial = new THREE.MeshStandardMaterial({
709
+ color: 0xff0000,
710
+ roughness: 0.8,
711
+ metalness: 0.2
712
+ });
713
+
714
+ const endZone1 = new THREE.Mesh(endZoneGeometry, endZoneMaterial);
715
+ endZone1.rotation.x = -Math.PI / 2;
716
+ endZone1.position.set(-13.5, 0.01, 0);
717
+ field.add(endZone1);
718
+
719
+ const endZone2 = new THREE.Mesh(endZoneGeometry, endZoneMaterial);
720
+ endZone2.rotation.x = -Math.PI / 2;
721
+ endZone2.position.set(13.5, 0.01, 0);
722
+ field.add(endZone2);
723
+
724
+ // Goal posts
725
+ const postGeometry = new THREE.CylinderGeometry(0.1, 0.1, 5, 16);
726
+ const postMaterial = new THREE.MeshStandardMaterial({
727
+ color: 0xffff00,
728
+ metalness: 0.8,
729
+ roughness: 0.2
730
+ });
731
+
732
+ const post1 = new THREE.Mesh(postGeometry, postMaterial);
733
+ post1.position.set(-15, 2.5, 0);
734
+ field.add(post1);
735
+
736
+ const post2 = new THREE.Mesh(postGeometry, postMaterial);
737
+ post2.position.set(15, 2.5, 0);
738
+ field.add(post2);
739
+
740
+ // Crossbars
741
+ const crossbarGeometry = new THREE.BoxGeometry(3, 0.1, 0.1);
742
+ const crossbar1 = new THREE.Mesh(crossbarGeometry, postMaterial);
743
+ crossbar1.position.set(-15, 5, 0);
744
+ field.add(crossbar1);
745
+
746
+ const crossbar2 = new THREE.Mesh(crossbarGeometry, postMaterial);
747
+ crossbar2.position.set(15, 5, 0);
748
+ field.add(crossbar2);
749
+ }
750
+
751
+ // Create the football
752
+ function createBall() {
753
+ const ballGroup = new THREE.Group();
754
+
755
+ // Main ball shape
756
+ const ballGeometry = new THREE.SphereGeometry(0.5, 32, 32);
757
+ const ballMaterial = new THREE.MeshStandardMaterial({
758
+ color: 0xffffff,
759
+ roughness: 0.4,
760
+ metalness: 0.1,
761
+ map: createFootballTexture()
762
+ });
763
+ ball = new THREE.Mesh(ballGeometry, ballMaterial);
764
+ ball.position.set(0, 0.5, 0);
765
+ ball.castShadow = true;
766
+ ballGroup.add(ball);
767
+
768
+ // Laces
769
+ const laceGeometry = new THREE.BoxGeometry(0.8, 0.1, 0.05);
770
+ const laceMaterial = new THREE.MeshStandardMaterial({ color: 0x000000 });
771
+ const lace = new THREE.Mesh(laceGeometry, laceMaterial);
772
+ lace.position.set(0, 0.5, 0);
773
+ lace.rotation.z = Math.PI / 4;
774
+ ballGroup.add(lace);
775
+
776
+ scene.add(ballGroup);
777
+ }
778
+
779
+ // Create football texture
780
+ function createFootballTexture() {
781
+ const canvas = document.createElement('canvas');
782
+ canvas.width = 512;
783
+ canvas.height = 256;
784
+ const context = canvas.getContext('2d');
785
+
786
+ // Draw white base
787
+ context.fillStyle = '#ffffff';
788
+ context.fillRect(0, 0, canvas.width, canvas.height);
789
+
790
+ // Draw brown panels
791
+ context.fillStyle = '#8B4513';
792
+ context.beginPath();
793
+ context.moveTo(0, 0);
794
+ context.lineTo(256, 0);
795
+ context.lineTo(128, 256);
796
+ context.closePath();
797
+ context.fill();
798
+
799
+ context.beginPath();
800
+ context.moveTo(256, 0);
801
+ context.lineTo(512, 0);
802
+ context.lineTo(384, 256);
803
+ context.lineTo(128, 256);
804
+ context.closePath();
805
+ context.fill();
806
+
807
+ // Draw stitching
808
+ context.strokeStyle = '#000000';
809
+ context.lineWidth = 2;
810
+
811
+ // Vertical stitching
812
+ for (let i = 0; i < 5; i++) {
813
+ const x = i * 100 + 20;
814
+ context.beginPath();
815
+ context.moveTo(x, 0);
816
+ context.lineTo(x + 50, 256);
817
+ context.stroke();
818
+ }
819
+
820
+ return new THREE.CanvasTexture(canvas);
821
+ }
822
+
823
+ // Load sound effects
824
+ function loadSounds() {
825
+ // In a real implementation, we would load actual sound files
826
+ // For this demo, we'll simulate sound effects with console logs
827
+ soundEffects.pass = () => playSoundEffect('pass');
828
+ soundEffects.run = () => playSoundEffect('run');
829
+ soundEffects.kick = () => playSoundEffect('kick');
830
+ soundEffects.touchdown = () => playSoundEffect('touchdown');
831
+ soundEffects.tackle = () => playSoundEffect('tackle');
832
+ }
833
+
834
+ // Play sound effect
835
+ function playSoundEffect(type) {
836
+ if (!gameState.soundEnabled) return;
837
+
838
+ // Simulate sound effects
839
+ const sounds = {
840
+ pass: 'Whoosh!',
841
+ run: 'Thud! Thud! Thud!',
842
+ kick: 'Boom!',
843
+ touchdown: 'Touchdown! Crowd cheering!',
844
+ tackle: 'Crunch!'
845
+ };
846
+
847
+ console.log(`[SOUND] ${sounds[type]}`);
848
+ }
849
+
850
+ // Set up event listeners
851
+ function setupEventListeners() {
852
+ // Button clicks
853
+ document.getElementById('pass-button').addEventListener('click', () => handleButtonClick('PASS'));
854
+ document.getElementById('run-button').addEventListener('click', () => handleButtonClick('RUN'));
855
+ document.getElementById('kick-button').addEventListener('click', () => handleButtonClick('KICK'));
856
+ document.getElementById('reset-button').addEventListener('click', resetGame);
857
+
858
+ // Sound toggle
859
+ document.getElementById('sound-toggle').addEventListener('click', toggleSound);
860
+
861
+ // Window resize
862
+ window.addEventListener('resize', onWindowResize);
863
+ }
864
+
865
+ // Handle button clicks
866
+ function handleButtonClick(buttonLabel) {
867
+ if (gameState.gameOver || gameState.playInProgress) return;
868
+
869
+ gameState.playInProgress = true;
870
+ gameState.lastPlay = buttonLabel;
871
+
872
+ // Highlight the pressed button
873
+ const button = buttons.find(b => b.label === buttonLabel);
874
+ if (button) {
875
+ button.mesh.material.color.setHex(0xffffff);
876
+ }
877
+
878
+ // Simulate the play
879
+ setTimeout(() => {
880
+ executePlay(buttonLabel);
881
+ gameState.playInProgress = false;
882
+
883
+ // Restore button color
884
+ if (button) {
885
+ button.mesh.material.color.setHex(button.originalColor);
886
+ }
887
+ }, 1000);
888
+ }
889
+
890
+ // Execute the play
891
+ function executePlay(playType) {
892
+ let yardsGained = 0;
893
+ let playDescription = '';
894
+
895
+ // Determine outcome based on play type
896
+ switch (playType) {
897
+ case 'PASS':
898
+ soundEffects.pass();
899
+ const passOutcomes = [
900
+ { yards: 20, desc: 'Long pass completion!' },
901
+ { yards: 10, desc: 'Short pass completion' },
902
+ { yards: 5, desc: 'Screen pass' },
903
+ { yards: 0, desc: 'Incomplete pass' },
904
+ { yards: -2, desc: 'Sacked!' }
905
+ ];
906
+ const passResult = passOutcomes[Math.floor(Math.random() * passOutcomes.length)];
907
+ yardsGained = passResult.yards;
908
+ playDescription = passResult.desc;
909
+ break;
910
+
911
+ case 'RUN':
912
+ soundEffects.run();
913
+ const runOutcomes = [
914
+ { yards: 8, desc: 'Big run up the middle!' },
915
+ { yards: 4, desc: 'Solid run' },
916
+ { yards: 2, desc: 'Short gain' },
917
+ { yards: 0, desc: 'No gain' },
918
+ { yards: -1, desc: 'Tackled for loss' }
919
+ ];
920
+ const runResult = runOutcomes[Math.floor(Math.random() * runOutcomes.length)];
921
+ yardsGained = runResult.yards;
922
+ playDescription = runResult.desc;
923
+ break;
924
+
925
+ case 'KICK':
926
+ soundEffects.kick();
927
+ if (gameState.down === 4) {
928
+ // Field goal attempt
929
+ const distance = gameState.fieldPosition > 50 ? 100 - gameState.fieldPosition : gameState.fieldPosition;
930
+ if (distance <= 30) {
931
+ yardsGained = 3; // Field goal
932
+ playDescription = 'Field goal is GOOD!';
933
+ gameState.score[gameState.possession] += 3;
934
+ } else {
935
+ playDescription = 'Field goal attempt missed!';
936
+ }
937
+ } else {
938
+ // Punt
939
+ yardsGained = Math.floor(Math.random() * 30) + 20;
940
+ playDescription = `Punt travels ${yardsGained} yards`;
941
+ gameState.possession = gameState.possession === 0 ? 1 : 0;
942
+ }
943
+ break;
944
+ }
945
+
946
+ // Update game state
947
+ if (playType !== 'KICK' || gameState.down !== 4) {
948
+ gameState.yardsGained += yardsGained;
949
+ gameState.fieldPosition += gameState.possession === 0 ? yardsGained : -yardsGained;
950
+
951
+ // Check for touchdown
952
+ if (gameState.fieldPosition >= 100) {
953
+ playDescription = 'TOUCHDOWN!';
954
+ soundEffects.touchdown();
955
+ gameState.score[gameState.possession] += 6;
956
+ gameState.fieldPosition = 80; // Kickoff from 20 yard line
957
+ gameState.possession = gameState.possession === 0 ? 1 : 0;
958
+ gameState.down = 1;
959
+ gameState.yardsToGo = 10;
960
+ gameState.yardsGained = 0;
961
+ } else if (gameState.fieldPosition <= 0) {
962
+ playDescription = 'TOUCHDOWN!';
963
+ soundEffects.touchdown();
964
+ gameState.score[gameState.possession] += 6;
965
+ gameState.fieldPosition = 20; // Kickoff from 20 yard line
966
+ gameState.possession = gameState.possession === 0 ? 1 : 0;
967
+ gameState.down = 1;
968
+ gameState.yardsToGo = 10;
969
+ gameState.yardsGained = 0;
970
+ } else {
971
+ // Update down and distance
972
+ if (gameState.yardsGained >= gameState.yardsToGo) {
973
+ // First down
974
+ gameState.down = 1;
975
+ gameState.yardsToGo = 10;
976
+ gameState.yardsGained = 0;
977
+ playDescription += ' First down!';
978
+ } else {
979
+ gameState.down++;
980
+ gameState.yardsToGo -= gameState.yardsGained;
981
+
982
+ if (gameState.down > 4) {
983
+ // Turnover on downs
984
+ playDescription = 'Turnover on downs!';
985
+ gameState.possession = gameState.possession === 0 ? 1 : 0;
986
+ gameState.down = 1;
987
+ gameState.yardsToGo = 10;
988
+ gameState.yardsGained = 0;
989
+ }
990
+ }
991
+ }
992
+ } else {
993
+ // After kick, switch possession
994
+ gameState.down = 1;
995
+ gameState.yardsToGo = 10;
996
+ gameState.yardsGained = 0;
997
+ }
998
+
999
+ // Update ball position
1000
+ const fieldWidth = 30;
1001
+ const fieldLength = 15;
1002
+ const ballX = THREE.MathUtils.mapLinear(gameState.fieldPosition, 0, 100, -fieldWidth/2, fieldWidth/2);
1003
+ const ballZ = (Math.random() - 0.5) * fieldLength * 0.8; // Random position across width
1004
+
1005
+ // Animate ball movement
1006
+ animateBallMovement(ballX, ballZ);
1007
+
1008
+ // Update UI
1009
+ updateGameUI();
1010
+
1011
+ // Show play result on screen
1012
+ showPlayResult(playDescription);
1013
+ }
1014
+
1015
+ // Animate ball movement
1016
+ function animateBallMovement(targetX, targetZ) {
1017
+ const startPosition = ball.position.clone();
1018
+ const targetPosition = new THREE.Vector3(targetX, 0.5, targetZ);
1019
+ const duration = 1.0; // seconds
1020
+ let elapsed = 0;
1021
+
1022
+ // Create a simple animation
1023
+ const animate = () => {
1024
+ elapsed += clock.getDelta();
1025
+ const progress = Math.min(elapsed / duration, 1);
1026
+
1027
+ // Ease in out animation
1028
+ const easedProgress = progress < 0.5
1029
+ ? 2 * progress * progress
1030
+ : -1 + (4 - 2 * progress) * progress;
1031
+
1032
+ ball.position.lerpVectors(startPosition, targetPosition, easedProgress);
1033
+
1034
+ if (progress < 1) {
1035
+ requestAnimationFrame(animate);
1036
+ }
1037
+ };
1038
+
1039
+ animate();
1040
+ }
1041
+
1042
+ // Show play result on the LED screen
1043
+ function showPlayResult(description) {
1044
+ // Create a texture with the play result
1045
+ const canvas = document.createElement('canvas');
1046
+ canvas.width = 256;
1047
+ canvas.height = 128;
1048
+ const context = canvas.getContext('2d');
1049
+
1050
+ // Draw green background
1051
+ context.fillStyle = '#00ff00';
1052
+ context.fillRect(0, 0, canvas.width, canvas.height);
1053
+
1054
+ // Draw black border
1055
+ context.strokeStyle = '#000000';
1056
+ context.lineWidth = 4;
1057
+ context.strokeRect(0, 0, canvas.width, canvas.height);
1058
+
1059
+ // Draw play description
1060
+ context.fillStyle = '#000000';
1061
+ context.font = 'Bold 16px Arial';
1062
+ context.textAlign = 'center';
1063
+
1064
+ // Split text into multiple lines if needed
1065
+ const words = description.split(' ');
1066
+ let line = '';
1067
+ let y = 40;
1068
+
1069
+ words.forEach(word => {
1070
+ const testLine = line + word + ' ';
1071
+ const metrics = context.measureText(testLine);
1072
+
1073
+ if (metrics.width > canvas.width - 40 && line.length > 0) {
1074
+ context.fillText(line.trim(), canvas.width / 2, y);
1075
+ line = word + ' ';
1076
+ y += 25;
1077
+ } else {
1078
+ line = testLine;
1079
+ }
1080
+ });
1081
+
1082
+ context.fillText(line.trim(), canvas.width / 2, y);
1083
+
1084
+ // Add game stats
1085
+ y += 30;
1086
+ context.font = 'Bold 14px Arial';
1087
+ context.fillText(`Score: ${gameState.score[0]}-${gameState.score[1]}`, canvas.width / 2, y);
1088
+ y += 20;
1089
+ context.fillText(`Down: ${gameState.down}`, canvas.width / 2, y);
1090
+ y += 20;
1091
+ context.fillText(`Yards: ${gameState.yardsToGo}`, canvas.width / 2, y);
1092
+
1093
+ // Update screen texture
1094
+ const texture = new THREE.CanvasTexture(canvas);
1095
+ screen.material.map = texture;
1096
+ screen.material.needsUpdate = true;
1097
+
1098
+ // Revert to original screen after delay
1099
+ setTimeout(() => {
1100
+ screen.material.map = null;
1101
+ screen.material.color.setHex(0x00ff00);
1102
+ screen.material.needsUpdate = true;
1103
+ }, 3000);
1104
+ }
1105
+
1106
+ // Update game UI
1107
+ function updateGameUI() {
1108
+ document.getElementById('score').textContent = `${gameState.score[0]}-${gameState.score[1]}`;
1109
+
1110
+ const downText = ['1st', '2nd', '3rd', '4th'][gameState.down - 1] || '4th';
1111
+ document.getElementById('down').textContent = downText;
1112
+
1113
+ document.getElementById('yards').textContent = gameState.yardsToGo;
1114
+ document.getElementById('quarter').textContent = gameState.quarter;
1115
+ }
1116
+
1117
+ // Reset the game
1118
+ function resetGame() {
1119
+ gameState = {
1120
+ score: [0, 0],
1121
+ down: 1,
1122
+ yardsToGo: 10,
1123
+ yardsGained: 0,
1124
+ quarter: 1,
1125
+ possession: 0,
1126
+ gameOver: false,
1127
+ playInProgress: false,
1128
+ lastPlay: null,
1129
+ fieldPosition: 50,
1130
+ soundEnabled: gameState.soundEnabled
1131
+ };
1132
+
1133
+ // Reset ball position
1134
+ ball.position.set(0, 0.5, 0);
1135
+
1136
+ // Update UI
1137
+ updateGameUI();
1138
+
1139
+ // Clear screen
1140
+ screen.material.map = null;
1141
+ screen.material.color.setHex(0x00ff00);
1142
+ screen.material.needsUpdate = true;
1143
+ }
1144
+
1145
+ // Toggle sound
1146
+ function toggleSound() {
1147
+ gameState.soundEnabled = !gameState.soundEnabled;
1148
+ const toggle = document.getElementById('sound-toggle');
1149
+ toggle.classList.toggle('on', gameState.soundEnabled);
1150
+ toggle.classList.toggle('off', !gameState.soundEnabled);
1151
+ }
1152
+
1153
+ // Handle window resize
1154
+ function onWindowResize() {
1155
+ camera.aspect = window.innerWidth / window.innerHeight;
1156
+ camera.updateProjectionMatrix();
1157
+ renderer.setSize(window.innerWidth * 0.8, window.innerHeight * 0.6);
1158
+ }
1159
+
1160
+ // Animation loop
1161
+ function animate() {
1162
+ requestAnimationFrame(animate);
1163
+
1164
+ // Update controls
1165
+ controls.update();
1166
+
1167
+ // Rotate the device slightly for better viewing
1168
+ if (device) {
1169
+ device.rotation.y += 0.005;
1170
+ }
1171
+
1172
+ // Render the scene
1173
+ renderer.render(scene, camera);
1174
+ }
1175
+
1176
+ // Start the game
1177
+ init();
1178
+ </script>
1179
+ </body>
1180
+ </html>