https://idle-breakout.github.io/
https://idle-breakout.github.io/
html.cafe - a realtime HTML editor and host
Edit the html in the top panel, see the result here.
Save your page and get a link to view it live, anywhere.
let LT_ANG;
let RT_ANG;
let RB_ANG;
let LB_ANG;
class BallHandler {
constructor() {
LT_ANG = atan2(-16, -32);
RT_ANG = atan2(-16, 32);
RB_ANG = atan2(16, 32);
LB_ANG = atan2(16, -32);
console.log(LT_ANG, RT_ANG, RB_ANG, LB_ANG);
this.balls = 0;
//this.displaying = 1;
// 24x24
this.basic = [];
this.basic.name = "Basic Ball";
this.basic.description = "Standard ball.\nBounces and stuff.";
this.basic.startPrice = BASIC_START_PRICE;
this.basic.price = BASIC_START_PRICE;
this.basic.growth = BASIC_GROWTH;
this.basic.damageDealt = 0;
this.basic.speedIncrease = 1;
this.basic.speedLimit = 10;
this.basic.powerIncrease = 1;
this.basic.size = 24;
this.basic.speed = 1;
this.basic.power = 1;
this.basic.graphic = BASIC_GRAPHIC;
this.plasma = [];
this.plasma.name = "Plasma Ball";
this.plasma.description = "25% splash dama\nge";
this.plasma.startPrice = PLASMA_START_PRICE;
this.plasma.price = PLASMA_START_PRICE;
this.plasma.growth = PLASMA_GROWTH;
this.plasma.damageDealt = 0;
//this.plasma.speedIncrease = 1;
//this.plasma.speedLimit = 10;
this.plasma.powerIncrease = 3;
this.plasma.range = 1;
this.plasma.size = 24;
this.plasma.speed = 2;
this.plasma.power = 3;
this.plasma.graphic = PLASMA_GRAPHIC;
this.sniper = [];
this.sniper.name = "Sniper Ball";
this.sniper.description = "Seeker boy";
this.sniper.startPrice = SNIPER_START_PRICE;
this.sniper.price = SNIPER_START_PRICE;
this.sniper.growth = SNIPER_GROWTH;
this.sniper.damageDealt = 0;
this.sniper.speedIncrease = 1;
this.sniper.speedLimit = 10;
this.sniper.powerIncrease = 5;
this.sniper.size = 24;
this.sniper.speed = 4; //30 (too fast);//4;
this.sniper.power = 5;
this.sniper.graphic = SNIPER_GRAPHIC;
this.scatter = [];
this.scatter.name = "Scatter Ball";
this.scatter.description = "Creates single hit\nballs with half dam-\nage";
this.scatter.startPrice = SCATTER_START_PRICE;
this.scatter.price = SCATTER_START_PRICE;
this.scatter.growth = SCATTER_GROWTH;
this.scatter.damageDealt = 0;
//this.scatter.speedIncrease = 1;
//this.scatter.speedLimit = 10;
this.scatter.powerIncrease = 10;
this.scatter.balls = 2;
this.scatter.size = 24;
this.scatter.speed = 3;
this.scatter.power = 10;
this.scatter.graphic = SCATTER_GRAPHIC;
this.mini = [];
this.mini.name = "Mini Ball";
this.mini.size = 16;
this.mini.graphic = MINI_GRAPHIC;
// 32x32
this.cannon = [];
this.cannon.name = "Cannon Ball";
this.cannon.description = "Doesn't stop for\nweaklings.";
this.cannon.startPrice = CANNON_START_PRICE;
this.cannon.price = CANNON_START_PRICE;
this.cannon.growth = CANNON_GROWTH;
this.cannon.damageDealt = 0;
this.cannon.speedIncrease = 2;
this.cannon.speedLimit = 16;
this.cannon.powerIncrease = 25;
this.cannon.size = 32;
this.cannon.speed = 4;
this.cannon.power = 50;
this.cannon.graphic = CANNON_GRAPHIC;
// 24x24
this.poison = [];
this.poison.name = "Poison Ball";
this.poison.description = "Hit bricks take\ndouble damage";
this.poison.startPrice = POISON_START_PRICE;
this.poison.price = POISON_START_PRICE;
this.poison.growth = POISON_GROWTH;
this.poison.damageDealt = 0;
this.poison.speedIncrease = 2;
this.poison.speedLimit = 15;
this.poison.powerIncrease = 5;
this.poison.size = 24;
this.poison.speed = 5;
this.poison.power = 5;
this.poison.graphic = POISON_GRAPHIC;
this.all = [
this.basic,
this.plasma,
this.sniper,
this.scatter,
this.cannon,
this.poison,
this.mini,
];
/*this.lastFrame = {
i: 0,
j: 0,
};*/
//this.br = 32 * 32;
}
canAddBall() {
return this.balls < maxBalls;
}
addBall(arr, ball) {
this.balls++;
arr.push(ball);
}
pushOut(ball, bl, distSq) {
let cx, cy;
do {
//console.log("Pushing out");
ball[0] -= ball[2] * 0.2;
ball[1] -= ball[3] * 0.2;
cx = max(min(ball[0], bl.x + 64), bl.x);
cy = max(min(ball[1], bl.y + 32), bl.y);
distSq = (ball[0] - cx) * (ball[0] - cx) +
(ball[1] - cy) * (ball[1] - cy);
} while (distSq === 0);
}
distSq(ball, block, y) {
let dx = ball[0] - (block.x !== undefined ? block.x : block);
dx *= dx;
let dy = ball[1] - (block.y !== undefined ? block.y : y);
dy *= dy;
return dx + dy;
}
containsPoint(block, p) {
return p.x >= block.x && p.x <= block.x + 64 && p.y >= block.y && p.y <= block.y + 32;
}
intersects(b, r, x, y) {
const dx = abs(x - b.x - 32);
const dy = abs(y - b.y - 16);
if (dx > 32 + r) return false;
if (dy > 16 + r) return false;
if (dx <= 32) return true;
if (dy <= 16) return true;
const cdsq = (dx - 32) * (dx - 32) +
(dy - 16) * (dy - 16);
return cdsq <= r * r;
}
update() {
for (const ballType of this.all) {
const r = ballType.size / 2;
const rSq = r * r;
let speed, dmg;
if (ballType === this.mini) {
speed = this.scatter.speed;
speed /= round(2);
dmg = this.scatter.power +
powerData[this.scatter.name].level * this.scatter.powerIncrease;
dmg *= round(0.5);
} else {
speed = ballType.speed +
(ballType.speedIncrease || 0) * speedData[ballType.name].level * 0.9;
dmg = ballType.power +
powerData[ballType.name].level * ballType.powerIncrease;
}
for (const ball of ballType) {
if (!ball) continue;
let [x, y, vx, vy] = ball;
ball[0] += ball[2] * speed;
ball[1] += ball[3] * speed;
const leftWall = x < r && vx < 0;
const rightWall = x > world.w - r && vx > 0;
const topWall = y < r && vy < 0;
const botWall = y > world.h - r && vy > 0;
if (leftWall || rightWall || topWall || botWall) {
ball[0] = leftWall ? r : rightWall ? world.w - r : ball[0];
ball[1] = topWall ? r : botWall ? world.h - r : ball[1];
if (ballType === this.sniper) {
let closest = 100000000;
let cx, cy;
for (const b of world.blocks) {
if (!b) continue;
const dsq = this.distSq(ball, b.x + 32, b.y + 16);
if (dsq < closest) {
closest = dsq;
cx = b.x + 32;
cy = b.y + 16;
}
}
const angRad = atan2(cy - ball[1], cx - ball[0]);
ball[2] = cos(angRad);
ball[3] = sin(angRad);
} else {
ball[2] = leftWall || rightWall ? -ball[2] : ball[2];
ball[3] = topWall || botWall ? -ball[3] : ball[3];
if (ballType === this.scatter) {
// TODO: Scatter logic
for (let i = 0; i < this.scatter.balls + speedData[this.scatter.name].level; i++) {
const idx = this.mini.length;
this.mini[idx] = [ball[0], ball[1], ball[2], ball[3], idx];
const r = random(HALF_PI);
let rad;
let mx = 1, my = 1;
if (leftWall) rad = -QUARTER_PI + r;
else if (topWall) rad = QUARTER_PI + r;
else if (botWall) rad = -QUARTER_PI * 3 + r;
else if (rightWall) rad = QUARTER_PI * 3 + r;
// TODO: Fix
this.mini[idx][2] = cos(rad);
this.mini[idx][3] = sin(rad);
}
}
}
} else {
for (const b of world.blocks) {
if (!b) continue;
const sqd = this.distSq(ball, b.x + 32, b.y + 16);
if (sqd > (r + 64) * (r + 64)) continue;
[x, y, vx, vy] = ball;
// Check
if (this.intersects(b, r, x, y)) {
const cx = b.x + 32;
const cy = b.y + 16;
const a = atan2(y - cy, x - cx);
if (a > LT_ANG && a < RT_ANG && vy > 0) {
vy = -vy;
} else if (a >= RT_ANG && a <= RB_ANG && vx < 0) {
vx = -vx;
} else if (a > RB_ANG && a < LB_ANG && vy < 0) {
vy = -vy;
} else if (a >= LB_ANG || a <= LT_ANG && vx > 0) {
vx = -vx;
}
if (ballType === this.poison) {
b.debuff = true;
}
}
if (ball[2] !== vx || ball[3] !== vy) {
if (ballType === this.mini) {
this.mini[ball[4]] = null;
}
// Collision
//const dmg = ballType.power + powerData[ballType.name].level * ballType.powerIncrease;
const dd = world.damage(b, dmg, ballType);
if (ballType === this.plasma) {
const range = 48 + 20 * (ballType.range + speedData[ballType.name].level);
const rangeSq = range * range;
for (let i = 0; i < world.blocks.length; i++) {
const bb = world.blocks[i];
if (!bb) continue;
// If commented out, applies splash damage to hit block too
// if (bb === b) continue;
const bx = max(min(ball[0], bb.x + 64), bb.x);
const by = max(min(ball[1], bb.y + 32), bb.y);
if (this.distSq(ball, bx, by) <= rangeSq) {
const splashDmg = ceil(dmg * 0.25);
const spdd = bb.health > splashDmg ? splashDmg : bb.health;
ballType.damageDealt += spdd;
world.damage(bb, splashDmg);
}
}
}
if (ballType !== this.cannon || b.health > 0) {
ball[2] = vx;
ball[3] = vy;
break;
}
}
}
}
}
}
}
render(time) {
for (let i = 0; i < this.all.length; i++) {
const type = this.all[i];
for (let j = 0; j < type.length; j++) {
//if (type === this.mini) console.log(type);
if (!type[j]) continue;
image(type.graphic, type[j][0], type[j][1]);
}
}
}
}