cutechicken commited on
Commit
92214c4
ยท
verified ยท
1 Parent(s): 8792afc

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +415 -15
game.js CHANGED
@@ -20,6 +20,7 @@ const GAME_CONSTANTS = {
20
  MISSILE_COUNT: 6,
21
  AMMO_COUNT: 940, // 940๋ฐœ๋กœ ๋ณ€๊ฒฝ
22
  BULLET_DAMAGE: 25, // ๋ฐœ๋‹น 25 ๋ฐ๋ฏธ์ง€
 
23
  MAX_HEALTH: 1000 // ์ฒด๋ ฅ 1000
24
  };
25
 
@@ -60,10 +61,19 @@ class Fighter {
60
  this.missiles = GAME_CONSTANTS.MISSILE_COUNT;
61
  this.ammo = GAME_CONSTANTS.AMMO_COUNT;
62
  this.bullets = [];
 
63
  this.lastShootTime = 0;
64
  this.isMouseDown = false; // ๋งˆ์šฐ์Šค ๋ˆ„๋ฆ„ ์ƒํƒœ ์ถ”์ 
65
  this.gunfireAudios = []; // ๊ธฐ๊ด€์ด ์†Œ๋ฆฌ ๋ฐฐ์—ด (์ตœ๋Œ€ 5๊ฐœ)
66
 
 
 
 
 
 
 
 
 
67
  // ์Šคํ†จ ํƒˆ์ถœ์„ ์œ„ํ•œ Fํ‚ค ์ƒํƒœ
68
  this.escapeKeyPressed = false;
69
  this.stallEscapeProgress = 0; // ์Šคํ†จ ํƒˆ์ถœ ์ง„ํ–‰๋„ (0~2์ดˆ)
@@ -642,6 +652,7 @@ class Fighter {
642
  }
643
 
644
  updateBullets(scene, deltaTime) {
 
645
  for (let i = this.bullets.length - 1; i >= 0; i--) {
646
  const bullet = this.bullets[i];
647
  bullet.position.add(bullet.velocity.clone().multiplyScalar(deltaTime));
@@ -659,6 +670,204 @@ class Fighter {
659
  this.bullets.splice(i, 1);
660
  }
661
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
  }
663
 
664
  takeDamage(damage) {
@@ -1087,7 +1296,7 @@ class Game {
1087
  this.bgm = null;
1088
  this.bgmPlaying = false;
1089
 
1090
- this.keys = { w: false, a: false, s: false, d: false, f: false };
1091
  this.isStarted = false;
1092
 
1093
  this.setupScene();
@@ -1328,6 +1537,12 @@ class Game {
1328
  case 'KeyS': this.keys.s = true; break;
1329
  case 'KeyD': this.keys.d = true; break;
1330
  case 'KeyF': this.keys.f = true; break;
 
 
 
 
 
 
1331
  }
1332
  });
1333
 
@@ -1340,6 +1555,7 @@ class Game {
1340
  case 'KeyS': this.keys.s = false; break;
1341
  case 'KeyD': this.keys.d = false; break;
1342
  case 'KeyF': this.keys.f = false; break;
 
1343
  }
1344
  });
1345
 
@@ -1357,8 +1573,15 @@ class Game {
1357
  if (!document.pointerLockElement || this.isGameOver || !gameStarted) return;
1358
 
1359
  if (event.button === 0) { // ์™ผ์ชฝ ๋งˆ์šฐ์Šค ๋ฒ„ํŠผ
1360
- this.fighter.isMouseDown = true;
1361
- this.lastShootTime = 0; // ์ฆ‰์‹œ ๋ฐœ์‚ฌ
 
 
 
 
 
 
 
1362
  }
1363
  });
1364
 
@@ -1375,19 +1598,24 @@ class Game {
1375
  });
1376
  }
1377
 
1378
- startGame() {
1379
- if (!this.isLoaded) {
1380
- console.log('๊ฒŒ์ž„์ด ์•„์ง ๋กœ๋”ฉ ์ค‘์ž…๋‹ˆ๋‹ค...');
1381
- return;
 
1382
  }
1383
 
1384
- this.isStarted = true;
1385
- this.startGameTimer();
 
 
1386
 
1387
- // ์—”์ง„ ์†Œ๋ฆฌ ์‹œ์ž‘
1388
- this.fighter.startEngineSound();
1389
-
1390
- console.log('๊ฒŒ์ž„ ์‹œ์ž‘!');
 
 
1391
  }
