new level boss that fires 2 streams of small bullets that chase you

mod: add a CPT gun to your inventory that rewinds your history, reverts your health, position, velocity for 10 seconds
  I expect that spamming rewind has some overpowered combos.
  Let me know what you find, and your ideas on balance.
This commit is contained in:
landgreen
2020-12-21 12:47:07 -08:00
parent 732b13d8a4
commit 60e59a858a
10 changed files with 815 additions and 458 deletions

View File

@@ -274,6 +274,132 @@ const b = {
} }
} }
}, },
pulse(energy, angle = mech.angle) {
let best;
let explosionRange = 1560 * energy
let range = 3000
const path = [{
x: mech.pos.x + 20 * Math.cos(angle),
y: mech.pos.y + 20 * Math.sin(angle)
},
{
x: mech.pos.x + range * Math.cos(angle),
y: mech.pos.y + range * Math.sin(angle)
}
];
const vertexCollision = function(v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
}
results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
}
}
}
};
//check for collisions
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
if (mod.isPulseAim) { //find mobs in line of sight
let dist = 2200
for (let i = 0, len = mob.length; i < len; i++) {
const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position))
if (explosionRange < newDist &&
newDist < dist &&
Matter.Query.ray(map, path[0], mob[i].position).length === 0 &&
Matter.Query.ray(body, path[0], mob[i].position).length === 0) {
dist = newDist
best.who = mob[i]
path[path.length - 1] = mob[i].position
}
}
}
if (!best.who) {
vertexCollision(path[0], path[1], mob);
vertexCollision(path[0], path[1], map);
vertexCollision(path[0], path[1], body);
if (best.dist2 != Infinity) { //if hitting something
path[path.length - 1] = {
x: best.x,
y: best.y
};
}
}
if (best.who) b.explosion(path[1], explosionRange, true)
if (mod.isPulseStun) {
const range = 100 + 2000 * energy
for (let i = 0, len = mob.length; i < len; ++i) {
if (mob[i].alive && !mob[i].isShielded) {
dist = Vector.magnitude(Vector.sub(path[1], mob[i].position)) - mob[i].radius;
if (dist < range) mobs.statusStun(mob[i], 30 + Math.floor(energy * 60))
}
}
}
//draw laser beam
ctx.beginPath();
ctx.moveTo(path[0].x, path[0].y);
ctx.lineTo(path[1].x, path[1].y);
ctx.strokeStyle = "rgba(255,0,0,0.13)"
ctx.lineWidth = 60 * energy / 0.2
ctx.stroke();
ctx.strokeStyle = "rgba(255,0,0,0.2)"
ctx.lineWidth = 18
ctx.stroke();
ctx.strokeStyle = "#f00";
ctx.lineWidth = 4
ctx.stroke();
//draw little dots along the laser path
const sub = Vector.sub(path[1], path[0])
const mag = Vector.magnitude(sub)
for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) {
const dist = Math.random()
game.drawList.push({
x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5),
y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5),
radius: 1 + 4 * Math.random(),
color: "rgba(255,0,0,0.5)",
time: Math.floor(2 + 33 * Math.random() * Math.random())
});
}
},
grenade() { grenade() {
}, },
@@ -550,7 +676,7 @@ const b = {
} }
} }
slow(body, this.damageRadius) slow(body, this.damageRadius)
slow([player], this.damageRadius) if (!mod.isNeutronImmune) slow([player], this.damageRadius)
} }
} }
} }
@@ -591,6 +717,7 @@ const b = {
}, },
onEnd() { onEnd() {
b.explosion(this.position, this.explodeRad * size); //makes bullet do explosive damage at end b.explosion(this.position, this.explodeRad * size); //makes bullet do explosive damage at end
if (mod.fragments) b.targetedNail(this.position, mod.fragments * 5)
if (spawn) { if (spawn) {
for (let i = 0; i < mod.recursiveMissiles; i++) { for (let i = 0; i < mod.recursiveMissiles; i++) {
if (0.2 - 0.02 * i > Math.random()) { if (0.2 - 0.02 * i > Math.random()) {
@@ -724,8 +851,8 @@ const b = {
const q = Matter.Query.point(mob, this.position) const q = Matter.Query.point(mob, this.position)
for (let i = 0; i < q.length; i++) { for (let i = 0; i < q.length; i++) {
Matter.Body.setVelocity(q[i], { Matter.Body.setVelocity(q[i], {
x: q[i].velocity.x * 0.7, x: q[i].velocity.x * 0.6,
y: q[i].velocity.y * 0.7 y: q[i].velocity.y * 0.6
}); });
Matter.Body.setPosition(this, Vector.add(this.position, q[i].velocity)) //move with the medium Matter.Body.setPosition(this, Vector.add(this.position, q[i].velocity)) //move with the medium
let dmg = this.dmg / Math.min(10, q[i].mass) let dmg = this.dmg / Math.min(10, q[i].mass)
@@ -743,9 +870,9 @@ const b = {
this.cycle++ this.cycle++
const wiggleMag = (mech.crouch ? 6 : 12) * Math.cos(game.cycle * 0.09) const wiggleMag = (mech.crouch ? 6 : 12) * Math.cos(game.cycle * 0.09)
const wiggle = Vector.mult(transverse, wiggleMag * Math.cos(this.cycle * 0.36)) //+ wiggleMag * Math.cos(game.cycle * 0.3)) const wiggle = Vector.mult(transverse, wiggleMag * Math.cos(this.cycle * 0.36)) //+ wiggleMag * Math.cos(game.cycle * 0.3))
// const velocity = Vector.mult(player.velocity, 0.25) //move with player const velocity = Vector.mult(player.velocity, 0.3) //move with player
// Matter.Body.setPosition(this, Vector.add(velocity, Vector.add(this.position, wiggle))) Matter.Body.setPosition(this, Vector.add(velocity, Vector.add(this.position, wiggle)))
Matter.Body.setPosition(this, Vector.add(this.position, wiggle)) // Matter.Body.setPosition(this, Vector.add(this.position, wiggle))
} }
} }
}); });
@@ -2089,11 +2216,11 @@ const b = {
const DIST = Vector.magnitude(sub); const DIST = Vector.magnitude(sub);
const unit = Vector.normalise(sub) const unit = Vector.normalise(sub)
if (DIST < mod.isPlasmaRange * 450 && mech.energy > this.drainThreshold) { if (DIST < mod.isPlasmaRange * 450 && mech.energy > this.drainThreshold) {
mech.energy -= 0.0012; mech.energy -= 0.005;
if (mech.energy < 0) { // if (mech.energy < 0) {
mech.fieldCDcycle = mech.cycle + 120; // mech.fieldCDcycle = mech.cycle + 120;
mech.energy = 0; // mech.energy = 0;
} // }
//calculate laser collision //calculate laser collision
let best; let best;
let range = mod.isPlasmaRange * (120 + 300 * Math.sqrt(Math.random())) let range = mod.isPlasmaRange * (120 + 300 * Math.sqrt(Math.random()))
@@ -2164,7 +2291,7 @@ const b = {
y: best.y y: best.y
}; };
if (best.who.alive) { if (best.who.alive) {
const dmg = 0.8 * b.dmgScale; //********** SCALE DAMAGE HERE ********************* const dmg = 0.6 * b.dmgScale; //********** SCALE DAMAGE HERE *********************
best.who.damage(dmg); best.who.damage(dmg);
best.who.locatePlayer(); best.who.locatePlayer();
//push mobs away //push mobs away
@@ -3612,13 +3739,13 @@ const b = {
y: mech.pos.y + 3000 * Math.sin(mech.angle) y: mech.pos.y + 3000 * Math.sin(mech.angle)
}, dmg, 0, true); }, dmg, 0, true);
for (let i = 1; i < len; i++) { for (let i = 1; i < len; i++) {
const history = mech.history[(mech.cycle - i * spacing) % 300] const history = mech.history[(mech.cycle - i * spacing) % 600]
b.laser({ b.laser({
x: history.position.x + 20 * Math.cos(history.angle), x: history.position.x + 20 * Math.cos(history.angle),
y: history.position.y + 20 * Math.sin(history.angle) y: history.position.y + 20 * Math.sin(history.angle) - mech.yPosDifference
}, { }, {
x: history.position.x + 3000 * Math.cos(history.angle), x: history.position.x + 3000 * Math.cos(history.angle),
y: history.position.y + 3000 * Math.sin(history.angle) y: history.position.y + 3000 * Math.sin(history.angle) - mech.yPosDifference
}, dmg, 0, true); }, dmg, 0, true);
} }
ctx.stroke(); ctx.stroke();
@@ -3642,130 +3769,38 @@ const b = {
}, },
}, },
], ],
pulse(energy, angle = mech.angle) { gunRewind: { //this gun is added with a mod
let best; name: "CPT gun",
let explosionRange = 1560 * energy description: "use <strong class='color-f'>energy</strong> to <strong>rewind</strong> your <strong class='color-h'>health</strong>, <strong>velocity</strong>,<br> and <strong>position</strong> up to <strong>10</strong> seconds",
let range = 3000 ammo: 0,
const path = [{ ammoPack: Infinity,
x: mech.pos.x + 20 * Math.cos(angle), have: false,
y: mech.pos.y + 20 * Math.sin(angle) isRewinding: false,
}, lastFireCycle: 0,
{ holdCount: 0,
x: mech.pos.x + range * Math.cos(angle), fire() {
y: mech.pos.y + range * Math.sin(angle) if (this.lastFireCycle === mech.cycle - 1) { //button has been held down
} this.rewindCount += 7;
]; const DRAIN = 0.01
const vertexCollision = function(v1, v1End, domain) { if (this.rewindCount > 599 || mech.energy < DRAIN) {
for (let i = 0; i < domain.length; ++i) { this.rewindCount = 0;
let vertices = domain[i].vertices; mech.resetHistory();
const len = vertices.length - 1; mech.fireCDcycle = mech.cycle + Math.floor(60 * b.fireCD); // cool down
for (let j = 0; j < len; j++) { } else {
results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); mech.energy -= DRAIN
if (results.onLine1 && results.onLine2) { mech.immuneCycle = mech.cycle + 5; //player is immune to collision damage for 5 cycles
const dx = v1.x - results.x; let history = mech.history[(mech.cycle - this.rewindCount) % 600]
const dy = v1.y - results.y; Matter.Body.setPosition(player, history.position);
const dist2 = dx * dx + dy * dy; Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y });
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { if (mech.health !== history.health) {
best = { mech.health = history.health
x: results.x, mech.displayHealth();
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
}
results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
} }
} }
} else { //button is held the first time
this.rewindCount = 0;
} }
}; this.lastFireCycle = mech.cycle;
//check for collisions
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
if (mod.isPulseAim) { //find mobs in line of sight
let dist = 2200
for (let i = 0, len = mob.length; i < len; i++) {
const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position))
if (explosionRange < newDist &&
newDist < dist &&
Matter.Query.ray(map, path[0], mob[i].position).length === 0 &&
Matter.Query.ray(body, path[0], mob[i].position).length === 0) {
dist = newDist
best.who = mob[i]
path[path.length - 1] = mob[i].position
}
}
}
if (!best.who) {
vertexCollision(path[0], path[1], mob);
vertexCollision(path[0], path[1], map);
vertexCollision(path[0], path[1], body);
if (best.dist2 != Infinity) { //if hitting something
path[path.length - 1] = {
x: best.x,
y: best.y
};
}
}
if (best.who) b.explosion(path[1], explosionRange, true)
if (mod.isPulseStun) {
const range = 100 + 2000 * energy
for (let i = 0, len = mob.length; i < len; ++i) {
if (mob[i].alive && !mob[i].isShielded) {
dist = Vector.magnitude(Vector.sub(path[1], mob[i].position)) - mob[i].radius;
if (dist < range) mobs.statusStun(mob[i], 30 + Math.floor(energy * 60))
}
}
}
//draw laser beam
ctx.beginPath();
ctx.moveTo(path[0].x, path[0].y);
ctx.lineTo(path[1].x, path[1].y);
ctx.strokeStyle = "rgba(255,0,0,0.13)"
ctx.lineWidth = 60 * energy / 0.2
ctx.stroke();
ctx.strokeStyle = "rgba(255,0,0,0.2)"
ctx.lineWidth = 18
ctx.stroke();
ctx.strokeStyle = "#f00";
ctx.lineWidth = 4
ctx.stroke();
//draw little dots along the laser path
const sub = Vector.sub(path[1], path[0])
const mag = Vector.magnitude(sub)
for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) {
const dist = Math.random()
game.drawList.push({
x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5),
y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5),
radius: 1 + 4 * Math.random(),
color: "rgba(255,0,0,0.5)",
time: Math.floor(2 + 33 * Math.random() * Math.random())
});
} }
} }
}; };

