offerpk3 commited on
Commit
4372151
·
verified ·
1 Parent(s): b4e2afb

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +293 -39
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Angry Birds Physics Game</title>
7
  <style>
8
  body {
9
  margin: 0;
@@ -11,6 +11,7 @@
11
  background: linear-gradient(to bottom, #87CEEB 0%, #98FB98 100%);
12
  font-family: 'Arial', sans-serif;
13
  overflow: hidden;
 
14
  }
15
 
16
  #gameCanvas {
@@ -27,6 +28,9 @@
27
  font-size: 18px;
28
  text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
29
  z-index: 100;
 
 
 
30
  }
31
 
32
  #instructions {
@@ -38,6 +42,10 @@
38
  text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
39
  text-align: right;
40
  z-index: 100;
 
 
 
 
41
  }
42
 
43
  #gameOver {
@@ -52,6 +60,7 @@
52
  text-align: center;
53
  display: none;
54
  z-index: 200;
 
55
  }
56
 
57
  button {
@@ -63,10 +72,64 @@
63
  border-radius: 8px;
64
  cursor: pointer;
65
  margin-top: 10px;
 
66
  }
67
 
68
  button:hover {
69
  background: #FF8C42;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
71
  </style>
72
  </head>
@@ -75,14 +138,28 @@
75
  <div>Score: <span id="score">0</span></div>
76
  <div>Birds: <span id="birds">3</span></div>
77
  <div>Pigs: <span id="pigs">3</span></div>
 
78
  </div>
79
 
80
  <div id="instructions">
81
- Click and drag to aim<br>
82
- Release to shoot<br>
83
- Destroy all pigs to win!
 
 
 
 
 
 
 
 
 
 
 
84
  </div>
85
 
 
 
86
  <div id="gameOver">
87
  <h2 id="gameOverTitle">Game Over</h2>
88
  <p id="gameOverMessage">Try again!</p>
@@ -94,6 +171,10 @@
94
  <script>
95
  const canvas = document.getElementById('gameCanvas');
96
  const ctx = canvas.getContext('2d');
 
 
 
 
97
 
98
  // Game state
99
  let gameState = {
@@ -103,7 +184,9 @@
103
  isAiming: false,
104
  isDragging: false,
105
  gameWon: false,
106
- gameLost: false
 
 
107
  };
108
 
109
  // Physics constants
@@ -124,6 +207,7 @@
124
  let currentBird = null;
125
  let aimStartX = 0;
126
  let aimStartY = 0;
 
127
 
128
  // Game objects
129
  let birds = [];
@@ -142,6 +226,7 @@
142
  this.color = '#FF0000';
143
  this.launched = false;
144
  this.trail = [];
 
145
  }
146
 
147
  update() {
@@ -161,6 +246,10 @@
161
  this.y = canvas.height - 50 - this.radius;
162
  this.vy *= -BOUNCE_DAMPING;
163
  this.vx *= FRICTION;
 
 
 
 
164
  }
165
 
166
  // Wall collision
@@ -212,6 +301,18 @@
212
  ctx.lineTo(this.x + 8, this.y + 8);
213
  ctx.closePath();
214
  ctx.fill();
 
 
 
 
 
 
 
 
 
 
 
 
215
  }
216
  }
217
 
@@ -336,14 +437,15 @@
336
 
337
  // Particle class for explosions
338
  class Particle {
339
- constructor(x, y) {
340
  this.x = x;
341
  this.y = y;
342
- this.vx = (Math.random() - 0.5) * 10;
343
- this.vy = (Math.random() - 0.5) * 10;
344
  this.life = 30;
345
  this.maxLife = 30;
346
- this.color = `hsl(${Math.random() * 60 + 15}, 100%, 50%)`;
 
347
  }
348
 
349
  update() {
@@ -359,16 +461,52 @@
359
  ctx.globalAlpha = alpha;
360
  ctx.fillStyle = this.color;
361
  ctx.beginPath();
362
- ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
363
  ctx.fill();
364
  ctx.globalAlpha = 1;
365
  }
366
  }
367
 
368
  function createExplosion(x, y) {
369
- for (let i = 0; i < 15; i++) {
370
- particles.push(new Particle(x, y));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  }
 
 
372
  }
373
 
