diff --git a/js/engine.js b/js/engine.js
index 3a0e8c4..76ad145 100644
--- a/js/engine.js
+++ b/js/engine.js
@@ -119,11 +119,25 @@ function collisionChecks(event) {
if (document.getElementById("tech-flip-flop")) document.getElementById("tech-flip-flop").innerHTML = ` = OFF`
m.eyeFillColor = 'transparent'
m.damage(dmg);
+ if (tech.isFlipFlopCoupling) {
+ m.couplingChange(-5)
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ spawn.WIMP()
+ mob[mob.length - 1].isDecoupling = true //so you can find it to remove
+ }
} else {
tech.isFlipFlopOn = true //immune to damage this hit, lose immunity for next hit
if (document.getElementById("tech-flip-flop")) document.getElementById("tech-flip-flop").innerHTML = ` = ON`
m.eyeFillColor = m.fieldMeterColor //'#0cf'
if (!tech.isFlipFlopHarm) m.damage(dmg);
+ if (tech.isFlipFlopCoupling) {
+ m.couplingChange(5)
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ }
}
if (tech.isFlipFlopHealth) {
m.setMaxHealth();
@@ -146,6 +160,7 @@ function collisionChecks(event) {
simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`);
}
if (tech.isPiezo) m.energy += 20.48;
+ if (tech.isCouplingNoHit) m.couplingChange(-0.5)
if (tech.isStimulatedEmission) powerUps.ejectTech()
if (mob[k].onHit) mob[k].onHit();
if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
diff --git a/js/index.js b/js/index.js
index e00e897..e2eb45a 100644
--- a/js/index.js
+++ b/js/index.js
@@ -257,7 +257,7 @@ const build = {
defense: ${(1-m.harmReduction()).toPrecision(3)} difficulty: ${(1/simulation.dmgScale).toPrecision(3)}
${b.fireCDscale < 1 ? `
fire rate: ${((1-b.fireCDscale)*100).toFixed(b.fireCDscale < 0.1 ? 2 : 0)}%`: ""}
${tech.duplicationChance() ? `
duplication: ${(tech.duplicationChance()*100).toFixed(0)}%`: ""}
-${m.coupling ? `
coupling: ${(m.coupling).toFixed(2)} `+m.couplingDescription(true)+"": ""}
+${m.coupling ? `
coupling: ${(m.coupling).toFixed(2)} `+m.couplingDescription()+"": ""}
${botText}
health: (${(m.health*100).toFixed(0)} / ${(m.maxHealth*100).toFixed(0)})
diff --git a/js/level.js b/js/level.js
index 61b4b3f..ee63822 100644
--- a/js/level.js
+++ b/js/level.js
@@ -10,7 +10,7 @@ const level = {
// playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"],
//see level.populateLevels: (intro, ... , reservoir, reactor, ... , gauntlet, final) added later
playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion"],
- communityLevels: ["stronghold", "basement", "crossfire", "vats", "run", "n-gon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp"],
+ communityLevels: ["stronghold", "basement", "crossfire", "vats", "run", "n-gon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard"],
trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"],
levels: [],
start() {
@@ -23,26 +23,27 @@ const level = {
// powerUps.research.changeRerolls(100000)
// m.immuneCycle = Infinity //you can't take damage
// tech.tech[297].frequency = 100
- // m.setField("metamaterial cloaking") //molecular assembler standing wave time dilation perfect diamagnetism metamaterial cloaking wormhole negative mass
- // b.giveGuns("shotgun") //0 nail gun 1 shotgun 2 super balls 3 matter wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
+ // m.setField("time dilation") //molecular assembler standing wave time dilation perfect diamagnetism metamaterial cloaking wormhole negative mass
+ // b.giveGuns("laser") //0 nail gun 1 shotgun 2 super balls 3 matter wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.guns[0].ammo = 1000000
- // tech.giveTech("rivet gun")
- // tech.isFoamShot = true
- // tech.isIncendiary = true
- // for (let i = 0; i < 1; ++i) tech.giveTech("field coupling")
- // for (let i = 0; i < 1; ++i) tech.giveTech("free-electron laser")
+ // tech.giveTech("fine-structure constant")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("relay switch")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("decoupling")
// m.damage(0.1);
- // for (let i = 0; i < 1; i++) tech.giveTech("dynamic equilibrium")
+ // tech.giveTech("lens")
+ // for (let i = 0; i < 9; i++) tech.giveTech("compound lens")
// 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");
// spawn.starter(1900, -500, 200)
- // spawn.ghoster(2538, -1950)
+ // spawn.beetleBoss(2538, -1950)
// for (let i = 0; i < 15; ++i) spawn.shooter(1900 + 300 * Math.random(), -500 + 300 * Math.random())
+ // tech.addJunkTechToPool(2)
+ // tech.tech[321].frequency = 100
// level.testing();
// spawn.blowSuckBoss(1900, -500)
- // for (let i = 0; i < 7; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research");
+ // for (let i = 0; i < 13; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research");
// for (let i = 0; i < 4; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech");
if (simulation.isTraining) { level.walk(); } else { level.intro(); } //normal starting level ************************************************
@@ -114,6 +115,7 @@ const level = {
if (tech.isMACHO) spawn.MACHO()
for (let i = 0; i < tech.wimpCount; i++) {
spawn.WIMP()
+ mob[mob.length - 1].isDecoupling = true //so you can find it to remove
for (let j = 0, len = 5; j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false)
}
// if (tech.isFlipFlopLevelReset && !tech.isFlipFlopOn) {
@@ -121,6 +123,12 @@ const level = {
tech.isFlipFlopOn = true
if (tech.isFlipFlopHealth) m.setMaxHealth()
if (tech.isRelayEnergy) m.setMaxEnergy()
+ if (tech.isFlipFlopCoupling) {
+ m.couplingChange(5)
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ }
m.eyeFillColor = m.fieldMeterColor
simulation.makeTextLog(`tech.isFlipFlopOn = true`);
}
@@ -11464,1407 +11472,6 @@ const level = {
DrawHandler.room2Top();
};
},
-
- // temple() {
- // simulation.makeTextLog(`temple by Scar1337`);
-
- // const V = Vector;
- // const Equation = (function() {
- // function Equation(a, b, c) {
- // this.a = a;
- // this.b = b;
- // this.c = c;
- // }
- // Equation.prototype.getXfromY = function(y) {
- // return (-this.b * y - this.c) / this.a;
- // }
- // Equation.prototype.getYfromX = function(x) {
- // return (-this.a * x - this.c) / this.b;
- // }
- // Equation.fromPoints = function(v1, v2) {
- // if (v1.x === v2.x) return new Equation(1, 0, -v1.x);
- // if (v1.y === v2.y) return new Equation(0, 1, -v1.y);
- // const d = (v2.y - v1.y) / (v2.x - v1.x);
- // return new Equation(-d, 1, d * v1.x - v1.y);
- // };
- // return Equation;
- // })();
- // const Rect = (function() {
- // function Rect(x, y, w, h) {
- // this.pos = { x, y };
- // this.width = w;
- // this.height = h;
- // }
- // Rect.prototype.has = function({ x, y }) {
- // return x >= this.pos.x && x <= this.pos.x + this.width &&
- // y >= this.pos.y && y <= this.pos.y + this.height;
- // }
- // Rect.prototype.hasLine = function(eq) {
- // const leftInter = eq.getYfromX(this.pos.x);
- // const rightInter = eq.getYfromX(this.pos.x + this.width);
- // const topInter = eq.getXfromY(this.pos.y);
- // return (leftInter >= this.pos.y && leftInter <= this.pos.y + this.height) ||
- // (rightInter >= this.pos.y && rightInter <= this.pos.y + this.height) ||
- // (topInter >= this.pos.x && topInter <= this.pos.x + this.width);
- // }
- // Rect.prototype.addToMap = function() {
- // spawn.mapRect(this.pos.x, this.pos.y, this.width, this.height);
- // }
- // Object.defineProperty(Rect.prototype, "midPos", {
- // get() {
- // return V.add(this.pos, { x: this.width / 2, y: this.height / 2 });
- // }
- // });
- // Rect.fromBounds = function(min, max) {
- // return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
- // }
- // return Rect;
- // })();
-
- // function isInBound(bound) {
- // return bound.has(player.bounds.min) || bound.has(player.bounds.max);
- // }
-
- // function addWIMP(x, y) {
- // spawn.WIMP(x, y);
- // const me = mob[mob.length - 1];
- // me.isWIMP = true;
- // }
-
- // function relocateWIMPs(x, y) {
- // for (const i of mob) {
- // if (i.isWIMP) {
- // setPos(i, { x: x + 300 * (Math.random() - 0.5), y: y + 300 * (Math.random() - 0.5) });
- // }
- // }
- // }
-
- // function secondRoomBoss(x, y, radius = 25, isDark = false) {
- // mobs.spawn(x, y, 12, radius, isDark ? "#000" : "#fff");
- // let me = mob[mob.length - 1];
- // me.isBoss = true;
- // me.isDark = isDark;
-
- // me.stroke = "transparent";
- // me.eventHorizon = 500; // How family friendly content much do I have to reduce this
- // me.seeAtDistance2 = 5e6; // Basically just see at all times, in the context it's given
- // me.accelMag = 0.00003 * simulation.accelScale;
- // me.collisionFilter.mask = cat.player | cat.bullet;
- // me.memory = 1600;
- // me.randomPRNGMult = Math.random() * 500;
-
- // me.attackCycle = 0;
- // me.lastAttackCycle = 0;
- // Matter.Body.setDensity(me, 0.012); // extra dense, normal is 0.001 // makes effective life much larger
- // me.onDeath = function() {
- // // applying forces to player doesn't seem to work inside this method, not sure why
- // powerUps.spawn(this.position.x + 20, this.position.y, "ammo");
- // if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo");
- // if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5));
- // };
- // me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1);
- // me.do = function() {
- // // keep it slow, to stop issues from explosion knock backs
- // if (this.speed > 1) {
- // Matter.Body.setVelocity(this, {
- // x: this.velocity.x * 0.95,
- // y: this.velocity.y * 0.95
- // });
- // }
- // if (!(simulation.cycle % this.seePlayerFreq)) {
- // if (this.distanceToPlayer2() < this.seeAtDistance2) { // ignore cloak for black holes
- // this.locatePlayer();
- // if (!this.seePlayer.yes) this.seePlayer.yes = true;
- // } else if (this.seePlayer.recall) {
- // this.lostPlayer();
- // }
- // }
- // this.checkStatus();
- // if (this.seePlayer.recall) {
- // // accelerate towards the player
- // const forceMag = this.accelMag * this.mass;
- // const dx = this.seePlayer.position.x - this.position.x
- // const dy = this.seePlayer.position.y - this.position.y
- // const mag = Math.sqrt(dx * dx + dy * dy)
- // this.force.x += forceMag * dx / mag;
- // this.force.y += forceMag * dy / mag;
-
- // // eventHorizon waves in and out
- // const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008));
-
- // // draw darkness
- // ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.6)" : "rgba(225,215,255,0.6)";
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI);
- // ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.4)" : "rgba(225,215,255,0.4)";
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI);
- // ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.3)" : "rgba(225,215,255,0.3)";
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI);
- // ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.2)" : "rgba(225,215,255,0.2)";
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI);
- // ctx.fillStyle = this.isDark ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.05)";
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI);
- // // when player is inside event horizon
- // if (distance(this.position, player.position) < eventHorizon) {
- // if (this.isDark) {
- // // Standard black hole stuff
- // if (m.immuneCycle < m.cycle) {
- // if (m.energy > 0) m.energy -= 0.003;
- // if (m.energy < 0.1) m.damage(0.00015 * simulation.dmgScale);
- // }
- // const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x);
- // player.force.x -= 0.0005 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1);
- // player.force.y -= 0.0005 * Math.sin(angle) * player.mass;
- // // draw line to player
- // ctx.lineWidth = Math.min(60, this.radius * 2);
- // ctx.strokeStyle = "rgba(0,0,0,0.5)";
- // DrawTools.line([this.position, m.pos]);
- // ctx.fillStyle = "rgba(0,0,0,0.3)";
- // DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
- // } else {
- // // Lightning attacks
- // this.attackCycle++;
- // if (this.attackCycle >= 30) {
- // this.attackCycle = 0;
- // this.lastAttackCycle = simulation.cycle;
- // Matter.Body.setVelocity(player, V.add(player.velocity, { x: 0, y: -10 }));
- // if (m.immuneCycle < m.cycle) {
- // if (m.energy > 0) m.energy -= 0.03;
- // m.damage(0.005 * simulation.dmgScale);
- // }
- // }
- // DrawTools.lightning(this.position, m.pos, this.lastAttackCycle, this.randomPRNGMult);
- // ctx.fillStyle = `rgba(255,240,127,${0.12 * Math.max(15 - simulation.cycle + this.lastAttackCycle, 0)})`;
- // DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
- // }
- // }
- // }
- // }
- // };
-
- // function mobGrenade(...args) {
- // spawn.grenade(...args);
- // const pulseRadius = args[3] || Math.min(550, 250 + simulation.difficulty * 3)
- // let me = mob[mob.length - 1];
- // me.fill = "#ace";
- // me.onDeath = function() {
- // //damage player if in range
- // if (distance(player.position, this.position) < pulseRadius && m.immuneCycle < m.cycle) {
- // m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage
- // m.damage(0.02 * simulation.dmgScale);
- // }
- // simulation.drawList.push({ //add dmg to draw queue
- // x: this.position.x,
- // y: this.position.y,
- // radius: pulseRadius,
- // color: "rgba(170,204,238,0.3)",
- // time: simulation.drawTime
- // });
- // };
- // me.do = function() {
- // this.timeLimit();
- // ctx.beginPath(); //draw explosion outline
- // ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay
- // ctx.fillStyle = "rgba(170,204,238,0.1)";
- // ctx.fill();
- // };
- // }
- // // Todo: nerf ThirdRoomBoss a bit?
- // function thirdRoomBoss(x, y) {
- // mobs.spawn(x, y, 6, 60, "#000");
- // let me = mob[mob.length - 1];
- // // Fix in place
- // 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);
- // me.isBoss = true;
-
- // me.stroke = "transparent";
- // me.eventHorizon = 950;
- // me.collisionFilter.mask = cat.player | cat.bullet | cat.body;
-
- // me.memory = Infinity;
- // me.attackCycle = 0;
- // me.lastAttackCycle = 0;
- // me.spawnCycle = 0;
- // Matter.Body.setDensity(me, 0.08); //extra dense //normal is 0.001 //makes effective life much larger
- // me.onDeath = function() {
- // 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);
- // }
- // }
- // }
- // // You earned it: One more tech
- // powerUps.spawn(this.position.x, this.position.y, "tech");
- // powerUps.spawnBossPowerUp(this.position.x, this.position.y);
- // templePlayer.room3ToEndAnim = 1;
- // };
- // me.nextHealthThreshold = 0.75;
- // me.trapCycle = 0;
- // me.onDamage = function() {
- // if (this.health < this.nextHealthThreshold) {
- // this.health = this.nextHealthThreshold - 0.01
- // this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25
- // this.trapCycle = 1;
- // this.isInvulnerable = true;
- // this.damageReduction = 0;
- // }
- // };
- // me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1);
- // me.rings = [{
- // colour: "#65f",
- // radius: 300,
- // id: 0
- // }, {
- // colour: "#0f0",
- // radius: 400,
- // id: 1
- // }, {
- // colour: "#f00",
- // radius: 500,
- // id: 2
- // }];
- // me.ring = function() {
- // const rings = this.isInvulnerable ? [] : this.rings;
- // ctx.lineWidth = 10;
- // for (const ring of rings) {
- // const radius = ring.radius * (1 + 0.3 * Math.sin(simulation.cycle / 60 * (ring.id + 2)));
- // if (Math.abs(distance(player.position, this.position) - radius) < 60 && m.immuneCycle < simulation.cycle) {
- // m.damage(0.4 / radius);
- // }
- // ctx.strokeStyle = ring.colour;
- // DrawTools.arcOut(this.position.x, this.position.y, radius, 0, Math.PI * 2);
- // }
- // }
- // me.horizon = function() {
- // // eventHorizon waves in and out
- // const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008));
-
- // const charge = this.attackCycle / 90;
- // this.fill = this.isInvulnerable ? "#f00" : `rgb(${charge * 255},${charge * 255},${charge * 255})`;
- // // draw darkness
- // ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.6)`;
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI);
- // ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.4)`;
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI);
- // ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.3)`;
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI);
- // ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.2)`;
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI);
- // ctx.fillStyle = `rgba(${charge * 255},${charge * 255},${charge * 255},0.05)`;
- // DrawTools.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI);
-
- // // when player is inside event horizon
- // if (V.magnitude(V.sub(this.position, player.position)) < eventHorizon) {
- // // Standard black hole stuff
- // if (m.immuneCycle < m.cycle) {
- // if (m.energy > 0) m.energy -= 0.004;
- // if (m.energy < 0.1) m.damage(0.0002 * simulation.dmgScale);
- // }
- // const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x);
- // player.force.x -= 0.001 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1);
- // player.force.y -= 0.001 * Math.sin(angle) * player.mass;
- // // draw line to player
- // ctx.lineWidth = Math.min(60, this.radius * 2);
- // ctx.strokeStyle = "rgba(0,0,0,0.5)";
- // DrawTools.line([this.position, m.pos]);
- // ctx.fillStyle = "rgba(0,0,0,0.3)";
- // DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
- // // Lightning attacks
- // this.attackCycle++;
- // if (this.attackCycle >= 90) {
- // this.attackCycle = 0;
- // this.lastAttackCycle = simulation.cycle;
- // Matter.Body.setVelocity(player, V.add(player.velocity, { x: 0, y: -20 }));
- // if (m.immuneCycle < m.cycle) {
- // m.damage(0.015 * simulation.dmgScale);
- // }
- // }
- // const lightningCycle = simulation.cycle * 2 / 3 + this.lastAttackCycle / 3;
- // DrawTools.lightning(this.position, m.pos, lightningCycle, 1, 12);
- // DrawTools.lightning(this.position, m.pos, lightningCycle, 2, 12);
- // ctx.fillStyle = `rgba(255,240,127,${0.12 * Math.max(15 - simulation.cycle + this.lastAttackCycle, 0)})`;
- // DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
- // }
- // }
- // me.periodicSpawns = function() {
- // if (this.isInvulnerable) return;
- // this.spawnCycle++;
- // // Spawn annoying purple thing(s) that chases the player
- // if (!(this.spawnCycle % 180)) {
- // spawn.seeker(this.position.x, this.position.y, 15 * (0.7 + 0.5 * Math.random()), 7);
- // spawn.seeker(this.position.x, this.position.y, 4 * (0.7 + 0.5 * Math.random()), 7);
- // spawn.seeker(this.position.x, this.position.y, 4 * (0.7 + 0.5 * Math.random()), 7);
- // }
- // // Spawn the annoying pink (now blue) exploder that doesn't chase the player
- // if (!(this.spawnCycle % 300)) {
- // for (let i = 0; i < 3; i++) {
- // mobGrenade(1100 + 700 * i, -13030, undefined, Math.min(700, 300 + simulation.difficulty * 4), 10);
- // setVel(mob[mob.length - 1], { x: 0, y: -10 });
- // mobGrenade(1100 + 700 * i, -14370, undefined, Math.min(700, 300 + simulation.difficulty * 4), 10);
- // setVel(mob[mob.length - 1], { x: 0, y: 10 });
- // }
- // }
- // // Spawn a bunch of mobs
- // if (!(this.spawnCycle % 600)) {
- // // This is just ripped off of spawn.nodeGroup because I don't want the shield
- // const nodes = 3;
- // const radius = Math.ceil(Math.random() * 10) + 18;
- // const sideLength = Math.ceil(Math.random() * 100) + 70;
- // const stiffness = Math.random() * 0.03 + 0.005;
- // spawn.allowShields = false; //don't want shields on individual group mobs
- // const angle = 2 * Math.PI / nodes
- // for (let i = 0; i < nodes; ++i) {
- // spawn.focuser(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius);
- // }
- // spawn.constrainAllMobCombos(nodes, stiffness);
- // spawn.allowShields = true;
- // }
- // }
- // me.invulnerableTrap = function() {
- // if (this.trapCycle < 1) return;
- // this.trapCycle++;
- // // 24 is just an arbitrarily large number
- // const spawnCycles = Math.min(24, Math.max(6, 4 + Math.floor(simulation.difficulty / 3)));
- // // I have no idea how to balance at all, please help me
- // const spawnDelay = Math.floor(5 + 10 / (1 + Math.sqrt(simulation.difficulty) / 5));
- // if (this.trapCycle >= 90) {
- // const cycle = this.trapCycle - 90;
- // if (!(cycle % spawnDelay)) {
- // const radius = Math.min(500, 200 + simulation.difficulty * 3);
- // mobGrenade(600, -13050, 30, radius);
- // Matter.Body.setVelocity(mob[mob.length - 1], { x: 35, y: 0 });
- // mobGrenade(3000, -13050, 30, radius);
- // Matter.Body.setVelocity(mob[mob.length - 1], { x: -35, y: 0 });
- // mobGrenade(600, -14350, 30, radius);
- // Matter.Body.setVelocity(mob[mob.length - 1], { x: 35, y: 0 });
- // mobGrenade(3000, -14350, 30, radius);
- // Matter.Body.setVelocity(mob[mob.length - 1], { x: -35, y: 0 });
- // if (Math.floor(cycle / spawnDelay) >= spawnCycles - 1) {
- // this.trapCycle = 0;
- // this.isInvulnerable = false;
- // this.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1);
- // }
- // }
- // }
- // ctx.font = "100px Arial";
- // ctx.fillStyle = "#f00";
- // ctx.shadowBlur = 10;
- // ctx.shadowColor = "#f00";
- // ctx.textAlign = "center";
- // ctx.textBaseLine = "middle";
- // ctx.fillText("!", 900, -13050);
- // ctx.fillText("!", 900, -14350);
- // ctx.fillText("!", 1800, -13050);
- // ctx.fillText("!", 1800, -14350);
- // ctx.fillText("!", 2700, -13050);
- // ctx.fillText("!", 2700, -14350);
- // ctx.shadowBlur = 0;
- // }
- // me.do = function() {
- // this.checkStatus();
- // this.horizon();
- // this.ring();
- // this.periodicSpawns();
- // this.invulnerableTrap();
- // }
- // };
- // let oldNextLevel = level.nextLevel;
- // const oldFallHeight = simulation.fallHeight;
- // level.nextLevel = () => {
- // color.map = "#444";
- // m.death = m.oldDeath;
- // canvas.style.filter = "";
- // level.nextLevel = oldNextLevel;
- // simulation.fallHeight = oldFallHeight;
- // oldNextLevel();
- // }
- // let bounds = [];
- // let mobPositionsQueue = Array.from(Array(10), () => []);
- // m.oldDeath = m.death;
- // m.death = function() {
- // if (!tech.isImmortal) {
- // requestAnimationFrame(() => color.map = "#444");
- // m.death = m.oldDeath;
- // canvas.style.filter = "";
- // level.nextLevel = oldNextLevel;
- // simulation.fallHeight = oldFallHeight;
- // }
- // m.oldDeath();
- // }
- // let name = "⥟ᘊ5⪊Ыᳪៗⱕ␥ዘᑧ⍗";
- // addPartToMap = (len = map.length - 1) => {
- // map[len].collisionFilter.category = cat.map;
- // map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
- // Matter.Body.setStatic(map[len], true); // make static
- // Composite.add(engine.world, map[len]);
- // }
- // level.setPosToSpawn(50, -50); // normal spawn
- // // Make the level exit really far away so WIMP powerups don't show up at all
- // level.exit.x = 1e6;
- // level.exit.y = -1e6;
- // Promise.resolve().then(() => {
- // // Clear all WIMPS and their research
- // for (let i = 0; i < mob.length; i++) {
- // while (mob[i] && !mob[i].isMACHO) {
- // mob[i].replace(i);
- // }
- // }
- // for (let i = 0; i < powerUp.length; i++) {
- // while (powerUp[i] && powerUp[i].name === "research") {
- // Matter.Composite.remove(engine.world, powerUp[i]);
- // powerUp.splice(i, 1);
- // }
- // }
- // level.exit.x = 1500;
- // level.exit.y = -30;
- // });
- // spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
- // spawn.mapRect(1500, -10, 100, 20);
- // level.defaultZoom = 1800
- // simulation.setZoom(1200);
- // document.body.style.backgroundColor = "#daa69f";
- // color.map = "#600";
-
- // function box(x, y, w, h, s) {
- // spawn.mapRect(x, y, w, s);
- // spawn.mapRect(x, y, s, h);
- // spawn.mapRect(x + w - s, y, s, h);
- // spawn.mapRect(x, y + h - s, w, s);
- // }
-
- // function diamond(x, y, s) {
- // spawn.mapVertex(x, y, `0 -${s} -${s} 0 0 ${s} ${s} 0`);
- // }
-
- // // Fake level
- // bounds.push(new Rect(-200, -500, 2000, 600));
- // box(-200, -500, 2000, 600, 100);
-
- // // Actual level, Room 1
- // const firstRoomBounds = new Rect(-200, -4000, 5000, 2100);
- // bounds.push(firstRoomBounds);
-
- // box(-200, -4000, 5000, 2100, 100);
- // spawn.mapRect(-200, -2500, 1300, 100);
- // spawn.mapRect(3500, -2500, 1300, 100);
- // spawn.mapRect(-200, -4000, 1000, 1600);
- // spawn.mapRect(3800, -4000, 1000, 1600);
- // // Enter and Exit platforms
- // spawn.mapRect(0, -2010, 100, 20);
- // spawn.mapRect(4500, -2010, 100, 20);
-
- // // Altar of Room 1
- // spawn.mapRect(2100, -2200, 100, 300);
- // spawn.mapRect(2400, -2200, 100, 300);
- // spawn.mapRect(2070, -2200, 460, 20);
-
- // spawn.debris(1700, -2100, 300, 10);
- // spawn.debris(2500, -2100, 300, 10);
-
- // // Actual level, Room 2
- // const secondRoomBounds = new Rect(-1500, -10500, 3000, 3600);
- // bounds.push(secondRoomBounds);
-
- // box(-1500, -10500, 3000, 3600, 100);
- // spawn.mapRect(-2000, -8500, 1600, 1600);
- // spawn.mapRect(400, -8500, 1600, 1600);
- // // Enter and Exit platforms
- // spawn.mapRect(-50, -7010, 100, 20);
- // spawn.mapRect(-50, -10010, 100, 20);
-
- // // Hazard traversing
- // spawn.mapRect(-300, -7320, 800, 20);
- // spawn.mapRect(175, -7600, 325, 20);
- // spawn.mapRect(200, -7775, 300, 20);
- // spawn.mapRect(-500, -7600, 325, 20);
- // spawn.mapRect(-500, -7775, 300, 20);
- // spawn.mapRect(-500, -7950, 800, 20);
- // spawn.mapRect(-300, -8100, 800, 20);
- // spawn.mapRect(-500, -8250, 800, 20);
- // for (let i = 0; i < 2; i++) spawn.mapRect(-250, -8400 + 150 * i, 500, 60);
- // const room2SlimePit = level.hazard(-400, -8410, 800, 1090);
- // room2SlimePit.logic = function() {
- // if (this.height > 0 && Matter.Query.region([player], this).length) {
- // if (m.immuneCycle < m.cycle) {
- // // Trolled
- // const hasCPT = tech.isRewindAvoidDeath;
- // tech.isRewindAvoidDeath = false;
- // const DRAIN = 0.002 * (tech.isRadioactiveResistance ? 0.25 : 1) + m.fieldRegen;
- // if (m.energy > DRAIN && !tech.isEnergyHealth) {
- // m.energy -= DRAIN;
- // }
- // m.damage(0.00015 * (tech.isRadioactiveResistance ? 0.25 : 1));
- // if (tech.isEnergyHealth) {
- // const previousEnergy = m.energy;
- // m.regenEnergy();
- // const energyRegenerated = m.energy - previousEnergy;
- // if (energyRegenerated > 0) {
- // m.energy = previousEnergy;
- // m.damage(energyRegenerated);
- // }
- // }
- // tech.isRewindAvoidDeath = hasCPT;
- // }
- // player.force.y -= 0.3 * player.mass * simulation.g;
- // setVel(player, Vector.sub(player.velocity, { x: 0, y: player.velocity.y * 0.02 }));
- // }
- // // Float power ups
- // powerUpCollide = Matter.Query.region(powerUp, this)
- // for (let i = 0, len = powerUpCollide.length; i < len; i++) {
- // const diameter = 2 * powerUpCollide[i].size
- // const buoyancy = 1 - 0.2 * Math.max(0, Math.min(diameter, this.min.y - powerUpCollide[i].position.y + powerUpCollide[i].size)) / diameter
- // powerUpCollide[i].force.y -= buoyancy * 1.14 * powerUpCollide[i].mass * simulation.g;
- // setVel(powerUpCollide[i], { x: powerUpCollide[i].velocity.x, y: 0.96 * powerUpCollide[i].velocity.y });
- // }
- // }
- // room2SlimePit.draw = function() {
- // if (this.isOn) {
- // ctx.fillStyle = "hsla(160, 100%, 35%, 0.75)";
- // ctx.fillRect(this.min.x, this.min.y, this.width, this.height);
- // }
- // }
- // // Room 2 spawning bounds
- // const secondRoomSpawnBounds = new Rect(-1500, -10500, 3000, 2000);
- // spawn.mapRect(-700, -8700, 150, 20);
- // spawn.mapRect(550, -8700, 150, 20);
- // spawn.mapRect(-400, -8900, 800, 20);
- // diamond(-600, -9800, 30);
- // diamond(0, -9800, 30);
- // diamond(600, -9800, 30);
-
- // spawn.mapRect(-1000, -10000, 2000, 30);
-
- // // Actual level, Room 3 (Final Boss?)
- // const thirdRoomBounds = new Rect(-200, -14500, 4000, 1600);
- // bounds.push(thirdRoomBounds);
- // box(-200, -14500, 4000, 1600, 100);
- // spawn.mapRect(-200, -14500, 800, 1100);
- // spawn.mapRect(3000, -14500, 800, 1100);
- // // Enter and Exit platforms
- // spawn.mapRect(0, -13110, 100, 20);
- // spawn.mapRect(-200, -13100, 800, 200);
- // spawn.mapRect(3500, -13110, 100, 20);
- // spawn.mapRect(3000, -13100, 800, 200);
- // for (let i = 0; i < 3; i++) spawn.bodyRect(500, -13400 + i * 100, 100, 100);
-
- // for (let i = 0; i < 3; i++) {
- // diamond(1100 + 700 * i, -13000, 30, 30);
- // diamond(1100 + 700 * i, -14400, 30, 30);
- // }
-
- // const Objects = {
- // altar: {
- // get isHeal() {
- // return simulation.cycle % 600 >= 300;
- // },
- // pos: {
- // x: 2300,
- // y: -2200
- // },
- // get isActive() {
- // const roughPlayerCentre = V.add(m.pos, { x: 0, y: 40 });
- // return distance(roughPlayerCentre, this.pos) < 240 &&
- // (Math.abs(angle(roughPlayerCentre, this.pos) - Math.PI / 2) < 1);
- // },
- // logic() {
- // if (!this.isActive) return;
- // if (this.isHeal) {
- // m.energy += 0.005;
- // } else {
- // m.energy = Math.max(m.energy - 0.007 - m.fieldRegen, 0);
- // if (m.energy <= 0.01 && m.immuneCycle < m.cycle) m.damage(0.002);
- // }
- // },
- // drawTop() {
- // if (!isInBound(firstRoomBounds)) return;
- // const colour = this.isHeal ? m.fieldMeterColor : "#f00";
- // DrawTools.flame([2300, -2200, 26, 4, colour], 7);
- // ctx.fillStyle = colour;
- // ctx.fillRect(2200, -2200, 200, 200);
- // },
- // drawBottom() {
- // ctx.fillStyle = this.isHeal ? "#fff5" : "#0005";
- // for (const radius of [230, 180, 130, 80, 30]) {
- // DrawTools.arc(2300, -2200, radius, 0, Math.PI, true);
- // }
- // }
- // },
- // room2Initiator: {
- // pos: {
- // x: 0,
- // y: -9050
- // },
- // get distance() {
- // return distance(player.position, this.pos);
- // },
- // range: 120,
- // rings: [{
- // colour: [102, 85, 255],
- // radius: 200
- // }, {
- // colour: [0, 255, 0],
- // radius: 300
- // }, {
- // colour: [255, 0, 0],
- // radius: 400
- // }],
- // get ringNumber() {
- // return this.rings.length;
- // },
- // get cap() {
- // return (this.ringNumber + 1) * 90 + 240;
- // },
- // get capped() {
- // return templePlayer.room2.spawnInitiatorCycles > this.cap;
- // },
- // logic() {
- // if (this.distance < this.range) {
- // templePlayer.room2.spawnInitiatorCycles++;
- // }
- // },
- // draw() {
- // Promise.resolve().then(() => {
- // const cycle = templePlayer.room2.spawnInitiatorCycles;
- // if (!this.capped && this.distance < 400) {
- // ctx.fillStyle = `rgba(0, 0, 0, ${Math.min(1, (400 - this.distance) / (400 - this.range)) * 0.9})`;
- // ctx.fillRect(0, 0, canvas.width, canvas.height);
- // }
- // ctx.save();
- // simulation.camera();
- // if (this.distance < this.range && !this.capped) {
- // DrawTools.lightning(V.sub(this.pos, { x: 300, y: 300 }), V.add(this.pos, { x: 300, y: 300 }), simulation.cycle - 5);
- // DrawTools.lightning(V.add(this.pos, { x: -300, y: 300 }), V.add(this.pos, { x: 300, y: -300 }), simulation.cycle - 5);
- // }
- // if (!this.capped && cycle >= this.cap - 200) {
- // const multCoeff = (cycle - this.cap + 200) * 0.4
- // ctx.translate((Math.random() - 0.5) * multCoeff, (Math.random() - 0.5) * multCoeff);
- // }
- // ctx.shadowBlur = 20;
- // ctx.lineWidth = 12;
- // ctx.strokeStyle = (templePlayer.room2.cycles % 60 < 30) ? "#fff" : "#000";
- // ctx.shadowColor = (templePlayer.room2.cycles % 60 < 30) ? "#fff" : "#000";
- // DrawTools.arcOut(this.pos.x, this.pos.y, 100, 0, Math.PI * 2);
- // if (templePlayer.room2.cycles <= 100) {
- // for (let i = 0; i < this.ringNumber; i++) {
- // if (cycle < i * 90 + 90) break;
- // const ring = this.rings[i];
- // ctx.shadowColor = `rgb(${ring.colour.join(",")})`;
- // const opacity = this.capped ? 1 - 0.01 * templePlayer.room2.cycles : (cycle / 180 - i / 2 - 0.5);
- // ctx.strokeStyle = `rgba(${ring.colour.join(",")}, ${Math.min(opacity, 1)})`;
- // const radius = (this.capped ? 1 + 0.07 * templePlayer.room2.cycles : Math.sin(Math.min(cycle - i * 90 - 90, 45) / 90 * Math.PI)) * ring.radius;
- // DrawTools.arcOut(this.pos.x, this.pos.y, radius, 0, Math.PI * 2);
- // }
- // }
- // ctx.restore();
- // });
- // }
- // },
- // room2Lightning: {
- // one: [{ x: -1400, y: -10400 }, { x: 1400, y: -8500 }],
- // two: [{ x: -1400, y: -8500 }, { x: 1400, y: -10400 }],
- // get isHeal() {
- // return simulation.cycle % 360 < 180;
- // },
- // get oneEq() {
- // return Equation.fromPoints(this.one[0], this.one[1]);
- // },
- // get twoEq() {
- // return Equation.fromPoints(this.two[0], this.two[1]);
- // },
- // logic() {
- // if (!isInBound(secondRoomSpawnBounds) || !templePlayer.room2.cycles) return;
-
- // const playerbounds = Rect.fromBounds(player.bounds.min, player.bounds.max);
- // if (playerbounds.hasLine(this.oneEq) || playerbounds.hasLine(this.twoEq)) {
- // if (this.isHeal) {
- // m.energy += 0.003;
- // } else if (m.immuneCycle < m.cycle) {
- // m.energy -= 0.003;
- // }
- // }
- // },
- // draw() {
- // if (!isInBound(secondRoomBounds) || !templePlayer.room2.cycles) return;
-
- // const colour = this.isHeal ? undefined : [0, 0, 0];
- // DrawTools.lightning(...this.one, Math.floor(simulation.cycle / 15) * 15, 1, 9, colour);
- // DrawTools.lightning(...this.two, Math.floor(simulation.cycle / 15) * 15, 2, 9, colour);
- // }
- // },
- // room2GeneratedPath: {
- // rects: (function() {
- // const rects = [];
- // for (let i = 0; i < 4; i++) {
- // rects.push(new Rect(-1405 + (i & 1) * 200, -9700 + i * 300, 205, 30));
- // rects.push(new Rect(1200 - (i & 1) * 200, -9700 + i * 300, 205, 30));
- // }
- // return rects;
- // })(),
- // logic() {
- // if (templePlayer.room2.readyPathCycle && simulation.cycle - templePlayer.room2.readyPathCycle === 180) {
- // for (const r of this.rects) {
- // r.addToMap();
- // addPartToMap();
- // simulation.draw.setPaths();
- // }
- // }
- // },
- // draw() {
- // if (templePlayer.room2.readyPathCycle && simulation.cycle - templePlayer.room2.readyPathCycle < 180) {
- // ctx.fillStyle = "#fe79";
- // for (const r of this.rects) {
- // ctx.fillRect(r.pos.x, r.pos.y, r.width, r.height);
- // }
- // } else if (simulation.cycle - templePlayer.room2.readyPathCycle < 195) {
- // for (const r of this.rects) {
- // DrawTools.lightning(Objects.room2Initiator.pos, r.midPos, templePlayer.room2.readyPathCycle + 180);
- // }
- // }
- // }
- // },
- // room3Rotors: {
- // rotor1: (function() {
- // const rotor = level.spinner(900, -13700, 200, 30);
- // rotor.rotate = function() {
- // Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity + 0.01) * 0.9)
- // }
- // return rotor;
- // })(),
- // rotor2: (function() {
- // const rotor = level.spinner(2700, -13700, 200, 30);
- // rotor.rotate = function() {
- // Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity - 0.01) * 0.9)
- // }
- // return rotor;
- // })(),
- // logic() {
- // this.rotor1.rotate();
- // this.rotor2.rotate();
- // }
- // },
- // room3SlimePits: {
- // pit1: level.hazard(-100, -13400, 0, 0, 0.004),
- // pit2: level.hazard(3700, -13400, 0, 0, 0.004),
- // logic() {
- // if (templePlayer.room2ToRoom3Anim >= 1320 && templePlayer.room2ToRoom3Anim <= 1570) {
- // this.pit1.height = this.pit2.height = 300;
- // this.pit1.max.y = this.pit2.max.y = -13100;
- // this.pit1.width = this.pit2.width = templePlayer.room2ToRoom3Anim * 2 - 2640;
- // this.pit1.max.x = this.pit1.min.x + this.pit1.width;
- // this.pit2.min.x = this.pit2.max.x - this.pit2.width;
- // }
- // if (templePlayer.room3ToEndAnim) {
- // this.pit1.height = this.pit1.width = 0;
- // this.pit2.height = this.pit2.width = 0;
- // }
- // },
- // draw() {
- // this.pit1.query();
- // this.pit2.query();
- // }
- // }
- // };
- // let templePlayer = {
- // room1: {
- // cycles: 300
- // },
- // room2: {
- // spawnInitiatorCycles: 0,
- // cycles: 0,
- // readyPathCycle: 0
- // },
- // stage: 1,
- // startAnim: 0,
- // room1ToRoom2Anim: 0,
- // room2ToRoom3Anim: 0,
- // room3ToEndAnim: 0,
- // initialTrapY: 0,
- // clearedCycle: 0,
- // drawExit: true
- // };
-
- // const RoomTransitionHandler = {
- // room0() {
- // if (templePlayer.startAnim <= 0) return;
- // templePlayer.startAnim++;
- // if (templePlayer.startAnim == 120) {
- // makeLore("Not so fast.");
- // }
- // if (templePlayer.startAnim < 360) {
- // trapPlayer(1000, templePlayer.initialTrapY);
- // } else {
- // level.exit.x = 4500;
- // level.exit.y = -2030;
- // relocateTo(50, -2050);
- // simulation.fallHeight = -1000;
- // simulation.setZoom(1800);
- // templePlayer.startAnim = -1;
- // for (let i = 0; i < tech.wimpCount + tech.wimpExperiment; i++) {
- // addWIMP();
- // }
- // templePlayer.drawExit = false;
- // }
- // },
- // room1() {
- // if (templePlayer.room1ToRoom2Anim <= 0) return;
- // if (templePlayer.room1ToRoom2Anim === 1) {
- // level.exit.x = -50;
- // level.exit.y = -10030;
- // makeLore("Pathetic.");
- // }
- // if (templePlayer.room1ToRoom2Anim === 241) {
- // makeLore("You will never succeed.");
- // }
- // if (templePlayer.room1ToRoom2Anim >= 480 && templePlayer.room1ToRoom2Anim <= 960) {
- // const factor = 200 - 200 * Math.cos((templePlayer.room1ToRoom2Anim / 120) * Math.PI);
- // ctx.translate(factor, factor);
- // Promise.resolve().then(() => {
- // ctx.save();
- // ctx.globalCompositeOperation = "color-burn";
- // ctx.fillStyle = DrawTools.randomColours;
- // DrawTools.updateRandomColours(5);
- // ctx.fillRect(0, 0, canvas.width, canvas.height);
- // ctx.restore();
- // });
- // }
- // if (templePlayer.room1ToRoom2Anim === 960) {
- // makeLore("You are trying too hard.");
- // relocateTo(0, -7050);
- // simulation.fallHeight = -6000;
- // templePlayer.stage = 2;
- // }
- // if (templePlayer.room1ToRoom2Anim === 1200) {
- // makeLore("I have mastered the understandings of the universe.");
- // }
- // if (templePlayer.room1ToRoom2Anim === 1260) {
- // // Congrats, you discovered the actual words by looking at the source code. Are you happy now?
- // const x = (
- // ["a speck of dust", "an insignificant hindrance", "a tiny obstacle"]
- // )[Math.floor(Math.random() * 3)].split("");
- // for (let i = 0; i < x.length / 1.6; i++) {
- // const randomIndex = Math.floor(Math.random() * x.length);
- // if (x[randomIndex] !== " ") {
- // x[randomIndex] = String.fromCharCode(Math.floor(Math.random() * 50) + 192);
- // }
- // };
- // makeLore(`You are no more than ${x.join("")} to me.`);
- // relocateWIMPs(0, -10030);
- // }
- // templePlayer.room1ToRoom2Anim++;
- // },
- // room2() {
- // if (templePlayer.room2ToRoom3Anim <= 0) return;
- // if (templePlayer.room2ToRoom3Anim === 1) {
- // level.exit.x = 3500;
- // level.exit.y = -13130;
- // makeLore("Do not try me.");
- // }
- // if (templePlayer.room2ToRoom3Anim === 240) {
- // makeLore("I have absolute power over you.");
- // canvas.style.filter = "hue-rotate(90deg)";
- // }
- // if (templePlayer.room2ToRoom3Anim === 480) {
- // makeLore("You will not succeed...");
- // canvas.style.filter = "invert(0.2)";
- // }
- // if (templePlayer.room2ToRoom3Anim === 600) {
- // makeLore("
succeed...
");
- // canvas.style.filter = "invert(0.4)";
- // }
- // if (templePlayer.room2ToRoom3Anim > 660 && templePlayer.room2ToRoom3Anim <= 840) {
- // canvas.style.filter = `sepia(${(templePlayer.room2ToRoom3Anim - 660) / 180}) invert(${0.5 + (templePlayer.room2ToRoom3Anim - 660) / 360})`;
- // }
- // if (templePlayer.room2ToRoom3Anim === 960) {
- // makeLore("Do not interfere with me.");
- // templePlayer.stage = 3;
- // relocateTo(50, -13150);
- // simulation.fallHeight = -10000;
- // simulation.zoomTransition(1800);
- // templePlayer.startAnim = -1;
- // // Might be a bit harsh to the player if the WIMPs are involved in the third level
- // for (let i = 0; i < mob.length; i++) {
- // while (mob[i] && !mob[i].isWIMP) {
- // mob[i].replace(i);
- // }
- // }
- // }
- // if (templePlayer.room2ToRoom3Anim > 960 && templePlayer.room2ToRoom3Anim <= 1140) {
- // canvas.style.filter = `sepia(${(1140 - templePlayer.room2ToRoom3Anim) / 180}) invert(${(1140 - templePlayer.room2ToRoom3Anim) / 180})`;
- // }
- // templePlayer.room2ToRoom3Anim++;
- // },
- // room3() {
- // if (templePlayer.room3ToEndAnim <= 0) return;
- // if (templePlayer.room3ToEndAnim === 1) {
- // makeLore("No.");
- // }
- // if (templePlayer.room3ToEndAnim === 120) {
- // makeLore("This cannot be.");
- // }
- // if (templePlayer.room3ToEndAnim === 240) {
- // makeLore("Has my power failed me?");
- // }
- // if (templePlayer.room3ToEndAnim === 360) {
- // makeLore("Was it worth it, destroying this place?");
- // }
- // if (templePlayer.room3ToEndAnim === 600) {
- // makeLore("No one is greater than me.");
- // }
- // const text = "noone-";
- // for (let i = 0; i < 12; i++) {
- // if (templePlayer.room3ToEndAnim === 720 + i * 20) {
- // name = name.slice(0, -1);
- // simulation.makeTextLog(`${name}: ${text[i % 6]}`);
- // canvas.style.filter = `brightness(${1 - i / 22})`;
- // }
- // }
- // if (templePlayer.room3ToEndAnim === 1060) {
- // templePlayer.drawExit = true;
- // for (let i = 0; i < 5 * tech.wimpCount; i++) {
- // powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false);
- // }
- // canvas.style.filter = "";
- // }
- // templePlayer.room3ToEndAnim++;
- // },
- // end() {
- // if (!templePlayer.clearedCycle) return;
- // Promise.resolve().then(() => {
- // ctx.save();
- // ctx.setTransform(1, 0, 0, 1, 0, 0);
- // ctx.fillStyle = `rgba(0, 0, 0, ${(simulation.cycle - templePlayer.clearedCycle) / 300})`;
- // ctx.fillRect(0, 0, canvas.width, canvas.height);
- // ctx.restore();
- // });
- // if (simulation.cycle - templePlayer.clearedCycle > 420) level.nextLevel();
- // }
- // };
- // const LogicHandler = {
- // bounds() {
- // let isInBounds = false;
- // for (const b of bounds) {
- // if (isInBound(b)) {
- // isInBounds = true;
- // break;
- // }
- // }
- // if (!isInBounds) {
- // m.damage(0.1 * simulation.difficultyMode);
- // trapPlayer(level.enter.x, level.enter.y);
- // simulation.makeTextLog("" + name + ": You thought I could let you get away with that?");
- // }
- // },
- // room0() {
- // // Block the player from entering the first seemingly innocuous exit
- // if ((m.pos.x > 1000) && templePlayer.startAnim === 0) {
- // spawn.mapRect(1200, -500, 100, 600);
- // templePlayer.initialTrapY = Math.min(player.position.y, -75);
- // trapPlayer(1000, templePlayer.initialTrapY);
-
- // addPartToMap();
- // simulation.draw.setPaths();
- // templePlayer.startAnim = 1;
- // }
- // },
- // room1() {
- // WaveHandler.room1();
- // Objects.altar.logic();
- // },
- // room2() {
- // room2SlimePit.logic();
- // Objects.room2Initiator.logic();
- // Objects.room2Lightning.logic();
- // Objects.room2GeneratedPath.logic();
- // WaveHandler.room2();
- // },
- // room3() {
- // Objects.room3Rotors.logic();
- // Objects.room3SlimePits.logic();
- // WaveHandler.room3();
- // },
- // exit() {
- // if (!templePlayer.drawExit) return;
- // if (player.position.x > level.exit.x &&
- // player.position.x < level.exit.x + 100 &&
- // player.position.y > level.exit.y - 150 &&
- // player.position.y < level.exit.y - 40 &&
- // player.velocity.y < 0.1 &&
- // level.exitCount + (input.down ? 8 : 2) > 100) {
- // if (templePlayer.stage === 1) {
- // templePlayer.drawExit = false;
- // templePlayer.room1ToRoom2Anim = 1;
- // } else if (templePlayer.stage === 2) {
- // templePlayer.drawExit = false;
- // templePlayer.room2ToRoom3Anim = 1;
- // } else {
- // level.exitCount = 99 - (input.down ? 8 : 2);
- // if (!templePlayer.clearedCycle) templePlayer.clearedCycle = simulation.cycle;
- // }
- // }
- // }
- // };
- // const DrawHandler = {
- // // Bottom layer
- // base() {
- // // Draw base red background
- // ctx.save();
- // ctx.setTransform(1, 0, 0, 1, 0, 0);
- // ctx.fillStyle = color.map;
- // ctx.fillRect(0, 0, canvas.width, canvas.height);
- // ctx.restore();
-
- // // Draw the normal bg on the bounds
- // ctx.fillStyle = "#eab6af";
- // for (const b of bounds) {
- // if (isInBound(b)) ctx.fillRect(b.pos.x + 2, b.pos.y + 2, b.width - 4, b.height - 4);
- // }
- // },
- // entrance() {
- // ctx.beginPath();
- // ctx.moveTo(level.enter.x, level.enter.y + 30);
- // ctx.lineTo(level.enter.x, level.enter.y - 80);
- // ctx.bezierCurveTo(level.enter.x, level.enter.y - 170, level.enter.x + 100, level.enter.y - 170, level.enter.x + 100, level.enter.y - 80);
- // ctx.lineTo(level.enter.x + 100, level.enter.y + 30);
- // ctx.lineTo(level.enter.x, level.enter.y + 30);
- // ctx.fillStyle = "#fca";
- // ctx.fill();
- // },
- // room1() {
- // if (!isInBound(firstRoomBounds)) return;
-
- // // Draw Cross
- // ctx.fillStyle = "#fed";
- // ctx.fillRect(2200, -3300, 200, 800);
- // ctx.fillRect(2000, -3100, 600, 200);
-
- // // Final boss-like spawn fire thing. Was it necessary? No!
- // const spawnFlameAngle = Math.min(Math.min(templePlayer.room1.cycles, 2520) % 600, 120) * Math.PI / 30 + Math.PI / 2;
- // DrawTools.flame([2300, -3000, 26, 4, "#f60", spawnFlameAngle], 7);
-
- // Objects.altar.drawBottom();
- // },
- // room2() {
- // if (!isInBound(secondRoomBounds)) return;
-
- // if (templePlayer.room2.cycles) {
- // ctx.fillStyle = "#0006";
- // ctx.fillRect(secondRoomBounds.pos.x + 2, secondRoomBounds.pos.y + 2, secondRoomBounds.width - 4, secondRoomBounds.height - 4);
- // }
- // room2SlimePit.draw();
- // },
- // room3() {
- // if (!isInBound(thirdRoomBounds)) return;
- // ctx.fillStyle = "#0006";
- // ctx.fillRect(thirdRoomBounds.pos.x + 2, thirdRoomBounds.pos.y + 2, thirdRoomBounds.width - 4, thirdRoomBounds.height - 4);
- // Objects.room3SlimePits.draw();
- // },
- // // Top layer
- // mobTrails() {
- // if (simulation.cycle % 4 === 0) {
- // let newMobPositions = [];
- // for (const i of mob) {
- // if (!(i.isMACHO || i.isWIMP)) newMobPositions.push({ x: i.position.x, y: i.position.y });
- // }
- // mobPositionsQueue.shift();
- // mobPositionsQueue.push(newMobPositions);
- // }
- // // Draw "holy" trails for mobs for no particular reason at all
- // ctx.strokeStyle = "#bae";
- // ctx.lineWidth = 3;
- // for (let i = 0; i < 10; i++) {
- // const p = mobPositionsQueue[i];
- // for (const m of p) {
- // DrawTools.holy(m.x, m.y, i / 2 + 10);
- // }
- // }
- // ctx.shadowBlur = 0;
- // },
- // waveTimer() {
- // const roomConditions = [
- // isInBound(firstRoomBounds) && templePlayer.room1.cycles < 2400,
- // isInBound(secondRoomBounds) && templePlayer.room2.cycles > 0 && templePlayer.room2.cycles < 2160,
- // isInBound(thirdRoomBounds) && templePlayer.room2ToRoom3Anim < 1320
- // ];
- // Promise.resolve(roomConditions).then(roomConditions => {
- // // First Room
- // if (roomConditions[0]) {
- // ctx.save();
- // ctx.setTransform(1, 0, 0, 1, 0, 0);
- // ctx.fillStyle = "#0004";
- // ctx.fillRect(canvas.width2 - 288, 50, 576, 20);
- // ctx.fillStyle = "#0cf";
- // ctx.fillRect(canvas.width2 - 288, 50, 0.8 * (600 - templePlayer.room1.cycles % 600), 20);
- // ctx.restore();
- // }
- // // Second Room
- // if (roomConditions[1]) {
- // ctx.save();
- // ctx.setTransform(1, 0, 0, 1, 0, 0);
- // ctx.fillStyle = "#0004";
- // ctx.fillRect(canvas.width2 - 288, 50, 576, 20);
- // ctx.fillStyle = (Math.ceil(templePlayer.room2.cycles / 720) & 1) ? "#000" : "#e1d7ff";
- // ctx.fillRect(canvas.width2 - 288, 50, 0.8 * (720 - templePlayer.room2.cycles % 720), 20);
- // ctx.restore();
- // }
- // // Third Room
- // if (roomConditions[2]) {
- // ctx.save();
- // ctx.setTransform(1, 0, 0, 1, 0, 0);
- // ctx.fillStyle = "#0004";
- // ctx.fillRect(canvas.width2 - 288, 50, 576, 20);
- // ctx.fillStyle = "#000";
- // ctx.fillRect(canvas.width2 - 288, 50, 1.6 * (1320 - templePlayer.room2ToRoom3Anim), 20);
- // ctx.restore();
- // }
- // });
- // },
- // room2Top() {
- // if (!isInBound(secondRoomBounds)) return;
- // Objects.room2Lightning.draw();
- // Objects.room2GeneratedPath.draw();
- // Objects.room2Initiator.draw();
- // }
- // };
- // const WaveHandler = {
- // room1() {
- // if (!isInBound(firstRoomBounds)) return;
- // if (templePlayer.room1.cycles === 0) powerUps.spawnStartingPowerUps(0, -2050);
- // templePlayer.room1.cycles++;
- // if (templePlayer.room1.cycles === 2400) {
- // spawn.secondaryBossChance(2300, -2800);
- // powerUps.addResearchToLevel();
- // }
- // if (templePlayer.room1.cycles % 600 === 0 && templePlayer.room1.cycles <= 2400) {
- // const spawnAmt = Math.min(3 + Math.pow(simulation.difficulty / 1.7, 0.6), 10) + Math.floor(templePlayer.room1.cycles / 720);
- // for (let i = 0; i < spawnAmt; i++) {
- // if (Math.random() < 0.5 + 0.07 * simulation.difficulty) {
- // spawn.randomMob(800 + Math.random() * 3e3, -2400 - Math.random() * 600, Infinity);
- // }
- // }
- // spawn.randomMob(800 + Math.random() * 3e3, -2400 - Math.random() * 600, Infinity);
- // }
- // if (templePlayer.room1.cycles === 2520) {
- // templePlayer.drawExit = true;
- // }
- // },
- // room2() {
- // if (!isInBound(secondRoomBounds)) return;
- // if (templePlayer.room2.spawnInitiatorCycles > Objects.room2Initiator.cap) {
- // if (templePlayer.room2.cycles % 720 === 0 && templePlayer.room2.cycles <= 2160) {
- // const isOdd = Math.floor(templePlayer.room2.cycles / 720) & 1;
- // secondRoomBoss(-600, -9800, 25, isOdd);
- // secondRoomBoss(600, -9800, 25, isOdd);
- // secondRoomBoss(0, -9800, 25, !isOdd);
- // }
- // templePlayer.room2.cycles++;
- // if (templePlayer.room2.cycles === 2400) {
- // templePlayer.drawExit = true;
- // templePlayer.room2.readyPathCycle = simulation.cycle;
- // }
- // }
- // },
- // room3() {
- // if (templePlayer.room2ToRoom3Anim === 1320) {
- // thirdRoomBoss(1800, -13700);
- // for (let i = 0; i < 3; i++) {
- // powerUps.spawn(m.spawnPos.x, m.spawnPos.y, "heal");
- // }
- // }
- // }
- // };
- // const DrawTools = {
- // get randomColours() {
- // return `rgb(${this._randomColours.join(",")})`
- // },
- // _randomColours: [Math.random() * 255, Math.random() * 255, Math.random() * 255],
- // updateRandomColours(x = 0.8) {
- // for (let i = 0; i < this._randomColours.length; i++) {
- // this._randomColours[i] = Math.max(Math.min(this._randomColours[i] + (this.randFact() * x * 2) - x, 255), 0);
- // }
- // },
- // randFact() {
- // return Math.random() * 0.8 + Math.sin(Date.now() / 300) * 0.2;
- // },
-
- // line(vecs) {
- // ctx.beginPath();
- // ctx.moveTo(vecs[0].x, vecs[0].y);
- // for (const v of vecs.slice(1)) ctx.lineTo(v.x, v.y);
- // ctx.stroke();
- // },
- // arc(...x) {
- // ctx.beginPath();
- // ctx.arc(...x);
- // ctx.fill();
- // },
- // arcOut(...x) {
- // ctx.beginPath();
- // ctx.arc(...x);
- // ctx.stroke();
- // },
- // flame(props, repeat) {
- // for (let i = 0; i < repeat; i++) this.singleFlame(...props);
- // },
- // singleFlame(x, y, size = 10, repeat = 3, color = "#f00", angle = Math.PI / 2) {
- // ctx.strokeStyle = color;
- // ctx.lineWidth = 3;
- // const path = [{ x, y }];
- // for (let i = 0; i < repeat; i++) {
- // const randAng = (Math.random() - 0.5) * 2 + angle;
- // const randLen = 2 * size + Math.random() * size;
-
- // x += Math.cos(randAng) * randLen;
- // y -= Math.sin(randAng) * randLen;
- // path.push({ x, y })
- // }
- // DrawTools.line(path);
- // },
- // lightning(from, to, cycle, randomPRNGMult = 1, width = 8, color = [255, 240, 127]) {
- // const diff = simulation.cycle - cycle;
- // if (diff >= 15) return;
- // ctx.strokeStyle = `rgba(${color.join(",")},${(1 - diff / 15) * 255})`;
- // ctx.lineWidth = width * (1 - diff / 15);
- // ctx.shadowColor = `rgb(${color.join(",")})`;
- // ctx.shadowBlur = 20;
- // const path = [{ x: from.x, y: from.y }];
- // let vector = { x: from.x, y: from.y };
- // let distanceLeft = V.magnitude(V.sub(from, to));
- // const d = distanceLeft > 800 ? distanceLeft / 40 : 20;
- // const normalised = V.normalise(V.sub(to, from));
- // while (1) {
- // const randOffset = rotateVector({ y: RNG(Math.floor(cycle * randomPRNGMult + distanceLeft)) * 2 * d - d, x: 0 }, normalised);
- // const randLen = RNG(Math.floor(cycle * (randomPRNGMult + 1) + distanceLeft)) * d + d;
- // distanceLeft -= randLen;
- // if (distanceLeft <= 0) {
- // path.push({ x: to.x, y: to.y });
- // break;
- // }
- // vector = V.add(vector, V.mult(normalised, randLen));
- // path.push({ x: vector.x + randOffset.x, y: vector.y + randOffset.y });
- // }
- // DrawTools.line(path);
- // ctx.shadowBlur = 0;
- // },
- // holy(x, y, size = 12) {
- // this.line([{ x, y: y - size }, { x: x - size, y },
- // { x, y: y + size }, { x: x + size, y }, { x, y: y - size }
- // ]);
- // }
- // };
-
- // function RNG(x) {
- // x += Math.seed;
- // let start = Math.pow(x % 97, 4.3) * 232344573;
- // const a = 15485863;
- // const b = 521791;
- // start = (start * a) % b;
- // for (let i = 0; i < (x * x) % 90 + 90; i++) {
- // start = (start * a) % b;
- // }
- // return start / b;
- // }
-
- // function rotateVector(v, ang) {
- // const c = typeof ang === "number" ? { x: Math.cos(ang), y: Math.sin(ang) } : V.normalise(ang);
- // return { x: v.x * c.x - v.y * c.y, y: v.y * c.x + v.x * c.y };
- // }
-
- // function trapPlayer(x, y) {
- // setPosAndFreeze(player, { x, y });
- // const bLen = bullet.length;
- // for (let i = 0; i < bLen; i++) {
- // if (bullet[i].botType) setPosAndFreeze(bullet[i], V.add(player.position, { x: 100 * (RNG(i) - 0.5), y: 100 * (RNG(i + bLen) - 0.5) }));
- // }
- // }
-
- // function relocateTo(x, y) {
- // level.setPosToSpawn(x, y);
- // trapPlayer(x, y);
- // for (let i = 0; i < mob.length; i++) {
- // if (mob[i].isMACHO) {
- // setPos(mob[i], { x, y });
- // break;
- // }
- // }
- // m.resetHistory();
- // }
- // const distance = (a, b) => V.magnitude(V.sub(a, b));
- // const angle = (a, b) => Math.atan2(b.y - a.y, a.x - b.x);
- // const setPos = (a, b) => Matter.Body.setPosition(a, b);
- // const setVel = (a, b) => Matter.Body.setVelocity(a, b);
- // const freeze = a => setVel(a, { x: 0, y: 0 });
- // const setPosAndFreeze = (a, b) => {
- // setPos(a, b);
- // freeze(a);
- // };
- // const makeLore = x => simulation.makeTextLog(`${name}:
${x}
`);
- // // TODO: Remove this before merging
- // window.TempleHelper = {
- // skipInitial() {
- // templePlayer.startAnim = 360;
- // },
- // skipWaves() {
- // this.skipInitial();
- // templePlayer.room1.cycles = 2500;
- // },
- // skipToRoom2() {
- // this.skipInitial();
- // templePlayer.room1ToRoom2Anim = 1;
- // },
- // skipBossWaves() {
- // this.skipToRoom2();
- // templePlayer.room2.spawnInitiatorCycles = Objects.room2Initiator.cap;
- // templePlayer.room2.cycles = 2200;
- // setTimeout(() => trapPlayer(0, -9100), 500);
- // },
- // skipToRoom3() {
- // this.skipToRoom2();
- // requestAnimationFrame(() => templePlayer.room2ToRoom3Anim = 1);
- // },
- // spawnSecondRoomBoss(x, y) {
- // secondRoomBoss(x, y);
- // }
- // };
- // level.custom = () => {
- // // All the logic gets handled here. How nice!
- // for (const i in LogicHandler) {
- // LogicHandler[i]();
- // }
-
- // // Animations and lore for things that seem like exits
- // for (const i in RoomTransitionHandler) {
- // RoomTransitionHandler[i]();
- // }
-
- // // Bottom layer graphics
- // DrawHandler.base();
- // DrawHandler.room1();
- // DrawHandler.room2();
- // DrawHandler.room3();
- // DrawHandler.entrance();
- // if (templePlayer.drawExit) level.exit.drawAndCheck();
- // };
- // level.customTopLayer = () => {
- // // Top layer graphics
- // DrawHandler.mobTrails();
- // Objects.altar.drawTop();
- // DrawHandler.waveTimer();
- // DrawHandler.room2Top();
- // };
- // },
dripp() {
simulation.makeTextLog(`dripp by M. B.`);
@@ -13056,6 +11663,1127 @@ const level = {
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
+ biohazard() {
+ // MAP BY INOOBBOI AND THESHWARMA
+ simulation.makeTextLog(`dripp by INOOBBOI and THESHWARMA`);
+
+ // set here for the cutscene later
+ level.setPosToSpawn(-2800, -150)
+
+ // set up cutscenes
+ simulation.cutscene = (locations, speed, stay, xPos = m.pos.x, yPos = m.pos.y) => {
+ // locations: an array of location vectors, reversed for the pop ahead
+ locations.reverse()
+ // speed: the speed of the cutscene transition (0 to 1)
+ // stay: how much to stay in the destination (ticks)
+ // xPos & yPos: the initial location, also used as the current location
+
+ // start by disabling the default camera draw. Don't worry, it's backed up
+ const camera = simulation.camera
+ // create a new camera function
+ simulation.camera = () => {
+ ctx.save()
+ ctx.translate(canvas.width2, canvas.height2) //center
+ ctx.scale(simulation.zoom, simulation.zoom)
+ const xScaled = canvas.width2 - xPos
+ const yScaled = canvas.height2 - yPos
+ ctx.translate(-canvas.width2 + xScaled, -canvas.height2 + yScaled) //translate
+ }
+
+ // and set a restoring function
+ const restore = () => (simulation.camera = camera)
+
+ // then choose the next destination. There should be always at least one destination,
+ // if there isn't there's no point checking, the game should and will crash
+ let dest = locations.pop()
+ // animate the camera
+ const lerp = (first, second, percent) => first * (1 - percent) + second * percent
+ const speedDelta = speed / 5
+ // wait timer
+ let wait = 0
+ // polls the animation, should be called every tick
+ const poll = () => {
+ // update position
+ xPos = lerp(xPos, dest.x, speedDelta)
+ yPos = lerp(yPos, dest.y, speedDelta)
+ // if position is close enough, wait and go to the next position
+ const TOO_CLOSE = 100
+ if (Math.abs(dest.x - xPos) < TOO_CLOSE && Math.abs(dest.y - yPos) < TOO_CLOSE) {
+ // wait for a bit
+ if (++wait > stay) {
+ // if there is another target, reset the wait timer and go there
+ // otherwise end the cutscene
+ wait = 0
+ if (!(dest = locations.pop())) {
+ // no more locations! End
+ restore()
+ return true
+ }
+ }
+ }
+ // early return if the player skips by fielding
+ if (input.field) {
+ restore()
+ return true
+ }
+ return false
+ }
+ return poll
+ }
+
+ const boost1 = level.boost(-1400, -100, 900)
+ const boost2 = level.boost(500, -900, 2500)
+ const boost3 = level.boost(4200, -100, 900)
+ const boost4 = level.boost(2200, -900, 2500)
+
+ const toggle = level.toggle(1340, -600, false, true)
+
+ let bossInit = false
+
+ const cutscenePoll = simulation.cutscene([{ x: 230, y: -2700 }, { x: 3500, y: -1400 }, { x: 1450, y: -1150 }, m.pos], 0.1, 10)
+ let hasEnded = false
+
+ // ** PROPS **
+ // create some drips
+ const rndInRange = (min, max) => Math.random() * (max - min) + min
+
+ const amount = Math.round(5 + 20 * Math.random())
+ const drips = []
+ for (let i = 0; i < amount; i++) {
+ const locX = rndInRange(-2000, 4800)
+ drips.push(level.drip(locX, -3100, 1500, 200 + Math.random() * 500))
+ }
+
+ // a barrel of radioactive waste, which can drop ammo and heals
+ const barrelMob = (x, y, dirVector) => {
+ const MAX_WIDTH = 150
+ const personalWidth = MAX_WIDTH / 2
+ mobs.spawn(x, y, 4, personalWidth, 'rgb(232, 191, 40)')
+ const me = mob[mob.length - 1]
+ // steal some vertices
+ const betterVertices = Matter.Bodies.rectangle(x, y, personalWidth, personalWidth * 1.7).vertices
+ me.vertices = betterVertices
+ me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob | cat.bullet
+ me.g = simulation.g
+ me.leaveBody = me.isDropPowerUp = false
+ me.do = function() {
+ this.gravity()
+ // apply shock damage when touching the map, if it's fast
+ if (this.speed > 5) {
+ const collision = Matter.Query.collides(this, map)
+ if (collision.length > 0) {
+ // on collision reduce health
+ this.health = this.health - this.speed / 250
+ // die when it's too low, doesn't register for some reason
+ }
+ }
+ // becomes more radioactive as it gets damaged!
+ this.fill = `rgb(${232 * this.health}, 191, 40)`
+ }
+
+ me.onDeath = function() {
+ const END = Math.floor(input.down ? 10 : 7)
+ const totalBullets = 10
+ const angleStep = (input.down ? 0.4 : 1.3) / totalBullets
+ let dir = m.angle - (angleStep * totalBullets) / 2
+ for (let i = 0; i < totalBullets; i++) {
+ //5 -> 7
+ dir += angleStep
+ const me = bullet.length
+ bullet[me] = Bodies.rectangle(
+ this.position.x + 50 * Math.cos(this.angle),
+ this.position.y + 50 * Math.sin(this.angle),
+ 17,
+ 4,
+ b.fireAttributes(dir)
+ )
+ const end = END + Math.random() * 4
+ bullet[me].endCycle = 2 * end + simulation.cycle
+ const speed = (25 * end) / END
+ const dirOff = dir + (Math.random() - 0.5) * 3
+ Matter.Body.setVelocity(bullet[me], {
+ x: speed * Math.cos(dirOff),
+ y: speed * Math.sin(dirOff)
+ })
+ bullet[me].onEnd = function() {
+ b.explosion(
+ this.position,
+ 150 + (Math.random() - 0.5) * 40
+ ) //makes bullet do explosive damage at end
+ }
+ bullet[me].beforeDmg = function() {
+ this.endCycle = 0 //bullet ends cycle after hitting a mob and triggers explosion
+ }
+ bullet[me].do = function() {}
+ Composite.add(engine.world, bullet[me]) //add bullet to world
+ }
+ // barrels drop a ton of ammo and some heals, scales up with difficulty because I have mercy
+ const amount = ~~(5 * Math.random() * simulation.difficulty / 10)
+ for (let i = 0; i < amount; i++) {
+ powerUps.spawn(this.position.x, this.position.y, 'ammo', true)
+ if (Math.random() > 0.7) {
+ powerUps.spawn(this.position.x, this.position.y, 'heal', true)
+ }
+ }
+ }
+ Matter.Body.rotate(me, Math.random() * Math.PI)
+ Matter.Body.setVelocity(me, dirVector)
+ }
+
+ // creates a platform with shadow
+ const platformShadow = (x, y, width, height, shadowList) => {
+ // faster than making manual shadows... Why not just calculate them semi-realsitically?
+ // the shadows are calculated on the object creation, so if you add map blocks it won't update.
+ // shadowList is an array of shadows that'll be rendered. When the platform shadow is ready,
+ // it is added to the list.
+ // some helper functions first
+ const perpCollision = point => {
+ // takes a point, and finds a collision with the map downwards
+ // the end of the ray, 3000 units down
+ const lowerPoint = Vector.add(point, { x: 0, y: 3000 })
+ // the destination point. If a collision was not found, then it defaults to some
+ // arbiterary point 3000 units down.
+ let dest = lowerPoint
+ for (const mapBody of map) {
+ const check = simulation.checkLineIntersection(point, lowerPoint, mapBody.vertices[0], mapBody.vertices[1])
+ // a collision was found
+ if (check.onLine1 && check.onLine2) {
+ dest = { x: check.x, y: check.y }
+ break
+ }
+ }
+ return dest
+ }
+ const boundsToRectangle = (firstBound, secondBound) => {
+ // takes two bounds and returns an (x, y, width, height) rectangle. The first one
+ // must be the top left, and the second one must be the bottom right
+ // sub to find the width and height
+ const width = Math.abs(firstBound.x - secondBound.x)
+ const height = Math.abs(firstBound.y - secondBound.y)
+ // compile to an object
+ return { x: firstBound.x, y: firstBound.y, width, height }
+ }
+ // create the actual platform
+ spawn.mapRect(x, y, width, height)
+ const me = map[map.length - 1]
+ // the bottom vertices are the third and fourth ones
+ const first = me.vertices[3]
+ const second = me.vertices[2]
+ // cast shadows to find the last shadow location.
+ // iterate over all map objects, and check for collisions between a perpendicular ray
+ // cast from the vertex down to the map object's top panel
+ // const firstDown = perpCollision(first) // not needed in a rectangle settings
+ const secondDown = perpCollision(second)
+ // possible TODO: make it multirect for efficiency
+ // create a single rectangle and return
+ shadowList.push(boundsToRectangle(first, secondDown))
+ }
+
+ // cages with mobs, One of them holds the boss pre mutation
+ const cage = (x, y, maxChainLength, drawList, mobType = null, isTheBoss = false) => {
+ // the drawList is an array that the drawing function is added to
+ // if the cage containing the boss it has a 50% chance to just not spawn. Spices things a bit
+ if (!isTheBoss && Math.random() > 0.5) {
+ return
+ }
+ if (!mobType) {
+ // if mob type is null, then it picks a random mob
+ mobType = spawn.fullPickList[~~(Math.random() * spawn.fullPickList.length)]
+ }
+ // create the chain length, must take into account the radius of the mob.
+ // therefore, it'll create a pseudo mob of that type, take it radius and instantly kill it
+ const chainLength = maxChainLength / 5 + maxChainLength * Math.random()
+
+ // spawn and insantly kill a mob of the same type to get the radius.
+ // this is used to prevent spawning the mob too short, it's a horrible
+ // solution but it works
+ spawn[mobType](0, 0)
+ mob[mob.length - 1].leaveBody = mob[mob.length - 1].isDropPowerUp = false
+ const radius = mob[mob.length - 1].radius
+ mob[mob.length - 1].alive = false
+ // spawn the mob. Disable shields first
+ spawn.allowShields = false
+ spawn[mobType](x, y + chainLength + radius * 2)
+ const trappedMob = mob[mob.length - 1]
+ // destroy its mind so it won't attack
+ trappedMob.do = () => {}
+ // spawn the cage
+ mobs.spawn(x, y + chainLength + radius * 2, 4, trappedMob.radius + 50, 'rgba(150, 255, 150, 0.3)')
+ const cage = mob[mob.length - 1]
+ cage.g = simulation.g
+ cage.do = function() {
+ this.gravity()
+ }
+ // label it
+ cage.label = 'Cage'
+ // a special orb when hit
+ let damageTick = 0
+ cage.onDamage = (dmg) => {
+ // add some damage ticks, if the trapped mob is still alive.
+ // activating the boss by this method is almost impossible, since you need 10x damage
+ if (trappedMob.alive) damageTick += ~~(isTheBoss ? 5 * dmg : 50 * dmg)
+ }
+ // collision filter
+ trappedMob.collisionFilter.mask = cage.collisionFilter.mask = cat.player | cat.map | cat.bullet
+ // constrain together
+ spawn.constrain2AdjacentMobs(2, 0.05, false)
+ // move them to be together
+ trappedMob.position = Vector.clone(cage.position) // make sure you clone... Otherwise........
+ // invincibility, make homing bullets not hit these, remove health bar
+ trappedMob.health = cage.health = Infinity
+ trappedMob.isBadTarget = cage.isBadTarget = true
+ trappedMob.showHealthBar = cage.showHealthBar = false
+ trappedMob.leaveBody = trappedMob.isDropPowerUp = cage.leaveBody = trappedMob.isDropPowerUp = false
+ // cross all edges of the cage with the rope, and see where it collides. Attach the rope there
+ const verts = cage.vertices
+ // the crossing location, doesn't stay null
+ let cross = null
+ for (let i = 0; i < verts.length; i++) {
+ // iterate over all vertices to form lines
+ const v1 = verts[i]
+ const v2 = verts[(i + 1) % verts.length]
+ const result = simulation.checkLineIntersection(cage.position, { x, y }, v1, v2)
+ if (result.onLine1 && result.onLine2) {
+ // both lines cross!
+ cross = result
+ break
+ }
+ }
+
+ if (!cross) {
+ // for some odd reason, sometimes it never finds a collision. I have no idea why
+ // just default to the center then
+ console.error("Couldn't find a cross... Origin: ", { x, y }, " center: ", cage.position, ' vertices: ', cage.vertices)
+ cross = cage.position
+ }
+ // create the rope
+ const rope = Constraint.create({
+ pointA: {
+ x,
+ y
+ },
+ // offset the point be in the attachment point
+ pointB: Vector.sub(cross, cage.position),
+ bodyB: cage,
+ // the longer the rope, the looser it is
+ stiffness: Math.max(0.0005 - chainLength / 10000000, 0.00000001),
+ length: chainLength
+ })
+ Matter.Composite.add(engine.world, rope)
+ // create and return a function for drawing the rope
+ const draw = () => {
+ // draw a little recantagle at the base
+ ctx.fillStyle = color.map
+ ctx.fillRect(x - 20, y - 5, 40, 25)
+ // if the cage was destroyed... Do nothing beyond
+ if (!cage.alive) {
+ return
+ }
+ // draw the rope
+ ctx.beginPath()
+ ctx.moveTo(x, y)
+ // line to the crossing point
+ // ctx.lineTo(cons[i].bodyB.position.x, cons[i].bodyB.position.y);
+ ctx.lineTo(cage.position.x + rope.pointB.x, cage.position.y + rope.pointB.y);
+ ctx.lineWidth = 7
+ ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'
+ ctx.stroke()
+ // now draw a mystic hit orb if touched
+ if (damageTick) damageTick-- // reduce the ticks
+ ctx.beginPath()
+ ctx.arc(cage.position.x, cage.position.y, cage.radius + 30, 0, Math.PI * 2)
+ ctx.lineWidth = 10
+ ctx.fillStyle = `rgba(255, 0, 0, ${Math.min(1, damageTick / 2000)})`
+ ctx.strokeStyle = `rgba(255, 100, 0, ${Math.min(1, damageTick / 1000)})`
+ ctx.setLineDash([125 * Math.random(), 125 * Math.random()])
+ ctx.stroke()
+ ctx.setLineDash([])
+ ctx.fill()
+ // if it's the boss, draw sucking arcs
+ if (isTheBoss && bossInit) {
+ for (const entity of mob) {
+ // suck the health of all mobs
+ // I hate string manipulation in control flow but heh
+ if (entity.alive) {
+ ctx.beginPath()
+ ctx.moveTo(entity.position.x, entity.position.y)
+ ctx.lineTo(trappedMob.position.x, trappedMob.position.y)
+ ctx.lineWidth = 10
+ ctx.strokeStyle = 'rgba(38, 0, 255, 0.67)'
+ ctx.stroke()
+ // damage the mob
+ entity.damage(1)
+ // damage itself bonus
+ cage.damage(1)
+ }
+ }
+ cage.damage(5)
+ }
+
+ // ok if it's too much, explode
+ if (damageTick > 2000) {
+ b.explosion(cage.position, cage.radius * 10)
+ // die a silent death
+ trappedMob.alive = cage.alive = false
+ damageTick = 0
+ if (isTheBoss) {
+ // become the real boss
+ geneticBoss(trappedMob.position.x, trappedMob.position.y)
+ }
+ }
+ }
+ // restore the shields
+ spawn.allowShields = true
+ // add the drawing function
+ drawList.push(draw)
+ }
+
+ // platform shadows
+ const shadows = []
+ // cages
+ const cages = []
+
+ level.custom = () => {
+ level.exit.drawAndCheck() //draws the exit
+ level.enter.draw() //draws the entrance
+
+ player.force.y -= player.mass * simulation.g * 0.25 //this gets rid of some gravity on player
+
+ // if the cutscene is yet to end, continue polling
+ if (!hasEnded) {
+ hasEnded = cutscenePoll()
+ }
+
+ for (const drip of drips) drip.draw()
+ // throw some barrels after the boss spawns
+ if (Math.random() > 0.999 && bossInit && !hasEnded) {
+ const spawnLocs = [-1415, -30, 1345, 2815, 4285]
+ // const randVec = Vector.mult({ x: Math.cos(randAngle), y: Math.sin(randAngle) }, Math.random() * 15)
+ barrelMob(spawnLocs[~~(spawnLocs.length * Math.random())], -4200, { x: 0, y: 0 })
+ }
+
+ // platform shadow
+ ctx.beginPath()
+ for (const shadow of shadows) {
+ ctx.rect(shadow.x, shadow.y, shadow.width, shadow.height)
+ }
+ ctx.fillStyle = 'rgba(0,10,30,0.1)'
+ ctx.fill()
+
+ // player pressed lever
+ if (toggle.isOn && !bossInit) {
+ bossInit = true
+ }
+ // draw the cage
+ } //for dynamic stuff that updates while playing that is one Z layer below the player
+
+ level.customTopLayer = () => {
+ boost1.query()
+ boost2.query()
+ boost3.query()
+ boost4.query()
+ toggle.query()
+
+ // shadow holes
+ ctx.fillStyle = 'rgba(68, 68, 68,0.95)'
+ ctx.fillRect(-1450 - 10, -4350, 150 + 20, 1250)
+ ctx.fillRect(-50 - 10, -4350, 150 + 20, 1250)
+ ctx.fillRect(1325 - 10, -4350, 150 + 20, 1250)
+ ctx.fillRect(2800 - 10, -4350, 150 + 20, 1250)
+ ctx.fillRect(4275 - 10, -4350, 150 + 20, 1250)
+
+ for (const drawCage of cages) {
+ drawCage()
+ }
+ } //for dynamic stuff that updates while playing that is one Z layer above the player
+
+ const anotherBoss = (x, y) => {
+ if (tech.isDuplicateBoss && Math.random() < tech.duplicationChance()) {
+ tech.isScaleMobsWithDuplication = true
+ spawn.historyBoss(x, y)
+ tech.isScaleMobsWithDuplication = false
+ } else if (tech.isResearchBoss) {
+ if (powerUps.research.count > 3) {
+ powerUps.research.changeRerolls(-4)
+ simulation.makeTextLog(
+ `m.research -= 4
${powerUps.research.count}`
+ )
+ } else {
+ tech.addJunkTechToPool(0.49)
+ }
+ spawn.historyBoss(x, y)
+ }
+ }
+
+ //GENETICBOSS
+ function drawEnergyBar(mob) {
+ if (mob.seePlayer.recall && mob.energy > 0) {
+ const h = mob.radius * 0.3
+ const w = mob.radius * 2
+ const x = mob.position.x - w / 2
+ const y = mob.position.y - w * 0.9
+ ctx.fillStyle = 'rgba(100, 100, 100, 0.3)'
+ ctx.fillRect(x, y, w, h)
+ ctx.fillStyle = '#0cf'
+ ctx.fillRect(x, y, w * mob.energy, h)
+ }
+ }
+
+ function generateGenome() {
+ // creates a random genome and returns it
+ const genome = {
+ density: Math.random() * 0.001,
+ size: 15 + Math.random() * 15,
+ speed: Math.random() * 0.1,
+ // color and vertex properties are "trash" genes as they don't really contribute to the orb
+ color: [Math.random() * 255, Math.random() * 255, Math.random() * 255, 50 + Math.random() * 205],
+ vertexCount: Math.floor(Math.random() * 5) + 3,
+ // TODO fix possible concaving
+ vertexOffset: null // placeholder
+ }
+ // initialized here as it depends on vertexCount. I could use `new function()` but nah.
+ genome.vertexOffset = Array(genome.vertexCount)
+ .fill()
+ .map(() => ({ x: Math.random() - 0.5, y: Math.random() - 0.5 }))
+ return genome
+ }
+
+ function mutateGenome(genome) {
+ // takes an existing genome and applies tiny changes
+ const randomInRange = (min, max) => Math.random() * (max - min) + min
+ const tinyChange = x => randomInRange(-x, x)
+
+ const vertexMutator = x => ({ x: x.x + tinyChange(0.5), y: x.y + tinyChange(0.5) })
+ // mutates a genome and returns the mutated version.
+ const newGenome = {
+ density: genome.density + tinyChange(0.0005),
+ size: genome.size + tinyChange(5),
+ speed: genome.speed + tinyChange(0.05),
+ color: genome.color.map(x => (x + tinyChange(10)) % 255), // wrap around
+ vertexCount: Math.max(genome.vertexCount + Math.round(tinyChange(1)), 3),
+ vertexOffset: genome.vertexOffset.map(vertexMutator)
+ }
+ if (genome.vertexOffset.length < newGenome.vertexCount) {
+ const vo = newGenome.vertexOffset
+ vo.push(vertexMutator(vo[~~(vo.length * Math.random())]))
+ } else if (genome.vertexOffset.length > newGenome.vertexCount) {
+ newGenome.vertexOffset.pop()
+ }
+
+ return newGenome
+ }
+
+ function calculateGenomeCost(genome) {
+ // calculates the cost of a genome and returns it. The cost is used to
+ // determine how "costly" the genome is to make, and after the orb's life ends it
+ // is used together with the orb success score to determine the fittest orb.
+ const score = (1 / (genome.density * genome.size * genome.speed)) * 0.000001
+ return score
+ }
+ // distance functions
+ const dist2 = (a, b) => (a.x - b.x) ** 2 + (a.y - b.y) ** 2
+ const dist = (a, b) => Math.sqrt(dist2(a, b))
+
+ // ** MAP SPECIFIC MOBS **
+ function energyTransferBall(origin, target, boss, charge) {
+ // transports energy to the boss
+ // when the boss is hit by it, how much of the energy stored the boss actually recives
+ const ENERGY_TRANSFER_RATE = 80 /*%*/
+ // add 1 to the active ball list
+ boss.activeBalls++
+ const color = `rgba(${150 + 105 * charge}, 81, 50, 0.6)`
+ mobs.spawn(origin.x, origin.y, 12, 20 + 20 * charge, color)
+ const me = mob[mob.length - 1]
+ me.end = function() {
+ simulation.drawList.push({
+ // some nice graphics
+ x: this.position.x,
+ y: this.position.y,
+ radius: this.radius,
+ color: '#f3571d',
+ time: ~~(Math.random() * 20 + 10)
+ })
+ // on death spawn and explode a bomb
+ if (Math.random() > 0.95) {
+ spawn.bomb(this.position.x, this.position.y, this.radius, this.vertices.length)
+ mob[mob.length - 1].death()
+ }
+ // remove 1 from the active ball list
+ boss.activeBalls--
+ this.death()
+ }
+ me.collisionFilter.mask = cat.player | cat.map
+ // me.onHit = this.end
+ me.life = 0
+ me.isDropPowerUp = false
+ me.leaveBody = false
+ me.do = function() {
+ // die on collision with the map
+ if (Matter.Query.collides(this, map).length > 0) {
+ this.end()
+ }
+ // die if too much time passes. Stronger bullets explode earlier
+ if (++this.life > 200 - charge * 100) {
+ this.end()
+ }
+ // if the orb collides with the boss, die but give energy to the boss
+ if (Matter.Query.collides(this, [boss]).length > 0) {
+ boss.energy = Math.min(charge * (ENERGY_TRANSFER_RATE / 100) + boss.energy, 1)
+ // also make the boss fire once regardless of energy
+ boss.spawnOrbs()
+ this.end()
+ }
+ const movement = Vector.normalise(Vector.sub(target, origin))
+ Matter.Body.setVelocity(this, {
+ x: this.velocity.x + movement.x,
+ y: this.velocity.y + movement.y
+ })
+ // nice graphics
+ simulation.drawList.push({
+ x: this.position.x,
+ y: this.position.y,
+ radius: this.radius,
+ color: '#e81e1e',
+ time: 3
+ })
+ simulation.drawList.push({
+ x: this.position.x,
+ y: this.position.y,
+ radius: this.radius,
+ color: '#e87f1e',
+ time: 6
+ })
+ simulation.drawList.push({
+ x: this.position.x,
+ y: this.position.y,
+ radius: this.radius,
+ color: '#e8e41e',
+ time: 9
+ })
+ }
+ me.onDamage = me.end
+ }
+
+ function energyBeacon(x, y, parentBoss) {
+ // an unmoving beacon that charges the genetic boss with energy either stolen
+ // from the player or generated. That energy is used to create stronger mobs.
+ mobs.spawn(x, y, 3, 50, '') // default color, changed an instant later
+ const me = mob[mob.length - 1]
+ me.laserRange = 500
+ me.leaveBody = false
+ me.isDropPowerUp = false
+ // custom variables
+ // max energy is 1
+ me.energy = 0
+ me.seed = simulation.cycle // seed to make sure this mob is unique render wise
+ me.chargeTicks = 0 // used to time charging the boss
+ me.bossPos = null // the position that the mob remembers when charging
+ me.density = me.density * 2
+ Matter.Body.setDensity(me, 0.0022 * 3 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense
+ me.do = function() {
+ // if the boss is dead, die
+ if (!parentBoss.alive) {
+ this.death()
+ }
+ // slowly rotate
+ Matter.Body.setAngularVelocity(this, 0.01)
+ this.fill = `rgba(${this.energy * 255}, 29, 136, 0.80)`
+ this.seePlayerCheck()
+ // steal energy from player
+ // this.harmZone() // regular harmZone
+ // custom effects on top of that
+ if (this.distanceToPlayer() < this.laserRange) {
+ if (m.immuneCycle < m.cycle) {
+ // suck extra energy from the player if it's in range
+ if (m.energy > 0.1 && this.energy < 1 - 0.012) {
+ m.energy -= 0.012
+ this.energy += 0.012
+ }
+ // special "sucking" graphics
+ ctx.beginPath()
+ ctx.moveTo(this.position.x, this.position.y)
+ ctx.lineTo(m.pos.x, m.pos.y)
+ ctx.lineWidth = 3 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 2
+ ctx.strokeStyle = `rgb(${(
+ Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255
+ ).toFixed(3)}, 204, 255)`
+ ctx.setLineDash([125 * Math.random(), 125 * Math.random()])
+ ctx.stroke()
+ ctx.setLineDash([])
+ }
+ }
+ // if the mob's energy is at least 50% full, try to send that energy to the boss.
+ // don't send that energy yet if more than 5 other transfer balls are active
+ if (this.energy > 0.5 && parentBoss.energy < 1 && parentBoss.activeBalls <= 5 && this.chargeTicks === 0) {
+ const seesBoss = Matter.Query.ray(map, this.position, parentBoss.position).length === 0
+ if (seesBoss) {
+ this.chargeTicks = 100
+ this.bossPos = Vector.clone(parentBoss.position)
+ }
+ }
+ if (this.chargeTicks > 0) {
+ if (--this.chargeTicks === 0) {
+ // spawn the orb
+ const location = Vector.add(
+ Vector.mult(Vector.normalise(Vector.sub(this.bossPos, this.position)), this.radius * 3),
+ this.position
+ )
+ energyTransferBall(location, this.bossPos, parentBoss, this.energy)
+ this.energy = 0
+ }
+ // create a beam and aim it at the bossPos
+ ctx.beginPath()
+ ctx.moveTo(this.position.x, this.position.y)
+ ctx.lineTo(this.bossPos.x, this.bossPos.y)
+ ctx.lineWidth = 10 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 5
+ ctx.strokeStyle = `rgb(${(
+ Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255
+ ).toFixed(3)}, 204, 255)`
+ ctx.setLineDash([125 * Math.random(), 125 * Math.random()])
+ ctx.stroke()
+ ctx.setLineDash([])
+ }
+ // generate (0.15 * difficulty / 4)% energy per tick
+ if (this.energy < 1) this.energy += 0.0015 * (simulation.difficulty / 4)
+ // draw energy bar
+ drawEnergyBar(this)
+ }
+ me.onDeath = function() {
+ // remove itself from the list
+ const beacons = parentBoss.energyBeacons
+ beacons.splice(beacons.indexOf(this), 1)
+ // explode with the strength of its energy!
+ this.alive = false // to prevent retriggering infinitly
+ b.explosion(this.position, this.energy * this.radius * 15)
+ // when it dies, it reduces some of the boss' energy
+ parentBoss.energy -= 0.025
+ // and stuns it
+ mobs.statusStun(parentBoss, 70 + ~~(100 / simulation.difficulty))
+ }
+ }
+
+ function geneticSeeker(x, y, genome, parentBoss) {
+ // special bullets that get score based on their performance.
+ mobs.spawn(x, y, genome.vertexCount, genome.size, '#' + genome.color.map(it => (~~it).toString(16)).join(''))
+ const me = mob[mob.length - 1]
+ // apply genome
+ Matter.Body.setDensity(me, genome.density)
+ me.accelMag = genome.speed
+ // apply vertex offset
+ for (let i = 0; i < me.vertices.length; i++) {
+ const vertex = me.vertices[i]
+ const offset = genome.vertexOffset[i]
+ if (!offset) console.log(genome, me)
+ vertex.x += offset.x
+ vertex.y += offset.y
+ }
+
+ me.stroke = 'transparent'
+ Matter.Body.setDensity(me, 0.00001) //normal is 0.001
+ // increased if the orb done things that are deemed successful
+ me.score = 30
+ me.timeLeft = 9001 / 9
+ me.accelMag = 0.00017 * simulation.accelScale //* (0.8 + 0.4 * Math.random())
+ me.frictionAir = 0.01
+ me.restitution = 0.5
+ me.leaveBody = false
+ me.isDropPowerUp = false
+ me.isBadTarget = true
+ me.isMobBullet = true
+ me.showHealthBar = false
+ me.collisionFilter.category = cat.mobBullet
+ me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet
+ me.do = function() {
+ this.alwaysSeePlayer()
+ this.attraction()
+ this.timeLimit()
+
+ if (Matter.Query.collides(this, map).length > 0) {
+ // colliding with the map gives a score reduction, 0.5 per tick
+ this.score -= 0.5
+ }
+ // default score is slowly reduced every tick to give mobs that reached the player faster a benefit
+ this.score -= 0.05
+ if (this.score < 0) {
+ this.alive = false // no point continuing if this orb is that bad! Silent death
+ }
+ // give a bonus if some projectile is nearby or the mouse position is close (like laser pointing)
+ // if a mob survives this for long, then it gets a score benefit.
+ const bulletCloseToOrb = bullet.some(it => dist2(this.position, it.position) < 10000 /* 100 ^ 2 */ )
+ // player shoots and aims close
+ const mouseCloseToOrb = dist2(this.position, simulation.mouseInGame) < 10000 && input.fire
+ if (bulletCloseToOrb || mouseCloseToOrb) {
+ this.score += 1
+ }
+ // die if too far from the boss... It would be incredibly difficult to dodge otherwise
+ if (dist2(this.position, parentBoss.position) > 2000 * 2000) {
+ this.alive = false
+ }
+ // DEBUG score printer
+ // ctx.font = '48px sans-serif'
+ // ctx.fillStyle = 'rgba(252, 0, 143, 1)'
+ // ctx.fillText(~~this.score, this.position.x - this.radius, this.position.y - this.radius)
+ }
+ me.onHit = function() {
+ // hitting the player gives a 50 points score bonus
+ this.score += 50
+ this.score += this.mass * 2 // bigger mass = bigger damage, add that too
+ // a marker for later
+ this.hitPlayer = true
+ this.explode(this.mass)
+ }
+ me.onDeath = function() {
+ if (!this.hitPlayer) {
+ // if it didn't hit the player, give it a score based on its distance
+ this.score += 10000 / this.distanceToPlayer()
+ }
+ // 3% chance to drop ammo
+ if (Math.random() > 0.97) {
+ powerUps.spawn(this.position.x, this.position.y, 'ammo', true)
+ }
+ parentBoss.deadOrbs.push({ genome: genome, score: this.score })
+ }
+ }
+
+ function geneticBoss(x, y, radius = 130, spawnBossPowerUp = true) {
+ // a modified orb shooting boss that evolves its orbs.
+ // the way this boss works is different from the regular orb shooting boss,
+ // because the orbs have evolving properties via a "machine learning" scoring algorithm.
+ const MAX_BEACONS = Math.round(3 + Math.random() * simulation.difficulty / 3)
+ mobs.spawn(x, y, 8, radius, 'rgb(83, 32, 58)')
+ let me = mob[mob.length - 1]
+ me.isBoss = true
+
+ me.accelMag = 0.0001 * simulation.accelScale
+ me.fireFreq = Math.floor((330 * simulation.CDScale) / simulation.difficulty)
+ me.frictionStatic = 0
+ me.friction = 0
+ me.frictionAir = 0.02
+ me.memory = (420 / 69) * 42 // 🧌
+ me.repulsionRange = 1000000
+ me.energyBeacons = []
+ me.activeBalls = 0
+ // starts by random, or by the stored genomes if they exist
+ const init = () => ({ genome: generateGenome(), score: 0 })
+ me.fittestOrbs = (localStorage && localStorage.genome) ? JSON.parse(localStorage.genome) : [init(), init(), init()] // best genomes so far. Size of three
+ // when an orb died it's moved here. When a new spawn cycle starts, their scores get calculated
+ // and they get put in the fittest orbs array, if they are better than the old ones.
+ me.deadOrbs = []
+ me.energy = 1
+ // this boss has no orbitals, because it's not meant to ever attack on its own
+ me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
+ // has a shield and sustains that shield
+ spawn.shield(me, x, y, Infinity)
+ me.fireFreq = 30
+ me.ionizeFreq = 20
+ me.ionized = []
+ me.laserRange = radius * 4
+
+ Matter.Body.setDensity(me, 0.0022 * 4 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense //normal is 0.001 //makes effective life much larger
+ me.onDeath = function() {
+ if (spawnBossPowerUp) {
+ powerUps.spawnBossPowerUp(this.position.x, this.position.y)
+ const amount = ~~(5 * Math.random() * simulation.difficulty / 10) * 2
+ for (let i = 0; i < amount; i++) {
+ powerUps.spawn(this.position.x, this.position.y, 'ammo', true)
+ if (Math.random() > 0.7) {
+ powerUps.spawn(this.position.x, this.position.y, 'heal', true)
+ }
+ }
+ }
+ // keep the best genome and use it next fight...
+ if (localStorage) {
+ localStorage.setItem("genome", JSON.stringify(this.fittestOrbs))
+ }
+
+ // stop spawning barrels
+ bossInit = false
+ }
+ me.onDamage = function() {}
+ me.spawnBeacon = function() {
+ // the vertex to spawn the beacon from
+ const vert = this.vertices[~~(Math.random() * this.vertices.length)]
+ // the position should be a little to the side to prevent crashing into the boss
+ // TODO check for collisions with the wall
+ const spawnPos = Vector.add(vert, Vector.mult(Vector.normalise(Vector.sub(this.position, vert)), -60))
+ // some velocity
+ const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, vert)), -5)
+ energyBeacon(spawnPos.x, spawnPos.y, this) // spawn the beacon, a bit ahead
+ const beacon = mob[mob.length - 1]
+ this.energyBeacons.push(beacon)
+ Matter.Body.setVelocity(beacon, {
+ x: this.velocity.x + velocity.x,
+ y: this.velocity.y + velocity.y
+ })
+ }
+ me.spawnOrbs = function() {
+ Matter.Body.setAngularVelocity(this, 0.11)
+ // sort the vertices by the distance to the player
+ const sorted = [...this.vertices].sort(dist2)
+ // spawn the bullets based on how close they are to the player.
+ // the way it works is it picks the fittest three orbs and clones them.
+ // but start by taking old mobs and checking if they are better than the new ones
+ let next
+ while ((next = this.deadOrbs.pop())) {
+ // material costs are calculated as a contra to the score
+ const cost = calculateGenomeCost(next.genome) * 500 // normalize via multiplication
+ const totalScore = next.score - cost
+ // try to insert itself into the best orbs, if it can
+ for (let i = 0; i < this.fittestOrbs.length; i++) {
+ const fitEntry = this.fittestOrbs[i]
+ if (fitEntry.score < totalScore) {
+ this.fittestOrbs[i] = next
+ break
+ }
+ }
+ }
+ // finally sort them using their score
+ this.fittestOrbs.sort((a, b) => a.score - b.score)
+ // only take the genome, the score doesn't matter here
+ const bestOrbs = this.fittestOrbs.map(it => it.genome)
+ for (let vertex of sorted) {
+ // pick a random fit orb and try to spawn it. If the cost is too high, it'll attempt
+ // to generate a new random orb instead. If that orb is too expensive too, just ignore this vertex.
+ // the evolution part comes here, as the genome is mutated first.
+ let randGenome = mutateGenome(bestOrbs[~~(Math.random() * bestOrbs.length)])
+ const cost = calculateGenomeCost(randGenome) * 2
+ if (this.energy - cost < 0) {
+ // okay this orb is too expensive for the boss to spawn,
+ // make a new orb from scratch
+ randGenome = generateGenome()
+ const secondCost = calculateGenomeCost(randGenome)
+ if (this.energy - secondCost < 0) {
+ // that was too expensive too, heh
+ continue
+ }
+ } else {
+ // alright the boss can afford that
+ this.energy -= Math.abs(cost) // TODO: Fix this, why the heck can it even be negative??
+ }
+
+ geneticSeeker(vertex.x, vertex.y, randGenome, this)
+ // 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, vertex))),
+ -10
+ )
+ Matter.Body.setVelocity(mob[mob.length - 1], {
+ x: this.velocity.x + velocity.x,
+ y: this.velocity.y + velocity.y
+ })
+ }
+ }
+ me.do = function() {
+ this.seePlayerCheck()
+ this.checkStatus()
+ this.attraction()
+ this.repulsion()
+ // draw laser arcs if it sees the player
+ this.harmZone()
+ //
+ const regularChance = Math.random() > 0.99
+ const biggerChance = Math.random() > 0.95 && this.energy > 0.25
+ // start by making sure there is always at least one beacon
+ if (this.energyBeacons.length === 0) {
+ this.spawnBeacon()
+ }
+ // then, spawn some energy beacons if there are less than the maximum.
+ // small chance if there's no energy, bigger chance if there is at least 10% (which is drained)
+ if ((this.energyBeacons.length < MAX_BEACONS && biggerChance) || regularChance) {
+ if (biggerChance) {
+ // if the spawn was a selection of bigger chance, reduce 10% energy
+ this.energy -= 0.10
+ }
+ this.spawnBeacon()
+ }
+ // then, spawn genetic seekers
+ if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq)) {
+ // fire a bullet from each vertex if there's enough energy
+ if (this.energy > 0.15) {
+ this.spawnOrbs()
+ }
+ }
+
+ if (this.energy > 1) {
+ // clean excess energy
+ this.energy -= 0.003
+ } else {
+ // or slowly generate energy
+ this.energy += 0.001
+ }
+ // the boss will ionize every bullet in its radius, but that will cause its energy to deplete
+ if (!(simulation.cycle % this.ionizeFreq)) {
+ for (let i = 0; i < bullet.length; i++) {
+ const it = bullet[i]
+ // if it's not a bot and it's close
+ if (!it.botType && dist(this.position, it.position) < this.laserRange) {
+ // add it to the ionized list
+ this.ionized.push({ target: it, ticks: 0 })
+ }
+ }
+ }
+
+ for (let i = 0; i < this.ionized.length; i++) {
+ const entry = this.ionized[i]
+
+ // skip if there's not enough energy
+ if (this.energy <= 0) break
+
+ // terminate if it's no longer in the radius
+ if (dist(this.position, entry.target.position) > this.laserRange) {
+ this.ionized.splice(i, 1)
+ continue
+ }
+ // terminate after some ticks
+ if (++entry.ticks === 10) {
+ entry.target.endCycle = 0
+ // draw nice popping graphics
+ simulation.drawList.push({
+ x: entry.target.position.x,
+ y: entry.target.position.y,
+ radius: 5,
+ color: '#f24',
+ time: ~~(Math.random() * 20 + 10)
+ })
+ // and remove
+ this.ionized.splice(i, 1)
+ continue
+ }
+ // draw line
+ ctx.beginPath()
+ ctx.moveTo(this.position.x, this.position.y)
+ ctx.lineTo(entry.target.position.x, entry.target.position.y)
+ ctx.lineWidth = 7
+ ctx.strokeStyle = `rgb(${60 - entry.ticks * 2}, 50, 50)`
+ ctx.stroke()
+ // reduce energy, as it's hard to ionize
+ this.energy -= entry.target.mass / 25
+ }
+
+ // if it has energy, shield itself and drain energy
+ if (!this.isShielded && this.energy > 0.5) {
+ spawn.shield(this, this.position.x, this.position.y, Infinity)
+ this.energy -= 0.25
+ }
+ drawEnergyBar(this)
+ // change fill color
+ this.fill = `rgb(${((Math.sin(simulation.cycle / 100) + 1) / 2) * 100}, 32, 58)`
+ }
+ // start by spawning several beacons to gain initial energy
+ const amount = Math.ceil(2 + Math.random() * simulation.difficulty / 5)
+ for (let i = 0; i < amount; i++)
+ me.spawnBeacon()
+ }
+
+ // LEVEL SETUP
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20) //don't change this
+
+ level.exit.x = 5700 //you exit at x
+ level.exit.y = -130 //you exit at y
+ spawn.mapRect(5800, -110, -100, 10)
+
+ level.defaultZoom = 2000 //how far out you want the image to be zoomed at (lower = zoom in, higher = zoom out)
+ simulation.zoomTransition(level.defaultZoom) //makes the level transition to have the zoom at the start of a level
+ document.body.style.backgroundColor = 'hsl(138, 3%, 74%)' //sets background color
+
+ //LEVEL STRUCTURE
+ spawn.mapRect(-3100, -100, 9200, 100)
+ spawn.mapRect(-3100, -600, 100, 500)
+ spawn.mapRect(-3100, -600, 1100, 100)
+ spawn.mapRect(-2100, -3100, 100, 2700)
+ spawn.mapRect(4800, -3100, 100, 2600)
+ spawn.mapRect(4800, -600, 1300, 100)
+ spawn.mapRect(6000, -600, 100, 600)
+
+ spawn.mapRect(400, -200, 2000, 100)
+ spawn.mapRect(600, -300, 1600, 100)
+ spawn.mapRect(800, -400, 1200, 100)
+ spawn.mapRect(1000, -500, 800, 100)
+ spawn.mapRect(1200, -600, 400, 100)
+ // roof
+ spawn.mapRect(-2100, -4350, 650, 1250)
+ spawn.mapRect(-1300, -4350, 1250, 1250)
+ spawn.mapRect(100, -4350, 1225, 1250)
+ spawn.mapRect(1475, -4350, 1325, 1250)
+ spawn.mapRect(2950, -4350, 1325, 1250)
+ spawn.mapRect(4425, -4350, 475, 1250)
+
+ // arc
+ // spawn.mapVertex(1400, -892, '700, -800, 700, -900, 1000, -1000, 1800, -1000, 2100, -900, 2100, -800')
+
+
+ //PLATFORMS
+ platformShadow(-1200, -500, 300, 100, shadows)
+ platformShadow(-400, -700, 300, 100, shadows)
+ platformShadow(400, -900, 300, 100, shadows)
+ platformShadow(-2000, -800, 300, 100, shadows)
+ platformShadow(-1000, -1000, 300, 100, shadows)
+ platformShadow(-400, -1300, 300, 100, shadows)
+ platformShadow(-1600, -1300, 300, 100, shadows)
+ platformShadow(-1300, -1600, 300, 100, shadows)
+ platformShadow(-2000, -1700, 300, 100, shadows)
+ platformShadow(-700, -1800, 300, 100, shadows)
+ platformShadow(-1500, -2100, 300, 100, shadows)
+ platformShadow(-600, -2200, 300, 100, shadows)
+ platformShadow(-2000, -2500, 300, 100, shadows)
+ platformShadow(-1100, -2400, 300, 100, shadows)
+ platformShadow(-500, -2700, 300, 100, shadows)
+ platformShadow(100, -2400, 300, 100, shadows)
+ platformShadow(700, -2700, 300, 100, shadows)
+
+ platformShadow(3700, -500, 300, 100, shadows)
+ platformShadow(2900, -700, 300, 100, shadows)
+ platformShadow(2100, -900, 300, 100, shadows)
+ platformShadow(4500, -800, 300, 100, shadows)
+ platformShadow(3500, -1000, 300, 100, shadows)
+ platformShadow(4100, -1300, 300, 100, shadows)
+ platformShadow(2900, -1300, 300, 100, shadows)
+ platformShadow(3800, -1600, 300, 100, shadows)
+ platformShadow(4500, -1700, 300, 100, shadows)
+ platformShadow(3200, -1800, 300, 100, shadows)
+ platformShadow(4000, -2100, 300, 100, shadows)
+ platformShadow(3100, -2200, 300, 100, shadows)
+ platformShadow(4500, -2500, 300, 100, shadows)
+ platformShadow(3600, -2400, 300, 100, shadows)
+ platformShadow(3000, -2700, 300, 100, shadows)
+ platformShadow(2400, -2400, 300, 100, shadows)
+ platformShadow(1800, -2700, 300, 100, shadows)
+
+ // cages
+ cage(-1492, -1200, 100, cages)
+ cage(-875, -2300, 300, cages)
+ cage(-1600, -3100, 1000, cages)
+ cage(225, -2300, 1000, cages)
+ cage(-750, -3100, 700, cages)
+ cage(-625, -1700, 1200, cages)
+ cage(2200, -3100, 500, cages)
+ cage(3275, -1700, 500, cages)
+ cage(3650, -900, 300, cages)
+ cage(2500, -2300, 300, cages)
+ cage(3625, -2300, 300, cages)
+ cage(3875, -1500, 300, cages)
+ cage(4025, -3100, 300, cages)
+
+ // boss cage
+ platformShadow(1275, -2150, 250, 100, shadows)
+ cage(1400, -2050, 500, cages, 'starter', true)
+ map[map.length] = Bodies.trapezoid(1400, -2193, 250, 100, 0.5)
+
+ //DEBRIS
+ //idk just put the debris wherever you want
+ spawn.debris(-550, -225, 100)
+ spawn.debris(-1150, -1725, 75)
+ spawn.debris(-275, -1400, 50)
+ spawn.debris(2850, -2075, 150)
+ spawn.debris(4250, -2250, 150)
+
+ //BOSS
+ // geneticBoss(1400, -3800)
+ anotherBoss(0, 0) //will only spawn historyBoss if there is an additional boss
+
+ //POWERUPS
+
+ },
// ********************************************************************************************************
// ********************************************************************************************************
diff --git a/js/player.js b/js/player.js
index d81de84..40f976d 100644
--- a/js/player.js
+++ b/js/player.js
@@ -317,6 +317,7 @@ const m = {
},
alive: false,
switchWorlds() {
+ powerUps.boost.endCycle = 0
const totalGuns = b.inventory.length
//track ammo/ ammoPack count
let ammoCount = 0
@@ -506,7 +507,7 @@ const m = {
},
baseHealth: 1,
setMaxHealth() {
- m.maxHealth = m.baseHealth + tech.extraMaxHealth + tech.isFallingDamage + 4 * tech.isFlipFlop * tech.isFlipFlopOn * tech.isFlipFlopHealth + (m.fieldMode === 0 || m.fieldMode === 5) * 0.5 * m.coupling
+ m.maxHealth = m.baseHealth + tech.extraMaxHealth + tech.isFallingDamage + 4 * tech.isFlipFlop * tech.isFlipFlopOn * tech.isFlipFlopHealth //+ (m.fieldMode === 0 || m.fieldMode === 5) * 0.5 * m.coupling
document.getElementById("health-bg").style.width = `${Math.floor(300 * m.maxHealth)}px`
simulation.makeTextLog(`m.maxHealth = ${m.maxHealth.toFixed(2)}`)
if (m.health > m.maxHealth) m.health = m.maxHealth;
@@ -658,11 +659,11 @@ const m = {
},
collisionImmuneCycles: 30,
damage(dmg) {
- if (tech.isCouplingNoHit) {
- for (let i = 0, len = tech.tech.length; i < len; i++) {
- if (tech.tech[i].name === "fine-structure constant") powerUps.ejectTech(i, true)
- }
- }
+ // if (tech.isCouplingNoHit) {
+ // for (let i = 0, len = tech.tech.length; i < len; i++) {
+ // if (tech.tech[i].name === "fine-structure constant") powerUps.ejectTech(i, true)
+ // }
+ // }
if (tech.isRewindAvoidDeath && m.energy > 0.6 && dmg > 0.01) {
const steps = Math.floor(Math.min(299, 150 * m.energy))
simulation.makeTextLog(`m.rewind(${steps})`)
@@ -1565,11 +1566,10 @@ const m = {
}
},
hold() {},
- couplingDescription(isScaled = false) {
- const couple = isScaled ? m.coupling : 1
+ couplingDescription(couple = m.coupling) {
switch (m.fieldMode) {
case 0: //field emitter
- return `gain the effects of all fields`
+ return `gain the coupling effects of all fields`
case 1: //standing wave
return `deflecting condenses +${couple.toFixed(1)} ice IX`
case 2: //perfect diamagnetism
@@ -1591,12 +1591,16 @@ const m = {
return `after eating blocks +${(20*couple).toFixed(0)} energy`
}
},
- couplingChange() {
+ couplingChange(change = 0) {
+ if (change > 0) simulation.makeTextLog(`m.coupling += ${change}`, 60);
+ m.coupling += change
+ if (m.coupling < 0) m.coupling = 0 //can't go negative
// m.setMaxEnergy();
// m.setMaxHealth();
m.setFieldRegen()
mobs.setMobSpawnHealth();
powerUps.setDupChance();
+
if ((m.fieldMode === 0 || m.fieldMode === 9) && !build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4);
// m.collisionImmuneCycles = 30 + m.coupling * 120 //2 seconds
// switch (m.fieldMode) {
@@ -2681,7 +2685,6 @@ const m = {
},
{
name: "time dilation",
- // description: "use energy to stop time
while time is stopped you can move and fire
and collisions do 50% less harm",
description: "use energy to stop time
+25% movement, jumping, and fire rate
generate 18 energy per second",
set() {
// m.fieldMeterColor = "#0fc"
@@ -2720,6 +2723,7 @@ const m = {
this.rewindCount = 0
m.grabPowerUpRange2 = 300000
m.hold = function() {
+ console.log(m.fieldCDcycle)
m.grabPowerUp();
// //grab power ups
// for (let i = 0, len = powerUp.length; i < len; ++i) {
diff --git a/js/powerup.js b/js/powerup.js
index 5552c04..6cbb28a 100644
--- a/js/powerup.js
+++ b/js/powerup.js
@@ -355,11 +355,7 @@ const powerUps = {
powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), spawnType, false);
}
}
- if (tech.isCancelCouple) {
- simulation.makeTextLog(`m.coupling += 0.5`);
- m.coupling += 0.5
- m.couplingChange()
- }
+ if (tech.isCancelCouple) powerUps.coupling.spawnDelay(5)
// if (tech.isCancelTech && Math.random() < 0.3) {
// powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "tech", false);
// simulation.makeTextLog(`options exchange: returns 1 tech`)
@@ -402,10 +398,22 @@ const powerUps = {
return 13;
},
effect() {
- simulation.makeTextLog(`m.coupling += 0.1`);
- m.coupling += 0.1
- m.couplingChange()
+ m.couplingChange(0.1)
},
+ spawnDelay(num) {
+ let count = num
+ let respawnDrones = () => {
+ if (count > 0) {
+ requestAnimationFrame(respawnDrones);
+ if (!simulation.paused && !simulation.isChoosing) { //&& !(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, "coupling");
+ }
+ }
+ }
+ requestAnimationFrame(respawnDrones);
+ }
},
boost: {
name: "boost",
@@ -429,7 +437,7 @@ const powerUps = {
ctx.lineWidth = 4
ctx.stroke();
}
- }
+ },
},
research: {
count: 0,
@@ -1069,151 +1077,20 @@ const powerUps = {
}
}
},
- // effectOld() {
- // if (m.alive) {
- // function pick(skip1 = null, skip2 = null, skip3 = null, skip4 = null) {
- // let options = [];
- // for (let i = 0; i < tech.tech.length; i++) {
- // if (tech.tech[i].count < tech.tech[i].maxCount && i !== skip1 && i !== skip2 && i !== skip3 && i !== skip4 && tech.tech[i].allowed() && !tech.tech[i].isBanished) {
- // for (let j = 0, len = tech.tech[i].frequency; j < len; j++) options.push(i);
- // }
- // }
- // powerUps.tech.lastTotalChoices = options.length //this is recorded so that banish can know how many tech were available
-
- // const totalChoices = tech.isDeterminism ? 1 : 3 + tech.extraChoices * 2
- // if (powerUps.tech.choiceLog.length > totalChoices || powerUps.tech.choiceLog.length === totalChoices) { //make sure this isn't the first time getting a power up and there are previous choices to remove
- // for (let i = 0; i < totalChoices; i++) { //repeat for each choice from the last selection
- // if (options.length > totalChoices) {
- // for (let j = 0, len = options.length; j < len; j++) {
- // if (powerUps.tech.choiceLog[powerUps.tech.length - 1 - i] === options[j]) {
- // options.splice(j, 1) //remove previous choice from option pool
- // break;
- // }
- // }
- // }
- // }
- // }
-
- // if (options.length > 0) {
- // // const choose = options[Math.floor(Math.random() * options.length)]
- // const choose = options[Math.floor(Math.seededRandom(0, options.length))]
- // const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : "";
-
- // if (tech.tech[choose].isFieldTech) {
- // text += `
- //
- //
- //
- //
- // ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() :tech.tech[choose].description}
`
- // } else if (tech.tech[choose].isGunTech) {
- // text += `
- //
- //
- //
- //
- // ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() :tech.tech[choose].description}
`
- // } else if (tech.tech[choose].isLore) {
- // text += ` ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
`
- // } else if (tech.tech[choose].isJunk) {
- // text += ` ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
`
- // } else {
- // text += ` ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
`
- // }
-
- // // text += ` ${tech.tech[choose].name}
${tech.tech[choose].description}
`
- // return choose
- // } else if (tech.isBanish) { //if no tech options available eject banish tech
- // for (let i = 0, len = tech.tech.length; i < len; i++) {
- // if (tech.tech[i].name === "decoherence") powerUps.ejectTech(i)
- // }
- // }
- // }
-
-
- // let text = ""
- // if (!tech.isSuperDeterminism) text += `${tech.isCancelTech ? "?":"✕"}
`
- // text += `tech
`
- // let choice1 = pick()
- // // console.log(choice1)
- // let choice2 = null
- // let choice3 = null
- // if (choice1 !== null) {
- // powerUps.tech.choiceLog.push(choice1)
- // if (!tech.isDeterminism) {
- // choice2 = pick(choice1)
- // // if (choice2 > -1) text += ` ${tech.tech[choice2].name}
${tech.tech[choice2].description}
`
- // choice3 = pick(choice1, choice2)
- // // if (choice3 > -1) text += ` ${tech.tech[choice3].name}
${tech.tech[choice3].description}
`
- // powerUps.tech.choiceLog.push(choice2)
- // powerUps.tech.choiceLog.push(choice3)
- // }
- // if (tech.extraChoices) {
- // let choice4 = pick(choice1, choice2, choice3)
- // // if (choice4 > -1) text += ` ${tech.tech[choice4].name}
${tech.tech[choice4].description}
`
- // let choice5 = pick(choice1, choice2, choice3, choice4)
- // // if (choice5 > -1) text += ` ${tech.tech[choice5].name}
${tech.tech[choice5].description}
`
- // powerUps.tech.choiceLog.push(choice4)
- // powerUps.tech.choiceLog.push(choice5)
- // }
- // // if (powerUps.research.count) text += ` research
${powerUps.research.count} `
-
- // if (tech.isExtraGunField) {
- // if (Math.random() > 0.5 && b.inventory.length < b.guns.length) {
- // //bonus gun in tech menu
- // let choiceGun = powerUps.gun.pick(b.guns)
- // powerUps.gun.choiceLog.push(choiceGun)
- // text += ` ${b.guns[choiceGun].name}
${b.guns[choiceGun].description}
`
- // } else {
- // //bonus field in tech menu
- // let choiceField = powerUps.field.pick(m.fieldUpgrades)
- // powerUps.field.choiceLog.push(choiceField)
- // text += ` ${m.fieldUpgrades[choiceField].name}
${m.fieldUpgrades[choiceField].description}
`
- // }
- // }
- // if (tech.tooManyTechChoices) {
- // tech.tooManyTechChoices--
- // for (let i = 0; i < powerUps.tech.lastTotalChoices; i++) pick()
- // }
-
- // if (tech.isBrainstorm && !tech.isBrainstormActive && !simulation.isChoosing) {
- // tech.isBrainstormActive = true
- // let count = 0
- // requestAnimationFrame(cycle);
-
- // function cycle() {
- // count++
- // if (count < tech.brainStormDelay * 5 && simulation.isChoosing) {
- // if (!(count % tech.brainStormDelay)) {
- // powerUps.tech.effect();
- // document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay
- // document.body.style.cursor = "auto";
- // document.getElementById("choose-grid").style.transitionDuration = "0s";
- // }
- // requestAnimationFrame(cycle);
- // } else {
- // tech.isBrainstormActive = false
- // }
- // }
- // }
-
- // //add in research button or pseudoscience button
- // if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) {
- // tech.junkResearchNumber = Math.floor(5 * Math.random())
- // text += ` `
- // for (let i = 0; i < tech.junkResearchNumber; i++) text += ``
- // text += ` pseudoscience `
- // } else if (powerUps.research.count) {
- // text += ` `
- // for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += ``
- // // text += ` research `
- // text += ` ${tech.isResearchReality?"alternate reality": "research"}`
- // }
- // document.getElementById("choose-grid").innerHTML = text
- // powerUps.showDraft();
- // }
- // }
- // }
+ },
+ spawnDelay(type, num) {
+ let count = num
+ let cycle = () => {
+ if (count > 0) {
+ requestAnimationFrame(cycle);
+ if (!simulation.paused && !simulation.isChoosing) { //&& !(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);
+ }
+ }
+ }
+ requestAnimationFrame(cycle);
},
onPickUp(who) {
powerUps.research.currentRerollCount = 0
@@ -1231,25 +1108,30 @@ const powerUps = {
tech.isFlipFlopOn = false
if (document.getElementById("tech-switch")) document.getElementById("tech-switch").innerHTML = ` = OFF`
m.eyeFillColor = 'transparent'
+ if (tech.isFlipFlopCoupling) {
+ m.couplingChange(-5)
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ spawn.WIMP()
+ mob[mob.length - 1].isDecoupling = true //so you can find it to remove
+ }
} else {
tech.isFlipFlopOn = true //immune to damage this hit, lose immunity for next hit
if (document.getElementById("tech-switch")) document.getElementById("tech-switch").innerHTML = ` = ON`
m.eyeFillColor = m.fieldMeterColor //'#0cf'
+ if (tech.isFlipFlopCoupling) {
+ m.couplingChange(5)
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ }
}
if (tech.isRelayEnergy) m.setMaxEnergy();
}
},
- // giveRandomAmmo() {
- // const ammoTarget = Math.floor(Math.random() * (b.guns.length));
- // const ammo = Math.ceil(b.guns[ammoTarget].ammoPack * 6);
- // if (ammo !== Infinity) {
- // b.guns[ammoTarget].ammo += ammo;
- // simulation.updateGunHUD();
- // simulation.makeTextLog(`${b.guns[ammoTarget].name}.ammo += ${ammo}`);
- // }
- // },
spawnRandomPowerUp(x, y) { //mostly used after mob dies, doesn't always return a power up
- if ((Math.random() * Math.random() - 0.3 > Math.sqrt(m.health) && !tech.isEnergyHealth) || Math.random() < 0.04) { //spawn heal chance is higher at low health
+ if (!tech.isEnergyHealth && (Math.random() * Math.random() - 0.3 > Math.sqrt(m.health)) || Math.random() < 0.04) { //spawn heal chance is higher at low health
powerUps.spawn(x, y, "heal");
return;
}
diff --git a/js/simulation.js b/js/simulation.js
index 77554cf..645c294 100644
--- a/js/simulation.js
+++ b/js/simulation.js
@@ -775,6 +775,7 @@ const simulation = {
powerUps.field.choiceLog = [];
powerUps.totalPowerUps = 0;
powerUps.research.count = 0;
+ powerUps.boost.endCycle = 0
m.setFillColors();
// m.maxHealth = 1
// m.maxEnergy = 1
diff --git a/js/spawn.js b/js/spawn.js
index 4814e70..18719d1 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -340,7 +340,7 @@ const spawn = {
}
setTimeout(() => {
this.do = this.awake;
- }, 500 + 2000 * Math.random());
+ }, 1000 * Math.random());
}
this.checkStatus();
};
@@ -2710,7 +2710,6 @@ const spawn = {
flutter(x, y, radius = 20 + 6 * Math.random()) {
mobs.spawn(x, y, 7, radius, '#16576b');
let me = mob[mob.length - 1];
- me.isBoss = true;
Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger
// me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
diff --git a/js/tech.js b/js/tech.js
index 29260a1..c856c45 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -57,7 +57,7 @@ const tech = {
}
if (tech.tech[index].count === 0) return 0
const totalRemoved = tech.tech[index].count
- simulation.makeTextLog(`tech.removeTech("${tech.tech[index].name}")`)
+ simulation.makeTextLog(`tech.removeTech("${tech.tech[index].name}")`, 360)
tech.tech[index].remove();
tech.tech[index].count = 0;
tech.totalCount -= totalRemoved
@@ -295,9 +295,7 @@ const tech = {
isNonRefundable: true,
// isExperimentHide: true,
isBadRandomOption: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
powerUps.spawn(m.pos.x, m.pos.y, "gun");
@@ -367,7 +365,7 @@ const tech = {
const index = Math.floor(Math.random() * gunTechPool.length)
tech.giveTech(gunTechPool[index]) // choose from the gun pool
tech.tech[gunTechPool[index]].isFromAppliedScience = true //makes it not remove properly under paradigm shift
- simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`)
+ simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360)
}
}
simulation.boldActiveGunHUD();
@@ -381,9 +379,7 @@ const tech = {
count: 0,
frequency: 1,
frequencyDefault: 1,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.isDamageForGuns = true;
@@ -399,9 +395,7 @@ const tech = {
count: 0,
frequency: 1,
frequencyDefault: 1,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.isFireRateForGuns = true;
@@ -605,9 +599,7 @@ const tech = {
count: 0,
frequency: 1,
frequencyDefault: 1,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.crouchAmmoCount = true
@@ -870,9 +862,7 @@ const tech = {
count: 0,
frequency: 1,
frequencyDefault: 1,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.isBulletsLastLonger += 0.3
@@ -989,9 +979,7 @@ const tech = {
count: 0,
frequency: 1,
frequencyDefault: 1,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.deathSkipTime++
@@ -1008,9 +996,9 @@ const tech = {
frequency: 1,
frequencyDefault: 1,
allowed() {
- return !tech.isEnergyHealth
+ return !tech.isEnergyHealth && !tech.isRewindField
},
- requires: "not mass-energy",
+ requires: "not mass-energy, retrocausality",
effect() {
tech.isNoFireDefense = true
},
@@ -1026,9 +1014,9 @@ const tech = {
frequency: 1,
frequencyDefault: 1,
allowed() {
- return true
+ return !tech.isRewindField
},
- requires: "",
+ requires: "not retrocausality",
effect() {
tech.isNoFireDamage = true
},
@@ -1856,14 +1844,30 @@ const tech = {
requires: "not relay switch",
effect() {
tech.isFlipFlop = true //do you have this tech?
- tech.isFlipFlopOn = true //what is the state of flip-Flop?
+ if (!tech.isFlipFlopOn) {
+ tech.isFlipFlopOn = true //what is the state of flip-Flop?
+ if (tech.isFlipFlopCoupling) {
+ m.couplingChange(5)
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ }
+ }
if (!m.isShipMode) {
m.draw = m.drawFlipFlop
}
},
remove() {
tech.isFlipFlop = false
- tech.isFlipFlopOn = false
+ if (tech.isFlipFlopOn) {
+ tech.isFlipFlopOn = false //what is the state of flip-Flop?
+ if (tech.isFlipFlopCoupling) {
+ m.couplingChange(5)
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ }
+ }
m.eyeFillColor = 'transparent'
}
},
@@ -1921,6 +1925,47 @@ const tech = {
tech.isFlipFlopEnergy = false;
}
},
+ {
+ name: "decoupling",
+ link: `decoupling`,
+ descriptionFunction() {
+ //(${ m.couplingDescription(this.bonus)})
+ return `if ON +5 coupling
if OFF a dangerous particle slowly chases you`
+ },
+ maxCount: 1,
+ count: 0,
+ frequency: 4,
+ frequencyDefault: 4,
+ bonus: 5, //coupling given
+ allowed() {
+ return tech.isFlipFlop || tech.isRelay
+ },
+ requires: "ON/OFF tech",
+ effect() {
+ tech.isFlipFlopCoupling = true;
+ if (tech.isFlipFlopOn) {
+ m.couplingChange(this.bonus)
+ } else {
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ spawn.WIMP()
+ mob[mob.length - 1].isDecoupling = true //so you can find it to remove
+ }
+ },
+ remove() {
+ tech.isFlipFlopCoupling = false;
+ if (tech.isFlipFlop || tech.isRelay) {
+ if (tech.isFlipFlopOn) {
+ m.couplingChange(-this.bonus)
+ } else {
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ }
+ }
+ }
+ },
{
name: "relay switch",
description: `toggle ON and OFF after picking up a power up
unlock advanced tech that runs if ON`,
@@ -1948,14 +1993,30 @@ const tech = {
requires: "not flip-flop",
effect() {
tech.isRelay = true //do you have this tech?
- tech.isFlipFlopOn = true //what is the state of flip-Flop?
+ if (!tech.isFlipFlopOn) {
+ tech.isFlipFlopOn = true //what is the state of flip-Flop?
+ if (tech.isFlipFlopCoupling) {
+ m.couplingChange(5)
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ }
+ }
if (!m.isShipMode) {
m.draw = m.drawFlipFlop
}
},
remove() {
tech.isRelay = false
- tech.isFlipFlopOn = false
+ if (tech.isFlipFlopOn) {
+ tech.isFlipFlopOn = false //what is the state of flip-Flop?
+ if (tech.isFlipFlopCoupling) {
+ m.couplingChange(-5)
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP
+ }
+ }
+ }
m.eyeFillColor = 'transparent'
}
},
@@ -2081,9 +2142,7 @@ const tech = {
count: 0,
frequency: 1,
frequencyDefault: 1,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.isDroneOnDamage = true;
@@ -2376,9 +2435,7 @@ const tech = {
count: 0,
frequency: 1,
frequencyDefault: 1,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.isLowEnergyDamage = true;
@@ -3304,9 +3361,7 @@ const tech = {
count: 0,
frequency: 1,
frequencyDefault: 1,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.isBoostPowerUps = true
@@ -3406,7 +3461,7 @@ const tech = {
{
name: "field coupling",
descriptionFunction() {
- return `+1 coupling (${m.fieldUpgrades[m.fieldMode].name})
${ m.couplingDescription()} ${m.fieldMode === 0 ? "" : "per coupling"}`
+ return `spawn ${powerUps.orb.coupling(10)}
that each give +0.1 coupling
${ m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}`
},
maxCount: 9,
count: 0,
@@ -3417,19 +3472,27 @@ const tech = {
},
requires: "",
effect() {
- simulation.makeTextLog(`m.coupling += 1`);
- m.coupling += 1
- m.couplingChange()
+ powerUps.spawnDelay("coupling", 10)
},
remove() {
- m.coupling -= this.count
- m.couplingChange()
+ if (this.count) {
+ m.couplingChange(-this.count)
+ }
}
},
{
name: "quintessence",
descriptionFunction() {
- return `use all your ${powerUps.orb.research(1)} to get +${this.count ? this.researchUsed*this.couplingToResearch:powerUps.research.count*this.couplingToResearch} coupling
${ m.couplingDescription()} ${m.fieldMode === 0 ? "" : "per coupling"}`
+ let converted = powerUps.research.count * this.couplingToResearch
+ if (this.count) converted = this.researchUsed * this.couplingToResearch
+
+ let orbText
+ if (converted > 20) {
+ orbText = `${converted} ${powerUps.orb.coupling()}`
+ } else {
+ orbText = powerUps.orb.coupling(converted)
+ }
+ return `use all your ${powerUps.orb.research(1)} to spawn ${orbText}
that each give +0.1 coupling
${ m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}`
},
maxCount: 1,
count: 0,
@@ -3442,35 +3505,32 @@ const tech = {
researchUsed: 0,
couplingToResearch: 0.25,
effect() {
+ let count = 0
while (powerUps.research.count > 0) {
powerUps.research.changeRerolls(-1)
+ count += 10
this.researchUsed++
- simulation.makeTextLog(`m.coupling += ${(this.couplingToResearch).toFixed(2)}`);
- m.coupling += this.couplingToResearch
}
- m.couplingChange()
+ powerUps.spawnDelay("coupling", count)
},
remove() {
if (this.count) {
- m.coupling -= this.researchUsed * this.couplingToResearch
+ m.couplingChange(-this.researchUsed * this.couplingToResearch)
powerUps.research.changeRerolls(this.researchUsed)
this.researchUsed = 0
}
- m.couplingChange()
}
},
{
name: "virtual particles",
descriptionFunction() {
- return `after mobs die they have a 17% chance to
spawn ${powerUps.orb.coupling(1)} that give +0.1 coupling`
+ return `after mobs die they have a 17% chance to
spawn ${powerUps.orb.coupling(1)} that each give +0.1 coupling
${ m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}`
},
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.isCouplingPowerUps = true //about 20-30 mobs per level so at 16% and 0.1 coupling that's about 25 * 0.16 * 0.1 = 0.4 coupling per level with out duplication
@@ -3482,38 +3542,35 @@ const tech = {
{
name: "fine-structure constant",
descriptionFunction() {
- return `+${this.value} coupling, eject this tech after losing health
${ m.couplingDescription()} ${m.fieldMode === 0 ? "" : "per coupling"}`
+ return `spawn ${this.value} ${powerUps.orb.coupling(1)} that each give +0.1 coupling
+
-0.5 coupling after mob collisions
+
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}`
},
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 100,
- allowed() {
- return true
- },
- value: 6,
+ isNonRefundable: true,
+ allowed: () => true,
+ value: 60,
requires: "",
effect() {
tech.isCouplingNoHit = true
- simulation.makeTextLog(`m.coupling += ${(this.value).toFixed(1)}`);
- m.coupling += this.value
- m.couplingChange()
- this.maxCount = 0
+ powerUps.spawnDelay("coupling", this.value)
},
remove() {
- if (this.count) {
- m.coupling -= this.value
- m.couplingChange()
- } else {
- this.maxCount = 1
- }
+ // if (this.count) {
+ // m.couplingChange(-this.value)
+ // } else {
+ // this.maxCount = 1 //reset only take this once per game
+ // }
tech.isCouplingNoHit = false
}
},
{
name: "residual dipolar coupling",
descriptionFunction() {
- return `clicking × to cancel yields +0.5 coupling
${ m.couplingDescription()} ${m.fieldMode === 0 ? "" : "per coupling"}`
+ return `clicking × to cancel a field, tech, or gun
spawns ${powerUps.orb.coupling(5)}that each give +0.1 coupling
${ m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}`
},
maxCount: 1,
count: 0,
@@ -3530,6 +3587,24 @@ const tech = {
tech.isCancelCouple = false
}
},
+ {
+ name: "commodities exchange",
+ description: `clicking × to cancel a field, tech, or gun
spawns 5-10 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`,
+ maxCount: 1,
+ count: 0,
+ frequency: 1,
+ frequencyDefault: 1,
+ allowed() {
+ return !tech.isSuperDeterminism
+ },
+ requires: "not superdeterminism",
+ effect() {
+ tech.isCancelRerolls = true
+ },
+ remove() {
+ tech.isCancelRerolls = false
+ }
+ },
{
name: "options exchange",
link: `options exchange`,
@@ -3549,24 +3624,6 @@ const tech = {
tech.isCancelTech = false
}
},
- {
- name: "commodities exchange",
- description: `clicking × to cancel a field, tech, or gun
spawns 5-10 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`,
- maxCount: 1,
- count: 0,
- frequency: 1,
- frequencyDefault: 1,
- allowed() {
- return !tech.isSuperDeterminism
- },
- requires: "not superdeterminism",
- effect() {
- tech.isCancelRerolls = true
- },
- remove() {
- tech.isCancelRerolls = false
- }
- },
{
name: "futures exchange",
description: "clicking × to cancel a field, tech, or gun
gives +4.5% power up duplication chance",
@@ -3797,7 +3854,7 @@ const tech = {
if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i)
}
const choose = have[Math.floor(Math.random() * have.length)]
- simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`)
+ simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`, 360)
for (let i = 0; i < tech.tech[choose].count; i++) {
powerUps.spawn(m.pos.x, m.pos.y, "gun");
}
@@ -6217,9 +6274,9 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun)
+ return tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun) && !tech.isEnergyNoAmmo
},
- requires: "harpoon, laser, not railgun",
+ requires: "harpoon, laser, not railgun, non-renewables",
effect() {
tech.isBoostReplaceAmmo = true
for (let i = powerUp.length - 1; i > -1; i--) {
@@ -6272,7 +6329,7 @@ const tech = {
//pick one option
if (options.length) {
const index = options[Math.floor(Math.random() * options.length)]
- simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //optical amplifier`);
+ simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //optical amplifier`, 360);
tech.giveTech(index)
techGiven++
}
@@ -7647,7 +7704,7 @@ const tech = {
},
{
name: "WIMPs",
- description: `at the end of each level spawn ${powerUps.orb.research(5)}
and a harmful particle that slowly chases you`,
+ description: `at the end of each level spawn ${powerUps.orb.research(5)}
and a dangerous particle that slowly chases you`,
isFieldTech: true,
maxCount: 9,
count: 0,
@@ -8041,6 +8098,41 @@ const tech = {
// },
// remove() {}
// },
+ {
+ name: "boost",
+ maxCount: 1,
+ count: 0,
+ frequency: 0,
+ isJunk: true,
+ allowed() {
+ return !build.isExperimentSelection
+ },
+ requires: "NOT EXPERIMENT MODE",
+ effect() {
+ powerUps.spawnDelay("boost", this.spawnCount)
+ },
+ remove() {},
+ id: 0,
+ text: "",
+ delay: 100,
+ spawnCount: 0,
+ descriptionFunction() {
+ let count = 9999 * Math.random()
+ const loop = () => {
+ if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) //simulation.paused ||
+ count += 4.5
+ const waves = 2 * Math.sin(count * 0.0133) + Math.sin(count * 0.013) + 0.5 * Math.sin(count * 0.031) + 0.33 * Math.sin(count * 0.03)
+ this.spawnCount = Math.floor(100 * Math.abs(waves))
+ this.text = `spawn ${this.spawnCount.toLocaleString(undefined, {minimumIntegerDigits:3})} ${powerUps.orb.boost(1)}
that give +${(powerUps.boost.damage*100).toFixed(0)}% damage for ${(powerUps.boost.duration/60).toFixed(0)} seconds`
+ if (document.getElementById(`boost-JUNK-id${this.id}`)) document.getElementById(`boost-JUNK-id${this.id}`).innerHTML = this.text
+ setTimeout(() => { loop() }, this.delay);
+ }
+ }
+ setTimeout(() => { loop() }, this.delay);
+ this.id++
+ return `${this.text}`
+ },
+ },
{
name: "return",
description: "return to the introduction level
reduce combat difficulty by 2 levels",
@@ -8049,9 +8141,7 @@ const tech = {
frequency: 0,
isJunk: true,
isNonRefundable: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
level.difficultyDecrease(simulation.difficultyMode * 2)
@@ -8068,9 +8158,7 @@ const tech = {
frequency: 0,
isJunk: true,
isNonRefundable: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
setInterval(() => {
@@ -8094,9 +8182,7 @@ const tech = {
frequency: 0,
isJunk: true,
isNonRefundable: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
setInterval(() => {
@@ -8136,9 +8222,7 @@ const tech = {
frequency: 0,
isJunk: true,
isNonRefundable: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field");
@@ -8215,9 +8299,7 @@ const tech = {
frequency: 0,
isJunk: true,
isNonRefundable: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.giveRandomJUNK()
@@ -8253,9 +8335,7 @@ const tech = {
frequencyDefault: 0,
isJunk: true,
isNonRefundable: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
powerUps.spawn(m.pos.x, m.pos.y, "tech");
@@ -8289,9 +8369,7 @@ const tech = {
frequencyDefault: 0,
isJunk: true,
isNonRefundable: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
for (let i = 0, len = mob.length; i < len; i++) {
@@ -8317,9 +8395,7 @@ const tech = {
frequency: 0,
frequencyDefault: 0,
isJunk: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
@@ -8336,9 +8412,7 @@ const tech = {
frequency: 0,
frequencyDefault: 0,
isJunk: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
tech.isBrainstorm = true
@@ -9982,9 +10056,7 @@ const tech = {
count: 0,
frequency: 0,
isJunk: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {},
remove() {},
@@ -10046,9 +10118,7 @@ const tech = {
count: 0,
frequency: 0,
isJunk: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {},
remove() {},
@@ -10191,9 +10261,7 @@ const tech = {
frequency: 0,
isJunk: true,
isNonRefundable: true,
- allowed() {
- return true
- },
+ allowed: () => true,
requires: "",
effect() {
localSettings.personalSeeds.push(Math.initialSeed)
@@ -10672,5 +10740,6 @@ const tech = {
isCancelCouple: null,
isCouplingPowerUps: null,
isBoostPowerUps: null,
- isBoostReplaceAmmo: null
+ isBoostReplaceAmmo: null,
+ isFlipFlopCoupling: null
}
\ No newline at end of file
diff --git a/todo.txt b/todo.txt
index 88050d7..ceb3560 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,21 +1,31 @@
******************************************************** NEXT PATCH **************************************************
-some shotgun ammo tech upgrades will continue to fire some original recipe shotgun bullets
- rivets, fleas, worms, iceIX
-tech: band gap - boosts give more damage but it lasts for 1 less second
+new level biohazard by INOOBBOI AND THESHWARMA
+ enable community maps in settings
-WIMPs are 10% faster
-controlled explosion renamed shaped charge
+some coupling tech spawns power ups instead of directly giving coupling
+fine-structure constant gives 60 coupling power ups, lose 0.5 coupling after mob collision
+tech: decoupling: when ON: +5.00 coupling OFF: spawn a WIMP
+ (probably adds coupling drift bugs)
+
+JUNK tech - boost - spawn a large number of boost power ups
-bug fixes
- construction mode works better with my buttons
- to unlock run this and press T to enter testing mode
- simulation.enableConstructMode() //used to build maps in testing mode
-
-removed -experiment- tech because it's function was reproduced by "tech - tinker"
+several bug fixes
+ mob - flutter is no longer treated as a boss
*********************************************************** TODO *****************************************************
+look for other tech that would benefit from a 3rd line of description text
+
+tech for lens - you can only fire through the lens
+ and some buff? damage or energy?
+
+add link to russian physics notes
+
+suggestion: if you have both laser-mines and lens, each laser-mine is given its own lens revolving around it
+
+hopMom fight make platforming with hop bullets harder?
+
complete blowSuckBoss... or don't
tech: laser reflections increase damage