Spaces:
Running
Running
Update game.js
Browse files
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 !==
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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');
|