bot dificulty
This commit is contained in:
parent
e193c09280
commit
ca53b4d5b3
121
Script.js
121
Script.js
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user