374
  function initializeLevel() {
@@ -377,32 +515,35 @@
377
  blocks = [];
378
  particles = [];
379
 
380
- // Create tower structure
381
- const towerX = 800;
382
  const towerY = 400;
383
 
384
- // Base blocks
385
- blocks.push(new Block(towerX, towerY + 200, 40, 100, 'wood'));
386
- blocks.push(new Block(towerX + 40, towerY + 200, 40, 100, 'stone'));
387
- blocks.push(new Block(towerX + 80, towerY + 200, 40, 100, 'wood'));
388
 
389
- // Middle level
390
- blocks.push(new Block(towerX + 10, towerY + 100, 80, 20, 'wood'));
391
- blocks.push(new Block(towerX + 20, towerY + 80, 20, 100, 'stone'));
392
- blocks.push(new Block(towerX + 60, towerY + 80, 20, 100, 'wood'));
393
 
394
- // Top level
395
- blocks.push(new Block(towerX + 30, towerY, 60, 20, 'wood'));
396
 
397
- // Place pigs
398
  pigs.push(new Pig(towerX + 60, towerY + 180));
399
- pigs.push(new Pig(towerX + 50, towerY + 60));
400
  pigs.push(new Pig(towerX + 60, towerY - 20));
401
 
 
 
 
402
  // Reset game state
403
  gameState.score = 0;
404
  gameState.birdsLeft = 3;
405
- gameState.pigsLeft = 3;
406
  gameState.gameWon = false;
407
  gameState.gameLost = false;
408
 
@@ -429,6 +570,16 @@
429
  const angle = Math.atan2(dy, dx);
430
  bird.vx = Math.cos(angle) * 5;
431
  bird.vy = Math.sin(angle) * 5;
 
 
 
 
 
 
 
 
 
 
432
  }
433
  });
434
 
@@ -447,6 +598,16 @@
447
  // Simple bounce
448
  bird.vx *= -0.5;
449
  bird.vy *= -0.5;
 
 
 
 
 
 
 
 
 
 
450
  }
451
  });
452
  });
@@ -456,18 +617,19 @@
456
  document.getElementById('score').textContent = gameState.score;
457
  document.getElementById('birds').textContent = gameState.birdsLeft;
458
  document.getElementById('pigs').textContent = gameState.pigsLeft;
 
459
  }
460
 
461
  function checkGameState() {
462
  if (gameState.pigsLeft <= 0 && !gameState.gameWon) {
463
  gameState.gameWon = true;
464
- document.getElementById('gameOverTitle').textContent = 'You Win!';
465
- document.getElementById('gameOverMessage').textContent = `Score: ${gameState.score}`;
466
  document.getElementById('gameOver').style.display = 'block';
467
  } else if (gameState.birdsLeft <= 0 && gameState.pigsLeft > 0 && !gameState.gameLost) {
468
  gameState.gameLost = true;
469
- document.getElementById('gameOverTitle').textContent = 'Game Over';
470
- document.getElementById('gameOverMessage').textContent = 'The pigs won this time!';
471
  document.getElementById('gameOver').style.display = 'block';
472
  }
473
  }
@@ -504,6 +666,45 @@
504
  }
505
  }
