This commit is contained in:
Bluwww 2025-12-15 22:24:14 +07:00
parent 71389ebb08
commit 2b55cdc7a4
2 changed files with 57 additions and 66 deletions

123
Script.js
View File

@ -41,7 +41,7 @@ let currentWave = null;
let waveCooldown = 0; let waveCooldown = 0;
let abilityCharges = 0; let abilityCharges = 0;
let missileAmmo = 0; // Missile Ammo let missileAmmo = 0;
var game = { var game = {
level: 1, level: 1,
@ -59,9 +59,15 @@ var keys = {
fire: false, fire: false,
}; };
// --- LOAD IMAGES ---
var playerShipImg = new Image(); var playerShipImg = new Image();
playerShipImg.src = "img/Player/pesawat22.png"; playerShipImg.src = "img/Player/pesawat22.png";
// *** GAMBAR BARU UNTUK PICKUP MISSILE ***
// Pastikan file gambar "missile_pickup.png" ada di folder img Anda
var missilePickupImg = new Image();
missilePickupImg.src = "img/Skills/missile.png";
var bg0 = new Image(); var bg0 = new Image();
bg0.src = "img/bg_0.png"; bg0.src = "img/bg_0.png";
var bg1 = new Image(); var bg1 = new Image();
@ -93,8 +99,8 @@ for (var i = 0; i < enemyImgArray.length; i++) {
enemyImgArray[i].src = "img/alien_" + [i] + ".png"; enemyImgArray[i].src = "img/alien_" + [i] + ".png";
} }
var missilesArray = []; // Laser Biasa var missilesArray = [];
var playerMissilesArray = []; // Homing Missile var playerMissilesArray = [];
var enemyShipArray = []; var enemyShipArray = [];
var enemyBulletsArray = []; var enemyBulletsArray = [];
var explosions = []; var explosions = [];
@ -131,6 +137,8 @@ function init() {
document.addEventListener("keydown", keyDownPressed, false); document.addEventListener("keydown", keyDownPressed, false);
document.addEventListener("keyup", keyUpPressed, false); document.addEventListener("keyup", keyUpPressed, false);
document.addEventListener("contextmenu", (event) => event.preventDefault());
gameStarted = true; gameStarted = true;
pickRandomBGM(); pickRandomBGM();
currentBGM.volume = 1; currentBGM.volume = 1;
@ -165,30 +173,27 @@ function gameLoop(timestamp) {
let gamePaused = false; let gamePaused = false;
function keyDownPressed(e) { function keyDownPressed(e) {
if (e.keyCode === 87 || e.keyCode === 38) keys.up = true; // W / UP if (e.keyCode === 87 || e.keyCode === 38) keys.up = true;
else if (e.keyCode === 83 || e.keyCode === 40) keys.down = true; // S / DOWN else if (e.keyCode === 83 || e.keyCode === 40) keys.down = true;
if (e.keyCode === 65 || e.keyCode === 37) keys.left = true; // A / LEFT if (e.keyCode === 65 || e.keyCode === 37) keys.left = true;
if (e.keyCode === 68 || e.keyCode === 39) keys.right = true; // D / RIGHT if (e.keyCode === 68 || e.keyCode === 39) keys.right = true;
if (e.keyCode === 32) { if (e.keyCode === 32) {
// SPACE
keys.fire = true; keys.fire = true;
if (!player1.dead) { if (!player1.dead) {
fireBullet(); fireBullet();
} }
} }
if (e.keyCode === 80) togglePause(); // P if (e.keyCode === 80) togglePause();
if (e.keyCode === 16) { if (e.keyCode === 16) {
// SHIFT (Bomb)
if (abilityCharges > 0 && !game.gameOver && !gamePaused && !player1.dead) { if (abilityCharges > 0 && !game.gameOver && !gamePaused && !player1.dead) {
useAbility(); useAbility();
abilityCharges--; abilityCharges--;
} }
} }
// --- TOMBOL Q UNTUK MISSILE ---
if (e.keyCode === 81) { if (e.keyCode === 81) {
// Q // Q
if (!game.gameOver && !gamePaused && !player1.dead) { if (!game.gameOver && !gamePaused && !player1.dead) {
@ -205,7 +210,6 @@ function keyUpPressed(e) {
if (e.keyCode === 32) keys.fire = false; if (e.keyCode === 32) keys.fire = false;
} }
// --- NORMAL LASER ---
function fireBullet() { function fireBullet() {
if (player1.doubleLaserTimer > 0) { if (player1.doubleLaserTimer > 0) {
missilesArray.push( missilesArray.push(
@ -237,7 +241,6 @@ function fireBullet() {
); );
} }
// --- FIRE HOMING MISSILE (Q) ---
function firePlayerMissile() { function firePlayerMissile() {
if (missileAmmo > 0) { if (missileAmmo > 0) {
missileAmmo--; missileAmmo--;
@ -248,7 +251,6 @@ function firePlayerMissile() {
) )
); );
// Sound effect
let sfx = explosion_enemy.cloneNode(); let sfx = explosion_enemy.cloneNode();
sfx.volume = 0.5; sfx.volume = 0.5;
sfx.playbackRate = 2.0; sfx.playbackRate = 2.0;
@ -289,7 +291,6 @@ function updateGame() {
player1.y = canvasHeight / 2 - player1.height / 2; player1.y = canvasHeight / 2 - player1.height / 2;
player1.vx = 0; player1.vx = 0;
player1.vy = 0; player1.vy = 0;
// Reset skill jika mati
player1.doubleLaserTimer = 0; player1.doubleLaserTimer = 0;
} }
} }
@ -311,7 +312,6 @@ function drawGame() {
drawParticles(); drawParticles();
// --- LOGIKA PICKUP ORB ---
for (let i = 0; i < abilityTokens.length; i++) { for (let i = 0; i < abilityTokens.length; i++) {
const t = abilityTokens[i]; const t = abilityTokens[i];
t.draw(); t.draw();
@ -327,15 +327,12 @@ function drawGame() {
}) })
) { ) {
if (t.type === "bomb") { if (t.type === "bomb") {
// KUNING = BOMB
abilityCharges++; abilityCharges++;
createParticles(t.x, t.y, 15, "#ffff00"); createParticles(t.x, t.y, 15, "#ffff00");
} else if (t.type === "double") { } else if (t.type === "double") {
// MERAH = DOUBLE LASER
player1.doubleLaserTimer = 600; player1.doubleLaserTimer = 600;
createParticles(t.x, t.y, 15, "#ff0000"); createParticles(t.x, t.y, 15, "#ff0000");
} else if (t.type === "missile") { } else if (t.type === "missile") {
// BIRU TUA = MISSILE (+3 Ammo)
missileAmmo += 3; missileAmmo += 3;
createParticles(t.x, t.y, 15, "#0000ff"); createParticles(t.x, t.y, 15, "#0000ff");
} }
@ -390,7 +387,6 @@ function drawGame() {
} }
} }
// --- UPDATE LASER BIASA ---
for (let i = 0; i < missilesArray.length; i++) { for (let i = 0; i < missilesArray.length; i++) {
let m = missilesArray[i]; let m = missilesArray[i];
m.draw(); m.draw();
@ -439,7 +435,6 @@ function drawGame() {
} }
} }
// --- UPDATE PLAYER MISSILES (AUTO LOCK) ---
for (let i = 0; i < playerMissilesArray.length; i++) { for (let i = 0; i < playerMissilesArray.length; i++) {
let pm = playerMissilesArray[i]; let pm = playerMissilesArray[i];
pm.draw(); pm.draw();
@ -450,11 +445,9 @@ function drawGame() {
let en = enemyShipArray[j]; let en = enemyShipArray[j];
if (Tabrakan(pm.getHitbox(), en.getHitbox())) { if (Tabrakan(pm.getHitbox(), en.getHitbox())) {
// DAMAGE MISSILE: 400 + (Level * 20)
let missileDmg = 400 + game.level * 20; let missileDmg = 400 + game.level * 20;
en.health -= missileDmg; en.health -= missileDmg;
// Efek ledakan besar biru
createParticles(pm.x + pm.width, pm.y, 20, "#0000ff"); createParticles(pm.x + pm.width, pm.y, 20, "#0000ff");
explosions.push(new Explosion(pm.x + pm.width, pm.y, 0.5)); explosions.push(new Explosion(pm.x + pm.width, pm.y, 0.5));
@ -479,7 +472,6 @@ function drawGame() {
continue; continue;
} }
// Hapus jika keluar layar jauh
if (pm.x > canvasWidth + 200 || pm.y < -200 || pm.y > canvasHeight + 200) { if (pm.x > canvasWidth + 200 || pm.y < -200 || pm.y > canvasHeight + 200) {
playerMissilesArray.splice(i, 1); playerMissilesArray.splice(i, 1);
i--; i--;
@ -562,9 +554,8 @@ function drawUI() {
} }
drawNewText(livesText, 30, canvasHeight - 50, "#ff3366"); drawNewText(livesText, 30, canvasHeight - 50, "#ff3366");
// UI SKILL
drawNewText("Bombs (Shift): " + abilityCharges, 30, 50, "#ffff00"); drawNewText("Bombs (Shift): " + abilityCharges, 30, 50, "#ffff00");
drawNewText("Missiles (Q): " + missileAmmo, 30, 85, "#00ccff"); // Updated Text drawNewText("Missiles (Q): " + missileAmmo, 30, 85, "#00ccff");
} }
class PlayerObject { class PlayerObject {
@ -821,18 +812,17 @@ class LaserBullet {
} }
} }
// --- NEW CLASS: HOMING PLAYER MISSILE ---
class PlayerMissile { class PlayerMissile {
constructor(x, y) { constructor(x, y) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.width = 30; this.width = 30;
this.height = 12; this.height = 12;
this.speed = 2; // Kecepatan Awal this.speed = 2;
this.maxSpeed = 18; this.maxSpeed = 18;
this.vx = 2; this.vx = 2;
this.vy = 0; this.vy = 0;
this.target = null; // Target yang akan dikejar this.target = null;
} }
getHitbox() { getHitbox() {
@ -841,7 +831,6 @@ class PlayerMissile {
draw() { draw() {
ctx.save(); ctx.save();
// Gambar body missile (Biru)
let g = ctx.createLinearGradient( let g = ctx.createLinearGradient(
this.x, this.x,
this.y, this.y,
@ -859,7 +848,6 @@ class PlayerMissile {
ctx.lineTo(this.x, this.y + this.height); ctx.lineTo(this.x, this.y + this.height);
ctx.fill(); ctx.fill();
// Trail Effect
if (Math.random() < 0.5) { if (Math.random() < 0.5) {
createParticles(this.x, this.y + this.height / 2, 2, "#00bfff"); createParticles(this.x, this.y + this.height / 2, 2, "#00bfff");
} }
@ -867,11 +855,9 @@ class PlayerMissile {
} }
update() { update() {
// 1. Akselerasi (makin lama makin cepat)
this.speed *= 1.08; this.speed *= 1.08;
if (this.speed > this.maxSpeed) this.speed = this.maxSpeed; if (this.speed > this.maxSpeed) this.speed = this.maxSpeed;
// 2. Logic Cari Musuh Terdekat (Auto Lock)
if (!this.target || !enemyShipArray.includes(this.target)) { if (!this.target || !enemyShipArray.includes(this.target)) {
let minDist = 100000; let minDist = 100000;
let closest = null; let closest = null;
@ -887,7 +873,6 @@ class PlayerMissile {
this.target = closest; this.target = closest;
} }
// 3. Gerak Mengejar Target
if (this.target) { if (this.target) {
let tx = this.target.x + this.target.width / 2; let tx = this.target.x + this.target.width / 2;
let ty = this.target.y + this.target.height / 2; let ty = this.target.y + this.target.height / 2;
@ -895,11 +880,9 @@ class PlayerMissile {
let dy = ty - this.y; let dy = ty - this.y;
let angle = Math.atan2(dy, dx); let angle = Math.atan2(dy, dx);
// Update velocity ke arah musuh
this.vx = Math.cos(angle) * this.speed; this.vx = Math.cos(angle) * this.speed;
this.vy = Math.sin(angle) * this.speed; this.vy = Math.sin(angle) * this.speed;
} else { } else {
// Jika tidak ada target, terbang lurus
this.vx = this.speed; this.vx = this.speed;
this.vy = 0; this.vy = 0;
} }
@ -1058,8 +1041,6 @@ function addShips() {
function startNewWave() { function startNewWave() {
// --- CHAOS WAVE MODE --- // --- CHAOS WAVE MODE ---
// Tidak ada pattern 'line', 'v', 'zigzag'.
// Kita hanya menentukan jumlah musuh yang akan muncul di wave ini.
let baseCount = 3; let baseCount = 3;
let scalingCount = Math.floor(game.level / 2); let scalingCount = Math.floor(game.level / 2);
@ -1116,17 +1097,14 @@ function spawnEnemyFromWave(wave) {
enemyShipArray.push(enemy); enemyShipArray.push(enemy);
} }
// --- CLASS ORB UPDATE: Support Type --- // --- CLASS ORB UPDATE: SUPPORT IMAGE & OUTLINE ---
class AbilityToken { class AbilityToken {
constructor(x, y) { constructor(x, y) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.width = 32; this.width = 40; // Ukuran sedikit diperbesar untuk gambar
this.height = 32; this.height = 40;
this.speed = 4; 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 // RAND: 0-0.33=Bomb, 0.33-0.66=Double, 0.66-1.0=Missile
let r = Math.random(); let r = Math.random();
if (r < 0.33) this.type = "bomb"; if (r < 0.33) this.type = "bomb";
@ -1136,31 +1114,44 @@ class AbilityToken {
draw() { draw() {
ctx.save(); ctx.save();
ctx.beginPath();
const cx = this.x + this.width / 2;
const cy = this.y + this.height / 2;
ctx.arc(cx, cy, 12, 0, Math.PI * 2);
// --- WARNA ORB BERDASARKAN TIPE --- if (this.type === "missile") {
const g = ctx.createRadialGradient(cx, cy, 0, cx, cy, 12); // --- METAL SLUG STYLE OUTLINE (KOTAK PUTIH TEBAL) ---
g.addColorStop(0, "#ffffff"); ctx.strokeStyle = "#ffffff";
ctx.lineWidth = 3;
ctx.strokeRect(this.x, this.y, this.width, this.height);
if (this.type === "bomb") { // Gambar Roket Piksel di dalamnya dengan sedikit padding
// Kuning (Bomb) const padding = 4;
g.addColorStop(0.5, "#ffff00"); ctx.drawImage(
g.addColorStop(1, "#ff9900"); missilePickupImg,
} else if (this.type === "double") { this.x + padding,
// Merah (Double Laser) this.y + padding,
g.addColorStop(0.5, "#ff3333"); this.width - padding * 2,
g.addColorStop(1, "#990000"); this.height - padding * 2
);
} else { } else {
// Biru Tua (Missile) // --- GAYA LAMA (ORB LINGKARAN) UNTUK BOMB & DOUBLE ---
g.addColorStop(0.5, "#0000ff"); ctx.beginPath();
g.addColorStop(1, "#00008b"); const cx = this.x + this.width / 2;
const cy = this.y + this.height / 2;
ctx.arc(cx, cy, 16, 0, Math.PI * 2); // Radius disesuaikan dengan ukuran baru
const g = ctx.createRadialGradient(cx, cy, 0, cx, cy, 16);
g.addColorStop(0, "#ffffff");
if (this.type === "bomb") {
g.addColorStop(0.5, "#ffff00"); // Kuning
g.addColorStop(1, "#ff9900");
} else if (this.type === "double") {
g.addColorStop(0.5, "#ff3333"); // Merah
g.addColorStop(1, "#990000");
}
ctx.fillStyle = g;
ctx.fill();
} }
ctx.fillStyle = g;
ctx.fill();
ctx.restore(); ctx.restore();
} }
@ -1170,8 +1161,8 @@ class AbilityToken {
} }
function maybeSpawnAbilityToken() { function maybeSpawnAbilityToken() {
// --- KEMBALI KE 0.2% (0.002) --- // --- 0.2% (0.002) ---
if (Math.random() < 0.003 && abilityTokens.length < 3) { if (Math.random() < 0.002 && abilityTokens.length < 3) {
const y = Math.random() * (canvasHeight - 120) + 60; const y = Math.random() * (canvasHeight - 120) + 60;
abilityTokens.push(new AbilityToken(canvasWidth + 40, y)); abilityTokens.push(new AbilityToken(canvasWidth + 40, y));
} }

BIN
img/Skills/missile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB