cutechicken commited on
Commit
d455e15
ยท
verified ยท
1 Parent(s): b575e4a

Update game.js

Browse files
Files changed (1) hide show
  1. game.js +317 -35
game.js CHANGED
@@ -25,7 +25,10 @@ const GAME_CONSTANTS = {
25
  AIM9_DAMAGE: 1000, // AIM-9 ๋ฏธ์‚ฌ์ผ ํ”ผํ•ด 1000!
26
  AIM9_SPEED: 514.4, // 1000kt๋ฅผ m/s๋กœ ๋ณ€ํ™˜
27
  AIM9_LOCK_RANGE: 6000, // ๋ฝ์˜จ ๊ฑฐ๋ฆฌ 6000m
28
- AIM9_LOCK_REQUIRED: 3 // 3๋ฒˆ ๋ฝ์˜จ ํ•„์š”
 
 
 
29
  };
30
 
31
  // ์ „ํˆฌ๊ธฐ ํด๋ž˜์Šค
@@ -108,9 +111,16 @@ class Fighter {
108
  pullup: null,
109
  overg: null,
110
  stall: null,
111
- normal: null // ์—”์ง„ ์†Œ๋ฆฌ
 
 
112
  };
113
  this.initializeWarningAudios();
 
 
 
 
 
114
  }
115
 
116
  // ํ—ค๋”ฉ์„ 0~360๋„๋กœ ์ •๊ทœํ™”ํ•˜๋Š” ํ—ฌํผ ํ•จ์ˆ˜
