This commit is contained in:
Bluwww 2025-12-15 21:49:09 +07:00
parent 7cc449240f
commit 71389ebb08

227
Script.js
View File

@ -41,6 +41,7 @@ let currentWave = null;
let waveCooldown = 0;
let abilityCharges = 0;
let missileAmmo = 0; // Missile Ammo
var game = {
level: 1,
@ -92,7 +93,8 @@ for (var i = 0; i < enemyImgArray.length; i++) {
enemyImgArray[i].src = "img/alien_" + [i] + ".png";
}
var missilesArray = [];
var missilesArray = []; // Laser Biasa
var playerMissilesArray = []; // Homing Missile
var enemyShipArray = [];
var enemyBulletsArray = [];
var explosions = [];
@ -163,26 +165,36 @@ function gameLoop(timestamp) {
let gamePaused = false;
function keyDownPressed(e) {
if (e.keyCode === 87 || e.keyCode === 38) keys.up = true;
else if (e.keyCode === 83 || e.keyCode === 40) keys.down = true;
if (e.keyCode === 65 || e.keyCode === 37) keys.left = true;
if (e.keyCode === 68 || e.keyCode === 39) keys.right = true;
if (e.keyCode === 87 || e.keyCode === 38) keys.up = true; // W / UP
else if (e.keyCode === 83 || e.keyCode === 40) keys.down = true; // S / DOWN
if (e.keyCode === 65 || e.keyCode === 37) keys.left = true; // A / LEFT
if (e.keyCode === 68 || e.keyCode === 39) keys.right = true; // D / RIGHT
if (e.keyCode === 32) {
// SPACE
keys.fire = true;
if (!player1.dead) {
fireBullet();
}
}
if (e.keyCode === 80) togglePause();
if (e.keyCode === 80) togglePause(); // P
if (e.keyCode === 16) {
// SHIFT (Bomb)
if (abilityCharges > 0 && !game.gameOver && !gamePaused && !player1.dead) {
useAbility();
abilityCharges--;
}
}
// --- TOMBOL Q UNTUK MISSILE ---
if (e.keyCode === 81) {
// Q
if (!game.gameOver && !gamePaused && !player1.dead) {
firePlayerMissile();
}
}
}
function keyUpPressed(e) {
@ -193,18 +205,15 @@ function keyUpPressed(e) {
if (e.keyCode === 32) keys.fire = false;
}
// --- UPDATE FIRE BULLET: Cek Skill Double Laser ---
// --- NORMAL LASER ---
function fireBullet() {
if (player1.doubleLaserTimer > 0) {
// --- SKILL AKTIF: TEMBAK 2 PELURU ---
// Atas
missilesArray.push(
new LaserBullet(
player1.x + player1.width,
player1.y + player1.height / 2 - 15
)
);
// Bawah
missilesArray.push(
new LaserBullet(
player1.x + player1.width,
@ -212,7 +221,6 @@ function fireBullet() {
)
);
} else {
// --- NORMAL: TEMBAK 1 PELURU ---
missilesArray.push(
new LaserBullet(player1.x + player1.width, player1.y + player1.height / 2)
);
@ -229,6 +237,25 @@ function fireBullet() {
);
}
// --- FIRE HOMING MISSILE (Q) ---
function firePlayerMissile() {
if (missileAmmo > 0) {
missileAmmo--;
playerMissilesArray.push(
new PlayerMissile(
player1.x + player1.width,
player1.y + player1.height / 2 - 10
)
);
// Sound effect
let sfx = explosion_enemy.cloneNode();
sfx.volume = 0.5;
sfx.playbackRate = 2.0;
sfx.play();
}
}
function clearGame() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
}
@ -299,16 +326,18 @@ function drawGame() {
height: t.height,
})
) {
// Cek Tipe Orb
if (t.type === "bomb") {
// --- ORB KUNING (Bomb) ---
// KUNING = BOMB
abilityCharges++;
createParticles(t.x, t.y, 15, "#ffff00"); // Partikel Kuning
createParticles(t.x, t.y, 15, "#ffff00");
} else if (t.type === "double") {
// --- ORB MERAH (Double Laser) ---
// 10 Detik = 60 FPS * 10 = 600 Frames
// MERAH = DOUBLE LASER
player1.doubleLaserTimer = 600;
createParticles(t.x, t.y, 15, "#ff0000"); // Partikel Merah
createParticles(t.x, t.y, 15, "#ff0000");
} else if (t.type === "missile") {
// BIRU TUA = MISSILE (+3 Ammo)
missileAmmo += 3;
createParticles(t.x, t.y, 15, "#0000ff");
}
abilityTokens.splice(i, 1);
@ -340,7 +369,6 @@ function drawGame() {
continue;
}
// BALANCING TEMBAKAN (Low Rate)
let shootChance = 0.005 + game.level * 0.0012;
if (shootChance > 0.04) shootChance = 0.04;
@ -362,6 +390,7 @@ function drawGame() {
}
}
// --- UPDATE LASER BIASA ---
for (let i = 0; i < missilesArray.length; i++) {
let m = missilesArray[i];
m.draw();
@ -374,7 +403,6 @@ function drawGame() {
let en = enemyShipArray[j];
if (Tabrakan(m.getHitbox(), en.getHitbox())) {
// --- BALANCING DAMAGE PLAYER ---
let playerDamage = 100 + game.level * 5;
en.health -= playerDamage;
@ -411,6 +439,53 @@ function drawGame() {
}
}
// --- UPDATE PLAYER MISSILES (AUTO LOCK) ---
for (let i = 0; i < playerMissilesArray.length; i++) {
let pm = playerMissilesArray[i];
pm.draw();
pm.update();
let hit = false;
for (let j = 0; j < enemyShipArray.length; j++) {
let en = enemyShipArray[j];
if (Tabrakan(pm.getHitbox(), en.getHitbox())) {
// DAMAGE MISSILE: 400 + (Level * 20)
let missileDmg = 400 + game.level * 20;
en.health -= missileDmg;
// Efek ledakan besar biru
createParticles(pm.x + pm.width, pm.y, 20, "#0000ff");
explosions.push(new Explosion(pm.x + pm.width, pm.y, 0.5));
playerMissilesArray.splice(i, 1);
hit = true;
if (en.health <= 0) {
player1.score += 100 + game.level * 10;
explosion_enemy.currentTime = 0;
explosion_enemy.play();
explosions.push(
new Explosion(en.x + en.width / 2, en.y + en.height / 2)
);
enemyShipArray.splice(j, 1);
}
break;
}
}
if (hit) {
i--;
continue;
}
// Hapus jika keluar layar jauh
if (pm.x > canvasWidth + 200 || pm.y < -200 || pm.y > canvasHeight + 200) {
playerMissilesArray.splice(i, 1);
i--;
}
}
for (let i = 0; i < enemyBulletsArray.length; i++) {
let b = enemyBulletsArray[i];
b.draw();
@ -486,10 +561,10 @@ function drawUI() {
livesText += "♥ ";
}
drawNewText(livesText, 30, canvasHeight - 50, "#ff3366");
drawNewText("Bombs: " + abilityCharges, 30, 50, "#ffffff");
// --- (HAPUS) INDIKATOR TIMER DOUBLE LASER ---
// Teks tidak lagi ditampilkan, tapi durasi skill tetap berjalan di update()
// UI SKILL
drawNewText("Bombs (Shift): " + abilityCharges, 30, 50, "#ffff00");
drawNewText("Missiles (Q): " + missileAmmo, 30, 85, "#00ccff"); // Updated Text
}
class PlayerObject {
@ -512,14 +587,12 @@ class PlayerObject {
this.invincible = 0;
this.dead = false;
// --- SKILL TIMER ---
this.doubleLaserTimer = 0; // Timer dalam frame
this.doubleLaserTimer = 0;
this.totalFrames = 5;
this.frameIndex = 2;
this.spriteWidth = 0;
this.sourceHeight = 0;
// --- SIZE PLAYER: Middle Ground ---
this.scale = 1.0;
this.image.onload = () => {
@ -620,7 +693,6 @@ class PlayerObject {
this.frameIndex = 2;
}
// --- KURANGI TIMER SKILL ---
if (this.doubleLaserTimer > 0) {
this.doubleLaserTimer--;
}
@ -749,11 +821,98 @@ class LaserBullet {
}
}
// --- NEW CLASS: HOMING PLAYER MISSILE ---
class PlayerMissile {
constructor(x, y) {
this.x = x;
this.y = y;
this.width = 30;
this.height = 12;
this.speed = 2; // Kecepatan Awal
this.maxSpeed = 18;
this.vx = 2;
this.vy = 0;
this.target = null; // Target yang akan dikejar
}
getHitbox() {
return { x: this.x, y: this.y, width: this.width, height: this.height };
}
draw() {
ctx.save();
// Gambar body missile (Biru)
let g = ctx.createLinearGradient(
this.x,
this.y,
this.x + this.width,
this.y
);
g.addColorStop(0, "#00008b");
g.addColorStop(0.5, "#4169e1");
g.addColorStop(1, "#ffffff");
ctx.fillStyle = g;
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + this.width, this.y + this.height / 2);
ctx.lineTo(this.x, this.y + this.height);
ctx.fill();
// Trail Effect
if (Math.random() < 0.5) {
createParticles(this.x, this.y + this.height / 2, 2, "#00bfff");
}
ctx.restore();
}
update() {
// 1. Akselerasi (makin lama makin cepat)
this.speed *= 1.08;
if (this.speed > this.maxSpeed) this.speed = this.maxSpeed;
// 2. Logic Cari Musuh Terdekat (Auto Lock)
if (!this.target || !enemyShipArray.includes(this.target)) {
let minDist = 100000;
let closest = null;
for (let e of enemyShipArray) {
let dx = e.x - this.x;
let dy = e.y - this.y;
let d = Math.sqrt(dx * dx + dy * dy);
if (d < minDist) {
minDist = d;
closest = e;
}
}
this.target = closest;
}
// 3. Gerak Mengejar Target
if (this.target) {
let tx = this.target.x + this.target.width / 2;
let ty = this.target.y + this.target.height / 2;
let dx = tx - this.x;
let dy = ty - this.y;
let angle = Math.atan2(dy, dx);
// Update velocity ke arah musuh
this.vx = Math.cos(angle) * this.speed;
this.vy = Math.sin(angle) * this.speed;
} else {
// Jika tidak ada target, terbang lurus
this.vx = this.speed;
this.vy = 0;
}
this.x += this.vx;
this.y += this.vy;
}
}
class EnemyObj {
constructor(x, y, speed, img, pattern = "straight") {
this.x = x;
this.y = y;
// --- SIZE MUSUH: Middle Ground ---
this.width = 145;
this.height = 90;
this.image = img;
@ -967,6 +1126,12 @@ class AbilityToken {
this.speed = 4;
// Random Type saat spawn: "bomb" atau "double"
this.type = Math.random() < 0.5 ? "bomb" : "double";
// RAND: 0-0.33=Bomb, 0.33-0.66=Double, 0.66-1.0=Missile
let r = Math.random();
if (r < 0.33) this.type = "bomb";
else if (r < 0.66) this.type = "double";
else this.type = "missile";
}
draw() {
@ -984,10 +1149,14 @@ class AbilityToken {
// Kuning (Bomb)
g.addColorStop(0.5, "#ffff00");
g.addColorStop(1, "#ff9900");
} else {
} else if (this.type === "double") {
// Merah (Double Laser)
g.addColorStop(0.5, "#ff3333");
g.addColorStop(1, "#990000");
} else {
// Biru Tua (Missile)
g.addColorStop(0.5, "#0000ff");
g.addColorStop(1, "#00008b");
}
ctx.fillStyle = g;
@ -1002,7 +1171,7 @@ class AbilityToken {
function maybeSpawnAbilityToken() {
// --- KEMBALI KE 0.2% (0.002) ---
if (Math.random() < 0.002 && abilityTokens.length < 3) {
if (Math.random() < 0.003 && abilityTokens.length < 3) {
const y = Math.random() * (canvasHeight - 120) + 60;
abilityTokens.push(new AbilityToken(canvasWidth + 40, y));
}