1392
 
1393
  startBGM() {
@@ -1460,7 +1688,47 @@ class Game {
1460
 
1461
  if (scoreElement) scoreElement.textContent = `Score: ${this.score}`;
1462
  if (timeElement) timeElement.textContent = `Time: ${this.gameTime}s`;
1463
- if (ammoElement) ammoElement.textContent = `AMMO: ${this.fighter.ammo}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1464
 
1465
  if (gameStatsElement) {
1466
  gameStatsElement.innerHTML = `
@@ -1895,6 +2163,32 @@ class Game {
1895
  }
1896
  }
1897
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1898
  // ์  ํƒ„ํ™˜ vs ํ”Œ๋ ˆ์ด์–ด ์ถฉ๋Œ
1899
  this.enemies.forEach(enemy => {
1900
  enemy.bullets.forEach((bullet, index) => {
@@ -1913,6 +2207,107 @@ class Game {
1913
  });
1914
  });
1915
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1916
 
1917
  createHitEffect(position) {
1918
  // ํ”ผ๊ฒฉ ํŒŒํ‹ฐํด ํšจ๊ณผ ์ƒ์„ฑ
@@ -2076,7 +2471,7 @@ class Game {
2076
  this.fighter.updateBullets(this.scene, deltaTime);
2077
 
2078
  // ๋งˆ์šฐ์Šค ๋ˆ„๋ฆ„ ์ƒํƒœ์ผ ๋•Œ ์—ฐ์† ๋ฐœ์‚ฌ
2079
- if (this.fighter.isMouseDown && this.isStarted) {
2080
  const currentShootTime = Date.now();
2081
  if (!this.lastShootTime || currentShootTime - this.lastShootTime >= 100) { // 0.1์ดˆ๋งˆ๋‹ค
2082
  this.fighter.shoot(this.scene);
@@ -2084,6 +2479,11 @@ class Game {
2084
  }
2085
  }
2086
 
 
 
 
 
 
2087
  if (this.isStarted) {
2088
  // ์ ๊ธฐ๋“ค์—๊ฒŒ ์„œ๋กœ์˜ ์ฐธ์กฐ ์ „๋‹ฌ (์ถฉ๋Œ ํšŒํ”ผ์šฉ)
2089
  this.enemies.forEach(enemy => {
 
20
  MISSILE_COUNT: 6,
21
  AMMO_COUNT: 940, // 940๋ฐœ๋กœ ๋ณ€๊ฒฝ
22
  BULLET_DAMAGE: 25, // ๋ฐœ๋‹น 25 ๋ฐ๋ฏธ์ง€
23
+ MISSILE_DAMAGE: 500, // ๋ฏธ์‚ฌ์ผ ๋ฐ๋ฏธ์ง€
24
  MAX_HEALTH: 1000 // ์ฒด๋ ฅ 1000
25
  };
26
 
 
61
  this.missiles = GAME_CONSTANTS.MISSILE_COUNT;
62
  this.ammo = GAME_CONSTANTS.AMMO_COUNT;
63
  this.bullets = [];
64
+ this.activeMissiles = []; // ๋ฐœ์‚ฌ๋œ ๋ฏธ์‚ฌ์ผ
65
  this.lastShootTime = 0;
66
  this.isMouseDown = false; // ๋งˆ์šฐ์Šค ๋ˆ„๋ฆ„ ์ƒํƒœ ์ถ”์ 
67
  this.gunfireAudios = []; // ๊ธฐ๊ด€์ด ์†Œ๋ฆฌ ๋ฐฐ์—ด (์ตœ๋Œ€ 5๊ฐœ)
68
 
69
+ // ๋ฌด๊ธฐ ์‹œ์Šคํ…œ
70
+ this.currentWeapon = 'mg'; // 'mg' or 'missile'
71
+ this.weaponCooldown = 0; // ๋ฌด๊ธฐ ์ฟจ๋‹ค์šด
72
+ this.missileTarget = null; // ๋ฏธ์‚ฌ์ผ ํƒ€๊ฒŸ
73
+ this.missileLockTimer = 0; // ๋ฝ์˜จ ํƒ€์ด๋จธ
74
+ this.missileLockCount = 0; // ๋ฝ์˜จ ์นด์šดํŠธ (3๋ฒˆ ํ•„์š”)
75
+ this.lastMissileFire = 0; // ๋งˆ์ง€๋ง‰ ๋ฏธ์‚ฌ์ผ ๋ฐœ์‚ฌ ์‹œ๊ฐ„
76
+
77
  // ์Šคํ†จ ํƒˆ์ถœ์„ ์œ„ํ•œ Fํ‚ค ์ƒํƒœ
78
  this.escapeKeyPressed = false;
79
  this.stallEscapeProgress = 0; // ์Šคํ†จ ํƒˆ์ถœ ์ง„ํ–‰๋„ (0~2์ดˆ)
 
652
  }
653
 
654
  updateBullets(scene, deltaTime) {
655
+ // ๊ธฐ๊ด€์ด ํƒ„ํ™˜ ์—…๋ฐ์ดํŠธ
656
  for (let i = this.bullets.length - 1; i >= 0; i--) {
657
  const bullet = this.bullets[i];
658
  bullet.position.add(bullet.velocity.clone().multiplyScalar(deltaTime));
 
670
  this.bullets.splice(i, 1);
671
  }
672
  }
673
+
674
+ // ๋ฏธ์‚ฌ์ผ ์—…๋ฐ์ดํŠธ
675
+ for (let i = this.activeMissiles.length - 1; i >= 0; i--) {
676
+ const missile = this.activeMissiles[i];
677
+
678
+ if (missile.target && missile.target.mesh && missile.target.isLoaded) {
679
+ // ํƒ€๊ฒŸ์„ ํ–ฅํ•ด ์œ ๋„
680
+ const targetDirection = missile.target.position.clone().sub(missile.position).normalize();
681
+
682
+ // ๋ถ€๋“œ๋Ÿฌ์šด ํšŒ์ „์„ ์œ„ํ•œ ๋ณด๊ฐ„
683
+ missile.velocity.lerp(targetDirection.multiplyScalar(2000), deltaTime * 4); // ๋ฏธ์‚ฌ์ผ ์†๋„ 2000m/s
684
+ missile.velocity.normalize().multiplyScalar(2000);
685
+
686
+ // ๋ฏธ์‚ฌ์ผ ํšŒ์ „
687
+ const missileDirection = missile.velocity.clone().normalize();
688
+ const angle = Math.atan2(missileDirection.x, missileDirection.z);
689
+ missile.rotation.y = angle;
690
+ missile.rotation.x = Math.asin(-missileDirection.y);
691
+ }
692
+
693
+ // ์œ„์น˜ ์—…๋ฐ์ดํŠธ
694
+ missile.position.add(missile.velocity.clone().multiplyScalar(deltaTime));
695
+
696
+ // ์—ฐ๊ธฐ ํšจ๊ณผ ์ถ”๊ฐ€
697
+ if (Math.random() > 0.7) {
698
+ this.createMissileTrail(missile.position);
699
+ }
700
+
701
+ // 10km ์ด์ƒ ๋‚ ์•„๊ฐ€๊ฑฐ๋‚˜ ์ง€๋ฉด ์ถฉ๋Œ์‹œ ์ œ๊ฑฐ
702
+ if (missile.position.distanceTo(missile.startPosition) > 10000 ||
703
+ missile.position.y < 0 ||
704
+ missile.life > 10) { // 10์ดˆ ํ›„ ์ž๋™ ํŒŒ๊ดด
705
+ scene.remove(missile);
706
+ this.activeMissiles.splice(i, 1);
707
+ } else {
708
+ missile.life += deltaTime;
709
+ }
710
+ }
711
+ }
712
+
713
+ createMissileTrail(position) {
714
+ const trailGeometry = new THREE.SphereGeometry(2, 4, 4);
715
+ const trailMaterial = new THREE.MeshBasicMaterial({
716
+ color: 0x888888,
717
+ transparent: true,
718
+ opacity: 0.6
719
+ });
720
+
721
+ const trail = new THREE.Mesh(trailGeometry, trailMaterial);
722
+ trail.position.copy(position);
723
+
724
+ this.mesh.parent.add(trail);
725
+
726
+ // ํŽ˜์ด๋“œ ์•„์›ƒ ์• ๋‹ˆ๋ฉ”์ด์…˜
727
+ const fadeOut = () => {
728
+ trail.material.opacity -= 0.02;
729
+ trail.scale.multiplyScalar(1.05);
730
+
731
+ if (trail.material.opacity > 0) {
732
+ requestAnimationFrame(fadeOut);
733
+ } else {
734
+ this.mesh.parent.remove(trail);
735
+ }
736
+ };
737
+
738
+ fadeOut();
739
+ }
740
+
741
+ shootMissile(scene, target) {
742
+ if (this.missiles <= 0 || !target || !target.mesh) return;
743
+
744
+ const currentTime = Date.now();
745
+ if (currentTime - this.lastMissileFire < 6000) return; // 6์ดˆ ์ฟจ๋‹ค์šด
746
+
747
+ this.missiles--;
748
+ this.lastMissileFire = currentTime;
749
+ this.weaponCooldown = 6000; // 6์ดˆ ์ฟจ๋‹ค์šด ์„ค์ •
750
+
751
+ // ๋ฏธ์‚ฌ์ผ ๋ชจ๋ธ
752
+ const missileGeometry = new THREE.CylinderGeometry(0.3, 0.5, 4, 8);
753
+ const missileMaterial = new THREE.MeshBasicMaterial({
754
+ color: 0xcccccc,
755
+ emissive: 0xff6600,
756
+ emissiveIntensity: 0.5
757
+ });
758
+ const missile = new THREE.Mesh(missileGeometry, missileMaterial);
759
+
760
+ // ๋‚ ๊ฐœ ์ถ”๊ฐ€
761
+ const wingGeometry = new THREE.BoxGeometry(2, 0.1, 0.5);
762
+ const wingMaterial = new THREE.MeshBasicMaterial({ color: 0x666666 });
763
+ const wing1 = new THREE.Mesh(wingGeometry, wingMaterial);
764
+ const wing2 = new THREE.Mesh(wingGeometry, wingMaterial);
765
+ wing1.position.z = -1;
766
+ wing2.position.z = -1;
767
+ wing2.rotation.z = Math.PI / 2;
768
+ missile.add(wing1);
769
+ missile.add(wing2);
770
+
771
+ // ๋ฏธ์‚ฌ์ผ ์ดˆ๊ธฐ ์œ„์น˜ (๋‚ ๊ฐœ ์•„๋ž˜์—์„œ ๋ฐœ์‚ฌ)
772
+ const launchOffset = new THREE.Vector3(0, -2, 5);
773
+ launchOffset.applyEuler(this.rotation);
774
+ missile.position.copy(this.position).add(launchOffset);
775
+
776
+ // ๋ฏธ์‚ฌ์ผ ์ดˆ๊ธฐ ์†๋„
777
+ const direction = new THREE.Vector3(0, 0, 1);
778
+ direction.applyEuler(this.rotation);
779
+ missile.velocity = direction.multiplyScalar(1000); // ์ดˆ๊ธฐ ์†๋„
780
+
781
+ missile.rotation.copy(this.rotation);
782
+ missile.rotateX(Math.PI / 2);
783
+
784
+ // ๋ฏธ์‚ฌ์ผ ์†์„ฑ
785
+ missile.target = target;
786
+ missile.startPosition = missile.position.clone();
787
+ missile.life = 0;
788
+ missile.type = 'missile';
789
+
790
+ scene.add(missile);
791
+ this.activeMissiles.push(missile);
792
+
793
+ // ๋ฐœ์‚ฌ ์‚ฌ์šด๋“œ
794
+ try {
795
+ const audio = new Audio('sounds/towfirelaunch.ogg');
796
+ audio.volume = 0.6;
797
+ audio.play().catch(e => console.log('Missile launch sound failed to play'));
798
+ } catch (e) {
799
+ console.log('Audio error:', e);
800
+ }
801
+
802
+ // ๋ฝ์˜จ ์ดˆ๊ธฐํ™”
803
+ this.missileTarget = null;
804
+ this.missileLockCount = 0;
805
+ this.missileLockTimer = 0;
806
+ }
807
+
808
+ updateMissileLock(enemies, deltaTime) {
809
+ if (this.currentWeapon !== 'missile' || this.missiles <= 0) {
810
+ this.missileTarget = null;
811
+ this.missileLockCount = 0;
812
+ return;
813
+ }
814
+
815
+ // ์ฟจ๋‹ค์šด ์—…๋ฐ์ดํŠธ
816
+ if (this.weaponCooldown > 0) {
817
+ this.weaponCooldown -= deltaTime * 1000;
818
+ return;
819
+ }
820
+
821
+ // ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์  ์ฐพ๊ธฐ
822
+ let closestEnemy = null;
823
+ let closestDistance = Infinity;
824
+
825
+ enemies.forEach(enemy => {
826
+ if (!enemy.mesh || !enemy.isLoaded) return;
827
+
828
+ const distance = this.position.distanceTo(enemy.position);
829
+ if (distance < closestDistance && distance < 5000) { // 5km ์ด๋‚ด
830
+ // ์‹œ์•ผ๊ฐ ํ™•์ธ
831
+ const toEnemy = enemy.position.clone().sub(this.position).normalize();
832
+ const forward = new THREE.Vector3(0, 0, 1);
833
+ forward.applyEuler(this.rotation);
834
+ const angle = forward.dot(toEnemy);
835
+
836
+ if (angle > 0.5) { // ์‹œ์•ผ๊ฐ 60๋„ ์ด๋‚ด
837
+ closestEnemy = enemy;
838
+ closestDistance = distance;
839
+ }
840
+ }
841
+ });
842
+
843
+ // ํƒ€๊ฒŸ ๋ณ€๊ฒฝ์‹œ ๋ฝ์˜จ ์ดˆ๊ธฐํ™”
844
+ if (closestEnemy !== this.missileTarget) {
845
+ this.missileTarget = closestEnemy;
846
+ this.missileLockCount = 0;
847
+ this.missileLockTimer = 0;
848
+ }
849
+
850
+ // ๋ฝ์˜จ ํ”„๋กœ์„ธ์Šค
851
+ if (this.missileTarget) {
852
+ this.missileLockTimer += deltaTime;
853
+
854
+ // 0.5์ดˆ๋งˆ๋‹ค ๋ฝ์˜จ ์นด์šดํŠธ ์ฆ๊ฐ€
855
+ if (this.missileLockTimer >= 0.5) {
856
+ this.missileLockTimer = 0;
857
+ this.missileLockCount++;
858
+
859
+ // ๋ฝ์˜จ ์‚ฌ์šด๋“œ
860
+ try {
861
+ const lockSound = new Audio('sounds/lock.ogg');
862
+ lockSound.volume = 0.3;
863
+ lockSound.play().catch(e => {});
864
+ } catch (e) {}
865
+
866
+ if (this.missileLockCount >= 3) {
867
+ this.missileLockCount = 3; // ์ตœ๋Œ€ 3
868
+ }
869
+ }
870
+ }
871
  }
872
 
873
  takeDamage(damage) {
 
1296
  this.bgm = null;
1297
  this.bgmPlaying = false;
1298
 
1299
+ this.keys = { w: false, a: false, s: false, d: false, f: false, r: false };
1300
  this.isStarted = false;
1301
 
1302
  this.setupScene();
 
1537
  case 'KeyS': this.keys.s = true; break;
1538
  case 'KeyD': this.keys.d = true; break;
1539
  case 'KeyF': this.keys.f = true; break;
1540
+ case 'KeyR':
1541
+ if (!this.keys.r) {
1542
+ this.keys.r = true;
1543
+ this.switchWeapon();
1544
+ }
1545
+ break;
1546
  }
1547
  });
1548
 
 
1555
  case 'KeyS': this.keys.s = false; break;
1556
  case 'KeyD': this.keys.d = false; break;
1557
  case 'KeyF': this.keys.f = false; break;
1558
+ case 'KeyR': this.keys.r = false; break;
1559
  }
1560
  });
