From 774fa44b8164e2d113cb6fba60b5aefb94f8f899 Mon Sep 17 00:00:00 2001 From: landgreen Date: Sun, 31 Jan 2021 07:55:01 -0800 Subject: [PATCH] dark patterns 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 --- js/bullet.js | 8 +- js/index.js | 27 +- js/level.js | 1554 +++++++++++++++++++++++----------------------- js/simulation.js | 1 + js/tech.js | 402 +++++++++++- style.css | 13 + todo.txt | 50 +- 7 files changed, 1204 insertions(+), 851 deletions(-) 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( - ` + `
- - + + - + - + - + - - + + - + - + - + - + - +
Tenter / exit testing modeTtoggle testing
RR teleport to mouse
FF cycle field
GG all guns
Hfill health and energyHfill health, energy
YY random tech
UU next level
I/OI/O zoom in / out
1-81-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