506
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  function drawBackground() {
508
  // Sky gradient
509
  const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
@@ -516,10 +717,18 @@
516
  ctx.fillStyle = '#228B22';
517
  ctx.fillRect(0, canvas.height - 50, canvas.width, 50);
518
 
 
 
 
 
 
 
 
 
519
  // Clouds
520
  ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
521
  for (let i = 0; i < 5; i++) {
522
- const x = (i * 250) + 100;
523
  const y = 100 + Math.sin(Date.now() * 0.001 + i) * 20;
524
  ctx.beginPath();
525
  ctx.arc(x, y, 30, 0, Math.PI * 2);
@@ -535,6 +744,11 @@
535
  drawBackground();
536
  drawSlingshot();
537
 
 
 
 
 
 
538
  // Update and draw birds
539
  birds.forEach(bird => {
540
  bird.update();
@@ -546,10 +760,10 @@
546
  currentBird.draw();
547
  }
548
 
549
- // Update and draw pigs
550
  pigs.forEach(pig => pig.draw());
551
 
552
- // Update and draw blocks
553
  blocks.forEach(block => block.draw());
554
 
555
  // Update and draw particles
@@ -568,7 +782,7 @@
568
 
569
  // Mouse events
570
  canvas.addEventListener('mousedown', (e) => {
571
- if (!currentBird || currentBird.launched) return;
572
 
573
  const rect = canvas.getBoundingClientRect();
574
  const mouseX = e.clientX - rect.left;
@@ -582,6 +796,7 @@
582
  gameState.isDragging = true;
583
  aimStartX = mouseX;
584
  aimStartY = mouseY;
 
585
  }
586
  });
587
 
@@ -595,7 +810,11 @@
595
  const dx = slingshot.x - mouseX;
596
  const dy = (slingshot.y - 20) - mouseY;
597
  const distance = Math.sqrt(dx * dx + dy * dy);
598
- const maxDistance = 80;
 
 
 
 
599
 
600
  if (distance > maxDistance) {
601
  const angle = Math.atan2(dy, dx);
@@ -605,6 +824,19 @@
605
  currentBird.x = mouseX;
606
  currentBird.y = mouseY;
607
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
608
  });
609
 
610
  canvas.addEventListener('mouseup', (e) => {
@@ -612,17 +844,24 @@
612
 
613
  gameState.isAiming = false;
614
  gameState.isDragging = false;
 
 
615
 
616
  const dx = slingshot.x - currentBird.x;
617
  const dy = (slingshot.y - 20) - currentBird.y;
618
- const power = Math.sqrt(dx * dx + dy * dy) * 0.15;
619
 
620
- currentBird.vx = dx * 0.15;
621
- currentBird.vy = dy * 0.15;
622
  currentBird.launched = true;
623
 
624
  birds.push(currentBird);
625
  gameState.birdsLeft--;
 
 
 
 
 
626
 
627
  // Create next bird if available
628
  if (gameState.birdsLeft > 0) {
@@ -634,6 +873,21 @@
634
  }
635
  });
636
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
  function resetGame() {
638
  document.getElementById('gameOver').style.display = 'none';
639
  initializeLevel();
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Enhanced Angry Birds Physics Game</title>
7
  <style>
8
  body {
9
  margin: 0;
 
11
  background: linear-gradient(to bottom, #87CEEB 0%, #98FB98 100%);
12
  font-family: 'Arial', sans-serif;
13
  overflow: hidden;
14
+ user-select: none;
15
  }
16
 
17
  #gameCanvas {
 
28
  font-size: 18px;
29
  text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
30
  z-index: 100;
31
+ background: rgba(0,0,0,0.3);
32
+ padding: 10px;
33
+ border-radius: 10px;
34
  }
35
 
36
  #instructions {
 
42
  text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
43
  text-align: right;
44
  z-index: 100;
45
+ background: rgba(0,0,0,0.3);
46
+ padding: 10px;
47
+ border-radius: 10px;
48
+ max-width: 200px;
49
  }
50
 
51
  #gameOver {
 
60
  text-align: center;
61
  display: none;
62
  z-index: 200;
63
+ box-shadow: 0 0 20px rgba(255, 165, 0, 0.5);
64
  }
65
 
66
  button {
 
72
  border-radius: 8px;
73
  cursor: pointer;
74
  margin-top: 10px;
75
+ transition: all 0.2s;
76
  }
77
 
78
  button:hover {
79
  background: #FF8C42;
80
+ transform: scale(1.05);
81
+ }
82
+
83
+ #powerBar {
84
+ position: absolute;
85
+ bottom: 20px;
86
+ left: 50%;
87
+ transform: translateX(-50%);
88
+ width: 200px;
89
+ height: 15px;
90
+ background: rgba(0,0,0,0.3);
91
+ border-radius: 10px;
92
+ overflow: hidden;
93
+ display: none;
94
+ z-index: 100;
95
+ }
96
+
97
+ #powerFill {
98
+ height: 100%;
99
+ background: linear-gradient(to right, #4CAF50, #FFC107, #F44336);
100
+ width: 0%;
101
+ transition: width 0.1s;
102
+ }
103
+
104
+ #targetReticle {
105
+ position: absolute;
106
+ width: 30px;
107
+ height: 30px;
108
+ border: 2px solid rgba(255, 255, 255, 0.8);
109
+ border-radius: 50%;
110
+ display: none;
111
+ pointer-events: none;
112
+ z-index: 150;
113
+ }
114
+
115
+ #targetReticle::before, #targetReticle::after {
116
+ content: '';
117
+ position: absolute;
118
+ background: rgba(255, 255, 255, 0.8);
119
+ }
120
+
121
+ #targetReticle::before {
122
+ width: 2px;
123
+ height: 100%;
124
+ left: 50%;
125
+ transform: translateX(-50%);
126
+ }
127
+
128
+ #targetReticle::after {
129
+ width: 100%;
130
+ height: 2px;
131
+ top: 50%;
132
+ transform: translateY(-50%);
133
  }
