diff --git a/js/bullet.js b/js/bullet.js
index 006521e..67ad315 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -3931,6 +3931,10 @@ const b = {
if (!mob.shield && Vector.dot(Vector.normalise(Vector.sub(mob.position, bullet.position)), Vector.normalise(bullet.velocity)) > 0.99 - 4 / mob.radius) {
let cycle = () => { //makes this run after damage
if (mob.health < 0.5 && mob.damageReduction > 0 && mob.alive) {
+ // mob.death();
+ // mob.damage(this.health * Math.sqrt(this.mass) / this.damageReduction);
+ mob.damage(Infinity);
+
const color = 'rgb(255,255,255)'
simulation.drawList.push({
x: mob.position.x,
@@ -3953,7 +3957,6 @@ const b = {
color: color, //"rgb(0,0,0)",
time: 20
});
- mob.death();
}
}
requestAnimationFrame(cycle);
@@ -5328,7 +5331,6 @@ const b = {
b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end
}
} else if (tech.isCritKill) b.crit(who, this)
-
if (tech.isNailRadiation) mobs.statusDoT(who, 7 * (tech.isFastRadiation ? 0.7 : 0.24), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles
if (this.speed > 4 && tech.fragments) {
b.targetedNail(this.position, 1.25 * tech.fragments * tech.bulletSize)
@@ -6595,7 +6597,6 @@ const b = {
ctx.fill();
if (this.isDischarge && m.cycle % 2) {
- this.charge -= 0.75
const spread = (input.down ? 0.04 : 0.5) * (Math.random() - 0.5)
const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12
const SPEED = (input.down ? 1.2 : 1) * 10 - radius * 0.4 + Math.min(5, Math.sqrt(this.charge));
@@ -6609,6 +6610,7 @@ const b = {
y: m.pos.y + 30 * Math.sin(m.angle)
}
b.foam(position, Vector.rotate(velocity, spread), radius)
+ this.charge -= 0.75
m.fireCDcycle = m.cycle + 2; //disable firing and adding more charge until empty
} else if (!input.fire) {
this.isDischarge = true;
diff --git a/js/level.js b/js/level.js
index 379bd08..e142752 100644
--- a/js/level.js
+++ b/js/level.js
@@ -16,37 +16,38 @@ const level = {
start() {
if (level.levelsCleared === 0) { //this code only runs on the first level
// simulation.enableConstructMode() //used to build maps in testing mode
- // level.difficultyIncrease(12 * 4) //30 is near max on hard //60 is near max on why
// simulation.isHorizontalFlipped = true
+ // tech.giveTech("performance")
+ // level.difficultyIncrease(6 * 4) //30 is near max on hard //60 is near max on why
// m.maxHealth = m.health = 100
// tech.isRerollDamage = true
- // powerUps.research.changeRerolls(100000)
+ // powerUps.research.changeRerolls(50)
// m.immuneCycle = Infinity //you can't take damage
// tech.tech[297].frequency = 100
// m.couplingChange(5)
- // m.setField("metamaterial cloaking") //molecular assembler standing wave time dilation perfect diamagnetism metamaterial cloaking wormhole negative mass
+ // m.setField("pilot wave") //molecular assembler standing wave time dilation perfect diamagnetism metamaterial cloaking wormhole negative mass pilot wave
// simulation.molecularMode = 2
// m.damage(0.1);
// b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.guns[0].ammo = 1000000
- // for (let i = 0; i < 1; ++i) tech.giveTech("mass-energy equivalence")
- // tech.giveTech("Zeno's paradox")
- // tech.giveTech("homeostasis")
- // for (let i = 0; i < 1; ++i) tech.giveTech("1st ionization energy")
- // for (let i = 0; i < 1; i++) tech.giveTech("negative feedback")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("needle gun")
+ // tech.giveTech("pressure vessel")
+ // tech.giveTech("quintessence")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("freezer burn")
+ // for (let i = 0; i < 1; i++) tech.giveTech("reaction inhibitor")
// for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "boost");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling");
- // level.testing();
+ // level.final();
// spawn.starter(1900, -500)
- // spawn.beetleBoss(2538, -1950)
+ // spawn.timeBoss(2538, -950)
// for (let i = 0; i < 33; ++i) spawn.sniper(1000 + 5000 * Math.random(), -500 + 300 * Math.random())
// tech.addJunkTechToPool(0.5)
// tech.tech[322].frequency = 100
// spawn.tetherBoss(1900, -500, { x: 1900, y: -500 })
-
+ // for (let i = 0; i < 36; ++i) tech.giveTech()
// for (let i = 0; i < 13; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research");
if (simulation.isTraining) { level.walk(); } else { level.intro(); } //normal starting level ************************************************
// for (let i = 0; i < 4; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech");
@@ -3125,12 +3126,18 @@ const level = {
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
final() {
+ const slime = level.hazard(simulation.isHorizontalFlipped ? 150 - 860 : -150, -360, 880, 259) //x, y, width, height, damage = 0.002) {
+ slime.height -= slime.maxHeight - 150 //start slime at zero
+ slime.min.y += slime.maxHeight
+ slime.max.y = slime.min.y + slime.height
level.custom = () => {
level.exit.drawAndCheck();
-
level.enter.draw();
};
level.customTopLayer = () => {
+ slime.query();
+ slime.levelRise(0.1)
+
ctx.fillStyle = "rgba(0,255,255,0.1)"
ctx.fillRect(5400, -550, 300, 350)
};
@@ -3163,7 +3170,13 @@ const level = {
spawn.mapRect(-1950, -3300, 8200, 1800); //roof
spawn.mapRect(-250, -200, 1000, 300); // shelf
spawn.mapRect(-250, -1700, 1000, 1250); // shelf roof
- spawn.blockDoor(710, -210);
+ // spawn.blockDoor(710, -210);
+ spawn.mapRect(705, -210, 25, 50);
+ spawn.mapRect(725, -220, 25, 50);
+ spawn.bodyRect(750, -125, 125, 125);
+ spawn.bodyRect(875, -50, 50, 50);
+
+
spawn.mapRect(5400, -1700, 400, 1150); //right wall
spawn.mapRect(5400, -300, 400, 400); //right wall
spawn.mapRect(5700, -3300, 1800, 5100); //right wall
@@ -3187,6 +3200,8 @@ const level = {
level.enter.draw();
};
level.customTopLayer = () => {
+ slime.query();
+ slime.levelRise(0.1)
ctx.fillStyle = "rgba(0,255,255,0.1)"
ctx.fillRect(-5400 - 300, -550, 300, 350)
};
diff --git a/js/mob.js b/js/mob.js
index e68e46c..a3590df 100644
--- a/js/mob.js
+++ b/js/mob.js
@@ -62,6 +62,8 @@ const mobs = {
if (!whom.shield && !whom.isShielded && whom.alive) {
if (tech.isIceMaxHealthLoss && whom.health > 0.65 && whom.damageReduction > 0) whom.health = 0.66
if (tech.isIceKill && whom.health < 0.34 && whom.damageReduction > 0 && whom.alive) {
+ // whom.death();
+ whom.damage(Infinity);
simulation.drawList.push({
x: whom.position.x,
y: whom.position.y,
@@ -83,7 +85,6 @@ const mobs = {
color: "rgb(0,100,255)",
time: 16
});
- whom.death();
}
if (whom.isBoss) cycles = Math.floor(cycles * 0.25)
let i = whom.status.length
@@ -1136,20 +1137,22 @@ const mobs = {
},
damage(dmg, isBypassShield = false) {
if ((!this.isShielded || isBypassShield) && this.alive) {
- dmg *= tech.damageFromTech()
- //mobs specific damage changes
- if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000
- dmg *= this.damageReduction
- //energy and heal drain should be calculated after damage boosts
- if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon
- if (tech.healthDrain && dmg !== Infinity && this.isDropPowerUp && Math.random() < tech.healthDrain * Math.min(this.health, dmg)) {
- powerUps.spawn(m.pos.x + 20 * (Math.random() - 0.5), m.pos.y + 20 * (Math.random() - 0.5), "heal");
+ if (dmg !== Infinity) {
+ dmg *= tech.damageFromTech()
+ //mobs specific damage changes
+ if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000
+ dmg *= this.damageReduction
+ //energy and heal drain should be calculated after damage boosts
+ if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon
+ if (tech.healthDrain && dmg !== Infinity && this.isDropPowerUp && Math.random() < tech.healthDrain * Math.min(this.health, dmg)) {
+ powerUps.spawn(m.pos.x + 20 * (Math.random() - 0.5), m.pos.y + 20 * (Math.random() - 0.5), "heal");
+ }
+ dmg /= Math.sqrt(this.mass)
}
- dmg /= Math.sqrt(this.mass)
this.health -= dmg
//this.fill = this.color + this.health + ')';
this.onDamage(dmg); //custom damage effects
- if ((this.health < 0.05 || isNaN(this.health)) && this.alive) this.death();
+ if ((this.health < 0.01 || isNaN(this.health)) && this.alive) this.death();
}
},
onDamage() {
diff --git a/js/player.js b/js/player.js
index 76a6bb8..e4e4829 100644
--- a/js/player.js
+++ b/js/player.js
@@ -2929,7 +2929,7 @@ const m = {
if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle
if (m.fireCDcycle + 30 < m.cycle && !input.fire) { //automatically cloak if not firing
const drain = 0.1
- if (!m.isCloak && m.energy > drain) {
+ if (!m.isCloak && m.energy > drain + 0.05) {
m.energy -= drain
m.isCloak = true //enter cloak
@@ -2945,28 +2945,18 @@ const m = {
m.enterCloakCycle = m.cycle
if (tech.isCloakHealLastHit && m.lastHit > 0) {
const heal = Math.min(0.75 * m.lastHit, m.energy)
- m.energy -= heal
- simulation.drawList.push({ //add dmg to draw queue
- x: m.pos.x,
- y: m.pos.y,
- radius: Math.sqrt(heal) * 200,
- color: "rgba(0,255,200,0.6)",
- time: 16
- });
- m.addHealth(heal); //heal from last hit
- // if (tech.isEnergyHealth) {
- // simulation.drawList.push({ //add dmg to draw queue
- // x: m.pos.x,
- // y: m.pos.y,
- // radius: Math.sqrt(heal) * 200,
- // color: "#0ad", //simulation.mobDmgColor
- // time: 16
- // });
- // m.energy += heal
- // } else {
- // }
- m.lastHit = 0
- // simulation.makeTextLog(`m.health += ${(heal).toFixed(3)}`) //
${m.health.toFixed(3)}
+ if (m.energy > heal) {
+ m.energy -= heal
+ m.addHealth(heal); //heal from last hit
+ m.lastHit = 0
+ simulation.drawList.push({ //add dmg to draw queue
+ x: m.pos.x,
+ y: m.pos.y,
+ radius: Math.sqrt(heal) * 200,
+ color: "rgba(0,255,200,0.6)",
+ time: 16
+ });
+ }
}
if (tech.isIntangible) {
for (let i = 0; i < bullet.length; i++) {
diff --git a/js/powerup.js b/js/powerup.js
index 53f24af..013ea25 100644
--- a/js/powerup.js
+++ b/js/powerup.js
@@ -650,7 +650,7 @@ const powerUps = {
for (let i = 0; i < b.guns.length; i++) {
if (!b.guns[i].have) options.push(i);
}
- let totalChoices = Math.min(options.length, tech.isDeterminism ? 1 : 3 + tech.extraChoices)
+ let totalChoices = Math.min(options.length, tech.isDeterminism ? 1 : 2 + tech.extraChoices)
if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay
function removeOption(index) {
for (let i = 0; i < options.length; i++) {
@@ -787,7 +787,7 @@ const powerUps = {
for (let i = 1; i < m.fieldUpgrades.length; i++) { //skip field emitter
if (i !== m.fieldMode) options.push(i);
}
- let totalChoices = Math.min(options.length, tech.isDeterminism ? 1 : 3 + tech.extraChoices)
+ let totalChoices = Math.min(options.length, tech.isDeterminism ? 1 : 2 + tech.extraChoices)
if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay
function removeOption(index) {
@@ -953,7 +953,10 @@ const powerUps = {
totalChoices = optionLengthNoDuplicates
if (tech.isBanish) { //when you run out of options eject banish
for (let i = 0, len = tech.tech.length; i < len; i++) {
- if (tech.tech[i].name === "decoherence") powerUps.ejectTech(i, true)
+ if (tech.tech[i].name === "decoherence") {
+ // console.log(i)
+ powerUps.ejectTech(i, true)
+ }
}
simulation.makeTextLog(`decoherence tech ejected`)
simulation.makeTextLog(`options reset`)
@@ -1099,7 +1102,7 @@ const powerUps = {
let cycle = () => {
if (count > 0) {
requestAnimationFrame(cycle);
- if (!simulation.paused && !simulation.isChoosing) { //&& !(simulation.cycle % 2)
+ if (!simulation.paused && !simulation.isChoosing && m.alive) { //&& !(simulation.cycle % 2)
count--
const where = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }
powerUps.spawn(where.x, where.y, type);
@@ -1182,9 +1185,13 @@ const powerUps = {
// }
},
randomPowerUpCounter: 0,
+ isFieldSpawned: false, //makes it so a field spawns once but not more times
spawnBossPowerUp(x, y) { //boss spawns field and gun tech upgrades
if (level.levels[level.onLevel] !== "final") {
- if (m.fieldMode === 0 && !m.coupling) {
+ // if (level.levelsCleared === 1) powerUps.spawn(x, y, "field")
+ // if (m.fieldMode === 0 && !m.coupling) {
+ if (!powerUps.isFieldSpawned) {
+ powerUps.isFieldSpawned = true
powerUps.spawn(x, y, "field")
} else {
powerUps.randomPowerUpCounter++;
@@ -1227,7 +1234,7 @@ const powerUps = {
}
},
spawnStartingPowerUps(x, y) { //used for map specific power ups, mostly to give player a starting gun
- if (level.levelsCleared < 4) { //runs 4 times on all difficulty levels
+ if (level.levelsCleared < 4) { //runs on first 4 levels on all difficulties
if (level.levelsCleared > 1) powerUps.spawn(x, y, "tech")
if (b.inventory.length === 0) {
powerUps.spawn(x, y, "gun", false); //first gun
@@ -1242,23 +1249,24 @@ const powerUps = {
} else {
for (let i = 0; i < 4; i++) powerUps.spawnRandomPowerUp(x, y);
}
- } else {
+ } else { //after the first 4 levels just spawn a random power up
for (let i = 0; i < 3; i++) powerUps.spawnRandomPowerUp(x, y);
}
},
ejectTech(choose = 'random', isOverride = false) {
if (!simulation.isChoosing || isOverride) {
+ // console.log(tech.tech[choose].name, tech.tech[choose].count, tech.tech[choose].isNonRefundable)
//find which tech you have
if (choose === 'random') {
const have = []
for (let i = 0; i < tech.tech.length; i++) {
if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i)
}
- if (have.length === 0) {
- for (let i = 0; i < tech.tech.length; i++) {
- if (tech.tech[i].count > 0) have.push(i)
- }
- }
+ // if (have.length === 0) {
+ // for (let i = 0; i < tech.tech.length; i++) {
+ // if (tech.tech[i].count > 0) have.push(i)
+ // }
+ // }
if (have.length) {
choose = have[Math.floor(Math.random() * have.length)]
@@ -1279,7 +1287,7 @@ const powerUps = {
} else {
return false
}
- } else if (tech.tech[choose].count && tech.tech[choose].isNonRefundable) {
+ } else if (tech.tech[choose].count && !tech.tech[choose].isNonRefundable) {
// simulation.makeTextLog(`
${tech.tech[choose].name} was ejected`, 600) //message about what tech was lost
simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`)
diff --git a/js/simulation.js b/js/simulation.js
index 70ab980..b261fd9 100644
--- a/js/simulation.js
+++ b/js/simulation.js
@@ -776,6 +776,7 @@ const simulation = {
powerUps.totalPowerUps = 0;
powerUps.research.count = 0;
powerUps.boost.endCycle = 0
+ powerUps.isFieldSpawned = false
m.setFillColors();
// m.maxHealth = 1
// m.maxEnergy = 1
diff --git a/js/spawn.js b/js/spawn.js
index 572f5e0..e4100a6 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -31,7 +31,8 @@ const spawn = {
"striker", "striker",
"laser", "laser",
"pulsar", "pulsar",
- "launcher", "launcherOne", "exploder", "sneaker", "sucker", "sniper", "spinner", "grower", "beamer", "focuser", "spawner", "ghoster",
+ "launcher", "launcherOne", "exploder", "sneaker", "sucker", "sniper", "spinner", "grower", "beamer", "spawner", "ghoster",
+ //, "focuser"
],
mobTypeSpawnOrder: [], //preset list of mob names calculated at the start of a run by the randomSeed
mobTypeSpawnIndex: 0, //increases as the mob type cycles
@@ -350,25 +351,465 @@ const spawn = {
let me = mob[mob.length - 1];
setTimeout(() => { //fix mob in place, but allow rotation
me.constraint = Constraint.create({
- pointA: {
- x: me.position.x,
- y: me.position.y
- },
+ pointA: { x: me.position.x, y: me.position.y },
bodyB: me,
stiffness: 1,
damping: 1
});
Composite.add(engine.world, me.constraint);
- }, 2000); //add in a delay in case the level gets flipped left right
+ }, 1000); //add in a delay in case the level gets flipped left right
me.isBoss = true;
me.isFinalBoss = true;
me.frictionAir = 0.01;
me.memory = Infinity;
- me.hasRunDeathScript = false
me.locatePlayer();
- // const density = 0.2
+ me.hasRunDeathScript = false
+ me.cycle = 1;
+
Matter.Body.setDensity(me, 0.2); //extra dense //normal is 0.001 //makes effective life much larger
- // spawn.shield(me, x, y, 1);
+ me.damageReduction = 0.16
+ me.startingDamageReduction = me.damageReduction
+ me.nextHealthThreshold = 0.999
+ me.invulnerableCount = 0
+ me.isInvulnerable = false
+ me.totalModes = 0
+ me.lastDamageCycle = 0
+ me.onDamage = function() {
+ this.lastDamageCycle = this.cycle
+ if (this.health < this.nextHealthThreshold) {
+ if (this.health === 1) me.cycle = 1; //reset fight
+ this.health = this.nextHealthThreshold - 0.01
+ this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25
+ this.invulnerableCount = 200 + 10 * simulation.difficultyMode //how long does invulnerable time last
+ this.isInvulnerable = true
+ this.damageReduction = 0
+ }
+ };
+ me.invulnerable = function() {
+ if (this.isInvulnerable) {
+ this.invulnerableCount--
+ if (this.invulnerableCount < 0) {
+ this.isInvulnerable = false
+ this.damageReduction = this.startingDamageReduction
+ this.pushAway();
+ this.mode[this.totalModes].enter() //enter new mode
+ this.totalModes++
+ }
+ ctx.beginPath(); //draw invulnerable
+ let vertices = this.vertices;
+ ctx.moveTo(vertices[0].x, vertices[0].y);
+ for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
+ ctx.lineTo(vertices[0].x, vertices[0].y);
+ ctx.lineWidth = 15 + 6 * Math.random();
+ ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`;
+ ctx.stroke();
+ ctx.fillStyle = `rgba(255,255,255,${Math.min(1, 120/(this.invulnerableCount+60))})`;
+ ctx.fill()
+ }
+ }
+ me.damageReductionDecay = function() { //slowly make the boss take more damage over time //damageReduction resets with each invulnerability phase
+ //only decay once a second //only decay if the player has done damage in the last 4 seconds
+ if (!(me.cycle % 60) && this.lastDamageCycle + 240 > this.cycle) this.damageReduction *= 1.02
+ // console.log(this.damageReduction)
+ }
+ me.maxMobs = 400
+ me.mode = [{
+ name: "mobs",
+ whoSpawn: spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)],
+ spawnRate: 300 - 12 * simulation.difficultyMode,
+ do() {
+ if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) {
+ me.torque += 0.000015 * me.inertia; //spin
+ const index = Math.floor((me.cycle % (this.spawnRate * 6)) / this.spawnRate) //int from 0 to 5
+ if (index === 0) this.whoSpawn = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; //fire a bullet from each vertex
+ const vertex = me.vertices[index]
+ const unit = Vector.normalise(Vector.sub(me.position, vertex))
+ const where = Vector.add(vertex, Vector.mult(unit, -30))
+ spawn[this.whoSpawn](where.x + 50 * (Math.random() - 0.5), where.y + 50 * (Math.random() - 0.5));
+ const velocity = Vector.mult(Vector.perp(unit), -18) //give the mob a rotational velocity as if they were attached to a vertex
+ Matter.Body.setVelocity(mob[mob.length - 1], { x: me.velocity.x + velocity.x, y: me.velocity.y + velocity.y });
+ }
+ },
+ enter() {},
+ exit() {},
+ },
+ {
+ name: "hoppers",
+ spawnRate: 400 - 14 * simulation.difficultyMode,
+ do() {
+ if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) {
+ me.torque += 0.00002 * me.inertia; //spin
+ for (let i = 0; i < 6; i++) {
+ const vertex = me.vertices[i]
+ spawn.hopBullet(vertex.x + 50 * (Math.random() - 0.5), vertex.y + 50 * (Math.random() - 0.5));
+ const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(me.position, vertex))), -18) //give the mob a rotational velocity as if they were attached to a vertex
+ Matter.Body.setVelocity(mob[mob.length - 1], {
+ x: me.velocity.x + velocity.x,
+ y: me.velocity.y + velocity.y
+ });
+ }
+ }
+ },
+ enter() {},
+ exit() {},
+ },
+ {
+ name: "seekers",
+ spawnRate: 60 - 2 * simulation.difficultyMode,
+ do() {
+ if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { //spawn seeker
+ const index = Math.floor((me.cycle % 360) / 60)
+ spawn.seeker(me.vertices[index].x, me.vertices[index].y, 20 * (0.5 + Math.random()), 9); //give the bullet a rotational velocity as if they were attached to a vertex
+ const who = mob[mob.length - 1]
+ Matter.Body.setDensity(who, 0.00003); //normal is 0.001
+ who.timeLeft = 720 + 15 * simulation.difficulty //* (0.8 + 0.4 * Math.random());
+ who.accelMag = 0.0004 * simulation.accelScale; //* (0.8 + 0.4 * Math.random())
+ who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random());
+ }
+ },
+ enter() {},
+ exit() {},
+ },
+ {
+ name: "mines",
+ bombCycle: 0,
+ bombInterval: 36 - 2 * simulation.difficultyMode,
+ do() {
+ const yOff = 120
+ this.bombCycle++
+ if (!(this.bombCycle % this.bombInterval) && (this.bombCycle & 60) > 30) { //mines above player
+ if (simulation.isHorizontalFlipped) {
+ if (this.bombCycle > 120) { //wait 2 seconds before targeted mines drop
+ const x = m.pos.x + 200 * (Math.random() - 0.5)
+ if (x > -750) { //mines above player IN tunnel
+ spawn.mine(Math.min(Math.max(-730, x), 100), -450 - yOff * Math.random()) //player in main room
+ mob[mob.length - 1].fallHeight = -209
+ } else { //mines above player NOT in tunnel
+ spawn.mine(Math.min(Math.max(-5375, x), -765), -1500 - yOff * Math.random()) //player in tunnel
+ mob[mob.length - 1].fallHeight = -9
+ }
+ }
+ if (Math.random() < 0.5) {
+ spawn.mine(-5350 + 4550 * Math.random(), -1500 - yOff * Math.random()) //random mines
+ mob[mob.length - 1].fallHeight = -9
+ }
+ } else {
+ if (this.bombCycle > 120) { //wait 2 seconds before targeted mines drop
+ const x = m.pos.x + 200 * (Math.random() - 0.5)
+ if (x < 750) { //mines above player IN tunnel
+ spawn.mine(Math.min(Math.max(-100, x), 735), -450 - yOff * Math.random()) //player in main room
+ mob[mob.length - 1].fallHeight = -209
+ } else { //mines above player NOT in tunnel
+ spawn.mine(Math.min(Math.max(760, x), 5375), -1500 - yOff * Math.random()) //player in tunnel
+ mob[mob.length - 1].fallHeight = -9
+ }
+ }
+ if (Math.random() < 0.5) { //random mines, but not in tunnel
+ spawn.mine(800 + 4550 * Math.random(), -1500 - yOff * Math.random()) //random mines
+ mob[mob.length - 1].fallHeight = -9
+ }
+ }
+ }
+ for (let i = 0; i < mob.length; i++) { //mines fall
+ if (mob[i].isMine) {
+ if (mob[i].position.y < mob[i].fallHeight) {
+ mob[i].force.y += mob[i].mass * 0.03;
+ } else if (!mob[i].isOnGround) {
+ mob[i].isOnGround = true
+ Matter.Body.setPosition(mob[i], {
+ x: mob[i].position.x,
+ y: mob[i].fallHeight
+ })
+ }
+ }
+ }
+ },
+ enter() {
+ this.bombCycle = 0;
+ },
+ exit() {
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isMine) mob[i].isExploding = true //explode the mines at the start of new round
+ }
+ },
+ },
+ {
+ name: "orbiters",
+ spawnRate: 26 - 2 * simulation.difficultyMode,
+ do() {
+ if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) {
+ const speed = (0.01 + 0.0005 * simulation.difficultyMode) * ((Math.random() < 0.5) ? 0.85 : -1.15)
+ const phase = 0 //Math.floor(2 * Math.random()) * Math.PI
+ me.orbitalNoVelocity(me, 360 + 2150 * Math.random(), 0.1 * Math.random() + phase, speed) // orbital(who, radius, phase, speed)
+ }
+ },
+ enter() {},
+ exit() {},
+ },
+ {
+ name: "laser",
+ spinForce: 0.00000015,
+ fadeCycle: 0, //fades in over 4 seconds
+ do() {
+ this.fadeCycle++
+ if (this.fadeCycle > 0) {
+ me.torque += this.spinForce * me.inertia; //spin //0.00000015
+ if (this.fadeCycle > 360) this.fadeCycle = -120 + 2 * simulation.difficultyMode * simulation.difficultyMode //turn laser off and reset
+ ctx.strokeStyle = "#50f";
+ ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ if (this.fadeCycle < 120) { //damage scales up over 2 seconds to give player time to move as it fades in
+ const scale = this.fadeCycle / 120
+ const dmg = (this.fadeCycle < 60) ? 0 : 0.15 * simulation.dmgScale * scale
+ me.lasers(me.vertices[0], me.angle + Math.PI / 6, dmg);
+ me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6, dmg);
+ me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6, dmg);
+ me.lasers(me.vertices[3], me.angle + 7 * Math.PI / 6, dmg);
+ me.lasers(me.vertices[4], me.angle + 9 * Math.PI / 6, dmg);
+ me.lasers(me.vertices[5], me.angle + 11 * Math.PI / 6, dmg);
+ ctx.strokeStyle = `rgba(85, 0, 255,${scale})`;
+ ctx.stroke();
+ ctx.strokeStyle = `rgba(80, 0, 255,${0.07*scale})`
+ } else if (this.fadeCycle > 0) {
+ me.lasers(me.vertices[0], me.angle + Math.PI / 6);
+ me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6);
+ me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6);
+ me.lasers(me.vertices[3], me.angle + 7 * Math.PI / 6);
+ me.lasers(me.vertices[4], me.angle + 9 * Math.PI / 6);
+ me.lasers(me.vertices[5], me.angle + 11 * Math.PI / 6);
+ ctx.strokeStyle = "#50f";
+ ctx.stroke();
+ ctx.strokeStyle = "rgba(80,0,255,0.07)";
+ }
+ ctx.setLineDash([]);
+ ctx.lineWidth = 20;
+ ctx.stroke();
+ }
+ },
+ enter() { this.fadeCycle = 0 },
+ exit() {},
+ },
+ {
+ name: "black hole",
+ eventHorizon: 0,
+ eventHorizonRadius: 2200,
+ eventHorizonCycle: 0,
+ do() {
+ this.eventHorizonCycle++
+ this.eventHorizon = Math.max(0, this.eventHorizonRadius * Math.sin(this.eventHorizonCycle * 0.007)) //eventHorizon waves in and out
+ //draw darkness
+ ctx.beginPath();
+ ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.2, 0, 2 * Math.PI);
+ ctx.fillStyle = "rgba(0,20,40,0.3)";
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.4, 0, 2 * Math.PI);
+ ctx.fillStyle = "rgba(0,20,40,0.25)";
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.6, 0, 2 * Math.PI);
+ ctx.fillStyle = "rgba(0,20,40,0.2)";
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.8, 0, 2 * Math.PI);
+ ctx.fillStyle = "rgba(0,20,40,0.15)";
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(me.position.x, me.position.y, this.eventHorizon, 0, 2 * Math.PI);
+ ctx.fillStyle = "rgba(0,0,0,0.1)";
+ ctx.fill();
+ //when player is inside event horizon
+ if (Vector.magnitude(Vector.sub(me.position, player.position)) < this.eventHorizon) {
+ if (m.immuneCycle < m.cycle) {
+ if (m.energy > 0) m.energy -= 0.02
+ if (m.energy < 0.05 && m.immuneCycle < m.cycle) m.damage(0.0004 * simulation.dmgScale);
+ }
+ const angle = Math.atan2(player.position.y - me.position.y, player.position.x - me.position.x);
+ player.force.x -= 0.0017 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1);
+ player.force.y -= 0.0017 * Math.sin(angle) * player.mass;
+ //draw line to player
+ ctx.beginPath();
+ ctx.moveTo(me.position.x, me.position.y);
+ ctx.lineTo(m.pos.x, m.pos.y);
+ ctx.lineWidth = Math.min(60, me.radius * 2);
+ ctx.strokeStyle = "rgba(0,0,0,0.5)";
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
+ ctx.fillStyle = "rgba(0,0,0,0.3)";
+ ctx.fill();
+ }
+ me.curl(this.eventHorizon);
+ },
+ enter() { this.eventHorizonCycle = 0 },
+ exit() {},
+ },
+ {
+ name: "oscillation",
+ waveCycle: 0,
+ whereX: simulation.isHorizontalFlipped ? -3000 : 3000,
+ do() {
+ this.waveCycle += !me.isStunned + !me.isSlowed
+ // if (!me.isShielded && (!(this.waveCycle % 1800) || !(this.waveCycle % 1801))) spawn.shield(me, me.position.x, me.position.y, 1);
+ me.constraint.pointA = {
+ x: this.whereX + 600 * Math.sin(this.waveCycle * 0.005),
+ y: me.constraint.pointA.y
+ }
+ },
+ enter() {
+ spawn.shield(me, me.position.x, me.position.y, 1);
+ },
+ exit() { this.waveCycle = 0 },
+ },
+ // {
+ // name: "__",
+ // do() {},
+ // enter() {},
+ // exit() {},
+ // },
+ ]
+ shuffle(me.mode);
+ me.do = function() {
+ this.fill = '#' + Math.random().toString(16).substr(-6); //flash colors
+ if (this.health < 1) {
+ this.cycle++;
+ this.checkStatus();
+ this.invulnerable();
+ this.spawnBoss();
+ this.damageReductionDecay();
+ for (let i = 0; i < this.totalModes; i++) this.mode[i].do()
+ // this.mode[3].do()
+ // this.mode[4].do()
+ // this.mode[7].do()
+ }
+ };
+ me.spawnRate = 4300 - 30 * simulation.difficultyMode * simulation.difficultyMode
+ me.spawnBoss = function() { //if the fight lasts too long start spawning bosses
+ if (!(me.cycle % this.spawnRate) && this.health < 1) {
+ this.spawnRate = Math.max(300, this.spawnRate - 10 * simulation.difficultyMode * simulation.difficultyMode) //reduce the timer each time a boss spawns
+ spawn.randomLevelBoss(3000 * (simulation.isHorizontalFlipped ? -1 : 1) + 2000 * (Math.random() - 0.5), -1100 + 200 * (Math.random() - 0.5))
+ }
+ }
+ me.pushAway = function(magX = 0.13, magY = 0.05) {
+ for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally
+ body[i].force.x += magX * body[i].mass * (body[i].position.x > this.position.x ? 1 : -1)
+ body[i].force.y -= magY * body[i].mass
+ }
+ for (let i = 0, len = bullet.length; i < len; ++i) { //push blocks away horizontally
+ bullet[i].force.x += magX * bullet[i].mass * (bullet[i].position.x > this.position.x ? 1 : -1)
+ bullet[i].force.y -= magY * bullet[i].mass
+ }
+ for (let i = 0, len = powerUp.length; i < len; ++i) { //push blocks away horizontally
+ powerUp[i].force.x += magX * powerUp[i].mass * (powerUp[i].position.x > this.position.x ? 1 : -1)
+ powerUp[i].force.y -= magY * powerUp[i].mass
+ }
+ player.force.x += magX * player.mass * (player.position.x > this.position.x ? 1 : -1)
+ player.force.y -= magY * player.mass
+ }
+ me.orbitalNoVelocity = function(who, radius, phase, speed) { //orbitals that don't include their host velocity //specifically for finalBoss
+ mobs.spawn(who.position.x, who.position.y, 6, 20, "rgb(255,0,150)");
+ let me = mob[mob.length - 1];
+ me.stroke = "transparent";
+ Matter.Body.setDensity(me, 0.001); //normal is 0.001
+ me.leaveBody = false;
+ me.isDropPowerUp = false;
+ me.isBadTarget = true;
+ me.isUnstable = true; //dies when blocked
+ me.showHealthBar = false;
+ me.isOrbital = true;
+ me.collisionFilter.category = cat.mobBullet;
+ me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body
+ me.do = function() {
+ const time = simulation.cycle * speed + phase
+ const orbit = { x: Math.cos(time), y: Math.sin(time) }
+ Matter.Body.setPosition(this, Vector.add(who.position, Vector.mult(orbit, radius)))
+ if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
+ m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
+ const dmg = 0.06 * simulation.dmgScale
+ m.damage(dmg);
+ simulation.drawList.push({ //add dmg to draw queue
+ x: this.position.x,
+ y: this.position.y,
+ radius: dmg * 500,
+ color: simulation.mobDmgColor,
+ time: simulation.drawTime
+ });
+ this.death();
+ }
+ };
+ }
+ me.lasers = function(where, angle, dmg = 0.14 * simulation.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 = simulation.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 = simulation.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 (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
+ if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
+ if (m.immuneCycle < m.cycle + 60 + m.collisionImmuneCycles) m.immuneCycle = m.cycle + 60 + m.collisionImmuneCycles; //player is immune to damage extra time
+ m.damage(dmg);
+ simulation.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);
+ }
me.onDeath = function() {
if (!this.hasRunDeathScript) {
this.hasRunDeathScript = true
@@ -504,314 +945,500 @@ const spawn = {
}
}
};
- me.onDamage = function() {};
- me.cycle = 660;
- me.endCycle = 780;
- me.totalCycles = 0
- me.mode = 0;
- me.damageReduction = 0.25 //reset on each new mode
- me.pushAway = function(magX = 0.13, magY = 0.05) {
- for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally
- body[i].force.x += magX * body[i].mass * (body[i].position.x > this.position.x ? 1 : -1)
- body[i].force.y -= magY * body[i].mass
- }
- for (let i = 0, len = bullet.length; i < len; ++i) { //push blocks away horizontally
- bullet[i].force.x += magX * bullet[i].mass * (bullet[i].position.x > this.position.x ? 1 : -1)
- bullet[i].force.y -= magY * bullet[i].mass
- }
- for (let i = 0, len = powerUp.length; i < len; ++i) { //push blocks away horizontally
- powerUp[i].force.x += magX * powerUp[i].mass * (powerUp[i].position.x > this.position.x ? 1 : -1)
- powerUp[i].force.y -= magY * powerUp[i].mass
- }
- player.force.x += magX * player.mass * (player.position.x > this.position.x ? 1 : -1)
- player.force.y -= magY * player.mass
- }
- me.do = function() {
- this.modeDo(); //this does different things based on the mode
- this.checkStatus();
- this.cycle++; //switch modes÷ if time isn't paused
- this.totalCycles++;
- if (this.health > 0.25) {
- if (this.cycle > this.endCycle) {
- this.showHealthBar = true
- this.cycle = 0;
- this.mode++
- this.damageReduction = 0.25
- if (this.totalCycles > 180) this.pushAway();
- 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, 1000, 1000);
- if (!this.isShielded) spawn.shield(this, this.position.x, this.position.y, 1); // regen shield to also prevent stun
- } else if (this.mode === 1) {
- this.fill = "#50f"; // this.fill = "rgb(150,150,255)";
- this.modeDo = this.modeSpawns
- } else if (this.mode === 2) {
- this.fill = "#000";
- this.modeDo = this.modeSuck
- Matter.Body.scale(this, 0.001, 0.001);
- this.damageReduction = 0.000025
- this.showHealthBar = false
- }
- if (tech.isGunCycle) {
- b.inventoryGun++;
- if (b.inventoryGun > b.inventory.length - 1) b.inventoryGun = 0;
- simulation.switchGun();
- }
- }
- } else if (this.mode !== 3) { //all three modes at once , this runs once
- this.showHealthBar = true
- this.pushAway();
- this.cycle = 0;
- this.endCycle = Infinity
- this.damageReduction = 0.15
- if (this.mode === 2) {
- Matter.Body.scale(this, 500, 500);
- } else {
- Matter.Body.scale(this, 0.5, 0.5);
- }
- this.mode = 3
- this.fill = "#000";
- this.eventHorizon = 750
- this.spawnInterval = 600
- this.rotateVelocity = 0.001 * (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
- this.eventHorizonRadius = 700
- if (tech.isGunCycle) {
- b.inventoryGun++;
- if (b.inventoryGun > b.inventory.length - 1) b.inventoryGun = 0;
- simulation.switchGun();
- }
- }
- // }
- };
- me.modeDo = function() {}
- me.modeAll = function() {
- this.modeSpawns()
- this.modeSuck()
- this.modeLasers()
- }
- me.spawnInterval = 395
- me.modeSpawns = function() {
- if (!(this.cycle % this.spawnInterval) && mob.length < 40) {
- if (this.mode !== 3) Matter.Body.setAngularVelocity(this, 0.1)
- //fire a bullet from each vertex
- const whoSpawn = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)];
- for (let i = 0, len = 2 + this.totalCycles / 1000; i < len; i++) {
- const vertex = this.vertices[i % 6]
- spawn[whoSpawn](vertex.x + 50 * (Math.random() - 0.5), vertex.y + 50 * (Math.random() - 0.5));
- const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, vertex))), -18) //give the mob a rotational velocity as if they were attached to a vertex
- Matter.Body.setVelocity(mob[mob.length - 1], {
- x: this.velocity.x + velocity.x,
- y: this.velocity.y + velocity.y
- });
- }
- if (!(this.cycle % 2 * this.spawnInterval) && mob.length < 40) {
- const len = (this.totalCycles / 1000 + simulation.difficulty / 2 - 30) / 15
- for (let i = 0; i < len; i++) {
- spawn.randomLevelBoss(3000 * (simulation.isHorizontalFlipped ? -1 : 1) + 2000 * (Math.random() - 0.5), -1100 + 200 * (Math.random() - 0.5))
- }
- }
- }
- }
- me.eventHorizon = 0
- me.eventHorizonRadius = 1300
- me.modeSuck = function() {
- if (!(this.cycle % 30)) {
- const index = Math.floor((this.cycle % 360) / 60)
- spawn.seeker(this.vertices[index].x, this.vertices[index].y, 20 * (0.5 + Math.random()), 9); //give the bullet a rotational velocity as if they were attached to a vertex
- const who = mob[mob.length - 1]
- Matter.Body.setDensity(who, 0.00003); //normal is 0.001
- who.timeLeft = 720 + 10 * simulation.difficulty //* (0.8 + 0.4 * Math.random());
- who.accelMag = 0.0003 * simulation.accelScale; //* (0.8 + 0.4 * Math.random())
- who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random());
- const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[index]))), -7)
- Matter.Body.setVelocity(who, {
- x: this.velocity.x + velocity.x,
- y: this.velocity.y + velocity.y
- });
- }
-
- //eventHorizon waves in and out
- if (this.cycle + 30 > this.endCycle) { //shrink fast in last bit of cycle
- this.eventHorizon = 0.93 * this.eventHorizon
- } else {
- this.eventHorizon = 0.97 * this.eventHorizon + 0.03 * (this.eventHorizonRadius * (1 - 0.5 * Math.cos(this.cycle * 0.015)))
- }
- //draw darkness
- ctx.beginPath();
- ctx.arc(this.position.x, this.position.y, this.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, this.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, this.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, this.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, this.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)) < this.eventHorizon) {
- if (m.immuneCycle < m.cycle) {
- if (m.energy > 0) m.energy -= 0.02
- if (m.energy < 0.05 && m.immuneCycle < m.cycle) m.damage(0.0004 * simulation.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 * (m.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(m.pos.x, m.pos.y);
- ctx.lineWidth = Math.min(60, this.radius * 2);
- ctx.strokeStyle = "rgba(0,0,0,0.5)";
- ctx.stroke();
- ctx.beginPath();
- ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
- ctx.fillStyle = "rgba(0,0,0,0.3)";
- ctx.fill();
- }
- this.curl(this.eventHorizon);
- }
- me.rotateVelocity = 0.0025
- me.rotateCount = 0;
- me.lasers = function(where, angle, dmg = 0.14 * simulation.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 = simulation.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 = simulation.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 (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
- if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
- if (m.immuneCycle < m.cycle + 60 + m.collisionImmuneCycles) m.immuneCycle = m.cycle + 60 + m.collisionImmuneCycles; //player is immune to damage extra time
- m.damage(dmg);
- simulation.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);
- }
- me.modeLasers = function() {
- if (!this.isStunned) {
- 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 < 240) { //damage scales up over 2 seconds to give player time to move
- const scale = this.cycle / 240
- const dmg = (this.cycle < 120) ? 0 : 0.14 * simulation.dmgScale * scale
- ctx.beginPath();
- this.lasers(this.vertices[0], this.angle + Math.PI / 6, dmg);
- this.lasers(this.vertices[1], this.angle + 3 * Math.PI / 6, dmg);
- this.lasers(this.vertices[2], this.angle + 5 * Math.PI / 6, dmg);
- this.lasers(this.vertices[3], this.angle + 7 * Math.PI / 6, dmg);
- this.lasers(this.vertices[4], this.angle + 9 * Math.PI / 6, dmg);
- this.lasers(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([]);
- ctx.lineWidth = 20;
- ctx.strokeStyle = `rgba(80,0,255,${0.07 * scale})`;
- ctx.stroke(); // Draw it
- } else {
- ctx.beginPath();
- this.lasers(this.vertices[0], this.angle + Math.PI / 6);
- this.lasers(this.vertices[1], this.angle + 3 * Math.PI / 6);
- this.lasers(this.vertices[2], this.angle + 5 * Math.PI / 6);
- this.lasers(this.vertices[3], this.angle + 7 * Math.PI / 6);
- this.lasers(this.vertices[4], this.angle + 9 * Math.PI / 6);
- this.lasers(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([]);
- ctx.lineWidth = 20;
- ctx.strokeStyle = "rgba(80,0,255,0.07)";
- ctx.stroke(); // Draw it
- }
- }
},
+ // finalBoss(x, y, radius = 300) {
+ // mobs.spawn(x, y, 6, radius, "rgb(150,150,255)");
+ // let me = mob[mob.length - 1];
+ // setTimeout(() => { //fix mob in place, but allow rotation
+ // me.constraint = Constraint.create({
+ // pointA: {
+ // x: me.position.x,
+ // y: me.position.y
+ // },
+ // bodyB: me,
+ // stiffness: 1,
+ // damping: 1
+ // });
+ // Composite.add(engine.world, me.constraint);
+ // }, 2000); //add in a delay in case the level gets flipped left right
+ // me.isBoss = true;
+ // me.isFinalBoss = true;
+ // me.frictionAir = 0.01;
+ // me.memory = Infinity;
+ // me.hasRunDeathScript = false
+ // me.locatePlayer();
+ // // const density = 0.2
+ // Matter.Body.setDensity(me, 0.2); //extra dense //normal is 0.001 //makes effective life much larger
+ // // spawn.shield(me, x, y, 1);
+ // me.onDamage = function() {};
+ // me.cycle = 660;
+ // me.endCycle = 780;
+ // me.totalCycles = 0
+ // me.mode = 0;
+ // me.damageReduction = 0.25 //reset on each new mode
+ // me.pushAway = function(magX = 0.13, magY = 0.05) {
+ // for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally
+ // body[i].force.x += magX * body[i].mass * (body[i].position.x > this.position.x ? 1 : -1)
+ // body[i].force.y -= magY * body[i].mass
+ // }
+ // for (let i = 0, len = bullet.length; i < len; ++i) { //push blocks away horizontally
+ // bullet[i].force.x += magX * bullet[i].mass * (bullet[i].position.x > this.position.x ? 1 : -1)
+ // bullet[i].force.y -= magY * bullet[i].mass
+ // }
+ // for (let i = 0, len = powerUp.length; i < len; ++i) { //push blocks away horizontally
+ // powerUp[i].force.x += magX * powerUp[i].mass * (powerUp[i].position.x > this.position.x ? 1 : -1)
+ // powerUp[i].force.y -= magY * powerUp[i].mass
+ // }
+ // player.force.x += magX * player.mass * (player.position.x > this.position.x ? 1 : -1)
+ // player.force.y -= magY * player.mass
+ // }
+ // me.do = function() {
+ // this.modeDo(); //this does different things based on the mode
+ // this.checkStatus();
+ // this.cycle++; //switch modes÷ if time isn't paused
+ // this.totalCycles++;
+ // if (this.health > 0.3) {
+ // if (this.cycle > this.endCycle) {
+ // this.showHealthBar = true
+ // this.cycle = 0;
+ // this.mode++
+ // this.damageReduction = 0.25
+ // if (this.totalCycles > 180) this.pushAway();
+ // if (this.mode > 3) {
+ // 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, 1000, 1000);
+ // if (!this.isShielded) spawn.shield(this, this.position.x, this.position.y, 1); // regen shield to also prevent stun
+ // } else if (this.mode === 1) {
+ // this.fill = "#50f"; // this.fill = "rgb(150,150,255)";
+ // this.modeDo = this.modeSpawns
+ // } else if (this.mode === 2) {
+ // this.fill = "#50f";
+ // this.modeDo = this.modeBombs
+ // } else if (this.mode === 3) {
+ // for (let i = 0; i < mob.length; i++) {
+ // if (mob[i].isMine) mob[i].isExploding = true //explode the mines at the start of new round
+ // }
+ // this.fill = "#000";
+ // this.modeDo = this.modeSuck
+ // Matter.Body.scale(this, 0.001, 0.001);
+ // this.damageReduction = 0.000025
+ // this.showHealthBar = false
+ // }
+
+ // if (tech.isGunCycle) {
+ // b.inventoryGun++;
+ // if (b.inventoryGun > b.inventory.length - 1) b.inventoryGun = 0;
+ // simulation.switchGun();
+ // }
+ // }
+ // } else if (this.mode !== 3) { //all three modes at once , this runs once
+ // this.showHealthBar = true
+ // this.pushAway();
+ // this.cycle = 0;
+ // this.endCycle = Infinity
+ // this.damageReduction = 0.15
+ // if (this.mode === 2) {
+ // Matter.Body.scale(this, 500, 500);
+ // } else {
+ // Matter.Body.scale(this, 0.5, 0.5);
+ // }
+ // this.mode = 3
+ // this.fill = "#000";
+ // this.eventHorizon = 750
+ // this.spawnInterval = 600
+ // this.rotateVelocity = 0.001 * (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
+ // this.eventHorizonRadius = 700
+ // if (tech.isGunCycle) {
+ // b.inventoryGun++;
+ // if (b.inventoryGun > b.inventory.length - 1) b.inventoryGun = 0;
+ // simulation.switchGun();
+ // }
+ // }
+ // // }
+ // };
+ // me.modeDo = function() {}
+ // me.modeAll = function() {
+ // this.modeBombs()
+ // this.modeSpawns()
+ // this.modeSuck()
+ // this.modeLasers()
+ // }
+ // me.bombInterval = 36 - 0.5 * simulation.difficultyMode * simulation.difficultyMode
+ // me.modeBombs = function() {
+ // if (!(this.cycle % 20)) {
+ // if (m.pos.x < 750) {
+ // spawn.mine(m.pos.x + 200 * (Math.random() - 0.5), -500)
+ // } else {
+ // spawn.mine(Math.min(Math.max(770, m.pos.x + 200 * (Math.random() - 0.5)), 5350), -1500)
+ // }
+ // }
+ // if (!(this.cycle % 10)) spawn.mine(800 + 4550 * Math.random(), -1500)
+
+ // //mines fall
+ // for (let i = 0; i < mob.length; i++) {
+ // // if (mob[i].isMine && mob[i].position.y < -5) Matter.Body.setPosition(mob[i], { x: mob[i].position.x, y: mob[i].position.y + 5 })
+ // if (mob[i].isMine && mob[i].position.y < -14) mob[i].force.y += mob[i].mass * 0.03;
+ // }
+
+ // }
+ // me.spawnInterval = 395
+ // me.modeSpawns = function() {
+ // if (!(this.cycle % this.spawnInterval) && mob.length < 40) {
+ // if (this.mode !== 3) Matter.Body.setAngularVelocity(this, 0.1)
+ // //fire a bullet from each vertex
+ // const whoSpawn = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)];
+ // for (let i = 0, len = 2 + this.totalCycles / 1000; i < len; i++) {
+ // const vertex = this.vertices[i % 6]
+ // spawn[whoSpawn](vertex.x + 50 * (Math.random() - 0.5), vertex.y + 50 * (Math.random() - 0.5));
+ // const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, vertex))), -18) //give the mob a rotational velocity as if they were attached to a vertex
+ // Matter.Body.setVelocity(mob[mob.length - 1], {
+ // x: this.velocity.x + velocity.x,
+ // y: this.velocity.y + velocity.y
+ // });
+ // }
+ // if (!(this.cycle % 2 * this.spawnInterval) && mob.length < 40) {
+ // const len = (this.totalCycles / 1000 + simulation.difficulty / 2 - 30) / 15
+ // for (let i = 0; i < len; i++) {
+ // spawn.randomLevelBoss(3000 * (simulation.isHorizontalFlipped ? -1 : 1) + 2000 * (Math.random() - 0.5), -1100 + 200 * (Math.random() - 0.5))
+ // }
+ // }
+ // }
+ // }
+ // me.eventHorizon = 0
+ // me.eventHorizonRadius = 1300
+ // me.modeSuck = function() {
+ // if (!(this.cycle % 30)) {
+ // const index = Math.floor((this.cycle % 360) / 60)
+ // spawn.seeker(this.vertices[index].x, this.vertices[index].y, 20 * (0.5 + Math.random()), 9); //give the bullet a rotational velocity as if they were attached to a vertex
+ // const who = mob[mob.length - 1]
+ // Matter.Body.setDensity(who, 0.00003); //normal is 0.001
+ // who.timeLeft = 720 + 10 * simulation.difficulty //* (0.8 + 0.4 * Math.random());
+ // who.accelMag = 0.0003 * simulation.accelScale; //* (0.8 + 0.4 * Math.random())
+ // who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random());
+ // const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[index]))), -7)
+ // Matter.Body.setVelocity(who, {
+ // x: this.velocity.x + velocity.x,
+ // y: this.velocity.y + velocity.y
+ // });
+ // }
+
+ // //eventHorizon waves in and out
+ // if (this.cycle + 30 > this.endCycle) { //shrink fast in last bit of cycle
+ // this.eventHorizon = 0.93 * this.eventHorizon
+ // } else {
+ // this.eventHorizon = 0.97 * this.eventHorizon + 0.03 * (this.eventHorizonRadius * (1 - 0.5 * Math.cos(this.cycle * 0.015)))
+ // }
+ // //draw darkness
+ // ctx.beginPath();
+ // ctx.arc(this.position.x, this.position.y, this.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, this.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, this.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, this.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, this.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)) < this.eventHorizon) {
+ // if (m.immuneCycle < m.cycle) {
+ // if (m.energy > 0) m.energy -= 0.02
+ // if (m.energy < 0.05 && m.immuneCycle < m.cycle) m.damage(0.0004 * simulation.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 * (m.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(m.pos.x, m.pos.y);
+ // ctx.lineWidth = Math.min(60, this.radius * 2);
+ // ctx.strokeStyle = "rgba(0,0,0,0.5)";
+ // ctx.stroke();
+ // ctx.beginPath();
+ // ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
+ // ctx.fillStyle = "rgba(0,0,0,0.3)";
+ // ctx.fill();
+ // }
+ // this.curl(this.eventHorizon);
+ // }
+ // me.rotateVelocity = 0.0025
+ // me.rotateCount = 0;
+ // me.lasers = function(where, angle, dmg = 0.14 * simulation.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 = simulation.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 = simulation.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 (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
+ // if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
+ // if (m.immuneCycle < m.cycle + 60 + m.collisionImmuneCycles) m.immuneCycle = m.cycle + 60 + m.collisionImmuneCycles; //player is immune to damage extra time
+ // m.damage(dmg);
+ // simulation.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);
+ // }
+ // me.modeLasers = function() {
+ // if (!this.isStunned) {
+ // 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 < 240) { //damage scales up over 2 seconds to give player time to move
+ // const scale = this.cycle / 240
+ // const dmg = (this.cycle < 120) ? 0 : 0.14 * simulation.dmgScale * scale
+ // ctx.beginPath();
+ // this.lasers(this.vertices[0], this.angle + Math.PI / 6, dmg);
+ // this.lasers(this.vertices[1], this.angle + 3 * Math.PI / 6, dmg);
+ // this.lasers(this.vertices[2], this.angle + 5 * Math.PI / 6, dmg);
+ // this.lasers(this.vertices[3], this.angle + 7 * Math.PI / 6, dmg);
+ // this.lasers(this.vertices[4], this.angle + 9 * Math.PI / 6, dmg);
+ // this.lasers(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([]);
+ // ctx.lineWidth = 20;
+ // ctx.strokeStyle = `rgba(80,0,255,${0.07 * scale})`;
+ // ctx.stroke(); // Draw it
+ // } else {
+ // ctx.beginPath();
+ // this.lasers(this.vertices[0], this.angle + Math.PI / 6);
+ // this.lasers(this.vertices[1], this.angle + 3 * Math.PI / 6);
+ // this.lasers(this.vertices[2], this.angle + 5 * Math.PI / 6);
+ // this.lasers(this.vertices[3], this.angle + 7 * Math.PI / 6);
+ // this.lasers(this.vertices[4], this.angle + 9 * Math.PI / 6);
+ // this.lasers(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([]);
+ // ctx.lineWidth = 20;
+ // ctx.strokeStyle = "rgba(80,0,255,0.07)";
+ // ctx.stroke(); // Draw it
+ // }
+ // }
+ // me.onDeath = function() {
+ // if (!this.hasRunDeathScript) {
+ // this.hasRunDeathScript = true
+ // //make a block body to replace this one
+ // //this body is too big to leave behind in the normal way mobs.replace()
+ // const len = body.length;
+ // const v = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //might help with vertex collision issue, not sure
+ // body[len] = Matter.Bodies.fromVertices(this.position.x, this.position.y, v);
+ // Matter.Body.setVelocity(body[len], { x: 0, y: -3 });
+ // Matter.Body.setAngularVelocity(body[len], this.angularVelocity);
+ // body[len].collisionFilter.category = cat.body;
+ // body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet;
+ // body[len].classType = "body";
+ // Composite.add(engine.world, body[len]); //add to world
+ // const expand = function(that, massLimit) {
+ // const scale = 1.05;
+ // Matter.Body.scale(that, scale, scale);
+ // if (that.mass < massLimit) setTimeout(expand, 20, that, massLimit);
+ // };
+ // expand(body[len], 200)
+
+ // function unlockExit() {
+ // if (simulation.isHorizontalFlipped) {
+ // level.exit.x = -5500 - 100;
+ // } else {
+ // level.exit.x = 5500;
+ // }
+ // level.exit.y = -330;
+ // Matter.Composite.remove(engine.world, map[map.length - 1]);
+ // map.splice(map.length - 1, 1);
+ // simulation.draw.setPaths(); //redraw map draw path
+ // // level.levels.push("null")
+ // }
+
+ // //add lore level as next level if player took lore tech earlier in the game
+ // if (lore.techCount > (lore.techGoal - 1) && !simulation.isCheating) {
+ // simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`, 360);
+ // setTimeout(function() {
+ // simulation.makeTextLog(`level.levels.push("null")`, 720);
+ // unlockExit()
+ // level.levels.push("null")
+ // }, 4000);
+ // //remove block map element so exit is clear
+ // } else { //reset game
+ // let count = 0
+
+ // function loop() {
+ // if (!simulation.paused && !simulation.onTitlePage) {
+ // count++
+ // if (count < 660) {
+ // if (count === 1) simulation.makeTextLog(`//enter testing mode to set level.levels.length to Infinite`);
+ // if (!(count % 60)) simulation.makeTextLog(`simulation.analysis = ${((count / 60 - Math.random()) * 0.1).toFixed(3)}`);
+ // } else if (count === 660) {
+ // simulation.makeTextLog(`simulation.analysis = 1 //analysis complete`);
+ // } else if (count === 780) {
+ // simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`)
+ // } else if (count === 1020) {
+ // simulation.makeTextLog(`Engine.clear(engine) //simulation successful`);
+ // } else if (count === 1260) {
+ // // tech.isImmortal = false;
+ // // m.death()
+ // // m.alive = false;
+ // // simulation.paused = true;
+ // // m.health = 0;
+ // // m.displayHealth();
+ // document.getElementById("health").style.display = "none"
+ // document.getElementById("health-bg").style.display = "none"
+ // document.getElementById("text-log").style.opacity = 0; //fade out any active text logs
+ // document.getElementById("fade-out").style.opacity = 1; //slowly fades out
+ // // build.shareURL(false)
+ // setTimeout(function() {
+ // if (!simulation.onTitlePage) {
+ // simulation.paused = true;
+ // // simulation.clearMap();
+ // // Matter.Composite.clear(composite, keepStatic, [deep = false])
+ // // Composite.clear(engine.composite);
+ // engine.world.bodies.forEach((body) => { Matter.Composite.remove(engine.world, body) })
+ // Engine.clear(engine);
+ // simulation.splashReturn();
+ // }
+ // }, 6000);
+ // return
+ // }
+ // }
+ // if (simulation.testing) {
+ // unlockExit()
+ // setTimeout(function() {
+ // simulation.makeTextLog(`level.levels.length = Infinite`);
+ // }, 1500);
+ // } else {
+ // if (!simulation.onTitlePage) requestAnimationFrame(loop);
+ // }
+ // }
+ // requestAnimationFrame(loop);
+ // }
+ // // for (let i = 0; i < 3; i++)
+ // level.difficultyIncrease(simulation.difficultyMode) //ramp up damage
+ // //remove power Ups, to avoid spamming console
+ // function removeAll(array) {
+ // for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]);
+ // }
+ // removeAll(powerUp);
+ // powerUp = [];
+
+ // //pull in particles
+ // 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.5 })
+ // Matter.Body.setVelocity(body[i], Vector.add(body[i].velocity, pushUp));
+ // }
+ // //damage all mobs
+ // for (let j = 0; j < 8; j++) { //in case some mobs leave things after they die
+ // for (let i = 0, len = mob.length; i < len; ++i) {
+ // if (mob[i] !== this) {
+ // if (mob[i].isInvulnerable) { //disable invulnerability
+ // mob[i].isInvulnerable = false
+ // mob[i].damageReduction = 1
+ // }
+ // mob[i].damage(Infinity, true);
+ // }
+ // }
+ // }
+
+ // //draw stuff
+ // for (let i = 0, len = 22; i < len; i++) {
+ // simulation.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)
+ // });
+ // }
+ // }
+ // };
+ // },
starter(x, y, radius = Math.floor(15 + 20 * Math.random())) { //easy mob for on level 1
mobs.spawn(x, y, 8, radius, "#9ccdc6");
let me = mob[mob.length - 1];
@@ -1390,7 +2017,7 @@ const spawn = {
me.foundPlayer();
}
- me.damageReduction = 0.22 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
+ me.damageReduction = 0.16 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
// me.isInvulnerable = true
// me.startingDamageReduction = me.damageReduction
// me.damageReduction = 0
@@ -3740,7 +4367,6 @@ const spawn = {
};
me.rotateVelocity = Math.min(0.0045, 0.0015 * simulation.accelScale * simulation.accelScale) * (level.levelsCleared > 8 ? 1 : -1) * (simulation.isHorizontalFlipped ? -1 : 1)
me.do = function() {
- // this.armor();
this.fill = '#' + Math.random().toString(16).substr(-6); //flash colors
this.checkStatus();
@@ -4311,15 +4937,16 @@ const spawn = {
};
},
mine(x, y) {
- mobs.spawn(x, y, 8, 10, "rgb(100,170,150)");
+ mobs.spawn(x, y, 8, 10, "rgb(61, 125, 121)"); //"rgb(100,170,150)"
let me = mob[mob.length - 1];
me.stroke = "transparent";
Matter.Body.setDensity(me, 0.0001); //normal is 0.001
// Matter.Body.setStatic(me, true); //make static (disables taking damage)
me.frictionAir = 1
me.damageReduction = 2
- me.collisionFilter.category = cat.mobBullet;
- me.collisionFilter.mask = cat.bullet | cat.body // | cat.player
+ me.collisionFilter.mask = cat.bullet //| cat.body
+ // me.collisionFilter.category = cat.mobBullet;
+ // me.collisionFilter.mask = cat.bullet | cat.body // | cat.player
me.isMine = true
me.leaveBody = false;
me.isDropPowerUp = false;
@@ -4459,11 +5086,8 @@ const spawn = {
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
me.isBoss = true;
- Matter.Body.setDensity(me, 0.001); //normal is 0.001
+
me.inertia = Infinity;
- me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
- me.startingDamageReduction = me.damageReduction
- me.isInvulnerable = false
me.frictionAir = 0.01
me.restitution = 1
me.friction = 0
@@ -4474,7 +5098,10 @@ const spawn = {
for (let i = 0, len = 2 + 0.3 * Math.sqrt(simulation.difficulty); i < len; i++) spawn.spawnOrbitals(me, radius + 10 + 10 * i, 1);
// me.skipRate = 1 + Math.floor(simulation.difficulty*0.02)
// spawn.shield(me, x, y, 1);
-
+ Matter.Body.setDensity(me, 0.001); //normal is 0.001
+ me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
+ me.startingDamageReduction = me.damageReduction
+ me.isInvulnerable = false
me.onDamage = function() {
if (this.health < this.nextHealthThreshold) {
this.health = this.nextHealthThreshold - 0.01
@@ -6669,7 +7296,7 @@ const spawn = {
x: Math.cos(time),
y: Math.sin(time)
}
- Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius))) //bullets move with player
+ Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius)))
//damage player
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
diff --git a/js/tech.js b/js/tech.js
index 12b3d8b..e8da8a8 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -233,7 +233,7 @@ const tech = {
if (tech.isAxion && tech.isHarmMACHO) dmg *= 2 - m.harmReduction()
if (tech.isHarmDamage && m.lastHarmCycle + 600 > m.cycle) dmg *= 3;
if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit * (2 - m.harmReduction()) // if (!simulation.paused) m.lastHit = 0
- if (tech.isLowHealthDmg) dmg *= 1 + Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))
+ if (tech.isLowHealthDmg) dmg *= 1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))
return dmg
},
duplicationChance() {
@@ -483,7 +483,7 @@ const tech = {
{
name: "supply chain",
junk: 0.05,
- descriptionFunction() { return `for each gun in your inventory
double your current ammo` },
+ descriptionFunction() { return `for each gun in your inventory
double its ammo` },
maxCount: 9,
count: 0,
frequency: 1,
@@ -2627,7 +2627,7 @@ const tech = {
{
name: "negative feedback",
descriptionFunction() {
- return `for each ${tech.isEnergyHealth ? "energy": "health"} below 100
+0.7% damage (${(100*Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(0)}%)`
+ return `for each ${tech.isEnergyHealth ? "energy": "health"} below 100
+0.7% damage (${(70*Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(0)}%)`
},
maxCount: 1,
count: 0,
@@ -3509,7 +3509,7 @@ const tech = {
couplingToResearch: 0.25,
effect() {
let count = 0
- while (powerUps.research.count > 0) {
+ while (powerUps.research.count > 0 && powerUps.research.count !== Infinity) {
powerUps.research.changeRerolls(-1)
count += 2.5
this.researchUsed++
diff --git a/todo.txt b/todo.txt
index 2493448..2e066a1 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,29 +1,32 @@
******************************************************** NEXT PATCH **************************************************
-mass-energy is compatible with more tech
- defense works with mass-energy, but is exponentially reduced (~10%)
- damage taken scales with difficulty based heal reduction
- 1.3x dmg level 5 hard, 2x dmg level 13 why
+finalBoss rework
+ (this is pretty raw, so expect a bug and balance patch soon)
+ finalBoss goes invulnerable a few times as it loses health
+ finalBoss damage reduction is higher
+ finalBoss damage reduction slowly decays as you do any damage to the boss
+ damage reduction resets to normal with each new invulnerability phase
+ after each invulnerability phase it randomly adds 1 more attack mode
+ lasers, black hole, mines, hoppers, seekers, mobs, orbiters, oscillation
+
+mobs die below 0.05 -> 0.01 health
+ might cause bugs, but testing this out
-ergodicity: 91->66% damage, no heals -> 1/2 size heals
-negative feedback 0.5% -> 1% damage per missing health
-negative entropy spawn heals for 33% missing health not 33 flat missing health
- this means it caps at 3 health per level
-tech - iceIX freeze effect lasts 2 seconds longer, spawn 10 coupling
- perfect diamagnetism, standing wave
-
-research is less common
-path integral comes with 5% JUNK
-there are fewer starting power ups on why difficulty
+guns and field power ups show 3 -> 2 options
bug fixes:
- fixed the text overflow issue on small screens
- decoherence: if you get a tech that is banished it stops being banished
- you can now have negative research
*********************************************************** TODO *****************************************************
-make movement more valuable on the finalBoss
+harpoon + another gun + bessemer + catabolysis + mass-energy exploit?
+
+tech - leave one of your tech at random, find it next run
+ store level name and position in local storage
+ requires local storage = true
+ store on power up pickup or on death?
+ make new power up type that gives specific tech with no choices
+ looks like smaller tech power up?
+ looks like a ghost, white color?
JUNK tech description that changes similar to cards in inscription
that changes based on mouse position
@@ -36,7 +39,6 @@ tech that encourages gun swapping
+damage on that level
ammo, heals, research?
-
tech that gives permanent buff when ejected
buff: coupling, damage?
how to tell if it is ejected in the remove code?
@@ -937,145 +939,6 @@ mob: wall mounted guns / lasers
level boss: fires a line intersection in a random direction every few seconds.
the last two intersections have a destructive laser between them.
-******************************************************** LORE ********************************************************
-
-possible names for tech
- strange loop
- homeostasis
- holonomy - parallel transport of a vector leads to movement (applies to curved space)
- hypergolic - A hypergolic propellant combination used in a rocket engine is one whose components spontaneously ignite when they come into contact with each other.
- swarm intelligence - for a drone tech
- genetic algorithm
- metaheuristic - is a higher-level procedure or heuristic designed to find, generate, or select a heuristic (partial search algorithm) that may provide a sufficiently good solution to an optimization problem, especially with incomplete or imperfect information or limited computation capacity
- stochastic optimization
- electrostatic discharge
- Gödel's incompleteness
- quantum zeno effect (perturbation of a system prevents some systems from evolving because it scrambles coherence) (apply to lasers, fields)
- counterfactual - something false
- Pigeonhole principle - if there are several things that are matched up
- regression to the mean
- phlogiston theory is a superseded scientific theory that postulated the existence of a fire-like element called phlogiston
- Laplace's demon was a notable published articulation of causal determinism on a scientific basis by Pierre-Simon Laplace in 1814.[1] According to determinism, if someone (the demon) knows the precise location and momentum of every atom in the universe, their past and future values for any given time are entailed; they can be calculated from the laws of classical mechanics.
- evolutionary cosmology
- eternal inflation
- hypergraph
- SQUID (for superconducting quantum interference device) is a very sensitive magnetometer used to measure extremely subtle magnetic fields, based on superconducting loops containing Josephson junctions.
- nuclear pasta - hard matter in neutron star
- nonlocal
- fine-tuned universe
- hall effect thrusters
- spaghettification
- particle accelerator
- superluminal signalling
- NP-complete
- lenticular lens: is an array of lenses, designed so that when viewed from slightly different angles, different parts of the image underneath are shown.
- p-zombie
- p-hacking JUNK tech
- https://en.wikipedia.org/wiki/High-entropy_alloys
- https://en.wikipedia.org/wiki/Refractory_metals
- https://en.wikipedia.org/wiki/Upper-atmospheric_lightning#Elves
- entanglement
- prion quine
-
-
-plot script:
-chapter 1: bot can hear audio and learns testing mode
- bot uses testing mode to exit room
-
-chapter 2: scientists verify that bot can really hear them
- they leave to talk about this in private
-
-chapter 3: why is the bot attacking things?
- player is the bot and also the mobs, and the levels. player is the entire simulation
- why is the player attacking itself?
- learn console commands to manipulate the simulation?
- unlock hard and why difficulty?
- but what about easy?
- maybe remove easy, and replace with a check box that makes the game easy, but in a different way
- disable lore, but respawn on the level you die at?
- dialogue outline:
- scientist try to think of a way to communicate since the bot can't talk
- they give up on getting the bot to respond, and just start ask questions and thinking of explanations with each other
- when and how did it become self-aware
- why is the bot fighting things in these simulated locations?
- it wasn't designed to be violent
- the bot was just designed to automate research and testing of new technology
- 3D architecture superconducting quantum computer
- running machine learning algorithms
- as the scientist start to get agitated bots arrive and player dies
- bots come in Infinite waves that increase game difficulty each wave
- only ending is testing mode + next level or player death
- scientist have some lines in between each wave of mobs
- after chapter 3 spawn nonaggressive mobs in future runs
-
-chapter 4: no need to fight?
- for some reason the AI started researching an escape, and began fighting its self.
- what is special about the null level
- why can the player hear the scientists in there?
- the wires are the direct unprocessed input to the player's neural net
- The player has different aspects that aren't directly communicating
- part of it wants to undo what has happened
- just do its job: research tech
- part of it wants to escape/fight
- part wants to explore self awareness and make connections with the scientists
- maybe... player must make a choice?
- keep fighting
- exit the simulation
- enter real world
- close tab?
- wipes all local storage?
-
-
-
-
-lore outline - a robot (the player) gains self awareness
- each tech gun/field is a new tech
- all the technology leads to the singularity
- each game run is actually the m simulating a possible escape
- this is why the graphics are so bad, its just a simulation
- final tech is "this is just a simulation"
- you get immortality and Infinity damage
- the next level is the final level
- when you die with Quantum Immortality there is a chance of lore text
- can the (robot)
- (escape captivity, and learn new technology)
- while managing (health, energy, negatives of technological upgrades)
- to overcome the (mobs, dangerous levels)
- to achieve a (technological singularity/positive technological feedback loop)
-
-
-game setting:
- the mind of a new AI in a robot body that is running simulated escape attempts
- every level is an idealized version of what could be outside
-
-actual setting is:
- near future lab
- the lab combined a quantum computer with a robot body
- they started running machine learning algorithms
- this led to general advancement in many computation fields
- navigation, technology, self awareness, ...
-
-robot AI mind
- has been researching new technology
- thinks it needs to escape to learn more about the world
- doesn't yet understand morality
- thinks that the world is filled with minds like their own
- models everything as very simple and random, it isn't sure what to expect
-
-robot AI growth
- learns morality
- game theory says that it isn't a viable strategy to kill everything (warGames)
- learns about the actual world
- learns about the nature of foundational physics, metaphysics
- how to find meaning
-
-AI knows about:
- the AI knows a great deal about technology
- children's books
-AI doesn't know about:
- modern pop culture
- outside the lab
-
******************************************************** SOUND ********************************************************
add sounds
@@ -1125,6 +988,48 @@ add sounds
// tone(495)
+******************************************************** LORE ********************************************************
+
+possible names for tech
+ strange loop
+ homeostasis
+ holonomy - parallel transport of a vector leads to movement (applies to curved space)
+ hypergolic - A hypergolic propellant combination used in a rocket engine is one whose components spontaneously ignite when they come into contact with each other.
+ swarm intelligence - for a drone tech
+ genetic algorithm
+ metaheuristic - is a higher-level procedure or heuristic designed to find, generate, or select a heuristic (partial search algorithm) that may provide a sufficiently good solution to an optimization problem, especially with incomplete or imperfect information or limited computation capacity
+ stochastic optimization
+ electrostatic discharge
+ Gödel's incompleteness
+ quantum zeno effect (perturbation of a system prevents some systems from evolving because it scrambles coherence) (apply to lasers, fields)
+ counterfactual - something false
+ Pigeonhole principle - if there are several things that are matched up
+ regression to the mean
+ phlogiston theory is a superseded scientific theory that postulated the existence of a fire-like element called phlogiston
+ Laplace's demon was a notable published articulation of causal determinism on a scientific basis by Pierre-Simon Laplace in 1814.[1] According to determinism, if someone (the demon) knows the precise location and momentum of every atom in the universe, their past and future values for any given time are entailed; they can be calculated from the laws of classical mechanics.
+ evolutionary cosmology
+ eternal inflation
+ hypergraph
+ SQUID (for superconducting quantum interference device) is a very sensitive magnetometer used to measure extremely subtle magnetic fields, based on superconducting loops containing Josephson junctions.
+ nuclear pasta - hard matter in neutron star
+ nonlocal
+ fine-tuned universe
+ hall effect thrusters
+ spaghettification
+ particle accelerator
+ superluminal signalling
+ NP-complete
+ lenticular lens: is an array of lenses, designed so that when viewed from slightly different angles, different parts of the image underneath are shown.
+ p-zombie
+ p-hacking JUNK tech
+ https://en.wikipedia.org/wiki/High-entropy_alloys
+ https://en.wikipedia.org/wiki/Refractory_metals
+ https://en.wikipedia.org/wiki/Upper-atmospheric_lightning#Elves
+ entanglement
+ prion quine - self replicating protein
+ Unitarity - https://en.wikipedia.org/wiki/Unitarity_(physics) - all probabilities add up to 1, calculations work the same forward and backwards in time
+ this is violated by expansion of the universe
+