@@ -147,9 +157,18 @@ class Fighter {
147
  this.warningAudios.normal.volume = 0.5;
148
  this.warningAudios.normal.loop = true; // ์—”์ง„ ์†Œ๋ฆฌ๋Š” ๊ณ„์† ๋ฐ˜๋ณต
149
 
 
 
 
 
 
 
 
 
 
150
  // ๊ฒฝ๊ณ ์Œ์—๋งŒ ended ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ถ”๊ฐ€ (์—”์ง„ ์†Œ๋ฆฌ ์ œ์™ธ)
151
  Object.keys(this.warningAudios).forEach(key => {
152
- if (key !== 'normal' && this.warningAudios[key]) {
153
  this.warningAudios[key].addEventListener('ended', () => {
154
  this.updateWarningAudios();
155
  });
@@ -185,6 +204,35 @@ class Fighter {
185
  updateWarningAudios() {
186
  let currentWarning = null;
187
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  if (this.altitude < 250) {
189
  currentWarning = 'pullup';
190
  }
@@ -198,9 +246,10 @@ class Fighter {
198
  currentWarning = 'stall';
199
  }
200
 
201
- // ๊ฒฝ๊ณ ์Œ๋งŒ ๊ด€๋ฆฌ (์—”์ง„ ์†Œ๋ฆฌ๋Š” ์ œ์™ธ)
202
  Object.keys(this.warningAudios).forEach(key => {
203
- if (key !== 'normal' && key !== currentWarning && this.warningAudios[key] && !this.warningAudios[key].paused) {
 
204
  this.warningAudios[key].pause();
205
  this.warningAudios[key].currentTime = 0;
206
  }
@@ -1616,6 +1665,15 @@ class EnemyFighter {
1616
  this.nearbyEnemies = [];
1617
  this.avoidanceVector = new THREE.Vector3();
1618
 
 
 
 
 
 
 
 
 
 
1619
  // ์ดˆ๊ธฐ ๋ชฉํ‘œ ์„ค์ •
1620
  this.selectNewPatrolTarget();
1621
  }
@@ -1711,6 +1769,14 @@ update(playerPosition, deltaTime) {
1711
 
1712
  // ํƒ„ํ™˜ ์—…๋ฐ์ดํŠธ
1713
  this.updateBullets(deltaTime);
 
 
 
 
 
 
 
 
1714
  }
1715
 
1716
  executePatrol(deltaTime) {
@@ -1732,7 +1798,6 @@ update(playerPosition, deltaTime) {
1732
  executeCombat(playerPosition, deltaTime) {
1733
  const distance = this.position.distanceTo(playerPosition);
1734
 
1735
-
1736
  // ํ”Œ๋ ˆ์ด์–ด๋ฅผ ํ–ฅํ•ด ํšŒ์ „
1737
  this.smoothTurnToTarget(playerPosition, deltaTime);
1738
 
@@ -2093,6 +2158,136 @@ update(playerPosition, deltaTime) {
2093
  }
2094
  }
2095
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2096
  calculateAimAccuracy(target) {
2097
  const toTarget = target.clone().sub(this.position).normalize();
2098
  const forward = new THREE.Vector3(0, 0, 1).applyEuler(this.rotation);
@@ -2152,6 +2347,8 @@ update(playerPosition, deltaTime) {
2152
  this.scene.remove(this.mesh);
2153
  this.bullets.forEach(bullet => this.scene.remove(bullet));
2154
  this.bullets = [];
 
 
2155
  this.isLoaded = false;
2156
  }
2157
  }
@@ -2565,6 +2762,50 @@ class Game {
2565
  }, 1000);
2566
  }
2567
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2568
  updateUI() {
2569
  if (this.fighter.isLoaded) {
2570
  const speedKnots = Math.round(this.fighter.speed * 1.94384);
@@ -2781,9 +3022,60 @@ class Game {
2781
  const existingStallWarnings = document.querySelectorAll('.stall-escape-warning');
2782
  existingStallWarnings.forEach(w => w.remove());
2783
 
 
 
 
 
2784
  // ๊ณ ๋„ ๊ฒฝ๊ณ  ์™ธ๊ณฝ ํšจ๊ณผ
2785
  let altitudeEdgeEffect = document.getElementById('altitudeEdgeEffect');
2786
- if (this.fighter.altitude < 500) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2787
  if (!altitudeEdgeEffect) {
2788
  altitudeEdgeEffect = document.createElement('div');
2789
  altitudeEdgeEffect.id = 'altitudeEdgeEffect';
@@ -2940,6 +3232,18 @@ class Game {
2940
  opacity: 0.7;
2941
  }
2942
  }
 
 
 
 
 
 
 
 
 
 
 
 
2943
  `;
2944
  document.head.appendChild(style);
2945
  }
@@ -3216,33 +3520,6 @@ class Game {
3216
  }
3217
  }
3218
 
3219
- // ์ ์ด ํ”Œ๋ ˆ์ด์–ด๋ฅผ ๋ฝ์˜จ ์‹œ์ž‘ํ•  ๋•Œ
3220
- onEnemyLockStart(enemy) {
3221
- console.log('Warning: Enemy locking on!');
3222
- // ์—ฌ๊ธฐ์— ๋ฝ์˜จ ๊ฒฝ๊ณ  UI๋‚˜ ์‚ฌ์šด๋“œ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ
3223
- }
3224
-
3225
- // ์ ์ด ๋ฝ์˜จ์„ ์žƒ์—ˆ์„ ๋•Œ
3226
- onEnemyLockLost(enemy) {
3227
- console.log('Enemy lock lost');
3228
- // ๋ฝ์˜จ ๊ฒฝ๊ณ  ํ•ด์ œ
3229
- }
3230
-
3231
- // ์ ์ด ๋ฏธ์‚ฌ์ผ์„ ๋ฐœ์‚ฌํ–ˆ์„ ๋•Œ
3232
- onEnemyMissileLaunch(enemy, missile) {
3233
- console.log('Warning: Missile launched!');
3234
- // ๋ฏธ์‚ฌ์ผ ๊ฒฝ๊ณ ์Œ ์žฌ์ƒ
3235
- try {
3236
- const warningSound = new Audio('sounds/missilewarning.ogg');
3237
- warningSound.volume = 0.8;
3238
- warningSound.play().catch(e => {
3239
- console.log('Missile warning sound not found');
3240
- });
3241
- } catch (e) {}
3242
- }
3243
-
3244
-
3245
-
3246
  checkCollisions() {
3247
  // ํ”Œ๋ ˆ์ด์–ด์™€ ์ ๊ธฐ์˜ ์ง์ ‘ ์ถฉ๋Œ ์ฒดํฌ (์ตœ์šฐ์„ )
3248
  for (let i = this.enemies.length - 1; i >= 0; i--) {
@@ -3370,6 +3647,11 @@ class Game {
3370
  }
3371
  }
3372
  });
 
 
 
 
 
3373
  }
3374
 
3375
  createHitEffect(position) {
@@ -3812,7 +4094,7 @@ class Game {
3812
  document.exitPointerLock();
3813
 
3814
  // ๋ชจ๋“  ๊ฒฝ๊ณ  ๋ฐ ํšจ๊ณผ ์ œ๊ฑฐ
3815
- const existingWarnings = document.querySelectorAll('.warning-message, .stall-escape-warning');
3816
  existingWarnings.forEach(w => w.remove());
3817
 
3818
  const blurEffect = document.getElementById('overGBlurEffect');
 
25
  AIM9_DAMAGE: 1000, // AIM-9 ๋ฏธ์‚ฌ์ผ ํ”ผํ•ด 1000!
26
  AIM9_SPEED: 514.4, // 1000kt๋ฅผ m/s๋กœ ๋ณ€ํ™˜
27
  AIM9_LOCK_RANGE: 6000, // ๋ฝ์˜จ ๊ฑฐ๋ฆฌ 6000m
28
+ AIM9_LOCK_REQUIRED: 3, // 3๋ฒˆ ๋ฝ์˜จ ํ•„์š”
29
+ ENEMY_AIM9_COUNT: 4, // ์  AIM-9 ๋ฏธ์‚ฌ์ผ ์ˆ˜
30
+ ENEMY_LOCK_RANGE: 5000, // ์  ๋ฝ์˜จ ๊ฑฐ๋ฆฌ
31
+ ENEMY_LOCK_ANGLE: Math.PI / 12 // ์  ๋ฝ์˜จ ๊ฐ๋„ (15๋„)
32
  };
33
 
34
  // ์ „ํˆฌ๊ธฐ ํด๋ž˜์Šค
 
111
  pullup: null,
112
  overg: null,
113
  stall: null,
114
+ normal: null, // ์—”์ง„ ์†Œ๋ฆฌ
115
+ missileLock: null,
116
+ missileIncoming: null
117
  };
118
  this.initializeWarningAudios();
119
+
120
+ // ๋ฏธ์‚ฌ์ผ ๊ฒฝ๊ณ  ์‹œ์Šคํ…œ
121
+ this.incomingMissiles = []; // ํ”Œ๋ ˆ์ด์–ด๋ฅผ ํ–ฅํ•ด ๋‚ ์•„์˜ค๋Š” ๋ฏธ์‚ฌ์ผ
122
+ this.missileWarningActive = false;
123
+ this.beingLockedBy = []; // ๋ฝ์˜จ ์ค‘์ธ ์ ๋“ค
124
  }
125
 
126
  // ํ—ค๋”ฉ์„ 0~360๋„๋กœ ์ •๊ทœํ™”ํ•˜๋Š” ํ—ฌํผ ํ•จ์ˆ˜
 
157
  this.warningAudios.normal.volume = 0.5;
158
  this.warningAudios.normal.loop = true; // ์—”์ง„ ์†Œ๋ฆฌ๋Š” ๊ณ„์† ๋ฐ˜๋ณต
159
 
160
+ // ๋ฏธ์‚ฌ์ผ ๊ฒฝ๊ณ ์Œ ์ถ”๊ฐ€
161
+ this.warningAudios.missileLock = new Audio('sounds/missile2.ogg');
162
+ this.warningAudios.missileLock.volume = 0.8;
163
+ this.warningAudios.missileLock.loop = true;
164
+
165
+ this.warningAudios.missileIncoming = new Audio('sounds/missile3.ogg');
166
+ this.warningAudios.missileIncoming.volume = 1.0;
167
+ this.warningAudios.missileIncoming.loop = true;
168
+
169
  // ๊ฒฝ๊ณ ์Œ์—๋งŒ ended ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ถ”๊ฐ€ (์—”์ง„ ์†Œ๋ฆฌ ์ œ์™ธ)
170
  Object.keys(this.warningAudios).forEach(key => {
171
+ if (key !== 'normal' && key !== 'missileLock' && key !== 'missileIncoming' && this.warningAudios[key]) {
172
  this.warningAudios[key].addEventListener('ended', () => {
173
  this.updateWarningAudios();
174
  });
 
204
  updateWarningAudios() {
205
  let currentWarning = null;
206
 
207
+ // ๋ฏธ์‚ฌ์ผ ๊ฒฝ๊ณ ๊ฐ€ ์ตœ์šฐ์„ 
208
+ if (this.incomingMissiles.length > 0) {
209
+ // missileIncoming ์žฌ์ƒ
210
+ if (this.warningAudios.missileIncoming && this.warningAudios.missileIncoming.paused) {
211
+ this.warningAudios.missileIncoming.play().catch(e => {});
212
+ }
213
+ } else {
214
+ // ๋ฏธ์‚ฌ์ผ์ด ์—†์œผ๋ฉด missileIncoming ์ค‘์ง€
215
+ if (this.warningAudios.missileIncoming && !this.warningAudios.missileIncoming.paused) {
216
+ this.warningAudios.missileIncoming.pause();
217
+ this.warningAudios.missileIncoming.currentTime = 0;
218
+ }
219
+ }
220
+
221
+ // ๋ฝ์˜จ ๊ฒฝ๊ณ 
222
+ if (this.beingLockedBy.length > 0 && this.incomingMissiles.length === 0) {
223
+ // missileLock ์žฌ์ƒ
224
+ if (this.warningAudios.missileLock && this.warningAudios.missileLock.paused) {
225
+ this.warningAudios.missileLock.play().catch(e => {});
226
+ }
227
+ } else {
228
+ // ๋ฝ์˜จ์ด ์—†์œผ๋ฉด missileLock ์ค‘์ง€
229
+ if (this.warningAudios.missileLock && !this.warningAudios.missileLock.paused) {
230
+ this.warningAudios.missileLock.pause();
231
+ this.warningAudios.missileLock.currentTime = 0;
232
+ }
233
+ }
234
+
235
+ // ๊ธฐ์กด ๊ฒฝ๊ณ ์Œ ์ฒ˜๋ฆฌ
236
  if (this.altitude < 250) {
237
  currentWarning = 'pullup';
238
  }
 
246
  currentWarning = 'stall';
247
  }
248
 
249
+ // ๊ฒฝ๊ณ ์Œ๋งŒ ๊ด€๋ฆฌ (์—”์ง„ ์†Œ๋ฆฌ, ๋ฏธ์‚ฌ์ผ ๊ฒฝ๊ณ ์Œ ์ œ์™ธ)
250
  Object.keys(this.warningAudios).forEach(key => {
251
+ if (key !== 'normal' && key !== 'missileLock' && key !== 'missileIncoming' &&
252
+ key !== currentWarning && this.warningAudios[key] && !this.warningAudios[key].paused) {
253
  this.warningAudios[key].pause();
254
  this.warningAudios[key].currentTime = 0;
255
  }
 
1665
  this.nearbyEnemies = [];
1666
  this.avoidanceVector = new THREE.Vector3();
1667
 
1668
+ // AIM-9 ๋ฏธ์‚ฌ์ผ ์‹œ์Šคํ…œ
1669
+ this.aim9Missiles = GAME_CONSTANTS.ENEMY_AIM9_COUNT;
1670
+ this.firedMissiles = [];
1671
+ this.lockTarget = null;
1672
+ this.lockProgress = 0;
1673
+ this.lastLockTime = 0;
1674
+ this.lastMissileFireTime = 0;
1675
+ this.isLocking = false;
1676
+
1677
  // ์ดˆ๊ธฐ ๋ชฉํ‘œ ์„ค์ •
1678
  this.selectNewPatrolTarget();
1679
  }
 
1769
 
1770
  // ํƒ„ํ™˜ ์—…๋ฐ์ดํŠธ
1771
  this.updateBullets(deltaTime);
1772
+
1773
+ // ๋ฏธ์‚ฌ์ผ ์—…๋ฐ์ดํŠธ
1774
+ this.updateMissiles(deltaTime);
1775
+
1776
+ // ๋ฝ์˜จ ์‹œ์Šคํ…œ ์—…๋ฐ์ดํŠธ
1777
+ if (this.playerFighter) {
1778
+ this.updateLockOn(deltaTime);
1779
+ }
1780
  }
1781
 
1782
  executePatrol(deltaTime) {
 
1798
  executeCombat(playerPosition, deltaTime) {
1799
  const distance = this.position.distanceTo(playerPosition);
1800
 
 
1801
  // ํ”Œ๋ ˆ์ด์–ด๋ฅผ ํ–ฅํ•ด ํšŒ์ „
1802
  this.smoothTurnToTarget(playerPosition, deltaTime);
1803
 
 
2158
  }
2159
  }
2160
 
2161
+ updateMissiles(deltaTime) {
2162
+ for (let i = this.firedMissiles.length - 1; i >= 0; i--) {
2163
+ const missile = this.firedMissiles[i];
2164
+ const result = missile.update(deltaTime, this.position);
2165
+
2166
+ if (result === 'hit' || result === 'expired') {
2167
+ this.firedMissiles.splice(i, 1);
2168
+ }
2169
+ }
2170
+ }
2171
+
2172
+ updateLockOn(deltaTime) {
2173
+ if (!this.playerFighter || this.aim9Missiles <= 0) {
2174
+ this.lockTarget = null;
2175
+ this.lockProgress = 0;
2176
+ this.isLocking = false;
2177
+ return;
2178
+ }
2179
+
2180
+ const now = Date.now();
2181
+ const distance = this.position.distanceTo(this.playerFighter.position);
2182
+
2183
+ // ๋ฝ์˜จ ๋ฒ”์œ„ ๋‚ด์— ์žˆ๊ณ  ์ „ํˆฌ ์ค‘์ผ ๋•Œ๋งŒ
2184
+ if (distance <= GAME_CONSTANTS.ENEMY_LOCK_RANGE && this.aiState === 'combat') {
2185
+ // ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์‹œ์•ผ๊ฐ ๋‚ด์— ์žˆ๋Š”์ง€ ํ™•์ธ
2186
+ const toPlayer = this.playerFighter.position.clone().sub(this.position).normalize();
2187
+ const forward = new THREE.Vector3(0, 0, 1).applyEuler(this.rotation);
2188
+ const dotProduct = forward.dot(toPlayer);
2189
+ const angle = Math.acos(Math.max(-1, Math.min(1, dotProduct)));
2190
+
2191
+ if (angle < GAME_CONSTANTS.ENEMY_LOCK_ANGLE) {
2192
+ // 1์ดˆ๋งˆ๋‹ค ๋ฝ์˜จ ์ง„ํ–‰
2193
+ if (now - this.lastLockTime >= 1000) {
2194
+ if (this.lockTarget === this.playerFighter) {
2195
+ this.lockProgress++;
2196
+ this.lastLockTime = now;
2197
+
2198
+ // ๋ฝ์˜จ ์‹œ์ž‘ ์•Œ๋ฆผ
2199
+ if (this.lockProgress === 1 && !this.isLocking) {
2200
+ this.isLocking = true;
2201
+ if (window.gameInstance) {
2202
+ window.gameInstance.onEnemyLockStart(this);
2203
+ }
2204
+ }
2205
+
2206
+ // ๋ฝ์˜จ ์™„๋ฃŒ ๋ฐ ๋ฐœ์‚ฌ
2207
+ if (this.lockProgress >= GAME_CONSTANTS.AIM9_LOCK_REQUIRED) {
2208
+ // ๋ฏธ์‚ฌ์ผ ์ฟจ๋‹ค์šด ์ฒดํฌ (3์ดˆ)
2209
+ if (now - this.lastMissileFireTime >= 3000) {
2210
+ this.fireAIM9();
2211
+ this.lastMissileFireTime = now;
2212
+ }
2213
+ // ๋ฝ์˜จ ๋ฆฌ์…‹
2214
+ this.lockTarget = null;
2215
+ this.lockProgress = 0;
2216
+ this.isLocking = false;
2217
+ }
2218
+ } else {
2219
+ // ์ƒˆ๋กœ์šด ๋ฝ์˜จ ์‹œ์ž‘
2220
+ this.lockTarget = this.playerFighter;
2221
+ this.lockProgress = 1;
2222
+ this.lastLockTime = now;
2223
+ this.isLocking = true;
2224
+ if (window.gameInstance) {
2225
+ window.gameInstance.onEnemyLockStart(this);
2226
+ }
2227
+ }
2228
+ }
2229
+ } else {
2230
+ // ์‹œ์•ผ๊ฐ์„ ๋ฒ—์–ด๋‚˜๋ฉด ๋ฝ์˜จ ํ•ด์ œ
2231
+ if (this.lockTarget) {
2232
+ this.lockTarget = null;
2233
+ this.lockProgress = 0;
2234
+ if (this.isLocking) {
2235
+ this.isLocking = false;
2236
+ if (window.gameInstance) {
2237
+ window.gameInstance.onEnemyLockLost(this);
2238
+ }
2239
+ }
2240
+ }
2241
+ }
2242
+ } else {
2243
+ // ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ๋ฝ์˜จ ํ•ด์ œ
2244
+ if (this.lockTarget) {
2245
+ this.lockTarget = null;
2246
+ this.lockProgress = 0;
2247
+ if (this.isLocking) {
2248
+ this.isLocking = false;
2249
+ if (window.gameInstance) {
2250
+ window.gameInstance.onEnemyLockLost(this);
2251
+ }
2252
+ }
2253
+ }
2254
+ }
2255
+ }
2256
+
2257
+ fireAIM9() {
2258
+ if (this.aim9Missiles <= 0 || !this.lockTarget) return;
2259
+
2260
+ this.aim9Missiles--;
2261
+
2262
+ // ๋‚ ๊ฐœ ๋ฐœ์‚ฌ ์œ„์น˜
2263
+ const isLeftWing = Math.random() < 0.5;
2264
+ const wingOffset = new THREE.Vector3(isLeftWing ? -6 : 6, -1, 2);
2265
+ wingOffset.applyEuler(this.rotation);
2266
+ const missileStartPos = this.position.clone().add(wingOffset);
2267
+
2268
+ // ๋ฏธ์‚ฌ์ผ ์ƒ์„ฑ
2269
+ const missile = new EnemyAIM9Missile(this.scene, missileStartPos, this.lockTarget, this.rotation.clone());
2270
+ this.firedMissiles.push(missile);
2271
+
2272
+ // ๋ฐœ์‚ฌ ์•Œ๋ฆผ
2273
+ if (window.gameInstance) {
2274
+ window.gameInstance.onEnemyMissileLaunch(this, missile);
2275
+ }
2276
+
2277
+ // ๋ฐœ์‚ฌ์Œ (๊ฑฐ๋ฆฌ์— ๋”ฐ๋ผ)
2278
+ if (this.playerFighter) {
2279
+ const distanceToPlayer = this.position.distanceTo(this.playerFighter.position);
2280
+ if (distanceToPlayer < 3000) {
2281
+ try {
2282
+ const missileSound = new Audio('sounds/missile.ogg');
2283
+ const volumeMultiplier = 1 - (distanceToPlayer / 3000);
2284
+ missileSound.volume = 0.5 * volumeMultiplier;
2285
+ missileSound.play().catch(e => {});
2286
+ } catch (e) {}
2287
+ }
2288
+ }
2289
+ }
2290
+
2291
  calculateAimAccuracy(target) {
2292
  const toTarget = target.clone().sub(this.position).normalize();
2293
  const forward = new THREE.Vector3(0, 0, 1).applyEuler(this.rotation);
 
2347
  this.scene.remove(this.mesh);
2348
  this.bullets.forEach(bullet => this.scene.remove(bullet));
2349
  this.bullets = [];
2350
+ this.firedMissiles.forEach(missile => missile.destroy());
2351
+ this.firedMissiles = [];
2352
  this.isLoaded = false;
2353
  }
2354
  }
 
2762
  }, 1000);
2763
  }
2764
 
2765
+ // ์ ์ด ํ”Œ๋ ˆ์ด์–ด๋ฅผ ๋ฝ์˜จ ์‹œ์ž‘ํ•  ๋•Œ
2766
+ onEnemyLockStart(enemy) {
2767
+ console.log('Warning: Enemy locking on!');
2768
+
2769
+ // ๋ฝ์˜จ ์ค‘์ธ ์  ์ถ”๊ฐ€
2770
+ if (!this.fighter.beingLockedBy.includes(enemy)) {
2771
+ this.fighter.beingLockedBy.push(enemy);
2772
+ }
2773
+
2774
+ // ๊ฒฝ๊ณ ์Œ ์—…๋ฐ์ดํŠธ
2775
+ this.fighter.updateWarningAudios();
2776
+ }
2777
+
2778
+ // ์ ์ด ๋ฝ์˜จ์„ ์žƒ์—ˆ์„ ๋•Œ
2779
+ onEnemyLockLost(enemy) {
2780
+ console.log('Enemy lock lost');
2781
+
2782
+ // ๋ฝ์˜จ ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐ
2783
+ const index = this.fighter.beingLockedBy.indexOf(enemy);
2784
+ if (index !== -1) {
2785
+ this.fighter.beingLockedBy.splice(index, 1);
2786
+ }
2787
+
2788
+ // ๊ฒฝ๊ณ ์Œ ์—…๋ฐ์ดํŠธ
2789
+ this.fighter.updateWarningAudios();
2790
+ }
2791
+
2792
+ // ์ ์ด ๋ฏธ์‚ฌ์ผ์„ ๋ฐœ์‚ฌํ–ˆ์„ ๋•Œ
2793
+ onEnemyMissileLaunch(enemy, missile) {
2794
+ console.log('Warning: Missile launched!');
2795
+
2796
+ // ํ”Œ๋ ˆ์ด์–ด์˜ incoming missiles ๋ชฉ๋ก์— ์ถ”๊ฐ€
2797
+ this.fighter.incomingMissiles.push(missile);
2798
+
2799
+ // ๋ฝ์˜จ ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐ (๋ฏธ์‚ฌ์ผ ๋ฐœ์‚ฌ ํ›„ ๋ฝ์˜จ ํ•ด์ œ)
2800
+ const index = this.fighter.beingLockedBy.indexOf(enemy);
2801
+ if (index !== -1) {
2802
+ this.fighter.beingLockedBy.splice(index, 1);
2803
+ }
2804
+
2805
+ // ๊ฒฝ๊ณ ์Œ ์—…๋ฐ์ดํŠธ
2806
+ this.fighter.updateWarningAudios();
2807
+ }
2808
+
2809
  updateUI() {
2810
  if (this.fighter.isLoaded) {
2811
  const speedKnots = Math.round(this.fighter.speed * 1.94384);
 
3022
  const existingStallWarnings = document.querySelectorAll('.stall-escape-warning');
3023
  existingStallWarnings.forEach(w => w.remove());
3024
 
3025
+ // ๋ฏธ์‚ฌ์ผ ๊ฒฝ๊ณ  ์ œ๊ฑฐ
3026
+ const existingMissileWarnings = document.querySelectorAll('.missile-warning');
3027
+ existingMissileWarnings.forEach(w => w.remove());
3028
+
3029
  // ๊ณ ๋„ ๊ฒฝ๊ณ  ์™ธ๊ณฝ ํšจ๊ณผ
3030
  let altitudeEdgeEffect = document.getElementById('altitudeEdgeEffect');
3031
+
3032
+ // ๋ฏธ์‚ฌ์ผ ๊ฒฝ๊ณ ๊ฐ€ ์ตœ์šฐ์„ 
3033
+ if (this.fighter.incomingMissiles.length > 0) {
3034
+ if (!altitudeEdgeEffect) {
3035
+ altitudeEdgeEffect = document.createElement('div');
3036
+ altitudeEdgeEffect.id = 'altitudeEdgeEffect';
3037
+ document.body.appendChild(altitudeEdgeEffect);
3038
+ }
3039
+
3040
+ // ๊ฐ•๋ ฌํ•œ ๋ถ‰์€์ƒ‰ ํšจ๊ณผ
3041
+ const edgeIntensity = 0.8;
3042
+ altitudeEdgeEffect.style.cssText = `
3043
+ position: fixed;
3044
+ top: 0;
3045
+ left: 0;
3046
+ width: 100%;
3047
+ height: 100%;
3048
+ pointer-events: none;
3049
+ z-index: 1300;
3050
+ background: radial-gradient(ellipse at center,
3051
+ transparent 30%,
3052
+ rgba(255, 0, 0, ${edgeIntensity * 0.4}) 50%,
3053
+ rgba(255, 0, 0, ${edgeIntensity}) 100%);
3054
+ box-shadow: inset 0 0 200px rgba(255, 0, 0, ${edgeIntensity}),
3055
+ inset 0 0 150px rgba(255, 0, 0, ${edgeIntensity * 0.8});
3056
+ animation: missile-pulse 0.3s infinite;
3057
+ `;
3058
+
3059
+ // ๋ฏธ์‚ฌ์ผ ๊ฒฝ๊ณ  ํ…์ŠคํŠธ
3060
+ const missileWarning = document.createElement('div');
3061
+ missileWarning.className = 'missile-warning';
3062
+ missileWarning.style.cssText = `
3063
+ position: fixed;
3064
+ top: 20%;
3065
+ left: 50%;
3066
+ transform: translateX(-50%);
3067
+ color: #ff0000;
3068
+ font-size: 36px;
3069
+ font-weight: bold;
3070
+ text-shadow: 0 0 20px rgba(255,0,0,1), 0 0 40px rgba(255,0,0,0.8);
3071
+ z-index: 1600;
3072
+ text-align: center;
3073
+ animation: missile-blink 0.2s infinite;
3074
+ `;
3075
+ missileWarning.innerHTML = 'MISSILE INCOMING!<br>EVADE! EVADE!';
3076
+ document.body.appendChild(missileWarning);
3077
+
3078
+ } else if (this.fighter.altitude < 500) {
3079
  if (!altitudeEdgeEffect) {
3080
  altitudeEdgeEffect = document.createElement('div');
3081
  altitudeEdgeEffect.id = 'altitudeEdgeEffect';
 
3232
  opacity: 0.7;
3233
  }
3234
  }
3235
+ @keyframes missile-pulse {
3236
+ 0%, 100% {
3237
+ opacity: 1;
3238
+ }
3239
+ 50% {
3240
+ opacity: 0.5;
3241
+ }
3242
+ }
3243
+ @keyframes missile-blink {
3244
+ 0%, 50% { opacity: 1; }
3245
+ 51%, 100% { opacity: 0.2; }
3246
+ }
3247
  `;
3248
  document.head.appendChild(style);
3249
  }
 
3520
  }
3521
  }
3522
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3523
  checkCollisions() {
3524
  // ํ”Œ๋ ˆ์ด์–ด์™€ ์ ๊ธฐ์˜ ์ง์ ‘ ์ถฉ๋Œ ์ฒดํฌ (์ตœ์šฐ์„ )
3525
  for (let i = this.enemies.length - 1; i >= 0; i--) {
 
3647
  }
3648
  }
3649
  });
3650
+
3651
+ // incoming missiles ์—…๋ฐ์ดํŠธ (ํŒŒ๊ดด๋œ ๋ฏธ์‚ฌ์ผ ์ œ๊ฑฐ)
3652
+ this.fighter.incomingMissiles = this.fighter.incomingMissiles.filter(missile => {
3653
+ return missile && missile.mesh && missile.mesh.parent;
3654
+ });
3655
  }
3656
 
3657
  createHitEffect(position) {
 
4094
  document.exitPointerLock();
4095
 
4096
  // ๋ชจ๋“  ๊ฒฝ๊ณ  ๋ฐ ํšจ๊ณผ ์ œ๊ฑฐ
4097
+ const existingWarnings = document.querySelectorAll('.warning-message, .stall-escape-warning, .missile-warning');
4098
  existingWarnings.forEach(w => w.remove());
4099
 
4100
  const blurEffect = document.getElementById('overGBlurEffect');