cutechicken commited on
Commit
03f0d26
ยท
verified ยท
1 Parent(s): b9c6a25

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1001 -640
index.html CHANGED
@@ -2,7 +2,7 @@
2
  <html>
3
  <head>
4
  <title>Tank Battle</title>
5
- <style>
6
  body {
7
  margin: 0;
8
  overflow: hidden;
@@ -52,10 +52,10 @@
52
  top: 80% !important;
53
  }
54
  #restart {
55
- top: 80% !important; /* ์žฌ์‹œ์ž‘ ๋ฒ„ํŠผ ์œ„์น˜ ์ถ”๊ฐ€ */
56
  }
57
  #winMessage {
58
- top: 30% !important; /* ์Šน๋ฆฌ ๋ฉ”์‹œ์ง€ ์œ„์น˜ ์ถ”๊ฐ€ */
59
  font-size: 72px;
60
  background: none;
61
  }
@@ -137,12 +137,11 @@
137
  <h1>TANK WAR</h1>
138
  <div id="stageSelect">
139
  <button class="stageButton" onclick="startStage(1)">Stage 1</button>
140
- <button class="stageButton" disabled>Stage 2</button>
141
  <button class="stageButton" disabled>Stage 3</button>
142
  <button class="stageButton" disabled>Stage 4</button>
143
  </div>
144
  </div>
145
-
146
  <div id="shop" style="display:none; position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); background:rgba(0,0,0,0.9); padding:20px; border-radius:10px; color:white; z-index:1000;">
147
  <h2>Tank Shop</h2>
148
  <div style="display:flex; gap:20px;">
@@ -200,6 +199,7 @@
200
 
201
  // Game state
202
  let currentRound = 1;
 
203
  let gameOver = false;
204
  let currentWeapon = 'cannon';
205
  let enemies = [];
@@ -217,8 +217,13 @@
217
  let hasJU87 = false;
218
  let lastJU87Spawn = 0;
219
  let supportUnits = [];
 
220
  let lastSupportSpawn = 0;
221
  let gameStarted = false;
 
 
 
 
222
 
223
  // Load assets
224
  const backgroundImg = new Image();
@@ -229,8 +234,7 @@
229
  enemyImg.src = 'enemy.png';
230
  const bulletImg = new Image();
231
  bulletImg.src = 'apcr2.png';
232
-
233
- // Audio setup
234
  const cannonSound = new Audio('firemn.ogg');
235
  const machinegunSound = new Audio('firemg.ogg');
236
  const enemyFireSound = new Audio('fireenemy.ogg');
@@ -266,28 +270,49 @@
266
  maxHealth: 1000
267
  };
268
 
269
- function startStage(stageNumber) {
270
- console.log("Starting stage:", stageNumber);
271
- const titleScreen = document.getElementById('titleScreen');
272
- if (stageNumber === 1) {
273
- titleScreen.style.display = 'none';
274
- document.getElementById('instructions').style.display = 'block';
275
- document.getElementById('weaponInfo').style.display = 'block';
276
- document.getElementById('gameCanvas').style.display = 'block';
277
-
278
- bgm.pause();
279
- bgm = new Audio('BGM2.ogg');
280
- bgm.loop = true;
281
- bgm.play().catch(err => console.error("Error playing game music:", err));
282
-
283
- gameStarted = true;
284
- currentRound = 1;
285
- initRound();
286
- gameLoop();
287
- }
288
- }
289
-
290
- function startCountdown() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  isCountingDown = true;
292
  countdownTime = 3;
293
  countdownEl.style.display = 'block';
@@ -318,7 +343,9 @@
318
 
319
  // ์  ์ƒ์„ฑ
320
  enemies = [];
321
- const enemyCount = currentRound;
 
 
322
  for(let i = 0; i < enemyCount; i++) {
323
  let x, y;
324
  const edge = Math.floor(Math.random() * 4);
@@ -343,6 +370,11 @@
343
  supportUnits = [];
344
  lastSupportSpawn = 0;
345
 
 
 
 
 
 
346
  console.log(`Round ${currentRound} initialized with ${enemies.length} enemies`);
347
 
348
  // ์นด์šดํŠธ๋‹ค์šด ์‹œ์ž‘
@@ -356,693 +388,1022 @@
356
  }, 3000);
357
  }
358
  }