View File

@@ -596,7 +596,17 @@ const game = {
} }
if (mod.isEndLevelPowerUp) { if (mod.isEndLevelPowerUp) {
for (let i = 0; i < powerUp.length; i++) powerUp[i].effect(); for (let i = 0; i < powerUp.length; i++) {
if (powerUp[i].name === "mod") {
mod.giveMod()
} else if (powerUp[i].name === "gun") {
if (!mod.isOneGun) b.giveGuns("random")
} else if (powerUp[i].name === "field") {
if (mech.fieldMode === 0) mech.setField(Math.ceil(Math.random() * (mech.fieldUpgrades.length - 1))) //pick a random field, but not field 0
} else {
powerUp[i].effect();
}
}
} }
powerUps.totalPowerUps = powerUp.length powerUps.totalPowerUps = powerUp.length
@@ -761,24 +771,24 @@ const game = {
x: 0, x: 0,
y: 40 y: 40
}); });
if ((playerHead.position.y - player.position.y) > 0) { // if ((playerHead.position.y - player.position.y) > 0) {
Matter.Body.translate(playerHead, { // Matter.Body.translate(playerHead, {
x: 0, // x: 0,
y: 40 // y: 40
}); // });
if ((playerHead.position.y - player.position.y) > 0) { // if ((playerHead.position.y - player.position.y) > 0) {
Matter.Body.translate(playerHead, { // Matter.Body.translate(playerHead, {
x: 0, // x: 0,
y: 40 // y: 40
}); // });
if ((playerHead.position.y - player.position.y) > 0) { // if ((playerHead.position.y - player.position.y) > 0) {
Matter.Body.translate(playerHead, { // Matter.Body.translate(playerHead, {
x: 0, // x: 0,
y: 40 // y: 40
}); // });
} // }
} // }
} // }
} else if (mech.crouch && ((playerHead.position.y - player.position.y) > 10)) { } else if (mech.crouch && ((playerHead.position.y - player.position.y) > 10)) {
Matter.Body.translate(playerHead, { Matter.Body.translate(playerHead, {
x: 0, x: 0,

View File

@@ -787,7 +787,7 @@ window.addEventListener("keydown", function(event) {
break break
} }
if (game.testing) { if (game.testing) {
switch (event.key) { switch (event.key.toLowerCase()) {
case "o": case "o":
game.isAutoZoom = false; game.isAutoZoom = false;
game.zoomScale /= 0.9; game.zoomScale /= 0.9;
@@ -1081,4 +1081,24 @@ function cycle() {
// loop[i]() // loop[i]()
// } // }
} }
} }
//display console logs in game
// function proxy(context, method, message) {
// return function() {
// // method.apply(context, [message].concat(Array.prototype.slice.apply(arguments)))
// game.makeTextLog(arguments[0], 300)
// }
// }
// console.log = proxy(console, console.log, 'n-gon: ')
// console.error = proxy(console, console.error, 'Error:')
// console.warn = proxy(console, console.warn, 'Warning:')
// let's test
// console.log('im from console.log', level, 2, 3);
// console.error('im from console.error', 1, 2, 3);
// console.warn('im from console.warn', 1, 2, 3);

View File

@@ -13,17 +13,15 @@ const level = {
start() { start() {
if (level.levelsCleared === 0) { //this code only runs on the first level if (level.levelsCleared === 0) { //this code only runs on the first level
// game.enableConstructMode() //used to build maps in testing mode // game.enableConstructMode() //used to build maps in testing mode
// level.difficultyIncrease(19) // level.difficultyIncrease(1)
// game.zoomScale = 1000; // game.zoomScale = 1000;
// game.setZoom(); // game.setZoom();
// mech.setField("plasma torch") // mech.setField("plasma torch")
// b.giveGuns("wave beam") // b.giveGuns("wave beam")
// mod.giveMod("micro-extruder") // mod.giveMod("CPT reversal")
// mod.giveMod("piezoelectricity") // mod.giveMod("CPT gun")
// for (let i = 0; i < 15; i++) mod.giveMod("plasma jet") // for (let i = 0; i < 15; i++) mod.giveMod("plasma jet")
level.intro(); //starting level level.intro(); //starting level
// level.testing(); //not in rotation // level.testing(); //not in rotation
// level.finalBoss() //final boss level // level.finalBoss() //final boss level
@@ -163,12 +161,12 @@ const level = {
// spawn.launcherBoss(1200, -500) // spawn.launcherBoss(1200, -500)
// spawn.laserTargetingBoss(1600, -400) // spawn.laserTargetingBoss(1600, -400)
// spawn.striker(1600, -500) // spawn.striker(1600, -500)
spawn.shooter(1700, -120) // spawn.shooter(1700, -120)
// spawn.bomberBoss(1400, -500) // spawn.bomberBoss(1400, -500)
// spawn.sniper(1800, -120) // spawn.sniper(1800, -120)
// spawn.cellBossCulture(1600, -500) // spawn.cellBossCulture(1600, -500)
// spawn.spiderBoss(1600, -500) spawn.streamBoss(1600, -500)
// spawn.laser(1200, -500) // spawn.beamer(1200, -500)
// spawn.shield(mob[mob.length - 1], 1800, -120, 1); // spawn.shield(mob[mob.length - 1], 1800, -120, 1);
// spawn.nodeBoss(1200, -500, "launcher") // spawn.nodeBoss(1200, -500, "launcher")
@@ -793,9 +791,9 @@ const level = {
powerUps.spawnBossPowerUp(-125, -1760); powerUps.spawnBossPowerUp(-125, -1760);
} else { } else {
if (Math.random() < 0.5) { if (Math.random() < 0.5) {
spawn.randomLevelBoss(700, -1550, ["shooterBoss", "launcherBoss", "laserTargetingBoss"]); spawn.randomLevelBoss(700, -1550, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]);
} else { } else {
spawn.randomLevelBoss(675, -2775, ["shooterBoss", "launcherBoss", "laserTargetingBoss"]); spawn.randomLevelBoss(675, -2775, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]);
} }
} }
powerUps.addRerollToLevel() //needs to run after mobs are spawned powerUps.addRerollToLevel() //needs to run after mobs are spawned
@@ -942,7 +940,7 @@ const level = {
spawn.randomMob(3600, 1725, 0.9); spawn.randomMob(3600, 1725, 0.9);
spawn.randomMob(4100, 1225, 0.9); spawn.randomMob(4100, 1225, 0.9);
spawn.randomMob(2825, 400, 0.9); spawn.randomMob(2825, 400, 0.9);
if (game.difficulty > 3) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss"]); if (game.difficulty > 3) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]);
powerUps.addRerollToLevel() //needs to run after mobs are spawned powerUps.addRerollToLevel() //needs to run after mobs are spawned
}, },
satellite() { satellite() {

View File

@@ -481,7 +481,7 @@ const mobs = {
} }
} }
}; };
if (this.seePlayer.recall) { if (this.seePlayer.recall && !this.isSlowed) {
this.torque = this.lookTorque * this.inertia * 2; this.torque = this.lookTorque * this.inertia * 2;
const seeRange = 2500; const seeRange = 2500;

View File

@@ -386,22 +386,6 @@ const mod = {
mod.throwChargeRate = 1 mod.throwChargeRate = 1
} }
}, },
{
name: "fragmentation",
description: "detonation or collisions with mobs eject <strong>nails</strong><br><em>blocks, rail gun, grenades, shotgun slugs</em>",
maxCount: 9,
count: 0,
allowed() {
return (mod.haveGunCheck("grenades") && !mod.isNeutronBomb) || mod.haveGunCheck("rail gun") || (mod.haveGunCheck("shotgun") && mod.isSlugShot) || mod.throwChargeRate > 1
},
requires: "grenades, rail gun, shotgun slugs, or mass driver",
effect() {
mod.fragments++
},
remove() {
mod.fragments = 0
}
},
{ {
name: "ammonium nitrate", name: "ammonium nitrate",
description: "increase <strong class='color-e'>explosive</strong> <strong class='color-d'>damage</strong> by <strong>20%</strong><br>increase <strong class='color-e'>explosive</strong> <strong>radius</strong> by <strong>20%</strong>", description: "increase <strong class='color-e'>explosive</strong> <strong class='color-d'>damage</strong> by <strong>20%</strong><br>increase <strong class='color-e'>explosive</strong> <strong>radius</strong> by <strong>20%</strong>",
@@ -569,7 +553,7 @@ const mod = {
b.nailBot(); b.nailBot();
}, },
remove() { remove() {
mod.nailBotCount = 0; mod.nailBotCount -= this.count;
} }
}, },
{ {
@@ -608,7 +592,7 @@ const mod = {
b.foamBot(); b.foamBot();
}, },
remove() { remove() {
mod.foamBotCount = 0; mod.foamBotCount -= this.count;
} }
}, },
{ {
@@ -647,7 +631,7 @@ const mod = {
b.boomBot(); b.boomBot();
}, },
remove() { remove() {
mod.boomBotCount = 0; mod.boomBotCount -= this.count;
} }
}, },
{ {
@@ -686,7 +670,7 @@ const mod = {
b.laserBot(); b.laserBot();
}, },
remove() { remove() {
mod.laserBotCount = 0; mod.laserBotCount -= this.count;
} }
}, },
{ {
@@ -725,7 +709,7 @@ const mod = {
mod.orbitBotCount++; mod.orbitBotCount++;
}, },
remove() { remove() {
mod.orbitBotCount = 0; mod.orbitBotCount -= this.count;
} }
}, },
{ {
@@ -1074,9 +1058,9 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { //&& (mech.fieldUpgrades[mech.fieldMode].name !== "nano-scale manufacturing" || mech.maxEnergy > 1) allowed() { //&& (mech.fieldUpgrades[mech.fieldMode].name !== "nano-scale manufacturing" || mech.maxEnergy > 1)
return mech.maxEnergy > 0.99 && mech.fieldUpgrades[mech.fieldMode].name !== "standing wave harmonics" && !mod.isEnergyHealth return mech.maxEnergy > 0.99 && mech.fieldUpgrades[mech.fieldMode].name !== "standing wave harmonics" && !mod.isEnergyHealth && !mod.isRewindGun
}, },
requires: "standing wave, mass-energy, piezoelectricity, max energy reduction", requires: "not standing wave, mass-energy, piezo, max energy reduction, CPT gun",
effect() { effect() {
mod.isRewindAvoidDeath = true; mod.isRewindAvoidDeath = true;
}, },
@@ -1135,13 +1119,13 @@ const mod = {
}, },
{ {
name: "ground state", name: "ground state",
description: "reduce <strong class='color-harm'>harm</strong> by <strong>66%</strong><br>you <strong>no longer</strong> passively regenerate <strong class='color-f'>energy</strong>", description: "reduce <strong class='color-harm'>harm</strong> by <strong>60%</strong><br>you <strong>no longer</strong> passively regenerate <strong class='color-f'>energy</strong>",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.isPiezo && mod.energyRegen !== 0.004 return (mod.iceEnergy || mod.isWormholeEnergy || mod.isPiezo || mod.isRailEnergyGain) && mod.energyRegen !== 0.004
}, },
requires: "piezoelectricity, not time crystals", requires: "piezoelectricity, Penrose, half-wave, or thermoelectric, but not time crystals",
effect: () => { effect: () => {
mod.energyRegen = 0; mod.energyRegen = 0;
mech.fieldRegen = mod.energyRegen; mech.fieldRegen = mod.energyRegen;
@@ -1157,7 +1141,7 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return !mod.isEnergyLoss && !mod.isPiezo && !mod.isRewindAvoidDeath && !mod.isSpeedHarm && mech.fieldUpgrades[mech.fieldMode].name !== "negative mass field" return !mod.isEnergyLoss && !mod.isPiezo && !mod.isRewindAvoidDeath && !mod.isRewindGun && !mod.isSpeedHarm && mech.fieldUpgrades[mech.fieldMode].name !== "negative mass field"
}, },
requires: "not exothermic process, piezoelectricity, CPT, 1st law, negative mass", requires: "not exothermic process, piezoelectricity, CPT, 1st law, negative mass",
effect: () => { effect: () => {
@@ -1328,13 +1312,13 @@ const mod = {
}, },
{ {
name: "transceiver chip", name: "transceiver chip",
description: "at the end of each <strong>level</strong><br>gain the full <strong>effect</strong> of unused <strong>power ups</strong>", description: "unused <strong>power ups</strong> at the end of each <strong>level</strong><br>are still activated <em>(selections are random)</em>",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.isArmorFromPowerUps return mod.isArmorFromPowerUps
}, },
requires: "crystallized armor", requires: "inductive coupling",
effect() { effect() {
mod.isEndLevelPowerUp = true; mod.isEndLevelPowerUp = true;
}, },
@@ -1535,7 +1519,7 @@ const mod = {
if (mod.mods[i].count > 0) have.push(i) if (mod.mods[i].count > 0) have.push(i)
} }
const choose = have[Math.floor(Math.random() * have.length)] const choose = have[Math.floor(Math.random() * have.length)]
game.makeTextLog(`<div class='circle mod'></div> &nbsp; <strong>${mod.mods[choose].name}</strong> removed by reallocation`, 300) game.makeTextLog(`<div class='circle mod'></div> &nbsp; <strong>${mod.mods[choose].name}</strong> removed by monte carlo experiment`, 300)
for (let i = 0; i < mod.mods[choose].count; i++) { for (let i = 0; i < mod.mods[choose].count; i++) {
powerUps.spawn(mech.pos.x, mech.pos.y, "mod"); powerUps.spawn(mech.pos.x, mech.pos.y, "mod");
} }
@@ -1935,10 +1919,50 @@ const mod = {
//************************************************** //**************************************************
//************************************************** gun //************************************************** gun
//************************************************** mods //************************************************** mods
//************************************************** //**************************************************
{
name: "CPT gun",
description: "adds the <strong>CPT</strong> <strong class='color-g'>gun</strong> to your inventory<br>it <strong>rewinds</strong> your <strong class='color-h'>health</strong>, <strong>velocity</strong>, and <strong>position</strong>",
isGunMod: true,
maxCount: 1,
count: 0,
allowed() {
return (mod.totalBots() > 5 || mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" || mech.fieldUpgrades[mech.fieldMode].name === "plasma torch" || mech.fieldUpgrades[mech.fieldMode].name === "pilot wave") && !mod.isEnergyHealth && !mod.isRewindAvoidDeath
},
requires: "bots > 5, plasma torch, nano-scale, pilot wave, not mass-energy equivalence, CPT",
effect() {
mod.isRewindGun = true
b.guns.push(b.gunRewind)
b.giveGuns("CPT gun");
},
remove() {
if (mod.isRewindGun) {
for (let i = 0; i < b.guns.length; i++) {
if (b.guns[i].name === "CPT gun") {
for (let j = 0; j < b.inventory.length; j++) {
if (b.inventory[j] === i) {
b.inventory.splice(j, 1)
break
}
}
if (b.inventory.length) {
b.activeGun = b.inventory[0];
} else {
b.activeGun = null;
}
game.makeGunHUD();
b.guns.splice(i, 1) //also remove CPT gun from gun pool array
break
}
}
mod.isRewindGun = false
}
}
},
{ {
name: "incendiary ammunition", name: "incendiary ammunition",
description: "<strong>bullets</strong> are loaded with <strong class='color-e'>explosives</strong><br><em style = 'font-size: 90%'>nail gun, shotgun, super balls, drones</em>", description: "some <strong>bullets</strong> are loaded with <strong class='color-e'>explosives</strong><br><em style = 'font-size: 90%'>nail gun, shotgun, super balls, drones</em>",
isGunMod: true, isGunMod: true,
maxCount: 1, maxCount: 1,
count: 0, count: 0,
@@ -1953,9 +1977,26 @@ const mod = {
mod.isIncendiary = false; mod.isIncendiary = false;
} }
}, },
{
name: "fragmentation",
description: "some <strong class='color-e'>detonations</strong> and collisions eject <strong>nails</strong><br><em style = 'font-size: 90%'>blocks, rail gun, grenades, missiles, shotgun slugs</em>",
isGunMod: true,
maxCount: 9,
count: 0,
allowed() {
return (mod.haveGunCheck("grenades") && !mod.isNeutronBomb) || mod.haveGunCheck("missiles") || mod.haveGunCheck("rail gun") || (mod.haveGunCheck("shotgun") && mod.isSlugShot) || mod.throwChargeRate > 1
},
requires: "grenades, missiles, rail gun, shotgun slugs, or mass driver",
effect() {
mod.fragments++
},
remove() {
mod.fragments = 0
}
},
{ {
name: "Lorentzian topology", name: "Lorentzian topology",
description: "<strong>bullets</strong> last <strong>30% longer</strong><br><em style = 'font-size: 83%'>drones, spores, missiles, foam, wave, ice IX, neutron</em>", description: "some <strong>bullets</strong> last <strong>30% longer</strong><br><em style = 'font-size: 83%'>drones, spores, missiles, foam, wave, ice IX, neutron</em>",
isGunMod: true, isGunMod: true,
maxCount: 3, maxCount: 3,
count: 0, count: 0,
@@ -2783,7 +2824,7 @@ const mod = {
}, },
{ {
name: "colloidal foam", name: "colloidal foam",
description: "increase <strong>foam</strong> <strong class='color-d'>damage</strong> by <strong>200%</strong><br><strong>foam</strong> dissipates <strong>40%</strong> faster", description: "increase <strong>foam</strong> <strong class='color-d'>damage</strong> by <strong>366%</strong><br><strong>foam</strong> dissipates <strong>40%</strong> faster",
isGunMod: true, isGunMod: true,
maxCount: 1, maxCount: 1,
count: 0, count: 0,
@@ -3259,8 +3300,8 @@ const mod = {
mod.giveMod("orbital-bot upgrade") mod.giveMod("orbital-bot upgrade")
mod.setModToNonRefundable("orbital-bot upgrade") mod.setModToNonRefundable("orbital-bot upgrade")
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
b.orbitalBot() b.orbitBot()
mod.orbitalBotCount++; mod.orbitBotCount++;
} }
}) })
//choose random function from the array and run it //choose random function from the array and run it
@@ -3370,6 +3411,23 @@ const mod = {
mod.isFreezeMobs = false mod.isFreezeMobs = false
} }
}, },
// {
// name: "thermal reservoir",
// description: "increase your <strong class='color-plasma'>plasma</strong> <strong class='color-d'>damage</strong> by <strong>100%</strong><br><strong class='color-plasma'>plasma</strong> temporarily lowers health not <strong class='color-f'>energy</strong>",
// isFieldMod: true,
// maxCount: 1,
// count: 0,
// allowed() {
// return mech.fieldUpgrades[mech.fieldMode].name === "plasma torch" && !mod.isEnergyHealth
// },
// requires: "plasma torch, not mass-energy equivalence",
// effect() {
// mod.isPlasmaRange += 0.27;
// },
// remove() {
// mod.isPlasmaRange = 1;
// }
// },
{ {
name: "plasma jet", name: "plasma jet",
description: "increase <strong class='color-plasma'>plasma</strong> <strong>torch's</strong> range by <strong>27%</strong>", description: "increase <strong class='color-plasma'>plasma</strong> <strong>torch's</strong> range by <strong>27%</strong>",
@@ -3874,5 +3932,7 @@ const mod = {
isRewindBot: null, isRewindBot: null,
isRewindGrenade: null, isRewindGrenade: null,
isExtruder: null, isExtruder: null,
isEndLevelPowerUp: null isEndLevelPowerUp: null,
isRewindGun: null
} }

View File

@@ -103,6 +103,7 @@ const mech = {
x: 0, x: 0,
y: 0 y: 0
}, },
yPosDifference: 24.285923217549026, //player.position.y - mech.pos.y
Sy: 0, //adds a smoothing effect to vertical only Sy: 0, //adds a smoothing effect to vertical only
Vx: 0, Vx: 0,
Vy: 0, Vy: 0,
@@ -135,11 +136,11 @@ const mech = {
transY: 0, transY: 0,
history: [], //tracks the last second of player position history: [], //tracks the last second of player position
resetHistory() { resetHistory() {
for (let i = 0; i < 300; i++) { //reset history for (let i = 0; i < 600; i++) { //reset history
mech.history[i] = { mech.history[i] = {
position: { position: {
x: mech.pos.x, x: player.position.x,
y: mech.pos.y, y: player.position.y,
}, },
velocity: { velocity: {
x: player.velocity.x, x: player.velocity.x,
@@ -159,10 +160,10 @@ const mech = {
//tracks the last second of player information //tracks the last second of player information
// console.log(mech.history) // console.log(mech.history)
mech.history.splice(mech.cycle % 300, 1, { mech.history.splice(mech.cycle % 600, 1, {
position: { position: {
x: mech.pos.x, x: player.position.x,
y: mech.pos.y, y: player.position.y,
}, },
velocity: { velocity: {
x: player.velocity.x, x: player.velocity.x,
@@ -461,14 +462,14 @@ const mech = {
if (mod.isBotArmor) dmg *= 0.97 ** mod.totalBots() if (mod.isBotArmor) dmg *= 0.97 ** mod.totalBots()
if (mod.isHarmArmor && mech.lastHarmCycle + 600 > mech.cycle) dmg *= 0.33; if (mod.isHarmArmor && mech.lastHarmCycle + 600 > mech.cycle) dmg *= 0.33;
if (mod.isNoFireDefense && mech.cycle > mech.fireCDcycle + 120) dmg *= 0.6 if (mod.isNoFireDefense && mech.cycle > mech.fireCDcycle + 120) dmg *= 0.6
if (mod.energyRegen === 0) dmg *= 0.33 //0.22 + 0.78 * mech.energy //77% damage reduction at zero energy if (mod.energyRegen === 0) dmg *= 0.4
if (mod.isTurret && mech.crouch) dmg *= 0.5; if (mod.isTurret && mech.crouch) dmg *= 0.5;
if (mod.isEntanglement && b.inventory[0] === b.activeGun) { if (mod.isEntanglement && b.inventory[0] === b.activeGun) {
for (let i = 0, len = b.inventory.length; i < len; i++) dmg *= 0.87 // 1 - 0.15 for (let i = 0, len = b.inventory.length; i < len; i++) dmg *= 0.87 // 1 - 0.15
} }
return dmg return dmg
}, },
rewind(steps) { rewind(steps) { // mech.rewind(Math.floor(Math.min(599, 137 * mech.energy)))
if (mod.isRewindGrenade) { if (mod.isRewindGrenade) {
for (let i = 1, len = Math.floor(2 + steps / 40); i < len; i++) { for (let i = 1, len = Math.floor(2 + steps / 40); i < len; i++) {
b.grenade(Vector.add(mech.pos, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }), -i * Math.PI / len) //fire different angles for each grenade b.grenade(Vector.add(mech.pos, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }), -i * Math.PI / len) //fire different angles for each grenade
@@ -494,7 +495,7 @@ const mech = {
} }
} }
} }
let history = mech.history[(mech.cycle - steps) % 300] let history = mech.history[(mech.cycle - steps) % 600]
Matter.Body.setPosition(player, history.position); Matter.Body.setPosition(player, history.position);
Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y });
// move bots to follow player // move bots to follow player
@@ -529,7 +530,7 @@ const mech = {
ctx.scale(game.zoom / game.edgeZoomOutSmooth, game.zoom / game.edgeZoomOutSmooth); //zoom in once centered ctx.scale(game.zoom / game.edgeZoomOutSmooth, game.zoom / game.edgeZoomOutSmooth); //zoom in once centered
ctx.translate(-canvas.width2 + mech.transX, -canvas.height2 + mech.transY); //translate ctx.translate(-canvas.width2 + mech.transX, -canvas.height2 + mech.transY); //translate
for (let i = 1; i < steps; i++) { for (let i = 1; i < steps; i++) {
history = mech.history[(mech.cycle - i) % 300] history = mech.history[(mech.cycle - i) % 600]
mech.pos.x = history.position.x mech.pos.x = history.position.x
mech.pos.y = history.position.y mech.pos.y = history.position.y
mech.draw(); mech.draw();
@@ -547,7 +548,7 @@ const mech = {
if (mod.isRewindBot) { if (mod.isRewindBot) {
const len = steps * 0.042 * mod.isRewindBot const len = steps * 0.042 * mod.isRewindBot
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
const where = mech.history[Math.abs(mech.cycle - i * 40) % 300].position //spread out spawn locations along past history const where = mech.history[Math.abs(mech.cycle - i * 40) % 600].position //spread out spawn locations along past history
b.randomBot({ b.randomBot({
x: where.x + 100 * (Math.random() - 0.5), x: where.x + 100 * (Math.random() - 0.5),
y: where.y + 100 * (Math.random() - 0.5) y: where.y + 100 * (Math.random() - 0.5)
@@ -558,8 +559,7 @@ const mech = {
}, },
damage(dmg) { damage(dmg) {
if (mod.isRewindAvoidDeath && mech.energy > 0.66) { if (mod.isRewindAvoidDeath && mech.energy > 0.66) {
const steps = Math.floor(Math.min(299, 137 * mech.energy)) mech.rewind(Math.floor(Math.min(299, 137 * mech.energy)))
mech.rewind(steps)
return return
} }
mech.lastHarmCycle = mech.cycle mech.lastHarmCycle = mech.cycle
@@ -764,7 +764,7 @@ const mech = {
ctx.stroke(); ctx.stroke();
// ctx.beginPath(); // ctx.beginPath();
// ctx.arc(15, 0, 3, 0, 2 * Math.PI); // ctx.arc(15, 0, 3, 0, 2 * Math.PI);
// ctx.fillStyle = '#9cf' //'#0cf'; // ctx.fillStyle = '#0cf';
// ctx.fill() // ctx.fill()
ctx.restore(); ctx.restore();
mech.yOff = mech.yOff * 0.85 + mech.yOffGoal * 0.15; //smoothly move leg height towards height goal mech.yOff = mech.yOff * 0.85 + mech.yOffGoal * 0.15; //smoothly move leg height towards height goal
@@ -2194,11 +2194,104 @@ const mech = {
{ {
name: "wormhole", name: "wormhole",
description: "use <strong class='color-f'>energy</strong> to <strong>tunnel</strong> through a <strong class='color-worm'>wormhole</strong><br><strong class='color-worm'>wormholes</strong> attract blocks and power ups<br><strong>10%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong>", //<br>bullets may also traverse <strong class='color-worm'>wormholes</strong> description: "use <strong class='color-f'>energy</strong> to <strong>tunnel</strong> through a <strong class='color-worm'>wormhole</strong><br><strong class='color-worm'>wormholes</strong> attract blocks and power ups<br><strong>10%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong>", //<br>bullets may also traverse <strong class='color-worm'>wormholes</strong>
effect: () => { effect: function() {
game.replaceTextLog = true; //allow text over write game.replaceTextLog = true; //allow text over write
mech.drop(); mech.drop();
mech.duplicateChance = 0.1 mech.duplicateChance = 0.1
game.draw.powerUp = game.draw.powerUpBonus //change power up draw game.draw.powerUp = game.draw.powerUpBonus //change power up draw
// if (mod.isRewindGun) {
// mech.hold = this.rewind
// } else {
mech.hold = this.teleport
// }
},
rewindCount: 0,
// rewind: function() {
// if (input.down) {
// if (input.field && mech.fieldCDcycle < mech.cycle) { //not hold but field button is pressed
// const DRAIN = 0.01
// if (this.rewindCount < 289 && mech.energy > DRAIN) {
// mech.energy -= DRAIN
// if (this.rewindCount === 0) {
// const shortPause = function() {
// if (mech.defaultFPSCycle < mech.cycle) { //back to default values
// game.fpsCap = game.fpsCapDefault
// game.fpsInterval = 1000 / game.fpsCap;
// // document.getElementById("dmg").style.transition = "opacity 1s";
// // document.getElementById("dmg").style.opacity = "0";
// } else {
// requestAnimationFrame(shortPause);
// }
// };
// if (mech.defaultFPSCycle < mech.cycle) requestAnimationFrame(shortPause);
// game.fpsCap = 4 //1 is longest pause, 4 is standard
// game.fpsInterval = 1000 / game.fpsCap;
// mech.defaultFPSCycle = mech.cycle
// }
// this.rewindCount += 10;
// game.wipe = function() { //set wipe to have trails
// // ctx.fillStyle = "rgba(255,255,255,0)";
// ctx.fillStyle = `rgba(221,221,221,${0.004})`;
// ctx.fillRect(0, 0, canvas.width, canvas.height);
// }
// let history = mech.history[(mech.cycle - this.rewindCount) % 300]
// Matter.Body.setPosition(player, history.position);
// Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y });
// if (history.health > mech.health) {
// mech.health = history.health
// mech.displayHealth();
// }
// //grab power ups
// for (let i = 0, len = powerUp.length; i < len; ++i) {
// const dxP = player.position.x - powerUp[i].position.x;
// const dyP = player.position.y - powerUp[i].position.y;
// if (dxP * dxP + dyP * dyP < 50000 && !game.isChoosing && !(mech.health === mech.maxHealth && powerUp[i].name === "heal")) {
// powerUps.onPickUp(player.position);
// powerUp[i].effect();
// Matter.World.remove(engine.world, powerUp[i]);
// powerUp.splice(i, 1);
// const shortPause = function() {
// if (mech.defaultFPSCycle < mech.cycle) { //back to default values
// game.fpsCap = game.fpsCapDefault
// game.fpsInterval = 1000 / game.fpsCap;
// // document.getElementById("dmg").style.transition = "opacity 1s";
// // document.getElementById("dmg").style.opacity = "0";
// } else {
// requestAnimationFrame(shortPause);
// }
// };
// if (mech.defaultFPSCycle < mech.cycle) requestAnimationFrame(shortPause);
// game.fpsCap = 3 //1 is longest pause, 4 is standard
// game.fpsInterval = 1000 / game.fpsCap;
// mech.defaultFPSCycle = mech.cycle
// break; //because the array order is messed up after splice
// }
// }
// mech.immuneCycle = mech.cycle + 5; //player is immune to collision damage for 30 cycles
// } else {
// mech.fieldCDcycle = mech.cycle + 30;
// // mech.resetHistory();
// }
// } else {
// if (this.rewindCount !== 0) {
// mech.fieldCDcycle = mech.cycle + 30;
// mech.resetHistory();
// this.rewindCount = 0;
// game.wipe = function() { //set wipe to normal
// ctx.clearRect(0, 0, canvas.width, canvas.height);
// }
// }
// mech.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
// }
// }
// mech.drawFieldMeter()
// },
teleport: function() {
// mech.hole = { //this is reset with each new field, but I'm leaving it here for reference // mech.hole = { //this is reset with each new field, but I'm leaving it here for reference
// isOn: false, // isOn: false,
// isReady: true, // isReady: true,
@@ -2207,237 +2300,235 @@ const mech = {
// angle: 0, // angle: 0,
// unit:{x:0,y:0}, // unit:{x:0,y:0},
// } // }
mech.hold = function() { if (mech.hole.isOn) {
if (mech.hole.isOn) { // draw holes
// draw holes mech.fieldRange = 0.97 * mech.fieldRange + 0.03 * (50 + 10 * Math.sin(game.cycle * 0.025))
mech.fieldRange = 0.97 * mech.fieldRange + 0.03 * (50 + 10 * Math.sin(game.cycle * 0.025)) const semiMajorAxis = mech.fieldRange + 30
const semiMajorAxis = mech.fieldRange + 30 const edge1a = Vector.add(Vector.mult(mech.hole.unit, semiMajorAxis), mech.hole.pos1)
const edge1a = Vector.add(Vector.mult(mech.hole.unit, semiMajorAxis), mech.hole.pos1) const edge1b = Vector.add(Vector.mult(mech.hole.unit, -semiMajorAxis), mech.hole.pos1)
const edge1b = Vector.add(Vector.mult(mech.hole.unit, -semiMajorAxis), mech.hole.pos1) const edge2a = Vector.add(Vector.mult(mech.hole.unit, semiMajorAxis), mech.hole.pos2)
const edge2a = Vector.add(Vector.mult(mech.hole.unit, semiMajorAxis), mech.hole.pos2) const edge2b = Vector.add(Vector.mult(mech.hole.unit, -semiMajorAxis), mech.hole.pos2)
const edge2b = Vector.add(Vector.mult(mech.hole.unit, -semiMajorAxis), mech.hole.pos2) ctx.beginPath();
ctx.beginPath(); ctx.moveTo(edge1a.x, edge1a.y)
ctx.moveTo(edge1a.x, edge1a.y) ctx.bezierCurveTo(mech.hole.pos1.x, mech.hole.pos1.y, mech.hole.pos2.x, mech.hole.pos2.y, edge2a.x, edge2a.y);
ctx.bezierCurveTo(mech.hole.pos1.x, mech.hole.pos1.y, mech.hole.pos2.x, mech.hole.pos2.y, edge2a.x, edge2a.y); ctx.lineTo(edge2b.x, edge2b.y)
ctx.lineTo(edge2b.x, edge2b.y) ctx.bezierCurveTo(mech.hole.pos2.x, mech.hole.pos2.y, mech.hole.pos1.x, mech.hole.pos1.y, edge1b.x, edge1b.y);
ctx.bezierCurveTo(mech.hole.pos2.x, mech.hole.pos2.y, mech.hole.pos1.x, mech.hole.pos1.y, edge1b.x, edge1b.y); ctx.fillStyle = `rgba(255,255,255,${200 / mech.fieldRange / mech.fieldRange})` //"rgba(0,0,0,0.1)"
ctx.fillStyle = `rgba(255,255,255,${200 / mech.fieldRange / mech.fieldRange})` //"rgba(0,0,0,0.1)" ctx.fill();
ctx.fill(); ctx.beginPath();
ctx.beginPath(); ctx.ellipse(mech.hole.pos1.x, mech.hole.pos1.y, mech.fieldRange, semiMajorAxis, mech.hole.angle, 0, 2 * Math.PI)
ctx.ellipse(mech.hole.pos1.x, mech.hole.pos1.y, mech.fieldRange, semiMajorAxis, mech.hole.angle, 0, 2 * Math.PI) ctx.ellipse(mech.hole.pos2.x, mech.hole.pos2.y, mech.fieldRange, semiMajorAxis, mech.hole.angle, 0, 2 * Math.PI)
ctx.ellipse(mech.hole.pos2.x, mech.hole.pos2.y, mech.fieldRange, semiMajorAxis, mech.hole.angle, 0, 2 * Math.PI) ctx.fillStyle = `rgba(255,255,255,${32 / mech.fieldRange})`
ctx.fillStyle = `rgba(255,255,255,${32 / mech.fieldRange})` ctx.fill();
ctx.fill();
//suck power ups //suck power ups
for (let i = 0, len = powerUp.length; i < len; ++i) { for (let i = 0, len = powerUp.length; i < len; ++i) {
//which hole is closer //which hole is closer
const dxP1 = mech.hole.pos1.x - powerUp[i].position.x; const dxP1 = mech.hole.pos1.x - powerUp[i].position.x;
const dyP1 = mech.hole.pos1.y - powerUp[i].position.y; const dyP1 = mech.hole.pos1.y - powerUp[i].position.y;
const dxP2 = mech.hole.pos2.x - powerUp[i].position.x; const dxP2 = mech.hole.pos2.x - powerUp[i].position.x;
const dyP2 = mech.hole.pos2.y - powerUp[i].position.y; const dyP2 = mech.hole.pos2.y - powerUp[i].position.y;
let dxP, dyP, dist2 let dxP, dyP, dist2
if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) { if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) {
dxP = dxP1 dxP = dxP1
dyP = dyP1 dyP = dyP1
} else { } else {
dxP = dxP2 dxP = dxP2
dyP = dyP2 dyP = dyP2
} }
dist2 = dxP * dxP + dyP * dyP; dist2 = dxP * dxP + dyP * dyP;
if (dist2 < 600000 && !(mech.health === mech.maxHealth && powerUp[i].name === "heal")) { if (dist2 < 600000 && !(mech.health === mech.maxHealth && powerUp[i].name === "heal")) {
powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole
powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * game.g; //negate gravity powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * game.g; //negate gravity
Matter.Body.setVelocity(powerUp[i], { //extra friction Matter.Body.setVelocity(powerUp[i], { //extra friction
x: powerUp[i].velocity.x * 0.05, x: powerUp[i].velocity.x * 0.05,
y: powerUp[i].velocity.y * 0.05 y: powerUp[i].velocity.y * 0.05
}); });
if (dist2 < 1000 && !game.isChoosing) { //use power up if it is close enough if (dist2 < 1000 && !game.isChoosing) { //use power up if it is close enough
mech.fieldRange *= 0.8 mech.fieldRange *= 0.8
powerUps.onPickUp(powerUp[i].position); powerUps.onPickUp(powerUp[i].position);
powerUp[i].effect(); powerUp[i].effect();
Matter.World.remove(engine.world, powerUp[i]); Matter.World.remove(engine.world, powerUp[i]);
powerUp.splice(i, 1); powerUp.splice(i, 1);
break; //because the array order is messed up after splice break; //because the array order is messed up after splice
}
} }
} }
//suck and shrink blocks }
const suckRange = 500 //suck and shrink blocks
const shrinkRange = 100 const suckRange = 500
const shrinkScale = 0.97; const shrinkRange = 100
const slowScale = 0.9 const shrinkScale = 0.97;
for (let i = 0, len = body.length; i < len; i++) { const slowScale = 0.9
if (!body[i].isNotHoldable) { for (let i = 0, len = body.length; i < len; i++) {
const dist1 = Vector.magnitude(Vector.sub(mech.hole.pos1, body[i].position)) if (!body[i].isNotHoldable) {
const dist2 = Vector.magnitude(Vector.sub(mech.hole.pos2, body[i].position)) const dist1 = Vector.magnitude(Vector.sub(mech.hole.pos1, body[i].position))
if (dist1 < dist2) { const dist2 = Vector.magnitude(Vector.sub(mech.hole.pos2, body[i].position))
if (dist1 < suckRange) { if (dist1 < dist2) {
const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos1, body[i].position)), 1) if (dist1 < suckRange) {
const slow = Vector.mult(body[i].velocity, slowScale) const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos1, body[i].position)), 1)
Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
//shrink
if (Vector.magnitude(Vector.sub(mech.hole.pos1, body[i].position)) < shrinkRange) {
Matter.Body.scale(body[i], shrinkScale, shrinkScale);
if (body[i].mass < 0.05) {
Matter.World.remove(engine.world, body[i]);
body.splice(i, 1);
mech.fieldRange *= 0.8
if (mod.isWormholeEnergy) mech.energy += 0.5
if (mod.isWormSpores) { //pandimensionalspermia
for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) {
b.spore(Vector.add(mech.hole.pos2, Vector.rotate({
x: mech.fieldRange * 0.4,
y: 0
}, 2 * Math.PI * Math.random())))
Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(mech.hole.unit, Math.PI / 2), -15));
}
}
break
}
}
}
} else if (dist2 < suckRange) {
const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos2, body[i].position)), 1)
const slow = Vector.mult(body[i].velocity, slowScale) const slow = Vector.mult(body[i].velocity, slowScale)
Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
//shrink //shrink
if (Vector.magnitude(Vector.sub(mech.hole.pos2, body[i].position)) < shrinkRange) { if (Vector.magnitude(Vector.sub(mech.hole.pos1, body[i].position)) < shrinkRange) {
Matter.Body.scale(body[i], shrinkScale, shrinkScale); Matter.Body.scale(body[i], shrinkScale, shrinkScale);
if (body[i].mass < 0.05) { if (body[i].mass < 0.05) {
Matter.World.remove(engine.world, body[i]); Matter.World.remove(engine.world, body[i]);
body.splice(i, 1); body.splice(i, 1);
mech.fieldRange *= 0.8 mech.fieldRange *= 0.8
// if (mod.isWormholeEnergy && mech.energy < mech.maxEnergy * 2) mech.energy = mech.maxEnergy * 2
if (mod.isWormholeEnergy) mech.energy += 0.5 if (mod.isWormholeEnergy) mech.energy += 0.5
if (mod.isWormSpores) { //pandimensionalspermia if (mod.isWormSpores) { //pandimensionalspermia
for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) { for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) {
b.spore(Vector.add(mech.hole.pos1, Vector.rotate({ b.spore(Vector.add(mech.hole.pos2, Vector.rotate({
x: mech.fieldRange * 0.4, x: mech.fieldRange * 0.4,
y: 0 y: 0
}, 2 * Math.PI * Math.random()))) }, 2 * Math.PI * Math.random())))
Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(mech.hole.unit, Math.PI / 2), 15)); Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(mech.hole.unit, Math.PI / 2), -15));
} }
} }
break break
} }
} }
} }
} } else if (dist2 < suckRange) {
} const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos2, body[i].position)), 1)
if (mod.isWormBullets) { const slow = Vector.mult(body[i].velocity, slowScale)
//teleport bullets Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2 //shrink
if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots if (Vector.magnitude(Vector.sub(mech.hole.pos2, body[i].position)) < shrinkRange) {
if (Vector.magnitude(Vector.sub(mech.hole.pos1, bullet[i].position)) < mech.fieldRange) { //find if bullet is touching hole1 Matter.Body.scale(body[i], shrinkScale, shrinkScale);
Matter.Body.setPosition(bullet[i], Vector.add(mech.hole.pos2, Vector.sub(mech.hole.pos1, bullet[i].position))); if (body[i].mass < 0.05) {
mech.fieldRange += 5 Matter.World.remove(engine.world, body[i]);
bullet[i].isInHole = true body.splice(i, 1);
} else if (Vector.magnitude(Vector.sub(mech.hole.pos2, bullet[i].position)) < mech.fieldRange) { //find if bullet is touching hole1 mech.fieldRange *= 0.8
Matter.Body.setPosition(bullet[i], Vector.add(mech.hole.pos1, Vector.sub(mech.hole.pos2, bullet[i].position))); // if (mod.isWormholeEnergy && mech.energy < mech.maxEnergy * 2) mech.energy = mech.maxEnergy * 2
mech.fieldRange += 5 if (mod.isWormholeEnergy) mech.energy += 0.5
bullet[i].isInHole = true if (mod.isWormSpores) { //pandimensionalspermia
for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) {
b.spore(Vector.add(mech.hole.pos1, Vector.rotate({
x: mech.fieldRange * 0.4,
y: 0
}, 2 * Math.PI * Math.random())))
Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(mech.hole.unit, Math.PI / 2), 15));
}
}
break
} }
} }
} }
// mobs get pushed away
for (let i = 0, len = mob.length; i < len; i++) {
if (Vector.magnitude(Vector.sub(mech.hole.pos1, mob[i].position)) < 200) {
const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos1, mob[i].position)), -0.07)
Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
}
if (Vector.magnitude(Vector.sub(mech.hole.pos2, mob[i].position)) < 200) {
const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos2, mob[i].position)), -0.07)
Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
}
}
} }
} }
if (mod.isWormBullets) {
//teleport bullets
for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2
if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots
if (Vector.magnitude(Vector.sub(mech.hole.pos1, bullet[i].position)) < mech.fieldRange) { //find if bullet is touching hole1
Matter.Body.setPosition(bullet[i], Vector.add(mech.hole.pos2, Vector.sub(mech.hole.pos1, bullet[i].position)));
mech.fieldRange += 5
bullet[i].isInHole = true
} else if (Vector.magnitude(Vector.sub(mech.hole.pos2, bullet[i].position)) < mech.fieldRange) { //find if bullet is touching hole1
Matter.Body.setPosition(bullet[i], Vector.add(mech.hole.pos1, Vector.sub(mech.hole.pos2, bullet[i].position)));
mech.fieldRange += 5
bullet[i].isInHole = true
}
}
}
// mobs get pushed away
for (let i = 0, len = mob.length; i < len; i++) {
if (Vector.magnitude(Vector.sub(mech.hole.pos1, mob[i].position)) < 200) {
const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos1, mob[i].position)), -0.07)
Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
}
if (Vector.magnitude(Vector.sub(mech.hole.pos2, mob[i].position)) < 200) {
const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos2, mob[i].position)), -0.07)
Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
}
}
}
}
if (input.field && mech.fieldCDcycle < mech.cycle) { //not hold but field button is pressed if (input.field && mech.fieldCDcycle < mech.cycle) { //not hold but field button is pressed
const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(game.mouseInGame, mech.pos)), 50), game.mouseInGame) const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(game.mouseInGame, mech.pos)), 50), game.mouseInGame)
const scale = 60 const scale = 60
// console.log(Matter.Query.region(map, bounds)) // console.log(Matter.Query.region(map, bounds))
if (mech.hole.isReady && if (mech.hole.isReady &&
( (
Matter.Query.region(map, { Matter.Query.region(map, {
min: { min: {
x: game.mouseInGame.x - scale, x: game.mouseInGame.x - scale,
y: game.mouseInGame.y - scale y: game.mouseInGame.y - scale
}, },
max: { max: {
x: game.mouseInGame.x + scale, x: game.mouseInGame.x + scale,
y: game.mouseInGame.y + scale y: game.mouseInGame.y + scale
} }
}).length === 0 && }).length === 0 &&
Matter.Query.ray(map, mech.pos, justPastMouse).length === 0 Matter.Query.ray(map, mech.pos, justPastMouse).length === 0
// Matter.Query.ray(map, mech.pos, game.mouseInGame).length === 0 && // Matter.Query.ray(map, mech.pos, game.mouseInGame).length === 0 &&
// Matter.Query.ray(map, player.position, game.mouseInGame).length === 0 && // Matter.Query.ray(map, player.position, game.mouseInGame).length === 0 &&
// Matter.Query.ray(map, player.position, justPastMouse).length === 0 // Matter.Query.ray(map, player.position, justPastMouse).length === 0
) )
) { ) {
const sub = Vector.sub(game.mouseInGame, mech.pos) const sub = Vector.sub(game.mouseInGame, mech.pos)
const mag = Vector.magnitude(sub) const mag = Vector.magnitude(sub)
const drain = 0.03 + 0.005 * Math.sqrt(mag) const drain = 0.03 + 0.005 * Math.sqrt(mag)
if (mech.energy > drain && mag > 300) { if (mech.energy > drain && mag > 300) {
mech.energy -= drain mech.energy -= drain
mech.hole.isReady = false; mech.hole.isReady = false;
mech.fieldRange = 0 mech.fieldRange = 0
Matter.Body.setPosition(player, game.mouseInGame); Matter.Body.setPosition(player, game.mouseInGame);
mech.buttonCD_jump = 0 //this might fix a bug with jumping mech.buttonCD_jump = 0 //this might fix a bug with jumping
const velocity = Vector.mult(Vector.normalise(sub), 18) const velocity = Vector.mult(Vector.normalise(sub), 18)
Matter.Body.setVelocity(player, { Matter.Body.setVelocity(player, {
x: velocity.x, x: velocity.x,
y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer
}); });
mech.immuneCycle = mech.cycle + 15; //player is immune to collision damage mech.immuneCycle = mech.cycle + 15; //player is immune to collision damage
// move bots to follow player // move bots to follow player
for (let i = 0; i < bullet.length; i++) { for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) { if (bullet[i].botType) {
Matter.Body.setPosition(bullet[i], Vector.add(player.position, { Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
x: 250 * (Math.random() - 0.5), x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5) y: 250 * (Math.random() - 0.5)
})); }));
Matter.Body.setVelocity(bullet[i], { Matter.Body.setVelocity(bullet[i], {
x: 0, x: 0,
y: 0 y: 0
}); });
}
}
//set holes
mech.hole.isOn = true;
mech.hole.pos1.x = mech.pos.x
mech.hole.pos1.y = mech.pos.y
mech.hole.pos2.x = player.position.x
mech.hole.pos2.y = player.position.y
mech.hole.angle = Math.atan2(sub.y, sub.x)
mech.hole.unit = Vector.perp(Vector.normalise(sub))
if (mod.isWormholeDamage) {
who = Matter.Query.ray(mob, mech.pos, game.mouseInGame, 80)
for (let i = 0; i < who.length; i++) {
if (who[i].body.alive) {
mobs.statusDoT(who[i].body, 0.6, 420)
mobs.statusStun(who[i].body, 240)
} }
} }
//set holes
mech.hole.isOn = true;
mech.hole.pos1.x = mech.pos.x
mech.hole.pos1.y = mech.pos.y
mech.hole.pos2.x = player.position.x
mech.hole.pos2.y = player.position.y
mech.hole.angle = Math.atan2(sub.y, sub.x)
mech.hole.unit = Vector.perp(Vector.normalise(sub))
if (mod.isWormholeDamage) {
who = Matter.Query.ray(mob, mech.pos, game.mouseInGame, 80)
for (let i = 0; i < who.length; i++) {
if (who[i].body.alive) {
mobs.statusDoT(who[i].body, 0.6, 420)
mobs.statusStun(who[i].body, 240)
}
}
}
} else {
mech.grabPowerUp();
} }
} else { } else {
mech.grabPowerUp(); mech.grabPowerUp();
} }
} else if (mech.holdingTarget && mech.fieldCDcycle < mech.cycle) { //holding, but field button is released
mech.pickUp();
} else { } else {
mech.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) mech.grabPowerUp();
mech.hole.isReady = true;
} }
mech.drawFieldMeter() } else if (mech.holdingTarget && mech.fieldCDcycle < mech.cycle) { //holding, but field button is released
mech.pickUp();
} else {
mech.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
mech.hole.isReady = true;
} }
} mech.drawFieldMeter()
},
}, },
], ],
}; };