134
  </style>
135
  </head>
 
138
  <div>Score: <span id="score">0</span></div>
139
  <div>Birds: <span id="birds">3</span></div>
140
  <div>Pigs: <span id="pigs">3</span></div>
141
+ <div>Power: <span id="powerValue">0%</span></div>
142
  </div>
143
 
144
  <div id="instructions">
145
+ <strong>HOW TO PLAY:</strong><br>
146
+ <div style="margin-top: 8px;">
147
+ <span style="color: #FFD700;">●</span> Click and drag to aim<br>
148
+ <span style="color: #FFD700;">●</span> Pull back for more power<br>
149
+ <span style="color: #FFD700;">●</span> Release to shoot<br>
150
+ <span style="color: #FFD700;">●</span> Destroy all pigs to win!<br>
151
+ </div>
152
+ <div style="margin-top: 10px; font-size: 12px; color: #FFD700;">
153
+ <span style="color: #FF6347;">PRO TIP:</span> Aim above distant targets to account for gravity
154
+ </div>
155
+ </div>
156
+
157
+ <div id="powerBar">
158
+ <div id="powerFill"></div>
159
  </div>
160
 
161
+ <div id="targetReticle"></div>
162
+
163
  <div id="gameOver">
164
  <h2 id="gameOverTitle">Game Over</h2>
165
  <p id="gameOverMessage">Try again!</p>
 
171
  <script>
172
  const canvas = document.getElementById('gameCanvas');
173
  const ctx = canvas.getContext('2d');
174
+ const powerBar = document.getElementById('powerBar');
175
+ const powerFill = document.getElementById('powerFill');
176
+ const powerValue = document.getElementById('powerValue');
177
+ const targetReticle = document.getElementById('targetReticle');
178
 
179
  // Game state
180
  let gameState = {
 
184
  isAiming: false,
185
  isDragging: false,
186
  gameWon: false,
187
+ gameLost: false,
188
+ maxPower: 150,
189
+ currentPower: 0
190
  };
191
 
192
  // Physics constants
 
207
  let currentBird = null;
208
  let aimStartX = 0;
209
  let aimStartY = 0;
210
+ let trajectoryPoints = [];
211
 
212
  // Game objects
213
  let birds = [];
 
226
  this.color = '#FF0000';
227
  this.launched = false;
228
  this.trail = [];
229
+ this.type = 'red'; // Default bird type
230
  }
231
 
232
  update() {
 
246
  this.y = canvas.height - 50 - this.radius;
247
  this.vy *= -BOUNCE_DAMPING;
248
  this.vx *= FRICTION;
249
+ // Create dust particles on impact
250
+ if (Math.abs(this.vy) > 1) {
251
+ createDust(this.x, canvas.height - 50);
252
+ }
253
  }
254
 
255
  // Wall collision
 
301
  ctx.lineTo(this.x + 8, this.y + 8);
302
  ctx.closePath();
303
  ctx.fill();
304
+
305
+ // Draw angry eyebrows if launched
306
+ if (this.launched) {
307
+ ctx.strokeStyle = 'black';
308
+ ctx.lineWidth = 2;
309
+ ctx.beginPath();
310
+ ctx.moveTo(this.x - 10, this.y - 10);
311
+ ctx.lineTo(this.x - 2, this.y - 8);
312
+ ctx.moveTo(this.x + 10, this.y - 10);
313
+ ctx.lineTo(this.x + 2, this.y - 8);
314
+ ctx.stroke();
315
+ }
316
  }
317
  }
318
 
 
437
 
438
  // Particle class for explosions