359
-
360
- function checkRoundClear() {
361
- if(enemies.length === 0) {
362
- console.log(`Checking round clear: Current round ${currentRound}, Boss stage: ${isBossStage}`);
363
-
364
- if (!isBossStage) {
365
- if(currentRound < 10) {
366
- console.log('Normal round clear - showing next round button and shop');
367
- nextRoundBtn.style.display = 'block';
368
- document.getElementById('bossButton').style.display = 'none';
369
- showShop();
370
- } else {
371
- console.log('Final round clear - showing boss button');
372
- nextRoundBtn.style.display = 'none';
373
- document.getElementById('bossButton').style.display = 'block';
374
- document.getElementById('shop').style.display = 'none';
375
- }
376
- } else {
377
- console.log('Boss clear - showing victory message');
378
- gameOver = true;
379
- document.getElementById('winMessage').style.display = 'block';
380
- document.getElementById('bossButton').style.display = 'none';
381
- nextRoundBtn.style.display = 'none';
382
- document.getElementById('shop').style.display = 'none';
383
- restartBtn.style.display = 'block';
384
- bgm.pause();
385
- const victorySound = new Audio('victory.ogg');
386
- victorySound.play();
387
- }
388
  }
 
 
 
 
 
 
 
 
 
 
 
389
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
 
391
- function showShop() {
392
- document.getElementById('shop').style.display = 'block';
393
- }
394
-
395
- const defaultPlayerStats = {
396
- maxHealth: 1000,
397
- speed: 5,
398
- width: 100,
399
- height: 45
400
- };
401
-
402
- // player ๋ถ€๋ถ„ ์ˆ˜์ • (buyTank ํ•จ์ˆ˜)
403
- function buyTank(tankImg, cost, tankId) {
404
- if (gold >= cost) {
405
- gold -= cost;
406
- playerImg.src = tankImg;
407
- document.getElementById(tankId).style.display = 'none';
408
- document.getElementById('shop').style.display = 'none';
409
 
410
- if (tankId === 'tank1') {
411
- player.maxHealth = 1500;
412
- player.speed = defaultPlayerStats.speed;
413
- player.width = 90;
414
- player.height = 50;
415
- } else if (tankId === 'tank2') {
416
- player.maxHealth = 2000;
417
- player.speed = defaultPlayerStats.speed * 0.7;
418
- player.width = 100;
419
- player.height = 45;
420
- }
421
 
422
- player.health = player.maxHealth;
 
 
 
 
 
 
 
423
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  }
425
 
426
- function buyAPCR() {
427
- if (gold >= 1000 && !hasAPCR) {
428
- gold -= 1000;
429
- hasAPCR = true;
430
- document.getElementById('apcr').style.display = 'none';
431
- document.getElementById('shop').style.display = 'none';
432
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  }
 
 
434
 
435
- function buyBF109() {
436
- if (gold >= 1000 && !hasBF109) {
437
- gold -= 1000;
438
- hasBF109 = true;
439
- document.getElementById('bf109').style.display = 'none';
440
- document.getElementById('shop').style.display = 'none';
441
- }
 
 
 
 
 
 
 
 
 
442
  }
 
443
 
444
- function buyJU87() {
445
- if (gold >= 1500 && !hasJU87) {
446
- gold -= 1500;
447
- hasJU87 = true;
448
- document.getElementById('ju87').style.display = 'none';
449
- document.getElementById('shop').style.display = 'none';
450
- lastJU87Spawn = Date.now();
 
 
 
451
  }
 
 
 
 
452
  }
453
-
454
- function updateGame() {
455
- if(gameOver) return;
456
- if(!isCountingDown) {
457
- // ํ”Œ๋ ˆ์ด์–ด ์›€์ง์ž„
458
- if(keys['w']) player.y -= player.speed;
459
- if(keys['s']) player.y += player.speed;
460
- if(keys['a']) player.x -= player.speed;
461
- if(keys['d']) player.x += player.speed;
462
-
463
- player.x = Math.max(player.width/2, Math.min(canvas.width - player.width/2, player.x));
464
- player.y = Math.max(player.height/2, Math.min(canvas.height - player.height/2, player.y));
465
-
466
- fireBullet();
467
  }
 
468
 
469
- // BF109 ๊ด€๋ จ ์ฝ”๋“œ
470
- if (hasBF109 && !isCountingDown) {
471
- const now = Date.now();
472
- if (now - lastSupportSpawn > 10000) { // 10์ดˆ๋งˆ๋‹ค
473
- supportUnits.push(
474
- new SupportUnit(canvas.height * 0.2),
475
- new SupportUnit(canvas.height * 0.5),
476
- new SupportUnit(canvas.height * 0.8)
477
- );
478
- lastSupportSpawn = now;
479
- }
480
- }
481
 
482
- // JU87 ๊ด€๋ จ ์ฝ”๋“œ
483
- if (hasJU87 && !isCountingDown) {
484
- const now = Date.now();
485
- if (now - lastJU87Spawn > 15000) { // 15์ดˆ๋งˆ๋‹ค
486
- supportUnits.push(new JU87());
487
- lastJU87Spawn = now;
488
- }
489
  }
 
 
490
 
491
- // ์ง€์› ์œ ๋‹› ์—…๋ฐ์ดํŠธ
492
- supportUnits = supportUnits.filter(unit => unit.update());
493
-
494
- // ์  ์—…๋ฐ์ดํŠธ - ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰
495
- enemies.forEach(enemy => enemy.update());
496
 
497
- if(!isCountingDown) {
498
- // ์ด์•Œ ์ฒ˜๋ฆฌ
499
- bullets = bullets.filter(bullet => {
500
- bullet.x += Math.cos(bullet.angle) * bullet.speed;
501
- bullet.y += Math.sin(bullet.angle) * bullet.speed;
502
-
503
- if(!bullet.isEnemy) {
504
- enemies = enemies.filter(enemy => {
505
- const dist = Math.hypot(bullet.x - enemy.x, bullet.y - enemy.y);
506
- if(dist < 30) {
507
- let damage = currentWeapon === 'cannon' ? 250 : 50;
508
- enemy.health -= damage;
509
- if(enemy.health <= 0) {
510
- spawnHealthItem(enemy.x, enemy.y);
511
- gold += 100;
512
- effects.push(new Effect(enemy.x, enemy.y, 1000, 'death'));
513
- deathSound.cloneNode().play();
514
- return false;
515
- }
516
- return true;
517
- }
518
- return true;
519
- });
520
- } else {
521
- const dist = Math.hypot(bullet.x - player.x, bullet.y - player.y);
522
- if(dist < 30) {
523
- player.health -= bullet.damage || 100;
524
- if(player.health <= 0) {
525
- gameOver = true;
526
- restartBtn.style.display = 'block';
527
- effects.push(new Effect(player.x, player.y, 1000, 'death'));
528
- deathSound.cloneNode().play();
529
- }
530
- return false;
531
- }
532
- }
533
- return bullet.x >= 0 && bullet.x <= canvas.width &&
534
- bullet.y >= 0 && bullet.y <= canvas.height;
535
- });
536
 
537
- // ์•„์ดํ…œ ์ฒ˜๋ฆฌ
538
- items = items.filter(item => {
539
- const dist = Math.hypot(item.x - player.x, item.y - player.y);
540
- if(dist < 30) {
541
- player.health = Math.min(player.health + 200, player.maxHealth);
542
- return false;
543
- }
544
- return true;
545
- });
 
 
 
 
 
 
 
546
 
547
- // ๋ผ์šด๋“œ ํด๋ฆฌ์–ด ์ฒดํฌ
548
- checkRoundClear();
549
- }
 
 
 
 
550
  }
 
 
 
 
 
 
 
 
 
 
 
551
 
552
- function drawGame() {
553
- ctx.clearRect(0, 0, canvas.width, canvas.height);
554
-
555
- // ๋ฐฐ๊ฒฝ ๊ทธ๋ฆฌ๊ธฐ
556
- const pattern = ctx.createPattern(backgroundImg, 'repeat');
557
- ctx.fillStyle = pattern;
558
- ctx.fillRect(0, 0, canvas.width, canvas.height);
559
-
560
- // ํ”Œ๋ ˆ์ด์–ด ๊ทธ๋ฆฌ๊ธฐ
561
- ctx.save();
562
- ctx.translate(player.x, player.y);
563
- ctx.rotate(player.angle);
564
- ctx.drawImage(playerImg, -player.width/2, -player.height/2, player.width, player.height);
565
- ctx.restore();
566
-
567
- // ์ฒด๋ ฅ๋ฐ” ๊ทธ๋ฆฌ๊ธฐ
568
- drawHealthBar(canvas.width/2, 30, player.health, player.maxHealth, 200, 20, 'green');
 
 
 
 
 
 
 
 
 
569
 
570
- // ์  ๊ทธ๋ฆฌ๊ธฐ
571
- enemies.forEach(enemy => {
572
- ctx.save();
573
- ctx.translate(enemy.x, enemy.y);
574
- ctx.rotate(enemy.angle);
575
- const img = enemy.isBoss ? enemy.enemyImg : (enemy.enemyImg || enemyImg);
576
- ctx.drawImage(img, -enemy.width/2, -enemy.height/2, enemy.width, enemy.height);
577
- ctx.restore();
578
- drawHealthBar(enemy.x, enemy.y - 40, enemy.health, enemy.maxHealth, 60, 5, 'red');
579
- });
580
-
581
- // ์ง€์› ์œ ๋‹› ๊ทธ๋ฆฌ๊ธฐ
582
- supportUnits.forEach(unit => {
583
- ctx.save();
584
- ctx.translate(unit.x, unit.y);
585
- ctx.rotate(unit.angle);
586
- ctx.drawImage(unit.img, -unit.width/2, -unit.height/2, unit.width, unit.height);
587
- ctx.restore();
588
  });
589
 
590
- // ์ด์•Œ ๊ทธ๋ฆฌ๊ธฐ
591
- bullets.forEach(bullet => {
592
- if (bullet.isEnemy || !bullet.isAPCR) {
593
- ctx.beginPath();
594
- ctx.fillStyle = bullet.isEnemy ? 'red' : 'blue';
595
- ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2);
596
- ctx.fill();
597
- } else {
598
- ctx.save();
599
- ctx.translate(bullet.x, bullet.y);
600
- ctx.rotate(bullet.angle);
601
- const width = currentWeapon === 'machinegun' ? 10 : 20;
602
- const height = currentWeapon === 'machinegun' ? 5 : 10;
603
- ctx.drawImage(bulletImg, -width/2, -height/2, width, height);
604
- ctx.restore();
605
- }
606
- });
607
 
608
- // ์•„์ดํ…œ ๊ทธ๋ฆฌ๊ธฐ
609
- items.forEach(item => {
610
- ctx.beginPath();
611
- ctx.fillStyle = 'green';
612
- ctx.arc(item.x, item.y, 10, 0, Math.PI * 2);
613
- ctx.fill();
614
- });
 
 
 
 
 
 
 
 
 
 
 
 
615
 
616
- // UI ๊ทธ๋ฆฌ๊ธฐ
617
- ctx.fillStyle = 'white';
618
- ctx.font = '24px Arial';
619
- ctx.fillText(`Round ${currentRound}/10`, 10, 30);
620
- ctx.fillText(`Gold: ${gold}`, 10, 60);
621
-
622
- // ์ดํŽ™ํŠธ ๊ทธ๋ฆฌ๊ธฐ
623
- effects = effects.filter(effect => !effect.isExpired());
624
- effects.forEach(effect => {
625
- effect.update();
626
- ctx.save();
627
- ctx.translate(effect.x, effect.y);
628
- if (effect.type === 'fire') ctx.rotate(effect.angle);
629
- const size = effect.type === 'death' ? 75 : 42;
630
- ctx.drawImage(effect.img, -size/2, -size/2, size, size);
631
- ctx.restore();
632
- });
633
 
634
- // ์นด์šดํŠธ๋‹ค์šด ์˜ค๋ฒ„๋ ˆ์ด
635
- if (isCountingDown) {
636
- ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
637
- ctx.fillRect(0, 0, canvas.width, canvas.height);
638
  }
 
639
  }
640
 
641
- function gameLoop() {
642
- if (!gameOver && gameStarted) {
643
- updateGame();
644
- drawGame();
645
- requestAnimationFrame(gameLoop);
646
- }
 
 
 
 
 
 
 
 
647
  }
648
 
649
- // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
650
- document.addEventListener('DOMContentLoaded', () => {
651
- const titleScreen = document.getElementById('titleScreen');
652
- const instructions = document.getElementById('instructions');
653
- const weaponInfo = document.getElementById('weaponInfo');
654
- const gameCanvas = document.getElementById('gameCanvas');
655
-
656
- instructions.style.display = 'none';
657
- weaponInfo.style.display = 'none';
658
- gameCanvas.style.display = 'none';
659
-
660
- bgm.play().catch(err => console.error("Error playing title music:", err));
661
-
662
- // ๋‹ค์Œ ๋ผ์šด๋“œ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
663
- nextRoundBtn.addEventListener('click', () => {
664
- currentRound++;
665
- nextRoundBtn.style.display = 'none';
666
- document.getElementById('shop').style.display = 'none';
667
- initRound();
668
- });
669
-
670
- // ์žฌ์‹œ์ž‘ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
671
- restartBtn.addEventListener('click', () => {
672
- location.reload();
673
- });
674
 
675
- // ๋ณด์Šค ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
676
- document.getElementById('bossButton').addEventListener('click', () => {
677
- startBossStage();
678
- });
 
 
 
 
679
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
680
 
681
- // ํ‚ค๋ณด๋“œ ๋ฐ ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ
682
- const keys = {};
683
- document.addEventListener('keydown', e => {
684
- keys[e.key] = true;
685
- if(e.key.toLowerCase() === 'c') {
686
- currentWeapon = currentWeapon === 'cannon' ? 'machinegun' : 'cannon';
687
- weaponInfo.textContent = `Current Weapon: ${currentWeapon.charAt(0).toUpperCase() + currentWeapon.slice(1)}`;
688
- } else if(e.key.toLowerCase() === 'r') {
689
- autoFire = !autoFire;
690
- }
 
 
691
  });
692
 
693
- document.addEventListener('keyup', e => keys[e.key] = false);
694
-
695
- canvas.addEventListener('mousemove', (e) => {
696
- player.angle = Math.atan2(e.clientY - player.y, e.clientX - player.x);
697
- });
 
 
 
 
698
 
699
- window.addEventListener('resize', () => {
700
- canvas.width = window.innerWidth;
701
- canvas.height = window.innerHeight;
 
 
 
 
 
 
 
 
 
 
702
  });
703
 
704
- function spawnHealthItem(x, y) {
705
- items.push({x, y});
 
 
706
  }
707
 
708
- function drawHealthBar(x, y, health, maxHealth, width, height, color) {
709
- ctx.fillStyle = '#333';
710
- ctx.fillRect(x - width/2, y - height/2, width, height);
711
- ctx.fillStyle = color;
712
- ctx.fillRect(x - width/2, y - height/2, width * (health/maxHealth), height);
713
- }
714
-
715
- function fireBullet() {
716
- if(isCountingDown) return;
717
- const weapon = weapons[currentWeapon];
718
  const now = Date.now();
719
- if ((keys[' '] || autoFire) && now - lastShot > weapon.fireRate) {
720
- weapon.sound.cloneNode().play();
721
- effects.push(new Effect(
722
- player.x + Math.cos(player.angle) * 30,
723
- player.y + Math.sin(player.angle) * 30,
724
- 500,
725
- 'fire',
726
- player.angle,
727
- player
728
- ));
729
-
730
- bullets.push({
731
- x: player.x + Math.cos(player.angle) * 30,
732
- y: player.y + Math.sin(player.angle) * 30,
733
- angle: player.angle,
734
- speed: hasAPCR ? 20 : 10,
735
- isEnemy: false,
736
- damage: weapon.damage,
737
- size: weapon.bulletSize,
738
- isAPCR: hasAPCR
739
- });
740
- lastShot = now;
741
  }
742
  }
743
 
744
- function startBossStage() {
745
- isBossStage = true;
746
- enemies = [];
747
- enemies.push(new Enemy(true));
748
- player.health = player.maxHealth;
749
- bullets = [];
750
- items = [];
751
- document.getElementById('bossButton').style.display = 'none';
752
- bgm.src = 'BGM.ogg';
753
- bgm.loop = true;
754
- bgm.play();
755
- startCountdown();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
756
  }
757
 
758
- // Enemy ํด๋ž˜์Šค
759
- class Enemy {
760
- constructor(isBoss = false) {
761
- this.x = Math.random() * canvas.width;
762
- this.y = Math.random() * canvas.height;
763
- this.health = isBoss ? 15000 : 1000;
764
- this.maxHealth = this.health;
765
- this.speed = isBoss ? 1 : 2;
766
- this.lastShot = 0;
767
- this.shootInterval = isBoss ? 1000 : 1000;
768
- this.angle = 0;
769
- this.width = 100;
770
- this.height = 45;
771
- this.moveTimer = 0;
772
- this.moveInterval = Math.random() * 2000 + 1000;
773
- this.moveAngle = Math.random() * Math.PI * 2;
774
- this.isBoss = isBoss;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775
 
776
- if (isBoss) {
777
- this.enemyImg = new Image();
778
- this.enemyImg.src = 'boss.png';
779
- } else if (currentRound >= 7) {
780
- this.enemyImg = new Image();
781
- this.enemyImg.src = 'enemy3.png';
782
- } else if (currentRound >= 4) {
783
- this.enemyImg = new Image();
784
- this.enemyImg.src = 'enemy2.png';
785
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
786
  }
787
-
788
- update() {
789
- if(isCountingDown) return;
790
- const now = Date.now();
791
-
792
- if (now - this.moveTimer > this.moveInterval) {
793
- this.moveAngle = Math.random() * Math.PI * 2;
794
- this.moveTimer = now;
795
- }
796
-
797
- // ์  ์›€์ง์ž„ ์—…๋ฐ์ดํŠธ
798
- this.x += Math.cos(this.moveAngle) * this.speed;
799
- this.y += Math.sin(this.moveAngle) * this.speed;
800
-
801
- // ํ™”๋ฉด ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ
802
- this.x = Math.max(this.width/2, Math.min(canvas.width - this.width/2, this.x));
803
- this.y = Math.max(this.height/2, Math.min(canvas.height - this.height/2, this.y));
804
-
805
- // ํ”Œ๋ ˆ์ด์–ด๋ฅผ ํ–ฅํ•ด ๊ฐ๋„ ์กฐ์ •
806
- this.angle = Math.atan2(player.y - this.y, player.x - this.x);
807
-
808
- // ๋ฐœ์‚ฌ ํƒ€์ด๋ฐ ์ฒดํฌ
809
- if (now - this.lastShot > this.shootInterval && !isCountingDown) {
810
- this.shoot();
811
- this.lastShot = now;
812
- }
813
  }
 
 
 
 
814
 
815
- shoot() {
816
- const sound = this.isBoss ? new Audio('firemn.ogg') : enemyFireSound.cloneNode();
817
- sound.play();
818
-
819
- // ๋ฐœ์‚ฌ ์ดํŽ™ํŠธ ์ถ”๊ฐ€
820
- effects.push(new Effect(
821
- this.x + Math.cos(this.angle) * 30,
822
- this.y + Math.sin(this.angle) * 30,
823
- 500,
824
- 'fire',
825
- this.angle,
826
- this
827
- ));
828
 
829
- // ์ด์•Œ ์ƒ์„ฑ
830
- bullets.push({
831
- x: this.x + Math.cos(this.angle) * 30,
832
- y: this.y + Math.sin(this.angle) * 30,
833
- angle: this.angle,
834
- speed: this.isBoss ? 10 : 5,
835
- isEnemy: true,
836
- size: this.isBoss ? 5 : 3,
837
- damage: this.isBoss ? 300 : 150
838
- });
839
- }
840
- }
841
- // Effect ํด๋ž˜์Šค
842
- class Effect {
843
- constructor(x, y, duration, type, angle = 0, parent = null) {
844
- this.x = x;
845
- this.y = y;
846
- this.startTime = Date.now();
847
- this.duration = duration;
848
- this.type = type;
849
- this.angle = angle;
850
- this.parent = parent;
851
- this.offset = { x: Math.cos(angle) * 30, y: Math.sin(angle) * 30 };
852
- this.img = new Image();
853
- this.img.src = type === 'death' ? 'bang.png' : 'fire2.png';
854
- }
855
 
856
- update() {
857
- if(this.parent && this.type === 'fire') {
858
- this.x = this.parent.x + this.offset.x;
859
- this.y = this.parent.y + this.offset.y;
860
- this.angle = this.parent.angle;
861
- }
862
- }
863
 
864
- isExpired() {
865
- return Date.now() - this.startTime > this.duration;
866
- }
867
- }
868
 
869
- // SupportUnit ํด๋ž˜์Šค
870
- class SupportUnit {
871
- constructor(yPosition) {
872
- this.x = 0;
873
- this.y = yPosition;
874
- this.speed = 5;
875
- this.lastShot = 0;
876
- this.width = 100;
877
- this.height = 100;
878
- this.angle = 0;
879
- this.img = new Image();
880
- this.img.src = 'bf109.png';
881
- this.hasPlayedSound = false;
882
- this.mgSound = null;
883
- }
884
 
885
- update() {
886
- this.x += this.speed;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
887
 
888
- if (isCountingDown) {
889
- if (this.mgSound) {
890
- this.mgSound.pause();
891
- this.mgSound.currentTime = 0;
 
 
 
892
  }
893
- this.hasPlayedSound = false;
894
  }
 
 
 
 
895
 
896
- const now = Date.now();
897
- if (now - this.lastShot > 200 && !isCountingDown) {
898
- this.shoot();
899
- this.lastShot = now;
900
- }
901
- return this.x < canvas.width;
902
  }
 
 
903
 
904
- shoot() {
905
- if (!this.hasPlayedSound) {
906
- const firstSound = new Audio('bf109mg.ogg');
907
- firstSound.volume = 0.7;
908
- firstSound.play();
909
- this.hasPlayedSound = true;
 
 
 
 
 
 
 
910
  }
911
-
912
- if (!isCountingDown) {
913
- const shootSound = new Audio('bf109mgse.ogg');
914
- shootSound.volume = 0.3;
915
- shootSound.play();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
916
  }
 
 
917
 
918
- bullets.push({
919
- x: this.x + Math.cos(this.angle) * 30,
920
- y: this.y + Math.sin(this.angle) * 30,
921
- angle: this.angle,
922
- speed: 10,
923
- isEnemy: false,
924
- damage: weapons.machinegun.damage,
925
- size: weapons.machinegun.bulletSize
926
- });
927
  }
928
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
929
 
930
- // JU87 ํด๋ž˜์Šค
931
- class JU87 {
932
- constructor() {
933
- this.x = canvas.width;
934
- this.y = 50;
935
- this.speed = 5;
936
- this.width = 100;
937
- this.height = 100;
938
- this.angle = Math.PI;
939
- this.img = new Image();
940
- this.img.src = 'ju87.png';
941
- this.target = null;
942
- this.lastShot = 0;
943
- this.spawnTime = Date.now();
944
- this.hasPlayedSound = false;
945
- this.hasPlayedMGSound = false;
946
- this.isReturning = false;
947
- this.circleAngle = 0;
948
- this.returningToCenter = false;
949
- }
950
-
951
- selectTarget() {
952
- return enemies.length > 0 ?
953
- enemies[Math.floor(Math.random() * enemies.length)] : null;
954
- }
955
-
956
- update() {
957
- if (!this.hasPlayedSound) {
958
- const sirenSound = new Audio('ju87siren.ogg');
959
- sirenSound.volume = 1.0;
960
- sirenSound.play();
961
- this.hasPlayedSound = true;
962
- }
963
-
964
- const timeSinceSpawn = Date.now() - this.spawnTime;
965
- const centerX = canvas.width / 2;
966
- const centerY = canvas.height / 2;
967
-
968
- if (timeSinceSpawn > 5000) {
969
- if (!this.isReturning) {
970
- this.isReturning = true;
971
- this.target = null;
972
- this.angle = Math.atan2(centerY - this.y, centerX - this.x);
973
- } else {
974
- this.angle = Math.PI;
975
- this.x -= this.speed;
976
- return this.x > 0;
977
- }
978
- } else if (this.returningToCenter) {
979
- const distToCenter = Math.hypot(this.x - centerX, this.y - centerY);
980
- if (distToCenter > 50) {
981
- this.angle = Math.atan2(centerY - this.y, centerX - this.x);
982
- } else {
983
- this.returningToCenter = false;
984
- this.target = this.selectTarget();
985
- }
986
- } else if (this.target) {
987
- const distToTarget = Math.hypot(this.x - this.target.x, this.y - this.target.y);
988
-
989
- if (!enemies.includes(this.target) || distToTarget < 30) {
990
- this.returningToCenter = true;
991
- this.target = null;
992
- } else {
993
- this.angle = Math.atan2(this.target.y - this.y, this.target.x - this.x);
994
- }
995
- } else {
996
- this.target = this.selectTarget();
997
-
998
- if (!this.target) {
999
- this.circleAngle += 0.02;
1000
- const radius = Math.min(canvas.width, canvas.height) / 4;
1001
- const targetX = centerX + Math.cos(this.circleAngle) * radius;
1002
- const targetY = centerY + Math.sin(this.circleAngle) * radius;
1003
- this.angle = Math.atan2(targetY - this.y, targetX - this.x);
1004
- }
1005
- }
1006
 
1007
- this.x += Math.cos(this.angle) * this.speed;
1008
- this.y += Math.sin(this.angle) * this.speed;
1009
 
1010
- if (!this.returningToCenter && !this.isReturning && this.target &&
1011
- Date.now() - this.lastShot > 200) {
1012
- this.shoot();
1013
- this.lastShot = Date.now();
1014
- }
1015
 
1016
- return true;
1017
- }
 
 
 
1018
 
1019
- shoot() {
1020
- if (!this.hasPlayedMGSound && !isCountingDown) {
1021
- const mgSound = new Audio('ju87mg.ogg');
1022
- mgSound.volume = 1.0;
1023
- mgSound.play();
1024
- this.hasPlayedMGSound = true;
1025
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1026
 
1027
- [[20, 50], [80, 50]].forEach(([x, y]) => {
1028
- const offsetX = x - 50;
1029
- const offsetY = y - 50;
1030
-
1031
- const rotatedX = this.x + (Math.cos(this.angle) * offsetX - Math.sin(this.angle) * offsetY);
1032
- const rotatedY = this.y + (Math.sin(this.angle) * offsetX + Math.cos(this.angle) * offsetY);
1033
-
1034
- bullets.push({
1035
- x: rotatedX,
1036
- y: rotatedY,
1037
- angle: this.angle,
1038
- speed: 10,
1039
- isEnemy: false,
1040
- damage: weapons.machinegun.damage * 2,
1041
- size: weapons.machinegun.bulletSize
1042
- });
1043
- });
1044
- }
1045
  }
1046
- </script>
 
 
 
 
 
 
1047
  </body>
1048
  </html>
 
2
  <html>
3
  <head>
4
  <title>Tank Battle</title>
5
+ <style>
6
  body {
7
  margin: 0;
8
  overflow: hidden;
 
52
  top: 80% !important;
53
  }
54
  #restart {
55
+ top: 80% !important;
56
  }
57
  #winMessage {
58
+ top: 30% !important;
59
  font-size: 72px;
60
  background: none;
61
  }
 
137
  <h1>TANK WAR</h1>
138
  <div id="stageSelect">
139
  <button class="stageButton" onclick="startStage(1)">Stage 1</button>
140
+ <button class="stageButton" onclick="startStage(2)">Stage 2</button>
141
  <button class="stageButton" disabled>Stage 3</button>
142
  <button class="stageButton" disabled>Stage 4</button>
143
  </div>
144
  </div>
 
145
  <div id="shop" style="display:none; position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); background:rgba(0,0,0,0.9); padding:20px; border-radius:10px; color:white; z-index:1000;">
146
  <h2>Tank Shop</h2>
147
  <div style="display:flex; gap:20px;">
 
199
 
200
  // Game state
201
  let currentRound = 1;
202
+ let currentStage = 1;
203
  let gameOver = false;
204
  let currentWeapon = 'cannon';
205
  let enemies = [];
 
217
  let hasJU87 = false;
218
  let lastJU87Spawn = 0;
219
  let supportUnits = [];
220
+ let allyUnits = [];
221
  let lastSupportSpawn = 0;
222
  let gameStarted = false;
223
+ //๊ฒฝ๊ณ  ์‹œ์Šคํ…œ ๊ณ„์Šน
224
+ let warningLines = [];
225
+ let spitfires = [];
226
+ let lastSpitfireSpawn = 0;
227
 
228
  // Load assets
229
  const backgroundImg = new Image();
 
234
  enemyImg.src = 'enemy.png';
235
  const bulletImg = new Image();
236
  bulletImg.src = 'apcr2.png';
237
+ // Audio setup
 
238
  const cannonSound = new Audio('firemn.ogg');
239
  const machinegunSound = new Audio('firemg.ogg');
240
  const enemyFireSound = new Audio('fireenemy.ogg');
 
270
  maxHealth: 1000
271
  };
272
 
273
+ function startStage(stageNumber) {
274
+ console.log("Starting stage:", stageNumber);
275
+ const titleScreen = document.getElementById('titleScreen');
276
+ titleScreen.style.display = 'none';
277
+ document.getElementById('instructions').style.display = 'block';
278
+ document.getElementById('weaponInfo').style.display = 'block';
279
+ document.getElementById('gameCanvas').style.display = 'block';
280
+
281
+ // ๊ธฐ์กด BGM ์ •์ง€
282
+ bgm.pause();
283
+ bgm.currentTime = 0;
284
+
285
+ if (stageNumber === 1) {
286
+ backgroundImg.src = 'city.png';
287
+ bgm = new Audio('BGM2.ogg');
288
+ } else if (stageNumber === 2) {
289
+ backgroundImg.src = 'city2.png';
290
+ bgm = new Audio('BGM3.ogg');
291
+ enemyImg.src = 'enemyuk1.png';
292
+ }
293
+
294
+ // ๊ฒŒ์ž„ ์ƒํƒœ ์ดˆ๊ธฐํ™” ์ถ”๊ฐ€
295
+ currentRound = 1;
296
+ currentStage = stageNumber;
297
+ gameOver = false;
298
+ gold = 0;
299
+ hasAPCR = false;
300
+ hasBF109 = false;
301
+ hasJU87 = false;
302
+ allyUnits = [];
303
+ supportUnits = [];
304
+ bullets = [];
305
+ items = [];
306
+ effects = [];
307
+
308
+ bgm.loop = true;
309
+ bgm.play().catch(err => console.error("Error playing game music:", err));
310
+
311
+ gameStarted = true;
312
+ initRound();
313
+ gameLoop();
314
+ }
315
+ function startCountdown() {
316
  isCountingDown = true;
317
  countdownTime = 3;
318
  countdownEl.style.display = 'block';
 
343
 
344
  // ์  ์ƒ์„ฑ
345
  enemies = [];
346
+ // 2์Šคํ…Œ์ด์ง€์—์„œ๋Š” 3๋ช…๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ 1๋ช…์”ฉ ์ฆ๊ฐ€
347
+ const enemyCount = currentStage === 2 ? currentRound + 2 : currentRound;
348
+
349
  for(let i = 0; i < enemyCount; i++) {
350
  let x, y;
351
  const edge = Math.floor(Math.random() * 4);
 
370
  supportUnits = [];
371
  lastSupportSpawn = 0;
372
 
373
+ // 2์Šคํ…Œ์ด์ง€์—์„œ 3ํ˜ธ์ „์ฐจ ์ง€์› ์œ ๋‹› ์ถ”๊ฐ€
374
+ if (currentStage === 2 && allyUnits.length < 2) {
375
+ allyUnits.push(new PanzerIII());
376
+ }
377
+
378
  console.log(`Round ${currentRound} initialized with ${enemies.length} enemies`);
379
 
380
  // ์นด์šดํŠธ๋‹ค์šด ์‹œ์ž‘
 
388
  }, 3000);
389
  }
390
  }
391
+ function checkRoundClear() {
392
+ if(enemies.length === 0) {
393
+ console.log(`Checking round clear: Current round ${currentRound}, Boss stage: ${isBossStage}`);
394
+
395
+ if (!isBossStage) {
396
+ if(currentRound < 10) {
397
+ console.log('Normal round clear - showing next round button and shop');
398
+ nextRoundBtn.style.display = 'block';
399
+ document.getElementById('bossButton').style.display = 'none';
400
+ showShop();
401
+ } else {
402
+ console.log('Final round clear - showing boss button');
403
+ nextRoundBtn.style.display = 'none';
404
+ document.getElementById('bossButton').style.display = 'block';
405
+ document.getElementById('shop').style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
  }
407
+ } else {
408
+ console.log('Boss clear - showing victory message');
409
+ gameOver = true;
410
+ document.getElementById('winMessage').style.display = 'block';
411
+ document.getElementById('bossButton').style.display = 'none';
412
+ nextRoundBtn.style.display = 'none';
413
+ document.getElementById('shop').style.display = 'none';
414
+ restartBtn.style.display = 'block';
415
+ bgm.pause();
416
+ const victorySound = new Audio('victory.ogg');
417
+ victorySound.play();
418
  }
419
+ }
420
+ }
421
+
422
+ function showShop() {
423
+ document.getElementById('shop').style.display = 'block';
424
+ }
425
+
426
+ const defaultPlayerStats = {
427
+ maxHealth: 1000,
428
+ speed: 5,
429
+ width: 100,
430
+ height: 45
431
+ };
432
+ class JU87 {
433
+ constructor() {
434
+ this.x = canvas.width;
435
+ this.y = 50;
436
+ this.speed = 5;
437
+ this.width = 100;
438
+ this.height = 100;
439
+ this.angle = Math.PI;
440
+ this.img = new Image();
441
+ this.img.src = 'ju87.png';
442
+ this.target = null;
443
+ this.lastShot = 0;
444
+ this.spawnTime = Date.now();
445
+ this.hasPlayedSound = false;
446
+ this.hasPlayedMGSound = false;
447
+ this.isReturning = false;
448
+ this.circleAngle = 0;
449
+ this.returningToCenter = false;
450
+ }
451
 
452
+ selectTarget() {
453
+ if (enemies.length === 0) return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
 
455
+ let nearestEnemy = null;
456
+ let minDist = Infinity;
 
 
 
 
 
 
 
 
 
457
 
458
+ enemies.forEach(enemy => {
459
+ // ์Šคํ•ํŒŒ์ด์–ด๋Š” ๋ฌด์‹œ
460
+ if (enemy instanceof Spitfire) return;
461
+
462
+ const dist = Math.hypot(enemy.x - this.x, enemy.y - this.y);
463
+ if (dist < minDist) {
464
+ minDist = dist;
465
+ nearestEnemy = enemy;
466
  }
467
+ });
468
+
469
+ return nearestEnemy;
470
+ }
471
+
472
+
473
+ checkCollision() {
474
+ if (!this.target) return false;
475
+
476
+ const dist = Math.hypot(this.target.x - this.x, this.target.y - this.y);
477
+ return dist < (this.width + this.target.width) / 2;
478
+ }
479
+
480
+ shoot() {
481
+ if (!this.hasPlayedMGSound && !isCountingDown) {
482
+ const mgSound = new Audio('ju87mg.ogg');
483
+ mgSound.volume = 1.0;
484
+ mgSound.play();
485
+ this.hasPlayedMGSound = true;
486
  }
487
 
488
+ [[20, 50], [80, 50]].forEach(([x, y]) => {
489
+ const offsetX = x - 50;
490
+ const offsetY = y - 50;
491
+
492
+ const rotatedX = this.x + (Math.cos(this.angle) * offsetX - Math.sin(this.angle) * offsetY);
493
+ const rotatedY = this.y + (Math.sin(this.angle) * offsetX + Math.cos(this.angle) * offsetY);
494
+
495
+ bullets.push({
496
+ x: rotatedX,
497
+ y: rotatedY,
498
+ angle: this.angle,
499
+ speed: 10,
500
+ isEnemy: false,
501
+ damage: weapons.machinegun.damage * 2,
502
+ size: weapons.machinegun.bulletSize
503
+ });
504
+ });
505
+ }
506
+
507
+ moveToCenter() {
508
+ const centerX = canvas.width / 2;
509
+ const centerY = canvas.height / 2;
510
+ this.angle = Math.atan2(centerY - this.y, centerX - this.x);
511
+ const dist = Math.hypot(centerX - this.x, centerY - this.y);
512
+
513
+ if (dist > 10) {
514
+ this.x += Math.cos(this.angle) * this.speed;
515
+ this.y += Math.sin(this.angle) * this.speed;
516
+ return false;
517
  }
518
+ return true;
519
+ }
520
 
521
+ update() {
522
+ if (!this.hasPlayedSound) {
523
+ const sirenSound = new Audio('ju87siren.ogg');
524
+ sirenSound.volume = 1.0;
525
+ sirenSound.play();
526
+ this.hasPlayedSound = true;
527
+ }
528
+
529
+ const timeSinceSpawn = Date.now() - this.spawnTime;
530
+
531
+ // ์‹œ๊ฐ„ ์ดˆ๊ณผ์‹œ์—๋งŒ ๊ท€ํ™˜
532
+ if (timeSinceSpawn > 5000) {
533
+ if (!this.isReturning) {
534
+ this.isReturning = true;
535
+ this.target = null;
536
+ this.returningToCenter = true;
537
  }
538
+ }
539
 
540
+ // ์ถฉ๋Œ ์‹œ ์ค‘์•™์œผ๋กœ ์ด๋™ ํ›„ ์žฌ๊ณต๊ฒฉ
541
+ if (this.checkCollision()) {
542
+ this.returningToCenter = true;
543
+ this.target = null;
544
+ }
545
+
546
+ if (this.isReturning) {
547
+ if (this.returningToCenter) {
548
+ if (this.moveToCenter()) {
549
+ this.returningToCenter = false;
550
  }
551
+ } else {
552
+ this.angle = Math.PI;
553
+ this.x -= this.speed;
554
+ return this.x > 0;
555
  }
556
+ } else {
557
+ if (!this.target || !enemies.includes(this.target)) {
558
+ this.target = this.selectTarget();
559
+ if (!this.target) {
560
+ this.moveToCenter();
 
 
 
 
 
 
 
 
 
561
  }
562
+ }
563
 
564
+ if (this.target) {
565
+ this.angle = Math.atan2(this.target.y - this.y, this.target.x - this.x);
566
+ this.x += Math.cos(this.angle) * this.speed;
567
+ this.y += Math.sin(this.angle) * this.speed;
 
 
 
 
 
 
 
 
568
 
569
+ if (Date.now() - this.lastShot > 200) {
570
+ this.shoot();
571
+ this.lastShot = Date.now();
 
 
 
 
572
  }
573
+ }
574
+ }
575
 
576
+ // ํ™”๋ฉด ๊ฒฝ๊ณ„ ์ฒดํฌ
577
+ this.x = Math.max(this.width/2, Math.min(canvas.width - this.width/2, this.x));
578
+ this.y = Math.max(this.height/2, Math.min(canvas.height - this.height/2, this.y));
 
 
579
 
580
+ return true;
581
+ }
582
+ }
583
+ //2์Šคํ…Œ์ด์ง€ ์Šคํ•ํŒŒ์ด์–ด
584
+ class Spitfire {
585
+ constructor(yPosition) {
586
+ this.x = canvas.width;
587
+ this.y = yPosition;
588
+ this.speed = 5;
589
+ this.width = 100;
590
+ this.height = 100;
591
+ this.lastShot = 0;
592
+ this.img = new Image();
593
+ this.img.src = 'spitfire.png';
594
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
595
 
596
+ shoot() {
597
+ const mgSound = new Audio('firemg.ogg');
598
+ mgSound.volume = 0.5;
599
+ mgSound.play();
600
+
601
+ bullets.push({
602
+ x: this.x,
603
+ y: this.y,
604
+ angle: Math.PI,
605
+ speed: 10,
606
+ isEnemy: true,
607
+ damage: 100,
608
+ size: 2,
609
+ isSpitfireBullet: true
610
+ });
611
+ }
612
 
613
+ update() {
614
+ this.x -= this.speed;
615
+
616
+ const now = Date.now();
617
+ if (now - this.lastShot > 200) { // 1์ดˆ์— 5๋ฐœ
618
+ this.shoot();
619
+ this.lastShot = now;
620
  }
621
+
622
+ return this.x > 0;
623
+ }
624
+ }
625
+ //์Šคํ•ํŒŒ์ด์–ด ๊ฒฝ๊ณ ์‹œ์Šคํ…œ
626
+ class WarningLine {
627
+ constructor(y) {
628
+ this.y = y;
629
+ this.startTime = Date.now();
630
+ this.duration = 2000; // 2์ดˆ
631
+ }
632
 
633
+ draw(ctx) {
634
+ ctx.beginPath();
635
+ ctx.strokeStyle = 'red';
636
+ ctx.lineWidth = 5; // ์„  ๋‘๊ป˜๋ฅผ 5๋กœ ์ฆ๊ฐ€
637
+ ctx.setLineDash([10, 20]); // ์ ์„  ํŒจํ„ด๋„ ๋” ํฌ๊ฒŒ ์กฐ์ •
638
+ ctx.moveTo(0, this.y);
639
+ ctx.lineTo(canvas.width, this.y);
640
+ ctx.stroke();
641
+ ctx.setLineDash([]);
642
+ ctx.lineWidth = 1; // ๋‹ค๋ฅธ ๊ทธ๋ฆฌ๊ธฐ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋„๋ก ๋ฆฌ์…‹
643
+ }//๋ฌธ์ œ์ƒ๊ธฐ๋ฉด }+ ์ˆ˜์ •
644
+
645
+ isExpired() {
646
+ return Date.now() - this.startTime > this.duration;
647
+ }
648
+ }
649
+
650
+ function spawnSpitfires() {
651
+ if (currentStage === 2 && !isCountingDown) {
652
+ const now = Date.now();
653
+ if (now - lastSpitfireSpawn > 15000) { // 15์ดˆ๋งˆ๋‹ค
654
+ const positions = [
655
+ canvas.height * 0.2,
656
+ canvas.height * 0.5,
657
+ canvas.height * 0.8
658
+ ];
659
 
660
+ // ๊ฒฝ๊ณ ์„  ์ƒ์„ฑ
661
+ positions.forEach(y => {
662
+ warningLines.push(new WarningLine(y));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
  });
664
 
665
+ // 2์ดˆ ํ›„ ์Šคํ•ํŒŒ์ด์–ด ์ƒ์„ฑ
666
+ setTimeout(() => {
667
+ positions.forEach(y => {
668
+ spitfires.push(new Spitfire(y));
669
+ });
670
+ }, 2000);
 
 
 
 
 
 
 
 
 
 
 
671
 
672
+ lastSpitfireSpawn = now;
673
+ }
674
+ }
675
+ }
676
+
677
+ class SupportUnit {
678
+ constructor(yPosition) {
679
+ this.x = 0;
680
+ this.y = yPosition;
681
+ this.speed = 5;
682
+ this.lastShot = 0;
683
+ this.width = 100;
684
+ this.height = 100;
685
+ this.angle = 0;
686
+ this.img = new Image();
687
+ this.img.src = 'bf109.png';
688
+ this.hasPlayedSound = false;
689
+ this.mgSound = null;
690
+ }
691
 
692
+ update() {
693
+ this.x += this.speed;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
694
 
695
+ if (isCountingDown) {
696
+ if (this.mgSound) {
697
+ this.mgSound.pause();
698
+ this.mgSound.currentTime = 0;
699
  }
700
+ this.hasPlayedSound = false;
701
  }
702
 
703
+ const now = Date.now();
704
+ if (now - this.lastShot > 200 && !isCountingDown) {
705
+ this.shoot();
706
+ this.lastShot = now;
707
+ }
708
+ return this.x < canvas.width;
709
+ }
710
+
711
+ shoot() {
712
+ if (!this.hasPlayedSound) {
713
+ const firstSound = new Audio('bf109mg.ogg');
714
+ firstSound.volume = 0.7;
715
+ firstSound.play();
716
+ this.hasPlayedSound = true;
717
  }
718
 
719
+ if (!isCountingDown) {
720
+ const shootSound = new Audio('bf109mgse.ogg');
721
+ shootSound.volume = 0.3;
722
+ shootSound.play();
723
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
724
 
725
+ bullets.push({
726
+ x: this.x + Math.cos(this.angle) * 30,
727
+ y: this.y + Math.sin(this.angle) * 30,
728
+ angle: this.angle,
729
+ speed: 10,
730
+ isEnemy: false,
731
+ damage: weapons.machinegun.damage,
732
+ size: weapons.machinegun.bulletSize
733
  });
734
+ }
735
+ }
736
+ class PanzerIII {
737
+ constructor() {
738
+ this.x = Math.random() * canvas.width;
739
+ this.y = Math.random() * canvas.height;
740
+ this.speed = 2;
741
+ this.health = 500;
742
+ this.maxHealth = 500;
743
+ this.angle = 0;
744
+ this.width = 70;
745
+ this.height = 40;
746
+ this.lastShot = 0;
747
+ this.shootInterval = 2000;
748
+ this.img = new Image();
749
+ this.img.src = 'team.png';
750
+ this.isDead = false;
751
+ }
752
 
753
+ shoot() {
754
+ enemyFireSound.cloneNode().play();
755
+
756
+ bullets.push({
757
+ x: this.x + Math.cos(this.angle) * 30,
758
+ y: this.y + Math.sin(this.angle) * 30,
759
+ angle: this.angle,
760
+ speed: 5,
761
+ isEnemy: false,
762
+ damage: 50,
763
+ size: 3,
764
+ color: 'blue'
765
  });
766
 
767
+ effects.push(new Effect(
768
+ this.x + Math.cos(this.angle) * 30,
769
+ this.y + Math.sin(this.angle) * 30,
770
+ 500,
771
+ 'fire',
772
+ this.angle,
773
+ this
774
+ ));
775
+ }
776
 
777
+ update() {
778
+ if(isCountingDown || this.isDead) return;
779
+
780
+ // ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์  ์ฐพ๊ธฐ
781
+ let nearestEnemy = null;
782
+ let minDist = Infinity;
783
+
784
+ enemies.forEach(enemy => {
785
+ const dist = Math.hypot(enemy.x - this.x, enemy.y - this.y);
786
+ if(dist < minDist) {
787
+ minDist = dist;
788
+ nearestEnemy = enemy;
789
+ }
790
  });
791
 
792
+ // ์ฒด๋ ฅ ์ฒดํฌ ๋ฐ ์‚ฌ๋ง ์ฒ˜๋ฆฌ
793
+ if(this.health <= 0 && !this.isDead) {
794
+ this.die();
795
+ return;
796
  }
797
 
798
+ // ์ ์„ ํ–ฅํ•ด ์ด๋™ ๋ฐ ๋ฐœ์‚ฌ
799
+ if(nearestEnemy) {
800
+ this.angle = Math.atan2(nearestEnemy.y - this.y, nearestEnemy.x - this.x);
801
+
802
+ // ์ผ์ • ๊ฑฐ๋ฆฌ ์œ ์ง€
803
+ if(minDist > 300) {
804
+ this.x += Math.cos(this.angle) * this.speed;
805
+ this.y += Math.sin(this.angle) * this.speed;
806
+ }
807
+
808
  const now = Date.now();
809
+ if(now - this.lastShot > this.shootInterval) {
810
+ this.shoot();
811
+ this.lastShot = now;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
812
  }
813
  }
814
 
815
+ // ํ™”๋ฉด ๊ฒฝ๊ณ„ ์ฒดํฌ
816
+ this.x = Math.max(this.width/2, Math.min(canvas.width - this.width/2, this.x));
817
+ this.y = Math.max(this.height/2, Math.min(canvas.height - this.height/2, this.y));
818
+ }
819
+
820
+ die() {
821
+ this.isDead = true;
822
+
823
+ // ํญ๋ฐœ ์ดํŽ™ํŠธ ์ถ”๊ฐ€
824
+ effects.push(new Effect(
825
+ this.x,
826
+ this.y,
827
+ 1000,
828
+ 'death'
829
+ ));
830
+
831
+ // ํญ๋ฐœ ์‚ฌ์šด๋“œ ์žฌ์ƒ
832
+ const deathSound = new Audio('death.ogg');
833
+ deathSound.volume = 1.0;
834
+ deathSound.play();
835
+ }
836
+ }
837
+
838
+ function buyTank(tankImg, cost, tankId) {
839
+ if (gold >= cost) {
840
+ gold -= cost;
841
+ playerImg.src = tankImg;
842
+ document.getElementById(tankId).style.display = 'none';
843
+ document.getElementById('shop').style.display = 'none';
844
+
845
+ if (tankId === 'tank1') {
846
+ player.maxHealth = 1500;
847
+ player.speed = defaultPlayerStats.speed;
848
+ player.width = 90;
849
+ player.height = 50;
850
+ } else if (tankId === 'tank2') {
851
+ player.maxHealth = 2000;
852
+ player.speed = defaultPlayerStats.speed * 0.7;
853
+ player.width = 100;
854
+ player.height = 45;
855
  }
856
 
857
+ player.health = player.maxHealth;
858
+ }
859
+ }
860
+
861
+ function buyAPCR() {
862
+ if (gold >= 1000 && !hasAPCR) {
863
+ gold -= 1000;
864
+ hasAPCR = true;
865
+ document.getElementById('apcr').style.display = 'none';
866
+ document.getElementById('shop').style.display = 'none';
867
+ }
868
+ }
869
+
870
+ function buyBF109() {
871
+ if (gold >= 1000 && !hasBF109) {
872
+ gold -= 1000;
873
+ hasBF109 = true;
874
+ document.getElementById('bf109').style.display = 'none';
875
+ document.getElementById('shop').style.display = 'none';
876
+ }
877
+ }
878
+
879
+ function buyJU87() {
880
+ if (gold >= 1500 && !hasJU87) {
881
+ gold -= 1500;
882
+ hasJU87 = true;
883
+ document.getElementById('ju87').style.display = 'none';
884
+ document.getElementById('shop').style.display = 'none';
885
+ lastJU87Spawn = Date.now();
886
+ }
887
+ }
888
 
889
+ function updateGame() {
890
+ if(gameOver) return;
891
+ if(!isCountingDown) {
892
+ // ํ”Œ๋ ˆ์ด์–ด ์›€์ง์ž„
893
+ if(keys['w']) player.y -= player.speed;
894
+ if(keys['s']) player.y += player.speed;
895
+ if(keys['a']) player.x -= player.speed;
896
+ if(keys['d']) player.x += player.speed;
897
+
898
+ player.x = Math.max(player.width/2, Math.min(canvas.width - player.width/2, player.x));
899
+ player.y = Math.max(player.height/2, Math.min(canvas.height - player.height/2, player.y));
900
+
901
+ fireBullet();
902
+ }
903
+
904
+ // BF109 ๊ด€๋ จ ์ฝ”๋“œ
905
+ if (hasBF109 && !isCountingDown) {
906
+ const now = Date.now();
907
+ if (now - lastSupportSpawn > 10000) { // 10์ดˆ๋งˆ๋‹ค
908
+ supportUnits.push(
909
+ new SupportUnit(canvas.height * 0.2),
910
+ new SupportUnit(canvas.height * 0.5),
911
+ new SupportUnit(canvas.height * 0.8)
912
+ );
913
+ lastSupportSpawn = now;
914
  }
915
+ }
916
+
917
+ // JU87 ๊ด€๋ จ ์ฝ”๋“œ
918
+ if (hasJU87 && !isCountingDown) {
919
+ const now = Date.now();
920
+ if (now - lastJU87Spawn > 15000) { // 15์ดˆ๋งˆ๋‹ค
921
+ supportUnits.push(new JU87());
922
+ lastJU87Spawn = now;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
923
  }
924
+ }
925
+
926
+ // ์Šคํ•ํŒŒ์ด์–ด ์Šคํฐ ๋ฐ ์—…๋ฐ์ดํŠธ ๋กœ์ง ์ถ”๊ฐ€
927
+ spawnSpitfires();
928
 
929
+ // ์Šคํ•ํŒŒ์ด์–ด ์—…๋ฐ์ดํŠธ
930
+ spitfires = spitfires.filter(spitfire => spitfire.update());
 
 
 
 
 
 
 
 
 
 
 
931
 
932
+ // ๊ฒฝ๊ณ ์„  ์—…๋ฐ์ดํŠธ
933
+ warningLines = warningLines.filter(line => !line.isExpired());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
934
 
935
+ // ์ง€์› ์œ ๋‹› ์—…๋ฐ์ดํŠธ
936
+ supportUnits = supportUnits.filter(unit => unit.update());
 
 
 
 
 
937
 
938
+ // ์•„๊ตฐ 3ํ˜ธ์ „์ฐจ ์—…๋ฐ์ดํŠธ
939
+ allyUnits.forEach(unit => unit.update());
940
+ allyUnits = allyUnits.filter(unit => unit.health > 0);
 
941
 
942
+ // ์  ์—…๋ฐ์ดํŠธ
943
+ enemies.forEach(enemy => enemy.update());
 
 
 
 
 
 
 
 
 
 
 
 
 
944
 
945
+ if(!isCountingDown) {
946
+ // ์ด์•Œ ์ฒ˜๋ฆฌ
947
+ bullets = bullets.filter(bullet => {
948
+ bullet.x += Math.cos(bullet.angle) * bullet.speed;
949
+ bullet.y += Math.sin(bullet.angle) * bullet.speed;
950
+
951
+ if(!bullet.isEnemy) {
952
+ enemies = enemies.filter(enemy => {
953
+ const dist = Math.hypot(bullet.x - enemy.x, bullet.y - enemy.y);
954
+ if(dist < 30) {
955
+ let damage = currentWeapon === 'cannon' ? 250 : 50;
956
+ enemy.health -= damage;
957
+ if(enemy.health <= 0) {
958
+ spawnHealthItem(enemy.x, enemy.y);
959
+ gold += 100;
960
+ effects.push(new Effect(enemy.x, enemy.y, 1000, 'death'));
961
+ deathSound.cloneNode().play();
962
+ return false;
963
+ }
964
+ return true;
965
+ }
966
+ return true;
967
+ });
968
+ } else {
969
+ // ์ƒ์ ์ด ์—ด๋ ค์žˆ์ง€ ์•Š์„ ๋•Œ๋งŒ ํ”Œ๋ ˆ์ด์–ด ๋ฐ๋ฏธ์ง€ ์ฒ˜๋ฆฌ
970
+ if (!document.getElementById('shop').style.display ||
971
+ document.getElementById('shop').style.display === 'none') {
972
+ const distToPlayer = Math.hypot(bullet.x - player.x, bullet.y - player.y);
973
+ if(distToPlayer < 30) {
974
+ player.health -= bullet.damage || 100;
975
+ if(player.health <= 0) {
976
+ gameOver = true;
977
+ restartBtn.style.display = 'block';
978
+ effects.push(new Effect(player.x, player.y, 1000, 'death'));
979
+ deathSound.cloneNode().play();
980
+ }
981
+ return false;
982
+ }
983
 
984
+ // ์•„๊ตฐ 3ํ˜ธ์ „์ฐจ ํ”ผ๊ฒฉ ์ฒดํฌ
985
+ for(let ally of allyUnits) {
986
+ const distToAlly = Math.hypot(bullet.x - ally.x, bullet.y - ally.y);
987
+ if(distToAlly < 30) {
988
+ ally.health -= bullet.damage || 100;
989
+ return false;
990
+ }
991
  }
 
992
  }
993
+ }
994
+ return bullet.x >= 0 && bullet.x <= canvas.width &&
995
+ bullet.y >= 0 && bullet.y <= canvas.height;
996
+ });
997
 
998
+ // ์•„์ดํ…œ ์ฒ˜๋ฆฌ
999
+ items = items.filter(item => {
1000
+ const dist = Math.hypot(item.x - player.x, item.y - player.y);
1001
+ if(dist < 30) {
1002
+ player.health = Math.min(player.health + 200, player.maxHealth);
1003
+ return false;
1004
  }
1005
+ return true;
1006
+ });
1007
 
1008
+ // ์•„๊ตฐ ์ „์ฐจ์™€ ์  ์ „์ฐจ ์ถฉ๋Œ ์ฒดํฌ
1009
+ allyUnits.forEach(ally => {
1010
+ enemies.forEach(enemy => {
1011
+ const dist = Math.hypot(ally.x - enemy.x, ally.y - enemy.y);
1012
+ if (dist < (ally.width + enemy.width) / 2) {
1013
+ // ์ถฉ๋Œ ์‹œ ์„œ๋กœ ๋ฐ€์–ด๋‚ด๊ธฐ
1014
+ const angle = Math.atan2(ally.y - enemy.y, ally.x - enemy.x);
1015
+ const pushDistance = ((ally.width + enemy.width) / 2 - dist) / 2;
1016
+
1017
+ ally.x += Math.cos(angle) * pushDistance;
1018
+ ally.y += Math.sin(angle) * pushDistance;
1019
+ enemy.x -= Math.cos(angle) * pushDistance;
1020
+ enemy.y -= Math.sin(angle) * pushDistance;
1021
  }
1022
+ });
1023
+ });
1024
+
1025
+ // ๋ผ์šด๋“œ ํด๋ฆฌ์–ด ์ฒดํฌ
1026
+ checkRoundClear();
1027
+ }
1028
+ }
1029
+ function drawGame() {
1030
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1031
+
1032
+ // ๋ฐฐ๊ฒฝ ๊ทธ๋ฆฌ๊ธฐ
1033
+ const pattern = ctx.createPattern(backgroundImg, 'repeat');
1034
+ ctx.fillStyle = pattern;
1035
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
1036
+
1037
+ // ํ”Œ๋ ˆ์ด์–ด ๊ทธ๋ฆฌ๊ธฐ
1038
+ ctx.save();
1039
+ ctx.translate(player.x, player.y);
1040
+ ctx.rotate(player.angle);
1041
+ ctx.drawImage(playerImg, -player.width/2, -player.height/2, player.width, player.height);
1042
+ ctx.restore();
1043
+
1044
+ // ์ฒด๋ ฅ๋ฐ” ๊ทธ๋ฆฌ๊ธฐ
1045
+ drawHealthBar(canvas.width/2, 30, player.health, player.maxHealth, 200, 20, 'green');
1046
+
1047
+ // ์  ๊ทธ๋ฆฌ๊ธฐ
1048
+ enemies.forEach(enemy => {
1049
+ ctx.save();
1050
+ ctx.translate(enemy.x, enemy.y);
1051
+ ctx.rotate(enemy.angle);
1052
+ //const img = enemy.isBoss ? (currentStage === 2 ? 'enemyukboss.png' : 'boss.png') : enemy.enemyImg;
1053
+ // ์ด ๋ถ€๋ถ„์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •
1054
+ const img = enemy.enemyImg; // ์ด๋ฏธ์ง€ ๊ฐ์ฒด ์ง์ ‘ ์‚ฌ์šฉ
1055
+ ctx.drawImage(img, -enemy.width/2, -enemy.height/2, enemy.width, enemy.height);
1056
+ ctx.restore();
1057
+ drawHealthBar(enemy.x, enemy.y - 40, enemy.health, enemy.maxHealth, 60, 5, 'red');
1058
+ });
1059
+
1060
+ // ์•„๊ตฐ 3ํ˜ธ์ „์ฐจ ๊ทธ๋ฆฌ๊ธฐ
1061
+ allyUnits.forEach(ally => {
1062
+ ctx.save();
1063
+ ctx.translate(ally.x, ally.y);
1064
+ ctx.rotate(ally.angle);
1065
+ ctx.drawImage(ally.img, -ally.width/2, -ally.height/2, ally.width, ally.height);
1066
+ ctx.restore();
1067
+ drawHealthBar(ally.x, ally.y - 40, ally.health, ally.maxHealth, 60, 5, 'blue');
1068
+ });
1069
+ // ๊ฒฝ๊ณ ์„  ๊ทธ๋ฆฌ๊ธฐ
1070
+ warningLines.forEach(line => line.draw(ctx));
1071
+
1072
+ // ์Šคํ•ํŒŒ์ด์–ด ๊ทธ๋ฆฌ๊ธฐ
1073
+ spitfires.forEach(spitfire => {
1074
+ ctx.save();
1075
+ ctx.translate(spitfire.x, spitfire.y);
1076
+ ctx.rotate(Math.PI); // ์™ผ์ชฝ์œผ๋กœ ๋น„ํ–‰ํ•˜๋ฏ€๋กœ 180๋„ ํšŒ์ „
1077
+ ctx.drawImage(spitfire.img, -spitfire.width/2, -spitfire.height/2, spitfire.width, spitfire.height);
1078
+ ctx.restore();
1079
+ });
1080
+ // ์ง€์› ์œ ๋‹› ๊ทธ๋ฆฌ๊ธฐ
1081
+ supportUnits.forEach(unit => {
1082
+ ctx.save();
1083
+ ctx.translate(unit.x, unit.y);
1084
+ ctx.rotate(unit.angle);
1085
+ ctx.drawImage(unit.img, -unit.width/2, -unit.height/2, unit.width, unit.height);
1086
+ ctx.restore();
1087
+ });
1088
+
1089
+ // ์ด์•Œ ๊ทธ๋ฆฌ๊ธฐ
1090
+ bullets.forEach(bullet => {
1091
+ if (bullet.isEnemy || !bullet.isAPCR) {
1092
+ ctx.beginPath();
1093
+ ctx.fillStyle = bullet.color || (bullet.isEnemy ? 'red' : 'blue');
1094
+ ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2);
1095
+ ctx.fill();
1096
+ } else {
1097
+ ctx.save();
1098
+ ctx.translate(bullet.x, bullet.y);
1099
+ ctx.rotate(bullet.angle);
1100
+ const width = currentWeapon === 'machinegun' ? 10 : 20;
1101
+ const height = currentWeapon === 'machinegun' ? 5 : 10;
1102
+ ctx.drawImage(bulletImg, -width/2, -height/2, width, height);
1103
+ ctx.restore();
1104
+ }
1105
+ });
1106
+
1107
+ // ์•„์ดํ…œ ๊ทธ๋ฆฌ๊ธฐ
1108
+ items.forEach(item => {
1109
+ ctx.beginPath();
1110
+ ctx.fillStyle = 'green';
1111
+ ctx.arc(item.x, item.y, 10, 0, Math.PI * 2);
1112
+ ctx.fill();
1113
+ });
1114
+
1115
+ // UI ๊ทธ๋ฆฌ๊ธฐ
1116
+ ctx.fillStyle = 'white';
1117
+ ctx.font = '24px Arial';
1118
+ ctx.fillText(`Stage ${currentStage} - Round ${currentRound}/10`, 10, 30);
1119
+ ctx.fillText(`Gold: ${gold}`, 10, 60);
1120
+
1121
+ // ์ดํŽ™ํŠธ ๊ทธ๋ฆฌ๊ธฐ
1122
+ effects = effects.filter(effect => !effect.isExpired());
1123
+ effects.forEach(effect => {
1124
+ effect.update();
1125
+ ctx.save();
1126
+ ctx.translate(effect.x, effect.y);
1127
+ if (effect.type === 'fire') ctx.rotate(effect.angle);
1128
+ const size = effect.type === 'death' ? 75 : 42;
1129
+ ctx.drawImage(effect.img, -size/2, -size/2, size, size);
1130
+ ctx.restore();
1131
+ });
1132
+
1133
+ // ์นด์šดํŠธ๋‹ค์šด ์˜ค๋ฒ„๋ ˆ์ด
1134
+ if (isCountingDown) {
1135
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
1136
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
1137
+ }
1138
+ }
1139
+ //bf109 ํžˆํŠธ ์‹œ์Šคํ…œ
1140
+ function updateBF109Damage() {
1141
+ supportUnits = supportUnits.filter(unit => {
1142
+ if (unit instanceof SupportUnit) { // BF109์ธ ๊ฒฝ์šฐ
1143
+ let hitCount = 0;
1144
+ bullets = bullets.filter(bullet => {
1145
+ if (bullet.isSpitfireBullet) {
1146
+ const dist = Math.hypot(bullet.x - unit.x, bullet.y - unit.y);
1147
+ if (dist < 30) {
1148
+ hitCount++;
1149
+ return false;
1150
+ }
1151
  }
1152
+ return true;
1153
+ });
1154
 
1155
+ if (hitCount >= 5) {
1156
+ effects.push(new Effect(unit.x, unit.y, 1000, 'death'));
1157
+ deathSound.cloneNode().play();
1158
+ return false;
 
 
 
 
 
1159
  }
1160
  }
1161
+ return true;
1162
+ });
1163
+ }
1164
+ function gameLoop() {
1165
+ if (!gameOver && gameStarted) {
1166
+ updateGame();
1167
+ drawGame();
1168
+ requestAnimationFrame(gameLoop);
1169
+ }
1170
+ }
1171
+ class Enemy {
1172
+ constructor(isBoss = false) {
1173
+ this.x = Math.random() * canvas.width;
1174
+ this.y = Math.random() * canvas.height;
1175
+ this.health = currentStage === 2 ? (isBoss ? 15000 : 1500) : (isBoss ? 15000 : 1000);
1176
+ this.maxHealth = this.health;
1177
+ this.speed = isBoss ? 1 : 2;
1178
+ this.lastShot = 0;
1179
+ this.shootInterval = isBoss ? 1000 : 1000;
1180
+ this.angle = 0;
1181
+ this.width = 100;
1182
+ this.height = 45;
1183
+ this.moveTimer = 0;
1184
+ this.moveInterval = Math.random() * 2000 + 1000;
1185
+ this.moveAngle = Math.random() * Math.PI * 2;
1186
+ this.isBoss = isBoss;
1187
+
1188
+ if (currentStage === 2) {
1189
+ if (isBoss) {
1190
+ this.enemyImg = new Image();
1191
+ this.enemyImg.src = 'enemyukboss.png';
1192
+ } else if (currentRound >= 7) {
1193
+ this.enemyImg = new Image();
1194
+ this.enemyImg.src = 'enemyuk3.png';
1195
+ } else if (currentRound >= 4) {
1196
+ this.enemyImg = new Image();
1197
+ this.enemyImg.src = 'enemyuk2.png';
1198
+ } else {
1199
+ this.enemyImg = new Image();
1200
+ this.enemyImg.src = 'enemyuk1.png';
1201
+ }
1202
+ } else {
1203
+ if (isBoss) {
1204
+ this.enemyImg = new Image();
1205
+ this.enemyImg.src = 'boss.png';
1206
+ } else if (currentRound >= 7) {
1207
+ this.enemyImg = new Image();
1208
+ this.enemyImg.src = 'enemy3.png';
1209
+ } else if (currentRound >= 4) {
1210
+ this.enemyImg = new Image();
1211
+ this.enemyImg.src = 'enemy2.png';
1212
+ } else {
1213
+ this.enemyImg = new Image();
1214
+ this.enemyImg.src = 'enemy.png'; // ๊ธฐ๋ณธ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
1215
+ }
1216
+ }
1217
+ }
1218
+
1219
+ update() {
1220
+ if(isCountingDown) return;
1221
+ const now = Date.now();
1222
+
1223
+ if (now - this.moveTimer > this.moveInterval) {
1224
+ this.moveAngle = Math.random() * Math.PI * 2;
1225
+ this.moveTimer = now;
1226
+ }
1227
 
1228
+ this.x += Math.cos(this.moveAngle) * this.speed;
1229
+ this.y += Math.sin(this.moveAngle) * this.speed;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1230
 
1231
+ this.x = Math.max(this.width/2, Math.min(canvas.width - this.width/2, this.x));
1232
+ this.y = Math.max(this.height/2, Math.min(canvas.height - this.height/2, this.y));
1233
 
1234
+ this.angle = Math.atan2(player.y - this.y, player.x - this.x);
 
 
 
 
1235
 
1236
+ if (now - this.lastShot > this.shootInterval && !isCountingDown) {
1237
+ this.shoot();
1238
+ this.lastShot = now;
1239
+ }
1240
+ }
1241
 
1242
+ shoot() {
1243
+ const sound = this.isBoss ? new Audio('firemn.ogg') : enemyFireSound.cloneNode();
1244
+ sound.play();
1245
+
1246
+ effects.push(new Effect(
1247
+ this.x + Math.cos(this.angle) * 30,
1248
+ this.y + Math.sin(this.angle) * 30,
1249
+ 500,
1250
+ 'fire',
1251
+ this.angle,
1252
+ this
1253
+ ));
1254
+
1255
+ bullets.push({
1256
+ x: this.x + Math.cos(this.angle) * 30,
1257
+ y: this.y + Math.sin(this.angle) * 30,
1258
+ angle: this.angle,
1259
+ speed: this.isBoss ? 10 : 5,
1260
+ isEnemy: true,
1261
+ size: this.isBoss ? 5 : 3,
1262
+ damage: this.isBoss ? 300 : 150
1263
+ });
1264
+ }
1265
+ }
1266
+
1267
+ // ๋ณด์Šค ์Šคํ…Œ์ด์ง€ ์‹œ์ž‘ ํ•จ์ˆ˜ ์ˆ˜์ •
1268
+ function startBossStage() {
1269
+ isBossStage = true;
1270
+ enemies = [];
1271
+ enemies.push(new Enemy(true));
1272
+ player.health = player.maxHealth;
1273
+ bullets = [];
1274
+ items = [];
1275
+ allyUnits = []; // 3ํ˜ธ์ „์ฐจ ์ดˆ๊ธฐํ™”
1276
+ if (currentStage === 2 && allyUnits.length < 2) {
1277
+ allyUnits.push(new PanzerIII());
1278
+ }
1279
+ document.getElementById('bossButton').style.display = 'none';
1280
+ bgm.src = 'BGM.ogg';
1281
+ bgm.loop = true;
1282
+ bgm.play();
1283
+ startCountdown();
1284
+ }
1285
+ // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
1286
+ document.addEventListener('DOMContentLoaded', () => {
1287
+ const titleScreen = document.getElementById('titleScreen');
1288
+ const instructions = document.getElementById('instructions');
1289
+ const weaponInfo = document.getElementById('weaponInfo');
1290
+ const gameCanvas = document.getElementById('gameCanvas');
1291
+
1292
+ instructions.style.display = 'none';
1293
+ weaponInfo.style.display = 'none';
1294
+ gameCanvas.style.display = 'none';
1295
+
1296
+ bgm.play().catch(err => console.error("Error playing title music:", err));
1297
+
1298
+ // ๋‹ค์Œ ๋ผ์šด๋“œ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
1299
+ nextRoundBtn.addEventListener('click', () => {
1300
+ currentRound++;
1301
+ nextRoundBtn.style.display = 'none';
1302
+ document.getElementById('shop').style.display = 'none';
1303
+ initRound();
1304
+ });
1305
+
1306
+ // ์žฌ์‹œ์ž‘ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
1307
+ restartBtn.addEventListener('click', () => {
1308
+ location.reload();
1309
+ });
1310
+
1311
+ // ๋ณด์Šค ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
1312
+ document.getElementById('bossButton').addEventListener('click', () => {
1313
+ startBossStage();
1314
+ });
1315
+ });
1316
+
1317
+ // ํ‚ค๋ณด๋“œ ๋ฐ ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ
1318
+ const keys = {};
1319
+ document.addEventListener('keydown', e => {
1320
+ keys[e.key] = true;
1321
+ if(e.key.toLowerCase() === 'c') {
1322
+ currentWeapon = currentWeapon === 'cannon' ? 'machinegun' : 'cannon';
1323
+ weaponInfo.textContent = `Current Weapon: ${currentWeapon.charAt(0).toUpperCase() + currentWeapon.slice(1)}`;
1324
+ } else if(e.key.toLowerCase() === 'r') {
1325
+ autoFire = !autoFire;
1326
+ }
1327
+ });
1328
+
1329
+ document.addEventListener('keyup', e => keys[e.key] = false);
1330
+
1331
+ canvas.addEventListener('mousemove', (e) => {
1332
+ player.angle = Math.atan2(e.clientY - player.y, e.clientX - player.x);
1333
+ });
1334
+
1335
+ window.addEventListener('resize', () => {
1336
+ canvas.width = window.innerWidth;
1337
+ canvas.height = window.innerHeight;
1338
+ });
1339
+
1340
+ function spawnHealthItem(x, y) {
1341
+ items.push({x, y});
1342
+ }
1343
+
1344
+ function drawHealthBar(x, y, health, maxHealth, width, height, color) {
1345
+ ctx.fillStyle = '#333';
1346
+ ctx.fillRect(x - width/2, y - height/2, width, height);
1347
+ ctx.fillStyle = color;
1348
+ ctx.fillRect(x - width/2, y - height/2, width * (health/maxHealth), height);
1349
+ }
1350
+
1351
+ function fireBullet() {
1352
+ if(isCountingDown) return;
1353
+ const weapon = weapons[currentWeapon];
1354
+ const now = Date.now();
1355
+ if ((keys[' '] || autoFire) && now - lastShot > weapon.fireRate) {
1356
+ weapon.sound.cloneNode().play();
1357
+ effects.push(new Effect(
1358
+ player.x + Math.cos(player.angle) * 30,
1359
+ player.y + Math.sin(player.angle) * 30,
1360
+ 500,
1361
+ 'fire',
1362
+ player.angle,
1363
+ player
1364
+ ));
1365
+
1366
+ bullets.push({
1367
+ x: player.x + Math.cos(player.angle) * 30,
1368
+ y: player.y + Math.sin(player.angle) * 30,
1369
+ angle: player.angle,
1370
+ speed: hasAPCR ? 20 : 10,
1371
+ isEnemy: false,
1372
+ damage: weapon.damage,
1373
+ size: weapon.bulletSize,
1374
+ isAPCR: hasAPCR
1375
+ });
1376
+ lastShot = now;
1377
+ }
1378
+ }
1379
+
1380
+ // Effect ํด๋ž˜์Šค
1381
+ class Effect {
1382
+ constructor(x, y, duration, type, angle = 0, parent = null) {
1383
+ this.x = x;
1384
+ this.y = y;
1385
+ this.startTime = Date.now();
1386
+ this.duration = duration;
1387
+ this.type = type;
1388
+ this.angle = angle;
1389
+ this.parent = parent;
1390
+ this.offset = { x: Math.cos(angle) * 30, y: Math.sin(angle) * 30 };
1391
+ this.img = new Image();
1392
+ this.img.src = type === 'death' ? 'bang.png' : 'fire2.png';
1393
+ }
1394
 
1395
+ update() {
1396
+ if(this.parent && this.type === 'fire') {
1397
+ this.x = this.parent.x + this.offset.x;
1398
+ this.y = this.parent.y + this.offset.y;
1399
+ this.angle = this.parent.angle;
 
 
 
 
 
 
 
 
 
 
 
 
 
1400
  }
1401
+ }
1402
+
1403
+ isExpired() {
1404
+ return Date.now() - this.startTime > this.duration;
1405
+ }
1406
+ }
1407
+ </script>
1408
  </body>
1409
  </html>