View File

@@ -81,7 +81,7 @@ const spawn = {
} }
} }
}, },
randomLevelBoss(x, y, options = ["shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "snakeBoss"]) { randomLevelBoss(x, y, options = ["shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "snakeBoss", "streamBoss"]) {
// other bosses: suckerBoss, laserBoss, tetherBoss, //all need a particular level to work so they are not included // other bosses: suckerBoss, laserBoss, tetherBoss, //all need a particular level to work so they are not included
spawn[options[Math.floor(Math.random() * options.length)]](x, y) spawn[options[Math.floor(Math.random() * options.length)]](x, y)
}, },
@@ -101,15 +101,34 @@ const spawn = {
level.bossKilled = true; level.bossKilled = true;
level.exit.x = 5500; level.exit.x = 5500;
level.exit.y = -330; level.exit.y = -330;
//ramp up damage
for (let i = 0; i < 4; i++) level.difficultyIncrease(game.difficultyMode)
//pull in particles //pull in particles
for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally for (let i = 0, len = body.length; i < len; ++i) {
const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, body[i].position)), 65) const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, body[i].position)), 65)
const pushUp = Vector.add(velocity, { x: 0, y: -0.3 }) const pushUp = Vector.add(velocity, { x: 0, y: -0.5 })
Matter.Body.setVelocity(body[i], Vector.add(body[i].velocity, pushUp)); Matter.Body.setVelocity(body[i], Vector.add(body[i].velocity, pushUp));
} }
//ramp up damage //push away mobs
for (let i = 0; i < 5; i++) level.difficultyIncrease(game.difficultyMode) for (let i = 0, len = mob.length; i < len; ++i) {
if (mob[i] !== this) {
const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, mob[i].position)), -65)
Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, velocity));
}
}
//draw stuff
for (let i = 0, len = 22; i < len; i++) {
game.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: (i + 1) * 150,
color: `rgba(255,255,255,0.17)`,
time: 5 * (len - i + 1)
});
}
}; };
me.onDamage = function() {}; me.onDamage = function() {};
@@ -761,7 +780,7 @@ const spawn = {
let me = mob[mob.length - 1]; let me = mob[mob.length - 1];
me.stroke = "transparent"; //used for drawSneaker me.stroke = "transparent"; //used for drawSneaker
me.eventHorizon = radius * 23; //required for blackhole me.eventHorizon = radius * 23; //required for blackhole
me.seeAtDistance2 = (me.eventHorizon + 300) * (me.eventHorizon + 300); //vision limit is event horizon me.seeAtDistance2 = (me.eventHorizon + 400) * (me.eventHorizon + 400); //vision limit is event horizon
me.accelMag = 0.00009 * game.accelScale; me.accelMag = 0.00009 * game.accelScale;
me.frictionAir = 0.025; me.frictionAir = 0.025;
me.collisionFilter.mask = cat.player | cat.bullet me.collisionFilter.mask = cat.player | cat.bullet
@@ -775,8 +794,15 @@ const spawn = {
y: this.velocity.y * 0.99 y: this.velocity.y * 0.99
}); });
} }
// this.seePlayerByDistOrLOS(); // this.seePlayerCheckByDistance()
this.seePlayerCheckByDistance() if (!(game.cycle % this.seePlayerFreq)) {
if (this.distanceToPlayer2() < this.seeAtDistance2) { //&& !mech.isCloak ignore cloak for black holes
this.locatePlayer();
if (!this.seePlayer.yes) this.seePlayer.yes = true;
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
this.checkStatus(); this.checkStatus();
if (this.seePlayer.recall) { if (this.seePlayer.recall) {
//eventHorizon waves in and out //eventHorizon waves in and out
@@ -832,7 +858,7 @@ const spawn = {
me.isBoss = true; me.isBoss = true;
me.stroke = "transparent"; //used for drawSneaker me.stroke = "transparent"; //used for drawSneaker
me.eventHorizon = 1100; //required for black hole me.eventHorizon = 1100; //required for black hole
me.seeAtDistance2 = (me.eventHorizon + 1000) * (me.eventHorizon + 1000); //vision limit is event horizon me.seeAtDistance2 = (me.eventHorizon + 1200) * (me.eventHorizon + 1200); //vision limit is event horizon
me.accelMag = 0.00003 * game.accelScale; me.accelMag = 0.00003 * game.accelScale;
me.collisionFilter.mask = cat.player | cat.bullet me.collisionFilter.mask = cat.player | cat.bullet
// me.frictionAir = 0.005; // me.frictionAir = 0.005;
@@ -865,7 +891,14 @@ const spawn = {
y: this.velocity.y * 0.95 y: this.velocity.y * 0.95
}); });
} }
this.seePlayerByDistOrLOS(); if (!(game.cycle % this.seePlayerFreq)) {
if (this.distanceToPlayer2() < this.seeAtDistance2) { //&& !mech.isCloak ignore cloak for black holes
this.locatePlayer();
if (!this.seePlayer.yes) this.seePlayer.yes = true;
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
this.checkStatus(); this.checkStatus();
if (this.seePlayer.recall) { if (this.seePlayer.recall) {
//accelerate towards the player //accelerate towards the player
@@ -1865,7 +1898,7 @@ const spawn = {
me.onHit = function() { me.onHit = function() {
this.explode(this.mass * 20); this.explode(this.mass * 20);
}; };
Matter.Body.setDensity(me, 0.00005); //normal is 0.001 Matter.Body.setDensity(me, 0.00004); //normal is 0.001
me.timeLeft = 200; me.timeLeft = 200;
me.g = 0.001; //required if using 'gravity' me.g = 0.001; //required if using 'gravity'
me.frictionAir = 0; me.frictionAir = 0;
@@ -2106,7 +2139,7 @@ const spawn = {
me.frictionStatic = 0; me.frictionStatic = 0;
me.friction = 0; me.friction = 0;
me.frictionAir = 0.02; me.frictionAir = 0.02;
me.memory = 420 * game.CDScale; me.memory = 420;
me.repulsionRange = 1200000; //squared me.repulsionRange = 1200000; //squared
spawn.shield(me, x, y, 1); spawn.shield(me, x, y, 1);
Matter.Body.setDensity(me, 0.004 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger Matter.Body.setDensity(me, 0.004 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger
@@ -2124,7 +2157,7 @@ const spawn = {
Matter.Body.setAngularVelocity(this, 0.11) Matter.Body.setAngularVelocity(this, 0.11)
//fire a bullet from each vertex //fire a bullet from each vertex
for (let i = 0, len = this.vertices.length; i < len; i++) { for (let i = 0, len = this.vertices.length; i < len; i++) {
spawn.seeker(this.vertices[i].x, this.vertices[i].y, 7) spawn.seeker(this.vertices[i].x, this.vertices[i].y, 6)
//give the bullet a rotational velocity as if they were attached to a vertex //give the bullet a rotational velocity as if they were attached to a vertex
const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[i]))), -10) const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[i]))), -10)
Matter.Body.setVelocity(mob[mob.length - 1], { Matter.Body.setVelocity(mob[mob.length - 1], {
@@ -2135,7 +2168,84 @@ const spawn = {
} }
}; };
}, },
seeker(x, y, radius = 5, sides = 0) { streamBoss(x, y, radius = 110) {
mobs.spawn(x, y, 5, radius, "rgb(245,180,255)");
let me = mob[mob.length - 1];
me.isBoss = true;
// me.accelMag = 0.00023 * game.accelScale;
me.accelMag = 0.00008 * game.accelScale;
// me.fireFreq = Math.floor(30 * game.CDScale)
me.canFire = false;
me.closestVertex1 = 0;
me.closestVertex2 = 1;
me.cycle = 0
me.frictionStatic = 0;
me.friction = 0;
me.frictionAir = 0.022;
me.memory = 240;
me.repulsionRange = 1200000; //squared
spawn.shield(me, x, y, 1);
Matter.Body.setDensity(me, 0.025); //extra dense //normal is 0.001 //makes effective life much larger
me.onDeath = function() {
powerUps.spawnBossPowerUp(this.position.x, this.position.y)
// this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed
};
me.onDamage = function() {};
me.do = function() {
this.seePlayerCheck();
this.checkStatus();
this.attraction();
this.repulsion();
this.cycle++
if (this.seePlayer.recall && ((this.cycle % 15) === 0) && !mech.isBodiesAsleep) {
if (this.canFire) {
if (this.cycle > 120) {
this.cycle = 0
this.canFire = false
// Matter.Body.setAngularVelocity(this, 0.1)
// const forceMag = 0.01 * this.mass;
// const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
// this.force.x -= 2 * forceMag * Math.cos(angle);
// this.force.y -= 2 * forceMag * Math.sin(angle); // - 0.0007 * this.mass; //antigravity
}
spawn.seeker(this.vertices[this.closestVertex1].x, this.vertices[this.closestVertex1].y, 4)
Matter.Body.setDensity(mob[mob.length - 1], 0.000001); //normal is 0.001
const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, this.vertices[this.closestVertex1])), -10)
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + velocity.x,
y: this.velocity.y + velocity.y
});
spawn.seeker(this.vertices[this.closestVertex2].x, this.vertices[this.closestVertex2].y, 4)
Matter.Body.setDensity(mob[mob.length - 1], 0.000001); //normal is 0.001
const velocity2 = Vector.mult(Vector.normalise(Vector.sub(this.position, this.vertices[this.closestVertex2])), -10)
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + velocity2.x,
y: this.velocity.y + velocity2.y
});
} else if (this.cycle > 210) {
this.cycle = 0
this.canFire = true
//find closest 2 vertexes
let distance2 = Infinity
for (let i = 0; i < this.vertices.length; i++) {
const d = Vector.magnitudeSquared(Vector.sub(this.vertices[i], player.position))
if (d < distance2) {
distance2 = d
this.closestVertex2 = this.closestVertex1
this.closestVertex1 = i
}
}
if (this.closestVertex2 === this.closestVertex1) {
this.closestVertex2++
if (this.closestVertex2 === this.vertices.length) this.closestVertex2 = 0
}
}
}
};
},
seeker(x, y, radius = 5, sides = 6) {
//bullets //bullets
mobs.spawn(x, y, sides, radius, "rgb(255,0,255)"); mobs.spawn(x, y, sides, radius, "rgb(255,0,255)");
let me = mob[mob.length - 1]; let me = mob[mob.length - 1];
@@ -2143,10 +2253,10 @@ const spawn = {
me.onHit = function() { me.onHit = function() {
this.explode(this.mass * 20); this.explode(this.mass * 20);
}; };
Matter.Body.setDensity(me, 0.00002); //normal is 0.001 Matter.Body.setDensity(me, 0.000015); //normal is 0.001
me.timeLeft = 420 * (0.8 + 0.4 * Math.random()); me.timeLeft = 420 * (0.8 + 0.4 * Math.random());
me.accelMag = 0.00017 * (0.8 + 0.4 * Math.random()) * game.accelScale; me.accelMag = 0.00017 * game.accelScale; //* (0.8 + 0.4 * Math.random())
me.frictionAir = 0.01 * (0.8 + 0.4 * Math.random()); me.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random());
me.restitution = 0.5; me.restitution = 0.5;
me.leaveBody = false; me.leaveBody = false;
me.dropPowerUp = false; me.dropPowerUp = false;
@@ -2190,6 +2300,7 @@ const spawn = {
//run this function on hitting player //run this function on hitting player
this.explode(); this.explode();
}; };
Matter.Body.setDensity(me, 0.0005); //normal is 0.001
me.g = 0.0001; //required if using 'gravity' me.g = 0.0001; //required if using 'gravity'
me.accelMag = 0.0003 * game.accelScale; me.accelMag = 0.0003 * game.accelScale;
me.memory = 30; me.memory = 30;

