final boss
missile moves slightly differently it used to slow when locked on to a target now it slows when turning missiles explode when near any mob wormhole mod: cosmic string - now stuns mobs and applies radiation damage mod time dilation: - quadruple your default energy regeneration added final boss level, it's still in progress so I'd love some feedback also the game loops back to the intro level after the boss I'll be working on the ending in the next patch, so the intro level is just a placeholder
This commit is contained in:
119
js/bullet.js
119
js/bullet.js
@@ -278,39 +278,42 @@ const b = {
|
||||
},
|
||||
missile(where, angle, speed, size = 1, spawn = 0) {
|
||||
const me = bullet.length;
|
||||
bullet[me] = Bodies.rectangle(where.x, where.y, 30 * size, 4 * size, b.fireAttributes(angle));
|
||||
const thrust = 0.00417 * bullet[me].mass;
|
||||
Matter.Body.setVelocity(bullet[me], {
|
||||
x: mech.Vx / 2 + speed * Math.cos(angle),
|
||||
y: mech.Vy / 2 + speed * Math.sin(angle)
|
||||
});
|
||||
World.add(engine.world, bullet[me]); //add bullet to world
|
||||
bullet[me].frictionAir = 0.023
|
||||
bullet[me].endCycle = game.cycle + Math.floor((280 + 40 * Math.random()) * mod.isBulletsLastLonger);
|
||||
bullet[me].explodeRad = 170 + 60 * Math.random();
|
||||
bullet[me].lookFrequency = Math.floor(21 + Math.random() * 7);
|
||||
bullet[me].onEnd = function () {
|
||||
bullet[me] = Bodies.rectangle(where.x, where.y, 30 * size, 4 * size, {
|
||||
angle: angle,
|
||||
friction: 0.5,
|
||||
frictionAir: 0.045,
|
||||
dmg: 0, //damage done in addition to the damage from momentum
|
||||
classType: "bullet",
|
||||
endCycle: game.cycle + Math.floor((230 + 40 * Math.random()) * mod.isBulletsLastLonger),
|
||||
collisionFilter: {
|
||||
category: cat.bullet,
|
||||
mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield
|
||||
},
|
||||
minDmgSpeed: 10,
|
||||
lookFrequency: Math.floor(15 + Math.random() * 3),
|
||||
explodeRad: 170 + 60 * Math.random(),
|
||||
beforeDmg() {
|
||||
this.tryToLockOn();
|
||||
this.endCycle = 0; //bullet ends cycle after doing damage // also triggers explosion
|
||||
}, //this.endCycle = 0 //triggers despawn
|
||||
onEnd() {
|
||||
b.explosion(this.position, this.explodeRad * size); //makes bullet do explosive damage at end
|
||||
if (spawn) {
|
||||
for (let i = 0; i < mod.recursiveMissiles; i++) {
|
||||
if (0.3 - 0.03 * i > Math.random()) {
|
||||
if (0.2 - 0.02 * i > Math.random()) {
|
||||
b.missile(this.position, this.angle + Math.PI + 0.5 * (Math.random() - 0.5), 0, 0.33 + size, mod.recursiveMissiles)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bullet[me].beforeDmg = function () {
|
||||
this.tryToLockOn();
|
||||
this.endCycle = 0; //bullet ends cycle after doing damage // also triggers explosion
|
||||
};
|
||||
bullet[me].lockedOn = null;
|
||||
bullet[me].tryToLockOn = function () {
|
||||
this.lockedOn = null;
|
||||
},
|
||||
lockedOn: null,
|
||||
tryToLockOn() {
|
||||
let closeDist = Infinity;
|
||||
|
||||
//look for closest target to where the missile will be in 30 cycles
|
||||
const futurePos = Vector.add(this.position, Vector.mult(this.velocity, 30))
|
||||
const futurePos = Vector.add(this.position, Vector.mult(this.velocity, 50))
|
||||
this.lockedOn = null;
|
||||
// const futurePos = this.lockedOn ? :Vector.add(this.position, Vector.mult(this.velocity, 50))
|
||||
for (let i = 0, len = mob.length; i < len; ++i) {
|
||||
if (
|
||||
mob[i].alive && mob[i].dropPowerUp &&
|
||||
@@ -321,25 +324,24 @@ const b = {
|
||||
if (futureDist < closeDist) {
|
||||
closeDist = futureDist;
|
||||
this.lockedOn = mob[i];
|
||||
this.frictionAir = 0.05; //extra friction once a target it locked
|
||||
// this.frictionAir = 0.04; //extra friction once a target it locked
|
||||
}
|
||||
if (Vector.magnitude(Vector.sub(this.position, mob[i].position) < this.explodeRad)) {
|
||||
this.endCycle = 0; //bullet ends cycle after doing damage //also triggers explosion
|
||||
mob[i].lockedOn.damage(b.dmgScale * 2 * size); //does extra damage to target
|
||||
}
|
||||
}
|
||||
}
|
||||
//explode when bullet is close enough to target
|
||||
if (this.lockedOn && Vector.magnitude(Vector.sub(this.position, this.lockedOn.position)) < this.explodeRad) {
|
||||
// console.log('hit')
|
||||
this.endCycle = 0; //bullet ends cycle after doing damage //also triggers explosion
|
||||
this.lockedOn.damage(b.dmgScale * 4 * size); //does extra damage to target
|
||||
}
|
||||
};
|
||||
bullet[me].do = function () {
|
||||
},
|
||||
do() {
|
||||
if (!mech.isBodiesAsleep) {
|
||||
if (!(mech.cycle % this.lookFrequency)) {
|
||||
this.tryToLockOn();
|
||||
}
|
||||
|
||||
//rotate missile towards the target
|
||||
if (this.lockedOn) {
|
||||
if (!(mech.cycle % this.lookFrequency)) this.tryToLockOn();
|
||||
if (this.lockedOn) { //rotate missile towards the target
|
||||
const face = {
|
||||
x: Math.cos(this.angle),
|
||||
y: Math.sin(this.angle)
|
||||
@@ -347,10 +349,13 @@ const b = {
|
||||
const target = Vector.normalise(Vector.sub(this.position, this.lockedOn.position));
|
||||
if (Vector.dot(target, face) > -0.98) {
|
||||
if (Vector.cross(target, face) > 0) {
|
||||
Matter.Body.rotate(this, 0.08);
|
||||
Matter.Body.rotate(this, 0.1);
|
||||
} else {
|
||||
Matter.Body.rotate(this, -0.08);
|
||||
Matter.Body.rotate(this, -0.1);
|
||||
}
|
||||
this.frictionAir = 0.06; //extra friction if turning
|
||||
} else {
|
||||
this.frictionAir = 0.025; //low friction if not turning
|
||||
}
|
||||
}
|
||||
//accelerate in direction bullet is facing
|
||||
@@ -358,8 +363,7 @@ const b = {
|
||||
this.force.x += Math.cos(dir) * thrust;
|
||||
this.force.y += Math.sin(dir) * thrust;
|
||||
|
||||
//draw rocket
|
||||
ctx.beginPath();
|
||||
ctx.beginPath(); //draw rocket
|
||||
ctx.arc(this.position.x - Math.cos(this.angle) * (25 * size - 3) + (Math.random() - 0.5) * 4,
|
||||
this.position.y - Math.sin(this.angle) * (25 * size - 3) + (Math.random() - 0.5) * 4,
|
||||
11 * size, 0, 2 * Math.PI);
|
||||
@@ -374,7 +378,14 @@ const b = {
|
||||
ctx.fillStyle = "rgba(255,155,0,0.5)";
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const thrust = 0.0044 * bullet[me].mass;
|
||||
Matter.Body.setVelocity(bullet[me], {
|
||||
x: mech.Vx / 2 + speed * Math.cos(angle),
|
||||
y: mech.Vy / 2 + speed * Math.sin(angle)
|
||||
});
|
||||
World.add(engine.world, bullet[me]); //add bullet to world
|
||||
},
|
||||
laser(where = {
|
||||
x: mech.pos.x + 20 * Math.cos(mech.angle),
|
||||
@@ -1302,7 +1313,7 @@ const b = {
|
||||
const radius = 6 + 7 * Math.random()
|
||||
const SPEED = 29 - radius * 0.5; //(mech.crouch ? 32 : 20) - radius * 0.7;
|
||||
const velocity = Vector.mult(Vector.normalise(Vector.sub(target, this.position)), SPEED)
|
||||
b.foam(this.position, velocity, radius + 14 * this.isUpgraded)
|
||||
b.foam(this.position, velocity, radius + 9 * this.isUpgraded)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1713,8 +1724,21 @@ const b = {
|
||||
mask: 0 //cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield
|
||||
},
|
||||
beforeDmg() {},
|
||||
onEnd() {},
|
||||
range: 190 + 50 * mod.isOrbitBotUpgrade, //range is set in bot upgrade too! //150 + (80 + 100 * mod.isOrbitBotUpgrade) * Math.random(), // + 5 * mod.orbitBotCount,
|
||||
onEnd() {
|
||||
//reorder orbital bot positions around a circle
|
||||
let totalOrbitalBots = 0
|
||||
for (let i = 0; i < bullet.length; i++) {
|
||||
if (bullet[i].botType === 'orbit' && bullet[i] !== this) totalOrbitalBots++
|
||||
}
|
||||
let index = 0
|
||||
for (let i = 0; i < bullet.length; i++) {
|
||||
if (bullet[i].botType === 'orbit' && bullet[i] !== this) {
|
||||
bullet[i].phase = (index / totalOrbitalBots) * 2 * Math.PI
|
||||
index++
|
||||
}
|
||||
}
|
||||
},
|
||||
range: 190 + 60 * mod.isOrbitBotUpgrade, //range is set in bot upgrade too! //150 + (80 + 100 * mod.isOrbitBotUpgrade) * Math.random(), // + 5 * mod.orbitBotCount,
|
||||
orbitalSpeed: 0,
|
||||
phase: 2 * Math.PI * Math.random(),
|
||||
do() {
|
||||
@@ -1735,7 +1759,7 @@ const b = {
|
||||
})
|
||||
for (let i = 0; i < q.length; i++) {
|
||||
mobs.statusStun(q[i], 180)
|
||||
const dmg = 0.5 * b.dmgScale * (this.isUpgraded ? 2.25 : 1) * (mod.isCrit ? 4 : 1)
|
||||
const dmg = 0.5 * b.dmgScale * (this.isUpgraded ? 2 : 1) * (mod.isCrit ? 4 : 1)
|
||||
q[i].damage(dmg);
|
||||
q[i].foundPlayer();
|
||||
game.drawList.push({ //add dmg to draw queue
|
||||
@@ -1759,7 +1783,6 @@ const b = {
|
||||
// bullet[me].orbitalSpeed = Math.sqrt(0.7 / bullet[me].range)
|
||||
bullet[me].orbitalSpeed = Math.sqrt(0.25 / bullet[me].range) //also set in bot upgrade too!
|
||||
// bullet[me].phase = (index / mod.orbitBotCount) * 2 * Math.PI
|
||||
|
||||
World.add(engine.world, bullet[me]); //add bullet to world
|
||||
|
||||
//reorder orbital bot positions around a circle
|
||||
@@ -2140,7 +2163,7 @@ const b = {
|
||||
// check if inside a mob
|
||||
q = Matter.Query.point(mob, this.position)
|
||||
for (let i = 0; i < q.length; i++) {
|
||||
let dmg = b.dmgScale * 0.36 / Math.sqrt(q[i].mass) * (mod.waveHelix === 1 ? 1 : 0.66) //1 - 0.4 = 0.6 for helix mod 40% damage reduction
|
||||
let dmg = b.dmgScale * 0.36 / Math.sqrt(q[i].mass) * (mod.waveHelix === 1 ? 1 : 0.8) //1 - 0.4 = 0.6 for helix mod 40% damage reduction
|
||||
q[i].damage(dmg);
|
||||
q[i].foundPlayer();
|
||||
game.drawList.push({ //add dmg to draw queue
|
||||
@@ -2173,7 +2196,7 @@ const b = {
|
||||
for (let i = 0; i < q.length; i++) {
|
||||
slowCheck = 0.3;
|
||||
Matter.Body.setPosition(this, Vector.add(this.position, q[i].velocity)) //move with the medium
|
||||
let dmg = b.dmgScale * 0.36 / Math.sqrt(q[i].mass) * (mod.waveHelix === 1 ? 1 : 0.66) //1 - 0.4 = 0.6 for helix mod 40% damage reduction
|
||||
let dmg = b.dmgScale * 0.36 / Math.sqrt(q[i].mass) * (mod.waveHelix === 1 ? 1 : 0.8) //1 - 0.4 = 0.6 for helix mod 40% damage reduction
|
||||
q[i].damage(dmg);
|
||||
q[i].foundPlayer();
|
||||
game.drawList.push({ //add dmg to draw queue
|
||||
@@ -2243,7 +2266,7 @@ const b = {
|
||||
fire() {
|
||||
if (mod.is3Missiles) {
|
||||
if (mech.crouch) {
|
||||
mech.fireCDcycle = mech.cycle + 60 * b.fireCD; // cool down
|
||||
mech.fireCDcycle = mech.cycle + 50 * b.fireCD; // cool down
|
||||
const direction = {
|
||||
x: Math.cos(mech.angle),
|
||||
y: Math.sin(mech.angle)
|
||||
@@ -2259,7 +2282,7 @@ const b = {
|
||||
bullet[bullet.length - 1].force.y += push.y * (i - 1);
|
||||
}
|
||||
} else {
|
||||
mech.fireCDcycle = mech.cycle + 45 * b.fireCD; // cool down
|
||||
mech.fireCDcycle = mech.cycle + 35 * b.fireCD; // cool down
|
||||
const direction = {
|
||||
x: Math.cos(mech.angle),
|
||||
y: Math.sin(mech.angle)
|
||||
@@ -2276,7 +2299,7 @@ const b = {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mech.fireCDcycle = mech.cycle + Math.floor(mech.crouch ? 45 : 30) * b.fireCD; // cool down
|
||||
mech.fireCDcycle = mech.cycle + Math.floor(mech.crouch ? 40 : 25) * b.fireCD; // cool down
|
||||
b.missile({
|
||||
x: mech.pos.x + 40 * Math.cos(mech.angle),
|
||||
y: mech.pos.y + 40 * Math.sin(mech.angle) - 3
|
||||
|
||||
121
js/game.js
121
js/game.js
@@ -459,65 +459,9 @@ const game = {
|
||||
addGravity(body, game.g);
|
||||
player.force.y += player.mass * game.g;
|
||||
},
|
||||
reset() { //run on first run, and each later run after you die
|
||||
input.endKeySensing();
|
||||
b.removeAllGuns();
|
||||
game.isNoPowerUps = false;
|
||||
mod.setupAllMods(); //sets mods to default values
|
||||
b.setFireCD();
|
||||
game.updateModHUD();
|
||||
powerUps.totalPowerUps = 0;
|
||||
powerUps.reroll.rerolls = 0;
|
||||
mech.setFillColors();
|
||||
mech.maxHealth = 1
|
||||
mech.maxEnergy = 1
|
||||
mech.energy = 1
|
||||
mech.hole.isOn = false
|
||||
game.paused = false;
|
||||
engine.timing.timeScale = 1;
|
||||
game.fpsCap = game.fpsCapDefault;
|
||||
game.isAutoZoom = true;
|
||||
game.makeGunHUD();
|
||||
mech.drop();
|
||||
mech.holdingTarget = null
|
||||
mech.addHealth(Infinity);
|
||||
mech.alive = true;
|
||||
level.onLevel = 0;
|
||||
level.levelsCleared = 0;
|
||||
// reset() { //run on first run, and each later run after you die
|
||||
|
||||
//resetting difficulty
|
||||
game.dmgScale = 0; //increases in level.difficultyIncrease
|
||||
b.dmgScale = 1; //decreases in level.difficultyIncrease
|
||||
game.accelScale = 1;
|
||||
game.lookFreqScale = 1;
|
||||
game.CDScale = 1;
|
||||
game.difficulty = 0;
|
||||
game.difficultyMode = Number(document.getElementById("difficulty-select").value)
|
||||
build.isCustomSelection = false;
|
||||
// if (game.difficultyMode > 2) {
|
||||
// level.difficultyIncrease(game.difficultyMode)
|
||||
// level.difficultyIncrease(game.difficultyMode)
|
||||
// }
|
||||
|
||||
game.clearNow = true;
|
||||
document.getElementById("text-log").style.opacity = 0;
|
||||
document.getElementById("fade-out").style.opacity = 0;
|
||||
document.title = "n-gon";
|
||||
//set to default field
|
||||
mech.fieldMode = 0;
|
||||
game.replaceTextLog = true;
|
||||
game.makeTextLog(`${game.SVGrightMouse}<strong style='font-size:30px;'> ${mech.fieldUpgrades[mech.fieldMode].name}</strong><br><span class='faded'></span><br>${mech.fieldUpgrades[mech.fieldMode].description}`, 600);
|
||||
mech.setField(mech.fieldMode)
|
||||
//exit testing
|
||||
if (game.testing) {
|
||||
game.testing = false;
|
||||
game.loop = game.normalLoop
|
||||
if (game.isConstructionMode) {
|
||||
document.getElementById("construct").style.display = 'none'
|
||||
}
|
||||
}
|
||||
game.isCheating = false
|
||||
},
|
||||
// },
|
||||
firstRun: true,
|
||||
splashReturn() {
|
||||
game.onTitlePage = true;
|
||||
@@ -553,8 +497,8 @@ const game = {
|
||||
document.getElementById("dmg").style.display = "inline";
|
||||
document.getElementById("health-bg").style.display = "inline";
|
||||
|
||||
if (game.firstRun) {
|
||||
mech.spawn(); //spawns the player
|
||||
|
||||
if (game.isCommunityMaps) {
|
||||
level.levels.push("stronghold");
|
||||
level.levels.push("basement");
|
||||
@@ -562,9 +506,64 @@ const game = {
|
||||
level.levels.push("house");
|
||||
}
|
||||
level.levels = shuffle(level.levels); //shuffles order of maps
|
||||
level.levels.unshift("bosses"); //add bosses level to the end of the randomized levels list
|
||||
level.levels.unshift("intro"); //add level to the start of the randomized levels list
|
||||
level.levels.push("gauntlet"); //add level to the end of the randomized levels list
|
||||
level.levels.push("finalBoss"); //add level to the end of the randomized levels list
|
||||
|
||||
input.endKeySensing();
|
||||
b.removeAllGuns();
|
||||
game.isNoPowerUps = false;
|
||||
mod.setupAllMods(); //sets mods to default values
|
||||
b.setFireCD();
|
||||
game.updateModHUD();
|
||||
powerUps.totalPowerUps = 0;
|
||||
powerUps.reroll.rerolls = 0;
|
||||
mech.setFillColors();
|
||||
mech.maxHealth = 1
|
||||
mech.maxEnergy = 1
|
||||
mech.energy = 1
|
||||
mech.hole.isOn = false
|
||||
game.paused = false;
|
||||
engine.timing.timeScale = 1;
|
||||
game.fpsCap = game.fpsCapDefault;
|
||||
game.isAutoZoom = true;
|
||||
game.makeGunHUD();
|
||||
mech.drop();
|
||||
mech.holdingTarget = null
|
||||
mech.health = 0.25;
|
||||
mech.displayHealth();
|
||||
mech.alive = true;
|
||||
level.onLevel = 0;
|
||||
level.levelsCleared = 0;
|
||||
|
||||
//resetting difficulty
|
||||
game.dmgScale = 0; //increases in level.difficultyIncrease
|
||||
b.dmgScale = 1; //decreases in level.difficultyIncrease
|
||||
game.accelScale = 1;
|
||||
game.lookFreqScale = 1;
|
||||
game.CDScale = 1;
|
||||
game.difficulty = 0;
|
||||
game.difficultyMode = Number(document.getElementById("difficulty-select").value)
|
||||
build.isCustomSelection = false;
|
||||
|
||||
game.clearNow = true;
|
||||
document.getElementById("text-log").style.opacity = 0;
|
||||
document.getElementById("fade-out").style.opacity = 0;
|
||||
document.title = "n-gon";
|
||||
//set to default field
|
||||
mech.fieldMode = 0;
|
||||
game.replaceTextLog = true;
|
||||
game.makeTextLog(`${game.SVGrightMouse}<strong style='font-size:30px;'> ${mech.fieldUpgrades[mech.fieldMode].name}</strong><br><span class='faded'></span><br>${mech.fieldUpgrades[mech.fieldMode].description}`, 600);
|
||||
mech.setField(mech.fieldMode)
|
||||
//exit testing
|
||||
if (game.testing) {
|
||||
game.testing = false;
|
||||
game.loop = game.normalLoop
|
||||
if (game.isConstructionMode) {
|
||||
document.getElementById("construct").style.display = 'none'
|
||||
}
|
||||
game.reset();
|
||||
}
|
||||
game.isCheating = false
|
||||
game.firstRun = false;
|
||||
|
||||
//setup FPS cap
|
||||
|
||||
@@ -191,7 +191,7 @@ const build = {
|
||||
<br>position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)}) velocity: (${player.velocity.x.toFixed(1)}, ${player.velocity.y.toFixed(1)})
|
||||
<br>mouse: (${game.mouseInGame.x.toFixed(1)}, ${game.mouseInGame.y.toFixed(1)}) mass: ${player.mass.toFixed(1)}
|
||||
<br>
|
||||
<br>level: ${level.levelsCleared} - ${level.levelsCleared===0?"intro":level.levels[level.onLevel]} (${level.difficultyText()}) ${mech.cycle} cycles
|
||||
<br>level: ${level.levels[level.onLevel]} (${level.difficultyText()}) ${mech.cycle} cycles
|
||||
<br>${mob.length} mobs, ${body.length} blocks, ${bullet.length} bullets, ${powerUp.length} power ups
|
||||
<br>damage difficulty scale: ${(b.dmgScale*100).toFixed(2) }%
|
||||
<br>harm difficulty scale: ${(game.dmgScale*100).toFixed(0)}%
|
||||
|
||||
1125
js/level.js
1125
js/level.js
File diff suppressed because it is too large
Load Diff
94
js/mods.js
94
js/mods.js
@@ -87,12 +87,12 @@ const mod = {
|
||||
if (mod.isDamageForGuns) dmg *= 1 + 0.07 * b.inventory.length
|
||||
if (mod.isLowHealthDmg) dmg *= 1 + 0.6 * Math.max(0, 1 - mech.health)
|
||||
if (mod.isHarmDamage && mech.lastHarmCycle + 600 > mech.cycle) dmg *= 2;
|
||||
if (mod.isEnergyLoss) dmg *= 1.43;
|
||||
if (mod.isEnergyLoss) dmg *= 1.5;
|
||||
if (mod.isAcidDmg && mech.health > 1) dmg *= 1.4;
|
||||
if (mod.restDamage > 1 && player.speed < 1) dmg *= mod.restDamage
|
||||
if (mod.isEnergyDamage) dmg *= 1 + mech.energy / 5.5;
|
||||
if (mod.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.0038
|
||||
if (mod.isRerollDamage) dmg *= 1 + 0.05 * powerUps.reroll.rerolls
|
||||
if (mod.isRerollDamage) dmg *= 1 + 0.06 * powerUps.reroll.rerolls
|
||||
if (mod.isOneGun && b.inventory.length < 2) dmg *= 1.25
|
||||
if (mod.isNoFireDamage && mech.cycle > mech.fireCDcycle + 120) dmg *= 1.5
|
||||
return dmg * mod.slowFire * mod.aimDamage
|
||||
@@ -149,7 +149,7 @@ const mod = {
|
||||
},
|
||||
{
|
||||
name: "acute stress response",
|
||||
description: "increase <strong class='color-d'>damage</strong> by <strong>43%</strong><br>if a mob <strong>dies</strong> drain stored <strong class='color-f'>energy</strong> by <strong>25%</strong>",
|
||||
description: "increase <strong class='color-d'>damage</strong> by <strong>50%</strong><br>if a mob <strong>dies</strong> drain stored <strong class='color-f'>energy</strong> by <strong>25%</strong>",
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
@@ -245,7 +245,7 @@ const mod = {
|
||||
},
|
||||
{
|
||||
name: "perturbation theory",
|
||||
description: "increase <strong class='color-d'>damage</strong> by <strong>5%</strong><br>for each of your <strong class='color-r'>rerolls</strong>",
|
||||
description: "increase <strong class='color-d'>damage</strong> by <strong>6%</strong><br>for each of your <strong class='color-r'>rerolls</strong>",
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
@@ -259,26 +259,6 @@ const mod = {
|
||||
mod.isRerollDamage = false;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Ψ(t) collapse",
|
||||
description: "<strong>60%</strong> decreased <strong>delay</strong> after firing<br>if you have no <strong class='color-r'>rerolls</strong>",
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
return powerUps.reroll.rerolls === 0 && !mod.manyWorlds
|
||||
},
|
||||
requires: "no rerolls",
|
||||
effect() {
|
||||
mod.isRerollHaste = true;
|
||||
mod.rerollHaste = 0.4;
|
||||
b.setFireCD();
|
||||
},
|
||||
remove() {
|
||||
mod.isRerollHaste = false;
|
||||
mod.rerollHaste = 1;
|
||||
b.setFireCD();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "electrostatic discharge",
|
||||
description: "increase <strong class='color-d'>damage</strong> by <strong>20%</strong><br><strong>20%</strong> increased <strong>delay</strong> after firing",
|
||||
@@ -296,6 +276,26 @@ const mod = {
|
||||
b.setFireCD();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Ψ(t) collapse",
|
||||
description: "<strong>66%</strong> decreased <strong>delay</strong> after firing<br>if you have no <strong class='color-r'>rerolls</strong>",
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
return powerUps.reroll.rerolls === 0 && !mod.manyWorlds
|
||||
},
|
||||
requires: "no rerolls",
|
||||
effect() {
|
||||
mod.isRerollHaste = true;
|
||||
mod.rerollHaste = 0.33;
|
||||
b.setFireCD();
|
||||
},
|
||||
remove() {
|
||||
mod.isRerollHaste = false;
|
||||
mod.rerollHaste = 1;
|
||||
b.setFireCD();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "auto-loading heuristics",
|
||||
description: "<strong>30%</strong> decreased <strong>delay</strong> after firing",
|
||||
@@ -558,7 +558,7 @@ const mod = {
|
||||
},
|
||||
{
|
||||
name: "foam-bot upgrade",
|
||||
description: "<strong>200%</strong> increased <strong>foam size</strong><br><em>applies to all current and future foam-bots</em>",
|
||||
description: "<strong>150%</strong> increased <strong>foam size</strong><br><em>applies to all current and future foam-bots</em>",
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
@@ -675,7 +675,7 @@ const mod = {
|
||||
},
|
||||
{
|
||||
name: "orbital-bot upgrade",
|
||||
description: "<strong>125%</strong> increased <strong class='color-d'>damage</strong><br><em>applies to all current and future orbit-bots</em>",
|
||||
description: "increase <strong class='color-d'>damage</strong> by <strong>100%</strong> and <strong>radius</strong> by <strong>30%</strong><br><em>applies to all current and future orbit-bots</em>",
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
@@ -684,15 +684,24 @@ const mod = {
|
||||
requires: "2 or more orbital bots",
|
||||
effect() {
|
||||
mod.isOrbitBotUpgrade = true
|
||||
const range = 190 + 60 * mod.isOrbitBotUpgrade
|
||||
for (let i = 0; i < bullet.length; i++) {
|
||||
if (bullet[i].botType === 'orbit') bullet[i].isUpgraded = true
|
||||
if (bullet[i].botType === 'orbit') {
|
||||
bullet[i].isUpgraded = true
|
||||
bullet[i].range = range
|
||||
bullet[i].orbitalSpeed = Math.sqrt(0.25 / range)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
remove() {
|
||||
mod.isOrbitBotUpgrade = false
|
||||
const range = 190 + 60 * mod.isOrbitBotUpgrade
|
||||
for (let i = 0; i < bullet.length; i++) {
|
||||
if (bullet[i].botType === 'orbit') bullet[i].isUpgraded = false
|
||||
if (bullet[i].botType === 'orbit') {
|
||||
bullet[i].range = range
|
||||
bullet[i].orbitalSpeed = Math.sqrt(0.25 / range)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -946,7 +955,7 @@ const mod = {
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
return mod.isPiezo
|
||||
return mod.isPiezo && !mod.timeEnergyRegen
|
||||
},
|
||||
requires: "piezoelectricity",
|
||||
effect: () => {
|
||||
@@ -1893,7 +1902,7 @@ const mod = {
|
||||
},
|
||||
{
|
||||
name: "wave packet",
|
||||
description: "<strong>wave beam</strong> emits <strong>two</strong> oscillating particles<br>decrease wave <strong class='color-d'>damage</strong> by <strong>33%</strong>",
|
||||
description: "<strong>wave beam</strong> emits <strong>two</strong> oscillating particles<br>decrease wave <strong class='color-d'>damage</strong> by <strong>20%</strong>",
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
@@ -1943,7 +1952,7 @@ const mod = {
|
||||
},
|
||||
{
|
||||
name: "recursion",
|
||||
description: "after <strong>missiles</strong> <strong class='color-e'>explode</strong> they have a<br><strong>30%</strong> chance to launch a larger <strong>missile</strong>",
|
||||
description: "after <strong>missiles</strong> <strong class='color-e'>explode</strong> they have a<br><strong>20%</strong> chance to launch a larger <strong>missile</strong>",
|
||||
maxCount: 6,
|
||||
count: 0,
|
||||
allowed() {
|
||||
@@ -2622,6 +2631,24 @@ const mod = {
|
||||
b.setFireCD();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "time crystals",
|
||||
description: "<strong>quadruple</strong> your default <strong class='color-f'>energy</strong> regeneration",
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
return mech.fieldUpgrades[mech.fieldMode].name === "time dilation field"
|
||||
},
|
||||
requires: "time dilation field",
|
||||
effect: () => {
|
||||
mod.energyRegen = 0.004;
|
||||
mech.fieldRegen = mod.energyRegen;
|
||||
},
|
||||
remove() {
|
||||
mod.energyRegen = 0.001;
|
||||
mech.fieldRegen = mod.energyRegen;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "plasma jet",
|
||||
description: "increase <strong class='color-plasma'>plasma</strong> <strong>torch's</strong> range by <strong>27%</strong>",
|
||||
@@ -2871,7 +2898,7 @@ const mod = {
|
||||
},
|
||||
{
|
||||
name: "cosmic string",
|
||||
description: "when you <strong> tunnel</strong> through a <strong class='color-worm'>wormhole</strong><br>mobs between the <strong>endpoints</strong> take <strong class='color-d'>damage</strong>",
|
||||
description: "<strong>stun</strong> and do <strong class='color-p'>radioactive</strong> <strong class='color-d'>damage</strong> to <strong>mobs</strong><br>if you tunnel through them with a <strong class='color-worm'>wormhole</strong>",
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
allowed() {
|
||||
@@ -3178,5 +3205,6 @@ const mod = {
|
||||
isWormBullets: null,
|
||||
isWideLaser: null,
|
||||
wideLaser: null,
|
||||
isPulseLaser: null
|
||||
isPulseLaser: null,
|
||||
timeEnergyRegen: null
|
||||
}
|
||||
47
js/player.js
47
js/player.js
@@ -1122,31 +1122,6 @@ const mech = {
|
||||
}
|
||||
}
|
||||
},
|
||||
// pushBodyFacing() { // push all body in range and in direction looking
|
||||
// for (let i = 0, len = body.length; i < len; ++i) {
|
||||
// if (
|
||||
// body[i].speed > 12 && body[i].mass > 2 &&
|
||||
// Vector.magnitude(Vector.sub(body[i].position, mech.pos)) < mech.fieldRange &&
|
||||
// mech.lookingAt(body[i]) &&
|
||||
// Matter.Query.ray(map, body[i].position, mech.pos).length === 0
|
||||
// ) {
|
||||
// mech.pushMass(body[i]);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// pushBody360(range = mech.fieldRange * 0.75) { // push all body in range and in direction looking
|
||||
// for (let i = 0, len = body.length; i < len; ++i) {
|
||||
// if (
|
||||
// body[i].speed > 12 && body[i].mass > 2 &&
|
||||
// Vector.magnitude(Vector.sub(body[i].position, mech.pos)) < range &&
|
||||
// mech.lookingAt(body[i]) &&
|
||||
// Matter.Query.ray(map, body[i].position, mech.pos).length === 0 &&
|
||||
// body[i].collisionFilter.category === cat.body
|
||||
// ) {
|
||||
// mech.pushMass(body[i]);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
lookForPickUp() { //find body to pickup
|
||||
if (mech.energy > mech.fieldRegen) mech.energy -= mech.fieldRegen;
|
||||
const grabbing = {
|
||||
@@ -1370,11 +1345,11 @@ const mech = {
|
||||
mech.drawFieldMeter()
|
||||
|
||||
if (mod.isPerfectBrake) { //cap mob speed around player
|
||||
const range = 400 + 120 * wave
|
||||
const range = 350 + 140 * wave
|
||||
for (let i = 0; i < mob.length; i++) {
|
||||
const distance = Vector.magnitude(Vector.sub(mech.pos, mob[i].position))
|
||||
if (distance < range) {
|
||||
const cap = mob[i].isShielded ? 8 : 4
|
||||
const cap = mob[i].isShielded ? 8.5 : 4.5
|
||||
if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(mech.pos, mob[i].position)) > 0) { // if velocity is directed towards player
|
||||
Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction
|
||||
}
|
||||
@@ -1390,7 +1365,7 @@ const mech = {
|
||||
},
|
||||
{
|
||||
name: "nano-scale manufacturing",
|
||||
description: "use <strong class='color-f'>energy</strong> to <strong>block</strong> mobs<br>excess <strong class='color-f'>energy</strong> used to build <strong>drones</strong><br>increase <strong class='color-f'>energy</strong> regeneration by <strong>100%</strong>",
|
||||
description: "use <strong class='color-f'>energy</strong> to <strong>block</strong> mobs<br>excess <strong class='color-f'>energy</strong> used to build <strong>drones</strong><br><strong>double</strong> your default <strong class='color-f'>energy</strong> regeneration",
|
||||
effect: () => {
|
||||
mech.hold = function() {
|
||||
if (mech.energy > mech.maxEnergy - 0.02 && mech.fieldCDcycle < mech.cycle) {
|
||||
@@ -1734,7 +1709,7 @@ const mech = {
|
||||
mech.grabPowerUp();
|
||||
mech.lookForPickUp(180);
|
||||
|
||||
const DRAIN = 0.0006
|
||||
const DRAIN = 0.0008
|
||||
if (mech.energy > DRAIN) {
|
||||
mech.energy -= DRAIN;
|
||||
if (mech.energy < DRAIN) {
|
||||
@@ -2461,19 +2436,11 @@ const mech = {
|
||||
mech.hole.unit = Vector.perp(Vector.normalise(sub))
|
||||
|
||||
if (mod.isWormholeDamage) {
|
||||
who = Matter.Query.ray(mob, mech.pos, game.mouseInGame, 60)
|
||||
who = Matter.Query.ray(mob, mech.pos, game.mouseInGame, 80)
|
||||
for (let i = 0; i < who.length; i++) {
|
||||
if (who[i].body.alive) {
|
||||
const dmg = b.dmgScale * 6
|
||||
who[i].body.damage(dmg);
|
||||
who[i].body.locatePlayer();
|
||||
game.drawList.push({ //add dmg to draw queue
|
||||
x: who[i].body.position.x,
|
||||
y: who[i].body.position.y,
|
||||
radius: Math.log(2 * dmg + 1.1) * 40,
|
||||
color: game.playerDmgColor,
|
||||
time: game.drawTime
|
||||
});
|
||||
mobs.statusDoT(who[i].body, 0.6, 420)
|
||||
mobs.statusStun(who[i].body, 240)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
466
js/spawn.js
466
js/spawn.js
@@ -87,6 +87,286 @@ const spawn = {
|
||||
},
|
||||
//mob templates *********************************************************************************************
|
||||
//***********************************************************************************************************
|
||||
finalBoss(x, y, radius = 300) {
|
||||
mobs.spawn(x, y, 6, radius, "rgb(150,150,255)");
|
||||
let me = mob[mob.length - 1];
|
||||
me.isBoss = true;
|
||||
me.frictionAir = 0.01;
|
||||
me.memory = Infinity;
|
||||
me.locatePlayer();
|
||||
const density = 5
|
||||
Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger
|
||||
spawn.shield(me, x, y, 1);
|
||||
me.onDeath = function() {
|
||||
level.bossKilled = true;
|
||||
level.exit.x = 5500;
|
||||
level.exit.y = -330;
|
||||
};
|
||||
me.onDamage = function() {};
|
||||
me.cycle = 300;
|
||||
me.endCycle = 600;
|
||||
me.mode = 0;
|
||||
me.do = function() {
|
||||
//hold position
|
||||
Matter.Body.setPosition(this, {
|
||||
x: x,
|
||||
y: y
|
||||
});
|
||||
Matter.Body.setVelocity(this, {
|
||||
x: 0,
|
||||
y: 0
|
||||
});
|
||||
this.modeDo(); //this does different things based on the mode
|
||||
this.checkStatus();
|
||||
if (!mech.isBodiesAsleep) {
|
||||
this.cycle++; //switch modes
|
||||
if (this.health > 0.33) {
|
||||
if (this.cycle > this.endCycle) {
|
||||
this.cycle = 0;
|
||||
this.mode++
|
||||
if (this.mode > 2) {
|
||||
this.mode = 0;
|
||||
this.fill = "#50f";
|
||||
this.rotateVelocity = Math.abs(this.rotateVelocity) * (player.position.x > this.position.x ? 1 : -1) //rotate so that the player can get away
|
||||
this.modeDo = this.modeLasers
|
||||
//push blocks and player away, since this is the end of suck, and suck causes blocks to fall on the boss and stun it
|
||||
Matter.Body.scale(this, 10, 10);
|
||||
Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger
|
||||
if (!this.isShielded) spawn.shield(this, x, y, 1); // regen shield to also prevent stun
|
||||
for (let i = 0, len = body.length; i < len; ++i) {
|
||||
if (body[i].position.x > this.position.x) {
|
||||
body[i].force.x = 0.5
|
||||
} else {
|
||||
body[i].force.x = -0.5
|
||||
}
|
||||
|
||||
}
|
||||
} else if (this.mode === 1) {
|
||||
this.fill = "rgb(150,150,255)";
|
||||
this.endCycle = 360
|
||||
this.modeDo = this.modeSpawns
|
||||
} else if (this.mode === 2) {
|
||||
this.fill = "#000";
|
||||
this.endCycle = 720
|
||||
this.modeDo = this.modeSuck
|
||||
Matter.Body.scale(this, 0.1, 0.1);
|
||||
Matter.Body.setDensity(me, 100 * density); //extra dense //normal is 0.001 //makes effective life much larger
|
||||
}
|
||||
}
|
||||
} else if (this.mode !== 3) { //all three modes at once
|
||||
Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger
|
||||
if (this.mode === 2) {
|
||||
Matter.Body.scale(this, 5, 5);
|
||||
} else {
|
||||
Matter.Body.scale(this, 0.5, 0.5);
|
||||
}
|
||||
this.mode = 3
|
||||
this.fill = "#000";
|
||||
this.eventHorizon = 1200
|
||||
this.rotateVelocity = Math.abs(this.rotateVelocity) * (player.position.x > this.position.x ? 1 : -1) //rotate so that the player can get away
|
||||
if (!this.isShielded) spawn.shield(this, x, y, 1); //regen shield here ?
|
||||
this.modeDo = this.modeAll
|
||||
}
|
||||
}
|
||||
};
|
||||
me.modeDo = function() {}
|
||||
me.modeAll = function() {
|
||||
this.modeSpawns()
|
||||
this.modeSuck()
|
||||
this.modeLasers()
|
||||
}
|
||||
me.modeSpawns = function() {
|
||||
if (!(this.cycle % 320) && !mech.isBodiesAsleep && mob.length < 40) {
|
||||
Matter.Body.setAngularVelocity(this, 0.11)
|
||||
//fire a bullet from each vertex
|
||||
for (let i = 0, len = this.vertices.length; i < len; i++) {
|
||||
let whoSpawn = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)];
|
||||
spawn[whoSpawn](this.vertices[i].x, this.vertices[i].y);
|
||||
//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]))), -20)
|
||||
Matter.Body.setVelocity(mob[mob.length - 1], {
|
||||
x: this.velocity.x + velocity.x,
|
||||
y: this.velocity.y + velocity.y
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
me.eventHorizon = 1400
|
||||
me.modeSuck = function() {
|
||||
//eventHorizon waves in and out
|
||||
eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(game.cycle * 0.015))
|
||||
//draw darkness
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = "rgba(0,20,40,0.6)";
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = "rgba(0,20,40,0.4)";
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = "rgba(0,20,40,0.3)";
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = "rgba(0,20,40,0.2)";
|
||||
ctx.fill();
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = "rgba(0,0,0,0.05)";
|
||||
ctx.fill();
|
||||
//when player is inside event horizon
|
||||
if (Vector.magnitude(Vector.sub(this.position, player.position)) < eventHorizon) {
|
||||
if (mech.energy > 0) mech.energy -= 0.01
|
||||
if (mech.energy < 0.15) {
|
||||
mech.damage(0.0004 * game.dmgScale);
|
||||
}
|
||||
const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x);
|
||||
player.force.x -= 0.0017 * Math.cos(angle) * player.mass * (mech.onGround ? 1.7 : 1);
|
||||
player.force.y -= 0.0017 * Math.sin(angle) * player.mass;
|
||||
//draw line to player
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.position.x, this.position.y);
|
||||
ctx.lineTo(mech.pos.x, mech.pos.y);
|
||||
ctx.lineWidth = Math.min(60, this.radius * 2);
|
||||
ctx.strokeStyle = "rgba(0,0,0,0.5)";
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.arc(mech.pos.x, mech.pos.y, 40, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = "rgba(0,0,0,0.3)";
|
||||
ctx.fill();
|
||||
}
|
||||
this.curl(eventHorizon);
|
||||
}
|
||||
me.rotateVelocity = 0.0025
|
||||
me.rotateCount = 0;
|
||||
me.modeLasers = function() {
|
||||
if (!this.isStunned) {
|
||||
if (!mech.isBodiesAsleep) {
|
||||
let slowed = false //check if slowed
|
||||
for (let i = 0; i < this.status.length; i++) {
|
||||
if (this.status[i].type === "slow") {
|
||||
slowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!slowed) {
|
||||
this.rotateCount++
|
||||
Matter.Body.setAngle(this, this.rotateCount * this.rotateVelocity)
|
||||
Matter.Body.setAngularVelocity(this, 0)
|
||||
Matter
|
||||
}
|
||||
}
|
||||
if (this.cycle < 180) { //damage scales up over 2 seconds to give player time to move
|
||||
const scale = this.cycle / 180
|
||||
const dmg = 0.14 * game.dmgScale * scale
|
||||
ctx.beginPath();
|
||||
this.laser(this.vertices[0], this.angle + Math.PI / 6, dmg);
|
||||
this.laser(this.vertices[1], this.angle + 3 * Math.PI / 6, dmg);
|
||||
this.laser(this.vertices[2], this.angle + 5 * Math.PI / 6, dmg);
|
||||
this.laser(this.vertices[3], this.angle + 7 * Math.PI / 6, dmg);
|
||||
this.laser(this.vertices[4], this.angle + 9 * Math.PI / 6, dmg);
|
||||
this.laser(this.vertices[5], this.angle + 11 * Math.PI / 6, dmg);
|
||||
ctx.strokeStyle = "#50f";
|
||||
ctx.lineWidth = 1.5 * scale;
|
||||
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
|
||||
ctx.stroke(); // Draw it
|
||||
ctx.setLineDash([0, 0]);
|
||||
ctx.lineWidth = 20;
|
||||
ctx.strokeStyle = `rgba(80,0,255,${0.07*scale})`;
|
||||
ctx.stroke(); // Draw it
|
||||
} else {
|
||||
ctx.beginPath();
|
||||
this.laser(this.vertices[0], this.angle + Math.PI / 6);
|
||||
this.laser(this.vertices[1], this.angle + 3 * Math.PI / 6);
|
||||
this.laser(this.vertices[2], this.angle + 5 * Math.PI / 6);
|
||||
this.laser(this.vertices[3], this.angle + 7 * Math.PI / 6);
|
||||
this.laser(this.vertices[4], this.angle + 9 * Math.PI / 6);
|
||||
this.laser(this.vertices[5], this.angle + 11 * Math.PI / 6);
|
||||
ctx.strokeStyle = "#50f";
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
|
||||
ctx.stroke(); // Draw it
|
||||
ctx.setLineDash([0, 0]);
|
||||
ctx.lineWidth = 20;
|
||||
ctx.strokeStyle = "rgba(80,0,255,0.07)";
|
||||
ctx.stroke(); // Draw it
|
||||
}
|
||||
}
|
||||
me.laser = function(where, angle, dmg = 0.14 * game.dmgScale) {
|
||||
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) best = {
|
||||
x: results.x,
|
||||
y: results.y,
|
||||
dist2: dist2,
|
||||
who: domain[i],
|
||||
v1: vertices[0],
|
||||
v2: vertices[len]
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const seeRange = 7000;
|
||||
best = {
|
||||
x: null,
|
||||
y: null,
|
||||
dist2: Infinity,
|
||||
who: null,
|
||||
v1: null,
|
||||
v2: null
|
||||
};
|
||||
const look = {
|
||||
x: where.x + seeRange * Math.cos(angle),
|
||||
y: where.y + seeRange * Math.sin(angle)
|
||||
};
|
||||
// vertexCollision(where, look, mob);
|
||||
vertexCollision(where, look, map);
|
||||
vertexCollision(where, look, body);
|
||||
if (!mech.isCloak) vertexCollision(where, look, [player]);
|
||||
if (best.who && best.who === player && mech.immuneCycle < mech.cycle) {
|
||||
mech.immuneCycle = mech.cycle + mod.collisionImmuneCycles; //player is immune to collision damage for 30 cycles
|
||||
mech.damage(dmg);
|
||||
game.drawList.push({ //add dmg to draw queue
|
||||
x: best.x,
|
||||
y: best.y,
|
||||
radius: dmg * 1500,
|
||||
color: "rgba(80,0,255,0.5)",
|
||||
time: 20
|
||||
});
|
||||
}
|
||||
//draw beam
|
||||
if (best.dist2 === Infinity) best = look;
|
||||
ctx.moveTo(where.x, where.y);
|
||||
ctx.lineTo(best.x, best.y);
|
||||
}
|
||||
}
|
||||
},
|
||||
groupBoss(x, y, num = 3 + Math.random() * 8) {
|
||||
for (let i = 0; i < num; i++) {
|
||||
const radius = 25 + Math.floor(Math.random() * 20)
|
||||
@@ -280,140 +560,6 @@ const spawn = {
|
||||
this.checkStatus();
|
||||
};
|
||||
},
|
||||
// powerUpBoss(x, y, vertices = 9, radius = 130) {
|
||||
// mobs.spawn(x, y, vertices, radius, "transparent");
|
||||
// let me = mob[mob.length - 1];
|
||||
// me.isBoss = true;
|
||||
// me.frictionAir = 0.025
|
||||
// me.seeAtDistance2 = 9000000;
|
||||
// me.accelMag = 0.0005 * game.accelScale;
|
||||
// Matter.Body.setDensity(me, 0.002); //normal is 0.001
|
||||
// me.collisionFilter.mask = cat.bullet | cat.player
|
||||
// me.memory = Infinity;
|
||||
// me.seePlayerFreq = 85 + Math.floor(10 * Math.random())
|
||||
|
||||
// me.lockedOn = null;
|
||||
// if (vertices === 9) {
|
||||
// //on primary spawn
|
||||
// powerUps.spawnBossPowerUp(me.position.x, me.position.y)
|
||||
// powerUps.spawn(me.position.x, me.position.y, "heal");
|
||||
// powerUps.spawn(me.position.x, me.position.y, "ammo");
|
||||
// } else if (!mech.isCloak) {
|
||||
// me.foundPlayer();
|
||||
// }
|
||||
|
||||
// me.onDeath = function () {
|
||||
// this.leaveBody = false;
|
||||
// this.dropPowerUp = false;
|
||||
|
||||
// if (vertices > 3) spawn.powerUpBoss(this.position.x, this.position.y, vertices - 1)
|
||||
// for (let i = 0; i < powerUp.length; i++) {
|
||||
// powerUp[i].collisionFilter.mask = cat.map | cat.powerUp
|
||||
// }
|
||||
// };
|
||||
// me.do = function () {
|
||||
// this.stroke = `hsl(0,0%,${80+25*Math.sin(game.cycle*0.01)}%)`
|
||||
|
||||
// //steal all power ups
|
||||
// for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) {
|
||||
// powerUp[i].collisionFilter.mask = 0
|
||||
// Matter.Body.setPosition(powerUp[i], this.vertices[i])
|
||||
// Matter.Body.setVelocity(powerUp[i], {
|
||||
// x: 0,
|
||||
// y: 0
|
||||
// })
|
||||
// }
|
||||
|
||||
// this.seePlayerCheckByDistance();
|
||||
// this.attraction();
|
||||
// this.checkStatus();
|
||||
// };
|
||||
// },
|
||||
// healer(x, y, radius = 20) {
|
||||
// mobs.spawn(x, y, 3, radius, "rgba(50,255,200,0.4)");
|
||||
// let me = mob[mob.length - 1];
|
||||
// me.frictionAir = 0.02;
|
||||
// me.accelMag = 0.0004 * game.accelScale;
|
||||
// if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search
|
||||
// me.lookFrequency = 160 + Math.floor(57 * Math.random())
|
||||
// me.lockedOn = null;
|
||||
// Matter.Body.setDensity(me, 0.003) // normal density is 0.001
|
||||
|
||||
// me.do = function () {
|
||||
|
||||
// if (!(game.cycle % this.lookFrequency)) {
|
||||
// //slow self heal
|
||||
// this.health += 0.02;
|
||||
// if (this.health > 1) this.health = 1;
|
||||
|
||||
// //target mobs with low health
|
||||
// let closeDist = Infinity;
|
||||
// for (let i = 0; i < mob.length; i++) {
|
||||
// if (mob[i] != this && Matter.Query.ray(map, this.position, mob[i].position).length === 0) {
|
||||
// const TARGET_VECTOR = Vector.sub(this.position, mob[i].position)
|
||||
// const DIST = Vector.magnitude(TARGET_VECTOR) * mob[i].health * mob[i].health * mob[i].health; //distance is multiplied by mob health to prioritize low health mobs
|
||||
// if (DIST < closeDist) {
|
||||
// closeDist = DIST;
|
||||
// this.lockedOn = mob[i]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// //move away from player if too close
|
||||
// if (this.distanceToPlayer2() < 400000) {
|
||||
// const TARGET_VECTOR = Vector.sub(this.position, player.position)
|
||||
// this.force = Vector.mult(Vector.normalise(TARGET_VECTOR), this.mass * this.accelMag * 1.4)
|
||||
// if (this.lockedOn) this.lockedOn = null
|
||||
// } else if (this.lockedOn && this.lockedOn.alive) {
|
||||
// //move towards and heal locked on target
|
||||
// const TARGET_VECTOR = Vector.sub(this.position, this.lockedOn.position)
|
||||
// const DIST = Vector.magnitude(TARGET_VECTOR);
|
||||
// if (DIST > 250) {
|
||||
// this.force = Vector.mult(Vector.normalise(TARGET_VECTOR), -this.mass * this.accelMag)
|
||||
// } else {
|
||||
// if (this.lockedOn.health < 1) {
|
||||
// this.lockedOn.health += 0.002;
|
||||
// if (this.lockedOn.health > 1) this.lockedOn.health = 1;
|
||||
// //spin when healing
|
||||
// this.torque = 0.000005 * this.inertia;
|
||||
// //draw heal
|
||||
// ctx.beginPath();
|
||||
// ctx.moveTo(this.position.x, this.position.y);
|
||||
// ctx.lineTo(this.lockedOn.position.x, this.lockedOn.position.y);
|
||||
// ctx.lineWidth = 10
|
||||
// ctx.strokeStyle = "rgba(50,255,200,0.4)"
|
||||
// ctx.stroke();
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// //wander if no heal targets visible
|
||||
// //be sure to declare searchTarget in mob spawn
|
||||
// const newTarget = function (that) {
|
||||
// that.searchTarget = mob[Math.floor(Math.random() * (mob.length - 1))].position;
|
||||
// };
|
||||
|
||||
// const sub = Vector.sub(this.searchTarget, this.position);
|
||||
// if (Vector.magnitude(sub) > this.radius * 2) {
|
||||
// ctx.beginPath();
|
||||
// ctx.strokeStyle = "#aaa";
|
||||
// ctx.moveTo(this.position.x, this.position.y);
|
||||
// ctx.lineTo(this.searchTarget.x, this.searchTarget.y);
|
||||
// ctx.stroke();
|
||||
// //accelerate at 0.6 of normal acceleration
|
||||
// this.force = Vector.mult(Vector.normalise(sub), this.accelMag * this.mass * 0.6);
|
||||
// } else {
|
||||
// //after reaching random target switch to new target
|
||||
// newTarget(this);
|
||||
// }
|
||||
// //switch to a new target after a while
|
||||
// if (!(game.cycle % (this.lookFrequency * 15))) {
|
||||
// newTarget(this);
|
||||
// }
|
||||
|
||||
// }
|
||||
// };
|
||||
// },
|
||||
chaser(x, y, radius = 35 + Math.ceil(Math.random() * 40)) {
|
||||
mobs.spawn(x, y, 8, radius, "rgb(255,150,100)"); //"#2c9790"
|
||||
let me = mob[mob.length - 1];
|
||||
@@ -1183,6 +1329,9 @@ const spawn = {
|
||||
me.rotateVelocity = Math.min(0.0045, 0.0015 * game.accelScale * game.accelScale) * (level.levelsCleared > 8 ? 1 : -1)
|
||||
me.do = function() {
|
||||
this.fill = '#' + Math.random().toString(16).substr(-6); //flash colors
|
||||
this.checkStatus();
|
||||
|
||||
if (!this.isStunned) {
|
||||
if (!mech.isBodiesAsleep) {
|
||||
//check if slowed
|
||||
let slowed = false
|
||||
@@ -1194,17 +1343,10 @@ const spawn = {
|
||||
}
|
||||
if (!slowed) {
|
||||
this.count++
|
||||
Matter.Body.setAngle(me, this.count * this.rotateVelocity)
|
||||
Matter.Body.setAngle(this, this.count * this.rotateVelocity)
|
||||
Matter.Body.setAngularVelocity(this, 0)
|
||||
}
|
||||
}
|
||||
// this.torque -= this.inertia * 0.0000025 / (4 + this.health);
|
||||
Matter.Body.setVelocity(this, {
|
||||
x: 0,
|
||||
y: 0
|
||||
});
|
||||
Matter.Body.setPosition(this, this.startingPosition);
|
||||
|
||||
if (!this.isStunned) {
|
||||
ctx.beginPath();
|
||||
this.laser(this.vertices[0], this.angle + Math.PI / 3);
|
||||
this.laser(this.vertices[1], this.angle + Math.PI);
|
||||
@@ -1218,8 +1360,14 @@ const spawn = {
|
||||
ctx.strokeStyle = "rgba(80,0,255,0.07)";
|
||||
ctx.stroke(); // Draw it
|
||||
}
|
||||
// this.laser(this.vertices[2], this.angle + Math.PI / 3);
|
||||
this.checkStatus();
|
||||
|
||||
|
||||
Matter.Body.setVelocity(this, {
|
||||
x: 0,
|
||||
y: 0
|
||||
});
|
||||
Matter.Body.setPosition(this, this.startingPosition);
|
||||
|
||||
};
|
||||
me.laser = function(where, angle) {
|
||||
const vertexCollision = function(v1, v1End, domain) {
|
||||
@@ -2188,30 +2336,6 @@ const spawn = {
|
||||
},
|
||||
//fan made mobs *****************************************************************************************
|
||||
//*******************************************************************************************************
|
||||
// mobBloc(x, y, radius, color) {
|
||||
// mobs.spawn(x, y, 4, radius, color);
|
||||
// let me = mob[mob.length - 1];
|
||||
// me.stroke = "transparent";
|
||||
// me.startingPosition = {
|
||||
// x: x,
|
||||
// y: y
|
||||
// }
|
||||
// Matter.Body.setDensity(me, 0.002);
|
||||
// me.leaveBody = false;
|
||||
// me.isStatic = true;
|
||||
// me.showHealthBar = false;
|
||||
// me.collisionFilter.category = cat.map;
|
||||
// me.collisionFilter.mask = cat.powerUp | cat.map | cat.player | cat.bullet | cat.body
|
||||
// me.rotateVelocity = 0
|
||||
// me.do = function () {
|
||||
// Matter.Body.setVelocity(this, {
|
||||
// x: 0,
|
||||
// y: 0
|
||||
// });
|
||||
// Matter.Body.setPosition(this, this.startingPosition);
|
||||
// this.checkStatus();
|
||||
// };
|
||||
// },
|
||||
//complex constrained mob templates**********************************************************************
|
||||
//*******************************************************************************************************
|
||||
allowShields: true,
|
||||
@@ -2677,11 +2801,11 @@ const spawn = {
|
||||
color: "#f0f0f3"
|
||||
});
|
||||
},
|
||||
blockDoor(x, y, blockSize = 58) {
|
||||
blockDoor(x, y, blockSize = 60) {
|
||||
spawn.mapRect(x, y - 290, 40, 60); // door lip
|
||||
spawn.mapRect(x, y, 40, 50); // door lip
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize);
|
||||
spawn.bodyRect(x + 5, y - 260 + i * blockSize + i * 3, 30, blockSize);
|
||||
}
|
||||
},
|
||||
debris(x, y, width, number = Math.floor(2 + Math.random() * 9)) {
|
||||
|
||||
61
todo.txt
61
todo.txt
@@ -1,10 +1,46 @@
|
||||
neutron bomb does 60% more damage
|
||||
powerUpBoss has a shorter vision range, and accelerates slower
|
||||
custom mode has the option to disable mod, guns, and fields
|
||||
several changes to community maps (by Francois 👑)
|
||||
missile moves slightly differently
|
||||
it used to slow when locked on to a target
|
||||
now it slows when turning
|
||||
missiles explode when near any mob
|
||||
|
||||
wormhole mod: cosmic string - now stuns mobs and applies radiation damage
|
||||
mod time dilation: - quadruple your default energy regeneration
|
||||
|
||||
added final boss level, it's still in progress so I'd love some feedback
|
||||
also the game loops back to the intro level after the boss
|
||||
I'll be working on the ending in the next patch, so the intro level is just a placeholder
|
||||
|
||||
************** TODO - n-gon **************
|
||||
|
||||
final boss has elements of other bosses
|
||||
laser mode
|
||||
if player is on left rotate counter clockwise
|
||||
if player is on right rotate clockwise
|
||||
start of laser mode
|
||||
push block either left or right, not away
|
||||
vibrating shape
|
||||
grow and shrink
|
||||
oscillate elliptical deformation (not sure how)
|
||||
|
||||
a bot that eats ammo and converts them into rerolls
|
||||
or 2 ammo power ups = 1 reroll
|
||||
|
||||
been getting some fps slow down after playing for a few minutes
|
||||
|
||||
new status effect: fear - push mob away from player for a time
|
||||
|
||||
new status effect - apply status effect to mobs that makes blocks attracted to them
|
||||
only lasts a few cycles
|
||||
|
||||
in hard and why have some mobs spawn in later in the level
|
||||
where
|
||||
at defined points in array levelSpawns = [{x:0,y:0},{x:0,y:0}]
|
||||
store the locations of mobs when the level starts to use as respawn points
|
||||
remove the locations that are close to player
|
||||
when?
|
||||
after some mobs are dead
|
||||
after the boss is killed
|
||||
|
||||
mod - explosions apply radiation damage over time
|
||||
or spawn a neutron bomb with a short timer
|
||||
|
||||
@@ -13,26 +49,15 @@ mod self destruct - drones explode when they die
|
||||
|
||||
add an ending to the game
|
||||
add a final boss battle level
|
||||
final boss has elements of other bosses
|
||||
alternate between black hole aura and laser beams
|
||||
fire seeker bullets
|
||||
drop bombs
|
||||
tail of shielded mobs
|
||||
shield
|
||||
mirror ending (if no cheats)
|
||||
level after final boss battle is the intro level, but flipped left right, with a fake player
|
||||
damage the fake player to end the game?
|
||||
damage the fake player to end the game
|
||||
message about go outside
|
||||
no ending (if cheats)
|
||||
game goes on forever
|
||||
|
||||
around level 15
|
||||
also game goes on if player attacks, the fake player
|
||||
game never ends if you have used cheats
|
||||
|
||||
new status effect - push mob away from player for a time
|
||||
|
||||
new status effect - apply status effect to mobs that makes blocks attracted to them
|
||||
only lasts a few cycles
|
||||
|
||||
foam or spore bullet on dmg shrink effect
|
||||
it might mess with the foam position of other bullets on the mob
|
||||
shrink when foam bullets end while attached, or shrink on collision?
|
||||
|
||||
Reference in New Issue
Block a user