1561
 
 
1573
  if (!document.pointerLockElement || this.isGameOver || !gameStarted) return;
1574
 
1575
  if (event.button === 0) { // ์™ผ์ชฝ ๋งˆ์šฐ์Šค ๋ฒ„ํŠผ
1576
+ if (this.fighter.currentWeapon === 'mg') {
1577
+ this.fighter.isMouseDown = true;
1578
+ this.lastShootTime = 0; // ์ฆ‰์‹œ ๋ฐœ์‚ฌ
1579
+ } else if (this.fighter.currentWeapon === 'missile') {
1580
+ // ๋ฏธ์‚ฌ์ผ ๋ฐœ์‚ฌ
1581
+ if (this.fighter.missileTarget && this.fighter.missileLockCount >= 3) {
1582
+ this.fighter.shootMissile(this.scene, this.fighter.missileTarget);
1583
+ }
1584
+ }
1585
  }
1586
  });
1587
 
 
1598
  });
1599
  }
1600
 
1601
+ switchWeapon() {
1602
+ if (this.fighter.currentWeapon === 'mg') {
1603
+ this.fighter.currentWeapon = 'missile';
1604
+ } else {
1605
+ this.fighter.currentWeapon = 'mg';
1606
  }
1607
 
1608
+ // ๋ฌด๊ธฐ ๋ณ€๊ฒฝ์‹œ ๋ฝ์˜จ ์ดˆ๊ธฐํ™”
1609
+ this.fighter.missileTarget = null;
1610
+ this.fighter.missileLockCount = 0;
1611
+ this.fighter.missileLockTimer = 0;
1612
 
1613
+ // ๋ฌด๊ธฐ ๋ณ€๊ฒฝ ์‚ฌ์šด๋“œ
1614
+ try {
1615
+ const switchSound = new Audio('sounds/switch.ogg');
1616
+ switchSound.volume = 0.3;
1617
+ switchSound.play().catch(e => {});
1618
+ } catch (e) {}
1619
  }
