diff --git a/js/bullet.js b/js/bullet.js
index 6480b49..7c6497f 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -2281,8 +2281,9 @@ const b = {
botType: "dynamo",
friction: 0,
frictionStatic: 0,
- frictionAir: 1,
- isStatic: true,
+ frictionAir: 0.02,
+ spin: 0.07 * (Math.random() < 0.5 ? -1 : 1),
+ // isStatic: true,
isSensor: true,
restitution: 0,
dmg: 0, // 0.14 //damage done in addition to the damage from momentum
@@ -2308,7 +2309,7 @@ const b = {
// }
if (!((m.cycle + this.phase) % 30)) { //twice a second
if (Vector.magnitude(Vector.sub(this.position, m.pos)) < 250) { //give energy
- // Matter.Body.setAngularVelocity(this, 10)
+ Matter.Body.setAngularVelocity(this, this.spin)
if (this.isUpgraded) {
m.energy += 0.06
simulation.drawList.push({ //add dmg to draw queue
@@ -2344,6 +2345,7 @@ const b = {
}
})
for (let i = 0; i < q.length; i++) {
+ Matter.Body.setAngularVelocity(this, this.spin)
// mobs.statusStun(q[i], 180)
// const dmg = 0.5 * b.dmgScale * (this.isUpgraded ? 2.5 : 1)
const dmg = 0.5 * b.dmgScale
diff --git a/js/index.js b/js/index.js
index 15c0cff..241e3e3 100644
--- a/js/index.js
+++ b/js/index.js
@@ -65,6 +65,7 @@ window.addEventListener('load', (event) => {
//add experimental selections based on url
for (const property in set) {
set[property] = set[property].replace(/%20/g, " ")
+ set[property] = set[property].replace(/%27/g, "'")
set[property] = set[property].replace(/%CE%A8/g, "Ψ")
if (property === "field") {
let found = false
@@ -744,45 +745,45 @@ window.addEventListener("keydown", function(event) {
tech.removeLoreTechFromPool();
}
simulation.makeTextLog(
- `
+ `
- | T |
- enter / exit testing mode |
+ T |
+ toggle testing |
- | R |
+ R |
teleport to mouse |
- | F |
+ F |
cycle field |
- | G |
+ G |
all guns |
- | H |
- fill health and energy |
+ H |
+ fill health, energy |
- | Y |
+ Y |
random tech |
- | U |
+ U |
next level |
- | I/O |
+ I/O |
zoom in / out |
- | 1-8 |
+ 1-8 |
spawn things |
- | ⇧X |
+ ⇧X |
restart |
`, Infinity);
diff --git a/js/level.js b/js/level.js
index 57f0f95..d9e475c 100644
--- a/js/level.js
+++ b/js/level.js
@@ -108,6 +108,765 @@ const level = {
},
custom() {},
customTopLayer() {},
+ difficultyIncrease(num = 1) {
+ for (let i = 0; i < num; i++) {
+ simulation.difficulty++
+ b.dmgScale *= 0.93; //damage done by player decreases each level
+ if (simulation.accelScale < 5) simulation.accelScale *= 1.02 //mob acceleration increases each level
+ if (simulation.lookFreqScale > 0.2) simulation.lookFreqScale *= 0.98 //mob cycles between looks decreases each level
+ if (simulation.CDScale > 0.2) simulation.CDScale *= 0.97 //mob CD time decreases each level
+ }
+ simulation.dmgScale = 0.378 * simulation.difficulty //damage done by mobs increases each level
+ simulation.healScale = 1 / (1 + simulation.difficulty * 0.06) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale;
+ },
+ difficultyDecrease(num = 1) { //used in easy mode for simulation.reset()
+ for (let i = 0; i < num; i++) {
+ simulation.difficulty--
+ b.dmgScale /= 0.93; //damage done by player decreases each level
+ if (simulation.accelScale > 0.2) simulation.accelScale /= 1.02 //mob acceleration increases each level
+ if (simulation.lookFreqScale < 5) simulation.lookFreqScale /= 0.98 //mob cycles between looks decreases each level
+ if (simulation.CDScale < 5) simulation.CDScale /= 0.97 //mob CD time decreases each level
+ }
+ if (simulation.difficulty < 1) simulation.difficulty = 0;
+ simulation.dmgScale = 0.378 * simulation.difficulty //damage done by mobs increases each level
+ if (simulation.dmgScale < 0.1) simulation.dmgScale = 0.1;
+ simulation.healScale = 1 / (1 + simulation.difficulty * 0.06)
+ },
+ difficultyText() {
+ if (simulation.difficultyMode === 1) {
+ return "easy"
+ } else if (simulation.difficultyMode === 2) {
+ return "normal"
+ } else if (simulation.difficultyMode === 4) {
+ return "hard"
+ } else if (simulation.difficultyMode === 6) {
+ return "why"
+ }
+ },
+ levelAnnounce() {
+ if (level.levelsCleared === 0) {
+ document.title = "n-gon: (" + level.difficultyText() + ")";
+ } else {
+ document.title = (simulation.isCheating ? "∅ " : "n-gon:") + (level.levelsCleared) + " " + level.levels[level.onLevel] + " (" + level.difficultyText() + ")";
+ simulation.makeTextLog(`level.onLevel = "${level.levels[level.onLevel]}"`);
+ }
+ // simulation.makeTextLog(`
+ // input.key.up = ["${input.key.up}", "ArrowUp"]
+ //
input.key.left = ["${input.key.left}", "ArrowLeft"]
+ //
input.key.down = ["${input.key.down}", "ArrowDown"]
+ //
input.key.right = ["${input.key.right}", "ArrowRight"]
+ //
+ //
m.fieldMode = "${m.fieldUpgrades[m.fieldMode].name}"
+ //
input.key.field = ["${input.key.field}", "right mouse"]
+ //
m.field.description = "${m.fieldUpgrades[m.fieldMode].description}"
+ // `, 1200);
+ },
+ nextLevel() {
+ level.levelsCleared++;
+ // level.difficultyIncrease(simulation.difficultyMode) //increase difficulty based on modes
+
+ //difficulty is increased 5 times when finalBoss dies
+ // const len = level.levelsCleared / level.levels.length //add 1 extra difficulty step for each time you have cleared all the levels
+ // for (let i = 0; i < len; i++)
+ level.difficultyIncrease(simulation.difficultyMode)
+
+ level.onLevel++; //cycles map to next level
+ if (level.onLevel > level.levels.length - 1) level.onLevel = 0;
+ //reset lost tech display
+ for (let i = 0; i < tech.tech.length; i++) {
+ if (tech.tech[i].isLost) tech.tech[i].isLost = false;
+ }
+ tech.isDeathAvoidedThisLevel = false;
+ simulation.updateTechHUD();
+ simulation.clearNow = true; //triggers in simulation.clearMap to remove all physics bodies and setup for new map
+ },
+ playerExitCheck() {
+ 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.nextLevel()
+ }
+ },
+ setPosToSpawn(xPos, yPos) {
+ m.spawnPos.x = m.pos.x = xPos;
+ m.spawnPos.y = m.pos.y = yPos;
+ level.enter.x = m.spawnPos.x - 50;
+ level.enter.y = m.spawnPos.y + 20;
+ m.transX = m.transSmoothX = canvas.width2 - m.pos.x;
+ m.transY = m.transSmoothY = canvas.height2 - m.pos.y;
+ m.Vx = m.spawnVel.x;
+ m.Vy = m.spawnVel.y;
+ player.force.x = 0;
+ player.force.y = 0;
+ Matter.Body.setPosition(player, m.spawnPos);
+ Matter.Body.setVelocity(player, m.spawnVel);
+ },
+ enter: {
+ x: 0,
+ y: 0,
+ draw() {
+ 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 = "#ccc";
+ ctx.fill();
+ }
+ },
+ exit: {
+ x: 0,
+ y: 0,
+ draw() {
+ ctx.beginPath();
+ ctx.moveTo(level.exit.x, level.exit.y + 30);
+ ctx.lineTo(level.exit.x, level.exit.y - 80);
+ ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80);
+ ctx.lineTo(level.exit.x + 100, level.exit.y + 30);
+ ctx.lineTo(level.exit.x, level.exit.y + 30);
+ ctx.fillStyle = "#0ff";
+ ctx.fill();
+ }
+ },
+ fillBG: [],
+ drawFillBGs() {
+ for (let i = 0, len = level.fillBG.length; i < len; ++i) {
+ const f = level.fillBG[i];
+ ctx.fillStyle = f.color;
+ ctx.fillRect(f.x, f.y, f.width, f.height);
+ }
+ },
+ fill: [],
+ drawFills() {
+ for (let i = 0, len = level.fill.length; i < len; ++i) {
+ const f = level.fill[i];
+ ctx.fillStyle = f.color;
+ ctx.fillRect(f.x, f.y, f.width, f.height);
+ }
+ },
+ queryList: [], //queries do actions on many objects in regions
+ checkQuery() {
+ let bounds, action, info;
+
+ function isInZone(targetArray) {
+ let results = Matter.Query.region(targetArray, bounds);
+ for (let i = 0, len = results.length; i < len; ++i) {
+ level.queryActions[action](results[i], info);
+ }
+ }
+ for (let i = 0, len = level.queryList.length; i < len; ++i) {
+ bounds = level.queryList[i].bounds;
+ action = level.queryList[i].action;
+ info = level.queryList[i].info;
+ for (let j = 0, l = level.queryList[i].groups.length; j < l; ++j) {
+ isInZone(level.queryList[i].groups[j]);
+ }
+ }
+ },
+ //oddly query regions can't get smaller than 50 width?
+ addQueryRegion(x, y, width, height, action, groups = [
+ [player], body, mob, powerUp, bullet
+ ], info) {
+ level.queryList[level.queryList.length] = {
+ bounds: {
+ min: {
+ x: x,
+ y: y
+ },
+ max: {
+ x: x + width,
+ y: y + height
+ }
+ },
+ action: action,
+ groups: groups,
+ info: info
+ };
+ },
+ queryActions: {
+ bounce(target, info) {
+ //jerky fling upwards
+ Matter.Body.setVelocity(target, {
+ x: info.Vx + (Math.random() - 0.5) * 6,
+ y: info.Vy
+ });
+ target.torque = (Math.random() - 0.5) * 2 * target.mass;
+ },
+ boost(target, yVelocity) {
+ m.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts
+ m.hardLandCD = 0 // disable hard landing
+ if (target.velocity.y > 30) {
+ Matter.Body.setVelocity(target, {
+ x: target.velocity.x + (Math.random() - 0.5) * 2,
+ y: -15 //gentle bounce if coming down super fast
+ });
+ } else {
+ Matter.Body.setVelocity(target, {
+ x: target.velocity.x + (Math.random() - 0.5) * 2,
+ y: yVelocity
+ });
+ }
+
+ },
+ force(target, info) {
+ if (target.velocity.y < 0) { //gently force up if already on the way up
+ target.force.x += info.Vx * target.mass;
+ target.force.y += info.Vy * target.mass;
+ } else {
+ target.force.y -= 0.0007 * target.mass; //gently fall in on the way down
+ }
+ },
+ antiGrav(target) {
+ target.force.y -= 0.0011 * target.mass;
+ },
+ death(target) {
+ target.death();
+ }
+ },
+ addToWorld() { //needs to be run to put bodies into the world
+ for (let i = 0; i < body.length; i++) {
+ //body[i].collisionFilter.group = 0;
+ if (body[i] !== m.holdingTarget) {
+ body[i].collisionFilter.category = cat.body;
+ body[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
+ }
+ body[i].classType = "body";
+ World.add(engine.world, body[i]); //add to world
+ }
+ for (let i = 0; i < map.length; i++) {
+ //map[i].collisionFilter.group = 0;
+ map[i].collisionFilter.category = cat.map;
+ map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
+ Matter.Body.setStatic(map[i], true); //make static
+ World.add(engine.world, map[i]); //add to world
+ }
+ // for (let i = 0; i < cons.length; i++) {
+ // World.add(engine.world, cons[i]);
+ // }
+
+ },
+ spinner(x, y, width, height, density = 0.001) {
+ x = x + width / 2
+ y = y + height / 2
+ const who = body[body.length] = Bodies.rectangle(x, y, width, height, {
+ collisionFilter: {
+ category: cat.body,
+ mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
+ },
+ isNotHoldable: true,
+ frictionAir: 0.001,
+ friction: 1,
+ frictionStatic: 1,
+ restitution: 0,
+ });
+
+ Matter.Body.setDensity(who, density)
+ const constraint = Constraint.create({ //fix rotor in place, but allow rotation
+ pointA: {
+ x: x,
+ y: y
+ },
+ bodyB: who,
+ stiffness: 1,
+ damping: 1
+ });
+ World.add(engine.world, constraint);
+ return constraint
+ },
+ platform(x, y, width, height, speed = 0, density = 0.001) {
+ x = x + width / 2
+ y = y + height / 2
+ const who = body[body.length] = Bodies.rectangle(x, y, width, height, {
+ collisionFilter: {
+ category: cat.body,
+ mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
+ },
+ inertia: Infinity, //prevents rotation
+ isNotHoldable: true,
+ friction: 1,
+ frictionStatic: 1,
+ restitution: 0,
+ });
+
+ Matter.Body.setDensity(who, density)
+ const constraint = Constraint.create({ //fix rotor in place, but allow rotation
+ pointA: {
+ x: x,
+ y: y
+ },
+ bodyB: who,
+ stiffness: 0.1,
+ damping: 0.3
+ });
+ World.add(engine.world, constraint);
+ constraint.plat = {
+ position: who.position,
+ speed: speed,
+ }
+ constraint.pauseUntilCycle = 0 //to to pause platform at top and bottom
+ return constraint
+ },
+ rotor(x, y, rotate = 0, radius = 800, width = 40, density = 0.0005) {
+ const rotor1 = Matter.Bodies.rectangle(x, y, width, radius, {
+ density: density,
+ isNotHoldable: true
+ });
+ const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, {
+ angle: Math.PI / 2,
+ density: density,
+ isNotHoldable: true
+ });
+ rotor = Body.create({ //combine rotor1 and rotor2
+ parts: [rotor1, rotor2],
+ restitution: 0,
+ collisionFilter: {
+ category: cat.body,
+ mask: cat.body | cat.mob | cat.mobBullet | cat.mobShield | cat.powerUp | cat.player | cat.bullet
+ },
+ });
+ Matter.Body.setPosition(rotor, {
+ x: x,
+ y: y
+ });
+ World.add(engine.world, [rotor]);
+ body[body.length] = rotor1
+ body[body.length] = rotor2
+
+ setTimeout(function() {
+ rotor.collisionFilter.category = cat.body;
+ rotor.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet //| cat.map
+ }, 1000);
+
+ const constraint = Constraint.create({ //fix rotor in place, but allow rotation
+ pointA: {
+ x: x,
+ y: y
+ },
+ bodyB: rotor
+ });
+ World.add(engine.world, constraint);
+
+ if (rotate) {
+ rotor.rotate = function() {
+ if (!m.isBodiesAsleep) {
+ Matter.Body.applyForce(rotor, {
+ x: rotor.position.x + 100,
+ y: rotor.position.y + 100
+ }, {
+ x: rotate * rotor.mass,
+ y: 0
+ })
+ } else {
+ Matter.Body.setAngularVelocity(rotor, 0);
+ }
+ }
+ }
+ composite[composite.length] = rotor
+ return rotor
+ },
+ button(x, y, width = 126) {
+ spawn.mapVertex(x + 65, y + 2, "100 10 -100 10 -70 -10 70 -10");
+ map[map.length - 1].restitution = 0;
+ map[map.length - 1].friction = 1;
+ map[map.length - 1].frictionStatic = 1;
+
+ // const buttonSensor = Bodies.rectangle(x + 35, y - 1, 70, 20, {
+ // isSensor: true
+ // });
+
+ return {
+ isUp: false,
+ min: {
+ x: x + 2,
+ y: y - 11
+ },
+ max: {
+ x: x + width,
+ y: y - 10
+ },
+ width: width,
+ height: 20,
+ query() {
+ if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
+ this.isUp = true;
+ } else {
+ // if (this.isUp === true) {
+ // const list = Matter.Query.region(body, this)
+ // console.log(list)
+ // if (list.length > 0) {
+ // Matter.Body.setPosition(list[0], {
+ // x: this.min.x + width / 2,
+ // y: list[0].position.y
+ // })
+ // Matter.Body.setVelocity(list[0], {
+ // x: 0,
+ // y: 0
+ // });
+ // }
+ // }
+ this.isUp = false;
+ }
+ },
+ draw() {
+ ctx.fillStyle = "hsl(0, 100%, 70%)"
+ if (this.isUp) {
+ ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20)
+ } else {
+ ctx.fillRect(this.min.x, this.min.y - 3, this.width, 25)
+ }
+ //draw sensor zone
+ // ctx.beginPath();
+ // sensor = buttonSensor.vertices;
+ // ctx.moveTo(sensor[0].x, sensor[0].y);
+ // for (let i = 1; i < sensor.length; ++i) {
+ // ctx.lineTo(sensor[i].x, sensor[i].y);
+ // }
+ // ctx.lineTo(sensor[0].x, sensor[0].y);
+ // ctx.fillStyle = "rgba(255, 255, 0, 0.3)";
+ // ctx.fill();
+ }
+ }
+ },
+ door(x, y, width, height, distance) {
+ x = x + width / 2
+ y = y + height / 2
+ const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, {
+ collisionFilter: {
+ category: cat.body,
+ mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
+ },
+ inertia: Infinity, //prevents rotation
+ isNotHoldable: true,
+ friction: 1,
+ frictionStatic: 1,
+ restitution: 0,
+ isOpen: false,
+ openClose() {
+ if (!m.isBodiesAsleep) {
+ if (!this.isOpen) {
+ if (this.position.y > y - distance) { //try to open
+ const position = {
+ x: this.position.x,
+ y: this.position.y - 1
+ }
+ Matter.Body.setPosition(this, position)
+ }
+ } else {
+ if (this.position.y < y) { //try to close
+ if (
+ Matter.Query.collides(this, [player]).length === 0 &&
+ Matter.Query.collides(this, body).length < 2 &&
+ Matter.Query.collides(this, mob).length === 0
+ ) {
+ const position = {
+ x: this.position.x,
+ y: this.position.y + 1
+ }
+ Matter.Body.setPosition(this, position)
+ }
+ }
+ }
+ }
+ },
+ draw() {
+ ctx.fillStyle = "#555"
+ ctx.beginPath();
+ const v = this.vertices;
+ ctx.moveTo(v[0].x, v[0].y);
+ for (let i = 1; i < v.length; ++i) {
+ ctx.lineTo(v[i].x, v[i].y);
+ }
+ ctx.lineTo(v[0].x, v[0].y);
+ ctx.fill();
+ }
+ });
+ Matter.Body.setStatic(doorBlock, true); //make static
+ return doorBlock
+ },
+ portal(centerA, angleA, centerB, angleB) {
+ const width = 50
+ const height = 150
+ const mapWidth = 200
+ const unitA = Matter.Vector.rotate({
+ x: 1,
+ y: 0
+ }, angleA)
+ const unitB = Matter.Vector.rotate({
+ x: 1,
+ y: 0
+ }, angleB)
+
+ draw = function() {
+ ctx.beginPath(); //portal
+ let v = this.vertices;
+ ctx.moveTo(v[0].x, v[0].y);
+ for (let i = 1; i < v.length; ++i) {
+ ctx.lineTo(v[i].x, v[i].y);
+ }
+ ctx.fillStyle = this.color
+ ctx.fill();
+ }
+ query = function() {
+ if (Matter.Query.collides(this, [player]).length === 0) { //not touching player
+ if (player.isInPortal === this) player.isInPortal = null
+ } else if (player.isInPortal !== this) { //touching player
+ if (m.buttonCD_jump === m.cycle) player.force.y = 0 // undo a jump right before entering the portal
+ m.buttonCD_jump = 0 //disable short jumps when letting go of jump key
+ player.isInPortal = this.portalPair
+ //teleport
+ if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down
+ Matter.Body.setPosition(player, this.portalPair.portal.position);
+ } else { //if at some odd angle
+ Matter.Body.setPosition(player, this.portalPair.position);
+ }
+ //rotate velocity
+ let mag
+ if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up
+ mag = Math.max(10, Math.min(50, player.velocity.y * 0.8)) + 11
+ } else {
+ mag = Math.max(6, Math.min(50, Vector.magnitude(player.velocity)))
+ }
+ let v = Vector.mult(this.portalPair.unit, mag)
+ Matter.Body.setVelocity(player, v);
+ // move bots to player
+ for (let i = 0; i < bullet.length; i++) {
+ if (bullet[i].botType) {
+ // Matter.Body.setPosition(bullet[i], this.portalPair.portal.position);
+ Matter.Body.setPosition(bullet[i], Vector.add(this.portalPair.portal.position, {
+ x: 250 * (Math.random() - 0.5),
+ y: 250 * (Math.random() - 0.5)
+ }));
+ Matter.Body.setVelocity(bullet[i], {
+ x: 0,
+ y: 0
+ });
+ }
+ }
+ }
+ // if (body.length) {
+ for (let i = 0, len = body.length; i < len; i++) {
+ if (body[i] !== m.holdingTarget) {
+ // body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100
+ if (Matter.Query.collides(this, [body[i]]).length === 0) {
+ if (body[i].isInPortal === this) body[i].isInPortal = null
+ } else if (body[i].isInPortal !== this) { //touching this portal, but for the first time
+ body[i].isInPortal = this.portalPair
+ //teleport
+ if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down
+ Matter.Body.setPosition(body[i], this.portalPair.portal.position);
+ } else { //if at some odd angle
+ Matter.Body.setPosition(body[i], this.portalPair.position);
+ }
+ //rotate velocity
+ let mag
+ if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up
+ mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11
+ } else {
+ mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity)))
+ }
+ let v = Vector.mult(this.portalPair.unit, mag)
+ Matter.Body.setVelocity(body[i], v);
+ }
+ // else if (body[i].speed < 0.1) { //touching this portal and very slow
+ // Matter.World.remove(engine.world, body[i]);
+ // body.splice(i, 1);
+ // break
+ // }
+ }
+ }
+ // }
+
+ //remove block if touching
+ // if (body.length) {
+ // touching = Matter.Query.collides(this, body)
+ // for (let i = 0; i < touching.length; i++) {
+ // if (touching[i].bodyB !== m.holdingTarget) {
+ // for (let j = 0, len = body.length; j < len; j++) {
+ // if (body[j] === touching[i].bodyB) {
+ // body.splice(j, 1);
+ // len--
+ // Matter.World.remove(engine.world, touching[i].bodyB);
+ // break;
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ // if (touching.length !== 0 && touching[0].bodyB !== m.holdingTarget) {
+ // if (body.length) {
+ // for (let i = 0; i < body.length; i++) {
+ // if (body[i] === touching[0].bodyB) {
+ // body.splice(i, 1);
+ // break;
+ // }
+ // }
+ // }
+ // Matter.World.remove(engine.world, touching[0].bodyB);
+ // }
+ }
+
+ const portalA = composite[composite.length] = Bodies.rectangle(centerA.x, centerA.y, width, height, {
+ isSensor: true,
+ angle: angleA,
+ color: "hsla(197, 100%, 50%,0.7)",
+ draw: draw,
+ });
+ const portalB = composite[composite.length] = Bodies.rectangle(centerB.x, centerB.y, width, height, {
+ isSensor: true,
+ angle: angleB,
+ color: "hsla(29, 100%, 50%, 0.7)",
+ draw: draw
+ });
+ const mapA = composite[composite.length] = Bodies.rectangle(centerA.x - 0.5 * unitA.x * mapWidth, centerA.y - 0.5 * unitA.y * mapWidth, mapWidth, height + 10, {
+ collisionFilter: {
+ category: cat.map,
+ mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
+ },
+ unit: unitA,
+ angle: angleA,
+ color: simulation.draw.mapFill,
+ draw: draw,
+ query: query,
+ lastPortalCycle: 0
+ });
+ Matter.Body.setStatic(mapA, true); //make static
+ World.add(engine.world, mapA); //add to world
+
+ const mapB = composite[composite.length] = Bodies.rectangle(centerB.x - 0.5 * unitB.x * mapWidth, centerB.y - 0.5 * unitB.y * mapWidth, mapWidth, height + 10, {
+ collisionFilter: {
+ category: cat.map,
+ mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
+ },
+ unit: unitB,
+ angle: angleB,
+ color: simulation.draw.mapFill,
+ draw: draw,
+ query: query,
+ lastPortalCycle: 0,
+ });
+ Matter.Body.setStatic(mapB, true); //make static
+ World.add(engine.world, mapB); //add to world
+
+ mapA.portal = portalA
+ mapB.portal = portalB
+ mapA.portalPair = mapB
+ mapB.portalPair = mapA
+ return [portalA, portalB, mapA, mapB]
+ },
+ hazard(x, y, width, height, damage = 0.0005, color = "hsla(160, 100%, 35%,0.75)", isOptical = false) {
+ return {
+ min: {
+ x: x,
+ y: y
+ },
+ max: {
+ x: x + width,
+ y: y + height
+ },
+ width: width,
+ height: height,
+ maxHeight: height,
+ isOn: true,
+ query() {
+ if (this.isOn && this.height > 0 && Matter.Query.region([player], this).length && !(m.isCloak && isOptical)) {
+ if (damage < 0.02) {
+ m.damage(damage)
+ } else if (m.immuneCycle < m.cycle) {
+ m.immuneCycle = m.cycle + tech.collisionImmuneCycles;
+ m.damage(damage)
+ simulation.drawList.push({ //add dmg to draw queue
+ x: player.position.x,
+ y: player.position.y,
+ radius: damage * 1500,
+ color: simulation.mobDmgColor,
+ time: 20
+ });
+ }
+ const drain = 0.005
+ if (m.energy > drain) m.energy -= drain
+ }
+ },
+ draw() {
+ if (this.isOn) {
+ ctx.fillStyle = color
+ ctx.fillRect(this.min.x, this.min.y, this.width, this.height)
+ }
+ },
+ drawTides() {
+ if (this.isOn) {
+ ctx.fillStyle = color
+ const offset = 10 * Math.sin(simulation.cycle * 0.015)
+ ctx.fillRect(this.min.x, this.min.y + offset, this.width, this.height - offset)
+ }
+ },
+ level(isFill) {
+ if (!m.isBodiesAsleep) {
+ const growSpeed = 1
+ if (isFill) {
+ if (this.height < this.maxHeight) {
+ this.height += growSpeed
+ this.min.y -= growSpeed
+ this.max.y = this.min.y + this.height
+ }
+ } else if (this.height > 0) {
+ this.height -= growSpeed
+ this.min.y += growSpeed
+ this.max.y = this.min.y + this.height
+ }
+ }
+ }
+ }
+ },
+ chain(x, y, angle = 0, isAttached = true, len = 15, radius = 20, stiffness = 1, damping = 1) {
+ const gap = 2 * radius
+ const unit = {
+ x: Math.cos(angle),
+ y: Math.sin(angle)
+ }
+ for (let i = 0; i < len; i++) {
+ body[body.length] = Bodies.polygon(x + gap * unit.x * i, y + gap * unit.y * i, 12, radius, {
+ inertia: Infinity,
+ isNotHoldable: true
+ });
+ }
+ for (let i = 1; i < len; i++) { //attach blocks to each other
+ consBB[consBB.length] = Constraint.create({
+ bodyA: body[body.length - i],
+ bodyB: body[body.length - i - 1],
+ stiffness: stiffness,
+ damping: damping
+ });
+ World.add(engine.world, consBB[consBB.length - 1]);
+ }
+ cons[cons.length] = Constraint.create({ //pin first block to a point in space
+ pointA: {
+ x: x,
+ y: y
+ },
+ bodyB: body[body.length - len],
+ stiffness: 1,
+ damping: damping
+ });
+ World.add(engine.world, cons[cons.length - 1]);
+ if (isAttached) {
+ cons[cons.length] = Constraint.create({ //pin last block to a point in space
+ pointA: {
+ x: x + gap * unit.x * (len - 1),
+ y: y + gap * unit.y * (len - 1)
+ },
+ bodyB: body[body.length - 1],
+ stiffness: 1,
+ damping: damping
+ });
+ World.add(engine.world, cons[cons.length - 1]);
+ }
+ },
//******************************************************************************************************************
//******************************************************************************************************************
//******************************************************************************************************************
@@ -293,7 +1052,7 @@ const level = {
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
// spawn.boost(1500, 0, 900);
- // spawn.starter(1900, -500, 200) //big boy
+ spawn.starter(1900, -500, 200) //big boy
// spawn.sneaker(2900, -500)
// spawn.launcherBoss(1200, -500)
// spawn.laserTargetingBoss(1600, -400)
@@ -349,7 +1108,7 @@ const level = {
// spawn.randomBoss(1700, -900, 0.4);
// if (simulation.difficulty > 3) spawn.randomLevelBoss(2200, -1300);
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- // if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(4800, -500);
+ // if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(4800, -500);
},
final() {
level.bossKilled = false; // if a boss needs to be killed
@@ -400,7 +1159,7 @@ const level = {
spawn.mapRect(5700, -3300, 1800, 5100); //right wall
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
spawn.mapRect(5425, -650, 375, 450); //blocking exit
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(4800, -500);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(4800, -500);
},
gauntlet() {
level.bossKilled = true; //if there is no boss this needs to be true to increase levels
@@ -463,7 +1222,7 @@ const level = {
}
}
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(4125, -350);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(4125, -350);
},
intro() {
level.bossKilled = true; //if there is no boss this needs to be true to increase levels
@@ -699,7 +1458,7 @@ const level = {
}
}
powerUps.spawnStartingPowerUps(2300, -150);
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(1900, -675);
+ // if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(1900, -675);
},
testChamber() {
level.setPosToSpawn(0, -50); //lower start
@@ -965,7 +1724,7 @@ const level = {
}
}
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(1925, -1250);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(1925, -1250);
},
sewers() {
level.bossKilled = false; // if a boss needs to be killed
@@ -1111,7 +1870,7 @@ const level = {
spawn.randomMob(2825, 400, 0.9);
if (simulation.difficulty > 3) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]);
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(7725, 2275);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(7725, 2275);
},
satellite() {
level.bossKilled = false; // if a boss needs to be killed
@@ -1318,7 +2077,7 @@ const level = {
}
}
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(3950, -850);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(3950, -850);
},
rooftops() {
level.bossKilled = false; // if a boss needs to be killed
@@ -1542,7 +2301,7 @@ const level = {
spawn.randomBoss(4900, -1200, 0);
if (simulation.difficulty > 3) spawn.randomLevelBoss(3200, -2050);
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(2175, -2425);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(2175, -2425);
},
aerie() {
level.bossKilled = false; // if a boss needs to be killed
@@ -1752,7 +2511,7 @@ const level = {
}
}
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(5350, -325);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(5350, -325);
},
skyscrapers() {
level.bossKilled = false; // if a boss needs to be killed
@@ -1914,7 +2673,7 @@ const level = {
spawn.randomBoss(1700, -900, 0.4);
if (simulation.difficulty > 3) spawn.randomLevelBoss(2600, -2300);
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(3075, -2050);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(3075, -2050);
},
highrise() {
level.bossKilled = false; // if a boss needs to be killed
@@ -2114,7 +2873,7 @@ const level = {
if (simulation.difficulty > 3) spawn.randomLevelBoss(-2400, -3000);
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(-1825, -1975);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(-1825, -1975);
},
warehouse() {
level.bossKilled = false; // if a boss needs to be killed
@@ -2291,7 +3050,7 @@ const level = {
}
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(300, -800);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(300, -800);
},
office() {
let button, door
@@ -2491,7 +3250,7 @@ const level = {
}
}
powerUps.addRerollToLevel() //needs to run after mobs are spawned
- if (tech.isDuplicateBoss && Math.random() < 3 * tech.duplicationChance()) spawn.randomLevelBoss(1875, -675);
+ if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(1875, -675);
},
stronghold() { // player made level by Francois 👑 from discord
level.custom = () => {
@@ -3934,770 +4693,5 @@ const level = {
spawn.randomLevelBoss(3100, -1850, ["shooterBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "snakeBoss", "laserBoss"]);
}
}
- },
- //******************************************************************************************************************
- //******************************************************************************************************************
- //******************************************************************************************************************
- //******************************************************************************************************************
- difficultyIncrease(num = 1) {
- for (let i = 0; i < num; i++) {
- simulation.difficulty++
- b.dmgScale *= 0.93; //damage done by player decreases each level
- if (simulation.accelScale < 5) simulation.accelScale *= 1.02 //mob acceleration increases each level
- if (simulation.lookFreqScale > 0.2) simulation.lookFreqScale *= 0.98 //mob cycles between looks decreases each level
- if (simulation.CDScale > 0.2) simulation.CDScale *= 0.97 //mob CD time decreases each level
- }
- simulation.dmgScale = 0.378 * simulation.difficulty //damage done by mobs increases each level
- simulation.healScale = 1 / (1 + simulation.difficulty * 0.06) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale;
- },
- difficultyDecrease(num = 1) { //used in easy mode for simulation.reset()
- for (let i = 0; i < num; i++) {
- simulation.difficulty--
- b.dmgScale /= 0.93; //damage done by player decreases each level
- if (simulation.accelScale > 0.2) simulation.accelScale /= 1.02 //mob acceleration increases each level
- if (simulation.lookFreqScale < 5) simulation.lookFreqScale /= 0.98 //mob cycles between looks decreases each level
- if (simulation.CDScale < 5) simulation.CDScale /= 0.97 //mob CD time decreases each level
- }
- if (simulation.difficulty < 1) simulation.difficulty = 0;
- simulation.dmgScale = 0.378 * simulation.difficulty //damage done by mobs increases each level
- if (simulation.dmgScale < 0.1) simulation.dmgScale = 0.1;
- simulation.healScale = 1 / (1 + simulation.difficulty * 0.06)
- },
- difficultyText() {
- if (simulation.difficultyMode === 1) {
- return "easy"
- } else if (simulation.difficultyMode === 2) {
- return "normal"
- } else if (simulation.difficultyMode === 4) {
- return "hard"
- } else if (simulation.difficultyMode === 6) {
- return "why"
- }
- },
- levelAnnounce() {
-
-
- if (level.levelsCleared === 0) {
- document.title = "n-gon: (" + level.difficultyText() + ")";
- } else {
- document.title = (simulation.isCheating ? "∅ " : "n-gon:") + (level.levelsCleared) + " " + level.levels[level.onLevel] + " (" + level.difficultyText() + ")";
- simulation.makeTextLog(`level.onLevel = "${level.levels[level.onLevel]}"`);
- }
- // simulation.makeTextLog(`
- // input.key.up = ["${input.key.up}", "ArrowUp"]
- //
input.key.left = ["${input.key.left}", "ArrowLeft"]
- //
input.key.down = ["${input.key.down}", "ArrowDown"]
- //
input.key.right = ["${input.key.right}", "ArrowRight"]
- //
- //
m.fieldMode = "${m.fieldUpgrades[m.fieldMode].name}"
- //
input.key.field = ["${input.key.field}", "right mouse"]
- //
m.field.description = "${m.fieldUpgrades[m.fieldMode].description}"
- // `, 1200);
- },
- nextLevel() {
- level.levelsCleared++;
- // level.difficultyIncrease(simulation.difficultyMode) //increase difficulty based on modes
-
- //difficulty is increased 5 times when finalBoss dies
- // const len = level.levelsCleared / level.levels.length //add 1 extra difficulty step for each time you have cleared all the levels
- // for (let i = 0; i < len; i++)
- level.difficultyIncrease(simulation.difficultyMode)
-
- level.onLevel++; //cycles map to next level
- if (level.onLevel > level.levels.length - 1) level.onLevel = 0;
- //reset lost tech display
- for (let i = 0; i < tech.tech.length; i++) {
- if (tech.tech[i].isLost) tech.tech[i].isLost = false;
- }
- tech.isDeathAvoidedThisLevel = false;
- simulation.updateTechHUD();
- simulation.clearNow = true; //triggers in simulation.clearMap to remove all physics bodies and setup for new map
- },
- playerExitCheck() {
- 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.nextLevel()
- }
- },
- setPosToSpawn(xPos, yPos) {
- m.spawnPos.x = m.pos.x = xPos;
- m.spawnPos.y = m.pos.y = yPos;
- level.enter.x = m.spawnPos.x - 50;
- level.enter.y = m.spawnPos.y + 20;
- m.transX = m.transSmoothX = canvas.width2 - m.pos.x;
- m.transY = m.transSmoothY = canvas.height2 - m.pos.y;
- m.Vx = m.spawnVel.x;
- m.Vy = m.spawnVel.y;
- player.force.x = 0;
- player.force.y = 0;
- Matter.Body.setPosition(player, m.spawnPos);
- Matter.Body.setVelocity(player, m.spawnVel);
- },
- enter: {
- x: 0,
- y: 0,
- draw() {
- 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 = "#ccc";
- ctx.fill();
- }
- },
- exit: {
- x: 0,
- y: 0,
- draw() {
- ctx.beginPath();
- ctx.moveTo(level.exit.x, level.exit.y + 30);
- ctx.lineTo(level.exit.x, level.exit.y - 80);
- ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80);
- ctx.lineTo(level.exit.x + 100, level.exit.y + 30);
- ctx.lineTo(level.exit.x, level.exit.y + 30);
- ctx.fillStyle = "#0ff";
- ctx.fill();
- }
- },
- fillBG: [],
- drawFillBGs() {
- for (let i = 0, len = level.fillBG.length; i < len; ++i) {
- const f = level.fillBG[i];
- ctx.fillStyle = f.color;
- ctx.fillRect(f.x, f.y, f.width, f.height);
- }
- },
- fill: [],
- drawFills() {
- for (let i = 0, len = level.fill.length; i < len; ++i) {
- const f = level.fill[i];
- ctx.fillStyle = f.color;
- ctx.fillRect(f.x, f.y, f.width, f.height);
- }
- },
- queryList: [], //queries do actions on many objects in regions
- checkQuery() {
- let bounds, action, info;
-
- function isInZone(targetArray) {
- let results = Matter.Query.region(targetArray, bounds);
- for (let i = 0, len = results.length; i < len; ++i) {
- level.queryActions[action](results[i], info);
- }
- }
- for (let i = 0, len = level.queryList.length; i < len; ++i) {
- bounds = level.queryList[i].bounds;
- action = level.queryList[i].action;
- info = level.queryList[i].info;
- for (let j = 0, l = level.queryList[i].groups.length; j < l; ++j) {
- isInZone(level.queryList[i].groups[j]);
- }
- }
- },
- //oddly query regions can't get smaller than 50 width?
- addQueryRegion(x, y, width, height, action, groups = [
- [player], body, mob, powerUp, bullet
- ], info) {
- level.queryList[level.queryList.length] = {
- bounds: {
- min: {
- x: x,
- y: y
- },
- max: {
- x: x + width,
- y: y + height
- }
- },
- action: action,
- groups: groups,
- info: info
- };
- },
- queryActions: {
- bounce(target, info) {
- //jerky fling upwards
- Matter.Body.setVelocity(target, {
- x: info.Vx + (Math.random() - 0.5) * 6,
- y: info.Vy
- });
- target.torque = (Math.random() - 0.5) * 2 * target.mass;
- },
- boost(target, yVelocity) {
- m.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts
- m.hardLandCD = 0 // disable hard landing
- if (target.velocity.y > 30) {
- Matter.Body.setVelocity(target, {
- x: target.velocity.x + (Math.random() - 0.5) * 2,
- y: -15 //gentle bounce if coming down super fast
- });
- } else {
- Matter.Body.setVelocity(target, {
- x: target.velocity.x + (Math.random() - 0.5) * 2,
- y: yVelocity
- });
- }
-
- },
- force(target, info) {
- if (target.velocity.y < 0) { //gently force up if already on the way up
- target.force.x += info.Vx * target.mass;
- target.force.y += info.Vy * target.mass;
- } else {
- target.force.y -= 0.0007 * target.mass; //gently fall in on the way down
- }
- },
- antiGrav(target) {
- target.force.y -= 0.0011 * target.mass;
- },
- death(target) {
- target.death();
- }
- },
- addToWorld() { //needs to be run to put bodies into the world
- for (let i = 0; i < body.length; i++) {
- //body[i].collisionFilter.group = 0;
- if (body[i] !== m.holdingTarget) {
- body[i].collisionFilter.category = cat.body;
- body[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
- }
- body[i].classType = "body";
- World.add(engine.world, body[i]); //add to world
- }
- for (let i = 0; i < map.length; i++) {
- //map[i].collisionFilter.group = 0;
- map[i].collisionFilter.category = cat.map;
- map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
- Matter.Body.setStatic(map[i], true); //make static
- World.add(engine.world, map[i]); //add to world
- }
- // for (let i = 0; i < cons.length; i++) {
- // World.add(engine.world, cons[i]);
- // }
-
- },
- spinner(x, y, width, height, density = 0.001) {
- x = x + width / 2
- y = y + height / 2
- const who = body[body.length] = Bodies.rectangle(x, y, width, height, {
- collisionFilter: {
- category: cat.body,
- mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
- },
- isNotHoldable: true,
- frictionAir: 0.001,
- friction: 1,
- frictionStatic: 1,
- restitution: 0,
- });
-
- Matter.Body.setDensity(who, density)
- const constraint = Constraint.create({ //fix rotor in place, but allow rotation
- pointA: {
- x: x,
- y: y
- },
- bodyB: who,
- stiffness: 1,
- damping: 1
- });
- World.add(engine.world, constraint);
- return constraint
- },
- platform(x, y, width, height, speed = 0, density = 0.001) {
- x = x + width / 2
- y = y + height / 2
- const who = body[body.length] = Bodies.rectangle(x, y, width, height, {
- collisionFilter: {
- category: cat.body,
- mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
- },
- inertia: Infinity, //prevents rotation
- isNotHoldable: true,
- friction: 1,
- frictionStatic: 1,
- restitution: 0,
- });
-
- Matter.Body.setDensity(who, density)
- const constraint = Constraint.create({ //fix rotor in place, but allow rotation
- pointA: {
- x: x,
- y: y
- },
- bodyB: who,
- stiffness: 0.1,
- damping: 0.3
- });
- World.add(engine.world, constraint);
- constraint.plat = {
- position: who.position,
- speed: speed,
- }
- constraint.pauseUntilCycle = 0 //to to pause platform at top and bottom
- return constraint
- },
- rotor(x, y, rotate = 0, radius = 800, width = 40, density = 0.0005) {
- const rotor1 = Matter.Bodies.rectangle(x, y, width, radius, {
- density: density,
- isNotHoldable: true
- });
- const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, {
- angle: Math.PI / 2,
- density: density,
- isNotHoldable: true
- });
- rotor = Body.create({ //combine rotor1 and rotor2
- parts: [rotor1, rotor2],
- restitution: 0,
- collisionFilter: {
- category: cat.body,
- mask: cat.body | cat.mob | cat.mobBullet | cat.mobShield | cat.powerUp | cat.player | cat.bullet
- },
- });
- Matter.Body.setPosition(rotor, {
- x: x,
- y: y
- });
- World.add(engine.world, [rotor]);
- body[body.length] = rotor1
- body[body.length] = rotor2
-
- setTimeout(function() {
- rotor.collisionFilter.category = cat.body;
- rotor.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet //| cat.map
- }, 1000);
-
- const constraint = Constraint.create({ //fix rotor in place, but allow rotation
- pointA: {
- x: x,
- y: y
- },
- bodyB: rotor
- });
- World.add(engine.world, constraint);
-
- if (rotate) {
- rotor.rotate = function() {
- if (!m.isBodiesAsleep) {
- Matter.Body.applyForce(rotor, {
- x: rotor.position.x + 100,
- y: rotor.position.y + 100
- }, {
- x: rotate * rotor.mass,
- y: 0
- })
- } else {
- Matter.Body.setAngularVelocity(rotor, 0);
- }
- }
- }
- composite[composite.length] = rotor
- return rotor
- },
- button(x, y, width = 126) {
- spawn.mapVertex(x + 65, y + 2, "100 10 -100 10 -70 -10 70 -10");
- map[map.length - 1].restitution = 0;
- map[map.length - 1].friction = 1;
- map[map.length - 1].frictionStatic = 1;
-
- // const buttonSensor = Bodies.rectangle(x + 35, y - 1, 70, 20, {
- // isSensor: true
- // });
-
- return {
- isUp: false,
- min: {
- x: x + 2,
- y: y - 11
- },
- max: {
- x: x + width,
- y: y - 10
- },
- width: width,
- height: 20,
- query() {
- if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
- this.isUp = true;
- } else {
- // if (this.isUp === true) {
- // const list = Matter.Query.region(body, this)
- // console.log(list)
- // if (list.length > 0) {
- // Matter.Body.setPosition(list[0], {
- // x: this.min.x + width / 2,
- // y: list[0].position.y
- // })
- // Matter.Body.setVelocity(list[0], {
- // x: 0,
- // y: 0
- // });
- // }
- // }
- this.isUp = false;
- }
- },
- draw() {
- ctx.fillStyle = "hsl(0, 100%, 70%)"
- if (this.isUp) {
- ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20)
- } else {
- ctx.fillRect(this.min.x, this.min.y - 3, this.width, 25)
- }
- //draw sensor zone
- // ctx.beginPath();
- // sensor = buttonSensor.vertices;
- // ctx.moveTo(sensor[0].x, sensor[0].y);
- // for (let i = 1; i < sensor.length; ++i) {
- // ctx.lineTo(sensor[i].x, sensor[i].y);
- // }
- // ctx.lineTo(sensor[0].x, sensor[0].y);
- // ctx.fillStyle = "rgba(255, 255, 0, 0.3)";
- // ctx.fill();
- }
- }
- },
- door(x, y, width, height, distance) {
- x = x + width / 2
- y = y + height / 2
- const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, {
- collisionFilter: {
- category: cat.body,
- mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
- },
- inertia: Infinity, //prevents rotation
- isNotHoldable: true,
- friction: 1,
- frictionStatic: 1,
- restitution: 0,
- isOpen: false,
- openClose() {
- if (!m.isBodiesAsleep) {
- if (!this.isOpen) {
- if (this.position.y > y - distance) { //try to open
- const position = {
- x: this.position.x,
- y: this.position.y - 1
- }
- Matter.Body.setPosition(this, position)
- }
- } else {
- if (this.position.y < y) { //try to close
- if (
- Matter.Query.collides(this, [player]).length === 0 &&
- Matter.Query.collides(this, body).length < 2 &&
- Matter.Query.collides(this, mob).length === 0
- ) {
- const position = {
- x: this.position.x,
- y: this.position.y + 1
- }
- Matter.Body.setPosition(this, position)
- }
- }
- }
- }
- },
- draw() {
- ctx.fillStyle = "#555"
- ctx.beginPath();
- const v = this.vertices;
- ctx.moveTo(v[0].x, v[0].y);
- for (let i = 1; i < v.length; ++i) {
- ctx.lineTo(v[i].x, v[i].y);
- }
- ctx.lineTo(v[0].x, v[0].y);
- ctx.fill();
- }
- });
- Matter.Body.setStatic(doorBlock, true); //make static
- return doorBlock
- },
- portal(centerA, angleA, centerB, angleB) {
- const width = 50
- const height = 150
- const mapWidth = 200
- const unitA = Matter.Vector.rotate({
- x: 1,
- y: 0
- }, angleA)
- const unitB = Matter.Vector.rotate({
- x: 1,
- y: 0
- }, angleB)
-
- draw = function() {
- ctx.beginPath(); //portal
- let v = this.vertices;
- ctx.moveTo(v[0].x, v[0].y);
- for (let i = 1; i < v.length; ++i) {
- ctx.lineTo(v[i].x, v[i].y);
- }
- ctx.fillStyle = this.color
- ctx.fill();
- }
- query = function() {
- if (Matter.Query.collides(this, [player]).length === 0) { //not touching player
- if (player.isInPortal === this) player.isInPortal = null
- } else if (player.isInPortal !== this) { //touching player
- if (m.buttonCD_jump === m.cycle) player.force.y = 0 // undo a jump right before entering the portal
- m.buttonCD_jump = 0 //disable short jumps when letting go of jump key
- player.isInPortal = this.portalPair
- //teleport
- if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down
- Matter.Body.setPosition(player, this.portalPair.portal.position);
- } else { //if at some odd angle
- Matter.Body.setPosition(player, this.portalPair.position);
- }
- //rotate velocity
- let mag
- if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up
- mag = Math.max(10, Math.min(50, player.velocity.y * 0.8)) + 11
- } else {
- mag = Math.max(6, Math.min(50, Vector.magnitude(player.velocity)))
- }
- let v = Vector.mult(this.portalPair.unit, mag)
- Matter.Body.setVelocity(player, v);
- // move bots to player
- for (let i = 0; i < bullet.length; i++) {
- if (bullet[i].botType) {
- // Matter.Body.setPosition(bullet[i], this.portalPair.portal.position);
- Matter.Body.setPosition(bullet[i], Vector.add(this.portalPair.portal.position, {
- x: 250 * (Math.random() - 0.5),
- y: 250 * (Math.random() - 0.5)
- }));
- Matter.Body.setVelocity(bullet[i], {
- x: 0,
- y: 0
- });
- }
- }
- }
- // if (body.length) {
- for (let i = 0, len = body.length; i < len; i++) {
- if (body[i] !== m.holdingTarget) {
- // body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100
- if (Matter.Query.collides(this, [body[i]]).length === 0) {
- if (body[i].isInPortal === this) body[i].isInPortal = null
- } else if (body[i].isInPortal !== this) { //touching this portal, but for the first time
- body[i].isInPortal = this.portalPair
- //teleport
- if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down
- Matter.Body.setPosition(body[i], this.portalPair.portal.position);
- } else { //if at some odd angle
- Matter.Body.setPosition(body[i], this.portalPair.position);
- }
- //rotate velocity
- let mag
- if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up
- mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11
- } else {
- mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity)))
- }
- let v = Vector.mult(this.portalPair.unit, mag)
- Matter.Body.setVelocity(body[i], v);
- }
- // else if (body[i].speed < 0.1) { //touching this portal and very slow
- // Matter.World.remove(engine.world, body[i]);
- // body.splice(i, 1);
- // break
- // }
- }
- }
- // }
-
- //remove block if touching
- // if (body.length) {
- // touching = Matter.Query.collides(this, body)
- // for (let i = 0; i < touching.length; i++) {
- // if (touching[i].bodyB !== m.holdingTarget) {
- // for (let j = 0, len = body.length; j < len; j++) {
- // if (body[j] === touching[i].bodyB) {
- // body.splice(j, 1);
- // len--
- // Matter.World.remove(engine.world, touching[i].bodyB);
- // break;
- // }
- // }
- // }
- // }
- // }
-
- // if (touching.length !== 0 && touching[0].bodyB !== m.holdingTarget) {
- // if (body.length) {
- // for (let i = 0; i < body.length; i++) {
- // if (body[i] === touching[0].bodyB) {
- // body.splice(i, 1);
- // break;
- // }
- // }
- // }
- // Matter.World.remove(engine.world, touching[0].bodyB);
- // }
- }
-
- const portalA = composite[composite.length] = Bodies.rectangle(centerA.x, centerA.y, width, height, {
- isSensor: true,
- angle: angleA,
- color: "hsla(197, 100%, 50%,0.7)",
- draw: draw,
- });
- const portalB = composite[composite.length] = Bodies.rectangle(centerB.x, centerB.y, width, height, {
- isSensor: true,
- angle: angleB,
- color: "hsla(29, 100%, 50%, 0.7)",
- draw: draw
- });
- const mapA = composite[composite.length] = Bodies.rectangle(centerA.x - 0.5 * unitA.x * mapWidth, centerA.y - 0.5 * unitA.y * mapWidth, mapWidth, height + 10, {
- collisionFilter: {
- category: cat.map,
- mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
- },
- unit: unitA,
- angle: angleA,
- color: simulation.draw.mapFill,
- draw: draw,
- query: query,
- lastPortalCycle: 0
- });
- Matter.Body.setStatic(mapA, true); //make static
- World.add(engine.world, mapA); //add to world
-
- const mapB = composite[composite.length] = Bodies.rectangle(centerB.x - 0.5 * unitB.x * mapWidth, centerB.y - 0.5 * unitB.y * mapWidth, mapWidth, height + 10, {
- collisionFilter: {
- category: cat.map,
- mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
- },
- unit: unitB,
- angle: angleB,
- color: simulation.draw.mapFill,
- draw: draw,
- query: query,
- lastPortalCycle: 0,
- });
- Matter.Body.setStatic(mapB, true); //make static
- World.add(engine.world, mapB); //add to world
-
- mapA.portal = portalA
- mapB.portal = portalB
- mapA.portalPair = mapB
- mapB.portalPair = mapA
- return [portalA, portalB, mapA, mapB]
- },
- hazard(x, y, width, height, damage = 0.0005, color = "hsla(160, 100%, 35%,0.75)", isOptical = false) {
- return {
- min: {
- x: x,
- y: y
- },
- max: {
- x: x + width,
- y: y + height
- },
- width: width,
- height: height,
- maxHeight: height,
- isOn: true,
- query() {
- if (this.isOn && this.height > 0 && Matter.Query.region([player], this).length && !(m.isCloak && isOptical)) {
- if (damage < 0.02) {
- m.damage(damage)
- } else if (m.immuneCycle < m.cycle) {
- m.immuneCycle = m.cycle + tech.collisionImmuneCycles;
- m.damage(damage)
- simulation.drawList.push({ //add dmg to draw queue
- x: player.position.x,
- y: player.position.y,
- radius: damage * 1500,
- color: simulation.mobDmgColor,
- time: 20
- });
- }
- const drain = 0.005
- if (m.energy > drain) m.energy -= drain
- }
- },
- draw() {
- if (this.isOn) {
- ctx.fillStyle = color
- ctx.fillRect(this.min.x, this.min.y, this.width, this.height)
- }
- },
- drawTides() {
- if (this.isOn) {
- ctx.fillStyle = color
- const offset = 10 * Math.sin(simulation.cycle * 0.015)
- ctx.fillRect(this.min.x, this.min.y + offset, this.width, this.height - offset)
- }
- },
- level(isFill) {
- if (!m.isBodiesAsleep) {
- const growSpeed = 1
- if (isFill) {
- if (this.height < this.maxHeight) {
- this.height += growSpeed
- this.min.y -= growSpeed
- this.max.y = this.min.y + this.height
- }
- } else if (this.height > 0) {
- this.height -= growSpeed
- this.min.y += growSpeed
- this.max.y = this.min.y + this.height
- }
- }
- }
- }
- },
- chain(x, y, angle = 0, isAttached = true, len = 15, radius = 20, stiffness = 1, damping = 1) {
- const gap = 2 * radius
- const unit = {
- x: Math.cos(angle),
- y: Math.sin(angle)
- }
- for (let i = 0; i < len; i++) {
- body[body.length] = Bodies.polygon(x + gap * unit.x * i, y + gap * unit.y * i, 12, radius, {
- inertia: Infinity,
- isNotHoldable: true
- });
- }
- for (let i = 1; i < len; i++) { //attach blocks to each other
- consBB[consBB.length] = Constraint.create({
- bodyA: body[body.length - i],
- bodyB: body[body.length - i - 1],
- stiffness: stiffness,
- damping: damping
- });
- World.add(engine.world, consBB[consBB.length - 1]);
- }
- cons[cons.length] = Constraint.create({ //pin first block to a point in space
- pointA: {
- x: x,
- y: y
- },
- bodyB: body[body.length - len],
- stiffness: 1,
- damping: damping
- });
- World.add(engine.world, cons[cons.length - 1]);
- if (isAttached) {
- cons[cons.length] = Constraint.create({ //pin last block to a point in space
- pointA: {
- x: x + gap * unit.x * (len - 1),
- y: y + gap * unit.y * (len - 1)
- },
- bodyB: body[body.length - 1],
- stiffness: 1,
- damping: damping
- });
- World.add(engine.world, cons[cons.length - 1]);
- }
- },
+ }
};
\ No newline at end of file
diff --git a/js/simulation.js b/js/simulation.js
index aa0e867..15aaf9d 100644
--- a/js/simulation.js
+++ b/js/simulation.js
@@ -515,6 +515,7 @@ const simulation = {
level.levels.push("basement");
level.levels.push("detours");
level.levels.push("house");
+ level.levels.splice(0, 4); //remove 4 random levels to make up for adding the 4 community levels
}
level.levels = shuffle(level.levels); //shuffles order of maps
level.levels.unshift("intro"); //add level to the start of the randomized levels list
diff --git a/js/tech.js b/js/tech.js
index 7d43739..039a8c3 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -7,6 +7,7 @@ const tech = {
tech.tech[i].count = 0
}
lore.techCount = 0;
+ tech.removeJunkTechFromPool();
tech.removeLoreTechFromPool();
tech.addLoreTechToPool();
tech.armorFromPowerUps = 0;
@@ -19,18 +20,15 @@ const tech = {
simulation.updateTechHUD();
},
removeLoreTechFromPool() {
- // for (let i = 0, len = tech.tech.length; i < len; i++) {
- // if (tech.tech[i].isLore) {
- // console.log('found one')
- // tech.tech.splice(i, 1)
- // tech.removeLoreTechFromPool();
- // return;
- // }
- // }
for (let i = tech.tech.length - 1; i > 0; i--) {
if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1)
}
},
+ removeJunkTechFromPool() {
+ for (let i = tech.tech.length - 1; i > 0; i--) {
+ if (tech.tech[i].isJunk && tech.tech[i].count === 0) tech.tech.splice(i, 1)
+ }
+ },
giveTech(index = 'random') {
if (index === 'random') {
let options = [];
@@ -935,7 +933,7 @@ const tech = {
},
{
name: "dynamo-bot upgrade",
- description: "dynamo-bots regen 12 energy per second
applies to all current and future orbit-bots",
+ description: "dynamo-bots regen 12 energy per second
applies to all current and future dynamo-bots",
maxCount: 1,
count: 0,
allowed() {
@@ -1782,7 +1780,7 @@ const tech = {
maxCount: 1,
count: 0,
allowed() {
- return tech.duplicationChance() > 0
+ return tech.duplicationChance() > 0.15
},
requires: "some duplication chance",
effect() {
@@ -1794,11 +1792,11 @@ const tech = {
},
{
name: "cloning",
- description: "each level has a chance to spawn a level boss
equal to triple your duplication chance",
+ description: "each level has a chance to spawn a level boss
equal to double your duplication chance",
maxCount: 1,
count: 0,
allowed() {
- return tech.duplicationChance() > 0
+ return tech.duplicationChance() > 0.25
},
requires: "some duplication chance",
effect() {
@@ -1974,6 +1972,23 @@ const tech = {
}
}
},
+ {
+ name: "dark patterns",
+ description: "reduce combat difficulty by 1 level
add 16 junk tech to the potential pool",
+ maxCount: 1,
+ isNonRefundable: true,
+ isCustomHide: true,
+ count: 0,
+ allowed() {
+ return powerUps.research.count === 0 && level.onLevel < 6
+ },
+ requires: "no research, and in the first 5 levels",
+ effect() {
+ level.difficultyDecrease(simulation.difficultyMode)
+ for (let i = 0; i < 160; i++) tech.tech.push(tech.junk[Math.floor(Math.random() * tech.junk.length)])
+ },
+ remove() {}
+ },
{
name: "cardinality",
description: "tech, fields, and guns have 5 choices",
@@ -2002,9 +2017,8 @@ const tech = {
requires: "not cardinality, not futures or commodities exchanges",
effect: () => {
tech.isDeterminism = true;
- for (let i = 0; i < 5; i++) { //if you change the six also change it in Born rule
- powerUps.spawn(m.pos.x, m.pos.y, "tech");
- }
+ //if you change the six also change it in Born rule
+ for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech");
},
remove() {
tech.isDeterminism = false;
@@ -3311,7 +3325,7 @@ const tech = {
},
remove() {
if (tech.isWideLaser) {
- tech.wideLaser = 0
+ // tech.wideLaser = 0
tech.isWideLaser = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
@@ -4034,9 +4048,7 @@ const tech = {
},
requires: "",
effect() {
- for (let i = 0; i < 6; i++) {
- powerUps.spawn(m.pos.x, m.pos.y, "heal");
- }
+ for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x, m.pos.y, "heal");
this.count--
},
remove() {}
@@ -4053,9 +4065,7 @@ const tech = {
},
requires: "not exciton lattice",
effect() {
- for (let i = 0; i < 6; i++) {
- powerUps.spawn(m.pos.x, m.pos.y, "ammo");
- }
+ for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x, m.pos.y, "ammo");
this.count--
},
remove() {}
@@ -4072,9 +4082,7 @@ const tech = {
},
requires: "not superdeterminism",
effect() {
- for (let i = 0; i < 4; i++) {
- powerUps.spawn(m.pos.x, m.pos.y, "research");
- }
+ for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x, m.pos.y, "research");
this.count--
},
remove() {}
@@ -4143,6 +4151,348 @@ const tech = {
remove() {}
})
},
+ junk: [
+ // {
+ // name: "junk",
+ // description: "",
+ // maxCount: 9,
+ // count: 0,
+ // isNonRefundable: true,
+ // isCustomHide: true,
+ // isJunk: true,
+ // allowed() {
+ // return true
+ // },
+ // requires: "",
+ // effect() {
+
+ // },
+ // remove() {}
+ // },
+ {
+ name: "Sleipnir",
+ description: "grow more legs",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return true
+ },
+ requires: "",
+ effect() {
+ m.draw = function() {
+ ctx.fillStyle = m.fillColor;
+ m.walk_cycle += m.flipLegs * m.Vx;
+
+ //draw body
+ ctx.save();
+ ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5
+ ctx.translate(m.pos.x, m.pos.y);
+ for (let i = 0; i < 16; i++) {
+ m.calcLeg(Math.PI * i / 8, -3 * i / 16)
+ m.drawLeg("#444")
+ }
+ ctx.rotate(m.angle);
+
+ ctx.beginPath();
+ ctx.arc(0, 0, 30, 0, 2 * Math.PI);
+ let grd = ctx.createLinearGradient(-30, 0, 30, 0);
+ grd.addColorStop(0, m.fillColorDark);
+ grd.addColorStop(1, m.fillColor);
+ ctx.fillStyle = grd;
+ ctx.fill();
+ ctx.arc(15, 0, 4, 0, 2 * Math.PI);
+ ctx.strokeStyle = "#333";
+ ctx.lineWidth = 2;
+ ctx.stroke();
+ // ctx.beginPath();
+ // ctx.arc(15, 0, 3, 0, 2 * Math.PI);
+ // ctx.fillStyle = '#0cf';
+ // ctx.fill()
+ ctx.restore();
+ m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
+ }
+ },
+ remove() {}
+ },
+ {
+ name: "pareidolia",
+ description: "don't",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return true
+ },
+ requires: "",
+ effect() {
+ m.draw = function() {
+ ctx.fillStyle = m.fillColor;
+ m.walk_cycle += m.flipLegs * m.Vx;
+ ctx.save();
+ ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.7
+ ctx.translate(m.pos.x, m.pos.y);
+ m.calcLeg(Math.PI, -3);
+ m.drawLeg("#4a4a4a");
+ m.calcLeg(0, 0);
+ m.drawLeg("#333");
+ ctx.rotate(m.angle);
+ ctx.beginPath();
+ ctx.arc(0, 0, 30, 0, 2 * Math.PI);
+ let grd = ctx.createLinearGradient(-30, 0, 30, 0);
+ grd.addColorStop(0, m.fillColorDark);
+ grd.addColorStop(1, m.fillColor);
+ ctx.fillStyle = grd;
+ ctx.fill();
+ ctx.strokeStyle = "#333";
+ ctx.lineWidth = 2;
+ if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) ctx.scale(1, -1); //here is the flip
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(2, -6, 7, 0, 2 * Math.PI);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(18, 13, 10, 0, 2 * Math.PI);
+ ctx.fillStyle = grd;
+ ctx.fill();
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(18, 13, 6, 0, 2 * Math.PI);
+ ctx.fillStyle = "#555";
+ ctx.fill();
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(3, -6, 3, 0, 2 * Math.PI);
+ ctx.fill();
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.arc(26, -6, 3, 0, 2 * Math.PI);
+ ctx.fill();
+ ctx.stroke();
+ ctx.restore();
+ m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15;
+ }
+ },
+ remove() {}
+ },
+ {
+ name: "prism",
+ description: "you cycle through different colors",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return true
+ },
+ requires: "",
+ effect() {
+ m.color = {
+ hue: 0,
+ sat: 100,
+ light: 50
+ }
+ setInterval(function() {
+ m.color.hue++
+ m.setFillColors()
+ }, 10);
+ },
+ remove() {}
+ },
+ {
+ name: "assimilation",
+ description: "all your bots are converted to the same random model",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return tech.totalBots() > 2
+ },
+ requires: "at least 3 bots",
+ effect() {
+ const total = tech.totalBots();
+ tech.dynamoBotCount = 0;
+ tech.nailBotCount = 0;
+ tech.laserBotCount = 0;
+ tech.orbitBotCount = 0;
+ tech.foamBotCount = 0;
+ tech.boomBotCount = 0;
+ tech.plasmaBotCount = 0;
+ tech.missileBotCount = 0;
+ for (let i = 0; i < bullet.length; i++) {
+ if (bullet[i].botType) bullet[i].endCycle = 0
+ }
+
+ const bots = [
+ () => {
+ b.nailBot();
+ tech.nailBotCount++;
+ },
+ () => {
+ b.foamBot();
+ tech.foamBotCount++;
+ },
+ () => {
+ b.boomBot();
+ tech.boomBotCount++;
+ },
+ () => {
+ b.laserBot();
+ tech.laserBotCount++;
+ },
+ () => {
+ b.orbitBot();
+ tech.orbitBotCount++
+ },
+ () => {
+ b.dynamoBot();
+ tech.dynamoBotCount++
+ }
+ ]
+ const index = Math.floor(Math.random() * bots.length)
+ for (let i = 0; i < total; i++) bots[index]()
+ },
+ remove() {}
+ },
+ {
+ name: "growth hacking",
+ description: "increase combat difficulty by 1 level",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return true
+ },
+ requires: "",
+ effect() {
+ level.difficultyIncrease(simulation.difficultyMode)
+ },
+ remove() {}
+ },
+ {
+ name: "stun",
+ description: "stun all mobs for up to 10 seconds",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return true
+ },
+ requires: "",
+ effect() {
+ for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 600)
+ },
+ remove() {}
+ },
+ {
+ name: "re-arm",
+ description: "eject all your guns",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return b.inventory.length > 0
+ },
+ requires: "at least 1 gun",
+ effect() {
+ for (let i = 0; i < b.inventory.length; i++) powerUps.spawn(m.pos.x, m.pos.y, "gun");
+
+ //removes guns and ammo
+ b.inventory = [];
+ b.activeGun = null;
+ b.inventoryGun = 0;
+ for (let i = 0, len = b.guns.length; i < len; ++i) {
+ b.guns[i].have = false;
+ if (b.guns[i].ammo !== Infinity) b.guns[i].ammo = 0;
+ }
+ simulation.makeGunHUD(); //update gun HUD
+ },
+ remove() {}
+ },
+ {
+ name: "re-research",
+ description: "eject all your research",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return powerUps.research.count > 3
+ },
+ requires: "at least 4 research",
+ effect() {
+ for (let i = 0; i < powerUps.research.count; i++) powerUps.spawn(m.pos.x, m.pos.y, "research");
+ powerUps.research.count = 0
+ },
+ remove() {}
+ },
+ {
+ name: "quantum black hole",
+ description: "use all your energy to
spawn inside the event horizon of a black hole boss",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return true
+ },
+ requires: "",
+ effect() {
+ m.energy = 0
+ spawn.suckerBoss(m.pos.x, m.pos.y - 1000)
+ },
+ remove() {}
+ },
+ {
+ name: "black hole cluster",
+ description: "spawn 2 research
spawn 40 nearby black holes",
+ maxCount: 1,
+ count: 0,
+ isNonRefundable: true,
+ isCustomHide: true,
+ isJunk: true,
+ allowed() {
+ return true
+ },
+ requires: "",
+ effect() {
+ for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x, m.pos.y, "research");
+ const unit = {
+ x: 1,
+ y: 0
+ }
+ for (let i = 0; i < 40; i++) {
+ const where = Vector.add(m.pos, Vector.mult(Vector.rotate(unit, Math.random() * 2 * Math.PI), 600 + 800 * Math.random()))
+ spawn.sucker(where.x, where.y)
+ }
+ },
+ remove() {}
+ },
+ ],
//variables use for gun tech upgrades
fireRate: null,
bulletSize: null,
@@ -4337,5 +4687,5 @@ const tech = {
isGunSwitchField: null,
isNeedleShieldPierce: null,
isDuplicateBoss: null,
- isDynamoBotUpgrade: null
+ isDynamoBotUpgrade: null,
}
\ No newline at end of file
diff --git a/style.css b/style.css
index 2303170..e55a7ba 100644
--- a/style.css
+++ b/style.css
@@ -84,6 +84,19 @@ td {
text-align: center;
}
+.pause-table {
+ width: auto;
+}
+
+.key-input-pause {
+ width: 15px;
+ padding: 0px 2px;
+ border: 2px solid #333;
+ /* border-radius: 50px; */
+ background-color: #fff;
+ text-align: center;
+}
+
.key-used {
color: #777;
font-size: 80%;
diff --git a/todo.txt b/todo.txt
index 80174a9..e5888aa 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,5 +1,9 @@
******************************************************** NEXT PATCH ********************************************************
+tech cloning requires > 25% dup chance, and is now only 2x (was 3x) your dup chance for a second boss
+playing with the 4 community levels now removes 4 random levels from the level list
+
+tech: dark patterns - reduce combat difficulty by 1 level and add 16 junk tech to the potential tech pool
******************************************************** BUGS ********************************************************
@@ -27,7 +31,23 @@
******************************************************** TODO ********************************************************
-smooth history following for dynamo-bot?
+reduce 3 difficulty, randomize all mods when you...
+ take damage
+ switch guns
+ pick up a tech
+
+add back in gamepad support
+ https://github.com/landgreen/landgreen.github.io/search?q=gamepadconnected
+
+mechanic: gain damage when there are fewer bullets
+
+mechanic: untested tech
+ negative effect (one time effects are better to avoid code clutter)
+ change the player draw to some of the dumb stuff in discord console
+ convert your bots to research (requires you to have some bots)
+ your bots are changed to random bots
+
+rename intro level to something lore related
give undefined tech different effects at different localSettings.loreCount values
or just random effects
@@ -52,11 +72,6 @@ bot: ice blast, long CD AOE freeze
RPG default or tech: grenades detonate on your cursor / where your cursor was when they were fired
-tech: double your research
- set your duplication chance to zero
- requires 5 rerolls and 20% duplication chance
- might want to use a single variable for all duplication
-
tech: dodge chance for cloaking, harmonic fields, also pilot wave
20% chance up to 3 stacks, not additive
0.8^count
@@ -74,25 +89,6 @@ tech: time dilation - when you exit time dilation rewind to the state you entere
mob ability bombs/bullets that suck in player
-mechanic: technological dead end - add tech to the tech pool with a dumb effect
- don't show up in custom?
- negative effect (one time effects are better to avoid code clutter)
- make the player rainbow colors
- m.color = {
- hue: 0,
- sat: 100,
- light: 50
- }
- setInterval(function(){
- m.color.hue++
- m.setFillColors()
- }, 10);
- remove all your energy
- eject all your rerolls (not bad with dup)
- teleport to the start of the level
- remove your bots (requires you to have some bots)
- your bots are changed to random bots
-
tech that requires integrated armament
mechanic - Your energy regen is only active when field and gun have not been used for 5 seconds.
@@ -109,8 +105,6 @@ tech power up magnetism - power ups drift towards player
super balls start at 3, not 4
have to balance damage
-RPG might need a buff, now that it disables the other cool grenade tech
-
make different move methods
tech crouch charge jump
tech double jump
@@ -143,8 +137,6 @@ mob vision: look at player history
wormhole - make it clear when the wormhole can and can't teleport to a location before the player clicks
-time dilation - slow down the game engine by 1/2, but run an extra player cycle to simulate slow motion
-
flavor - your bullets destroy blocks
this isn't really a bonus, so maybe just add this as flavor to another tech field/gun
a chance for destroyed blocks to drop stuff