439
  class Particle {
440
+ constructor(x, y, color, size = 3, velocityMultiplier = 1) {
441
  this.x = x;
442
  this.y = y;
443
+ this.vx = (Math.random() - 0.5) * 10 * velocityMultiplier;
444
+ this.vy = (Math.random() - 0.5) * 10 * velocityMultiplier;
445
  this.life = 30;
446
  this.maxLife = 30;
447
+ this.color = color || `hsl(${Math.random() * 60 + 15}, 100%, 50%)`;
448
+ this.size = size;
449
  }
450
 
451
  update() {
 
461
  ctx.globalAlpha = alpha;
462
  ctx.fillStyle = this.color;
463
  ctx.beginPath();
464
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
465
  ctx.fill();
466
  ctx.globalAlpha = 1;
467
  }
468
  }
469
 
470
  function createExplosion(x, y) {
471
+ for (let i = 0; i < 20; i++) {
472
+ particles.push(new Particle(x, y, `hsl(${Math.random() * 60 + 15}, 100%, 50%)`, Math.random() * 4 + 2, 1.5));
473
+ }
474
+ }
475
+
476
+ function createDust(x, y) {
477
+ for (let i = 0; i < 10; i++) {
478
+ particles.push(new Particle(x, y, '#8B4513', Math.random() * 3 + 1, 0.8));
479
+ }
480
+ }
481
+
482
+ // Calculate trajectory points for aiming prediction
483
+ function calculateTrajectory(startX, startY, velX, velY) {
484
+ const points = [];
485
+ let simX = startX;
486
+ let simY = startY;
487
+ let simVX = velX;
488
+ let simVY = velY;
489
+
490
+ for (let i = 0; i < 100; i++) {
491
+ simVY += GRAVITY;
492
+ simVX *= FRICTION;
493
+ simVY *= FRICTION;
494
+
495
+ simX += simVX;
496
+ simY += simVY;
497
+
498
+ points.push({x: simX, y: simY});
499
+
500
+ // Stop if below ground or out of bounds
501
+ if (simY > canvas.height - 50 ||
502
+ simX < 0 ||
503
+ simX > canvas.width ||
504
+ points.length > 80) {
505
+ break;
506
+ }
507
  }
508
+
509
+ return points;
510
  }
511
 
512
  function initializeLevel() {
 
515
  blocks = [];
516
  particles = [];
517
 
518
+ // Create more complex tower structure
519
+ const towerX = 900;
520
  const towerY = 400;
521
 
522
+ // Base blocks (strong stone)
523
+ blocks.push(new Block(towerX - 20, towerY + 200, 40, 100, 'stone'));
524
+ blocks.push(new Block(towerX + 20, towerY + 200, 40, 100, 'stone'));
525
+ blocks.push(new Block(towerX + 60, towerY + 200, 40, 100, 'stone'));
526
 
527
+ // Middle level (wood)
528
+ blocks.push(new Block(towerX, towerY + 120, 80, 20, 'wood'));
529
+ blocks.push(new Block(towerX - 20, towerY + 80, 40, 100, 'wood'));
530
+ blocks.push(new Block(towerX + 40, towerY + 80, 40, 100, 'wood'));
531
 
532
+ // Top level (wood)
533
+ blocks.push(new Block(towerX + 10, towerY, 60, 20, 'wood'));
534
 
535
+ // Place pigs in more strategic positions
536
  pigs.push(new Pig(towerX + 60, towerY + 180));
537
+ pigs.push(new Pig(towerX + 30, towerY + 60));
538
  pigs.push(new Pig(towerX + 60, towerY - 20));
539
 
540
+ // Add a pig on the ground near the tower for easier targeting
541
+ pigs.push(new Pig(towerX + 120, canvas.height - 70));
542
+
543
  // Reset game state
544
  gameState.score = 0;
545
  gameState.birdsLeft = 3;
546
+ gameState.pigsLeft = pigs.length;
547
  gameState.gameWon = false;
548
  gameState.gameLost = false;
549
 
 
570
  const angle = Math.atan2(dy, dx);
571
  bird.vx = Math.cos(angle) * 5;
572
  bird.vy = Math.sin(angle) * 5;
573
+
574
+ // Create impact particles
575
+ for (let i = 0; i < 8; i++) {
576
+ particles.push(new Particle(
577
+ pig.x, pig.y,
578
+ '#00FF00',
579
+ Math.random() * 3 + 1,
580
+ 0.7
581
+ ));
582
+ }
583
  }
584
  });
585
 
 
598
  // Simple bounce
599
  bird.vx *= -0.5;
