awacke1 commited on
Commit
2496c30
·
verified ·
1 Parent(s): f7d2786

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +419 -18
index.html CHANGED
@@ -1,19 +1,420 @@
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>Plinko Vows to Sam - Golden Ticket Edition</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ overflow: hidden;
11
+ font-family: 'Garamond', serif;
12
+ background: linear-gradient(to bottom, #0d121a, #1a2330);
13
+ color: #f0f0f0;
14
+ display: flex;
15
+ justify-content: center;
16
+ align-items: center;
17
+ min-height: 100vh;
18
+ }
19
+ #container {
20
+ display: flex;
21
+ flex-direction: column;
22
+ align-items: center;
23
+ width: 100%;
24
+ height: 100vh;
25
+ padding: 10px;
26
+ box-sizing: border-box;
27
+ }
28
+ #vows-container {
29
+ width: 100%;
30
+ max-width: 800px;
31
+ height: 150px;
32
+ overflow: hidden;
33
+ position: relative;
34
+ background-color: rgba(0, 0, 0, 0.2);
35
+ border-radius: 15px;
36
+ margin-bottom: 5px;
37
+ box-shadow: 0 4px 15px rgba(0,0,0,0.4);
38
+ border: 1px solid rgba(255,255,255,0.1);
39
+ }
40
+ #vows-content {
41
+ position: absolute;
42
+ top: 100%;
43
+ width: 100%;
44
+ text-align: center;
45
+ animation: scroll-vows 30s linear infinite;
46
+ }
47
+ #vows-content h2 { font-size: 1.6em; margin-bottom: 15px; color: #ffd700; }
48
+ #vows-content p { font-size: 1.2em; line-height: 1.6; margin: 0 20px 20px; }
49
+ @keyframes scroll-vows {
50
+ 0% { top: 100%; }
51
+ 100% { top: -250%; }
52
+ }
53
+ #life-bar-container {
54
+ width: 80%;
55
+ max-width: 400px;
56
+ height: 30px;
57
+ background-color: rgba(0, 0, 0, 0.4);
58
+ border-radius: 15px;
59
+ border: 2px solid #ffd700;
60
+ padding: 3px;
61
+ box-shadow: 0 0 15px rgba(255, 215, 0, 0.5);
62
+ margin-bottom: 5px;
63
+ }
64
+ #life-bar {
65
+ width: 0%;
66
+ height: 100%;
67
+ background: linear-gradient(90deg, #ff8c00, #ffd700);
68
+ border-radius: 12px;
69
+ transition: width 0.5s ease-out;
70
+ text-align: right;
71
+ color: #333;
72
+ font-weight: bold;
73
+ line-height: 24px;
74
+ padding-right: 10px;
75
+ box-sizing: border-box;
76
+ }
77
+ #game-area {
78
+ width: 100%;
79
+ flex-grow: 1;
80
+ display: flex;
81
+ flex-direction: column;
82
+ align-items: center;
83
+ position: relative;
84
+ }
85
+ #game-container {
86
+ width: 100%;
87
+ flex-grow: 1;
88
+ position: relative;
89
+ }
90
+ canvas { display: block; width: 100%; height: 100%; }
91
+ #score-slots {
92
+ display: flex;
93
+ justify-content: center;
94
+ width: 100%;
95
+ max-width: 500px;
96
+ padding: 5px 0;
97
+ }
98
+ .slot { flex: 1; text-align: center; font-size: 1.2em; font-weight: bold; color: #f0f0f0; }
99
+ .slot.jackpot { color: #ffd700; font-size: 1.4em; animation: glow 2s ease-in-out infinite; }
100
+ @keyframes glow {
101
+ 0%, 100% { text-shadow: 0 0 5px #ffd700, 0 0 10px #ff0; }
102
+ 50% { text-shadow: 0 0 15px #ffd700, 0 0 25px #ff0; }
103
+ }
104
+ #ticket-controls {
105
+ position: absolute;
106
+ bottom: 30px;
107
+ left: 50%;
108
+ transform: translateX(-50%);
109
+ display: flex;
110
+ gap: 10px;
111
+ padding: 10px;
112
+ background-color: rgba(0,0,0,0.3);
113
+ border-radius: 15px;
114
+ z-index: 100;
115
+ }
116
+ .ticket-button {
117
+ background: linear-gradient(145deg, #fceabb, #f8b500);
118
+ border: 2px solid #c79100;
119
+ color: #4a2c00;
120
+ font-family: 'Garamond', serif;
121
+ font-size: 18px;
122
+ font-weight: bold;
123
+ padding: 10px 15px;
124
+ border-radius: 10px;
125
+ cursor: pointer;
126
+ box-shadow: 0 4px 6px rgba(0,0,0,0.5), inset 0 1px 1px rgba(255,255,255,0.5);
127
+ transition: all 0.2s ease-in-out;
128
+ }
129
+ .ticket-button:hover {
130
+ transform: translateY(-2px);
131
+ box-shadow: 0 6px 10px rgba(0,0,0,0.6), inset 0 1px 1px rgba(255,255,255,0.5);
132
+ }
133
+ .ticket-button:active {
134
+ transform: translateY(0);
135
+ }
136
+ </style>
137
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
138
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script>
139
+ <!-- Add Tone.js for sound effects -->
140
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.7.77/Tone.js"></script>
141
+ </head>
142
+ <body>
143
+ <div id="container">
144
+ <!-- Vows Container and other elements remain the same -->
145
+ <div id="vows-container">
146
+ <div id="vows-content">
147
+ <h2>A Prayer for My Love, Sam</h2>
148
+ <p>Dear Lord, help me this day to be...</p>
149
+ <p><strong>1. Physical:</strong> to walk with you, hand in hand, in pleasure and intimacy as we adventure.</p>
150
+ <p><strong>2. Emotion:</strong> to support you, make you smile and laugh, and be your biggest fan.</p>
151
+ <p><strong>3. Spirit:</strong> To lift your heart, help you dream, and help you be your best.</p>
152
+ <p><strong>4. Pure:</strong> To be selfless, being the best version of myself to be worthy of your love.</p>
153
+ <p><strong>5. Family:</strong> To be dependable, smart, safe, and responsible so we can grow together.</p>
154
+ <p><strong>6. Love:</strong> To satisfy you, make your eyes sparkle, to wipe your tears in happiness of love.</p>
155
+ <p><strong>7. Respect:</strong> To be your best friend, honoring your wants, needs and dreams.</p>
156
+ <p><strong>8. Trust:</strong> To be an open book to you, one you can always depend on to be there when you need me.</p>
157
+ <p><strong>9. Commit:</strong> To be hopelessly devoted favorite sunrise to sunset.</p>
158
+ <p><strong>10. Bond:</strong> To communicate with you even without words.</p>
159
+ </div>
160
+ </div>
161
+
162
+ <div id="game-area">
163
+ <div id="life-bar-container"><div id="life-bar"></div></div>
164
+ <div id="game-container">
165
+ <div id="ticket-controls">
166
+ <button class="ticket-button" data-count="1">Drop 1 Ball</button>
167
+ <button class="ticket-button" data-count="5">Drop 5 Balls</button>
168
+ <button class="ticket-button" data-count="10">Drop 10 Balls</button>
169
+ </div>
170
+ </div>
171
+ <div id="score-slots">
172
+ <div class="slot jackpot">1000</div><div class="slot">250</div><div class="slot">100</div>
173
+ <div class="slot">500</div>
174
+ <div class="slot">100</div><div class="slot">250</div><div class="slot jackpot">1000</div>
175
+ </div>
176
+ </div>
177
+ </div>
178
+
179
+ <script>
180
+ window.onload = function() {
181
+ // --- Game State, DOM, and Scene Setup (largely unchanged) ---
182
+ let score = 0;
183
+ const targetScore = 50000;
184
+ const lifeBar = document.getElementById('life-bar');
185
+ const gameContainer = document.getElementById('game-container');
186
+ const scene = new THREE.Scene();
187
+ const world = new CANNON.World();
188
+ world.gravity.set(0, -30, 0);
189
+ world.broadphase = new CANNON.NaiveBroadphase();
190
+ world.solver.iterations = 10;
191
+ const camera = new THREE.PerspectiveCamera(75, gameContainer.clientWidth / gameContainer.clientHeight, 0.1, 1000);
192
+ camera.position.set(0, 2, 32);
193
+ const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
194
+ renderer.setSize(gameContainer.clientWidth, gameContainer.clientHeight);
195
+ renderer.setPixelRatio(window.devicePixelRatio);
196
+ gameContainer.appendChild(renderer.domElement);
197
+ scene.add(new THREE.AmbientLight(0xffffff, 0.7));
198
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.9);
199
+ directionalLight.position.set(10, 20, 30);
200
+ scene.add(directionalLight);
201
+
202
+ // --- NEW: Sound Synthesis Setup ---
203
+ let soundsReady = false;
204
+ // Sound for hitting pegs
205
+ const pegHitSynth = new Tone.PolySynth(Tone.Synth, {
206
+ oscillator: { type: 'sine' },
207
+ envelope: { attack: 0.001, decay: 0.1, sustain: 0.01, release: 0.1 },
208
+ volume: -12
209
+ }).toDestination();
210
+ // Sound for scoring
211
+ const scoreSynth = new Tone.Synth({
212
+ oscillator: { type: 'triangle8' },
213
+ envelope: { attack: 0.01, decay: 0.2, sustain: 0.2, release: 0.5 },
214
+ volume: -5
215
+ }).toDestination();
216
+ const scoreLfo = new Tone.LFO("4n", 400, 1000).connect(scoreSynth.frequency);
217
+ // Sound for dropping balls
218
+ const dropSynth = new Tone.NoiseSynth({
219
+ noise: { type: 'white' },
220
+ envelope: { attack: 0.005, decay: 0.1, sustain: 0 },
221
+ volume: -10
222
+ }).toDestination();
223
+
224
+
225
+ // --- Materials and Physics (largely unchanged) ---
226
+ const ballMaterial = new THREE.MeshPhongMaterial({ color: 0xff4500, emissive: 0xcc3700, shininess: 100 });
227
+ const pegMaterial = new THREE.MeshPhongMaterial({ color: 0x87ceeb, emissive: 0x1f5c7a, shininess: 80 });
228
+ const wallMaterial = new THREE.MeshPhongMaterial({ color: 0x6a5acd, transparent: true, opacity: 0.4 });
229
+ const goldenTicketMaterial = new THREE.MeshStandardMaterial({ color: 0xfdd835, metalness: 0.8, roughness: 0.2, emissive: 0xc19300 });
230
+ const ballPhysicsMaterial = new CANNON.Material('ball');
231
+ const pegPhysicsMaterial = new CANNON.Material('peg');
232
+ const staticPhysicsMaterial = new CANNON.Material('static');
233
+ world.addContactMaterial(new CANNON.ContactMaterial(ballPhysicsMaterial, pegPhysicsMaterial, { friction: 0.2, restitution: 0.5 }));
234
+ world.addContactMaterial(new CANNON.ContactMaterial(ballPhysicsMaterial, staticPhysicsMaterial, { friction: 0.1, restitution: 0.4 }));
235
+ world.addContactMaterial(new CANNON.ContactMaterial(ballPhysicsMaterial, ballPhysicsMaterial, { friction: 0.05, restitution: 0.9 }));
236
+
237
+ const gameElements = [];
238
+ const boardWidth = 28;
239
+ const boardHeight = 38;
240
+
241
+ // --- Helper Functions ---
242
+ const createStaticBox = (w, h, d, pos, rot = {x:0,y:0,z:0}) => {
243
+ const shape = new CANNON.Box(new CANNON.Vec3(w/2, h/2, d/2));
244
+ const body = new CANNON.Body({mass:0, material: staticPhysicsMaterial});
245
+ body.addShape(shape);
246
+ body.position.copy(pos);
247
+ body.quaternion.setFromEuler(rot.x, rot.y, rot.z, 'XYZ');
248
+ world.addBody(body);
249
+ const mesh = new THREE.Mesh(new THREE.BoxGeometry(w,h,d), wallMaterial);
250
+ mesh.position.copy(body.position);
251
+ mesh.quaternion.copy(body.quaternion);
252
+ scene.add(mesh);
253
+ };
254
+ const createPeg = (x, y, z) => {
255
+ const r = 0.4, h = 2;
256
+ const body = new CANNON.Body({mass: 0, material: pegPhysicsMaterial});
257
+ // Add a flag to identify pegs for sound triggers
258
+ body.isPeg = true;
259
+ const shape = new CANNON.Cylinder(r, r, h, 16);
260
+ const quat = new CANNON.Quaternion();
261
+ quat.setFromAxisAngle(new CANNON.Vec3(1,0,0), -Math.PI/2);
262
+ body.addShape(shape, new CANNON.Vec3(), quat);
263
+ body.position.set(x,y,z);
264
+ world.addBody(body);
265
+ const mesh = new THREE.Mesh(new THREE.CylinderGeometry(r,r,h,16), pegMaterial);
266
+ mesh.position.copy(body.position);
267
+ mesh.quaternion.copy(body.quaternion);
268
+ scene.add(mesh);
269
+ };
270
+
271
+ // --- Board Setup ---
272
+ const setupBoard = () => {
273
+ // (Board creation logic is unchanged)
274
+ createStaticBox(1, boardHeight, 2, { x: -boardWidth / 2, y: 0, z: 0 });
275
+ createStaticBox(1, boardHeight, 2, { x: boardWidth / 2, y: 0, z: 0 });
276
+ createStaticBox(boardWidth, 1, 2, { x: 0, y: -boardHeight / 2 - 1, z: 0 });
277
+ createStaticBox(12, 1, 2, {x: -8, y: 19, z: 0}, {x:0, y:0, z: 0.7});
278
+ createStaticBox(12, 1, 2, {x: 8, y: 19, z: 0}, {x:0, y:0, z: -0.7});
279
+ const rows = 12, spacingY = 2.9, spacingX = 2.9;
280
+ for (let i = 0; i < rows; i++) {
281
+ const numPegs = i % 2 === 0 ? 8 : 7;
282
+ const rowWidth = (numPegs - 1) * spacingX;
283
+ for (let j = 0; j < numPegs; j++) {
284
+ const x = -rowWidth/2 + j * spacingX;
285
+ const y = 14 - i * spacingY;
286
+ createPeg(x, y, 0);
287
+ }
288
+ }
289
+ const scoreSlotsData = [1000, 250, 100, 500, 100, 250, 1000];
290
+ const slotWidth = boardWidth / scoreSlotsData.length;
291
+ const slotY = -boardHeight / 2 + 1;
292
+ for (let i = 0; i < scoreSlotsData.length - 1; i++) {
293
+ createStaticBox(0.5, 4, 2, {x: -boardWidth/2 + (i + 1) * slotWidth, y: slotY + 1, z: 0});
294
+ }
295
+ scoreSlotsData.forEach((points, i) => {
296
+ const x = -boardWidth/2 + (i + 0.5) * slotWidth;
297
+ const triggerBody = new CANNON.Body({isTrigger: true});
298
+ triggerBody.addShape(new CANNON.Box(new CANNON.Vec3(slotWidth/2, 2, 2)));
299
+ triggerBody.position.set(x, slotY, 0);
300
+ triggerBody.points = points;
301
+ triggerBody.isScoreZone = true;
302
+ world.addBody(triggerBody);
303
+ });
304
+ };
305
+ const goldenTicketMesh = new THREE.Mesh(new THREE.PlaneGeometry(16, 6, 1, 1), goldenTicketMaterial);
306
+ goldenTicketMesh.position.set(0, -20, 0);
307
+ scene.add(goldenTicketMesh);
308
+
309
+ // --- Scoring and Ball Creation Logic ---
310
+ const updateScore = (points, scoreZoneBody) => {
311
+ score += points;
312
+ const lifeBarPercent = Math.min((score / targetScore) * 100, 100);
313
+ lifeBar.style.width = `${lifeBarPercent}%`;
314
+ lifeBar.innerText = score;
315
+ // --- Play score sound ---
316
+ if (soundsReady) {
317
+ let note = "C4";
318
+ if (points >= 1000) note = "G5";
319
+ else if (points >= 500) note = "E5";
320
+ else if (points >= 250) note = "C5";
321
+ scoreSynth.triggerAttackRelease(note, "8n");
322
+ }
323
+ };
324
+
325
+ const dropBalls = (count) => {
326
+ if (soundsReady) dropSynth.triggerAttackRelease("8n");
327
+
328
+ for (let i = 0; i < count; i++) {
329
+ const radius = 0.5;
330
+ const spawnX = (Math.random() - 0.5) * count * 0.2;
331
+ const spawnZ = (Math.random() - 0.5) * count * 0.2;
332
+
333
+ const body = new CANNON.Body({ mass: 1, position: new CANNON.Vec3(spawnX, 25, spawnZ), shape: new CANNON.Sphere(radius), material: ballPhysicsMaterial });
334
+ body.angularVelocity.set((Math.random()-0.5)*8, (Math.random()-0.5)*8, (Math.random()-0.5)*8);
335
+ body.hasScored = false;
336
+
337
+ body.addEventListener("collide", (event) => {
338
+ // --- Play score sound ---
339
+ if (event.body.isScoreZone && !body.hasScored) {
340
+ body.hasScored = true;
341
+ updateScore(event.body.points, event.body);
342
+ const elementToRemove = gameElements.find(el => el.body === body);
343
+ if(elementToRemove) {
344
+ elementToRemove.mesh.material.emissive.setHex(0xffffff);
345
+ setTimeout(() => {
346
+ scene.remove(elementToRemove.mesh);
347
+ world.removeBody(elementToRemove.body);
348
+ gameElements.splice(gameElements.indexOf(elementToRemove), 1);
349
+ }, 200);
350
+ }
351
+ }
352
+ // --- Play peg hit sound ---
353
+ if (event.body.isPeg && soundsReady) {
354
+ const impactVelocity = event.contact.getImpactVelocityAlongNormal();
355
+ if (impactVelocity > 1.5) { // Only play sound for significant hits
356
+ const randomNote = ['C5', 'E5', 'G5'][Math.floor(Math.random() * 3)];
357
+ // Tie volume to impact velocity
358
+ const volume = Math.min(-12 + impactVelocity, 0);
359
+ pegHitSynth.triggerAttackRelease(randomNote, "32n", Tone.now(), volume);
360
+ }
361
+ }
362
+ });
363
+
364
+ const mesh = new THREE.Mesh(new THREE.SphereGeometry(radius, 32, 32), ballMaterial.clone());
365
+ world.addBody(body);
366
+ scene.add(mesh);
367
+ gameElements.push({ mesh, body });
368
+ }
369
+ };
370
+
371
+ // --- Animation Loop ---
372
+ const clock = new THREE.Clock();
373
+ let ticketAnimationDone = false;
374
+ function animate() {
375
+ requestAnimationFrame(animate);
376
+ const deltaTime = Math.min(clock.getDelta(), 0.1);
377
+ world.step(1 / 60, deltaTime, 3);
378
+ if (!ticketAnimationDone && goldenTicketMesh.position.y < -15) {
379
+ goldenTicketMesh.position.y += 20 * deltaTime;
380
+ } else {
381
+ ticketAnimationDone = true;
382
+ }
383
+ gameElements.forEach(el => {
384
+ el.mesh.position.copy(el.body.position);
385
+ el.mesh.quaternion.copy(el.body.quaternion);
386
+ if (el.mesh.position.y < -boardHeight / 2 - 5) {
387
+ scene.remove(el.mesh);
388
+ world.removeBody(el.body);
389
+ gameElements.splice(gameElements.indexOf(el), 1);
390
+ }
391
+ });
392
+ renderer.render(scene, camera);
393
+ }
394
+
395
+ // --- Event Listeners ---
396
+ document.querySelectorAll('.ticket-button').forEach(button => {
397
+ button.addEventListener('click', async (e) => {
398
+ // --- Start audio context on first user interaction ---
399
+ if (!soundsReady) {
400
+ await Tone.start();
401
+ soundsReady = true;
402
+ console.log('Audio context started!');
403
+ }
404
+ const count = parseInt(e.target.getAttribute('data-count'), 10);
405
+ dropBalls(count);
406
+ });
407
+ });
408
+ window.addEventListener('resize', () => {
409
+ camera.aspect = gameContainer.clientWidth / gameContainer.clientHeight;
410
+ camera.updateProjectionMatrix();
411
+ renderer.setSize(gameContainer.clientWidth, gameContainer.clientHeight);
412
+ }, false);
413
+
414
+ // --- Start ---
415
+ setupBoard();
416
+ animate();
417
+ };
418
+ </script>
419
+ </body>
420
  </html>