bot dificulty

This commit is contained in:
Bluwww 2025-12-13 13:13:24 +07:00
parent e193c09280
commit ca53b4d5b3

121
Script.js
View File

@ -1,8 +1,7 @@
"use strict";
const DEBUG_HITBOX = true;
const DEBUG_HITBOX = false;
// ================== BGM SYSTEM ==================
const bgmList = [
{ normal: "music/Scary.mp3", gameover: "music/ScaryGO.mp3" },
{ normal: "music/Fear.mp3", gameover: "music/FearGO.mp3" },
@ -20,7 +19,6 @@ function pickRandomBGM() {
gameOverBGM.src = bgm.gameover;
}
// ================== GLOBALS ==================
var canvasWidth = 1280;
var canvasHeight = 650;
var c, ctx;
@ -116,7 +114,6 @@ window.onload = function () {
init();
};
// Init
function init() {
c = document.getElementById("canvas");
ctx = c.getContext("2d");
@ -160,7 +157,6 @@ function gameLoop(timestamp) {
requestAnimationFrame(gameLoop);
}
// ================== INPUT ==================
let gamePaused = false;
function keyDownPressed(e) {
@ -209,7 +205,6 @@ function fireBullet() {
);
}
// ================== CORE UPDATE / DRAW ==================
function clearGame() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
}
@ -217,17 +212,19 @@ function clearGame() {
function updateGame() {
game.frames++;
game.level = 1 + Math.floor(player1.score / 500);
game.speed = 1 + game.level * 0.1;
updateStarField();
addShips();
maybeSpawnAbilityToken();
// Auto-fire when holding space
if (keys.fire && !player1.dead && game.frames % 8 === 0) {
fireBullet();
}
// Player update / respawn
if (!player1.dead) {
player1.update();
if (player1.invincible > 0) player1.invincible--;
@ -260,17 +257,13 @@ function drawGame() {
if (currentPlanet) currentPlanet.draw();
// Particles (background layer)
drawParticles();
// Ability tokens
for (let i = 0; i < abilityTokens.length; i++) {
const t = abilityTokens[i];
t.draw();
t.update();
// Hitbox token sederhana (tidak perlu .getHitbox karena bentuknya simple)
// Tapi kita pakai logika bounding box manual di sini
if (
!player1.dead &&
Tabrakan(player1.getHitbox(), {
@ -293,14 +286,11 @@ function drawGame() {
}
}
// Player
if (!player1.dead) {
player1.draw();
// DEBUG: Lihat Hitbox Player
if (DEBUG_HITBOX) drawDebugHitbox(player1.getHitbox(), "lime");
}
// Enemies
for (let i = 0; i < enemyShipArray.length; i++) {
let s = enemyShipArray[i];
s.draw();
@ -314,16 +304,17 @@ function drawGame() {
continue;
}
// Enemy shooting (aimed, non-homing)
if (!player1.dead && Math.random() < 0.01 && s.x > player1.x + 50) {
const ex = s.x;
const ey = s.y + s.height / 2;
const px = player1.x + player1.width / 2;
const py = player1.y + player1.height / 2;
enemyBulletsArray.push(new EnemyBullet(ex, ey, px, py));
}
let shootChance = 0.02 + game.level * 0.003;
if (shootChance > 0.1) shootChance = 0.1;
if (!player1.dead && Math.random() < shootChance && s.x > player1.x + 50) {
const ex = s.x;
const ey = s.y + s.height / 2;
const px = player1.x + player1.width / 2;
const py = player1.y + player1.height / 2;
enemyBulletsArray.push(new EnemyBullet(ex, ey, px, py));
}
// Collision with player (hitbox vs hitbox)
if (!player1.dead && Tabrakan(player1.getHitbox(), s.getHitbox())) {
explosions.push(new Explosion(s.x + s.width / 2, s.y + s.height / 2));
createParticles(s.x + s.width / 2, s.y + s.height / 2, 20, "#ff6600");
@ -334,7 +325,6 @@ function drawGame() {
}
}
// Player bullets
for (let i = 0; i < missilesArray.length; i++) {
let m = missilesArray[i];
m.draw();
@ -346,23 +336,26 @@ function drawGame() {
for (let j = 0; j < enemyShipArray.length; j++) {
let en = enemyShipArray[j];
// PERBAIKAN: Gunakan .getHitbox() untuk kedua objek
if (Tabrakan(m.getHitbox(), en.getHitbox())) {
player1.score += 100;
explosion_enemy.currentTime = 0;
explosion_enemy.play();
explosions.push(
new Explosion(en.x + en.width / 2, en.y + en.height / 2)
);
en.health -= 100;
createParticles(
en.x + en.width / 2,
en.y + en.height / 2,
15,
5,
"#ff9900"
);
missilesArray.splice(i, 1);
enemyShipArray.splice(j, 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;
}
}
@ -378,7 +371,6 @@ function drawGame() {
}
}
// Enemy bullets
for (let i = 0; i < enemyBulletsArray.length; i++) {
let b = enemyBulletsArray[i];
b.draw();
@ -386,7 +378,6 @@ function drawGame() {
if (DEBUG_HITBOX) drawDebugHitbox(b.getHitbox(), "orange");
// PERBAIKAN: Gunakan b.getHitbox() agar collision lebih akurat (tidak kena glow)
if (!player1.dead && Tabrakan(b.getHitbox(), player1.getHitbox())) {
explosions.push(
new Explosion(
@ -417,7 +408,6 @@ function drawGame() {
}
}
// Explosions
for (let i = 0; i < explosions.length; i++) {
let ex = explosions[i];
ex.draw();
@ -444,6 +434,7 @@ function drawDebugHitbox(rect, color) {
function drawUI() {
drawNewText(" " + player1.score, 1400, 760, "white");
drawNewText("LVL " + game.level, 1400, 50, "#00ff00");
let livesText = "Lives: ";
for (let i = 0; i < player1.lives; i++) {
@ -488,7 +479,6 @@ class PlayerObject {
};
}
getHitbox() {
const h = this.height * 0.05;
@ -601,7 +591,6 @@ function handlePlayerHit() {
respawnCounter = 80 * 3;
}
// ================== TEXT ==================
function drawNewText(txt, x, y, color) {
ctx.font = "20px Arial";
ctx.fillStyle = color;
@ -657,7 +646,6 @@ function drawStarField() {
background1a.draw();
}
// panning
function updateCamera() {
const offset = player1.y + player1.height / 2 - canvasHeight / 2;
const target = -offset * 0.7;
@ -669,7 +657,6 @@ function updateCamera() {
cameraY += (clamped - cameraY) * 0.1;
}
//
class LaserBullet {
constructor(x, y) {
this.x = x;
@ -705,7 +692,6 @@ class LaserBullet {
}
}
//
class EnemyObj {
constructor(x, y, speed, img, pattern = "straight") {
this.x = x;
@ -720,7 +706,6 @@ class EnemyObj {
this.angle = 0;
}
getHitbox() {
const w = this.width * 0.55;
const h = this.height * 0.55;
@ -760,7 +745,6 @@ class EnemyBullet {
this.vy = (dy / len) * speed;
}
getHitbox() {
const padding = 1;
return {
@ -840,9 +824,7 @@ function addShips() {
} else {
if (enemyShipArray.length === 0) {
currentWave = null;
waveCooldown = 120;
game.level++;
game.speed += 0.1;
waveCooldown = Math.max(60, 120 - game.level * 2);
}
}
} else {
@ -855,10 +837,16 @@ function addShips() {
}
function startNewWave() {
const patterns = ["line", "v", "sine"];
const patterns = ["line", "v", "sine", "scatter"];
const pattern = patterns[Math.floor(Math.random() * patterns.length)];
const count = 4 + Math.floor(Math.random() * 4);
const spacing = 30 + Math.floor(Math.random() * 20);
let baseCount = 3;
let scalingCount = Math.floor(game.level / 2);
let count = Math.min(
15,
baseCount + scalingCount + Math.floor(Math.random() * 3)
);
let spacing = Math.max(20, 50 - game.level);
currentWave = {
pattern: pattern,
@ -871,7 +859,7 @@ function startNewWave() {
function spawnEnemyFromWave(wave) {
const baseY = canvasHeight / 2;
const spread = 200;
const spread = 250;
const index = wave.spawned;
let y = Math.random() * (canvasHeight - 120) + 60;
@ -880,30 +868,32 @@ function spawnEnemyFromWave(wave) {
y = baseY - spread + step * index;
} else if (wave.pattern === "v") {
const centerIndex = (wave.count - 1) / 2;
const offset = (index - centerIndex) * 40;
y = baseY + Math.abs(offset) * 1.5;
const offset = (index - centerIndex) * 50;
y = baseY + Math.abs(offset) * 1.2;
} else if (wave.pattern === "sine") {
const angle = (index / wave.count) * Math.PI * 2;
y = baseY + Math.sin(angle) * spread;
} else if (wave.pattern === "scatter") {
y = Math.random() * (canvasHeight - 150) + 75;
}
y = Math.max(40, Math.min(canvasHeight - 140, y));
y = Math.max(60, Math.min(canvasHeight - 100, y));
const randomShip = Math.floor(Math.random() * enemyImgArray.length);
const speed = 5 + Math.random() * 4;
const speed = 4 + Math.random() * 3 + game.level * 0.25;
enemyShipArray.push(
new EnemyObj(
canvasWidth + 50,
y,
speed + game.speed,
enemyImgArray[randomShip],
wave.pattern === "sine" ? "sine" : "straight"
)
let enemy = new EnemyObj(
canvasWidth + 50,
y,
speed,
enemyImgArray[randomShip],
wave.pattern === "sine" ? "sine" : "straight"
);
enemy.health = 100 + game.level * 25;
enemyShipArray.push(enemy);
}
// bombs
class AbilityToken {
constructor(x, y) {
this.x = x;
@ -946,7 +936,6 @@ function useAbility() {
explosion_enemy.currentTime = 0;
explosion_enemy.play();
// Explosions for all enemies
enemyShipArray.forEach((e) => {
explosions.push(new Explosion(e.x + e.width / 2, e.y + e.height / 2));
createParticles(e.x + e.width / 2, e.y + e.height / 2, 20, "#ff9900");
@ -959,7 +948,6 @@ function useAbility() {
damageFlash = 10;
}
// ================== PARTICLES ==================
class Particle {
constructor(x, y, color) {
this.x = x;
@ -1069,7 +1057,6 @@ class Explosion {
}
}
// GAME OVER / PAUSE
function drawGameOver() {
ctx.fillStyle = "rgba(53, 0, 0, 0.7)";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);