600
  bird.vy *= -0.5;
601
+
602
+ // Create impact particles
603
+ for (let i = 0; i < 8; i++) {
604
+ particles.push(new Particle(
605
+ bird.x, bird.y,
606
+ block.color,
607
+ Math.random() * 3 + 1,
608
+ 0.7
609
+ ));
610
+ }
611
  }
612
  });
613
  });
 
617
  document.getElementById('score').textContent = gameState.score;
618
  document.getElementById('birds').textContent = gameState.birdsLeft;
619
  document.getElementById('pigs').textContent = gameState.pigsLeft;
620
+ powerValue.textContent = Math.min(100, Math.round(gameState.currentPower / gameState.maxPower * 100)) + '%';
621
  }
622
 
623
  function checkGameState() {
624
  if (gameState.pigsLeft <= 0 && !gameState.gameWon) {
625
  gameState.gameWon = true;
626
+ document.getElementById('gameOverTitle').textContent = 'Victory!';
627
+ document.getElementById('gameOverMessage').textContent = `You destroyed all pigs with a score of ${gameState.score}!`;
628
  document.getElementById('gameOver').style.display = 'block';
629
  } else if (gameState.birdsLeft <= 0 && gameState.pigsLeft > 0 && !gameState.gameLost) {
630
  gameState.gameLost = true;
631
+ document.getElementById('gameOverTitle').textContent = 'Defeat';
632
+ document.getElementById('gameOverMessage').textContent = 'The pigs are still standing!';
633
  document.getElementById('gameOver').style.display = 'block';
634
  }
635
  }
 
666
  }
667
  }
668
 
669
+ function drawTrajectory() {
670
+ if (trajectoryPoints.length < 2) return;
671
+
672
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
673
+ ctx.lineWidth = 2;
674
+ ctx.beginPath();
675
+ ctx.moveTo(trajectoryPoints[0].x, trajectoryPoints[0].y);
676
+
677
+ for (let i = 1; i < trajectoryPoints.length; i++) {
678
+ ctx.lineTo(trajectoryPoints[i].x, trajectoryPoints[i].y);
679
+ }
680
+ ctx.stroke();
681
+
682
+ // Draw dots at points
683
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
684
+ for (let i = 0; i < trajectoryPoints.length; i += 3) {
685
+ ctx.beginPath();
686
+ ctx.arc(trajectoryPoints[i].x, trajectoryPoints[i].y, 3, 0, Math.PI * 2);
687
+ ctx.fill();
688
+ }
689
+
690
+ // Draw impact marker if trajectory hits something
691
+ const lastPoint = trajectoryPoints[trajectoryPoints.length - 1];
692
+ if (lastPoint.y > canvas.height - 60) {
693
+ ctx.strokeStyle = 'rgba(255, 100, 100, 0.8)';
694
+ ctx.lineWidth = 2;
695
+ ctx.beginPath();
696
+ ctx.arc(lastPoint.x, lastPoint.y, 10, 0, Math.PI * 2);
697
+ ctx.stroke();
698
+
699
+ ctx.beginPath();
700
+ ctx.moveTo(lastPoint.x - 15, lastPoint.y);
701
+ ctx.lineTo(lastPoint.x + 15, lastPoint.y);
702
+ ctx.moveTo(lastPoint.x, lastPoint.y - 15);
703
+ ctx.lineTo(lastPoint.x, lastPoint.y + 15);
704
+ ctx.stroke();
705
+ }
706
+ }
707
+
708
  function drawBackground() {
709
  // Sky gradient
710
  const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
 
717
  ctx.fillStyle = '#228B22';
718
  ctx.fillRect(0, canvas.height - 50, canvas.width, 50);
719
 
720
+ // Ground details
721
+ ctx.fillStyle = '#1E7A1E';
722
+ for (let i = 0; i < 20; i++) {
723
+ const x = Math.random() * canvas.width;
724
+ const width = Math.random() * 30 + 10;
725
+ ctx.fillRect(x, canvas.height - 50, width, 3);
726
+ }
727
+
728
  // Clouds
729
  ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
730
  for (let i = 0; i < 5; i++) {
731
+ const x = (i * 250) + 100 + Math.sin(Date.now() * 0.0005 + i) * 50;
732
  const y = 100 + Math.sin(Date.now() * 0.001 + i) * 20;
733
  ctx.beginPath();
734
  ctx.arc(x, y, 30, 0, Math.PI * 2);
 
744
  drawBackground();
745
  drawSlingshot();
746
 
747
+ // Draw trajectory if aiming
748
+ if (gameState.isAiming && trajectoryPoints.length > 0) {
749
+ drawTrajectory();
750
+ }
751
+
752
  // Update and draw birds
753
  birds.forEach(bird => {
754
  bird.update();
 
760
  currentBird.draw();
761
  }
762
 
763
+ // Draw pigs
764
  pigs.forEach(pig => pig.draw());
765
 
766
+ // Draw blocks
767
  blocks.forEach(block => block.draw());
768
 
769
  // Update and draw particles
 
782
 
783
  // Mouse events
784
  canvas.addEventListener('mousedown', (e) => {
785
+ if (!currentBird || currentBird.launched || gameState.gameWon || gameState.gameLost) return;
786
 
787
  const rect = canvas.getBoundingClientRect();
788
  const mouseX = e.clientX - rect.left;
 
796
  gameState.isDragging = true;
797
  aimStartX = mouseX;
798
  aimStartY = mouseY;
799
+ powerBar.style.display = 'block';
800
  }
801
  });
802
 
 
810
  const dx = slingshot.x - mouseX;
811
  const dy = (slingshot.y - 20) - mouseY;
812
  const distance = Math.sqrt(dx * dx + dy * dy);
813
+ const maxDistance = gameState.maxPower;
814
+
815
+ // Update power value
816
+ gameState.currentPower = Math.min(maxDistance, distance);
817
+ powerFill.style.width = (gameState.currentPower / maxDistance * 100) + '%';
818
 
819
  if (distance > maxDistance) {
820
  const angle = Math.atan2(dy, dx);
 
824
  currentBird.x = mouseX;
825
  currentBird.y = mouseY;
826
  }
827
+
828
+ // Calculate trajectory
829
+ const velX = (slingshot.x - currentBird.x) * 0.2;
830
+ const velY = (slingshot.y - 20 - currentBird.y) * 0.2;
831
+ trajectoryPoints = calculateTrajectory(currentBird.x, currentBird.y, velX, velY);
832
+
833
+ // Show target reticle at the end of trajectory
834
+ if (trajectoryPoints.length > 0) {
835
+ const lastPoint = trajectoryPoints[trajectoryPoints.length - 1];
836
+ targetReticle.style.display = 'block';
837
+ targetReticle.style.left = (lastPoint.x - 15) + 'px';
838
+ targetReticle.style.top = (lastPoint.y - 15) + 'px';
839
+ }
840
  });