View File

@@ -587,6 +587,7 @@ em {
/* background: rgb(116, 102, 238); */ /* background: rgb(116, 102, 238); */
/* background: hsl(253, 57%, 52%); */ /* background: hsl(253, 57%, 52%); */
background: hsl(255, 100%, 71%); background: hsl(255, 100%, 71%);
/* background: hsl(282, 100%, 64%); */
} }
/* .grey { /* .grey {

View File

@@ -1,22 +1,16 @@
******************************************************** NEXT PATCH ******************************************************** ******************************************************** NEXT PATCH ********************************************************
your build url can now be copied in the pause screen new level boss that fires 2 streams of small bullets that chase you
mod: inductive coupling - 4 max health per power up, but limited to 44 max health per level (replaces crystalized armor) mod: add a CPT gun to your inventory that rewinds your history, reverts your health, position, velocity for 10 seconds
mod: transceiver chip - use all the power ups left over at the end of a level I expect that spamming rewind has some overpowered combos.
mod: catabolism - does a flat 5 damage to your health for 3 ammo (was 2% of max health for 1 ammo) Let me know what you find, and your ideas on balance.
******************************************************** BUGS ******************************************************** ******************************************************** BUGS ********************************************************
give worm hole pair production? check for crouch after rewind
CPT, tesseract
2nd mod for crystalized armor
no cap for health power ups?
(not able to reproduce, might be fixed) possible bug with neutron rewind
status doesn't apply correctly for spawned neutron bombs that are stuck to a shield
also saw neutron bombs bounce off shield, for normal bullets
test this more
mod and mob are too similar mod and mob are too similar
@@ -26,13 +20,15 @@ mod and mob are too similar
trigger a short term non-collide if that occurs trigger a short term non-collide if that occurs
(12+ reports) bug - crouch and worm hole? -> crouch locked in (12+ reports) bug - crouch and worm hole? -> crouch locked in
doesn't occur on my computer? ***try checking the date of the first bug, and then look at what patches came out right before that***
doesn't occur on my computer? but it does occur on fast computers
you can spoof it with mech.crouch = true in console you can spoof it with mech.crouch = true in console
players have extra gravity players have extra gravity
might be from the short jump code might be from the short jump code
add in a check every 7 seconds to try and fix it add in a check every 7 seconds to try and fix it
this fix was added and it is working for some cases this fix was added and it is working for some cases
maybe move the fix to once a second? maybe move the fix to once a second?
bug fix - rewrite crouch to not translate the player height, but instead switch between 2 sensors
(intermittent, but almost every time) bug - capping the fps causes random slow downs, that can be fixed with pause (intermittent, but almost every time) bug - capping the fps causes random slow downs, that can be fixed with pause
@@ -44,7 +40,39 @@ mod and mob are too similar
******************************************************** TODO ******************************************************** ******************************************************** TODO ********************************************************
make a reset hit box function as a way to fix crouch bug mod: laser beams push like plasma torch pushes with directional force
mod: worm hole - you move through time instead of space. (field click to rewind)
add support for eating blocks, and damaging mobs that are nearby when you rewind
allow those mods
mod might not work as is because player needs to be able to pick up and move blocks sometimes
what about as a gun that uses energy not bullets?
mechanic: technological dead end - add mods to the mod pool with a dumb effect
don't show up in custom?
negative effect (one time effects are better to avoid code clutter)
make the player rainbow colors
mech.color = {
hue: 0,
sat: 100,
light: 50
}
setInterval(function(){
mech.color.hue++
mech.setFillColors()
}, 10);
remove all your energy
eject all your rerolls (not bad with dup)
teleport to the start of the level
remove your bots (requires you to have some bots)
your bots are changed to random bots
Mod: "High Risk": Spawn two bosses per level.
maybe limit to just the power up boss and spawn it at the exit every time to keep it simple
also weaken the player
remove a mod up?
lower harm reduction?
increase game difficulty by one level
mod that requires integrated armament mod that requires integrated armament
@@ -75,15 +103,6 @@ make different move methods
mod: when mobs are at full health you do 40% to them mod: when mobs are at full health you do 40% to them
mechanic: failed technology - add mods to the mod pool with a dumb effect
don't show up in custom?
negative effect (one time effects are better to avoid code clutter)
remove all your energy
eject all your rerolls (not bad with dup)
teleport to the start of the level
remove your bots (requires you to have some bots)
your bots are changed to random bots
mod - move super fast, go intangible, drain energy very fast mod - move super fast, go intangible, drain energy very fast
this is like a dodge roll this is like a dodge roll
mod for standing wave?, cloaking? mod for standing wave?, cloaking?
@@ -371,12 +390,16 @@ robot AI communication
output to output to
bottom left message bottom left message
tab title? tab title?
style style
output console.log?
make it look like a computer terminal make it look like a computer terminal
track multiple lines, like your vocoder program
messages about heal, ammo, mods, that just list internal computer code
example: a heal would be mech.health += 12
mono space font mono space font
square edges square edges
in baby talk? black text on bottom right with no background?
with random ASCII gibberish letters or white text, or yellow
end each message with a hexadecimal encryption code/hash end each message with a hexadecimal encryption code/hash
message after selecting each new (mod / gun / field) message after selecting each new (mod / gun / field)
put messages in (mod / gun / field) method put messages in (mod / gun / field) method
@@ -385,6 +408,14 @@ robot AI communication
you'd have to store an array of guns/fields/mod used last game you'd have to store an array of guns/fields/mod used last game
n-gon escape simulation ${random seed} n-gon escape simulation ${random seed}
say something about what mobs types are queued up, and level order say something about what mobs types are queued up, and level order
**all communication should be from scientists watching the simulation; the robot shouldn't talk**
talking about the robot, watching
trying to communicate with the robot? but how
lines:
I think it's planing to escape
Why is it attacking those shapes?
Are those shapes supposed to be us?
add an ending to the game add an ending to the game
maybe the game ending should ask you to open the console and type in some commands like in the end of doki doki maybe the game ending should ask you to open the console and type in some commands like in the end of doki doki