1620
 
1621
  startBGM() {
 
1688
 
1689
  if (scoreElement) scoreElement.textContent = `Score: ${this.score}`;
1690
  if (timeElement) timeElement.textContent = `Time: ${this.gameTime}s`;
1691
+
1692
+ // ๋ฌด๊ธฐ ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ
1693
+ if (ammoElement) {
1694
+ let weaponDisplay = '';
1695
+ if (this.fighter.currentWeapon === 'mg') {
1696
+ weaponDisplay = `
1697
+ <div style="font-size: 12px; margin-bottom: 5px;">R: WEAPON CHANGE</div>
1698
+ <div style="font-size: 16px; color: #00ff00;">20MM MG</div>
1699
+ <div>AMMO: ${this.fighter.ammo}</div>
1700
+ `;
1701
+ } else {
1702
+ const cooldownSeconds = Math.ceil(this.fighter.weaponCooldown / 1000);
1703
+ weaponDisplay = `
1704
+ <div style="font-size: 12px; margin-bottom: 5px;">R: WEAPON CHANGE</div>
1705
+ <div style="font-size: 16px; color: #ff8800;">AIM-9 MISSILE</div>
1706
+ <div>COUNT: ${this.fighter.missiles}</div>
1707
+ `;
1708
+
1709
+ // ์ฟจ๋‹ค์šด ์ค‘์ด๋ฉด ๋ชจ๋ž˜์‹œ๊ณ„ ํ‘œ์‹œ
1710
+ if (this.fighter.weaponCooldown > 0) {
1711
+ weaponDisplay += `<div style="font-size: 20px; margin-top: 5px;">โณ ${cooldownSeconds}s</div>`;
1712
+ } else if (this.fighter.missileTarget) {
1713
+ // ๋ฝ์˜จ ์ง„ํ–‰ ์ƒํƒœ ํ‘œ์‹œ
1714
+ const lockSymbols = ['โ—ฏ', 'โ—‰', 'โ—‰'];
1715
+ let lockDisplay = '';
1716
+ for (let i = 0; i < 3; i++) {
1717
+ if (i < this.fighter.missileLockCount) {
1718
+ lockDisplay += `<span style="color: #ff0000;">${lockSymbols[i]}</span> `;
1719
+ } else {
1720
+ lockDisplay += `<span style="color: #666;">${lockSymbols[0]}</span> `;
1721
+ }
1722
+ }
1723
+ weaponDisplay += `<div style="margin-top: 5px;">LOCK: ${lockDisplay}</div>`;
1724
+
1725
+ if (this.fighter.missileLockCount >= 3) {
1726
+ weaponDisplay += `<div style="color: #ff0000; animation: blink 0.5s infinite;">READY TO FIRE</div>`;
1727
+ }
1728
+ }
1729
+ }
1730
+ ammoElement.innerHTML = weaponDisplay;
1731
+ }
1732
 
1733
  if (gameStatsElement) {
1734
  gameStatsElement.innerHTML = `
 
2163
  }
2164
  }
2165
 
2166
+ // ํ”Œ๋ ˆ์ด์–ด ๋ฏธ์‚ฌ์ผ vs ์ ๊ธฐ ์ถฉ๋Œ
2167
+ for (let i = this.fighter.activeMissiles.length - 1; i >= 0; i--) {
2168
+ const missile = this.fighter.activeMissiles[i];
2169
+
2170
+ if (missile.target && missile.target.mesh && missile.target.isLoaded) {
2171
+ const distance = missile.position.distanceTo(missile.target.position);
2172
+ if (distance < 50) { // ๋ฏธ์‚ฌ์ผ ์ถฉ๋Œ ๊ฑฐ๋ฆฌ
2173
+ this.scene.remove(missile);
2174
+ this.fighter.activeMissiles.splice(i, 1);
2175
+
2176
+ // ํฐ ํญ๋ฐœ ์ดํŽ™ํŠธ
2177
+ this.createMissileExplosion(missile.target.position);
2178
+
2179
+ // ๋ฏธ์‚ฌ์ผ ๋ฐ๋ฏธ์ง€ (500)
2180
+ if (missile.target.takeDamage(GAME_CONSTANTS.MISSILE_DAMAGE)) {
2181
+ const enemyIndex = this.enemies.indexOf(missile.target);
2182
+ if (enemyIndex !== -1) {
2183
+ missile.target.destroy();
2184
+ this.enemies.splice(enemyIndex, 1);
2185
+ this.score += 250; // ๋ฏธ์‚ฌ์ผ ๊ฒฉ์ถ”๋Š” ๋” ๋งŽ์€ ์ ์ˆ˜
2186
+ }
2187
+ }
2188
+ }
2189
+ }
2190
+ }
2191
+
2192
  // ์  ํƒ„ํ™˜ vs ํ”Œ๋ ˆ์ด์–ด ์ถฉ๋Œ
2193
  this.enemies.forEach(enemy => {
2194
  enemy.bullets.forEach((bullet, index) => {
 
2207
  });
2208
  });
2209
  }
2210
+
2211
+ createMissileExplosion(position) {
2212
+ // ๋Œ€ํ˜• ํญ๋ฐœ ํšจ๊ณผ
2213
+ const explosionGroup = new THREE.Group();
2214
+ explosionGroup.position.copy(position);
2215
+
2216
+ // ์ค‘์•™ ํญ๋ฐœ
2217
+ const coreGeometry = new THREE.SphereGeometry(10, 16, 16);
2218
+ const coreMaterial = new THREE.MeshBasicMaterial({
2219
+ color: 0xffff00,
2220
+ emissive: 0xffaa00,
2221
+ emissiveIntensity: 2.0,
2222
+ transparent: true,
2223
+ opacity: 1.0
2224
+ });
2225
+ const core = new THREE.Mesh(coreGeometry, coreMaterial);
2226
+ explosionGroup.add(core);
2227
+
2228
+ // ์™ธ๋ถ€ ํญ๋ฐœ ๋ง
2229
+ const ringGeometry = new THREE.TorusGeometry(15, 5, 8, 16);
2230
+ const ringMaterial = new THREE.MeshBasicMaterial({
2231
+ color: 0xff6600,
2232
+ emissive: 0xff3300,
2233
+ emissiveIntensity: 1.5,
2234
+ transparent: true,
2235
+ opacity: 0.8
2236
+ });
2237
+ const ring = new THREE.Mesh(ringGeometry, ringMaterial);
2238
+ explosionGroup.add(ring);
2239
+
2240
+ // ํŒŒํŽธ ํŒŒํ‹ฐํด
2241
+ const particleCount = 30;
2242
+ const particles = [];
2243
+
2244
+ for (let i = 0; i < particleCount; i++) {
2245
+ const particleGeometry = new THREE.SphereGeometry(Math.random() * 2 + 0.5, 4, 4);
2246
+ const particleMaterial = new THREE.MeshBasicMaterial({
2247
+ color: Math.random() > 0.5 ? 0xffaa00 : 0xff6600,
2248
+ emissive: 0xffaa00,
2249
+ emissiveIntensity: 1.0,
2250
+ transparent: true,
2251
+ opacity: 1.0
2252
+ });
2253
+
2254
+ const particle = new THREE.Mesh(particleGeometry, particleMaterial);
2255
+ particle.position.set(
2256
+ (Math.random() - 0.5) * 10,
2257
+ (Math.random() - 0.5) * 10,
2258
+ (Math.random() - 0.5) * 10
2259
+ );
2260
+
2261
+ particle.velocity = new THREE.Vector3(
2262
+ (Math.random() - 0.5) * 200,
2263
+ (Math.random() - 0.5) * 200,
2264
+ (Math.random() - 0.5) * 200
2265
+ );
2266
+
2267
+ particle.life = 1.5;
2268
+ explosionGroup.add(particle);
2269
+ particles.push(particle);
2270
+ }
2271
+
2272
+ this.scene.add(explosionGroup);
2273
+
2274
+ // ํญ๋ฐœ ์• ๋‹ˆ๋ฉ”์ด์…˜
2275
+ const animateExplosion = () => {
2276
+ // ์ค‘์•™ ํญ๋ฐœ ํ™•์žฅ
2277
+ core.scale.multiplyScalar(1.15);
2278
+ core.material.opacity *= 0.92;
2279
+
2280
+ // ๋ง ํ™•์žฅ ๋ฐ ํšŒ์ „
2281
+ ring.scale.multiplyScalar(1.1);
2282
+ ring.rotation.x += 0.1;
2283
+ ring.rotation.y += 0.15;
2284
+ ring.material.opacity *= 0.94;
2285
+
2286
+ // ํŒŒํ‹ฐํด ์• ๋‹ˆ๋ฉ”์ด์…˜
2287
+ particles.forEach(particle => {
2288
+ particle.life -= 0.02;
2289
+ particle.position.add(particle.velocity.clone().multiplyScalar(0.02));
2290
+ particle.velocity.y -= 3; // ์ค‘๋ ฅ
2291
+ particle.material.opacity = particle.life / 1.5;
2292
+ particle.scale.multiplyScalar(0.98);
2293
+ });
2294
+
2295
+ if (core.material.opacity > 0.01) {
2296
+ requestAnimationFrame(animateExplosion);
2297
+ } else {
2298
+ this.scene.remove(explosionGroup);
2299
+ }
2300
+ };
2301
+
2302
+ animateExplosion();
2303
+
2304
+ // ํญ๋ฐœ ์‚ฌ์šด๋“œ
2305
+ try {
2306
+ const explosionSound = new Audio('sounds/explosion.ogg');
2307
+ explosionSound.volume = 0.6;
2308
+ explosionSound.play().catch(e => {});
2309
+ } catch (e) {}
2310
+ }
2311
 
2312
  createHitEffect(position) {
2313
  // ํ”ผ๊ฒฉ ํŒŒํ‹ฐํด ํšจ๊ณผ ์ƒ์„ฑ
 
2471
  this.fighter.updateBullets(this.scene, deltaTime);
2472
 
2473
  // ๋งˆ์šฐ์Šค ๋ˆ„๋ฆ„ ์ƒํƒœ์ผ ๋•Œ ์—ฐ์† ๋ฐœ์‚ฌ
2474
+ if (this.fighter.isMouseDown && this.isStarted && this.fighter.currentWeapon === 'mg') {
2475
  const currentShootTime = Date.now();
2476
  if (!this.lastShootTime || currentShootTime - this.lastShootTime >= 100) { // 0.1์ดˆ๋งˆ๋‹ค
2477
  this.fighter.shoot(this.scene);
 
2479
  }
2480
  }
2481
 
2482
+ // ๋ฏธ์‚ฌ์ผ ๋ฝ์˜จ ์—…๋ฐ์ดํŠธ
2483
+ if (this.fighter.currentWeapon === 'missile') {
2484
+ this.fighter.updateMissileLock(this.enemies, deltaTime);
2485
+ }
2486
+
2487
  if (this.isStarted) {
2488
  // ์ ๊ธฐ๋“ค์—๊ฒŒ ์„œ๋กœ์˜ ์ฐธ์กฐ ์ „๋‹ฌ (์ถฉ๋Œ ํšŒํ”ผ์šฉ)
2489
  this.enemies.forEach(enemy => {