841
 
842
  canvas.addEventListener('mouseup', (e) => {
 
844
 
845
  gameState.isAiming = false;
846
  gameState.isDragging = false;
847
+ powerBar.style.display = 'none';
848
+ targetReticle.style.display = 'none';
849
 
850
  const dx = slingshot.x - currentBird.x;
851
  const dy = (slingshot.y - 20) - currentBird.y;
852
+ const power = Math.sqrt(dx * dx + dy * dy) * 0.2;
853
 
854
+ currentBird.vx = dx * 0.2;
855
+ currentBird.vy = dy * 0.2;
856
  currentBird.launched = true;
857
 
858
  birds.push(currentBird);
859
  gameState.birdsLeft--;
860
+ gameState.currentPower = 0;
861
+ powerFill.style.width = '0%';
862
+
863
+ // Reset trajectory
864
+ trajectoryPoints = [];
865
 
866
  // Create next bird if available
867
  if (gameState.birdsLeft > 0) {
 
873
  }
874
  });
875
 
876
+ canvas.addEventListener('mouseout', (e) => {
877
+ if (gameState.isDragging) {
878
+ gameState.isDragging = false;
879
+ gameState.isAiming = false;
880
+ powerBar.style.display = 'none';
881
+ targetReticle.style.display = 'none';
882
+
883
+ // Reset bird position if dragging was interrupted
884
+ if (currentBird && !currentBird.launched) {
885
+ currentBird.x = slingshot.x;
886
+ currentBird.y = slingshot.y - 20;
887
+ }
888
+ }
889
+ });
890
+
891
  function resetGame() {
892
  document.getElementById('gameOver').style.display = 'none';
893
  initializeLevel();