CPT gun
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:
307
js/bullet.js
307
js/bullet.js
@@ -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() {
|
||||
|
||||
},
|
||||
@@ -550,7 +676,7 @@ const b = {
|
||||
}
|
||||
}
|
||||
slow(body, this.damageRadius)
|
||||
slow([player], this.damageRadius)
|
||||
if (!mod.isNeutronImmune) slow([player], this.damageRadius)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,6 +717,7 @@ const b = {
|
||||
},
|
||||
onEnd() {
|
||||
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) {
|
||||
for (let i = 0; i < mod.recursiveMissiles; i++) {
|
||||
if (0.2 - 0.02 * i > Math.random()) {
|
||||
@@ -724,8 +851,8 @@ const b = {
|
||||
const q = Matter.Query.point(mob, this.position)
|
||||
for (let i = 0; i < q.length; i++) {
|
||||
Matter.Body.setVelocity(q[i], {
|
||||
x: q[i].velocity.x * 0.7,
|
||||
y: q[i].velocity.y * 0.7
|
||||
x: q[i].velocity.x * 0.6,
|
||||
y: q[i].velocity.y * 0.6
|
||||
});
|
||||
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)
|
||||
@@ -743,9 +870,9 @@ const b = {
|
||||
this.cycle++
|
||||
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 velocity = Vector.mult(player.velocity, 0.25) //move with player
|
||||
// Matter.Body.setPosition(this, Vector.add(velocity, Vector.add(this.position, wiggle)))
|
||||
Matter.Body.setPosition(this, Vector.add(this.position, wiggle))
|
||||
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(this.position, wiggle))
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2089,11 +2216,11 @@ const b = {
|
||||
const DIST = Vector.magnitude(sub);
|
||||
const unit = Vector.normalise(sub)
|
||||
if (DIST < mod.isPlasmaRange * 450 && mech.energy > this.drainThreshold) {
|
||||
mech.energy -= 0.0012;
|
||||
if (mech.energy < 0) {
|
||||
mech.fieldCDcycle = mech.cycle + 120;
|
||||
mech.energy = 0;
|
||||
}
|
||||
mech.energy -= 0.005;
|
||||
// if (mech.energy < 0) {
|
||||
// mech.fieldCDcycle = mech.cycle + 120;
|
||||
// mech.energy = 0;
|
||||
// }
|
||||
//calculate laser collision
|
||||
let best;
|
||||
let range = mod.isPlasmaRange * (120 + 300 * Math.sqrt(Math.random()))
|
||||
@@ -2164,7 +2291,7 @@ const b = {
|
||||
y: best.y
|
||||
};
|
||||
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.locatePlayer();
|
||||
//push mobs away
|
||||
@@ -3612,13 +3739,13 @@ const b = {
|
||||
y: mech.pos.y + 3000 * Math.sin(mech.angle)
|
||||
}, dmg, 0, true);
|
||||
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({
|
||||
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),
|
||||
y: history.position.y + 3000 * Math.sin(history.angle)
|
||||
y: history.position.y + 3000 * Math.sin(history.angle) - mech.yPosDifference
|
||||
}, dmg, 0, true);
|
||||
}
|
||||
ctx.stroke();
|
||||
@@ -3642,130 +3769,38 @@ 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]
|
||||
};
|
||||
gunRewind: { //this gun is added with a mod
|
||||
name: "CPT gun",
|
||||
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",
|
||||
ammo: 0,
|
||||
ammoPack: Infinity,
|
||||
have: false,
|
||||
isRewinding: false,
|
||||
lastFireCycle: 0,
|
||||
holdCount: 0,
|
||||
fire() {
|
||||
if (this.lastFireCycle === mech.cycle - 1) { //button has been held down
|
||||
this.rewindCount += 7;
|
||||
const DRAIN = 0.01
|
||||
if (this.rewindCount > 599 || mech.energy < DRAIN) {
|
||||
this.rewindCount = 0;
|
||||
mech.resetHistory();
|
||||
mech.fireCDcycle = mech.cycle + Math.floor(60 * b.fireCD); // cool down
|
||||
} else {
|
||||
mech.energy -= DRAIN
|
||||
mech.immuneCycle = mech.cycle + 5; //player is immune to collision damage for 5 cycles
|
||||
let history = mech.history[(mech.cycle - this.rewindCount) % 600]
|
||||
Matter.Body.setPosition(player, history.position);
|
||||
Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y });
|
||||
if (mech.health !== history.health) {
|
||||
mech.health = history.health
|
||||
mech.displayHealth();
|
||||
}
|
||||
}
|
||||
} else { //button is held the first time
|
||||
this.rewindCount = 0;
|
||||
}
|
||||
};
|
||||
//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())
|
||||
});
|
||||
this.lastFireCycle = mech.cycle;
|
||||
}
|
||||
}
|
||||
};
|
||||
48
js/game.js
48
js/game.js
@@ -596,7 +596,17 @@ const game = {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -761,24 +771,24 @@ const game = {
|
||||
x: 0,
|
||||
y: 40
|
||||
});
|
||||
if ((playerHead.position.y - player.position.y) > 0) {
|
||||
Matter.Body.translate(playerHead, {
|
||||
x: 0,
|
||||
y: 40
|
||||
});
|
||||
if ((playerHead.position.y - player.position.y) > 0) {
|
||||
Matter.Body.translate(playerHead, {
|
||||
x: 0,
|
||||
y: 40
|
||||
});
|
||||
if ((playerHead.position.y - player.position.y) > 0) {
|
||||
Matter.Body.translate(playerHead, {
|
||||
x: 0,
|
||||
y: 40
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// if ((playerHead.position.y - player.position.y) > 0) {
|
||||
// Matter.Body.translate(playerHead, {
|
||||
// x: 0,
|
||||
// y: 40
|
||||
// });
|
||||
// if ((playerHead.position.y - player.position.y) > 0) {
|
||||
// Matter.Body.translate(playerHead, {
|
||||
// x: 0,
|
||||
// y: 40
|
||||
// });
|
||||
// if ((playerHead.position.y - player.position.y) > 0) {
|
||||
// Matter.Body.translate(playerHead, {
|
||||
// x: 0,
|
||||
// y: 40
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
} else if (mech.crouch && ((playerHead.position.y - player.position.y) > 10)) {
|
||||
Matter.Body.translate(playerHead, {
|
||||
x: 0,
|
||||
|
||||
22
js/index.js
22
js/index.js
@@ -787,7 +787,7 @@ window.addEventListener("keydown", function(event) {
|
||||
break
|
||||
}
|
||||
if (game.testing) {
|
||||
switch (event.key) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
case "o":
|
||||
game.isAutoZoom = false;
|
||||
game.zoomScale /= 0.9;
|
||||
@@ -1082,3 +1082,23 @@ function cycle() {
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//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);
|
||||
20
js/level.js
20
js/level.js
@@ -13,17 +13,15 @@ const level = {
|
||||
start() {
|
||||
if (level.levelsCleared === 0) { //this code only runs on the first level
|
||||
// game.enableConstructMode() //used to build maps in testing mode
|
||||
// level.difficultyIncrease(19)
|
||||
// level.difficultyIncrease(1)
|
||||
// game.zoomScale = 1000;
|
||||
// game.setZoom();
|
||||
// mech.setField("plasma torch")
|
||||
// b.giveGuns("wave beam")
|
||||
// mod.giveMod("micro-extruder")
|
||||
// mod.giveMod("piezoelectricity")
|
||||
// mod.giveMod("CPT reversal")
|
||||
// mod.giveMod("CPT gun")
|
||||
// for (let i = 0; i < 15; i++) mod.giveMod("plasma jet")
|
||||
|
||||
|
||||
|
||||
level.intro(); //starting level
|
||||
// level.testing(); //not in rotation
|
||||
// level.finalBoss() //final boss level
|
||||
@@ -163,12 +161,12 @@ const level = {
|
||||
// spawn.launcherBoss(1200, -500)
|
||||
// spawn.laserTargetingBoss(1600, -400)
|
||||
// spawn.striker(1600, -500)
|
||||
spawn.shooter(1700, -120)
|
||||
// spawn.shooter(1700, -120)
|
||||
// spawn.bomberBoss(1400, -500)
|
||||
// spawn.sniper(1800, -120)
|
||||
// spawn.cellBossCulture(1600, -500)
|
||||
// spawn.spiderBoss(1600, -500)
|
||||
// spawn.laser(1200, -500)
|
||||
spawn.streamBoss(1600, -500)
|
||||
// spawn.beamer(1200, -500)
|
||||
// spawn.shield(mob[mob.length - 1], 1800, -120, 1);
|
||||
|
||||
// spawn.nodeBoss(1200, -500, "launcher")
|
||||
@@ -793,9 +791,9 @@ const level = {
|
||||
powerUps.spawnBossPowerUp(-125, -1760);
|
||||
} else {
|
||||
if (Math.random() < 0.5) {
|
||||
spawn.randomLevelBoss(700, -1550, ["shooterBoss", "launcherBoss", "laserTargetingBoss"]);
|
||||
spawn.randomLevelBoss(700, -1550, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]);
|
||||
} 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
|
||||
@@ -942,7 +940,7 @@ const level = {
|
||||
spawn.randomMob(3600, 1725, 0.9);
|
||||
spawn.randomMob(4100, 1225, 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
|
||||
},
|
||||
satellite() {
|
||||
|
||||
@@ -481,7 +481,7 @@ const mobs = {
|
||||
}
|
||||
}
|
||||
};
|
||||
if (this.seePlayer.recall) {
|
||||
if (this.seePlayer.recall && !this.isSlowed) {
|
||||
this.torque = this.lookTorque * this.inertia * 2;
|
||||
|
||||
const seeRange = 2500;
|
||||
|
||||
132
js/mods.js
132
js/mods.js
@@ -386,22 +386,6 @@ const mod = {
|
||||
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",
|
||||
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();
|
||||
},
|
||||
remove() {
|
||||
mod.nailBotCount = 0;
|
||||
mod.nailBotCount -= this.count;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -608,7 +592,7 @@ const mod = {
|
||||
b.foamBot();
|
||||
},
|
||||
remove() {
|
||||
mod.foamBotCount = 0;
|
||||
mod.foamBotCount -= this.count;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -647,7 +631,7 @@ const mod = {
|
||||
b.boomBot();
|
||||
},
|
||||
remove() {
|
||||
mod.boomBotCount = 0;
|
||||
mod.boomBotCount -= this.count;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -686,7 +670,7 @@ const mod = {
|
||||
b.laserBot();
|
||||
},
|
||||
remove() {
|
||||
mod.laserBotCount = 0;
|
||||
mod.laserBotCount -= this.count;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -725,7 +709,7 @@ const mod = {
|
||||
mod.orbitBotCount++;
|
||||
},
|
||||
remove() {
|
||||
mod.orbitBotCount = 0;
|
||||
mod.orbitBotCount -= this.count;
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1074,9 +1058,9 @@ const mod = {
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
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() {
|
||||
mod.isRewindAvoidDeath = true;
|
||||
},
|
||||
@@ -1135,13 +1119,13 @@ const mod = {
|
||||
},
|
||||
{
|
||||
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,
|
||||
count: 0,
|
||||
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: () => {
|
||||
mod.energyRegen = 0;
|
||||
mech.fieldRegen = mod.energyRegen;
|
||||
@@ -1157,7 +1141,7 @@ const mod = {
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
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",
|
||||
effect: () => {
|
||||
@@ -1328,13 +1312,13 @@ const mod = {
|
||||
},
|
||||
{
|
||||
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,
|
||||
count: 0,
|
||||
allowed() {
|
||||
return mod.isArmorFromPowerUps
|
||||
},
|
||||
requires: "crystallized armor",
|
||||
requires: "inductive coupling",
|
||||
effect() {
|
||||
mod.isEndLevelPowerUp = true;
|
||||
},
|
||||
@@ -1535,7 +1519,7 @@ const mod = {
|
||||
if (mod.mods[i].count > 0) have.push(i)
|
||||
}
|
||||
const choose = have[Math.floor(Math.random() * have.length)]
|
||||
game.makeTextLog(`<div class='circle mod'></div> <strong>${mod.mods[choose].name}</strong> removed by reallocation`, 300)
|
||||
game.makeTextLog(`<div class='circle mod'></div> <strong>${mod.mods[choose].name}</strong> removed by monte carlo experiment`, 300)
|
||||
for (let i = 0; i < mod.mods[choose].count; i++) {
|
||||
powerUps.spawn(mech.pos.x, mech.pos.y, "mod");
|
||||
}
|
||||
@@ -1936,9 +1920,49 @@ const mod = {
|
||||
//************************************************** gun
|
||||
//************************************************** 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",
|
||||
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,
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
@@ -1953,9 +1977,26 @@ const mod = {
|
||||
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",
|
||||
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,
|
||||
maxCount: 3,
|
||||
count: 0,
|
||||
@@ -2783,7 +2824,7 @@ const mod = {
|
||||
},
|
||||
{
|
||||
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,
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
@@ -3259,8 +3300,8 @@ const mod = {
|
||||
mod.giveMod("orbital-bot upgrade")
|
||||
mod.setModToNonRefundable("orbital-bot upgrade")
|
||||
for (let i = 0; i < 2; i++) {
|
||||
b.orbitalBot()
|
||||
mod.orbitalBotCount++;
|
||||
b.orbitBot()
|
||||
mod.orbitBotCount++;
|
||||
}
|
||||
})
|
||||
//choose random function from the array and run it
|
||||
@@ -3370,6 +3411,23 @@ const mod = {
|
||||
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",
|
||||
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,
|
||||
isRewindGrenade: null,
|
||||
isExtruder: null,
|
||||
isEndLevelPowerUp: null
|
||||
isEndLevelPowerUp: null,
|
||||
isRewindGun: null
|
||||
|
||||
}
|
||||
507
js/player.js
507
js/player.js
@@ -103,6 +103,7 @@ const mech = {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
yPosDifference: 24.285923217549026, //player.position.y - mech.pos.y
|
||||
Sy: 0, //adds a smoothing effect to vertical only
|
||||
Vx: 0,
|
||||
Vy: 0,
|
||||
@@ -135,11 +136,11 @@ const mech = {
|
||||
transY: 0,
|
||||
history: [], //tracks the last second of player position
|
||||
resetHistory() {
|
||||
for (let i = 0; i < 300; i++) { //reset history
|
||||
for (let i = 0; i < 600; i++) { //reset history
|
||||
mech.history[i] = {
|
||||
position: {
|
||||
x: mech.pos.x,
|
||||
y: mech.pos.y,
|
||||
x: player.position.x,
|
||||
y: player.position.y,
|
||||
},
|
||||
velocity: {
|
||||
x: player.velocity.x,
|
||||
@@ -159,10 +160,10 @@ const mech = {
|
||||
|
||||
//tracks the last second of player information
|
||||
// console.log(mech.history)
|
||||
mech.history.splice(mech.cycle % 300, 1, {
|
||||
mech.history.splice(mech.cycle % 600, 1, {
|
||||
position: {
|
||||
x: mech.pos.x,
|
||||
y: mech.pos.y,
|
||||
x: player.position.x,
|
||||
y: player.position.y,
|
||||
},
|
||||
velocity: {
|
||||
x: player.velocity.x,
|
||||
@@ -461,14 +462,14 @@ const mech = {
|
||||
if (mod.isBotArmor) dmg *= 0.97 ** mod.totalBots()
|
||||
if (mod.isHarmArmor && mech.lastHarmCycle + 600 > mech.cycle) dmg *= 0.33;
|
||||
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.isEntanglement && b.inventory[0] === b.activeGun) {
|
||||
for (let i = 0, len = b.inventory.length; i < len; i++) dmg *= 0.87 // 1 - 0.15
|
||||
}
|
||||
return dmg
|
||||
},
|
||||
rewind(steps) {
|
||||
rewind(steps) { // mech.rewind(Math.floor(Math.min(599, 137 * mech.energy)))
|
||||
if (mod.isRewindGrenade) {
|
||||
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
|
||||
@@ -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.setVelocity(player, { x: history.velocity.x, y: history.velocity.y });
|
||||
// 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.translate(-canvas.width2 + mech.transX, -canvas.height2 + mech.transY); //translate
|
||||
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.y = history.position.y
|
||||
mech.draw();
|
||||
@@ -547,7 +548,7 @@ const mech = {
|
||||
if (mod.isRewindBot) {
|
||||
const len = steps * 0.042 * mod.isRewindBot
|
||||
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({
|
||||
x: where.x + 100 * (Math.random() - 0.5),
|
||||
y: where.y + 100 * (Math.random() - 0.5)
|
||||
@@ -558,8 +559,7 @@ const mech = {
|
||||
},
|
||||
damage(dmg) {
|
||||
if (mod.isRewindAvoidDeath && mech.energy > 0.66) {
|
||||
const steps = Math.floor(Math.min(299, 137 * mech.energy))
|
||||
mech.rewind(steps)
|
||||
mech.rewind(Math.floor(Math.min(299, 137 * mech.energy)))
|
||||
return
|
||||
}
|
||||
mech.lastHarmCycle = mech.cycle
|
||||
@@ -764,7 +764,7 @@ const mech = {
|
||||
ctx.stroke();
|
||||
// ctx.beginPath();
|
||||
// ctx.arc(15, 0, 3, 0, 2 * Math.PI);
|
||||
// ctx.fillStyle = '#9cf' //'#0cf';
|
||||
// ctx.fillStyle = '#0cf';
|
||||
// ctx.fill()
|
||||
ctx.restore();
|
||||
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",
|
||||
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
|
||||
mech.drop();
|
||||
mech.duplicateChance = 0.1
|
||||
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
|
||||
// isOn: false,
|
||||
// isReady: true,
|
||||
@@ -2207,237 +2300,235 @@ const mech = {
|
||||
// angle: 0,
|
||||
// unit:{x:0,y:0},
|
||||
// }
|
||||
mech.hold = function() {
|
||||
if (mech.hole.isOn) {
|
||||
// draw holes
|
||||
mech.fieldRange = 0.97 * mech.fieldRange + 0.03 * (50 + 10 * Math.sin(game.cycle * 0.025))
|
||||
const semiMajorAxis = mech.fieldRange + 30
|
||||
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 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)
|
||||
ctx.beginPath();
|
||||
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.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.fillStyle = `rgba(255,255,255,${200 / mech.fieldRange / mech.fieldRange})` //"rgba(0,0,0,0.1)"
|
||||
ctx.fill();
|
||||
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.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.fill();
|
||||
if (mech.hole.isOn) {
|
||||
// draw holes
|
||||
mech.fieldRange = 0.97 * mech.fieldRange + 0.03 * (50 + 10 * Math.sin(game.cycle * 0.025))
|
||||
const semiMajorAxis = mech.fieldRange + 30
|
||||
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 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)
|
||||
ctx.beginPath();
|
||||
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.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.fillStyle = `rgba(255,255,255,${200 / mech.fieldRange / mech.fieldRange})` //"rgba(0,0,0,0.1)"
|
||||
ctx.fill();
|
||||
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.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.fill();
|
||||
|
||||
//suck power ups
|
||||
for (let i = 0, len = powerUp.length; i < len; ++i) {
|
||||
//which hole is closer
|
||||
const dxP1 = mech.hole.pos1.x - powerUp[i].position.x;
|
||||
const dyP1 = mech.hole.pos1.y - powerUp[i].position.y;
|
||||
const dxP2 = mech.hole.pos2.x - powerUp[i].position.x;
|
||||
const dyP2 = mech.hole.pos2.y - powerUp[i].position.y;
|
||||
let dxP, dyP, dist2
|
||||
if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) {
|
||||
dxP = dxP1
|
||||
dyP = dyP1
|
||||
} else {
|
||||
dxP = dxP2
|
||||
dyP = dyP2
|
||||
}
|
||||
dist2 = dxP * dxP + dyP * dyP;
|
||||
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.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * game.g; //negate gravity
|
||||
Matter.Body.setVelocity(powerUp[i], { //extra friction
|
||||
x: powerUp[i].velocity.x * 0.05,
|
||||
y: powerUp[i].velocity.y * 0.05
|
||||
});
|
||||
if (dist2 < 1000 && !game.isChoosing) { //use power up if it is close enough
|
||||
mech.fieldRange *= 0.8
|
||||
powerUps.onPickUp(powerUp[i].position);
|
||||
powerUp[i].effect();
|
||||
Matter.World.remove(engine.world, powerUp[i]);
|
||||
powerUp.splice(i, 1);
|
||||
break; //because the array order is messed up after splice
|
||||
}
|
||||
//suck power ups
|
||||
for (let i = 0, len = powerUp.length; i < len; ++i) {
|
||||
//which hole is closer
|
||||
const dxP1 = mech.hole.pos1.x - powerUp[i].position.x;
|
||||
const dyP1 = mech.hole.pos1.y - powerUp[i].position.y;
|
||||
const dxP2 = mech.hole.pos2.x - powerUp[i].position.x;
|
||||
const dyP2 = mech.hole.pos2.y - powerUp[i].position.y;
|
||||
let dxP, dyP, dist2
|
||||
if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) {
|
||||
dxP = dxP1
|
||||
dyP = dyP1
|
||||
} else {
|
||||
dxP = dxP2
|
||||
dyP = dyP2
|
||||
}
|
||||
dist2 = dxP * dxP + dyP * dyP;
|
||||
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.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * game.g; //negate gravity
|
||||
Matter.Body.setVelocity(powerUp[i], { //extra friction
|
||||
x: powerUp[i].velocity.x * 0.05,
|
||||
y: powerUp[i].velocity.y * 0.05
|
||||
});
|
||||
if (dist2 < 1000 && !game.isChoosing) { //use power up if it is close enough
|
||||
mech.fieldRange *= 0.8
|
||||
powerUps.onPickUp(powerUp[i].position);
|
||||
powerUp[i].effect();
|
||||
Matter.World.remove(engine.world, powerUp[i]);
|
||||
powerUp.splice(i, 1);
|
||||
break; //because the array order is messed up after splice
|
||||
}
|
||||
}
|
||||
//suck and shrink blocks
|
||||
const suckRange = 500
|
||||
const shrinkRange = 100
|
||||
const shrinkScale = 0.97;
|
||||
const slowScale = 0.9
|
||||
for (let i = 0, len = body.length; i < len; i++) {
|
||||
if (!body[i].isNotHoldable) {
|
||||
const dist1 = Vector.magnitude(Vector.sub(mech.hole.pos1, body[i].position))
|
||||
const dist2 = Vector.magnitude(Vector.sub(mech.hole.pos2, body[i].position))
|
||||
if (dist1 < dist2) {
|
||||
if (dist1 < suckRange) {
|
||||
const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos1, body[i].position)), 1)
|
||||
const slow = Vector.mult(body[i].velocity, slowScale)
|
||||
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)
|
||||
}
|
||||
//suck and shrink blocks
|
||||
const suckRange = 500
|
||||
const shrinkRange = 100
|
||||
const shrinkScale = 0.97;
|
||||
const slowScale = 0.9
|
||||
for (let i = 0, len = body.length; i < len; i++) {
|
||||
if (!body[i].isNotHoldable) {
|
||||
const dist1 = Vector.magnitude(Vector.sub(mech.hole.pos1, body[i].position))
|
||||
const dist2 = Vector.magnitude(Vector.sub(mech.hole.pos2, body[i].position))
|
||||
if (dist1 < dist2) {
|
||||
if (dist1 < suckRange) {
|
||||
const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos1, body[i].position)), 1)
|
||||
const slow = Vector.mult(body[i].velocity, slowScale)
|
||||
Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
|
||||
//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);
|
||||
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 < mech.maxEnergy * 2) mech.energy = mech.maxEnergy * 2
|
||||
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.pos1, Vector.rotate({
|
||||
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));
|
||||
Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(mech.hole.unit, Math.PI / 2), -15));
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
} 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)
|
||||
Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
|
||||
//shrink
|
||||
if (Vector.magnitude(Vector.sub(mech.hole.pos2, 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 < mech.maxEnergy * 2) mech.energy = mech.maxEnergy * 2
|
||||
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.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
|
||||
const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(game.mouseInGame, mech.pos)), 50), game.mouseInGame)
|
||||
const scale = 60
|
||||
// console.log(Matter.Query.region(map, bounds))
|
||||
if (mech.hole.isReady &&
|
||||
(
|
||||
Matter.Query.region(map, {
|
||||
min: {
|
||||
x: game.mouseInGame.x - scale,
|
||||
y: game.mouseInGame.y - scale
|
||||
},
|
||||
max: {
|
||||
x: game.mouseInGame.x + scale,
|
||||
y: game.mouseInGame.y + scale
|
||||
}
|
||||
}).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, player.position, game.mouseInGame).length === 0 &&
|
||||
// Matter.Query.ray(map, player.position, justPastMouse).length === 0
|
||||
)
|
||||
) {
|
||||
const sub = Vector.sub(game.mouseInGame, mech.pos)
|
||||
const mag = Vector.magnitude(sub)
|
||||
const drain = 0.03 + 0.005 * Math.sqrt(mag)
|
||||
if (mech.energy > drain && mag > 300) {
|
||||
mech.energy -= drain
|
||||
mech.hole.isReady = false;
|
||||
mech.fieldRange = 0
|
||||
Matter.Body.setPosition(player, game.mouseInGame);
|
||||
mech.buttonCD_jump = 0 //this might fix a bug with jumping
|
||||
const velocity = Vector.mult(Vector.normalise(sub), 18)
|
||||
Matter.Body.setVelocity(player, {
|
||||
x: velocity.x,
|
||||
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
|
||||
// move bots to follow player
|
||||
for (let i = 0; i < bullet.length; i++) {
|
||||
if (bullet[i].botType) {
|
||||
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
|
||||
x: 250 * (Math.random() - 0.5),
|
||||
y: 250 * (Math.random() - 0.5)
|
||||
}));
|
||||
Matter.Body.setVelocity(bullet[i], {
|
||||
x: 0,
|
||||
y: 0
|
||||
});
|
||||
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 scale = 60
|
||||
// console.log(Matter.Query.region(map, bounds))
|
||||
if (mech.hole.isReady &&
|
||||
(
|
||||
Matter.Query.region(map, {
|
||||
min: {
|
||||
x: game.mouseInGame.x - scale,
|
||||
y: game.mouseInGame.y - scale
|
||||
},
|
||||
max: {
|
||||
x: game.mouseInGame.x + scale,
|
||||
y: game.mouseInGame.y + scale
|
||||
}
|
||||
}).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, player.position, game.mouseInGame).length === 0 &&
|
||||
// Matter.Query.ray(map, player.position, justPastMouse).length === 0
|
||||
)
|
||||
) {
|
||||
const sub = Vector.sub(game.mouseInGame, mech.pos)
|
||||
const mag = Vector.magnitude(sub)
|
||||
const drain = 0.03 + 0.005 * Math.sqrt(mag)
|
||||
if (mech.energy > drain && mag > 300) {
|
||||
mech.energy -= drain
|
||||
mech.hole.isReady = false;
|
||||
mech.fieldRange = 0
|
||||
Matter.Body.setPosition(player, game.mouseInGame);
|
||||
mech.buttonCD_jump = 0 //this might fix a bug with jumping
|
||||
const velocity = Vector.mult(Vector.normalise(sub), 18)
|
||||
Matter.Body.setVelocity(player, {
|
||||
x: velocity.x,
|
||||
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
|
||||
// move bots to follow player
|
||||
for (let i = 0; i < bullet.length; i++) {
|
||||
if (bullet[i].botType) {
|
||||
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
|
||||
x: 250 * (Math.random() - 0.5),
|
||||
y: 250 * (Math.random() - 0.5)
|
||||
}));
|
||||
Matter.Body.setVelocity(bullet[i], {
|
||||
x: 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 {
|
||||
mech.grabPowerUp();
|
||||
}
|
||||
} 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.grabPowerUp();
|
||||
}
|
||||
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()
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
145
js/spawn.js
145
js/spawn.js
@@ -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
|
||||
spawn[options[Math.floor(Math.random() * options.length)]](x, y)
|
||||
},
|
||||
@@ -101,15 +101,34 @@ const spawn = {
|
||||
level.bossKilled = true;
|
||||
level.exit.x = 5500;
|
||||
level.exit.y = -330;
|
||||
//ramp up damage
|
||||
for (let i = 0; i < 4; i++) level.difficultyIncrease(game.difficultyMode)
|
||||
|
||||
|
||||
//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 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));
|
||||
}
|
||||
//ramp up damage
|
||||
for (let i = 0; i < 5; i++) level.difficultyIncrease(game.difficultyMode)
|
||||
//push away mobs
|
||||
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() {};
|
||||
@@ -761,7 +780,7 @@ const spawn = {
|
||||
let me = mob[mob.length - 1];
|
||||
me.stroke = "transparent"; //used for drawSneaker
|
||||
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.frictionAir = 0.025;
|
||||
me.collisionFilter.mask = cat.player | cat.bullet
|
||||
@@ -775,8 +794,15 @@ const spawn = {
|
||||
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();
|
||||
if (this.seePlayer.recall) {
|
||||
//eventHorizon waves in and out
|
||||
@@ -832,7 +858,7 @@ const spawn = {
|
||||
me.isBoss = true;
|
||||
me.stroke = "transparent"; //used for drawSneaker
|
||||
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.collisionFilter.mask = cat.player | cat.bullet
|
||||
// me.frictionAir = 0.005;
|
||||
@@ -865,7 +891,14 @@ const spawn = {
|
||||
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();
|
||||
if (this.seePlayer.recall) {
|
||||
//accelerate towards the player
|
||||
@@ -1865,7 +1898,7 @@ const spawn = {
|
||||
me.onHit = function() {
|
||||
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.g = 0.001; //required if using 'gravity'
|
||||
me.frictionAir = 0;
|
||||
@@ -2106,7 +2139,7 @@ const spawn = {
|
||||
me.frictionStatic = 0;
|
||||
me.friction = 0;
|
||||
me.frictionAir = 0.02;
|
||||
me.memory = 420 * game.CDScale;
|
||||
me.memory = 420;
|
||||
me.repulsionRange = 1200000; //squared
|
||||
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
|
||||
@@ -2124,7 +2157,7 @@ const spawn = {
|
||||
Matter.Body.setAngularVelocity(this, 0.11)
|
||||
//fire a bullet from each vertex
|
||||
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
|
||||
const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[i]))), -10)
|
||||
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
|
||||
mobs.spawn(x, y, sides, radius, "rgb(255,0,255)");
|
||||
let me = mob[mob.length - 1];
|
||||
@@ -2143,10 +2253,10 @@ const spawn = {
|
||||
me.onHit = function() {
|
||||
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.accelMag = 0.00017 * (0.8 + 0.4 * Math.random()) * game.accelScale;
|
||||
me.frictionAir = 0.01 * (0.8 + 0.4 * Math.random());
|
||||
me.accelMag = 0.00017 * game.accelScale; //* (0.8 + 0.4 * Math.random())
|
||||
me.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random());
|
||||
me.restitution = 0.5;
|
||||
me.leaveBody = false;
|
||||
me.dropPowerUp = false;
|
||||
@@ -2190,6 +2300,7 @@ const spawn = {
|
||||
//run this function on hitting player
|
||||
this.explode();
|
||||
};
|
||||
Matter.Body.setDensity(me, 0.0005); //normal is 0.001
|
||||
me.g = 0.0001; //required if using 'gravity'
|
||||
me.accelMag = 0.0003 * game.accelScale;
|
||||
me.memory = 30;
|
||||
|
||||
@@ -587,6 +587,7 @@ em {
|
||||
/* background: rgb(116, 102, 238); */
|
||||
/* background: hsl(253, 57%, 52%); */
|
||||
background: hsl(255, 100%, 71%);
|
||||
/* background: hsl(282, 100%, 64%); */
|
||||
}
|
||||
|
||||
/* .grey {
|
||||
|
||||
83
todo.txt
83
todo.txt
@@ -1,22 +1,16 @@
|
||||
******************************************************** 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: 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.
|
||||
|
||||
mod: inductive coupling - 4 max health per power up, but limited to 44 max health per level (replaces crystalized armor)
|
||||
mod: transceiver chip - use all the power ups left over at the end of a level
|
||||
mod: catabolism - does a flat 5 damage to your health for 3 ammo (was 2% of max health for 1 ammo)
|
||||
|
||||
******************************************************** BUGS ********************************************************
|
||||
|
||||
give worm hole pair production?
|
||||
|
||||
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
|
||||
check for crouch after rewind
|
||||
CPT, tesseract
|
||||
|
||||
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
|
||||
|
||||
(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
|
||||
players have extra gravity
|
||||
might be from the short jump code
|
||||
add in a check every 7 seconds to try and fix it
|
||||
this fix was added and it is working for some cases
|
||||
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
|
||||
|
||||
@@ -44,7 +40,39 @@ mod and mob are too similar
|
||||
|
||||
******************************************************** 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
|
||||
|
||||
@@ -75,15 +103,6 @@ make different move methods
|
||||
|
||||
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
|
||||
this is like a dodge roll
|
||||
mod for standing wave?, cloaking?
|
||||
@@ -372,11 +391,15 @@ robot AI communication
|
||||
bottom left message
|
||||
tab title?
|
||||
style
|
||||
output console.log?
|
||||
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
|
||||
square edges
|
||||
in baby talk?
|
||||
with random ASCII gibberish letters
|
||||
black text on bottom right with no background?
|
||||
or white text, or yellow
|
||||
end each message with a hexadecimal encryption code/hash
|
||||
message after selecting each new (mod / gun / field)
|
||||
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
|
||||
n-gon escape simulation ${random seed}
|
||||
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
|
||||
maybe the game ending should ask you to open the console and type in some commands like in the end of doki doki
|
||||
|
||||
Reference in New Issue
Block a user