diff --git a/README.md b/README.md index 56af4460..63e9d5eb 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,4 @@ Hosted on my own alternative site [CycloKid's Site](https://cyclokid.github.io/n-gon-personal) -no audio or sfx in this game 😔 (maybe a good thing if youre in school rn tho 🤔) +no audio or sfx in this game 😔 (maybe a good thing if youre in school rn tho 🤔) \ No newline at end of file diff --git a/img/Abelian group.webp b/img/Abelian group.webp new file mode 100644 index 00000000..916f9551 Binary files /dev/null and b/img/Abelian group.webp differ diff --git a/img/MACHO.webp b/img/MACHO.webp index 8570ee05..0875e673 100644 Binary files a/img/MACHO.webp and b/img/MACHO.webp differ diff --git a/img/MOND.webp b/img/MOND.webp new file mode 100644 index 00000000..53a447a0 Binary files /dev/null and b/img/MOND.webp differ diff --git a/img/Newtons 1st law.webp b/img/Newtons 1st law.webp index 82cf9707..02ac9b67 100644 Binary files a/img/Newtons 1st law.webp and b/img/Newtons 1st law.webp differ diff --git a/img/Newtons 2nd law.webp b/img/Newtons 2nd law.webp index eb17dfeb..9e173548 100644 Binary files a/img/Newtons 2nd law.webp and b/img/Newtons 2nd law.webp differ diff --git a/img/Pareto efficiency.webp b/img/Pareto efficiency.webp new file mode 100644 index 00000000..e8d661dd Binary files /dev/null and b/img/Pareto efficiency.webp differ diff --git a/img/propagator.webp b/img/Verlet integration.webp similarity index 100% rename from img/propagator.webp rename to img/Verlet integration.webp diff --git a/img/anyon.webp b/img/anyon.webp new file mode 100644 index 00000000..3f88a63f Binary files /dev/null and b/img/anyon.webp differ diff --git a/img/control theory.webp b/img/control theory.webp new file mode 100644 index 00000000..2843288c Binary files /dev/null and b/img/control theory.webp differ diff --git a/img/dark energy.webp b/img/dark energy.webp new file mode 100644 index 00000000..faca967e Binary files /dev/null and b/img/dark energy.webp differ diff --git a/img/dark matter.webp b/img/dark matter.webp new file mode 100644 index 00000000..8570ee05 Binary files /dev/null and b/img/dark matter.webp differ diff --git a/img/slow light.webp b/img/delayed-choice.webp similarity index 100% rename from img/slow light.webp rename to img/delayed-choice.webp diff --git a/img/deprecated.webp b/img/deprecated.webp new file mode 100644 index 00000000..8ef13875 Binary files /dev/null and b/img/deprecated.webp differ diff --git a/img/entropic gravity.webp b/img/entropic gravity.webp new file mode 100644 index 00000000..339af23d Binary files /dev/null and b/img/entropic gravity.webp differ diff --git a/img/externality.webp b/img/externality.webp new file mode 100644 index 00000000..69fda3a5 Binary files /dev/null and b/img/externality.webp differ diff --git a/img/fermion.webp b/img/fermion.webp new file mode 100644 index 00000000..0de0dab6 Binary files /dev/null and b/img/fermion.webp differ diff --git a/img/hidden-variable theory.webp b/img/hidden-variable theory.webp new file mode 100644 index 00000000..1db74f5d Binary files /dev/null and b/img/hidden-variable theory.webp differ diff --git a/img/inertial confinement.webp b/img/inertial confinement.webp new file mode 100644 index 00000000..068f0c08 Binary files /dev/null and b/img/inertial confinement.webp differ diff --git a/img/instability.webp b/img/instability.webp new file mode 100644 index 00000000..fdcbf8f4 Binary files /dev/null and b/img/instability.webp differ diff --git a/img/interest.webp b/img/interest.webp new file mode 100644 index 00000000..51efb085 Binary files /dev/null and b/img/interest.webp differ diff --git a/img/marginal utility.webp b/img/marginal utility.webp new file mode 100644 index 00000000..92e54bd4 Binary files /dev/null and b/img/marginal utility.webp differ diff --git a/img/peer review.webp b/img/peer review.webp new file mode 100644 index 00000000..0a91809a Binary files /dev/null and b/img/peer review.webp differ diff --git a/img/planned obsolescence.webp b/img/planned obsolescence.webp new file mode 100644 index 00000000..b47d9284 Binary files /dev/null and b/img/planned obsolescence.webp differ diff --git a/img/polariton.webp b/img/polariton.webp new file mode 100644 index 00000000..b512f02d Binary files /dev/null and b/img/polariton.webp differ diff --git a/img/hyperpolarization.webp b/img/repolarization.webp similarity index 100% rename from img/hyperpolarization.webp rename to img/repolarization.webp diff --git "a/img/spin\342\200\223statistics theorem.webp" b/img/spin-statistics theorem.webp similarity index 100% rename from "img/spin\342\200\223statistics theorem.webp" rename to img/spin-statistics theorem.webp diff --git a/img/stability.webp b/img/stability.webp new file mode 100644 index 00000000..f9a7dab2 Binary files /dev/null and b/img/stability.webp differ diff --git a/img/stellarator.webp b/img/stellarator.webp new file mode 100644 index 00000000..49fe2efa Binary files /dev/null and b/img/stellarator.webp differ diff --git a/img/working mass.webp b/img/working mass.webp new file mode 100644 index 00000000..d98237bc Binary files /dev/null and b/img/working mass.webp differ diff --git a/index.html b/index.html index 0aa9e8c2..7e4f9080 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,7 @@ + n-gon @@ -19,7 +20,8 @@
-
+
+
@@ -45,24 +47,17 @@ training -
-
+ +
+
settings
- - -

- +
- - -
-

@@ -205,7 +156,6 @@ PAUSE P - @@ -214,7 +164,6 @@ -
to default keys
@@ -242,6 +191,7 @@
+ diff --git a/js/bullet.js b/js/bullet.js index 7f500981..5459cac5 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -1,7 +1,7 @@ let bullet = []; const b = { - // dmgScale: null, //scales all damage, but not raw .dmg //set in levels.setDifficulty + // dmgScale: null, //scales all damage, but not raw .dmg gravity: 0.0006, //most other bodies have gravity = 0.001 activeGun: null, //current gun in use by player inventoryGun: 0, @@ -85,16 +85,18 @@ const b = { if (tech.crouchAmmoCount && m.crouch) { if (tech.crouchAmmoCount % 2) { b.guns[b.activeGun].ammo--; + if (level.is2xAmmo && b.guns[b.activeGun].ammo > 0) b.guns[b.activeGun].ammo--; simulation.updateGunHUD(); } tech.crouchAmmoCount++ //makes the no ammo toggle off and on } else { b.guns[b.activeGun].ammo--; + if (level.is2xAmmo && b.guns[b.activeGun].ammo > 0) b.guns[b.activeGun].ammo--; simulation.updateGunHUD(); } }, outOfAmmo() { //triggers after firing when you have NO ammo - simulation.makeTextLog(`${b.guns[b.activeGun].name}.ammo: 0`); + simulation.inGameConsole(`${b.guns[b.activeGun].name}.ammo: 0`); m.fireCDcycle = m.cycle + 30; //fire cooldown if (tech.isAmmoFromHealth) { const amount = 0.02 @@ -130,9 +132,9 @@ const b = { // if (b.guns[i].name === name) return b.guns[i].ammo // } // }, - giveGuns(gun = "random", ammoPacks = 10) { - if (tech.ammoCap) ammoPacks = 0.45 * tech.ammoCap - if (tech.isOneGun) b.removeAllGuns(); + giveGuns(gun = "random", ammoPacks = 22) { + if (tech.ammoCap) ammoPacks = tech.ammoCap + if (tech.isOneGun) b.resetAllGuns(); if (gun === "random") { //find what guns player doesn't have options = [] @@ -179,7 +181,7 @@ const b = { //count how many gun tech you have and remove them let gunTechCount = 0 //2 bonus gun tech for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isGunTech && tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable && !tech.tech[i].isRemoveGun) { + if (tech.tech[i].isGunTech && tech.tech[i].count > 0 && !tech.tech[i].isInstant && !tech.tech[i].isRemoveGun) { const remove = tech.removeTech(i) gunTechCount += remove } @@ -198,8 +200,8 @@ const b = { } if (gunTechPool.length) { const index = Math.floor(Math.random() * gunTechPool.length) + simulation.inGameConsole(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`) tech.giveTech(gunTechPool[index]) // choose from the gun pool - simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`) } else { tech.giveTech() //get normal tech if you can't find any gun tech } @@ -229,11 +231,12 @@ const b = { } b.setFireCD(); }, - removeAllGuns() { + resetAllGuns() { b.inventory = []; //removes guns and ammo for (let i = 0, len = b.guns.length; i < len; ++i) { b.guns[i].count = 0; b.guns[i].have = false; + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; if (b.guns[i].ammo != Infinity) b.guns[i].ammo = 0; } tech.buffedGun = 0 @@ -286,9 +289,9 @@ const b = { setFireCD() { b.fireCDscale = tech.fireRate * tech.slowFire * tech.researchHaste * tech.aimDamage if (m.fieldMode === 6) b.fireCDscale *= 0.8 - if (tech.isFastTime) b.fireCDscale *= 0.5 - if (tech.isFireRateForGuns) b.fireCDscale *= Math.pow(0.82, Math.max(0, b.inventory.length - 1)) - if (tech.isFireMoveLock) b.fireCDscale *= 0.23 // 77% fire rate + if (tech.isFastTime) b.fireCDscale *= 0.666 + if (tech.isFireRateForGuns) b.fireCDscale *= Math.pow(0.76923, Math.max(0, b.inventory.length - 1)) + if (tech.isFireMoveLock) b.fireCDscale *= 0.25 }, fireAttributes(dir, rotate = true) { if (rotate) { @@ -369,7 +372,7 @@ const b = { } }, explosionRange() { - return tech.explosiveRadius * (tech.isExplosionHarm ? 1.7 : 1) * (tech.isSmallExplosion ? 0.66 : 1) * (tech.isExplodeRadio ? 1.25 : 1) + return tech.explosiveRadius * (tech.isExplosionHarm ? 1.7 : 1) * (tech.isSmallExplosion ? 0.7 : 1) * (tech.isExplodeRadio ? 1.25 : 1) }, explosion(where, radius, color = "rgba(255,25,0,0.6)") { // typically explode is used for some bullets with .onEnd radius *= tech.explosiveRadius @@ -379,8 +382,8 @@ const b = { if (tech.isExplosionHarm) radius *= 1.7 // 1/sqrt(2) radius -> area if (tech.isSmallExplosion) { // color = "rgba(255,0,30,0.7)" - radius *= 0.66 - dmg *= 1.66 + radius *= 0.7 + dmg *= 1.7 } if (tech.isExplodeRadio) { //radiation explosion @@ -397,11 +400,11 @@ const b = { //player damage if (Vector.magnitude(Vector.sub(where, player.position)) < radius) { - const DRAIN = (tech.isExplosionHarm ? 0.6 : 0.45) * (tech.isRadioactiveResistance ? 0.25 : 1) + const DRAIN = (tech.isExplosionHarm ? 0.6 : 0.45) * (tech.isRadioactiveResistance ? 0.2 : 1) if (m.immuneCycle < m.cycle) m.energy -= DRAIN if (m.energy < 0) { m.energy = 0 - if (simulation.dmgScale) m.damage(tech.radioactiveDamage * 0.03 * (tech.isRadioactiveResistance ? 0.25 : 1)); + if (simulation.dmgScale) m.damage(tech.radioactiveDamage * 0.03 * (tech.isRadioactiveResistance ? 0.2 : 1)); } } @@ -835,10 +838,7 @@ const b = { suck([player], this.explodeRad * 1.3) } - Matter.Body.setVelocity(this, { - x: 0, - y: 0 - }); //keep bomb in place + Matter.Body.setVelocity(this, { x: 0, y: 0 }); //keep bomb in place //draw suck const radius = 2.75 * this.explodeRad * (this.endCycle - simulation.cycle) / this.suckCycles ctx.fillStyle = "rgba(0,0,0,0.1)"; @@ -1032,12 +1032,12 @@ const b = { } else { //aoe damage to player if (Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) { - const DRAIN = (tech.isRadioactiveResistance ? 0.0025 * 0.25 : 0.0025) + const DRAIN = (tech.isRadioactiveResistance ? 0.0025 * 0.2 : 0.0025) if (m.energy > DRAIN) { if (m.immuneCycle < m.cycle) m.energy -= DRAIN } else { m.energy = 0; - if (simulation.dmgScale) m.damage((tech.isRadioactiveResistance ? 0.00016 * 0.25 : 0.00016) * tech.radioactiveDamage) //0.00015 + if (simulation.dmgScale) m.damage((tech.isRadioactiveResistance ? 0.00016 * 0.2 : 0.00016) * tech.radioactiveDamage) //0.00015 } } //aoe damage to mobs @@ -1362,29 +1362,6 @@ const b = { ctx.lineWidth = 0.5 ctx.stroke(); - // ctx.lineTo(this.vertices[0].x, this.vertices[0].y); - // if (tech.isHookWire) { - // //draw wire - // const hitMob = Matter.Query.ray(mob, this.position, m.pos, 10) - // if (hitMob.length && m.immuneCycle < m.cycle) { - // for (let i = 0; i < hitMob.length; i++) { - // console.log(hitMob[i].bodyA) - // // simulation.drawList.push({ //add dmg to draw queue - // // x: path[path.length - 1].x, - // // y: path[path.length - 1].y, - // // radius: Math.sqrt(2000 * damage * best.who.damageReduction) + 2, - // // color: tech.laserColorAlpha, - // // time: simulation.drawTime - // // }); - // hitMob[i].bodyA.damage(0.001) - // } - // } - // //draw glow around wire - // ctx.strokeStyle = "rgba(0,255,255,0.2)" // "#0ce" - // ctx.lineWidth = 20 - // ctx.stroke(); - // } - if (this.powerUpDamage) { ctx.beginPath(); ctx.moveTo(this.vertices[0].x, this.vertices[0].y); @@ -1418,9 +1395,7 @@ const b = { beforeDmg(who) { if (tech.isShieldPierce && who.isShielded) { //disable shields who.isShielded = false - requestAnimationFrame(() => { - who.isShielded = true - }); + requestAnimationFrame(() => { who.isShielded = true }); } if (m.fieldCDcycle < m.cycle + 40) m.fieldCDcycle = m.cycle + 40 //extra long cooldown on hitting mobs if (tech.hookNails) { @@ -1503,8 +1478,10 @@ const b = { if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) this.collisionFilter.mask = 0//cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body //recoil on pulling grapple back - const mag = this.pickUpTarget ? Math.max(this.pickUpTarget.mass, 0.5) : 0.5 - const momentum = Vector.mult(Vector.sub(this.position, m.pos), mag * (m.crouch ? 0.0001 : 0.0002)) + // if (this.pickUpTarget.mass) console.log(this.pickUpTarget.mass) + const mag = this.pickUpTarget ? Math.min(5, Math.max(this.pickUpTarget.mass, 0.5)) : 0.5 + const unit = Vector.normalise(Vector.sub(this.position, m.pos)) + const momentum = Vector.mult(unit, mag * (m.crouch ? 0.1 : 0.2)) player.force.x += momentum.x player.force.y += momentum.y }, @@ -1513,13 +1490,15 @@ const b = { if (Vector.magnitude(Vector.sub(this.position, m.pos)) < returnRadius) { //near player this.endCycle = 0; //recoil on catching grapple - const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + // const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + const unit = Vector.normalise(Vector.sub(this.velocity, player.velocity)) + const momentum = Vector.mult(unit, (m.crouch ? 0.0001 : 0.0002)) player.force.x += momentum.x player.force.y += momentum.y if (this.pickUpTarget) { if (tech.isReel && this.blockDist > 150) { // console.log(0.0003 * Math.min(this.blockDist, 1000)) - m.energy += 0.00113 * Math.min(this.blockDist, 800) //max 0.352 energy + m.energy += 0.00113 * Math.min(this.blockDist, 800) * level.isReducedRegen //max 0.352 energy simulation.drawList.push({ //add dmg to draw queue x: m.pos.x, y: m.pos.y, @@ -1532,8 +1511,11 @@ const b = { // give block to player after it returns m.isHolding = true; //conserve momentum when player mass changes - totalMomentum = Vector.add(Vector.mult(player.velocity, player.mass), Vector.mult(Vector.normalise(this.velocity), 15 * Math.min(20, this.pickUpTarget.mass))) - Matter.Body.setVelocity(player, Vector.mult(totalMomentum, 1 / (m.defaultMass + this.pickUpTarget.mass))); + const blockMass = Math.min(5, this.pickUpTarget.mass) + const grappleMomentum = Vector.mult(Vector.normalise(this.velocity), 15 * blockMass) + const playerMomentum = Vector.mult(player.velocity, player.mass) + totalMomentum = Vector.add(playerMomentum, grappleMomentum) + Matter.Body.setVelocity(player, Vector.mult(totalMomentum, 1 / (m.defaultMass + blockMass))); m.definePlayerMass(m.defaultMass + this.pickUpTarget.mass * m.holdingMassScale) //make block collide with nothing @@ -1544,7 +1526,7 @@ const b = { } else { if (m.energy > this.drain) m.energy -= this.drain const sub = Vector.sub(this.position, m.pos) - const rangeScale = 1 + 0.000001 * Vector.magnitude(sub) * Vector.magnitude(sub) //return faster when far from player + const rangeScale = 1 + 0.000003 * Vector.magnitude(sub) //return faster when far from player const returnForce = Vector.mult(Vector.normalise(sub), rangeScale * this.thrustMag * this.mass) this.force.x -= returnForce.x this.force.y -= returnForce.y @@ -1584,16 +1566,13 @@ const b = { //position block on hook Matter.Body.setPosition(this.pickUpTarget, Vector.add(this.vertices[2], this.velocity)) Matter.Body.setVelocity(this.pickUpTarget, { x: 0, y: 0 }) - } else { // if (!input.down) + } else { const blocks = Matter.Query.collides(this, body) if (blocks.length) { - // console.log(blocks) for (let i = 0; i < blocks.length; i++) { - if (blocks[i].bodyA.classType === "body" && !blocks[i].bodyA.isNotHoldable && blocks[0].bodyA.mass < 60) { + if (blocks[i].bodyA.classType === "body" && !blocks[i].bodyA.isNotHoldable && blocks[0].bodyA.mass < 40) { this.retract() if (tech.hookNails) { - // if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + 5; //player is immune to damage for 5 cycles - // b.explosion(this.position, 300 + 150 * Math.random()); //makes bullet do explosive damage at end b.targetedNail(this.position, 3 * tech.hookNails) const ANGLE = 2 * Math.PI * Math.random() //make a few random ones for (let i = 0; i < 13; i++) b.nail(this.position, { x: 10.5 * Math.cos(ANGLE), y: 10.5 * Math.sin(ANGLE) }, 1.2) @@ -1622,22 +1601,7 @@ const b = { this.blockDist = Vector.magnitude(Vector.sub(this.pickUpTarget.position, m.pos)) } } - // else if (blocks[i].bodyB.classType === "body" && !blocks[i].bodyB.isNotHoldable && blocks[0].bodyB.mass < 60) { - // this.retract() - // this.pickUpTarget = blocks[i].bodyB - // this.blockDist = Vector.magnitude(Vector.sub(this.pickUpTarget.position, m.pos)) - // if (tech.hookNails) { - // // if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + 5; //player is immune to damage for 5 cycles - // // b.explosion(this.position, 300 + 150 * Math.random()); //makes bullet do explosive damage at end - // b.targetedNail(this.position, tech.hookNails) - // const ANGLE = 2 * Math.PI * Math.random() //make a few random ones - // for (let i = 0; i < 4; i++) b.nail(this.position, { x: 10.5 * Math.cos(ANGLE), y: 10.5 * Math.sin(ANGLE) }, 1.2) - - // } - // } } - // if (blocks[0].bodyA.mass > 2.5 && blocks[0].bodyA.mass > 15) { - } } }, @@ -1667,41 +1631,17 @@ const b = { }, do() { if (m.fieldCDcycle < m.cycle + 5) m.fieldCDcycle = m.cycle + 5 - if (input.field) { //&& !Matter.Query.collides(this, body).length - // this.destroyBlocks() + if (input.field) { this.grabBlocks() this.grabPowerUp() - // if (this.endCycle < simulation.cycle + 1) { //if at end of lifespan, but player is holding down field, force retraction - // this.endCycle = simulation.cycle + 30 - // // m.fireCDcycle = m.cycle + 120 // cool down - // this.do = this.returnToPlayer - // Matter.Body.setDensity(this, 0.0005); //reduce density on return - // if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) - // this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body - // } } else { - //if not enough energy - // if (m.energy < 0.01) this.dropCaughtPowerUp() - // const returnForce = Vector.mult(Vector.normalise(Vector.sub(this.position, m.pos)), 3 * this.thrustMag * this.mass) - // this.force.x -= returnForce.x - // this.force.y -= returnForce.y - // this.frictionAir = 0.002 - // this.do = () => { - // if (this.speed < 20) this.force.y += 0.0005 * this.mass; - // } - - // } else { - //return to player this.retract() - // } } //grappling hook if (input.field && Matter.Query.collides(this, map).length) { Matter.Body.setPosition(this, Vector.add(this.position, { x: -20 * Math.cos(this.angle), y: -20 * Math.sin(this.angle) })) if (Matter.Query.collides(this, map).length) { if (tech.hookNails) { - // if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + 5; //player is immune to damage for 5 cycles - // b.explosion(this.position, 200 + 150 * Math.random()); //makes bullet do explosive damage at end b.targetedNail(this.position, tech.hookNails) const ANGLE = 2 * Math.PI * Math.random() //make a few random ones for (let i = 0; i < 4; i++) b.nail(this.position, { x: 10.5 * Math.cos(ANGLE), y: 10.5 * Math.sin(ANGLE) }, 1.2) @@ -1711,22 +1651,14 @@ const b = { Matter.Body.setVelocity(this, { x: 0, y: 0 }); Matter.Sleeping.set(this, true) this.endCycle = simulation.cycle + 5 - // this.dropCaughtPowerUp() this.do = () => { if (m.fieldCDcycle < m.cycle + 5) m.fieldCDcycle = m.cycle + 5 - // if (this.caughtPowerUp) { - // Matter.Body.setPosition(this.caughtPowerUp, Vector.add(this.vertices[2], this.velocity)) - // Matter.Body.setVelocity(this.caughtPowerUp, { x: 0, y: 0 }) - // } this.grabPowerUp() //between player nose and the grapple const sub = Vector.sub(this.vertices[0], { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) }) let dist = Vector.magnitude(sub) if (input.field) { - // m.fireCDcycle = m.cycle + 30; // cool down if out of energy - // m.fireCDcycle = m.cycle + 5 + 40 * b.fireCDscale + 60 * (m.energy < 0.05) - // if (m.fieldCDcycle < m.cycle + 5) m.fieldCDcycle = m.cycle + 5 this.endCycle = simulation.cycle + 10 if (input.down) { //down this.isSlowPull = true @@ -1740,52 +1672,18 @@ const b = { // pulling friction that allowed a slight swinging, but has high linear pull at short dist const drag = 1 - 30 / Math.min(Math.max(100, dist), 700) - 0.1 * (player.speed > 66) - // console.log(player.speed) Matter.Body.setVelocity(player, { x: player.velocity.x * drag, y: player.velocity.y * drag }); - const pullScale = 0.0004 - const pull = Vector.mult(Vector.normalise(sub), pullScale * Math.min(Math.max(15, dist), this.isSlowPull ? 70 : 200)) + const pull = Vector.mult(Vector.normalise(sub), 0.0004 * Math.min(Math.max(15, dist), this.isSlowPull ? 70 : 200)) //original pulling force with high friction and very linear pull // Matter.Body.setVelocity(player, { x: player.velocity.x * 0.85, y: player.velocity.y * 0.85 }); // const pull = Vector.mult(Vector.normalise(sub), 0.0008 * Math.min(Math.max(15, dist), this.isSlowPull ? 100 : 200)) player.force.x += pull.x player.force.y += pull.y - if (dist > 500) { - m.energy -= this.drain - // if (m.energy < 0) this.endCycle = 0; - } - - // if (tech.isImmuneGrapple && m.immuneCycle < m.cycle + 10) { - // m.immuneCycle = m.cycle + 10; - // if (m.energy > 0.001) { - // m.energy -= 0.001 - // } else { //out of energy - // Matter.Sleeping.set(this, false) - // this.collisionFilter.category = 0 - // this.collisionFilter.mask = 0 - // this.do = this.returnToPlayer - // this.endCycle = simulation.cycle + 60 - // // m.fireCDcycle = m.cycle + 120; //fire cooldown - // if (m.fieldCDcycle < m.cycle + 120) m.fieldCDcycle = m.cycle + 120 - - // //recoil on catching - // const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) - // player.force.x += momentum.x - // player.force.y += momentum.y - // } - // } + if (dist > 500) m.energy -= this.drain } else { Matter.Sleeping.set(this, false) this.retract() - // Matter.Sleeping.set(this, false) - // this.collisionFilter.category = 0 - // this.collisionFilter.mask = 0 - // this.do = this.returnToPlayer - // this.endCycle = simulation.cycle + 60 - // //recoil on catching - // const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) - // player.force.x += momentum.x - // player.force.y += momentum.y } this.draw(); } @@ -1827,7 +1725,7 @@ const b = { friction: 1, frictionAir: 0.4, // thrustMag: 0.1, - drain: tech.isRailEnergy ? 0 : 0.006, + drain: tech.isRailEnergy ? 0.0002 : 0.006, turnRate: isReturn ? 0.1 : 0.03, //0.015 drawStringControlMagnitude: 3000 + 5000 * Math.random(), drawStringFlip: (Math.round(Math.random()) ? 1 : -1), @@ -1841,6 +1739,7 @@ const b = { minDmgSpeed: 4, lookFrequency: Math.floor(7 + Math.random() * 3), density: tech.harpoonDensity, //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + foamSpawned: 0, beforeDmg(who) { if (tech.isShieldPierce && who.isShielded) { //disable shields who.isShielded = false @@ -1863,17 +1762,18 @@ const b = { } } } - if (tech.isFoamBall) { - for (let i = 0, len = Math.min(30, 2 + 2 * Math.sqrt(this.mass)); i < len; i++) { - const radius = 5 + 8 * Math.random() + if (tech.isFoamBall && this.foamSpawned < 55) { + for (let i = 0, len = Math.min(30, 2 + 3 * Math.sqrt(this.mass)); i < len; i++) { + const radius = 5 + 9 * Math.random() const velocity = { x: Math.max(0.5, 2 - radius * 0.1), y: 0 } b.foam(this.position, Vector.rotate(velocity, 6.28 * Math.random()), radius) + this.foamSpawned++ } } if (tech.isHarpoonPowerUp && simulation.cycle - 480 < tech.harpoonPowerUpCycle) { Matter.Body.setDensity(this, 1.8 * tech.harpoonDensity); //+90% damage after pick up power up for 8 seconds } else if (tech.isHarpoonFullHealth && who.health === 1) { - Matter.Body.setDensity(this, 2.11 * tech.harpoonDensity); //+90% damage if mob has full health do + Matter.Body.setDensity(this, 2.2 * tech.harpoonDensity); //+90% damage if mob has full health do simulation.ephemera.push({ name: "harpoon outline", count: 2, //cycles before it self removes @@ -1976,6 +1876,7 @@ const b = { // refund ammo if (isReturnAmmo) { b.guns[9].ammo++; + if (level.is2xAmmo) b.guns[9].ammo++; simulation.updateGunHUD(); // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun // if (b.guns[i].name === "harpoon") { @@ -2288,14 +2189,9 @@ const b = { //calculate laser collision let range = tech.isPlasmaRange * (120 + (m.crouch ? 400 : 300) * Math.sqrt(Math.random())) //+ 100 * Math.sin(m.cycle * 0.3); // const dir = m.angle // + 0.04 * (Math.random() - 0.5) - const path = [{ - x: m.pos.x + 20 * Math.cos(m.angle), - y: m.pos.y + 20 * Math.sin(m.angle) - }, - { - x: m.pos.x + range * Math.cos(m.angle), - y: m.pos.y + range * Math.sin(m.angle) - } + const path = [ + { x: m.pos.x + 20 * Math.cos(m.angle), y: m.pos.y + 20 * Math.sin(m.angle) }, + { x: m.pos.x + range * Math.cos(m.angle), y: m.pos.y + range * Math.sin(m.angle) } ]; //check for collisions let best = { @@ -2315,13 +2211,14 @@ const b = { best.who.locatePlayer(); //push mobs away - const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.01 * Math.min(5, best.who.mass)) - Matter.Body.applyForce(best.who, path[1], force) - if (best.who.speed > 4) { - Matter.Body.setVelocity(best.who, { //friction - x: best.who.velocity.x * 0.9, - y: best.who.velocity.y * 0.9 - }); + if (best.who.speed > 3) { + const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.005 * Math.min(5, best.who.mass)) + Matter.Body.applyForce(best.who, path[1], force) + Matter.Body.setVelocity(best.who, { x: best.who.velocity.x * 0.4, y: best.who.velocity.y * 0.4 }); + } else { + const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.01 * Math.min(5, best.who.mass)) + Matter.Body.applyForce(best.who, path[1], force) + Matter.Body.setVelocity(best.who, { x: best.who.velocity.x * 0.7, y: best.who.velocity.y * 0.7 }); } //draw mob damage circle simulation.drawList.push({ @@ -2375,22 +2272,8 @@ const b = { }, dmg = tech.laserDamage, reflections = tech.laserReflections, isThickBeam = false, push = 1) { const reflectivity = 1 - 1 / (reflections * 3) let damage = m.dmgScale * dmg - - let best = { - x: 1, - y: 1, - dist2: Infinity, - who: null, - v1: 1, - v2: 1 - }; - const path = [{ - x: where.x, - y: where.y - }, { - x: whereEnd.x, - y: whereEnd.y - }]; + let best = { x: 1, y: 1, dist2: Infinity, who: null, v1: 1, v2: 1 }; + const path = [{ x: where.x, y: where.y }, { x: whereEnd.x, y: whereEnd.y }]; const checkForCollisions = function () { best = vertexCollision(path[path.length - 2], path[path.length - 1], [mob, map, body]); @@ -2704,28 +2587,13 @@ const b = { this.lookFrequency = 55 + Math.floor(22 * Math.random()) simulation.drawList.push({ x: this.position.x, y: this.position.y, radius: 10, color: "#f00", time: 4 }); this.do = function () { //overwrite the do method for this bullet - - - //make mobs think the mine is where the player is - // for (let i = 0; i < mob.length; i++) { - // mob[i].seePlayer.recall = mob[i].memory + Math.round(mob[i].memory * Math.random()); //cycles before mob falls a sleep - // mob[i].seePlayer.position.x = this.position.x; - // mob[i].seePlayer.position.y = this.position.y; - // mob[i].seePlayer.yes = true; - // } - - this.force.y += this.mass * 0.002; //extra gravity if (!(simulation.cycle % this.lookFrequency)) { //find mob targets - - - - - const random = 300 * Math.random() for (let i = 0, len = mob.length; i < len; ++i) { if ( !mob[i].isBadTarget && + !mob[i].isInvulnerable && Vector.magnitude(Vector.sub(this.position, mob[i].position)) < this.range + mob[i].radius + random && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && Matter.Query.ray(body, this.position, mob[i].position).length === 0 @@ -3057,7 +2925,7 @@ const b = { if (!who.isInvulnerable) { if (tech.iceEnergy && !who.shield && !who.isShielded && who.isDropPowerUp && who.alive && m.immuneCycle < m.cycle) { setTimeout(() => { - if (!who.alive) m.energy += tech.iceEnergy * 0.8 + if (!who.alive) m.energy += tech.iceEnergy * 0.8 * level.isReducedRegen }, 10); } mobs.statusSlow(who, tech.iceIXFreezeTime) @@ -3134,13 +3002,13 @@ const b = { cd: simulation.cycle + 10, dmg: 0, setDamage() { //dmg is set to zero after doing damage once, and set back to normal after jumping - this.dmg = radius * (tech.isMutualism ? 2.9 : 1) //damage done in addition to the damage from momentum //spores do 7 dmg, worms do 18 + this.dmg = radius * (tech.isMutualism ? 3.3 : 1.1) //damage done in addition to the damage from momentum //spores do 7 dmg, worms do 18 }, beforeDmg(who) { Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), 10 + 10 * Math.random())); //push away from target this.cd = simulation.cycle + this.delay; - if (!who.isInvulnerable) { - this.endCycle -= 130 + if (!who.isInvulnerable && this.dmg !== 0) { + this.endCycle -= 110 if (tech.isSporeFreeze) mobs.statusSlow(who, 90) if (tech.isSpawnBulletsOnDeath && who.alive && who.isDropPowerUp) { setTimeout(() => { @@ -3197,10 +3065,7 @@ const b = { if (tech.isSporeFollow && !this.lockedOn && Matter.Query.ray(map, this.position, m.pos).length === 0) { this.lockedOn = { //make target player if there are no mobs to target position: m.pos, - velocity: { - x: 0, - y: 0 - } + velocity: { x: 0, y: 0 } } } if (this.lockedOn) { //hop towards mob target @@ -3222,10 +3087,7 @@ const b = { } this.force.y = -(0.03 + 0.08 * Math.random()) * this.mass } - Matter.Body.setVelocity(this, { - x: 0, - y: 0 - }); + Matter.Body.setVelocity(this, { x: 0, y: 0 }); this.setDamage() //after jumping damage is no longer zero } } @@ -3238,7 +3100,7 @@ const b = { bullet[bullet.length - 1].isMutualismActive = true } }, - delayDrones(where, droneCount = 1) { + delayDrones(where, droneCount = 1, deliveryCount = 0) { let respawnDrones = () => { if (droneCount > 0) { requestAnimationFrame(respawnDrones); @@ -3248,7 +3110,7 @@ const b = { b.droneRadioactive({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0) } else { b.drone({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0) - if (tech.isDroneGrab && deliveryCount > 0) { + if (tech.isDroneGrab && deliveryCount > 0) { // const who = bullet[bullet.length - 1] who.isImproved = true; const SCALE = 2.25 @@ -3295,26 +3157,26 @@ const b = { Matter.Body.setVelocity(this, { x: unit.x, y: unit.y }); this.lockedOn = null } else { - if (tech.isIncendiary && simulation.cycle + this.deathCycles < this.endCycle && !tech.isForeverDrones) { - const max = Math.max(Math.min(this.endCycle - simulation.cycle - this.deathCycles, 1500), 0) - b.explosion(this.position, max * 0.14 + this.isImproved * 110 + 60 * Math.random()); //makes bullet do explosive damage at end - if (tech.isForeverDrones) { - this.endCycle = 0 - b.drone({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) - bullet[bullet.length - 1].endCycle = Infinity - } else { - this.endCycle -= max - } - } else { - //move away from target after hitting - const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20) - Matter.Body.setVelocity(this, { x: unit.x, y: unit.y }); - this.lockedOn = null - if (this.endCycle > simulation.cycle + this.deathCycles) { - this.endCycle -= 60 - if (simulation.cycle + this.deathCycles > this.endCycle) this.endCycle = simulation.cycle + this.deathCycles - } + // if (tech.isIncendiary && simulation.cycle + this.deathCycles < this.endCycle && !tech.isForeverDrones) { + // const max = Math.max(Math.min(this.endCycle - simulation.cycle - this.deathCycles, 1500), 0) + // b.explosion(this.position, max * 0.14 + this.isImproved * 110 + 60 * Math.random()); //makes bullet do explosive damage at end + // if (tech.isForeverDrones) { + // this.endCycle = 0 + // b.drone({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) + // bullet[bullet.length - 1].endCycle = Infinity + // } else { + // this.endCycle -= max + // } + // } else { + //move away from target after hitting + const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20) + Matter.Body.setVelocity(this, { x: unit.x, y: unit.y }); + this.lockedOn = null + if (this.endCycle > simulation.cycle + this.deathCycles) { + this.endCycle -= 60 + if (simulation.cycle + this.deathCycles > this.endCycle) this.endCycle = simulation.cycle + this.deathCycles } + // } } }, onEnd() { @@ -3328,12 +3190,12 @@ const b = { const distB = Vector.magnitude(Vector.sub(this.position, b.position)) return distA < distB ? a : b }) - if (found && m.energy > 0.05) { - m.energy -= 0.05 + if (found && m.energy > 0.041) { + m.energy -= 0.04 //remove the body and spawn a new drone Composite.remove(engine.world, found) body.splice(body.indexOf(found), 1) - b.delayDrones(found.position, 0.7 * Math.sqrt(found.mass)) + b.delayDrones(found.position, Math.sqrt(found.mass)) //draw a line from the drone to the body on the canvas ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); @@ -3352,9 +3214,7 @@ const b = { ctx.beginPath(); let vertices = found.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1; j < vertices.length; j++) { - ctx.lineTo(vertices[j].x, vertices[j].y); - } + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 2; ctx.strokeStyle = `rgba(0,0,0,${this.count / 60})` @@ -3379,8 +3239,15 @@ const b = { const scale = 0.995; Matter.Body.scale(this, scale, scale); }, + hasExploded: false, do() { if (simulation.cycle + this.deathCycles > this.endCycle) { + if (tech.isIncendiary && !this.hasExploded) { + this.hasExploded = true + // const max = Math.max(Math.min(this.endCycle - simulation.cycle - this.deathCycles, 1500), 0) + // this.endCycle -= max + b.explosion(this.position, 200 + this.isImproved * 110 + 60 * Math.random()); //makes bullet do explosive damage at end + } this.restitution = 0.2; if (tech.isDroneRespawn) { this.do = this.doRespawning @@ -3570,12 +3437,12 @@ const b = { const distB = Vector.magnitude(Vector.sub(this.position, b.position)) return distA < distB ? a : b }) - if (found && m.energy > 0.05) { - m.energy -= 0.1 + if (found && m.energy > 0.091) { + m.energy -= 0.09 //remove the body and spawn a new drone Composite.remove(engine.world, found) body.splice(body.indexOf(found), 1) - b.delayDrones(found.position, 0.35 * Math.sqrt(found.mass)) + b.delayDrones(found.position, 0.5 * Math.sqrt(found.mass)) //draw a line from the drone to the body on the canvas ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); @@ -3617,7 +3484,7 @@ const b = { if (m.immuneCycle < m.cycle) m.energy -= DRAIN } else { m.energy = 0; - if (simulation.dmgScale) m.damage((tech.isRadioactiveResistance ? 0.00005 : 0.0002) * tech.radioactiveDamage) //0.00015 + if (simulation.dmgScale) m.damage((tech.isRadioactiveResistance ? 0.00004 : 0.0002) * tech.radioactiveDamage) //0.00015 } } //aoe damage to mobs @@ -3774,13 +3641,17 @@ const b = { }); }, superBall(where, velocity, radius) { + let gravity = 0.001 + if (tech.superBallDelay) { + velocity = Vector.mult(velocity, 1.4) + gravity *= 6 + } let dir = m.angle const me = bullet.length; bullet[me] = Bodies.polygon(where.x, where.y, 12, radius, b.fireAttributes(dir, false)); Composite.add(engine.world, bullet[me]); //add bullet to world Matter.Body.setVelocity(bullet[me], velocity); - bullet[me].calcDensity = function () { return 0.0007 + 0.00065 * tech.isSuperHarm + 0.0004 * tech.isBulletTeleport } - Matter.Body.setDensity(bullet[me], bullet[me].calcDensity()); + Matter.Body.setDensity(bullet[me], 0.0007 + 0.0007 * tech.isSuperHarm + 0.0007 * tech.isBulletTeleport); bullet[me].endCycle = simulation.cycle + Math.floor(270 + 90 * Math.random()); bullet[me].minDmgSpeed = 0; bullet[me].restitution = 1; @@ -3789,17 +3660,18 @@ const b = { bullet[me].frictionStatic = 0; if (tech.isSuperHarm) { bullet[me].collidePlayerDo = function () { - this.force.y += this.mass * 0.001; + this.force.y += this.mass * gravity;; if (Matter.Query.collides(this, [player]).length) { this.endCycle = 0 - m.energy -= m.energy * 0.2 - simulation.drawList.push({ //add dmg to draw queue - x: this.position.x, - y: this.position.y, - radius: radius, - color: "#0ad", - time: 15 - }); + // m.energy -= 0.04 + // if (m.energy < 0) m.energy = 0 + // simulation.drawList.push({ //add dmg to draw queue + // x: this.position.x, + // y: this.position.y, + // radius: radius, + // color: "#0ad", + // time: 15 + // }); } } bullet[me].cycle = 0 @@ -3812,7 +3684,7 @@ const b = { bullet[me].portFrequency = 25 + Math.floor(10 * Math.random()) bullet[me].nextPortCycle = simulation.cycle + bullet[me].portFrequency bullet[me].do = function () { - this.force.y += this.mass * 0.001; + this.force.y += this.mass * gravity; if (this.nextPortCycle < simulation.cycle) { //teleport around if you have tech.isBulletTeleport this.nextPortCycle = simulation.cycle + this.portFrequency const range = 33 * Math.sqrt(radius) * Math.random() @@ -3822,15 +3694,15 @@ const b = { }; } else { bullet[me].do = function () { - this.force.y += this.mass * 0.001; + this.force.y += this.mass * gravity; }; } bullet[me].beforeDmg = function (who) { if (!who.isInvulnerable) { if (tech.oneSuperBall) mobs.statusStun(who, 120) // (2.3) * 2 / 14 ticks (2x damage over 7 seconds) if (tech.isFoamBall) { - for (let i = 0, len = 5 * this.mass; i < len; i++) { - const radius = 5 + 8 * Math.random() + for (let i = 0, len = 6 * this.mass; i < len; i++) { + const radius = 6 + 9 * Math.random() const velocity = { x: Math.max(0.5, 2 - radius * 0.1), y: 0 } b.foam(this.position, Vector.rotate(velocity, 6.28 * Math.random()), radius) } @@ -3841,10 +3713,9 @@ const b = { this.endCycle = 0 } else if (tech.isSuperBounce) { const cycle = () => { - Matter.Body.setDensity(bullet[me], bullet[me].calcDensity() * 1.33);//33% more density and damage + Matter.Body.setDensity(this, (0.0007 + 0.0007 * tech.isSuperHarm + 0.0007 * tech.isBulletTeleport) * 1.33);//33% more density and damage this.endCycle = simulation.cycle + Math.floor(300 + 90 * Math.random()); //reset to full duration of time Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(this.velocity), 60)); //reset to high velocity - let count = 5 const wait = () => { count-- @@ -3858,7 +3729,6 @@ const b = { }); } requestAnimationFrame(wait); - simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, @@ -4645,31 +4515,31 @@ const b = { for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) - }) + }, !level.isSlowBots) for (let i = 0; i < tech.laserBotCount; i++) b.laserBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) - }) + }, !level.isSlowBots) for (let i = 0; i < tech.nailBotCount; i++) b.nailBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) - }) + }, !level.isSlowBots) for (let i = 0; i < tech.foamBotCount; i++) b.foamBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) - }) + }, !level.isSlowBots) for (let i = 0; i < tech.soundBotCount; i++) b.soundBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) - }) + }, !level.isSlowBots) for (let i = 0; i < tech.boomBotCount; i++) b.boomBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) - }) + }, !level.isSlowBots) for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) - }) + }, !level.isSlowBots) for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) @@ -4774,7 +4644,6 @@ const b = { } }, dynamoBot(position = player.position, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.dynamoBot()`); const me = bullet.length; bullet[me] = Bodies.polygon(position.x, position.y, 5, 10, { isUpgraded: tech.isDynamoBotUpgrade, @@ -4814,7 +4683,7 @@ const b = { if (Vector.magnitude(Vector.sub(this.position, player.position)) < 250 && m.immuneCycle < m.cycle) { //give energy Matter.Body.setAngularVelocity(this, this.spin) if (this.isUpgraded) { - m.energy += 0.12 + m.energy += 0.12 * level.isReducedRegen simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, @@ -4823,7 +4692,7 @@ const b = { time: simulation.drawTime }); } else { - m.energy += 0.04 + m.energy += 0.04 * level.isReducedRegen simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, @@ -4836,7 +4705,7 @@ const b = { } if (!m.isCloak) { //if cloaking field isn't active - const size = 33 + const size = 33 - 6 * isKeep q = Matter.Query.region(mob, { min: { x: this.position.x - size, @@ -4876,7 +4745,6 @@ const b = { b.setDynamoBotDelay() }, nailBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.nailBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = (12 + 4 * Math.random()) @@ -4892,7 +4760,7 @@ const b = { minDmgSpeed: 2, // lookFrequency: 56 + Math.floor(17 * Math.random()) - isUpgraded * 20, lastLookCycle: simulation.cycle + 60 * Math.random(), - delay: Math.floor((tech.isNailBotUpgrade ? 18 : 85) * b.fireCDscale), + delay: Math.floor((tech.isNailBotUpgrade ? 22 : 85) + 10 * isKeep), acceleration: (isKeep ? 0.005 : 0.001) * (1 + 0.5 * Math.random()), range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots() + !isKeep * 100, endCycle: Infinity, @@ -4941,7 +4809,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, missileBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.missileBot()`); const me = bullet.length; bullet[me] = Bodies.rectangle(position.x, position.y, 28, 11, { botType: "missile", @@ -4954,7 +4821,7 @@ const b = { minDmgSpeed: 2, lookFrequency: 26 + Math.ceil(6 * Math.random()), cd: 0, - delay: Math.floor(60 * b.fireCDscale), + delay: Math.floor(60), range: 70 + 3 * b.totalBots(), endCycle: Infinity, classType: "bullet", @@ -5012,7 +4879,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, foamBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.foamBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = (10 + 5 * Math.random()) @@ -5029,8 +4895,8 @@ const b = { lookFrequency: 60 + Math.floor(17 * Math.random()) - 50 * tech.isFoamBotUpgrade, cd: 0, fireCount: 0, - fireLimit: 5 + 2 * tech.isFoamBotUpgrade, - delay: Math.floor((145 + (tech.isFoamBotUpgrade ? 0 : 230)) * b.fireCDscale),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, + fireLimit: 5 + 2 * tech.isFoamBotUpgrade - isKeep, + delay: Math.floor((200 + (tech.isFoamBotUpgrade ? 0 : 200))),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, acceleration: (isKeep ? 0.005 : 0.001) * (1 + 0.5 * Math.random()), range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots() + !isKeep * 100, //how far from the player the bot will move endCycle: Infinity, @@ -5052,7 +4918,7 @@ const b = { const radius = 5 + 3 * Math.random() const SPEED = Math.max(5, 25 - radius * 0.4); //(m.crouch ? 32 : 20) - radius * 0.7; const velocity = Vector.mult(Vector.normalise(Vector.sub(this.fireTarget, this.position)), SPEED) - b.foam(this.position, Vector.rotate(velocity, 0.07 * (Math.random() - 0.5)), radius + 6 * this.isUpgraded) + b.foam(this.position, Vector.rotate(velocity, 0.07 * (Math.random() - 0.5)), radius + 5 * this.isUpgraded) //recoil // const force = Vector.mult(Vector.normalise(velocity), 0.005 * this.mass * (tech.isFoamCavitation ? 2 : 1)) @@ -5082,58 +4948,11 @@ const b = { } else { //fire mode: quickly fire at targets and doesn't follow player this.fire() } - - - - - - - - - - // const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos)) - // if (distanceToPlayer > this.range) { //if far away move towards player - // this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.acceleration) - // } else { //close to player - // Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity - - // //&& !(simulation.cycle % this.lookFrequency) - // if (this.cd < simulation.cycle && !m.isCloak) { - // let target - // for (let i = 0, len = mob.length; i < len; i++) { - // const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); - // if (dist2 < 2000000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) { - - // this.fireCount++ - // if (this.fireCount > 5) { - // this.fireCount = 0 - // this.cd = simulation.cycle + this.delay; - // } else { - // // this.cd = simulation.cycle + 1; - // } - - // target = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) - // const radius = 6 + 7 * Math.random() - // const SPEED = Math.max(5, 25 - radius * 0.4); //(m.crouch ? 32 : 20) - radius * 0.7; - // const velocity = Vector.mult(Vector.normalise(Vector.sub(target, this.position)), SPEED) - // b.foam(this.position, velocity, radius + 7.5 * this.isUpgraded) - - // //recoil - // // const force = Vector.mult(Vector.normalise(velocity), 0.005 * this.mass * (tech.isFoamCavitation ? 2 : 1)) - // const force = Vector.mult(velocity, 0.0003 * this.mass * (tech.isFoamCavitation ? 2 : 1)) - // this.force.x -= force.x - // this.force.y -= force.y - // break; - // } - // } - // } - // } } }) Composite.add(engine.world, bullet[me]); //add bullet to world }, soundBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.soundBot()`); const me = bullet.length; const dir = m.angle; bullet[me] = Bodies.rectangle(position.x, position.y, 12, 30, { @@ -5146,11 +4965,11 @@ const b = { restitution: 0.6 * (1 + 0.5 * Math.random()), dmg: 0, // 0.14 //damage done in addition to the damage from momentum minDmgSpeed: 2, - lookFrequency: 17 + Math.floor(7 * Math.random()) - 5 * tech.isSoundBotUpgrade, + lookFrequency: 17 + Math.floor(7 * Math.random()) - 3 * tech.isSoundBotUpgrade, cd: 0, fireCount: 0, - fireLimit: 5 + 2 * tech.isSoundBotUpgrade, - delay: Math.floor((120 + (tech.isSoundBotUpgrade ? 0 : 70)) * b.fireCDscale),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, + fireLimit: 5 - isKeep, + delay: Math.floor(140),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade, acceleration: (isKeep ? 0.005 : 0.001) * (1 + 0.5 * Math.random()), range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots() + !isKeep * 100, //how far from the player the bot will move endCycle: Infinity, @@ -5165,7 +4984,7 @@ const b = { waves: [], phononWaveCD: 0, addWave(where, angle) { - const halfArc = 0.2 * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) + 0.05 * tech.isSoundBotUpgrade //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions + const halfArc = 0.2 * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) + 0.04 * tech.isSoundBotUpgrade //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions this.waves.push({ position: where, angle: angle - halfArc, //used in drawing ctx.arc @@ -5174,11 +4993,11 @@ const b = { arc: halfArc * 2, radius: 25, resonanceCount: 0, - dmg: (tech.isUpgraded ? 4 : 1.5) * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.5 : 1), + dmg: (tech.isUpgraded ? 9 : 1.5) * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.5 : 1), }) }, fire() { - if (!(simulation.cycle % (6 - 2 * tech.isSoundBotUpgrade))) { + if (!(simulation.cycle % 6)) { this.fireCount++ if (this.fireCount > this.fireLimit) { this.fireCount = 0 @@ -5299,7 +5118,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, laserBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.laserBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = (14 + 6 * Math.random()) @@ -5318,7 +5136,7 @@ const b = { lookFrequency: 20 + Math.floor(7 * Math.random()) - 13 * tech.isLaserBotUpgrade, range: (600 + 375 * tech.isLaserBotUpgrade) * (1 + 0.12 * Math.random()), drainThreshold: 0.15 + 0.5 * Math.random() + (tech.isEnergyHealth ? 0.3 : 0),// laser bot will not attack if the player is below this energy - drain: (0.57 - 0.43 * tech.isLaserBotUpgrade) * tech.laserDrain, + drain: (0.57 - 0.43 * tech.isLaserBotUpgrade + isKeep * 0.08) * tech.laserDrain, laserDamage: 0.75 + 0.75 * tech.isLaserBotUpgrade, endCycle: Infinity, classType: "bullet", @@ -5482,7 +5300,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, boomBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.boomBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = (7 + 2 * Math.random()) @@ -5496,10 +5313,10 @@ const b = { restitution: 1, dmg: 0, minDmgSpeed: 0, - lookFrequency: 43 + Math.floor(7 * Math.random()) - 13 * tech.isBoomBotUpgrade, + lookFrequency: 43 + Math.floor(7 * Math.random()) - 15 * tech.isBoomBotUpgrade, acceleration: (isKeep ? 0.005 : 0.001) * (1 + 0.5 * Math.random()), - attackAcceleration: 0.012 + 0.006 * tech.isBoomBotUpgrade, - range: 500 * (1 + 0.1 * Math.random()) + 350 * tech.isBoomBotUpgrade + !isKeep * 100, + attackAcceleration: 0.012 + 0.005 * tech.isBoomBotUpgrade, + range: 500 * (1 + 0.1 * Math.random()) + 250 * tech.isBoomBotUpgrade + !isKeep * 100, endCycle: Infinity, classType: "bullet", collisionFilter: { @@ -5570,7 +5387,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, plasmaBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.plasmaBot()`); const me = bullet.length; const dir = m.angle; const RADIUS = 21 @@ -5627,11 +5443,7 @@ const b = { const DIST = Vector.magnitude(sub); const unit = Vector.normalise(sub) if (DIST < tech.isPlasmaRange * 450 && m.energy > this.drainThreshold) { - m.energy -= 0.0013 //0.004; //normal plasma field is 0.00008 + m.fieldRegen = 0.00108 - // if (m.energy < 0) { - // m.fieldCDcycle = m.cycle + 120; - // m.energy = 0; - // } + m.energy -= 0.001 //calculate laser collision let best; let range = tech.isPlasmaRange * (120 + 300 * Math.sqrt(Math.random())) @@ -5646,8 +5458,19 @@ const b = { best.who.damage(dmg); best.who.locatePlayer(); //push mobs away - const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.007 * Math.min(5, best.who.mass)) - Matter.Body.applyForce(best.who, path[1], force) + // const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.007 * Math.min(5, best.who.mass)) + // Matter.Body.applyForce(best.who, path[1], force) + //push mobs away + if (best.who.speed > 3) { + const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.004 * Math.min(5, best.who.mass)) + Matter.Body.applyForce(best.who, path[1], force) + Matter.Body.setVelocity(best.who, { x: best.who.velocity.x * 0.5, y: best.who.velocity.y * 0.5 }); + } else { + const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.01 * Math.min(5, best.who.mass)) + Matter.Body.applyForce(best.who, path[1], force) + Matter.Body.setVelocity(best.who, { x: best.who.velocity.x * 0.7, y: best.who.velocity.y * 0.7 }); + } + if (best.who.speed > 2.5) Matter.Body.setVelocity(best.who, { x: best.who.velocity.x * 0.75, y: best.who.velocity.y * 0.75 }); //draw mob damage circle if (best.who.damageReduction) { @@ -5697,7 +5520,6 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, orbitBot(position = player.position, isKeep = true) { - // if (isKeep) simulation.makeTextLog(`b.orbitBot()`); const me = bullet.length; bullet[me] = Bodies.polygon(position.x, position.y, 9, 12, { isUpgraded: tech.isOrbitBotUpgrade, @@ -5732,12 +5554,12 @@ const b = { } } }, - range: 190 + 130 * tech.isOrbitBotUpgrade + !isKeep * 60 * (0.5 - Math.random()), //range is set in bot upgrade too! + range: 160 + 170 * tech.isOrbitBotUpgrade + !isKeep * 100 * (0.5 - Math.random()), //range is set in bot upgrade too! orbitalSpeed: 0, phase: 2 * Math.PI * Math.random(), do() { if (!m.isCloak) { //if time dilation isn't active - const size = 33 + const size = 40 q = Matter.Query.region(mob, { min: { x: this.position.x - size, @@ -5816,8 +5638,8 @@ const b = { return `use compressed air to rapidly drive nails
fire rate increases the longer you fire
${this.ammoPack.toFixed(0)} nails per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 60, - defaultAmmoPack: 60, + ammoPack: 27, + defaultAmmoPack: 27, recordedAmmo: 0, have: false, nextFireCycle: 0, //use to remember how longs its been since last fire, used to reset count @@ -6128,11 +5950,11 @@ const b = { name: "shotgun", //1 // description: `fire a wide burst of short range bullets
with a low fire rate
3-4 nails per ${powerUps.orb.ammo()}`, descriptionFunction() { - return `fire a wide burst of short range bullets
has a slow fire rate
${this.ammoPack.toFixed(1)} shots per ${powerUps.orb.ammo()}` + return `fire a wide burst of short range pellets
has a slow fire rate
${this.ammoPack.toFixed(1)} shots per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 3.5, - defaultAmmoPack: 3.5, + ammoPack: 1.6, + defaultAmmoPack: 1.6, have: false, do() { //fade cross hairs @@ -6417,19 +6239,16 @@ const b = { } }, { name: "super balls", //2 - // description: `fire 3 balls in a wide arc
balls bounce with no momentum loss
9 balls per ${powerUps.orb.ammo()}`, descriptionFunction() { - return `fire 3 balls in a wide arc
balls bounce with no momentum loss
${this.ammoPack.toFixed(0)} balls per ${powerUps.orb.ammo()}` + return `fire 3 balls that retain
momentum and kinetic energy after collisions
${this.ammoPack.toFixed(0)} balls per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 9, + ammoPack: 4.05, + defaultAmmoPack: 4.05, have: false, // num: 5, do() { }, - foamBall() { - - - }, + foamBall() { }, fireOne() { m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 27 : 19) * b.fireCDscale); // cool down const speed = m.crouch ? 43 : 36 @@ -6446,21 +6265,33 @@ const b = { const SPREAD = m.crouch ? 0.08 : 0.13 const num = 3 + Math.floor(tech.extraSuperBalls * Math.random()) const speed = m.crouch ? 43 : 36 - let dir = m.angle - SPREAD * (num - 1) / 2; - for (let i = 0; i < num; i++) { - b.superBall({ - x: m.pos.x + 30 * Math.cos(dir), - y: m.pos.y + 30 * Math.sin(dir) - }, { - x: speed * Math.cos(dir), - y: speed * Math.sin(dir) - }, 11 * tech.bulletSize) - dir += SPREAD; + if (tech.isBulletTeleport) { + for (let i = 0; i < num; i++) { + b.superBall({ + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }, 11 * tech.bulletSize) + } + } else { + let dir = m.angle - SPREAD * (num - 1) / 2; + for (let i = 0; i < num; i++) { + b.superBall({ + x: m.pos.x + 30 * Math.cos(dir), + y: m.pos.y + 30 * Math.sin(dir) + }, { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }, 11 * tech.bulletSize) + dir += SPREAD; + } } }, fireQueue() { m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 23 : 15) * b.fireCDscale); // cool down - const num = 1 + 3 + Math.floor(tech.extraSuperBalls * Math.random()) //1 extra + const num = 2 + 3 + Math.floor(tech.extraSuperBalls * Math.random()) //2 extra const speed = m.crouch ? 43 : 36 const delay = Math.floor((m.crouch ? 18 : 12) * b.fireCDscale) @@ -6479,8 +6310,6 @@ const b = { } let count = 0 requestAnimationFrame(cycle); - - }, chooseFireMethod() { //set in simulation.startGame if (tech.oneSuperBall) { @@ -6500,8 +6329,8 @@ const b = { return `emit wave packets that propagate through solids
waves slow mobs
${this.ammoPack.toFixed(0)} wave packets per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 115, - defaultAmmoPack: 115, + ammoPack: 52, + defaultAmmoPack: 52, have: false, wavePacketCycle: 0, delay: 40, @@ -6863,7 +6692,8 @@ const b = { return `launch homing missiles that target mobs
missiles explode on contact with mobs
${this.ammoPack.toFixed(1)} missiles per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 5, + ammoPack: 2.3, + defaultAmmoPack: 2.3, have: false, fireCycle: 0, do() { }, @@ -6968,10 +6798,11 @@ const b = { name: "grenades", //5 // description: `lob a single bouncy projectile
explodes on contact or after one second
7 grenades per ${powerUps.orb.ammo()}`, descriptionFunction() { - return `lob a single bouncy projectile
explodes on contact or after one second
${this.ammoPack.toFixed(0)} grenades per ${powerUps.orb.ammo()}` + return `lob a single bouncy projectile
explodes on contact or after 1.5 seconds
${this.ammoPack.toFixed(0)} grenades per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 7, + ammoPack: 3.2, + defaultAmmoPack: 3.2, have: false, do() { }, //do is set in b.setGrenadeMode() fire() { @@ -6995,7 +6826,8 @@ const b = { return `toss a sporangium that discharges ${b.guns[6].nameString("s")}
${b.guns[6].nameString("s")} seek out nearby mobs
${this.ammoPack.toFixed(1)} sporangium per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 2.6, + ammoPack: 1.22, + defaultAmmoPack: 1.22, have: false, nameString(suffix = "") { if (tech.isSporeFlea) { @@ -7020,7 +6852,7 @@ const b = { bullet[me].maxRadius = 30; bullet[me].restitution = 0.3; bullet[me].minDmgSpeed = 0; - bullet[me].totalSpores = 8 + 2 * tech.isSporeFreeze + 4 * tech.isSporeColony + bullet[me].totalSpores = 8 + 2 * tech.isSporeFreeze + 5 * tech.isSporeColony bullet[me].stuck = function () { }; bullet[me].beforeDmg = function () { }; bullet[me].do = function () { @@ -7165,7 +6997,7 @@ const b = { ] for (len = this.totalSpores; count < len; count++) { - if (tech.isSporeColony && Math.random() < 0.5) { + if (tech.isSporeColony && Math.random() < 0.33) { things[Math.floor(Math.random() * things.length)]() } else if (tech.isSporeFlea) { things[2]() @@ -7192,8 +7024,8 @@ const b = { return `deploy autonomous drones that smash into mobs
drones collect nearby power ups
${this.ammoPack.toFixed(0)} drones per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 16, - defaultAmmoPack: 16, + ammoPack: 7.3, + defaultAmmoPack: 7.3, have: false, do() { }, fire() { @@ -7230,12 +7062,12 @@ const b = { }, { name: "foam", //8 - // description: `spray bubbly foam that sticks to mobs
slows mobs and does damage over time
24 bubbles per ${powerUps.orb.ammo()}`, descriptionFunction() { return `spray bubbly foam that sticks to mobs
slows mobs and does damage over time
${this.ammoPack.toFixed(0)} bubbles per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 28, + ammoPack: 12.6, + defaultAmmoPack: 12.6, have: false, charge: 0, isDischarge: false, @@ -7349,10 +7181,11 @@ const b = { name: "harpoon", //9 // description: `throw a self-steering harpoon that uses energy
to retract and refund its ammo cost
1-2 harpoons per ${powerUps.orb.ammo()}`, descriptionFunction() { - return `throw a self-steering harpoon that uses energy
to retract and refund its ammo cost
${this.ammoPack.toFixed(1)} harpoons per ${powerUps.orb.ammo()}` + return `throw a harpoon that uses energy to retract
harpoons refund ammo
${this.ammoPack.toFixed(1)} harpoons per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 1.7, //update this in railgun tech + ammoPack: 0.77, //update this in railgun tech + defaultAmmoPack: 0.77, have: false, fire() { }, do() { }, @@ -7374,8 +7207,6 @@ const b = { const DRAIN = (tech.isRailEnergy ? 0 : 0.002) //exit railgun charging without firing if (m.energy < DRAIN) { - // m.energy += 0.025 + this.charge * 22 * this.drain - // m.energy -= this.drain m.fireCDcycle = m.cycle + 120; // cool down if out of energy this.endCycle = 0; this.charge = 0 @@ -7455,7 +7286,7 @@ const b = { const recoil = Vector.mult(Vector.normalise(Vector.sub(where, m.pos)), m.crouch ? 0.03 : 0.06) player.force.x -= recoil.x player.force.y -= recoil.y - const harpoonSize = tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1 + const harpoonSize = tech.isLargeHarpoon ? 1 + 0.07 * Math.sqrt(this.ammo) : 1 const thrust = 0.15 * (this.charge) if (tech.extraHarpoons) { let targetCount = 0 @@ -7615,10 +7446,7 @@ const b = { if (tech.extraHarpoons && !m.crouch) { //multiple harpoons const SPREAD = 0.2 let angle = m.angle - SPREAD * tech.extraHarpoons / 2; - const dir = { - x: Math.cos(angle), - y: Math.sin(angle) - }; //make a vector for the player's direction of length 1; used in dot product + const dir = { x: Math.cos(angle), y: Math.sin(angle) }; //make a vector for the player's direction of length 1; used in dot product const range = 450 * (tech.isFilament ? 1 + 0.012 * Math.min(110, this.ammo) : 1) let targetCount = 0 for (let i = 0, len = mob.length; i < len; ++i) { @@ -7693,7 +7521,8 @@ const b = { return `toss a proximity mine that sticks to walls
refund undetonated mines on exiting a level
${this.ammoPack.toFixed(1)} mines per ${powerUps.orb.ammo()}` }, ammo: 0, - ammoPack: 1.7, + ammoPack: 0.77, + defaultAmmoPack: 0.77, have: false, nameString(suffix = "") { if (tech.isFoamMine) { @@ -7739,10 +7568,7 @@ const b = { } let speed = 36 if (Matter.Query.point(map, pos).length > 0) speed = -2 //don't launch if mine will spawn inside map - b.mine(pos, { - x: speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - }, 0) + b.mine(pos, { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) }, 0) m.fireCDcycle = m.cycle + Math.floor(55 * b.fireCDscale); // cool down } } else { @@ -7752,22 +7578,19 @@ const b = { } let speed = 23 if (Matter.Query.point(map, pos).length > 0) speed = -2 //don't launch if mine will spawn inside map - b.mine(pos, { - x: speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - }, 0) + b.mine(pos, { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) }, 0) m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down } } }, { name: "laser", //11 - // description: `emit a beam of collimated coherent light
drains energy instead of ammo
drains ${(0.001 + tech.laserDrain) * 100}% energy per second`, descriptionFunction() { - return `emit a beam of collimated coherent light
drains ${((0.001 + tech.laserDrain) * 600).toFixed(2)} energy per second
doesn't use ammo` + return `emit a beam of collimated coherent light
reflects off map, blocks, and mobs ${(tech.isWideLaser || tech.isPulseLaser) ? 0 : tech.laserReflections} times
costs ${(tech.laserDrain * 6000).toFixed(1)} energy per second and 0 ammo` }, ammo: 0, ammoPack: Infinity, + defaultAmmoPack: Infinity, have: false, charge: 0, isStuckOn: false, @@ -7784,7 +7607,7 @@ const b = { lensDamageOn: 0, //set in tech lens() { this.stuckOn(); - this.angle += 0.02 + this.angle += 0.03 if (this.isInsideArc(m.angle)) { this.lensDamage = this.lensDamageOn ctx.lineWidth = 6 + this.lensDamageOn @@ -7796,11 +7619,6 @@ const b = { ctx.arc(m.pos.x, m.pos.y, 60, this.angle - this.arcRange, this.angle + this.arcRange); ctx.strokeStyle = '#fff' //'rgba(255,255,255,0.9)' //'hsl(189, 100%, 95%)' ctx.stroke(); - // const a = { x: radius * Math.cos(this.angle + this.arcRange), y: radius * Math.sin(this.angle + this.arcRange) } - // const b = Vector.add(m.pos, a) - // ctx.lineTo(b.x, b.y) - // ctx.fillStyle = '#fff' - // ctx.fill() }, stuckOn() { if (tech.isStuckOn) { @@ -7904,7 +7722,7 @@ const b = { // this.fire = this.firePhoton }, fireLaser() { - const drain = 0.001 + tech.laserDrain / b.fireCDscale + const drain = tech.laserDrain / b.fireCDscale if (m.energy < drain) { m.fireCDcycle = m.cycle + 100; // cool down if out of energy } else { @@ -7922,7 +7740,7 @@ const b = { }, firePulse() { }, fireSplit() { - const drain = 0.001 + tech.laserDrain / b.fireCDscale + const drain = tech.laserDrain / b.fireCDscale if (m.energy < drain) { m.fireCDcycle = m.cycle + 100; // cool down if out of energy } else { @@ -7932,10 +7750,7 @@ const b = { // const scale = Math.pow(0.9, tech.beamSplitter) // const pushScale = scale * scale let dmg = tech.laserDamage / b.fireCDscale * this.lensDamage // * scale //Math.pow(0.9, tech.laserDamage) - const where = { - x: m.pos.x + 20 * Math.cos(m.angle), - y: m.pos.y + 20 * Math.sin(m.angle) - } + const where = { x: m.pos.x + 20 * Math.cos(m.angle), y: m.pos.y + 20 * Math.sin(m.angle) } const divergence = m.crouch ? 0.15 : 0.35 const angle = m.angle - tech.beamSplitter * divergence / 2 for (let i = 0; i < 1 + tech.beamSplitter; i++) { @@ -7947,7 +7762,7 @@ const b = { } }, fireWideBeam() { - const drain = 0.001 + tech.laserDrain / b.fireCDscale + const drain = tech.laserDrain / b.fireCDscale if (m.energy < drain) { m.fireCDcycle = m.cycle + 100; // cool down if out of energy } else { @@ -8020,14 +7835,14 @@ const b = { } }, fireHistory() { - drain = 0.001 + tech.laserDrain / b.fireCDscale + drain = tech.laserDrain / b.fireCDscale if (m.energy < drain) { m.fireCDcycle = m.cycle + 100; // cool down if out of energy } else { m.fireCDcycle = m.cycle m.energy -= drain - const dmg = 0.5 * tech.laserDamage / b.fireCDscale * this.lensDamage // 3.5 * 0.55 = 200% more damage - const spacing = Math.ceil(10 - 0.4 * tech.historyLaser) + const dmg = tech.laserDamage / b.fireCDscale * this.lensDamage + const spacing = Math.ceil(23 - tech.historyLaser) ctx.beginPath(); b.laser({ x: m.pos.x + 20 * Math.cos(m.angle), @@ -8035,18 +7850,21 @@ const b = { }, { x: m.pos.x + 3000 * Math.cos(m.angle), y: m.pos.y + 3000 * Math.sin(m.angle) - }, dmg, 0, true, 0.2); - for (let i = 1, len = 3 + tech.historyLaser * 3; i < len; i++) { + }, dmg); + + for (let i = 1, len = 1 + tech.historyLaser; i < len; i++) { const history = m.history[(m.cycle - i * spacing) % 600] - const off = history.yOff - 24.2859 + const off = history.yOff - 24.2859 + 2 * i + // ctx.globalAlpha = 0.13 b.laser({ x: history.position.x + 20 * Math.cos(history.angle), y: history.position.y + 20 * Math.sin(history.angle) - off }, { x: history.position.x + 3000 * Math.cos(history.angle), y: history.position.y + 3000 * Math.sin(history.angle) - off - }, dmg, 0, true, 0.2); + }, 0.7 * dmg, tech.laserReflections, true); } + // ctx.globalAlpha = 1 ctx.strokeStyle = tech.laserColor; ctx.lineWidth = 1 ctx.stroke(); diff --git a/js/engine.js b/js/engine.js index 9c799889..5a8f5442 100644 --- a/js/engine.js +++ b/js/engine.js @@ -35,10 +35,10 @@ function playerOnGroundCheck(event) { //sets a hard land where player stays in a crouch for a bit and can't jump //crouch is forced in groundControl below const momentum = player.velocity.y * player.mass //player mass is 5 so this triggers at 26 down velocity, unless the player is holding something - if (momentum > tech.hardLanding) { + if (momentum > m.hardLanding) { m.doCrouch(); m.yOff = m.yOffWhen.jump; - m.hardLandCD = m.cycle + Math.min(momentum / 6.5 - 6, 40) + m.hardLandCD = m.cycle + m.hardLandCDScale * Math.min(momentum / 6.5 - 6, 40) //falling damage if (tech.isFallingDamage && m.immuneCycle < m.cycle && momentum > 150) { m.damage(Math.min(Math.sqrt(momentum - 133) * 0.01, 0.25)); @@ -56,10 +56,10 @@ function playerOnGroundCheck(event) { let pair = pairs[i]; if (pair.bodyA === jumpSensor) { m.standingOn = pair.bodyB; //keeping track to correctly provide recoil on jump - if (m.standingOn.alive !== true) enter(); + if (m.standingOn.alive !== true || m.immuneCycle > m.cycle) enter(); } else if (pair.bodyB === jumpSensor) { m.standingOn = pair.bodyA; //keeping track to correctly provide recoil on jump - if (m.standingOn.alive !== true) enter(); + if (m.standingOn.alive !== true || m.immuneCycle > m.cycle) enter(); } } m.numTouching = 0; @@ -113,41 +113,16 @@ function collisionChecks(event) { m.damage(dmg); return } - if (tech.isFlipFlop) { - if (tech.isFlipFlopOn) { - tech.isFlipFlopOn = false - if (document.getElementById("tech-flip-flop")) document.getElementById("tech-flip-flop").innerHTML = ` = OFF` - m.eyeFillColor = 'transparent' - m.damage(dmg); - } else { - tech.isFlipFlopOn = true //immune to damage this hit, lose immunity for next hit - if (document.getElementById("tech-flip-flop")) document.getElementById("tech-flip-flop").innerHTML = ` = ON` - m.eyeFillColor = m.fieldMeterColor //'#0cf' - if (!tech.isFlipFlopHarm) m.damage(dmg); - } - if (tech.isFlipFlopHealth) { - m.setMaxHealth(); - for (let i = 0; i < powerUp.length; i++) { - if (powerUp[i].name === "heal") { - const oldSize = powerUp[i].size - powerUp[i].size = powerUps.heal.size() //update current heals - const scale = powerUp[i].size / oldSize - Matter.Body.scale(powerUp[i], scale, scale); //grow - } - } - } - } else { - m.damage(dmg); //normal damage - } + m.damage(dmg); //normal damage if (tech.isCollisionRealitySwitch && m.alive) { m.switchWorlds() - simulation.trails() - simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`); + simulation.trails(90) + simulation.inGameConsole(`simulation.amplitude = ${Math.random()}`); } - if (tech.isPiezo) m.energy += 20.48; + if (tech.isPiezo) m.energy += 20.48 * level.isReducedRegen; if (tech.isCouplingNoHit && m.coupling > 0) { - m.couplingChange(-5) + m.couplingChange(-3) const unit = Vector.rotate({ x: 1, y: 0 }, 6.28 * Math.random()) let where = Vector.add(m.pos, Vector.mult(unit, 17)) @@ -182,36 +157,17 @@ function collisionChecks(event) { color: 'rgba(0, 171, 238, 0.7)', time: 32 }); - // simulation.drawList.push({ //add dmg to draw queue - // x: m.pos.x, - // y: m.pos.y, - // radius: 150, - // color: 'rgba(0, 171, 238, 0.33)', - // time: 6 - // }); - // simulation.drawList.push({ //add dmg to draw queue - // x: m.pos.x, - // y: m.pos.y, - // radius: 75, - // color: 'rgba(0, 171, 238, 0.5)', - // time: 16 - // }); - // simulation.drawList.push({ //add dmg to draw queue - // x: m.pos.x, - // y: m.pos.y, - // radius: 25, - // color: 'rgba(0, 171, 238, 0.75)', - // time: 25 - // }); } if (tech.isHarpoonDefense) { //fire harpoons at mobs after getting hit const maxCount = 10 + 3 * tech.extraHarpoons //scale the number of hooks fired let count = maxCount - 1 const angle = Math.atan2(mob[k].position.y - player.position.y, mob[k].position.x - player.position.x); - b.harpoon(m.pos, mob[k], angle, 0.75, true, 7) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { + + const mass = 0.75 * (tech.isLargeHarpoon ? 1 + 0.05 * Math.sqrt(this.ammo) : 1) + b.harpoon(m.pos, mob[k], angle, mass, true, 7) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { bullet[bullet.length - 1].drain = 0 for (; count > 0; count--) { - b.harpoon(m.pos, mob[k], angle + count * 2 * Math.PI / maxCount, 0.75, true, 7) + b.harpoon(m.pos, mob[k], angle + count * 2 * Math.PI / maxCount, mass, true, 7) bullet[bullet.length - 1].drain = 0 } } @@ -220,15 +176,8 @@ function collisionChecks(event) { if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles //extra kick between player and mob //this section would be better with forces but they don't work... let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x); - Matter.Body.setVelocity(player, { - x: player.velocity.x + 8 * Math.cos(angle), - y: player.velocity.y + 8 * Math.sin(angle) - }); - Matter.Body.setVelocity(mob[k], { - x: mob[k].velocity.x - 8 * Math.cos(angle), - y: mob[k].velocity.y - 8 * Math.sin(angle) - }); - + Matter.Body.setVelocity(player, { x: player.velocity.x + 8 * Math.cos(angle), y: player.velocity.y + 8 * Math.sin(angle) }); + Matter.Body.setVelocity(mob[k], { x: mob[k].velocity.x - 8 * Math.cos(angle), y: mob[k].velocity.y - 8 * Math.sin(angle) }); if (tech.isAnnihilation && !mob[k].shield && !mob[k].isShielded && !mob[k].isBoss && mob[k].isDropPowerUp && m.energy > 0.1 && mob[k].damageReduction > 0) { m.energy -= 0.1 //* Math.max(m.maxEnergy, m.energy) //0.33 * m.energy if (m.immuneCycle === m.cycle + m.collisionImmuneCycles) m.immuneCycle = 0; //player doesn't go immune to collision damage @@ -292,11 +241,11 @@ function collisionChecks(event) { } } - let dmg = tech.blockDamage * m.dmgScale * v * obj.mass * (tech.isMobBlockFling ? 2.5 : 1) * (tech.isBlockRestitution ? 2.5 : 1) * ((m.fieldMode === 0 || m.fieldMode === 8) ? 1 + 0.04 * m.coupling : 1); + let dmg = tech.blockDamage * m.dmgScale * v * obj.mass * (tech.isMobBlockFling ? 2.5 : 1) * (tech.isBlockRestitution ? 2.5 : 1) * ((m.fieldMode === 0 || m.fieldMode === 8) ? 1 + 0.05 * m.coupling : 1); if (mob[k].isShielded) dmg *= 0.7 mob[k].damage(dmg, true); - if (tech.isBlockPowerUps && !mob[k].alive && mob[k].isDropPowerUp && m.throwCycle > m.cycle) { + if (tech.isBlockPowerUps && !mob[k].alive && mob[k].isDropPowerUp && Math.random() < 0.5) { options = ["coupling", "boost", "heal", "research"] if (!tech.isEnergyNoAmmo) options.push("ammo") powerUps.spawn(mob[k].position.x, mob[k].position.y, options[Math.floor(Math.random() * options.length)]); diff --git a/js/index.js b/js/index.js index 633155b8..d36ff1b0 100644 --- a/js/index.js +++ b/js/index.js @@ -12,6 +12,10 @@ Math.hash = s => { // document.getElementById("seed").placeholder = Math.initialSeed = Math.floor(Date.now() % 100000) //random every time: just the time in milliseconds UTC +window.addEventListener('error', error => { + simulation.inGameConsole(`ERROR: ${error.message} ${error.filename}:${error.lineno}`) +}); + document.getElementById("seed").placeholder = Math.initialSeed = String(Math.floor(Date.now() % 100000)) Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it Math.seededRandom = function (min = 0, max = 1) { // in order to work 'Math.seed' must NOT be undefined @@ -134,7 +138,8 @@ function beforeUnloadEventListener(event) { event.preventDefault(); if (tech.isExitPrompt) { tech.damage *= 1.25 - simulation.makeTextLog(`damage *= ${1.25}`) + // simulation.inGameConsole(`damage *= ${1.25}`) + simulation.inGameConsole(`tech.damage *= ${1.25} //beforeunload`); if (Math.random() < 0.25) { removeEventListener('beforeunload', beforeUnloadEventListener); } @@ -191,7 +196,7 @@ let color = { //light // check for URL parameters to load an experimental game //********************************************************************** -//example https://landgreen.github.io/sidescroller/index.html? +//example https://landgreen.github.io/n-gon/index.html? // &gun1=minigun&gun2=laser // &tech1=laser-bot&tech2=mass%20driver&tech3=overcharge&tech4=laser-bot&tech5=laser-bot&field=phase%20decoherence%20field&difficulty=2 //add ? to end of url then for each power up add @@ -251,11 +256,11 @@ window.addEventListener('load', () => { } } - if (property === "difficulty") { - simulation.difficultyMode = Number(set[property]) - lore.setTechGoal() - document.getElementById("difficulty-select-experiment").value = Number(set[property]) - } + // if (property === "difficulty") { + // simulation.difficultyMode = Number(set[property]) + // lore.setTechGoal() + // document.getElementById("difficulty-select-experiment").value = Number(set[property]) + // } if (property === "molMode") { simulation.molecularMode = Number(set[property]) const i = 4 //update experiment text @@ -439,7 +444,7 @@ const build = { build.generatePauseLeft() //makes the left side of the pause menu with the tech build.generatePauseRight() //makes the right side of the pause menu with the tech // build.sortTech('') //sorts tech into the order the player got them using tech.tech[i].cycle = m.cycle - document.getElementById("tech").style.display = "none" + document.getElementById("right-HUD").style.display = "none" document.getElementById("guns").style.display = "none" document.getElementById("field").style.display = "none" document.getElementById("health").style.display = "none" @@ -452,61 +457,86 @@ const build = { generatePauseLeft() { //left side let botText = "" - if (tech.nailBotCount) botText += `
nail-bots: ${tech.nailBotCount}` - if (tech.orbitBotCount) botText += `
orbital-bots: ${tech.orbitBotCount}` - if (tech.boomBotCount) botText += `
boom-bots: ${tech.boomBotCount}` - if (tech.laserBotCount) botText += `
laser-bots: ${tech.laserBotCount}` - if (tech.foamBotCount) botText += `
foam-bots: ${tech.foamBotCount}` - if (tech.soundBotCount) botText += `
sound-bots: ${tech.soundBotCount}` - if (tech.dynamoBotCount) botText += `
dynamo-bots: ${tech.dynamoBotCount}` - if (tech.plasmaBotCount) botText += `
plasma-bots: ${tech.plasmaBotCount}` - if (tech.missileBotCount) botText += `
missile-bots: ${tech.missileBotCount}` - - let text = `
- -PAUSED -press ${input.key.pause} to resume + if (tech.nailBotCount) botText += `
nail-bots ${tech.nailBotCount}` + if (tech.orbitBotCount) botText += `
orbital-bots ${tech.orbitBotCount}` + if (tech.boomBotCount) botText += `
boom-bots ${tech.boomBotCount}` + if (tech.laserBotCount) botText += `
laser-bots ${tech.laserBotCount}` + if (tech.foamBotCount) botText += `
foam-bots ${tech.foamBotCount}` + if (tech.soundBotCount) botText += `
sound-bots ${tech.soundBotCount}` + if (tech.dynamoBotCount) botText += `
dynamo-bots ${tech.dynamoBotCount}` + if (tech.plasmaBotCount) botText += `
plasma-bots ${tech.plasmaBotCount}` + if (tech.missileBotCount) botText += `
missile-bots ${tech.missileBotCount}` + + // ${b.activeGun === null || b.activeGun === undefined ? "undefined" : b.guns[b.activeGun].name} (${b.activeGun === null || b.activeGun === undefined ? "0" : b.guns[b.activeGun].ammo}) + + let text = `
+PAUSED +press ${input.key.pause} to resume
-
- -
+ - -
+ +
-
damage: ${((tech.damageFromTech())).toPrecision(4)}     difficulty: ${((m.dmgScale)).toPrecision(4)} -
defense: ${(1 - m.defense()).toPrecision(5)}     difficulty: ${(1 / simulation.dmgScale).toPrecision(4)} -
fire rate: ${((1 - b.fireCDscale) * 100).toFixed(b.fireCDscale < 0.1 ? 2 : 0)}% -${tech.duplicationChance() ? `
duplication: ${(tech.duplicationChance() * 100).toFixed(0)}%` : ""} +
+
+simulation variables +
+damage ${((tech.damageFromTech())).toPrecision(4)}x +level ${((m.dmgScale)).toPrecision(4)}x +
damage taken ${(m.defense()).toPrecision(4)}x +level ${(simulation.dmgScale).toPrecision(4)}x +
health (${(m.health * 100).toFixed(0)} / ${(m.maxHealth * 100).toFixed(0)}) +${powerUps.research.count} ${powerUps.orb.research()} +
energy (${(m.energy * 100).toFixed(0)} / ${(m.maxEnergy * 100).toFixed(0)}) + (${(m.fieldRegen * 6000 * level.isReducedRegen).toFixed(0)}/s) +${tech.totalCount} ${powerUps.orb.tech()} +
fire rate ${(1 / b.fireCDscale).toFixed(2)}x +mass ${player.mass.toFixed(1)} ${m.coupling ? `
` + m.couplingDescription(m.coupling) + ` from ${(m.coupling).toFixed(0)} ${powerUps.orb.coupling(1)}` : ""} +
duplication ${(tech.duplicationChance() * 100).toFixed(0)}% +JUNK ${(100 * (tech.junkChance + level.junkAdded)).toFixed(0)}% ${botText}
-
health: (${(m.health * 100).toFixed(0)} / ${(m.maxHealth * 100).toFixed(0)}) -mass: ${player.mass.toFixed(1)} -
energy: (${(m.energy * 100).toFixed(0)} / ${(m.maxEnergy * 100).toFixed(0)}) + (${(m.fieldRegen * 6000).toFixed(0)}/s) -position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)}) -
gun: ${b.activeGun === null || b.activeGun === undefined ? "undefined" : b.guns[b.activeGun].name}   ammo: ${b.activeGun === null || b.activeGun === undefined ? "0" : b.guns[b.activeGun].ammo} -mouse: (${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)}) -
tech: ${tech.totalCount}   research: ${powerUps.research.count} -velocity: (${player.velocity.x.toFixed(3)}, ${player.velocity.y.toFixed(3)}) -${tech.junkChance ? `
JUNK: ${(100 * tech.junkChance).toFixed(1)}% ` : ""} -
-
level: ${level.levelsCleared} ${level.levels[level.onLevel]} (${level.difficultyText()}) -
mobs: ${spawn.pickList[0]}, ${spawn.pickList[0]} -
seed: ${Math.initialSeed}   ${m.cycle} cycles -
mobs: ${mob.length}   blocks: ${body.length}   bullets: ${bullet.length}   power ups: ${powerUp.length} +
${level.levelAnnounce()} +position (${player.position.x.toFixed(0)}, ${player.position.y.toFixed(0)}) +
seed ${Math.initialSeed} +mouse (${simulation.mouseInGame.x.toFixed(0)}, ${simulation.mouseInGame.y.toFixed(0)}) +
cycles ${m.cycle} +velocity (${player.velocity.x.toFixed(2)}, ${player.velocity.y.toFixed(2)}) +
mobs ${mob.length} (${spawn.pickList[0]}, ${spawn.pickList[0]}) +blocks ${body.length} +
bullets ${bullet.length} +power ups ${powerUp.length} + ${simulation.isCheating ? "

lore disabled" : ""} -
`; - // deaths: ${mobs.mobDeaths}   - // if (tech.isPauseSwitchField && !simulation.isChoosing) { - // const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` - // text += `
- //
- //
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
- // ${m.fieldUpgrades[m.fieldMode].description}
` +
+ +
` + text += `
+
+difficulty parameters +
+ ${simulation.difficultyMode > 0 ? `
0.87x damage, 1.2x damage taken per level
+1 boss on each level
` : " "} + ${simulation.difficultyMode > 1 ? `
more mob per level
faster mobs per level
` : " "} + ${simulation.difficultyMode > 2 ? `
0.87x damage, 1.2x damage taken per level
+1 random constraint on each level
` : " "} + ${simulation.difficultyMode > 3 ? `
+1 boss on each level
bosses spawn 1 fewer ${powerUps.orb.tech()}
` : " "} + ${simulation.difficultyMode > 4 ? `
0.87x damage, 1.2x damage taken per level
+1 random constraint on each level
` : " "} + ${simulation.difficultyMode > 5 ? `
0.5x initial damage
2x initial damage taken
` : " "} +
+
+${simulation.difficultyMode > 2 ? `
active constraints
${level.constraintDescription1}
${level.constraintDescription2}
` : ""} +
` + if (!localSettings.isHideHUD) text += `
+
+console log +
+
${document.getElementById("text-log").innerHTML}
+
+
+
` if ((tech.isPauseSwitchField || simulation.testing)) { //&& !simulation.isChoosing // const fieldNameP = m.fieldUpgrades[m.fieldMode > 1 ? m.fieldMode - 1 : m.fieldUpgrades.length - 1].name // const fieldNameN = m.fieldUpgrades[m.fieldMode === m.fieldUpgrades.length - 2 ? 1 : m.fieldMode + 1].name @@ -515,21 +545,21 @@ ${simulation.isCheating ? "

lore disabled" : ""}
` //button for current const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` - text += `
-
-
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
- ${m.fieldUpgrades[m.fieldMode].description}
` + text += `
+
+
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
+ ${m.fieldUpgrades[m.fieldMode].description}
` //button below for next - text += `
-
` + text += `
+
` } else { const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` - text += `
-
-
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
- ${m.fieldUpgrades[m.fieldMode].description}
` + text += `
+
+
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
+ ${m.fieldUpgrades[m.fieldMode].description}
` } // for (let i = 0, len = b.inventory.length; i < len; i++) { // text += `
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
${b.guns[b.inventory[i]].description}
` @@ -537,33 +567,40 @@ ${simulation.isCheating ? "

lore disabled" : ""} for (let i = 0, len = b.inventory.length; i < len; i++) { const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/gun/${b.guns[b.inventory[i]].name}.webp');"` text += `
-
-
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
- ${b.guns[b.inventory[i]].descriptionFunction()}
` +
+
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
+ ${b.guns[b.inventory[i]].descriptionFunction()}
` } - if (!localSettings.isHideHUD) text += `
${document.getElementById("text-log").innerHTML}
` //show last in game console message let el = document.getElementById("pause-grid-left") el.style.display = "grid" el.innerHTML = text + requestAnimationFrame(() => { + if (localSettings.isAllowed) { + document.getElementById("simulation-variables-details").open = localSettings.pauseMenuDetailsOpen[0] + document.getElementById("difficulty-parameters-details").open = localSettings.pauseMenuDetailsOpen[1] + document.getElementById("console-log-details").open = localSettings.pauseMenuDetailsOpen[2] + if (document.getElementById("constraints-details")) document.getElementById("constraints-details").open = localSettings.pauseMenuDetailsOpen[3] + } + }); }, generatePauseRight() { let text = `
- - - - - - - - + + + + + + + +
`; const ejectClass = (tech.isPauseEjectTech && !simulation.isChoosing) ? 'pause-eject' : '' for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].count > 0) { const style = (localSettings.isHideImages || tech.tech[i].isJunk || tech.tech[i].isLore) ? `style="height:auto;"` : `style = "background-image: url('img/${tech.tech[i].name}.webp');"` const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; - if (tech.tech[i].isNonRefundable) { - text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + if (tech.tech[i].isInstant) { + // text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` } else if (tech.tech[i].isFieldTech) { text += `
` text += build.fieldTechText(i) + "
" @@ -689,7 +726,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} } else if (find === 'energy') { tech.tech.sort(sortKeyword); } else if (find === 'input') { - find = document.getElementById("sort-input").value; + find = document.getElementById("sort-input").value.toLowerCase(); tech.tech.sort(sortKeyword); } if (isExperiment) { @@ -703,6 +740,15 @@ ${simulation.isCheating ? "

lore disabled" : ""} simulation.updateTechHUD(); }, unPauseGrid() { + if (localSettings.isAllowed) { + //save details open/close state + if (document.getElementById("simulation-variables-details")) localSettings.pauseMenuDetailsOpen[0] = document.getElementById("simulation-variables-details").open + if (document.getElementById("difficulty-parameters-details")) localSettings.pauseMenuDetailsOpen[1] = document.getElementById("difficulty-parameters-details").open + if (document.getElementById("console-log-details")) localSettings.pauseMenuDetailsOpen[2] = document.getElementById("console-log-details").open + if (document.getElementById("constraints-details")) localSettings.pauseMenuDetailsOpen[3] = document.getElementById("constraints-details").open + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + document.getElementById("guns").style.display = "inline" document.getElementById("field").style.display = "inline" if (tech.isEnergyHealth) { @@ -713,7 +759,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} document.getElementById("health-bg").style.display = "inline" } if (!localSettings.isHideHUD) { - document.getElementById("tech").style.display = "inline" + document.getElementById("right-HUD").style.display = "inline" document.getElementById("defense-bar").style.display = "inline" document.getElementById("damage-bar").style.display = "inline" } @@ -728,37 +774,43 @@ ${simulation.isCheating ? "

lore disabled" : ""} isExperimentRun: false, techText(i) { return `
-
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` +
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
+ ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` + }, + instantTechText(i) { + // + return `
+
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
+ ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` }, skinTechText(i) { return `
- -
-
-
       ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + +
+
+
        ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""} + ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` }, gunTechText(i) { return `
- -
-
-
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + +
+
+
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""} + ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` }, fieldTechText(i) { return `
- -
-
-
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` + +
+
+
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""} + ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` }, junkTechText(i) { return `
-
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` +
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
+ ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` }, choosePowerUp(index, type, isAllowed = false) { if (type === "gun") { @@ -798,24 +850,22 @@ ${simulation.isCheating ? "

lore disabled" : ""} // document.getElementById(`field-${i}`).innerHTML = `
  ${build.nameLink(m.fieldUpgrades[i].name)}
${m.fieldUpgrades[i].description}` document.getElementById(`field-${i}`).innerHTML = `
-
  ${build.nameLink(m.fieldUpgrades[i].name)}
- ${m.fieldUpgrades[i].description}
` +
  ${build.nameLink(m.fieldUpgrades[i].name)}
+ ${m.fieldUpgrades[i].description}` } } else if (type === "tech") { if (tech.tech[index].count < tech.tech[index].maxCount) { - // if (!tech.tech[index].isLore && !tech.tech[index].isNonRefundable && !who.classList.contains("build-tech-selected")) who.classList.add("build-tech-selected"); + // if (!tech.tech[index].isLore && !tech.tech[index].isInstant && !who.classList.contains("build-tech-selected")) who.classList.add("build-tech-selected"); if (!document.getElementById("tech-" + index).classList.contains("build-tech-selected")) document.getElementById("tech-" + index).classList.add("build-tech-selected"); tech.giveTech(index) - } else if (!tech.tech[index].isNonRefundable) { + } else if (!tech.tech[index].isInstant) { // tech.totalCount -= tech.tech[index].count - tech.removeTech(index); document.getElementById("tech-" + index).classList.remove("build-tech-selected"); + tech.removeTech(index); } else { // for non refundable tech this makes it flash off for a second, but return to on to show that it can't be set off document.getElementById("tech-" + index).classList.remove("build-tech-selected") - setTimeout(() => { - document.getElementById("tech-" + index).classList.add("build-tech-selected") - }, 50); + setTimeout(() => { document.getElementById("tech-" + index).classList.add("build-tech-selected") }, 50); } } build.updateExperimentText(isAllowed) @@ -837,6 +887,8 @@ ${simulation.isCheating ? "

lore disabled" : ""} } else if (tech.tech[i].isSkin) { techID.classList.remove('experiment-grid-hide'); techID.innerHTML = build.skinTechText(i) + } else if (tech.tech[i].isInstant) { + techID.innerHTML = build.instantTechText(i) } else { techID.innerHTML = build.techText(i) } @@ -862,6 +914,8 @@ ${simulation.isCheating ? "

lore disabled" : ""} techID.innerHTML = build.junkTechText(i) } else if (tech.tech[i].isSkin) { techID.innerHTML = build.skinTechText(i) + } else if (tech.tech[i].isInstant) { + techID.innerHTML = build.instantTechText(i) } else { techID.innerHTML = build.techText(i) } @@ -869,75 +923,78 @@ ${simulation.isCheating ? "

lore disabled" : ""} } } }, + //
+ // + //     + // + // + //
+ + // + // populateGrid() { //background-color:var(--build-bg-color); let text = ` -
+
- - + + - - - - + + + +
-
- -     - - -
-
-
-
- - - start - - -
-
- - - reset - - +
+
+
+ + + start + + +
+
+ + + reset + + +
+
+ + + share + + +
+
-
- - - share - - -
-
-
` const hideStyle = `style="height:auto; border: none; background-color: transparent;"` for (let i = 0, len = m.fieldUpgrades.length; i < len; i++) { const style = localSettings.isHideImages ? hideStyle : `style="background-image: url('img/field/${m.fieldUpgrades[i].name}${i === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` - text += `
-
-
  ${build.nameLink(m.fieldUpgrades[i].name)}
- ${m.fieldUpgrades[i].description}
` + text += `
+
+
  ${build.nameLink(m.fieldUpgrades[i].name)}
+ ${m.fieldUpgrades[i].description}
` } for (let i = 0, len = b.guns.length; i < len; i++) { const style = localSettings.isHideImages ? hideStyle : `style="background-image: url('img/gun/${b.guns[i].name}.webp');"` - text += `
-
-
  ${build.nameLink(b.guns[i].name)}
- ${b.guns[i].descriptionFunction()}
` + text += `
+
+
  ${build.nameLink(b.guns[i].name)}
+ ${b.guns[i].descriptionFunction()}
` } for (let i = 0, len = tech.tech.length; i < len; i++) { if ((!tech.tech[i].isJunk || localSettings.isJunkExperiment) && !tech.tech[i].isLore) { const style = (localSettings.isHideImages || tech.tech[i].isJunk) ? hideStyle : `style="background-image: url('img/${tech.tech[i].name}.webp');"` - if ((tech.tech[i].allowed() || tech.tech[i].count > 0) && (!tech.tech[i].isNonRefundable || localSettings.isJunkExperiment)) { // || tech.tech[i].name === "+1 cardinality") { //|| tech.tech[i].name === "leveraged investment" + if ((tech.tech[i].allowed() || tech.tech[i].count > 0) && (!tech.tech[i].isInstant || localSettings.isJunkExperiment)) { // || tech.tech[i].name === "+1 cardinality") { //|| tech.tech[i].name === "leveraged investment" text += `
` } else { //disabled text += `
` @@ -951,6 +1008,8 @@ ${simulation.isCheating ? "

lore disabled" : ""} text += build.skinTechText(i) } else if (tech.tech[i].isJunk) { text += build.junkTechText(i) + } else if (tech.tech[i].isInstant) { + text += build.instantTechText(i) } else { text += build.techText(i) } @@ -970,14 +1029,14 @@ ${simulation.isCheating ? "

lore disabled" : ""} } document.getElementById("sort-input").addEventListener('keydown', pressEnterSort); - document.getElementById("difficulty-select-experiment").value = document.getElementById("difficulty-select").value - document.getElementById("difficulty-select-experiment").addEventListener("input", () => { - simulation.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) - lore.setTechGoal() - localSettings.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) - document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-experiment").value - if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage - }); + // document.getElementById("difficulty-select-experiment").value = document.getElementById("difficulty-select").value + // document.getElementById("difficulty-select-experiment").addEventListener("input", () => { + // simulation.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) + // lore.setTechGoal() + // localSettings.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) + // document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-experiment").value + // if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + // }); //add tooltips for (let i = 0, len = tech.tech.length; i < len; i++) { if (document.getElementById(`tech-${i}`)) { @@ -985,11 +1044,11 @@ ${simulation.isCheating ? "

lore disabled" : ""} // document.getElementById(`tech-${i}`).setAttribute('title', tech.tech[i].requires); //add tooltip } } - //highlight selected + requestAnimationFrame(() => { document.getElementById("sort-input").focus(); }); }, nameLink(text) { //converts text into a clickable wikipedia search - return `${text}` + return `${text}` }, reset() { build.isExperimentSelection = true; @@ -998,7 +1057,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} build.isExperimentSelection = true; build.isExperimentRun = true; simulation.paused = true; - b.inventory = []; //removes guns and ammo + b.inventory = []; //removes guns and ammo for (let i = 0, len = b.guns.length; i < len; ++i) { b.guns[i].count = 0; b.guns[i].have = false; @@ -1014,7 +1073,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} document.getElementById("experiment-grid").style.display = "grid" }, shareURL(isCustom = false) { - let url = "https://landgreen.github.io/sidescroller/index.html?" + let url = "https://landgreen.github.io/n-gon/index.html?" url += `&seed=${Math.initialSeed}` let count = 0; for (let i = 0; i < b.inventory.length; i++) { @@ -1026,7 +1085,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} count = 0; for (let i = 0; i < tech.tech.length; i++) { for (let j = 0; j < tech.tech[i].count; j++) { - if (!tech.tech[i].isLore && !tech.tech[i].isJunk && !tech.tech[i].isNonRefundable) { + if (!tech.tech[i].isLore && !tech.tech[i].isJunk && !tech.tech[i].isInstant) { url += `&tech${count}=${encodeURIComponent(tech.tech[i].name.trim())}` count++ } @@ -1045,7 +1104,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} // url += `&level=${Math.abs(Number(document.getElementById("starting-level").value))}` // alert('n-gon build URL copied to clipboard.\nPaste into browser address bar.') } else { - simulation.makeTextLog("n-gon build URL copied to clipboard.
Paste into browser address bar.") + simulation.inGameConsole("n-gon build URL copied to clipboard.
Paste into browser address bar.") } console.log('n-gon build URL copied to clipboard.\nPaste into browser address bar.') console.log(url) @@ -1097,9 +1156,6 @@ ${simulation.isCheating ? "

lore disabled" : ""} } else { //if you have no tech (not cheating) remove all power ups that might have spawned from tech for (let i = 0; i < powerUp.length; ++i) Matter.Composite.remove(engine.world, powerUp[i]); powerUp = []; - // if (build.hasExperimentalMode) { - // for (let i = 0; i < 7; i++) tech.giveTech("undefined") - // } } document.body.style.cursor = "none"; document.body.style.overflow = "hidden" @@ -1188,7 +1244,7 @@ const input = { document.getElementById("key-pause").innerHTML = cleanText(input.key.pause) document.getElementById("key-next-gun").innerHTML = cleanText(input.key.nextGun) document.getElementById("key-previous-gun").innerHTML = cleanText(input.key.previousGun) - document.getElementById("key-testing").innerHTML = cleanText(input.key.testing) //if (localSettings.loreCount > 0) + document.getElementById("key-testing").innerHTML = cleanText(input.key.testing) //if (localSettings.loreCount > 0) document.getElementById("splash-up").innerHTML = cleanText(input.key.up)[0] document.getElementById("splash-down").innerHTML = cleanText(input.key.down)[0] @@ -1352,7 +1408,6 @@ window.addEventListener("keydown", function (event) { simulation.previousGun(); break case input.key.pause: - if (input.isPauseKeyReady && m.alive && !build.isExperimentSelection) { input.isPauseKeyReady = false setTimeout(function () { input.isPauseKeyReady = true }, 300); @@ -1412,75 +1467,77 @@ window.addEventListener("keydown", function (event) { break case input.key.testing: if (m.alive && localSettings.loreCount > 0 && !simulation.paused && !build.isExperimentSelection) { - if (simulation.difficultyMode > 4) { - simulation.makeTextLog("testing mode disabled for this difficulty"); + if (simulation.difficultyMode > 5) { + simulation.inGameConsole("testing mode disabled for this difficulty"); break } if (simulation.testing) { simulation.testing = false; simulation.loop = simulation.normalLoop if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'none' - simulation.makeTextLog("", 0); - } else { //if (keys[191]) + simulation.inGameConsole("", 0); + } else { simulation.testing = true; simulation.loop = simulation.testingLoop - if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'inline' if (simulation.testing) tech.setCheating(); - simulation.makeTextLog( - ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Ttoggle testing
Rteleport to mouse
Fcycle field
Gall guns
H+100% defense
Bdamage, research
Nfill health, energy
Yrandom tech
Unext level
Jclear mobs
I/Ozoom in / out
1-8spawn things
⇧Xrestart
`, Infinity); + if (simulation.isConstructionMode) { + document.getElementById("construct").style.display = 'inline' + } else { + simulation.inGameConsole( + ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Ttoggle testing
Rteleport to mouse
Fcycle field
Gall guns
H+100% defense
Bdamage, research
Nfill health, energy
Yrandom tech
Unext level
Jclear mobs
I/Ozoom in / out
1-8spawn things
⇧Xrestart
`, Infinity); + } } } break @@ -1602,10 +1659,7 @@ window.addEventListener("keydown", function (event) { case "r": m.resetHistory(); Matter.Body.setPosition(player, simulation.mouseInGame); - Matter.Body.setVelocity(player, { - x: 0, - y: 0 - }); + Matter.Body.setVelocity(player, { x: 0, y: 0 }); // move bots to player for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { @@ -1635,7 +1689,7 @@ window.addEventListener("keydown", function (event) { case "l": document.getElementById("field").style.display = "none" document.getElementById("guns").style.display = "none" - document.getElementById("tech").style.display = "none" + document.getElementById("right-HUD").style.display = "none" break } } @@ -1753,11 +1807,6 @@ if (localSettings.isAllowed && !localSettings.isEmpty) { simulation.isCommunityMaps = localSettings.isCommunityMaps document.getElementById("community-maps").checked = localSettings.isCommunityMaps - if (localSettings.difficultyMode === undefined) localSettings.difficultyMode = "2" - simulation.difficultyMode = localSettings.difficultyMode - lore.setTechGoal() - document.getElementById("difficulty-select").value = localSettings.difficultyMode - if (localSettings.fpsCapDefault === undefined) localSettings.fpsCapDefault = 'max' if (localSettings.personalSeeds === undefined) localSettings.personalSeeds = []; if (localSettings.fpsCapDefault === 'max') { @@ -1785,6 +1834,19 @@ if (localSettings.isAllowed && !localSettings.isEmpty) { if (localSettings.isHideHUD === undefined) localSettings.isHideHUD = true document.getElementById("hide-hud").checked = localSettings.isHideHUD + if (localSettings.difficultyCompleted === undefined) { + localSettings.difficultyCompleted = [null, false, false, false, false, false, false] //null because there isn't a difficulty zero + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + + if (localSettings.difficultyMode === undefined) localSettings.difficultyMode = "2" + simulation.difficultyMode = localSettings.difficultyMode + lore.setTechGoal() + + if (localSettings.pauseMenuDetailsOpen === undefined) { + localSettings.pauseMenuDetailsOpen = [true, false, false, true] + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } } else { console.log('setting default localSettings') const isAllowed = localSettings.isAllowed //don't overwrite isAllowed value @@ -1795,6 +1857,7 @@ if (localSettings.isAllowed && !localSettings.isEmpty) { isJunkExperiment: false, isCommunityMaps: false, difficultyMode: '2', + difficultyCompleted: [null, false, false, false, false, false, false], fpsCapDefault: 'max', runCount: 0, isTrainingNotAttempted: true, @@ -1805,24 +1868,37 @@ if (localSettings.isAllowed && !localSettings.isEmpty) { key: undefined, isHideImages: true, //default to hide images isHideHUD: false, + pauseMenuDetailsOpen: [true, false, false, true] }; input.setDefault() if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage document.getElementById("community-maps").checked = localSettings.isCommunityMaps simulation.isCommunityMaps = localSettings.isCommunityMaps document.getElementById("hide-images").checked = localSettings.isHideImages - document.getElementById("difficulty-select").value = localSettings.difficultyMode document.getElementById("fps-select").value = localSettings.fpsCapDefault document.getElementById("banned").value = localSettings.banList } document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" // document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" - input.controlTextUpdate() + //********************************************************************** -// settings +// settings //********************************************************************** + + +// difficulty-select-experiment event listener is set in build.makeGrid +// document.getElementById("difficulty-select").addEventListener("input", () => { +// simulation.difficultyMode = Number(document.getElementById("difficulty-select").value) +// lore.setTechGoal() +// localSettings.difficultyMode = simulation.difficultyMode +// localSettings.levelsClearedLastGame = 0 //after changing difficulty, reset run history +// localSettings.entanglement = undefined //after changing difficulty, reset stored tech +// if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage +// }); + + document.getElementById("fps-select").addEventListener("input", () => { let value = document.getElementById("fps-select").value if (value === 'max') { @@ -1845,17 +1921,6 @@ document.getElementById("community-maps").addEventListener("input", () => { if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); -// difficulty-select-experiment event listener is set in build.makeGrid -document.getElementById("difficulty-select").addEventListener("input", () => { - simulation.difficultyMode = Number(document.getElementById("difficulty-select").value) - lore.setTechGoal() - localSettings.difficultyMode = simulation.difficultyMode - localSettings.levelsClearedLastGame = 0 //after changing difficulty, reset run history - localSettings.entanglement = undefined //after changing difficulty, reset stored tech - if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage -}); - - document.getElementById("updates").addEventListener("toggle", function () { function loadJSON(path, success, error) { //generic function to get JSON var xhr = new XMLHttpRequest(); diff --git a/js/level.js b/js/level.js index 884f39a8..18ee2452 100644 --- a/js/level.js +++ b/js/level.js @@ -4,87 +4,75 @@ let cons = []; //all constraints between a point and a body let consBB = []; //all constraints between two bodies let composite = [] //rotors and other map elements that don't fit const level = { - isEndlessFall: false, + fallMode: "", defaultZoom: 1400, onLevel: -1, levelsCleared: 0, - // playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"], //see level.populateLevels: (initial, ... , reservoir or factory, reactor, ... , subway, final) added later - playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"], - communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "tlinat", "ruins", "ace", "crimsonTowers", "LaunchSite", "shipwreck", "unchartedCave", "dojo", 'arena'], + playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock", "towers", "flocculation"], + communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "tlinat", "ruins", "ace", "crimsonTowers", "LaunchSite", "shipwreck", "unchartedCave", "dojo", "arena", "soft", "flappyGon", "rings", "trial"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"], levels: [], start() { if (level.levelsCleared === 0) { //this code only runs on the first level // simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode // simulation.isHorizontalFlipped = true + // level.levelsCleared = 4 + // level.updateDifficulty() // tech.giveTech("performance") - // level.difficultyIncrease(7 * 2) //30 is near max on hard //60 is near max on why // m.maxHealth = m.health = 1//00000000 // m.maxEnergy = m.energy = 10000000 // tech.isRerollDamage = true // powerUps.research.changeRerolls(99999) // m.immuneCycle = Infinity //you can't take damage // tech.tech[297].frequency = 100 + // tech.addJunkTechToPool(0.5) // m.couplingChange(10) - // m.setField("plasma torch") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole 10 grappling hook + // m.setField("grappling hook") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole 10 grappling hook // m.energy = 0 + // powerUps.research.count = 3 // tech.isHookWire = true // m.energy = 0 // simulation.molecularMode = 2 // m.damage(0.1); - // b.giveGuns("mine") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser + // b.giveGuns("super balls") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser + // b.giveGuns("spores") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.giveGuns("laser") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser + // tech.laserColor = "#fff" + // tech.laserColorAlpha = "rgba(255, 255, 255, 0.5)" + // b.guns[8].ammo = 100000000 - // requestAnimationFrame(() => { tech.giveTech("eternalism") }); - // for (let i = 0; i < 1; ++i) tech.giveTech("beforeunload") - // for (let i = 0; i < 1; ++i) tech.giveTech("Sleipnir") - // for (let i = 0; i < 1; ++i) tech.giveTech("dark patterns") - // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("paradigm shift") }); - // requestAnimationFrame(() => { for (let i = 0; i < 10; i++) b.orbitBot(m.pos, false) }); - // m.skin.hexagon(); - // for (let i = 0; i < 1; i++) tech.giveTech("tungsten carbide") + // requestAnimationFrame(() => { tech.giveTech("stimulated emission") }); + // tech.giveTech("Hilbert space") + // for (let i = 0; i < 1; ++i) tech.giveTech("decoherence") + // for (let i = 0; i < 1; ++i) tech.giveTech("mass-energy equivalence") + // for (let i = 0; i < 1; ++i) tech.giveTech("depolarization") + // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("wikipedia") }); + // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("field coupling") }); + // for (let i = 0; i < 1; i++) tech.giveTech("interest") // m.lastKillCycle = m.cycle - // for (let i = 0; i < 1; ++i) tech.giveTech("what the block?") - // for (let i = 0; i < 1; ++i) tech.giveTech("unified field theory") - // for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech"); - // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research"); - // for (let i = 0; i < 100; i++) powerUps.directSpawn(1750, -500, "coupling"); - // spawn.mapRect(575, -700, 25, 425); //block mob line of site on testing - // level.testChamber(); - - // for (let i = 0; i < 1; ++i) spawn.laserLayer(1400, -500) - // Matter.Body.setPosition(player, { x: -200, y: -3330 }); - // for (let i = 0; i < 4; ++i) spawn.laserLayer(1300, -500 + 100 * Math.random()) - // for (let i = 0; i < 1; ++i) spawn.laserLayerBoss(1900, -500) - // for (let i = 0; i < 1; ++i) spawn.dragonFlyBoss(1900, -500) - // spawn.beetleBoss(1900, -500, 25) - // spawn.zombie(-3000, -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color) - // for (let i = 0; i < 5; ++i) spawn.starter(1000 + 1000 * Math.random(), -500 + 300 * Math.random()) - // tech.addJunkTechToPool(2) - // tech.tech[322].frequency = 100 - // spawn.tetherBoss(1900, -500, { x: 1900, y: -500 }) - // for (let i = 0; i < 40; ++i) tech.giveTech() + // for (let i = 0; i < 1; i++) powerUps.directSpawn(450, -50, "tech"); + // for (let i = 0; i < 3; i++) powerUps.directSpawn(m.pos.x + 200, m.pos.y - 50, "boost", false); + // spawn.bodyRect(575, -700, 150, 150); //block mob line of site on testing + // level.heal(); level[simulation.isTraining ? "walk" : "initial"]() //normal starting level ************************************************** - // for (let i = 0; i < 2; i++) spawn.ghoster(1300, -500) //ghosters need to spawn after the map loads - // spawn.bodyRect(2425, -120, 200, 200); - // console.log(body[body.length - 1].mass) - // simulation.isAutoZoom = false; //look in close - // simulation.zoomScale *= 0.5; - // simulation.setZoom(); - // for (let i = 0; i < 10; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech"); + // for (let i = 0; i < 1; ++i) spawn.revolutionBoss(1900, -500) + // for (let i = 0; i < 3; i++) spawn.starter(1900, -500) //ghosters need to spawn after the map loads + + // for (let i = 0; i < 1; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "entanglement"); // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 450, m.pos.y + 50 * Math.random(), "boost"); - // for (let i = 0; i < 20; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "heal"); + // for (let i = 0; i < 100; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "ammo"); // for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "field", false); //lore testing // localSettings.isTrainingNotAttempted = true // simulation.isCheating = false //true; // for (let i = 0; i < 5; i++) tech.giveTech("undefined") - // lore.techCount = 2 + // lore.techCount = 1 // level.levelsCleared = 10 - // localSettings.loreCount = 5 //this sets what conversation is heard + // localSettings.loreCount = 2 //this sets what conversation is heard + // localSettings.levelsClearedLastGame = 10 // if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage // level.onLevel = -1 //this sets level.levels[level.onLevel] = undefined which is required to run the conversation // level.null() @@ -96,9 +84,10 @@ const level = { // tech.giveTech("tinker"); //show junk tech in experiment mode // m.storeTech() // powerUps.spawn(m.pos.x, m.pos.y, "entanglement", false); + // for (let i = 0; i < 6; i++) localSettings.difficultyCompleted[i] = false + // localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } else { spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns - // spawn.pickList = ["focuser", "focuser"] level[level.levels[level.onLevel]](); //picks the current map from the the levels array if (!simulation.isCheating && !build.isExperimentRun && !simulation.isTraining) { @@ -107,7 +96,6 @@ const level = { if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } } - if (!simulation.isTraining) level.levelAnnounce(); simulation.setupCamera(player.position); simulation.setZoom(); level.addToWorld(); //add bodies to game engine @@ -128,30 +116,49 @@ const level = { } } } - if (tech.isMACHO) spawn.MACHO() + if (tech.isDarkMatter) spawn.darkMatter() for (let i = 0; i < tech.wimpCount; i++) { spawn.WIMP() mob[mob.length - 1].isDecoupling = true //so you can find it to remove for (let j = 0, len = 4; j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false) } - // if (tech.isFlipFlopLevelReset && !tech.isFlipFlopOn) { - if ((tech.isRelay || tech.isFlipFlop) && !tech.isFlipFlopOn) { - tech.isFlipFlopOn = true - if (tech.isFlipFlopHealth) m.setMaxHealth() - if (tech.isRelayEnergy) m.setMaxEnergy() - m.eyeFillColor = m.fieldMeterColor - simulation.makeTextLog(`tech.isFlipFlopOn = true`); - } - // if (m.plasmaBall) m.plasmaBall.reset() + if (m.plasmaBall) m.plasmaBall.fire() if (localSettings.entanglement && localSettings.entanglement.levelName === level.levels[level.onLevel]) { const flip = localSettings.entanglement.isHorizontalFlipped === simulation.isHorizontalFlipped ? 1 : -1 powerUps.directSpawn(flip * localSettings.entanglement.position.x, localSettings.entanglement.position.y, "entanglement", false); } level.newLevelOrPhase() + if (simulation.isTraining) { + simulation.difficultyMode = 2 + } else { + simulation.inGameConsole(`level.onLevel = "${level.levels[level.onLevel]}"`); + document.title = "n-gon: " + level.levelAnnounce(); + } + + level.setConstraints() + if (!localSettings.isHideHUD) { + requestAnimationFrame(() => { + //grow and get bright + document.getElementById("right-HUD-constraint").style.opacity = 1 + document.getElementById("right-HUD-constraint").style.fontSize = "23px" + document.getElementById("right-HUD-constraint").style.top = simulation.difficultyMode > 4 ? "6px" : "9px" + setTimeout(() => { + if (m.alive) { + //fade to background + document.getElementById("right-HUD-constraint").style.opacity = 0.35 + document.getElementById("right-HUD-constraint").style.fontSize = "20px" + document.getElementById("right-HUD-constraint").style.top = "12px" + } + }, 5000); + }); + } + }, newLevelOrPhase() { //runs on each new level but also on final boss phases //used for generalist and pigeonhole principle + tech.cancelTechCount = 0 + tech.tokamakHealCount = 0 tech.buffedGun++ if (tech.buffedGun > b.inventory.length - 1) tech.buffedGun = 0; if (tech.isGunCycle && b.activeGun !== null && b.inventory.length) { @@ -160,102 +167,418 @@ const level = { } if (tech.isGunChoice && Number.isInteger(tech.buffedGun) && b.inventory.length) { var gun = b.guns[b.inventory[tech.buffedGun]].name - simulation.makeTextLog(`pigeonhole principle: +${(31 * Math.max(0, b.inventory.length)).toFixed(0)}% damage for ${gun}`, 600); + simulation.inGameConsole(`pigeonhole principle: ${(1.3 * Math.max(0, b.inventory.length)).toFixed(2)}x damage for ${gun}`, 600); } if (tech.isSwitchReality && level.levelsCleared !== 0) { - simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`); + simulation.inGameConsole(`simulation.amplitude = ${Math.random()}`); m.switchWorlds() simulation.trails() - powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "tech", false); + powerUps.spawn(player.position.x + 50, player.position.y - Math.random() * 50, "tech", false); + powerUps.spawnDelay("coupling", 3); } if (tech.isHealLowHealth) { - if (tech.isEnergyHealth) { - var len = 4 * (1 - m.energy / m.maxEnergy) //as a percent - } else { - var len = 4 * (1 - m.health / m.maxHealth) //as a percent - } + const len = tech.isEnergyHealth ? 5 * Math.max(0, m.maxEnergy - m.energy) : 5 * Math.max(0, m.maxHealth - m.health) for (let i = 0; i < len; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "heal", false); } + if (tech.interestRate > 0) { + const rate = ((level[level.levels[level.onLevel]].name === "final" || level[level.levels[level.onLevel]].name === "subway") ? 1 / 3 : 1) * tech.interestRate //this effect triggers extra times on these final levels + + let ammoSum = 0 + for (let i = 0; i < b.inventory.length; i++) { + if (b.guns[b.inventory[i]].ammo !== Infinity) ammoSum += b.guns[b.inventory[i]].ammo / b.guns[b.inventory[i]].ammoPack + } + if (ammoSum > 0 && b.inventory.length > 0) { + const amount = Math.ceil(rate * ammoSum / b.inventory.length) + powerUps.spawnDelay("ammo", amount, 4); + simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on ammo = ${amount > 20 ? amount + powerUps.orb.ammo(1) : powerUps.orb.ammo(amount)}`) + } + + // if (b.activeGun !== null && b.activeGun !== undefined && b.guns[b.activeGun].ammo !== Infinity) { + // const ammoPerOrb = b.guns[b.activeGun].ammoPack + // const a = Math.ceil(rate * b.guns[b.activeGun].ammo / ammoPerOrb) + // powerUps.spawnDelay("ammo", a, 4); + // simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on ammo = ${a > 20 ? a + powerUps.orb.ammo(1) : powerUps.orb.ammo(a)}`) + // } + if (powerUps.research.count > 0) { + const r = Math.ceil(rate * powerUps.research.count) + simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on research = ${r > 20 ? r + powerUps.orb.research(1) : powerUps.orb.research(r)}`) + powerUps.spawnDelay("research", r, 4); + } + if (m.coupling > 0) { + const c = Math.ceil(rate * m.coupling) + powerUps.spawnDelay("coupling", c, 4); + simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on coupling = ${c > 20 ? c + powerUps.orb.coupling(1) : powerUps.orb.coupling(c)}`) + } + const healPerOrb = (powerUps.heal.size() / 40 / (simulation.healScale ** 0.25)) ** 2 + const h = Math.ceil(rate * m.health / healPerOrb) + powerUps.spawnDelay("heal", h, 4); + simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on health = ${h > 20 ? h + powerUps.orb.heal(1) : powerUps.orb.heal(h)}`) + + // trying to spawn smaller heals + // const healPerOrb = (powerUps.heal.size() / 40 / (simulation.healScale ** 0.25)) ** 2 + // console.log(healPerOrb) + // let h = tech.interestRate * m.health / healPerOrb + // console.log(tech.interestRate, m.health, healPerOrb, h) + // const overHeal = h - Math.floor(h) + // powerUps.spawn(m.pos.x, m.pos.y, "heal", true, null, Math.max(0.25, overHeal) * 40 * (simulation.healScale ** 0.25)) + // if (h > healPerOrb) powerUps.spawnDelay("heal", h); + // simulation.inGameConsole(`${(Math.ceil(tech.interestRate * 100)).toFixed(0)}% interest on health = ${h > 20 ? h + powerUps.orb.heal(1) : powerUps.orb.heal(h)}`) + } + if (tech.isEjectOld) { + let index = null //find oldest tech that you have + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count > 0 && !tech.tech[i].isInstant) { + index = i + } + } + if (index) { //eject it + const effect = Math.pow(1.1, tech.tech[index].count) + simulation.inGameConsole(`${(effect).toFixed(2)}x damage //from obsolescence`, 360) + tech.damage *= effect + powerUps.ejectTech(index) + } + } }, trainingText(say) { simulation.lastLogTime = 0; //clear previous messages simulation.isTextLogOpen = true - simulation.makeTextLog(`supervised.learning(${(Date.now() / 1000).toFixed(0)} s):
${say}
`, Infinity) + simulation.inGameConsole(`supervised.learning(${(Date.now() / 1000).toFixed(0)} s):
${say}
`, Infinity) simulation.isTextLogOpen = false - // lore.trainer.text("Wow. Just a platform.") }, trainingBackgroundColor: "#e1e1e1", custom() { }, customTopLayer() { }, - setDifficulty() { - simulation.difficulty = 0 - m.dmgScale = 1; //damage done by player decreases each level - simulation.accelScale = 1 //mob acceleration increases each level - simulation.CDScale = 1 //mob CD time decreases each level - simulation.dmgScale = Math.max(0.1, 0.25 * simulation.difficulty) //damage done by mobs scales with total levels - simulation.healScale = 1 / (1 + simulation.difficulty * 0.043) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; - }, - difficultyIncrease(num = 1) { - for (let i = 0; i < num; i++) { - simulation.difficulty++ - m.dmgScale *= 0.905; //damage done by player decreases each level - if (simulation.accelScale < 6) simulation.accelScale *= 1.024 //mob acceleration increases each level - if (simulation.CDScale > 0.15) simulation.CDScale *= 0.964 //mob CD time decreases each level - } - simulation.dmgScale = Math.max(0.1, 0.25 * simulation.difficulty) //damage done by mobs scales with total levels + updateDifficulty() { + simulation.difficulty = level.levelsCleared * simulation.difficultyMode + if (simulation.isTraining) simulation.difficulty = 1 + + let scale = 1 + if (simulation.difficultyMode > 3) { + scale = 3 + } else if (simulation.difficultyMode > 1) { + scale = 2 + } + m.dmgScale = Math.pow(0.87, level.levelsCleared * scale) + simulation.dmgScale = Math.max(0.1, 0.22 * level.levelsCleared * scale) //damage done by mobs scales with total levels //a bigger number means the player takes more damage + if (simulation.difficultyMode === 6) { + m.dmgScale *= 0.5 + simulation.dmgScale *= 2 + } + simulation.healScale = 1 / (1 + simulation.difficulty * 0.043) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; - // console.log(`CD = ${simulation.CDScale}`) - }, - difficultyDecrease(num = 1) { //used in easy mode for simulation.reset() - for (let i = 0; i < num; i++) { - simulation.difficulty-- - m.dmgScale /= 0.905; //damage done by player decreases each level - if (simulation.accelScale > 1) simulation.accelScale /= 1.024 //mob acceleration increases each level - if (simulation.CDScale < 1) simulation.CDScale /= 0.964 //mob CD time decreases each level - } - if (simulation.difficulty < 1) simulation.difficulty = 0; - simulation.dmgScale = Math.max(0.1, 0.25 * simulation.difficulty) //damage done by mobs scales with total levels - simulation.healScale = 1 / (1 + simulation.difficulty * 0.043) - }, - 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 === 5) { - return "why" + simulation.accelScale = 1.1 + simulation.CDScale = 0.9 + } else { + simulation.accelScale = Math.min(6, Math.pow(1.024, simulation.difficulty)) + simulation.CDScale = Math.max(0.15, Math.pow(0.964, simulation.difficulty)) + } + }, + constraintIndex: 0, + setConstraints() { + //populate array with possible constraints and reset constraints + level.constraintDescription1 = level.constraintDescription2 = "" + const possible = [] + for (let i = 0; i < level.constraint.length; i++) { + level.constraint[i].remove() + possible.push(i) + } + if (level.levels[level.onLevel] !== "null" && level.levels[level.onLevel] !== "initial" && !simulation.isTraining && m.alive && level.levelsCleared) { + if (simulation.difficultyMode > 2 && possible.length) { + //choose a random constraint from possible array and remove it from that array + // const index = possible[Math.floor(possible.length * Math.random())] + // const index = level.constraintIndex + // level.constraintIndex = 0 //REMOVE THIS FROM LIVE GAME, FOR TESTING ONLY!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + level.constraint[level.constraintIndex].effect() + possible.splice(level.constraintIndex, 1) + //generate text to describe the active constraints for the pause menu + level.constraintDescription1 = level.constraint[level.constraintIndex].description + // simulation.inGameConsole(`${level.constraint[level.constraintIndex].description}`, 900); + + level.constraintIndex++ + if (level.constraintIndex > level.constraint.length - 1) level.constraintIndex = 0 + + if (simulation.difficultyMode > 4 && possible.length) { + // const index = possible[Math.floor(possible.length * Math.random())] + level.constraint[level.constraintIndex].effect() + possible.splice(level.constraintIndex, 1) + level.constraintDescription2 += level.constraint[level.constraintIndex].description + // simulation.inGameConsole(`${level.constraint[level.constraintIndex].description}`, 900); + + level.constraintIndex++ + if (level.constraintIndex > level.constraint.length - 1) level.constraintIndex = 0 + } + document.getElementById("right-HUD-constraint").style.display = "block"; + } else { + document.getElementById("right-HUD-constraint").style.display = "none"; + } + } else { + document.getElementById("right-HUD-constraint").style.display = "none"; + } + //update HUD with constraints + let text = `${level.constraintDescription1}` + if (simulation.difficultyMode > 4 && level.constraintDescription2) { + text += `
${level.constraintDescription2}` + } + document.getElementById("right-HUD-constraint").innerHTML = text + if (level.constraintDescription1) { + if (level.constraintDescription2) { + document.getElementById("right-HUD").style.top = "80px"; + } else { + document.getElementById("right-HUD").style.top = "57px"; //make room for tech list in "right-HUD" + } + } else { + document.getElementById("right-HUD").style.top = "15px"; } }, + constraintDescription1: "", //used in pause menu and console + constraintDescription2: "", + constraint: [ + { + description: "0.5x energy regen", + effect() { + level.isReducedRegen = 0.5 + }, + remove() { + level.isReducedRegen = 1 + } + }, + { + description: "0.5x max health", + effect() { + level.isReducedHealth = true + m.setMaxHealth() + }, + remove() { + if (level.isReducedHealth) { + level.isReducedHealth = false + m.setMaxHealth() + m.addHealth(level.reducedHealthLost); + level.reducedHealthLost = 0 + } else { + level.isReducedHealth = false + } + + } + }, + { + description: "after 30 seconds spawn WIMPs", + effect() { + simulation.ephemera.push({ + name: "WIMPS", + time: 0, + levelName: level.levels[level.onLevel], + do() { + this.time++ + if (level.levels[level.onLevel] === this.levelName) { + if (this.time > 1800 && !(this.time % 360)) spawn.WIMP(level.enter.x, level.enter.y) + } else { + simulation.removeEphemera(this.name); + } + }, + }) + }, + remove() { + + } + }, + { + description: "0.1x damage after getting power ups", + effect() { + level.isNoDamage = true + level.noDamageCycle = 0 + }, + remove() { + level.isNoDamage = false + level.noDamageCycle = 0 + } + }, + { + description: "mobs heal after you take damage", + effect() { + level.isMobHealPlayerDamage = true + }, + remove() { + level.isMobHealPlayerDamage = false + } + }, + { + description: "mob death heals nearby mobs", + effect() { + level.isMobDeathHeal = true + }, + remove() { + level.isMobDeathHeal = false + } + }, + // { + // description: "full damage taken after boss dies", + // // description: "after boss dies damage taken = 1", + // effect() { + // level.noDefenseSetting = 1 //defense goes to zero once equal to 2 + // }, + // remove() { + // level.noDefenseSetting = 0 + // } + // }, + { + description: "4x shielded mobs", + effect() { + level.isMobShields = true + }, + remove() { + level.isMobShields = false + } + }, + { + description: "40% JUNK chance", + effect() { + level.junkAdded = 0.4 + }, + remove() { + level.junkAdded = 0 + } + }, + { + description: "-1 choice", + effect() { + level.fewerChoices = true + }, + remove() { + level.fewerChoices = false + } + }, + { + description: "power ups in stasis", + effect() { + level.isNextLevelPowerUps = true + //remove all current power ups + for (let i = powerUp.length - 1; i > -1; i--) { + powerUps.powerUpStorage.push({ name: powerUp[i].name, size: powerUp[i].size }) + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1) + } + }, + remove() { + level.isNextLevelPowerUps = false + if (powerUps.powerUpStorage.length) { + const delay = 5 + let i = 0 + let cycle = () => { + if (powerUps.powerUpStorage.length && m.alive && powerUp.length < 300) { + requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { + if (!(simulation.cycle % delay)) { + const where = { x: m.pos.x + 70 * (Math.random() - 0.5), y: m.pos.y + 70 * (Math.random() - 0.5) } + powerUps.directSpawn(where.x, where.y, powerUps.powerUpStorage[i].name, true, powerUps.powerUpStorage[i].size); + powerUps.powerUpStorage.splice(i, 1); + } + } + } else { + powerUps.powerUpStorage = [] + } + } + requestAnimationFrame(cycle); + } + } + }, + { + description: "33% of mobs respawn", + effect() { + level.isMobRespawn = true + }, + remove() { + level.isMobRespawn = false + } + }, + { + description: "0 duplication", + effect() { + level.isNoDuplicate = true + }, + remove() { + level.isNoDuplicate = false + } + }, + { + description: "2x ammo cost", + effect() { + level.is2xAmmo = true + }, + remove() { + level.is2xAmmo = false + } + }, + { + description: "0.5x max energy", + effect() { + level.isReducedEnergy = true + m.setMaxEnergy() + }, + remove() { + if (level.isReducedEnergy) { + level.isReducedEnergy = false + m.setMaxEnergy() + } else { + level.isReducedEnergy = false + } + + } + }, + { + description: "slow bots", + effect() { + level.isSlowBots = true + b.clearPermanentBots(); + b.respawnBots(); + }, + remove() { + if (level.isSlowBots) { + level.isSlowBots = false + b.clearPermanentBots(); + b.respawnBots(); + } else { + level.isSlowBots = false + } + + } + }, + ], + isMobShields: false, + junkAdded: 0, + isNextLevelPowerUps: false, + isMobRespawn: false, + fewerChoices: false, + isNoDuplicate: false, + is2xAmmo: false, + isReducedEnergy: false, + isSlowBots: false, + // noDefenseSetting: 0, + isMobDeathHeal: false, + isMobHealPlayerDamage: false, + isNoDamage: false, + noDamageCycle: 0, + reducedHealthLost: 0, + isReducedHealth: false, + isReducedRegen: 1, levelAnnounce() { - const difficulty = simulation.isCheating ? "testing" : level.difficultyText() + const cheating = simulation.isCheating ? "(testing)" : "" if (level.levelsCleared === 0) { - document.title = "n-gon: (" + difficulty + ")"; + return `initial ${cheating}`; } else { - document.title = `n-gon: ${level.levelsCleared} ${level.levels[level.onLevel]} (${difficulty})` - 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); + return `${level.levelsCleared} ${level.levels[level.onLevel]} ${cheating}` + } }, announceMobTypes() { - simulation.makeTextLog(`spawn.${spawn.pickList[0]}(x,y)`) - simulation.makeTextLog(`spawn.${spawn.pickList[1]}(x,y)`) + simulation.inGameConsole(`spawn.${spawn.pickList[0]}(x,y)`) + simulation.inGameConsole(`spawn.${spawn.pickList[1]}(x,y)`) }, disableExit: false, nextLevel() { if (!level.disableExit) { level.levelsCleared++; level.onLevel++; //cycles map to next level + level.updateDifficulty() if (simulation.isTraining) { if (level.onLevel > level.levels.length - 1) { //if all training levels are completed @@ -276,14 +599,10 @@ const level = { simulation.splashReturn(); }, 6000); return - } else { - level.setDifficulty() } } else { if (level.onLevel > level.levels.length - 1) level.onLevel = 0; - level.difficultyIncrease(simulation.difficultyMode) } - //reset lost tech display for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].isLost) tech.tech[i].isLost = false; @@ -293,7 +612,8 @@ const level = { simulation.clearNow = true; //triggers in simulation.clearMap to remove all physics bodies and setup for new map //pop up new level info screen for a few seconds //|| level.levels[level.onLevel] === "subway" - if (!localSettings.isHideHUD && !simulation.isCheating && m.alive && (level.levels[level.onLevel] === "final" || level.levels[level.onLevel] === "reactor")) { + if (!localSettings.isHideHUD && m.alive && (level.levels[level.onLevel] === "final" || level.levels[level.onLevel] === "reactor")) { + // if (!localSettings.isHideHUD && m.alive) { //pause if (!simulation.paused) { simulation.paused = true; @@ -302,7 +622,8 @@ const level = { //build level info document.getElementById("choose-grid").style.gridTemplateColumns = "250px" //onclick="level.unPause()" - let text = `
` + // if (level.levels[level.onLevel] === "final") { //|| level.levels[level.onLevel] === "reactor" + let text = `
` for (let i = 0; i < level.levels.length; i++) { if (i < level.levelsCleared) { text += `
${level.levels[i]}
` @@ -317,14 +638,14 @@ const level = { // text += `
` } } - text += `
` + text += `
` document.getElementById("choose-grid").innerHTML = text //show level info document.getElementById("choose-grid").style.opacity = "1" document.getElementById("choose-grid").style.transitionDuration = "0.25s"; //how long is the fade in on document.getElementById("choose-grid").style.visibility = "visible" - + // } simulation.draw.cons(); simulation.draw.body(); level.customTopLayer(); @@ -358,37 +679,6 @@ const level = { } requestAnimationFrame(newLevelDraw); } - // else { - // //pause - // if (!simulation.paused) { - // simulation.paused = true; - // simulation.isChoosing = true; //stops p from un pausing on key down - // } - // let count = countMax = simulation.testing ? 0 : 60 - // let newLevelDraw = () => { - // count-- - // if (count > 0) { - // requestAnimationFrame(newLevelDraw); - // } else { //unpause - // // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 30; //player is immune to damage for 30 cycles - // if (simulation.paused) requestAnimationFrame(cycle); - // if (m.alive) simulation.paused = false; - // simulation.isChoosing = false; //stops p from un pausing on key down - // build.unPauseGrid() - // } - // //draw - // simulation.wipe(); - // m.look(); - // simulation.camera(); - // const scale = 30 - // ctx.setLineDash([scale * (countMax - count), scale * count]); - // simulation.draw.wireFrame(); - // ctx.setLineDash([]); - // ctx.restore(); - // simulation.drawCursor(); - // } - // requestAnimationFrame(newLevelDraw); - // } } }, unPause() { @@ -413,7 +703,7 @@ const level = { if (index !== -1) { level.communityLevels.splice(index, 1); // console.log('removed level:', remove[i]) - requestAnimationFrame(() => { simulation.makeTextLog(`banned level: ${remove[i]}`); }); + requestAnimationFrame(() => { simulation.inGameConsole(`banned level: ${remove[i]}`); }); } } // console.log('community levels after', level.communityLevels) @@ -424,7 +714,7 @@ const level = { if (index !== -1) { level.playableLevels.splice(index, 1); // console.log('removed level:', remove[i]) - requestAnimationFrame(() => { simulation.makeTextLog(`banned level: ${remove[i]}`); }); + requestAnimationFrame(() => { simulation.inGameConsole(`banned level: ${remove[i]}`); }); } } // console.log('Landgreen levels after', level.playableLevels) @@ -487,21 +777,6 @@ const level = { level.exit.x = -level.exit.x - 100 //minus the 100 because of the width of the graphic }, exitCount: 0, - // 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.exitCount++ - // if (level.exitCount > 120) { - // level.exitCount = 0 - // level.nextLevel() - // } - // } - // }, setPosToSpawn(xPos, yPos) { m.spawnPos.x = m.pos.x = xPos; m.spawnPos.y = m.pos.y = yPos; @@ -548,9 +823,9 @@ const level = { player.velocity.y < 0.15 ) { // level.exitCount += input.down ? 8 : 2 - level.exitCount += 2 + level.exitCount += 3 } else if (level.exitCount > 0) { - level.exitCount -= 2 + level.exitCount -= 3 } ctx.beginPath(); @@ -598,13 +873,12 @@ const level = { let text = `

training

- Begin the guided tutorial that shows you how to use your field and gun. + Begin the guided tutorial that shows you how to use your ${powerUps.orb.field()} and ${powerUps.orb.gun()}.

play

Begin the standard game where you progress through 13 random levels and beat the final boss.
` - //use you use your gun, field, and tech document.getElementById("choose-grid").innerHTML = text //show level info document.getElementById("choose-grid").style.opacity = "1" @@ -769,70 +1043,121 @@ const level = { return who }, - boost(x, y, height = 1000) { //height is how high the player will be flung above y - who = map[map.length] = Matter.Bodies.fromVertices(x + 50, y + 35, Vertices.fromPath("120 40 -120 40 -50 -40 50 -40"), { - 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 - }, - boostBounds: { - min: { - x: x, - y: y - 20 + boost(x, y, speed = 1000, angle = Math.PI / 2) { //height is how high the player will be flung above y + if (angle !== Math.PI / 2) { //angle !== 3 * Math.PI / 2 + angle *= -1 + who = map[map.length] = Matter.Bodies.fromVertices(x + 50, y + 35, Vertices.fromPath("80 40 -80 40 -50 -40 50 -40"), { + 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 }, - max: { - x: x + 100, - y: y - } - }, - yVelocity: -1.21 * Math.sqrt(Math.abs(height)), - query() { - // check for collisions - query = (who) => { - if (Matter.Query.region(who, this.boostBounds).length > 0) { - list = Matter.Query.region(who, this.boostBounds) - Matter.Body.setVelocity(list[0], { - x: list[0].velocity.x + (Math.random() - 0.5) * 2.5, //add a bit of horizontal drift to reduce endless bounces - y: this.yVelocity //give a upwards velocity - }); + query() { + // check for collisions + const rayVector = Vector.add(this.position, Vector.rotate({ x: 100, y: 0 }, angle)) + query = (who) => { + const list = Matter.Query.ray(who, this.position, rayVector, 100) + if (list.length > 0) { + Matter.Body.setVelocity(list[0].bodyA, Vector.rotate({ x: 1.21 * Math.sqrt(Math.abs(speed)), y: 0 }, angle)); + } } - } - query(body) - query(mob) - query(bullet) - query(powerUp) - //player collision - if (Matter.Query.region([player], this.boostBounds).length > 0 && !input.down) { - m.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts - m.hardLandCD = 0 // disable hard landing - if (player.velocity.y > 26) { - Matter.Body.setVelocity(player, { - x: player.velocity.x, - y: -15 //gentle bounce if coming down super fast - }); - } else { - Matter.Body.setVelocity(player, { - x: player.velocity.x + (Math.random() - 0.5) * 2.5, - y: this.yVelocity //give a upwards velocity that will put the player that the height desired - }); + query(body) + query(mob) + query(bullet) + query(powerUp) + //player collision + const list = Matter.Query.ray([player], this.position, rayVector, 100) + if (list.length > 0) { + Matter.Body.setVelocity(player, Vector.rotate({ x: 1.21 * Math.sqrt(Math.abs(speed)), y: 0 }, angle)); + m.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts + m.hardLandCD = 0 // disable hard landing } - } - //draw - ctx.fillStyle = "rgba(200,0,255,0.15)"; - ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 10, 100, 30); - ctx.fillStyle = "rgba(200,0,255,0.05)"; - ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 50, 100, 70); - // ctx.fillStyle = "rgba(200,0,255,0.02)"; - // ctx.fillRect(x, y - 120, 100, 120); - }, - }); - return who + //draw + const v1 = this.vertices[0] + const v2 = this.vertices[1] + let unit = Vector.rotate({ x: 60, y: 0 }, angle) + let v3 = Vector.add(v2, unit) + let v4 = Vector.add(v1, unit) + ctx.beginPath(); + ctx.moveTo(v1.x, v1.y) + ctx.lineTo(v2.x, v2.y) + ctx.lineTo(v3.x, v3.y) + ctx.lineTo(v4.x, v4.y) + ctx.fillStyle = "rgba(200,0,255,0.05)"; + ctx.fill() + unit = Vector.rotate({ x: 20, y: 0 }, angle) + v3 = Vector.add(v2, unit) + v4 = Vector.add(v1, unit) + ctx.beginPath(); + ctx.moveTo(v1.x, v1.y) + ctx.lineTo(v2.x, v2.y) + ctx.lineTo(v3.x, v3.y) + ctx.lineTo(v4.x, v4.y) + ctx.fillStyle = "rgba(200,0,255,0.15)"; + ctx.fill() + }, + }); + Matter.Body.rotate(who, angle + Math.PI / 2); + return who + } else { + who = map[map.length] = Matter.Bodies.fromVertices(x + 50, y + 35, Vertices.fromPath("120 40 -120 40 -50 -40 50 -40"), { + 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 + }, + boostBounds: { + min: { + x: x, + y: y - 20 + }, + max: { + x: x + 100, + y: y + } + }, + query() { + // check for collisions + query = (who) => { + if (Matter.Query.region(who, this.boostBounds).length > 0) { + list = Matter.Query.region(who, this.boostBounds) + Matter.Body.setVelocity(list[0], { + x: list[0].velocity.x + (Math.random() - 0.5) * 2.5, //add a bit of horizontal drift to reduce endless bounces + y: -1.21 * Math.sqrt(Math.abs(speed)) //give a upwards velocity + }); + } + } + query(body) + query(mob) + query(bullet) + query(powerUp) + //player collision + if (Matter.Query.region([player], this.boostBounds).length > 0 && !input.down) { + m.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts + m.hardLandCD = 0 // disable hard landing + if (player.velocity.y > 26) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: -15 //gentle bounce if coming down super fast + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x + (Math.random() - 0.5) * 2.5, + y: -1.21 * Math.sqrt(Math.abs(speed)) //give an upwards velocity that will put the player that the height desired + }); + } + } + + //draw + ctx.fillStyle = "rgba(200,0,255,0.15)"; + ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 10, 100, 30); + ctx.fillStyle = "rgba(200,0,255,0.05)"; + ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 50, 100, 70); + }, + }); + return who + } }, - elevator(x, y, width, height, maxHeight, force = 0.003, friction = { - up: 0.01, - down: 0.2 - }, isAtTop = false) { + elevator(x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }, isAtTop = false) { x += width / 2 y += height / 2 maxHeight += height / 2 @@ -841,7 +1166,7 @@ const level = { const who = body[body.length] = Bodies.rectangle(x, isAtTop ? maxHeight : y, width, height, { collisionFilter: { category: cat.body, //cat.map, - mask: cat.map | cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + mask: cat.player | cat.body | cat.bullet | cat.mob | cat.mobBullet //| cat.powerUp }, inertia: Infinity, //prevents rotation isNotHoldable: true, @@ -897,17 +1222,69 @@ const level = { x: this.holdX, y: this.position.y }); + }, + moveOnTouch() { + if (!m.isBodiesAsleep) { + if (this.isUp) { //moving up still with high air friction + this.force.y -= force * this.mass //hard force propels up, even with high friction + if (this.position.y < maxHeight) { //switch to down mode + this.isUp = false + this.frictionAir = friction.down + //adds a hard jerk at the top of vertical motion because it's fun + Matter.Body.setPosition(this, { x: this.holdX, y: maxHeight }); + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + } + } else if (this.position.y + 10 * this.velocity.y > y) { //free falling down, with only air friction + //slow down early to avoid a jerky stop that can pass through blocks + Matter.Body.setVelocity(this, { x: 0, y: this.velocity.y * 0.7 }); + //switch to up mode + // if (this.position.y + this.velocity.y > y) { + // this.isUp = true + // this.frictionAir = friction.up + // } + } + Matter.Body.setVelocity(this, { x: 0, y: this.velocity.y }); + } + //draw line to show how far to will extend + ctx.beginPath(); + ctx.moveTo(x, y + height / 2); + ctx.lineTo(x, maxHeight - height / 2); + ctx.strokeStyle = `rgba(0,0,0,0.2)` + ctx.lineWidth = "2" + ctx.stroke(); + + //draw body + ctx.beginPath(); + ctx.moveTo(this.vertices[0].x, this.vertices[0].y); + for (let j = 1; j < this.vertices.length; j++) { + ctx.lineTo(this.vertices[j].x, this.vertices[j].y); + } + ctx.lineTo(this.vertices[0].x, this.vertices[0].y); + ctx.lineWidth = "2" + ctx.strokeStyle = `#333` + ctx.fillStyle = `rgba(200,200,200,1)` + //edge limits + if (this.position.y < maxHeight) { + Matter.Body.setPosition(this, { x: this.holdX, y: maxHeight }); + } else if (this.position.y > y) { + ctx.fillStyle = `rgba(255,255,255,${0.5 + 0.15 * Math.random()})` + Matter.Body.setPosition(this, { x: this.holdX, y: y }); + //undoing force of gravity + this.force.y -= this.mass * simulation.g; + if (Matter.Query.collides(this, [player]).length) { + this.isUp = true + this.frictionAir = friction.up + } + } + ctx.fill(); + ctx.stroke(); + // hold horizontal position + Matter.Body.setPosition(this, { x: this.holdX, y: this.position.y }); }, off() { - Matter.Body.setPosition(this, { - x: this.holdX, - y: this.position.y - }); - Matter.Body.setVelocity(this, { - x: 0, - y: this.velocity.y - }); + Matter.Body.setPosition(this, { x: this.holdX, y: this.position.y }); + Matter.Body.setVelocity(this, { x: 0, y: this.velocity.y }); }, constraint: this.null, addConstraint() { @@ -935,73 +1312,69 @@ const level = { who.classType = "body" return who }, - spring(x, y, v = "-100 0 100 0 70 40 0 50 -70 40", force = 0.01, distance = 300, angle = 0) { - const who = body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(v), { - 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, - frictionAir: 1, - density: 0.1, - isReady: true, - isResetting: false, - query() { - if (this.isReady) { - if (Matter.Query.collides(this, [player]).length) { - this.isReady = false - this.constraint.stiffness = 0 - this.constraint.damping = 0 //0.3 - this.frictionAir = 0 - Matter.Body.setVelocity(this, { - x: 0, - y: 0 - }); - //show graphically being ready? - - } - } else { - if (this.isResetting) { - this.constraint.stiffness += 0.0005 - if (this.constraint.stiffness > 0.1) { - this.isResetting = false - this.isReady = true - } - } else { - if (Vector.magnitudeSquared(Vector.sub(this.position, { - x: x, - y: y - })) < distance * distance) { - this.force.y -= force * this.mass - } else { - this.constraint.damping = 1 - this.frictionAir = 1 - this.isResetting = true - Matter.Body.setVelocity(this, { - x: 0, - y: 0 - }); - } - } - } - } - }); - who.constraint = Constraint.create({ - pointA: { - x: who.position.x, - y: who.position.y - }, - bodyB: who, - stiffness: 1, - damping: 1 - }); - Composite.add(engine.world, who.constraint); - return who - }, + // spring(x, y, v = "-100 0 100 0 70 40 0 50 -70 40", force = 0.01, distance = 300, angle = 0) { + // const who = body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(v), { + // 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, + // frictionAir: 1, + // density: 0.1, + // isReady: true, + // isResetting: false, + // query() { + // if (this.isReady) { + // if (Matter.Query.collides(this, [player]).length) { + // this.isReady = false + // this.constraint.stiffness = 0 + // this.constraint.damping = 0 //0.3 + // this.frictionAir = 0 + // Matter.Body.setVelocity(this, { x: 0, y: 0 }); + // //show graphically being ready? + // } + // } else { + // if (this.isResetting) { + // this.constraint.stiffness += 0.0005 + // if (this.constraint.stiffness > 0.1) { + // this.isResetting = false + // this.isReady = true + // } + // } else { + // if (Vector.magnitudeSquared(Vector.sub(this.position, { + // x: x, + // y: y + // })) < distance * distance) { + // this.force.y -= force * this.mass + // } else { + // this.constraint.damping = 1 + // this.frictionAir = 1 + // this.isResetting = true + // Matter.Body.setVelocity(this, { + // x: 0, + // y: 0 + // }); + // } + // } + // } + // } + // }); + // who.constraint = Constraint.create({ + // pointA: { + // x: who.position.x, + // y: who.position.y + // }, + // bodyB: who, + // stiffness: 1, + // damping: 1 + // }); + // Composite.add(engine.world, who.constraint); + // return who + // }, // rotor(x, y, rotate = 0, radius = 800, width = 40, density = 0.0005) { // const rotor1 = Matter.Bodies.rectangle(x, y, width, radius, { // density: density, @@ -1170,10 +1543,7 @@ const level = { y: list[0].position.y }) } - Matter.Body.setVelocity(list[0], { - x: 0, - y: 0 - }); + Matter.Body.setVelocity(list[0], { x: 0, y: 0 }); } } this.isUp = false; @@ -1747,12 +2117,12 @@ const level = { if (this.height > 0 && Matter.Query.region([player], this).length) { if (m.immuneCycle < m.cycle) { - const DRAIN = 0.004 * (tech.isRadioactiveResistance ? 0.25 : 1) + const DRAIN = 0.004 * (tech.isRadioactiveResistance ? 0.2 : 1) if (m.energy > DRAIN) { m.energy -= DRAIN if (tech.isEnergyHealth && m.energy < 0) m.death() } else { - m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1)) + m.damage(damage * (tech.isRadioactiveResistance ? 0.2 : 1)) } } @@ -2102,7 +2472,6 @@ const level = { document.body.style.backgroundColor = "#fff"; // color.map = "#444" //custom map color - // level.difficultyIncrease(14); //hard mode level 7 level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) @@ -2189,7 +2558,6 @@ const level = { //??? - // level.difficultyIncrease(3 * 4) //30 is near max on hard //60 is near max on why // m.addHealth(Infinity) // spawn.starter(1900, -500, 200) //big boy @@ -2345,56 +2713,68 @@ const level = { spawn.mapRect(-500, -25, 25, 50); //edge shelf spawn.mapRect(475, -25, 25, 50); //edge shelf }, - initial() { - if (level.levelsCleared === 0) { //if this is the 1st level of the game - if (simulation.difficultyMode > 2) spawn.setSpawnList() // hard and why difficulty don't begin with starter mobs - - //wait to spawn power ups until unpaused - //power ups don't spawn in experiment mode, so they don't get removed at the start of experiment mode - const goal = simulation.cycle + 10 + initialPowerUps() { + //wait to spawn power ups until unpaused + //power ups don't spawn in experiment mode, so they don't get removed at the start of experiment mode + const goal = simulation.cycle + 10 + function cycle() { + if (simulation.cycle > goal) { + if (localSettings.loreCount === 6) { + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2170, "field", false); + } else { + powerUps.spawnStartingPowerUps(2095 + 20 * (Math.random() - 0.5), -2200); + } + if (simulation.difficultyMode === 1) { + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2600, "ammo", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2550, "ammo", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2400, "heal", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2350, "heal", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2350, "heal", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2100, "research", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2060, "research", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2120, "research", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2075, "research", false); + } else if (simulation.difficultyMode === 6) { - function cycle() { - if (simulation.cycle > goal) { - if (localSettings.loreCount === 6) { - powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2170, "field", false); - } else { - powerUps.spawnStartingPowerUps(2095 + 15 * (Math.random() - 0.5), -2070 - 125); - } - if (simulation.difficultyMode < 5) { //hard, normal and easy - powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 25, "heal", false); - powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070, "research", false); - } - if (simulation.difficultyMode < 3) { //normal and easy - powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 75, "heal", false); - } - if (simulation.difficultyMode < 2) { //easy - powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 75, "heal", false); - powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070, "research", false); - powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070, "research", false); - } } else { - requestAnimationFrame(cycle); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2300, "heal", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2100, "heal", false); + powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2060, "research", false); } + //spin the power ups to prevent them from stacking awkwardly + for (let i = 0; i < powerUp.length; i++) { + Matter.Body.setAngularVelocity(powerUp[i], 5 * (Math.random() - 0.5)) + } + } else { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + }, + initial() { + if (level.levelsCleared === 0) { //if this is the 1st level of the game + if (simulation.difficultyMode > 2) spawn.setSpawnList() // hard and why difficulty don't begin with starter mobs + level.initialPowerUps() + if (level.levelsCleared === 0) powerUps.directSpawn(-60, -950, "difficulty", false); + + if (!simulation.isCheating && !m.isShipMode && !build.isExperimentRun) { + spawn.wireFoot(); + spawn.wireFootLeft(); + spawn.wireKnee(); + spawn.wireKneeLeft(); + spawn.wireHead(); + } else { + simulation.isCheating = true; } - requestAnimationFrame(cycle); if (localSettings.levelsClearedLastGame < 3) { - if (!simulation.isCheating && !m.isShipMode && !build.isExperimentRun) { - spawn.wireFoot(); - spawn.wireFootLeft(); - spawn.wireKnee(); - spawn.wireKneeLeft(); - spawn.wireHead(); - // for (let i = 0; i < 3; i++) powerUps.spawn(2095, -1220 - 50 * i, "tech", false); //unavailable tech spawns - // spawn.mapRect(2000, -1025, 200, 25); - } } else if (!build.isExperimentRun) { - simulation.trails() + simulation.trails(70) //bonus power ups for clearing runs in the last game if (!simulation.isCheating && localSettings.levelsClearedLastGame > 1) { for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) powerUps.spawn(2095 + 2 * Math.random(), -1270 - 50 * i, "tech", false); //spawn a tech for levels cleared in last game - simulation.makeTextLog(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`); - simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") //simulation superposition}`); + simulation.inGameConsole(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`); + simulation.inGameConsole(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") //simulation superposition}`); localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } @@ -2492,6 +2872,14 @@ const level = { wires.lineTo(2600, -690) level.custom = () => { + //working on a message using text + // ctx.font = "50px Arial"; + // ctx.fillStyle = "rgba(0,0,0,0.3)" + // for (let i = 0; i < 5; i++) { + // const wiggle = 10 + // ctx.fillText("move", 500 + wiggle * Math.random(), -500 + wiggle * Math.random()); + // } + //push around power ups stuck in the tube wall if (!(simulation.cycle % 30)) { for (let i = 0, len = powerUp.length; i < len; i++) { @@ -2538,27 +2926,6 @@ const level = { ctx.fillStyle = "#ccc" ctx.fill() - //power up dispenser - // ctx.beginPath() - // for (let i = 2; i < 10; i++) { - // ctx.moveTo(2000, -100 * i) - // ctx.lineTo(2080, -100 * i) - // } - // ctx.strokeStyle = "#ddd" - // ctx.lineWidth = 5; - // ctx.stroke(); - - // ctx.beginPath() - // for (let i = 2; i < 10; i++) { - // ctx.arc(2040, -100 * i, 30, 0, 2 * Math.PI); - // ctx.moveTo(2040, -100 * i) - // } - // ctx.fillStyle = "rgba(0,0,0,0.3)" - // ctx.fill() - - // ctx.fillStyle = "rgba(240,255,255,0.5)" - // ctx.fillRect(2000, -1000, 80, 700) - //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(2600, -600, 400, 300) @@ -2591,8 +2958,13 @@ const level = { spawn.mapRect(3000, -2800, 2600, 4600); //right wall // spawn.mapRect(-250, 0, 3600, 1800); //ground - spawn.mapRect(-250, 0, 2300, 1800); //split roof - spawn.mapRect(2150, 0, 1200, 1800); //split roof + spawn.mapRect(-250, 0, 2300, 1800); //ground + + Matter.Body.setVelocity(map[map.length - 1], { + x: 10, + y: -10 + }); + spawn.mapRect(2150, 0, 1200, 1800); //ground spawn.mapRect(2025, -3, 25, 15); //lip on power up chamber spawn.mapRect(2150, -3, 25, 15); //lip on power up chamber @@ -2790,7 +3162,6 @@ const level = { }, subway() { // simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode - // level.difficultyIncrease(10 * 4); // m.maxHealth = m.health = 100 // color.map = "#333" //custom map color document.body.style.backgroundColor = "#e3e3e3"//"#e3e3e3"//color.map//"#333"//"#000" @@ -2801,7 +3172,6 @@ const level = { level.exit.x = 0; level.exit.y = -9000; // spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump disabled for performance - const stationWidth = 9000 let stationNumber = 0; let stationCustom = () => { } @@ -2857,7 +3227,7 @@ const level = { if (gateButton.isUp) { gateButton.query(); if (!gateButton.isUp) { - simulation.makeTextLog(`station gate opened`, 360); + simulation.inGameConsole(`station gate opened`, 360); if (stationNumber > 0) { if (!isExitOpen && gatesOpenRight < stationNumber) level.newLevelOrPhase() //run some new level tech effects gatesOpenRight = stationNumber @@ -2869,7 +3239,7 @@ const level = { gatesOpenRight = stationNumber } if (Math.abs(stationNumber) > 0 && ((Math.abs(stationNumber) + 1) % stationList.length) === 0) { - simulation.makeTextLog(`level exit opened`, 360); + simulation.inGameConsole(`level exit opened`, 360); isExitOpen = true; } } @@ -2882,6 +3252,7 @@ const level = { if (isExitOpen) { level.exit.x = x - 50; level.exit.y = -260; + if (simulation.difficultyMode < 6) powerUps.spawn(level.exit.x, level.exit.y - 100, "tech"); } else { var gateButton = level.button(x - 62, -237, 125, false) //x, y, width = 126, isSpawnBase = true gateButton.isUp = true @@ -4013,9 +4384,10 @@ const level = { } else { isSpawnedBoss = true isDoorsLocked = true - for (let i = 0; i < 9; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "ammo") - for (let i = 0; i < 3; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "heal"); - const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54 + for (let i = 0; i < 12; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1800, "ammo") + for (let i = 0; i < 5; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "heal"); + for (let i = 0; i < 1; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1750, "research"); + const scale = Math.pow(simulation.difficulty, 0.7) if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { for (let i = 0; i < 250; i++) spawn.starter(-2700 + 2400 * Math.random(), -1300 - 500 * Math.random()) } else { @@ -4036,7 +4408,6 @@ const level = { } } } - // spawn.secondaryBossChance(-2300, -800) } } else { doorIn.isClosing = false @@ -4056,8 +4427,7 @@ const level = { // powerUps.spawnBossPowerUp(-3600, -100) powerUps.spawn(-3650, -50, "tech") powerUps.spawn(-3650, -150, "tech") - powerUps.spawn(-3650, -300, "tech") - // if (player.position.x < 2760 && player.position.x > 210) {} + if (simulation.difficultyMode < 6) powerUps.spawn(-3650, -300, "tech") } } }; @@ -4166,10 +4536,267 @@ const level = { // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); powerUps.addResearchToLevel() //needs to run after mobs are spawned }, + towers() { + // simulation.isHorizontalFlipped = true + level.announceMobTypes() + const isFlipped = (simulation.isHorizontalFlipped && Math.random() < 0.33) ? true : false + if (isFlipped) { + level.setPosToSpawn(9150 + 50, -2230 - 25); + level.exit.x = 400 - 50; + level.exit.y = -50 + 25; + leftRoomColor = "#cff" + rightRoomColor = "rgba(0,0,0,0.13)" + } else { + level.setPosToSpawn(400, -50); + level.exit.x = 9150; + level.exit.y = -2230; + } + + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + level.fallMode = "position"; //must set level.fallModeBounds in this mode to prevent player getting stuck left or right + level.fallModeBounds = { left: level.enter.x, right: level.exit.x } //used with level.fallMode = "position"; + if (isFlipped) level.fallModeBounds = { left: level.exit.x, right: level.enter.x } //used with level.fallMode = "position"; + simulation.fallHeight = 5000 //level.enter.y - 4000 + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 2300 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#cdd9df"; + powerUps.spawnStartingPowerUps(6300, 1025) + + const boost1 = level.boost(7560, 1480, 1700, 1.75) + const boost2 = level.boost(7098, 0, 1250, Math.PI / 3) //x,y,push,angle radians + const boost3 = level.boost(9700, -730, 1050, 1.95) + const boost4 = level.boost(4300, -720, 1500, 1.25) + const boost5 = level.boost(3000, -1215, 3000, 1.25) + const boost6 = level.boost(8251, -619, 1200, 2.7) + const boost7 = level.boost(7750, -1540, 1050, 1.2) + // const boost6 = level.boost(8235, -619, 3500, 2.9) + + const train1 = level.transport(3650, 100, 415, 500, 8); //x,y,width.height,VxGoal,force + const train2 = level.transport(1250, 100, 415, 500, -8); //x,y,width.height,VxGoal,force + const train3 = level.transport(4050, 100, 415, 500, 8); //x,y,width.height,VxGoal,force + + let portal1, portal2 + portal1 = level.portal({ + x: 3675, + y: -2225 + 1025 + }, -Math.PI / 2, { //up + x: 3675, + y: -375 + }, Math.PI / 2) //down + + portal2 = level.portal({ + x: 6300, + y: -1225 + }, -Math.PI / 2, { //up + x: 6300, + y: -375 + }, Math.PI / 2) //down + level.custom = () => { + boost1.query(); + boost2.query(); + boost3.query(); + boost4.query(); + boost5.query(); + boost6.query(); + boost7.query(); + //trains oscillate back and forth and act like they are bouncing off each other + if (train1.position.x < 2850) { + train1.changeDirection(true) //go right + } else if (train1.position.x > 3850) { + train1.changeDirection(false) //go left + } + if (train2.position.x < 1450) { + train2.changeDirection(true) //go right + } else if (train2.position.x > 2450) { + train2.changeDirection(false) //go left + } + if (train3.position.x < 4250) { + train3.changeDirection(true) //go right + } else if (train3.position.x > 5250) { + train3.changeDirection(false) //go left + } + train1.move(); + train2.move(); + train3.move(); + ctx.fillStyle = "rgba(0,0,0,0.25)" + ctx.fillRect(1250, 121, 4200, 6) + ctx.fillStyle = "rgba(50,70,100,0.04)" + ctx.fillRect(2500, -10000, 1800, 30000); + ctx.fillRect(8300, -10000, 1800, 30000); + ctx.fillRect(-500, -10000, 1800, 30000); + ctx.fillRect(5400, -10000, 1800, 30000); + + portal1[2].query() + portal1[3].query() + portal2[2].query() + portal2[3].query() + + ctx.fillStyle = "#cff" + if (isFlipped) { + ctx.fillRect(150, -300, 525, 325); //entrance typically + } else { + ctx.fillRect(8925, -2575, 525, 400) //exit typically + } + + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + ctx.fillStyle = "rgba(0,0,0,0.13)" + ctx.fillRect(8300, -1950, 1550, 1275); + ctx.fillRect(5400, 875, 1800, 650); + ctx.fillRect(2950, -2200, 875, 1050); + ctx.fillRect(5900, -1025, 800, 450); + if (isFlipped) { + ctx.fillRect(8925, -2575, 575, 400) //exit typically + } else { + ctx.fillRect(150, -300, 525, 325); //entrance typically + } + + ctx.fillStyle = "rgba(0,0,0,0.5)" + ctx.fillRect(7175, -1515, 125, 180); + portal1[0].draw(); + portal1[1].draw(); + portal1[2].draw(); + portal1[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + }; + + // four large rounded squares + let a = 900 //side length + let c = 100 //corner offset + // spawn.mapVertex(3400, -1300, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off + // spawn.mapVertex(9200, -1300, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off + // spawn.mapVertex(6300, 900, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off + spawn.mapVertex(400, 900, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off + //lower 1st zone entrance /exit + spawn.mapRect(100, -350, 575, 75); + spawn.mapRect(100, -300, 75, 375); + spawn.mapRect(600, -325, 75, 175); + spawn.mapRect(600, -10, 75, 50); + + + //2nd zone upper hollow square + spawn.mapVertex(5650 - 2900, 900 - 2200, `${-a} ${-a + c} ${-a + c} ${-a} ${-400} ${-a} ${-400} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //1/2 square with edges cut off + spawn.mapVertex(6950 - 2900, 900 - 2200, `${400} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${400} ${a}`); //1/2 square with edges cut off + // spawn.mapRect(5600 - 2900, 1400 - 2200, 1350, 400); + spawn.mapRect(2950, -1175, 650, 775); + spawn.mapRect(3750, -1175, 100, 775); + spawn.mapRect(3575, -1025, 200, 475); + + + //4th zone far right hollow square near exit + spawn.mapVertex(9200, -2050, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${-600} ${-a} ${-600}`); //square with edges cut off --- hollow top + spawn.mapVertex(9200, -550, `${-a} ${600} ${a} ${600} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off --- hollow bottom + spawn.mapRect(9800, -2100, 300, 1600); //hollow left wall + spawn.mapVertex(8175, -1425, "-1400 -90 350 -90 400 -40 400 40 350 90 -1400 90"); + spawn.mapVertex(6856, -1425, "300 -90 -350 -90 -400 -40 -400 40 -350 90 300 90"); + //exit housing + spawn.mapRect(8925, -2575, 575, 75); + if (isFlipped) { + spawn.mapRect(8925, -2550, 75, 400); + spawn.mapRect(9425, -2550, 75, 125); + spawn.mapRect(9425, -2215, 75, 50); + spawn.bodyRect(9425, -2425, 75, 210); + } else { + spawn.mapRect(9425, -2550, 75, 400); + spawn.mapRect(8925, -2550, 75, 125); + spawn.mapRect(8925, -2215, 75, 50); + } + + + + //lower 3rd zone + spawn.mapVertex(6300, 450, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${0} ${-a} ${0}`); //square with edges cut off --- hollow top + spawn.mapVertex(6300, 1200, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); + spawn.mapVertex(6450, 1650, `${-a} ${600} ${a + 700} ${600} ${a + 700} ${a - c} ${a - c + 700} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off --- hollow bottom + + //upper 3rd zone + a = 400 //side length + c = 50 //corner offset + // spawn.mapVertex(6300, -800, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off + spawn.mapVertex(6300, -1100, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${-200} ${-a} ${-200}`); //square with edges cut off + spawn.mapVertex(6300, -500, `${-a} ${200} ${a} ${200} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off + spawn.mapVertex(5800, -1425, "-300 -40 -250 -90 250 -90 300 -40 300 40 250 90 -250 90 -300 40"); + spawn.mapVertex(5485, -1850, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); + spawn.mapVertex(7115, -1850, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); //long + spawn.mapVertex(6300, -2175, "-300 -40 -250 -90 250 -90 300 -40 300 40 250 90 -250 90 -300 40"); //highest + spawn.mapVertex(4450, -1850, "-200 -40 -150 -90 150 -90 200 -40 200 40 150 90 -150 90 -200 40"); + // spawn.mapVertex(5300, -300, "-300 -60 -270 -90 270 -90 300 -60 300 60 270 90 -270 90 -300 60"); + spawn.mapVertex(5300, -300, "-300 -40 -250 -90 250 -90 300 -40 300 40 250 90 -250 90 -300 40"); + spawn.mapVertex(4500, -590, "-300 -90 250 -90 300 -40 300 40 250 90 -300 90"); + // spawn.mapVertex(4600, -590, "-500 -90 170 -90 200 -60 200 60 170 90 -500 90"); + + //no debris on this level, so spawn some heals and ammo + powerUps.chooseRandomPowerUp(6275, 1425); + powerUps.chooseRandomPowerUp(6300, -650); + powerUps.chooseRandomPowerUp(9550, -750); + + //random blocks + spawn.bodyRect(7725, -2200, 150, 250, 0.2); + spawn.bodyRect(4625, -825, 75, 125, 0.2); + spawn.bodyRect(3250, -1200, 25, 25, 0.2); + spawn.bodyRect(3375, -1275, 25, 75, 0.2); + spawn.bodyRect(3450, -1200, 50, 25, 0.2); + spawn.bodyRect(2825, -2225, 25, 25, 0.2); + spawn.bodyRect(4075, -2225, 50, 25, 0.2); + spawn.bodyRect(8850, -800, 75, 100, 0.2); + spawn.bodyRect(6900, -100, 75, 100, 0.2); + spawn.bodyRect(8975, -1575, 50, 50, 0.2); + spawn.bodyRect(5725, -1700, 125, 175, 0.2); + spawn.bodyRect(6850, -1725, 150, 200, 0.2); + spawn.bodyRect(500, -400, 100, 50, 0.3); + spawn.bodyRect(6025, 1050, 100, 50, 0.2); + spawn.bodyRect(6000, -800, 75, 200, 0.2); + spawn.bodyRect(6775, -75, 125, 75, 0.5); + spawn.bodyRect(7200, 1300, 50, 200, 0.5); + + + //mobs + spawn.randomMob(5700, -75, 0); + spawn.randomMob(6200, -100, 0); + spawn.randomMob(6900, -100, 0.1); + spawn.randomMob(5550, -500, 0.1); + spawn.randomMob(4675, -850, 0.1); + spawn.randomMob(4450, -2050, 0.1); + spawn.randomMob(4050, -2325, 0.1); + spawn.randomMob(3350, -1325, 0.2); + spawn.randomMob(5300, -2050, 0.2); + spawn.randomMob(5675, -2050, 0.2); + spawn.randomMob(5850, -1625, 0.3); + spawn.randomMob(6775, -1600, 0.3); + spawn.randomMob(7700, -1625, 0.4); + spawn.randomMob(7850, -2000, 0.4); + spawn.randomMob(7225, -2000, 0.4); + spawn.randomMob(6350, -2400, 0.5); + spawn.randomMob(8850, -1650, 0.5); + spawn.randomMob(9500, -1300, 0.5); + spawn.randomMob(9250, -900, 0.5); + spawn.randomMob(8600, -875, 0.6); + spawn.randomMob(5575, 1350, 0.6); + spawn.randomMob(6075, 1025, 0.6); + spawn.randomMob(6300, 1025, 0.7); + spawn.randomMob(6525, 1425, 0.8); + spawn.randomMob(7125, 1450, 0.9); + // spawn.randomMob(8600, -2325, 0.7); + // spawn.randomMob(8650, -2825, 0.8); + // spawn.randomMob(9225, -2850, 0.9); + // spawn.randomMob(8525, -2375, 0.9); + spawn.randomGroup(4925, -2850, 1); + if (simulation.difficulty > 1) { + spawn.randomLevelBoss(7275, -2475); + spawn.secondaryBossChance(8400, -1025) + } + powerUps.addResearchToLevel() //needs to run after mobs are spawned + + }, factory() { level.announceMobTypes() // simulation.enableConstructMode() //remove this!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // level.difficultyIncrease(10 * 4) //30 is near max on hard //60 is near max on why level.setPosToSpawn(2235, -1375); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance @@ -4179,7 +4806,7 @@ const level = { spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) - document.body.style.backgroundColor = "#d0d2d4s"; + document.body.style.backgroundColor = "#d0d2d4"; // color.map = "#262a2f" let isPowerLeft = true const movers = [] @@ -4290,6 +4917,23 @@ const level = { if (!buttonLeft.isUp) { setMoverDirection(7) buttonRight.isUp = true //flip the other button up + + //remove any blocks on top of right button + const badBlocks = Matter.Query.region(body, buttonRight) + //figure out block's index + for (let j = 0; j < badBlocks.length; j++) { + let index = null + for (let i = 0; i < body.length; i++) { + if (badBlocks[j] === body[i]) index = i + } + //remove block + console.log(index, j) + if (index) { + Matter.Composite.remove(engine.world, badBlocks[j]); + body.splice(index, 1); + } + } + } } else if (buttonRight.isUp) { buttonRight.query(); @@ -4311,7 +4955,7 @@ const level = { } } - if (button1.isUp) { + if (button1.isUp) { //opens up secondary zone button1.query(); if (!button1.isUp) { isPowerLeft = false @@ -4547,7 +5191,6 @@ const level = { balance1 = level.spinner(x + 200, y - 500, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) { balance2 = level.spinner(x + 200, y - 950, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) balance3 = level.spinner(x + 650, y - 750, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) - // balance4 = level.spinner(x + 750, y - 1050, 25, 350, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) balance4 = level.spinner(x + 1250, y - 1000, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) let isInRoom = false @@ -4997,7 +5640,7 @@ const level = { powerUps.directSpawn(x + 998, y - 333, "tech", false); } const powerUp1 = powerUp[powerUp.length - 1] - powerUp1.holdPosition = { x: powerUp1.position.x, y: powerUp1.position.y } + if (powerUp1) powerUp1.holdPosition = { x: powerUp1.position.x, y: powerUp1.position.y } let isSpawnedMobs = false doCustom.push( () => { @@ -5006,7 +5649,7 @@ const level = { // if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2000 && m.pos.y > y - 1300 && m.pos.y < y) { //is player inside this room? // isInRoom = true // } else - if (powerUp1.velocity.y !== 0) { //don't run this code if power up is gone //hack: powerUp1.velocity.y !== 0 seems to only be true if the power up up doesn't exist and is no longer being affected by gravity + if (powerUp1 && powerUp1.velocity.y !== 0) { //don't run this code if power up is gone //hack: powerUp1.velocity.y !== 0 seems to only be true if the power up doesn't exist and is no longer being affected by gravity ctx.strokeStyle = "#f0f" ctx.lineWidth = 2; if (Vector.magnitudeSquared(Vector.sub(m.pos, powerUp1.position)) < 90000) { //zone radius is 300 @@ -5068,10 +5711,7 @@ const level = { x: powerUp1.holdPosition.x + 4 * Math.random(), //1300 -2 y: powerUp1.holdPosition.y + 4 * Math.random() //335 -2 }); - Matter.Body.setVelocity(powerUp1, { - x: 0, - y: 0 - }); + Matter.Body.setVelocity(powerUp1, { x: 0, y: 0 }); } else if (!isSpawnedMobs) { isSpawnedMobs = true if (chamberY === -650) { //lower chamber @@ -5564,7 +6204,7 @@ const level = { }, pavilion() { level.announceMobTypes() - level.isEndlessFall = true; + level.fallMode = "start"; const vanish = [] level.exit.x = -850; level.exit.y = -1485; @@ -6414,10 +7054,264 @@ const level = { balance5 = level.rotor(2605, 1100, 390, 25, 0.001) //falling } + }, + flocculation() { + level.announceMobTypes() + const button0 = level.button(1125, 795) + const button1 = level.button(6538, 2670) + const button2 = level.button(1225, -100) + button0.isUp = true + button1.isUp = true + button2.isUp = true + // const hazard = level.hazard(4550, 2750, 4550, 150) + const hazard = level.hazard(simulation.isHorizontalFlipped ? -7200 : 675, 50, 7500, 3000) //1869 + // hazard.min.y = 3000 //REMOVE THIS IN LIVE VERSION!!!!! set slime to lowest level + let balance1, balance2, balance3, rotor1, rotor2 + + const drip1 = level.drip(6100, 1900, 2900, 100) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { + const drip2 = level.drip(7300, 1900, 2900, 150) + const drip3 = level.drip(8750, 1900, 2900, 70) + + //up mode triggered by player contact + const elevator0 = level.elevator(700, 1865, 200, 490, 1400, 0.011, { up: 0.01, down: 0.7 }) + const elevator1 = level.elevator(3995, 2335, 210, 150, 1700, 0.011, { up: 0.01, down: 0.7 }) + + level.custom = () => { + drip1.draw(); + drip2.draw(); + drip3.draw(); + if (button0.isUp) { + button0.query(); + if (!button0.isUp) { //summon second set of mobs + //1 boss, 1-2 groups, 11 mobs (all on lower ground level, where the slime is leaving) + spawn.randomMob(918, 2695, 0.1); + spawn.randomMob(1818, 2719, 0.2); + spawn.randomMob(2530, 2460, 0.2); + spawn.randomMob(3109, 2665, 0.3); + spawn.randomMob(3909, 2191, 0.3); + spawn.randomMob(4705, 2711, 0.4); + spawn.randomMob(5800, 2796, 0.5); + spawn.randomMob(7287, 2757, 0.6); + spawn.randomMob(5759, 2691, 0.9); + spawn.randomMob(5675, 2225, 0.8); + spawn.randomMob(7450, 2775, 0.8); + + spawn.randomGroup(6600, 2400, 0.1); + if (simulation.difficulty > 1) spawn.randomLevelBoss(6076, 2341); + } + } + button0.draw(); + if (button1.isUp) button1.query(); + button1.draw(); + if (button2.isUp) button2.query(); + button2.draw(); + ctx.fillStyle = "hsl(175, 15%, 76%)" + ctx.fillRect(7625, 2625, 400, 300) + ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows + ctx.fillRect(6250, 1875, 700, 875) + ctx.fillRect(900, 1200, 600, 1725) //900, 1350, 600, 1600); + ctx.fillRect(3000, 1200, 1000, 1750); + ctx.fillRect(2200, 625, 400, 2050); + + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + elevator0.moveOnTouch() + elevator1.moveOnTouch() + rotor1.rotate(); + rotor2.rotate(); + + ctx.fillStyle = "#233" + ctx.beginPath(); + ctx.arc(balance1.center.x, balance1.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance2.center.x, balance2.center.y) + ctx.arc(balance2.center.x, balance2.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance3.center.x, balance3.center.y) + ctx.arc(balance3.center.x, balance3.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(balance5.center.x, balance5.center.y) + ctx.arc(balance5.center.x, balance5.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(rotor1.center.x, rotor1.center.y) + ctx.arc(rotor1.center.x, rotor1.center.y, 9, 0, 2 * Math.PI); + ctx.moveTo(rotor2.center.x, rotor2.center.y) + ctx.arc(rotor2.center.x, rotor2.center.y, 9, 0, 2 * Math.PI); + ctx.fill(); + hazard.query(); + const drainRate = Math.max(1, 4 - hazard.min.y / 800) + hazard.level( + (button2.isUp || hazard.height < 1150) && + (button0.isUp || hazard.height < 350) && + button1.isUp + , drainRate) //true = hold, false = lower + exitDoor.isClosing = hazard.min.y < 2900 + exitDoor.openClose(); + exitDoor.draw(); + }; + + level.setPosToSpawn(0, -50); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 7800; + level.exit.y = 2865; + const exitDoor = level.door(7637, 2680, 25, 225, 195, 5) + + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "hsl(138, 3%, 74%)"; + color.map = "#3d4240" + powerUps.spawnStartingPowerUps(3475, 1775); + spawn.debris(4575, 2550, 1600, 6); //16 debris per level + spawn.debris(750, 2550, 2250, 6); //16 debris per level + + spawn.mapRect(-500, -600, 200, 800); //left entrance wall + spawn.mapRect(-400, -600, 3550, 200); //ceiling + spawn.mapRect(-400, 0, 1400, 600); + + spawn.mapRect(575, 475, 250, 250); + Matter.Body.setAngle(map[map.length - 1], map[map.length - 1].angle - Math.PI / 4); + spawn.mapRect(4075, 75, 250, 250); + Matter.Body.setAngle(map[map.length - 1], map[map.length - 1].angle - Math.PI / 4); + spawn.mapRect(4259, 1559, 282.5, 282.5); + Matter.Body.setAngle(map[map.length - 1], map[map.length - 1].angle - Math.PI / 4); + + spawn.mapRect(3140, -600, 200, 800); //right down tube wall + spawn.mapRect(3150, 0, 1200, 200); //tube right exit ceiling + spawn.mapVertex(2400, 500, "-200 -100 -100 -200 100 -200 200 -100 200 200 -200 200"); + spawn.mapVertex(2400, 1200, "-200 -200 200 -200 200 100 100 200 -100 200 -200 100"); + spawn.mapVertex(1200, 2150, "-300 -300 300 -300 300 200 200 300 -200 300 -300 200"); + spawn.mapVertex(1200, 1100, "-300 -200 -200 -300 200 -300 300 -200 300 300 -300 300"); + spawn.mapVertex(3500, 950, "-500 -450 -400 -550 400 -550 500 -450 500 450 400 550 -400 550 -500 450"); + spawn.mapVertex(3500, 1990, "-300 -40 -230 -110 230 -110 300 -40 300 40 230 110 -230 110 -300 40"); + // spawn.mapVertex(2400, 1940, "-200 -40 -150 -90 150 -90 200 -40 200 40 150 90 -150 90 -200 40"); + spawn.mapRect(2200, 1850, 400, 200); + spawn.bodyRect(3825, 2240, 150, 75, 0.5); + + spawn.mapVertex(3500, 2452, "-500 -135 500 -135 500 35 400 135 -400 135 -500 35"); + spawn.mapVertex(1200, 2875, "-400 0 -300 -100 300 -100 400 0"); + spawn.mapVertex(1317, 275, "-500 0 -300 -200 300 -200 550 50 550 500 -500 500"); + spawn.mapVertex(1300, -357, "-300 0 -400 -100 400 -100 300 0"); + spawn.bodyRect(1550, -308, 50, 208, 0.5); + spawn.bodyRect(2000, 965, 525, 25, 0.5); + spawn.mapVertex(2400, 2850, "-350 -50 -300 -100 300 -100 350 -50 350 300 -350 300"); + spawn.mapVertex(6600, 1925, "-350 0 -450 -100 450 -100 350 0"); + spawn.mapVertex(6600, 2875, "-400 -50 -350 -100 350 -100 400 -50 400 300 -400 300"); + + spawn.bodyRect(2375, 300, 100, 100, 0.6); + spawn.bodyRect(1025, 1775, 100, 75, 0.6); + spawn.bodyRect(1250, 1825, 50, 25, 0.6); + spawn.bodyRect(3700, 275, 125, 125, 0.6); + spawn.bodyRect(5875, 2725, 200, 200, 0.6); + spawn.bodyRect(6900, 2590, 50, 50, 0.6); + spawn.mapRect(4200, 2325, 250, 625); + spawn.mapRect(-500, 50, 1200, 3050); + spawn.mapRect(-500, 2900, 9600, 775); + + spawn.mapRect(4400, 0, 4700, 1900); + spawn.mapRect(4200, 0, 200, 1700); + // spawn.mapRect(6250, 2675, 700, 325); + // spawn.mapRect(6250, 1875, 700, 150); + + //exit room + spawn.mapRect(8000, 1775, 1100, 1375); + spawn.mapRect(7625, 1825, 450, 825); + spawn.mapRect(7625, 2625, 50, 75); + spawn.mapRect(7625, 2890, 400, 25); + spawn.mapRect(7800, 2880, 100, 25); + + spawn.randomMob(2450, 250, 0.2); + spawn.randomMob(3250, 325, 0.2); + spawn.randomMob(3625, 350, 0.3); + spawn.randomMob(1750, -25, 0.4); + spawn.randomMob(1300, 1750, 0.5); + spawn.randomMob(2350, 1725, 0.6); + spawn.randomMob(3350, 1775, 0.7); + spawn.randomMob(1025, 750, 0.8); + spawn.randomMob(2400, 1775, 0.8); + spawn.randomMob(1250, 1725, 0.8); + spawn.randomMob(775, 1775, 0.9); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + spawn.secondaryBossChance(1822, 1336) + + if (simulation.isHorizontalFlipped) { //flip the map horizontally + level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit + rotor1 = level.rotor(-5600, 2390, 850, 50, 0.001, 0, 0.01, 0, 0.001) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { + rotor2 = level.rotor(-2175, 1900, 650, 50, 0.001, 0, 0.01, 0, 0.0007) + + balance1 = level.rotor(-800 - 25, -395, 25, 390, 0.001) //entrance + balance2 = level.rotor(-2605 - 390, 500, 390, 25, 0.001) //falling + balance3 = level.rotor(-2608 - 584, 1950, 584, 25, 0.001) //falling + balance5 = level.rotor(-2605 - 390, 1020, 390, 25, 0.001) //falling + + button1.min.x = -button1.min.x - 126 + button1.max.x = -button1.max.x + 126 + button0.min.x = -button0.min.x - 126 + button0.max.x = -button0.max.x + 126 + button2.min.x = -button2.min.x - 126 + button2.max.x = -button2.max.x + 126 // flip the button horizontally + drip1.x *= -1 + drip2.x *= -1 + drip3.x *= -1 + elevator0.holdX *= -1 + elevator1.holdX *= -1 + // console.log(hazard) + hazard.min.x -= 840 + hazard.max.x -= 840 + + level.custom = () => { + drip1.draw(); + drip2.draw(); + drip3.draw(); + + if (button0.isUp) { + button0.query(); + if (!button0.isUp) { //summon second set of mobs + //1 boss, 1-2 groups, 11 mobs (all on lower ground level, where the slime is leaving) + spawn.randomMob(-7475, 2800, 0.1); + spawn.randomMob(-6475, 2500, 0.2); + spawn.randomMob(-4575, 2775, 0.3); + spawn.randomMob(-7575, 2850, 0.3); + spawn.randomMob(-6425, 2575, 0.3); + spawn.randomMob(-5750, 2775, 0.4); + spawn.randomMob(-4675, 2800, 0.5); + spawn.randomMob(-3425, 2800, 0.6); + spawn.randomMob(-2475, 2475, 0.7); + spawn.randomMob(-3350, 2250, 0.8); + spawn.randomMob(-1275, 2725, 0.9); + spawn.randomGroup(-6225, 2400, 0.1); + if (simulation.difficulty > 1) spawn.randomLevelBoss(-6250, 2350); + } + } + button0.draw(); + if (button1.isUp) button1.query(); + button1.draw(); + if (button2.isUp) button2.query(); + button2.draw(); + rotor1.rotate(); + rotor2.rotate(); + ctx.fillStyle = "hsl(175, 15%, 76%)" + ctx.fillRect(-8025, 2625, 400, 300) + ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows + ctx.fillRect(-6950, 1875, 700, 875) + ctx.fillRect(-4000, 1400, 1000, 1550); + ctx.fillRect(-2600, 675, 400, 2025); + ctx.fillRect(-1500, 1375, 600, 1500); + + level.exit.drawAndCheck(); + level.enter.draw(); + }; + // level.customTopLayer = () => {}; + } else { + rotor1 = level.rotor(4700, 2390, 850, 50, 0.001, 0, 0.01, 0, -0.001) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { + rotor2 = level.rotor(1525, 1900, 650, 50, 0.001, 0, 0.01, 0, -0.0007) + balance1 = level.rotor(800, -395, 25, 390, 0.001) //entrance + balance2 = level.rotor(2605, 500, 390, 25, 0.001) //falling + balance3 = level.rotor(2608, 1950, 584, 25, 0.001) //falling + balance5 = level.rotor(2605, 1020, 390, 25, 0.001) //falling + } + }, satellite() { level.announceMobTypes() - level.isEndlessFall = true; + level.fallMode = "start"; const boost1 = level.boost(5825, 235, 1400) const elevator = level.elevator(4210, -1265, 380, 50, -3450) //, 0.003, { up: 0.01, down: 0.2 } level.custom = () => { @@ -6594,7 +7488,7 @@ const level = { }, rooftops() { level.announceMobTypes() - level.isEndlessFall = true; + // level.fallPosition = { x: 5000, y:-4000} const elevator = level.elevator(1450, -990, 235, 45, -2000) const boost1 = level.boost(4950, 0, 1100) @@ -6603,7 +7497,6 @@ const level = { boost1.query(); elevator.move(); elevator.drawTrack(); - ctx.fillStyle = "#d4f4f4" if (isBackwards) { ctx.fillRect(-650, -2300, 440, 300) @@ -6611,7 +7504,6 @@ const level = { ctx.fillRect(3460, -700, 1090, 800) } level.exit.drawAndCheck(); - level.enter.draw(); }; @@ -6637,6 +7529,9 @@ const level = { simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#dcdcde"; + + + // level.fallMode = "start"; let isBackwards = false if (Math.random() < 0.75) { //normal direction start in top left @@ -6780,10 +7675,16 @@ const level = { } }; } + level.fallMode = "position"; //must set level.fallModeBounds in this mode to prevent player getting stuck left or right + if (level.enter.x > level.exit.x) { + level.fallModeBounds = { left: level.exit.x, right: level.enter.x } //used with level.fallMode = "position"; + } else { + level.fallModeBounds = { left: level.enter.x, right: level.exit.x } //used with level.fallMode = "position"; + } }, aerie() { level.announceMobTypes() - level.isEndlessFall = true; + level.fallMode = "start"; const boost1 = level.boost(-425, 100, 1400) const boost2 = level.boost(5350, 275, 2850); @@ -6831,7 +7732,6 @@ const level = { ctx.fill() }; - // simulation.difficulty = 4; //for testing to simulate possible mobs spawns level.defaultZoom = 2100 simulation.zoomTransition(level.defaultZoom) @@ -7013,7 +7913,7 @@ const level = { }, skyscrapers() { level.announceMobTypes() - level.isEndlessFall = true; + level.fallMode = "start"; const boost1 = level.boost(475, 0, 1300) const boost2 = level.boost(4450, 0, 1300); level.custom = () => { @@ -7152,11 +8052,8 @@ const level = { }, highrise() { level.announceMobTypes() - level.isEndlessFall = true; - const elevator1 = level.elevator(-790, -190, 180, 25, -1150, 0.0025, { - up: 0.01, - down: 0.2 - }, true) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + level.fallMode = "start"; + const elevator1 = level.elevator(-790, -190, 180, 25, -1150, 0.0025, { up: 0.01, down: 0.2 }, true) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { elevator1.addConstraint(); // const button1 = level.button(-500, -200) const toggle1 = level.toggle(-300, -200) //(x,y,isOn,isLockOn = true/false) @@ -7438,7 +8335,7 @@ const level = { }, warehouse() { level.announceMobTypes() - level.isEndlessFall = true; + level.fallMode = "start"; level.custom = () => { ctx.fillStyle = "#444" //light fixtures ctx.fillRect(-920, -505, 40, 10) @@ -7946,7 +8843,7 @@ const level = { }, stronghold() { // player made level by Francois 👑 from discord - simulation.makeTextLog(`stronghold by Francois`); + simulation.inGameConsole(`stronghold by Francois`); const boost1 = level.boost(1470, -250, 1080) const boost2 = level.boost(-370, 0, 800) @@ -8116,7 +9013,7 @@ const level = { powerUps.addResearchToLevel() //needs to run after mobs are spawned }, basement() { // player made level by Francois 👑 from discord - simulation.makeTextLog(`basement by Francois`); + simulation.inGameConsole(`basement by Francois`); let button, door, buttonDoor, buttonPlateformEnd, doorPlateform let isLevelReversed = Math.random(); if (isLevelReversed < 0.7) { @@ -8400,7 +9297,7 @@ const level = { powerUps.chooseRandomPowerUp(3100, 1630); }, // detours() { //by Francois from discord - // simulation.makeTextLog(`detours by Francois`); + // simulation.inGameConsole(`detours by Francois`); // level.setPosToSpawn(0, 0); //lower start // level.exit.y = 150; // spawn.mapRect(level.enter.x, 45, 100, 20); @@ -8713,7 +9610,7 @@ const level = { // } // }, house() { //by Francois from discord - simulation.makeTextLog(`house by Francois`); + simulation.inGameConsole(`house by Francois`); const rotor = level.rotor(4251, -325, 120, 20, 200, 0, 0.01, 0, -0.0001); const hazard = level.hazard(4350, -1000, 300, 110); const doorBedroom = level.door(1152, -1150, 25, 250, 250); @@ -9189,7 +10086,7 @@ const level = { } }, perplex() { //by Oranger from discord - simulation.makeTextLog(`perplex by Oranger`); + simulation.inGameConsole(`perplex by Oranger`); document.body.style.backgroundColor = "#dcdcde"; level.setPosToSpawn(-600, 400); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); @@ -9375,7 +10272,7 @@ const level = { spawn.secondaryBossChance(7725, 2275) }, coliseum() { - simulation.makeTextLog(`coliseum by iNoobBoi`); + simulation.inGameConsole(`coliseum by iNoobBoi`); level.custom = () => { level.exit.drawAndCheck(); @@ -9526,7 +10423,7 @@ const level = { spawn.secondaryBossChance(6600, 600) }, crossfire() { - simulation.makeTextLog(`crossfire by iNoobBoi`); + simulation.inGameConsole(`crossfire by iNoobBoi`); //*1.5 //Level Setup @@ -9691,9 +10588,9 @@ const level = { spawn.debris(9300, -900, 400, debrisCount); }, vats() { // Made by Dablux#6610 on Discord - simulation.makeTextLog(`vats by Dablux`); - - simulation.zoomScale = 1500; + simulation.inGameConsole(`vats by Dablux`); + level.defaultZoom = 1500 + simulation.zoomTransition(level.defaultZoom) level.setPosToSpawn(4400, -1060) spawn.mapRect(level.enter.x, level.enter.y + 30, 100, 20) level.exit.x = 3900; @@ -10133,7 +11030,7 @@ const level = { } }, ngon() { //make by Oranger - simulation.makeTextLog(`"ngon" by Oranger`); + simulation.inGameConsole(`"ngon" by Oranger`); document.body.style.backgroundColor = "#dcdcde"; let needGravity = []; @@ -10538,7 +11435,7 @@ const level = { } }, tunnel() { // by Scarlettt - simulation.makeTextLog(`tunnel by Scarlettt`); + simulation.inGameConsole(`tunnel by Scarlettt`); level.custom = () => { level.exit.drawAndCheck(); @@ -10855,8 +11752,8 @@ const level = { let isButtonTapped = false; // if (b.inventory.length < 5) powerUps.spawn(3800, -3200, "gun"); - powerUps.spawn(3900, -3100, "heal", true, null, 30); - powerUps.spawn(3900, -3100, "heal", true, null, 30); + powerUps.spawn(3900, -3100, "heal", true, 30); + powerUps.spawn(3900, -3100, "heal", true, 30); // path to the third room spawn.mapRect(2000, -1850, 50, 200); @@ -11012,7 +11909,7 @@ const level = { } }, run() { - simulation.makeTextLog(`run by iNoobBoi`); + simulation.inGameConsole(`run by iNoobBoi`); addPartToMap = (len) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually map[len].collisionFilter.category = cat.map; @@ -11023,16 +11920,6 @@ const level = { anotherBoss = (x, y) => { if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { - tech.isScaleMobsWithDuplication = true - spawn.randomLevelBoss(x, y, ["historyBoss"]); - tech.isScaleMobsWithDuplication = false - } else if (tech.isResearchBoss) { - if (powerUps.research.count > 2) { - powerUps.research.changeRerolls(-3) - simulation.makeTextLog(`m.research -= 3
${powerUps.research.count}`) - } else { - tech.addJunkTechToPool(0.49) - } spawn.randomLevelBoss(x, y, ["historyBoss"]); } } @@ -11071,14 +11958,14 @@ const level = { addPartToMap(map.length - 1); simulation.draw.setPaths(); - simulation.makeTextLog(`UNKNOWN: "Well done. Now climb."`, 600); - simulation.makeTextLog(`UNKNOWN: "I left a gift at the top."`, 600); + simulation.inGameConsole(`UNKNOWN: "Well done. Now climb."`, 600); + simulation.inGameConsole(`UNKNOWN: "I left a gift at the top."`, 600); climbTime = true; } //toggles on a mapRect when player passes a certain area if (m.pos.x > 9000 && endTime === false) { - simulation.makeTextLog("UNKNOWN: \"Good luck. I hope you get out of here.\"", 600); + simulation.inGameConsole("UNKNOWN: \"Good luck. I hope you get out of here.\"", 600); endTime = true; } @@ -11259,11 +12146,11 @@ const level = { //Mob Spawning setTimeout(() => { - simulation.makeTextLog("UNKNOWN: \"You cannot kill them.\"", 600); + simulation.inGameConsole("UNKNOWN: \"You cannot kill them.\"", 600); }, 2000); setTimeout(() => { - simulation.makeTextLog("UNKNOWN: \"But I have slowed them down for you.\"", 600); + simulation.inGameConsole("UNKNOWN: \"But I have slowed them down for you.\"", 600); }, 6000); @@ -11279,7 +12166,7 @@ const level = { spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](6600, -1000); setTimeout(() => { - simulation.makeTextLog("UNKNOWN: \"Run.\"", 600); + simulation.inGameConsole("UNKNOWN: \"Run.\"", 600); }, 10000); } //some of the mobs if (simulation.difficulty > 20) { @@ -11289,7 +12176,7 @@ const level = { spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](7400, -800); setTimeout(() => { - simulation.makeTextLog("UNKNOWN: \"RUN!\"", 600); + simulation.inGameConsole("UNKNOWN: \"RUN!\"", 600); }, 11000); } //most of the mobs if (simulation.difficulty > 30) { @@ -11299,7 +12186,7 @@ const level = { spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](7500, -300); setTimeout(() => { - simulation.makeTextLog("UNKNOWN: \"GET OUT OF HERE.\"", 600); + simulation.inGameConsole("UNKNOWN: \"GET OUT OF HERE.\"", 600); }, 12000); } //all the mobs @@ -11308,7 +12195,7 @@ const level = { spawn.randomLevelBoss(-2200, -700, ["powerUpBossBaby", "blockBoss", "revolutionBoss"]); setTimeout(() => { - simulation.makeTextLog("UNKNOWN: \"They are coming for you.\"", 600); + simulation.inGameConsole("UNKNOWN: \"They are coming for you.\"", 600); }, 14000); } anotherBoss(-1800, -700); //custom second boss spawn @@ -11364,7 +12251,7 @@ const level = { } }, islands() { - simulation.makeTextLog(`islands by Richard0820`); + simulation.inGameConsole(`islands by Richard0820`); const boost1 = level.boost(58500, -18264, 1300); let portal2, portal3; @@ -11707,10 +12594,9 @@ const level = { simulation.fallHeight = -15000; powerUps.addResearchToLevel(); powerUps.spawn(3000, -230, "heal"); - // level.difficultyIncrease(60) }, temple() { - simulation.makeTextLog(`temple by Scar1337`); + simulation.inGameConsole(`temple by Scar1337`); const V = Vector; const Equation = (function () { @@ -11798,12 +12684,6 @@ const level = { return bound.has(player.bounds.min) || bound.has(player.bounds.max); } - function addWIMP(x, y) { - spawn.WIMP(x, y); - const me = mob[mob.length - 1]; - me.isWIMP = true; - } - function relocateWIMPs(x, y) { for (const i of mob) { if (i.isWIMP) { @@ -11836,9 +12716,9 @@ const level = { // applying forces to player doesn't seem to work inside this method, not sure why powerUps.spawn(this.position.x + 20, this.position.y, "ammo"); if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo"); - if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); + if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); }; - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.damageReduction = 0.25 me.do = function () { // keep it slow, to stop issues from explosion knock backs if (this.speed > 1) { @@ -11939,9 +12819,9 @@ const level = { me.onDeath = function () { powerUps.spawn(this.position.x + 20, this.position.y, "ammo"); if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo"); - if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); + if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); }; - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.damageReduction = 0.25 me.do = function () { // keep it slow, to stop issues from explosion knock backs if (this.speed > 2) { @@ -11975,7 +12855,6 @@ const level = { } } }; - function secondRoomObstacle(x, y, isDark = false, size = 70) { mobs.spawn(x, y, isDark ? 3 : 4, size, isDark ? "#0004" : "#fff4"); let me = mob[mob.length - 1]; @@ -11987,7 +12866,7 @@ const level = { me.leaveBody = false; me.timeLeft = 1200; me.isObstacle = true; - me.damageReduction = isDark ? 0.5 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) : 0; + me.damageReduction = isDark ? 0.5 : 0 if (!isDark) { me.isBadTarget = true; me.attackCycle = 0; @@ -12115,7 +12994,7 @@ const level = { this.damageReduction = 0; } }; - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.damageReduction = 0.25 me.rings = [{ colour: "#65f", radius: 300, @@ -12270,7 +13149,7 @@ const level = { if (Math.floor(cycle / spawnDelay) >= spawnCycles - 1) { this.trapCycle = 0; this.isInvulnerable = false; - this.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + this.damageReduction = 0.25 } } } @@ -12342,10 +13221,11 @@ const level = { Promise.resolve().then(() => { // Clear all WIMPS and their research for (let i = 0; i < mob.length; i++) { - while (mob[i] && !mob[i].isMACHO) { - mob[i].replace(i); + if (mob[i] && !mob[i].isDarkMatter) { + mob[i].isWIMP = true; } } + relocateWIMPs(0, -10030); for (let i = 0; i < powerUp.length; i++) { while (powerUp[i] && powerUp[i].name === "research") { Matter.Composite.remove(engine.world, powerUp[i]); @@ -12429,11 +13309,11 @@ const level = { // Trolled const hasCPT = tech.isRewindAvoidDeath; tech.isRewindAvoidDeath = false; - const DRAIN = 0.002 * (tech.isRadioactiveResistance ? 0.25 : 1) + 0.001; + const DRAIN = 0.002 * (tech.isRadioactiveResistance ? 0.2 : 1) + 0.001; if (m.energy > DRAIN && !tech.isEnergyHealth) { m.energy -= DRAIN; } - m.damage(0.00015 * (tech.isRadioactiveResistance ? 0.25 : 1)); + m.damage(0.00015 * (tech.isRadioactiveResistance ? 0.2 : 1)); if (tech.isEnergyHealth) { const previousEnergy = m.energy; m.regenEnergy(); @@ -12561,7 +13441,7 @@ const level = { return this.rings.length; }, get cap() { - return (this.ringNumber + 1) * 90 + 240; + return this.ringNumber * 90 + 180; }, get capped() { return templePlayer.room2.spawnInitiatorCycles > this.cap; @@ -12596,8 +13476,8 @@ const level = { y: -300 }), simulation.cycle - 5); } - if (!this.capped && cycle >= this.cap - 200) { - const multCoeff = (cycle - this.cap + 200) * 0.4 + if (!this.capped && cycle >= this.cap - 180) { + const multCoeff = (cycle - this.cap + 180) * 0.4 ctx.translate((Math.random() - 0.5) * multCoeff, (Math.random() - 0.5) * multCoeff); } ctx.shadowBlur = 20; @@ -12607,12 +13487,12 @@ const level = { DrawTools.arcOut(this.pos.x, this.pos.y, 100, 0, Math.PI * 2); if (templePlayer.room2.cycles <= 100) { for (let i = 0; i < this.ringNumber; i++) { - if (cycle < i * 90 + 90) break; + if (cycle < i * 90) break; const ring = this.rings[i]; ctx.shadowColor = `rgb(${ring.colour.join(",")})`; - const opacity = this.capped ? 1 - 0.01 * templePlayer.room2.cycles : (cycle / 180 - i / 2 - 0.5); + const opacity = this.capped ? 1 - 0.01 * templePlayer.room2.cycles : (cycle / 180 - i / 2); ctx.strokeStyle = `rgba(${ring.colour.join(",")}, ${Math.min(opacity, 1)})`; - const radius = (this.capped ? 1 + 0.07 * templePlayer.room2.cycles : Math.sin(Math.min(cycle - i * 90 - 90, 45) / 90 * Math.PI)) * ring.radius; + const radius = (this.capped ? 1 + 0.07 * templePlayer.room2.cycles : Math.sin(Math.min(cycle - i * 90, 45) / 90 * Math.PI)) * ring.radius; DrawTools.arcOut(this.pos.x, this.pos.y, radius, 0, Math.PI * 2); } } @@ -12760,67 +13640,50 @@ const level = { room0() { if (templePlayer.startAnim <= 0) return; templePlayer.startAnim++; - if (templePlayer.startAnim == 120) { + if (templePlayer.startAnim == 60) { makeLore("Not so fast."); } - if (templePlayer.startAnim < 360) { + if (templePlayer.startAnim < 180) { trapPlayer(1000, templePlayer.initialTrapY); } else { level.exit.x = 4500; level.exit.y = -2030; + relocateWIMPs(level.exit.x, level.exit.y); relocateTo(50, -2050); simulation.fallHeight = -1000; - // simulation.setZoom(1800); - simulation.zoomTransition(1800) + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) templePlayer.startAnim = -1; templePlayer.drawExit = false; } }, room1() { - if (templePlayer.room1ToRoom2Anim <= 0) return; - if (templePlayer.room1ToRoom2Anim === 1) { + const frame = templePlayer.room1ToRoom2Anim; + if (frame <= 0) return; + if (frame === 1) { level.exit.x = -50; level.exit.y = -10030; makeLore("Pathetic."); } - if (templePlayer.room1ToRoom2Anim === 121) { - makeLore("You will never succeed."); - } - if (templePlayer.room1ToRoom2Anim >= 360 && templePlayer.room1ToRoom2Anim <= 720) { - const factor = 200 - 200 * Math.cos((templePlayer.room1ToRoom2Anim / 120 - 3) * Math.PI); + if (frame >= 1 && frame <= 360) { + const factor = 100 - 100 * Math.cos((frame / 90) * Math.PI); ctx.translate(factor, factor); Promise.resolve().then(() => { ctx.save(); ctx.globalCompositeOperation = "color-burn"; - ctx.fillStyle = DrawTools.randomColours; + ctx.fillStyle = DrawTools.randomColours((frame) * (360 - frame) / 32400); DrawTools.updateRandomColours(5); ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore(); }); } - if (templePlayer.room1ToRoom2Anim === 720) { + if (frame === 180) { makeLore("You are trying too hard."); relocateTo(0, -7050); simulation.fallHeight = -6000; templePlayer.stage = 2; - } - if (templePlayer.room1ToRoom2Anim === 960) { - makeLore("I have mastered the understandings of the universe."); - } - if (templePlayer.room1ToRoom2Anim === 1200) { - // Congrats, you discovered the actual words by looking at the source code. Are you happy now? - const x = ( - ["a speck of dust", "an insignificant hindrance", "a tiny obstacle"] - )[Math.floor(Math.random() * 3)].split(""); - for (let i = 0; i < x.length / 1.6; i++) { - const randomIndex = Math.floor(Math.random() * x.length); - if (x[randomIndex] !== " ") { - x[randomIndex] = String.fromCharCode(Math.floor(Math.random() * 50) + 192); - } - }; - makeLore(`You are no more than ${x.join("")} to me.`); - relocateWIMPs(0, -10030); + relocateWIMPs(level.exit.x, level.exit.y - 3000); } templePlayer.room1ToRoom2Anim++; }, @@ -12831,23 +13694,10 @@ const level = { level.exit.y = -13130; makeLore("Do not try me."); } - if (templePlayer.room2ToRoom3Anim === 180) { - makeLore("I have absolute power over you."); - canvas.style.filter = "hue-rotate(90deg)"; - } - if (templePlayer.room2ToRoom3Anim === 360) { - makeLore("You will not succeed..."); - canvas.style.filter = "invert(0.2)"; + if (templePlayer.room2ToRoom3Anim >= 1 && templePlayer.room2ToRoom3Anim <= 180) { + canvas.style.filter = `sepia(${templePlayer.room2ToRoom3Anim / 180}) invert(${templePlayer.room2ToRoom3Anim / 180})`; } - if (templePlayer.room2ToRoom3Anim === 420) { - makeLore("
...
"); - canvas.style.filter = "invert(0.4)"; - } - if (templePlayer.room2ToRoom3Anim > 480 && templePlayer.room2ToRoom3Anim <= 660) { - canvas.style.filter = `sepia(${(templePlayer.room2ToRoom3Anim - 480) / 180}) invert(${0.5 + (templePlayer.room2ToRoom3Anim - 480) / 180})`; - } - if (templePlayer.room2ToRoom3Anim === 780) { - makeLore("Do not interfere with me."); + if (templePlayer.room2ToRoom3Anim === 180) { templePlayer.stage = 3; relocateTo(50, -13150); simulation.fallHeight = -10000; @@ -12859,43 +13709,28 @@ const level = { mob[i].replace(i); } } + templePlayer.drawExit = true; + for (let i = 0; i < 5 * tech.wimpCount; i++) { + powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false); + } + canvas.style.filter = ""; } - if (templePlayer.room2ToRoom3Anim > 780 && templePlayer.room2ToRoom3Anim <= 960) { - canvas.style.filter = `sepia(${(960 - templePlayer.room2ToRoom3Anim) / 180}) invert(${(960 - templePlayer.room2ToRoom3Anim) / 180})`; + if (templePlayer.room2ToRoom3Anim > 180 && templePlayer.room2ToRoom3Anim <= 360) { + canvas.style.filter = `sepia(${(360 - templePlayer.room2ToRoom3Anim) / 180}) invert(${(360 - templePlayer.room2ToRoom3Anim) / 180})`; } templePlayer.room2ToRoom3Anim++; }, room3() { if (templePlayer.room3ToEndAnim <= 0) return; if (templePlayer.room3ToEndAnim === 1) { - makeLore("No."); - } - if (templePlayer.room3ToEndAnim === 120) { - makeLore("This cannot be."); - } - if (templePlayer.room3ToEndAnim === 240) { - makeLore("Has my power failed me?"); - } - if (templePlayer.room3ToEndAnim === 360) { - makeLore("Was it worth it, destroying this place?"); - } - if (templePlayer.room3ToEndAnim === 600) { - makeLore("No one is greater than me."); - } - const text = "noone-"; - for (let i = 0; i < 12; i++) { - if (templePlayer.room3ToEndAnim === 720 + i * 20) { - name = name.slice(0, -1); - simulation.makeTextLog(`${name}:   ${text[i % 6]}`); - canvas.style.filter = `brightness(${1 - i / 22})`; - } - } - if (templePlayer.room3ToEndAnim === 1060) { - templePlayer.drawExit = true; - for (let i = 0; i < 5 * tech.wimpCount; i++) { - powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false); - } - canvas.style.filter = ""; + const x = "Nooooooooooo".split(""); + for (let i = 0; i < x.length / 1.6; i++) { + const randomIndex = Math.floor(Math.random() * x.length); + if (x[randomIndex] !== " ") { + x[randomIndex] = String.fromCharCode(Math.floor(Math.random() * 50) + 192); + } + }; + makeLore(x.join("")); } templePlayer.room3ToEndAnim++; }, @@ -12904,11 +13739,11 @@ const level = { Promise.resolve().then(() => { ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.fillStyle = `rgba(0, 0, 0, ${(simulation.cycle - templePlayer.clearedCycle) / 300})`; + ctx.fillStyle = `rgba(0, 0, 0, ${(simulation.cycle - templePlayer.clearedCycle) / 30})`; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore(); }); - if (simulation.cycle - templePlayer.clearedCycle > 420) level.nextLevel(); + if (simulation.cycle - templePlayer.clearedCycle > 30) level.nextLevel(); } }; const LogicHandler = { @@ -12923,7 +13758,7 @@ const level = { if (!isInBounds) { m.damage(0.1 * simulation.difficultyMode); trapPlayer(level.enter.x, level.enter.y); - simulation.makeTextLog("" + name + ":   You thought I could let you get away with that?"); + simulation.inGameConsole("" + name + ":   You thought I could let you get away with that?"); } }, room0() { @@ -13037,7 +13872,7 @@ const level = { if (simulation.cycle % 4 === 0) { let newMobPositions = []; for (const i of mob) { - if (!(i.isMACHO || i.isWIMP || i.isObstacle)) newMobPositions.push({ + if (!(i.isDarkMatter || i.isWIMP || i.isObstacle)) newMobPositions.push({ x: i.position.x, y: i.position.y }); @@ -13060,7 +13895,7 @@ const level = { const roomConditions = [ isInBound(firstRoomBounds) && templePlayer.room1.cycles < 2400, isInBound(secondRoomBounds) && templePlayer.room2.cycles > 0 && templePlayer.room2.cycles < 2160, - isInBound(thirdRoomBounds) && templePlayer.room2ToRoom3Anim < 1320 + isInBound(thirdRoomBounds) && templePlayer.room2ToRoom3Anim < 540 ]; Promise.resolve(roomConditions).then(roomConditions => { // First Room @@ -13090,7 +13925,7 @@ const level = { ctx.fillStyle = "#0004"; ctx.fillRect(canvas.width2 - 288, 50, 576, 20); ctx.fillStyle = "#000"; - ctx.fillRect(canvas.width2 - 288, 50, 1.6 * (1320 - templePlayer.room2ToRoom3Anim), 20); + ctx.fillRect(canvas.width2 - 288, 50, 1.6 * (540 - templePlayer.room2ToRoom3Anim), 20); ctx.restore(); } }); @@ -13142,7 +13977,7 @@ const level = { } }, room3() { - if (templePlayer.room2ToRoom3Anim === 1320) { + if (templePlayer.room2ToRoom3Anim === 540) { thirdRoomBoss(1800, -13700); for (let i = 0; i < 3; i++) { powerUps.spawn(m.spawnPos.x, m.spawnPos.y, "heal"); @@ -13151,8 +13986,8 @@ const level = { } }; const DrawTools = { - get randomColours() { - return `rgb(${this._randomColours.join(",")})` + randomColours(alpha = 1) { + return `rgba(${this._randomColours.join(",")},${alpha})` }, _randomColours: [Math.random() * 255, Math.random() * 255, Math.random() * 255], updateRandomColours(x = 0.8) { @@ -13307,7 +14142,7 @@ const level = { level.setPosToSpawn(x, y); trapPlayer(x, y); for (let i = 0; i < mob.length; i++) { - if (mob[i].isMACHO) { + if (mob[i].isDarkMatter) { setPos(mob[i], { x, y @@ -13329,7 +14164,7 @@ const level = { setPos(a, b); freeze(a); }; - const makeLore = (x, t) => simulation.makeTextLog(`

${name}:

 

${x}

`, t); + const makeLore = (x, t) => simulation.inGameConsole(`

${name}:

 

${x}

`, t); level.custom = () => { // All the logic gets handled here. How nice! for (const i in LogicHandler) { @@ -13358,7 +14193,7 @@ const level = { }; }, dripp() { - simulation.makeTextLog(`dripp by M. B.`); + simulation.inGameConsole(`dripp by M. B.`); const door = level.door(780, -350, 15, 400, 265); const buttonDoor = level.button(420, -10); @@ -13553,7 +14388,7 @@ const level = { }, biohazard() { // MAP BY INOOBBOI AND THESHWARMA - simulation.makeTextLog(`biohazard by INOOBBOI and THESHWARMA`); + simulation.inGameConsole(`biohazard by INOOBBOI and THESHWARMA`); // set here for the cutscene later level.setPosToSpawn(-2800, -150) @@ -14016,18 +14851,6 @@ const level = { const anotherBoss = (x, y) => { if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { - tech.isScaleMobsWithDuplication = true - spawn.historyBoss(x, y) - tech.isScaleMobsWithDuplication = false - } else if (tech.isResearchBoss) { - if (powerUps.research.count > 2) { - powerUps.research.changeRerolls(-3) - simulation.makeTextLog( - `m.research -= 3
${powerUps.research.count}` - ) - } else { - tech.addJunkTechToPool(0.49) - } spawn.historyBoss(x, y) } } @@ -14400,7 +15223,7 @@ const level = { me.deadOrbs = [] me.energy = 1 // this boss has no orbitals, because it's not meant to ever attack on its own - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.25 // has a shield and sustains that shield spawn.shield(me, x, y, Infinity) me.fireFreq = 30 @@ -14712,7 +15535,7 @@ const level = { anotherBoss(0, 0) //will only spawn historyBoss if there is an additional boss }, stereoMadness() { - simulation.makeTextLog(`stereoMadness by Richard0820`); + simulation.inGameConsole(`stereoMadness by Richard0820`); let totalCoin = 0; const hunter = function (x, y, radius = 30) { //doesn't stop chasing until past 105000 mobs.spawn(x, y, 6, radius, "black"); @@ -14726,7 +15549,7 @@ const level = { me.memory = Infinity; me.seeAtDistance2 = Infinity; Matter.Body.setDensity(me, 1) - simulation.makeTextLog(`Ω: Intruder Detected`); + simulation.inGameConsole(`Ω: Intruder Detected`); me.boost = 10; me.do = function () { if (me.boost == 1 && m.fieldMode == 3 || m.fieldMode == 9 && me.boost == 1) { @@ -14763,7 +15586,7 @@ const level = { me.onDeath = function () { totalCoin++; }; - me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.35 me.do = function () { ctx.save() ctx.translate(this.position.x, this.position.y) @@ -14840,9 +15663,6 @@ const level = { me.collisionFilter.mask = cat.player | cat.mob | cat.bullet; }, 1000); } - me.onDeath = function () { - tech.addJunkTechToPool(0.1) - } Composite.add(engine.world, me.constraint); } @@ -14912,7 +15732,7 @@ const level = { innerBar.style.backgroundColor = m.eyeFillColor; } if (m.pos.x > 25360 && textlogOne == 0) { - simulation.makeTextLog(`
A stong force pushes you forward...
`) + simulation.inGameConsole(`
A stong force pushes you forward...
`) textlogOne++; } if (m.pos.x < -3000) { @@ -14922,12 +15742,12 @@ const level = { }); if (textlogTwo == 0) - simulation.makeTextLog(`
A strong force pushes you away...
`); + simulation.inGameConsole(`
A strong force pushes you away...
`); textlogTwo++; } if (m.pos.y > 1055) { Matter.Body.setPosition(player, { x: 0, y: -150 }); - simulation.makeTextLog(`
There is nowhere to run...
`); + simulation.inGameConsole(`
There is nowhere to run...
`); m.damage(0.1 * simulation.difficultyMode); } if (m.alive == false && barThere == true) { @@ -15529,7 +16349,7 @@ const level = { } document.body.style.transitionDuration = "0ms"; document.body.style.backgroundColor = "#696969"; - simulation.makeTextLog(`
You have earned: ` + Math.min(3, totalCoin) + ` tech
`) + simulation.inGameConsole(`
You have earned: ` + Math.min(3, totalCoin) + ` tech
`) if (barThere == true) { //only runs once document.body.removeChild(bar); for (let i = 0, len = Math.min(3, totalCoin); i < len; i++) powerUps.directSpawn(player.position.x, player.position.y, "tech"); @@ -16295,7 +17115,7 @@ const level = { hunter(0, -1000) }, yingYang() { - simulation.makeTextLog(`yingYang by Richard0820`); + simulation.inGameConsole(`yingYang by Richard0820`); let destroyed = false; const lock = level.door(425, -1400, 50, 300, 300); @@ -16647,7 +17467,7 @@ const level = { powerUps.addResearchToLevel() }, staircase() { - simulation.makeTextLog(`staircase by ryanbear`); + simulation.inGameConsole(`staircase by ryanbear`); level.custom = () => { level.exit.drawAndCheck(); @@ -16735,7 +17555,7 @@ const level = { powerUps.addResearchToLevel() //needs to run after mobs are spawned }, fortress() { - simulation.makeTextLog(`fortress by Desboot`); + simulation.inGameConsole(`fortress by Desboot`); const boost1 = level.boost(3600, -250, 1000) const boost2 = level.boost(60, -604, 1000) const boost3 = level.boost(2160, -1260, 1000) @@ -16876,7 +17696,7 @@ const level = { powerUps.addResearchToLevel() //needs to run after mobs are spawned }, commandeer() { - simulation.makeTextLog(`commandeer by Desboot`); + simulation.inGameConsole(`commandeer by Desboot`); let waterFallWidth = 400 let waterFallX = 15900 @@ -17479,7 +18299,7 @@ const level = { powerUps.addResearchToLevel() //needs to run after mobs are spawned }, clock() { - simulation.makeTextLog(`clock by Cornbread 2100`); + simulation.inGameConsole(`clock by Cornbread 2100`); function drawBackgroundGear(x, y, r1, r2, rot, color, speed, numTeeth = 5, toothWidth = 75, linew = 2) { var vertices = getGearVertices(x, y, r1, r2, numTeeth, simulation.cycle * speed + rot, toothWidth / 100); @@ -19217,7 +20037,7 @@ const level = { } }, buttonbutton() { - simulation.makeTextLog(`buttonbutton by ||Destabilized E||`); + simulation.inGameConsole(`buttonbutton by ||Destabilized E||`); const mover = level.mover(1425, -1949, 600, 25); //x,y,width.height,VxGoal,force let portal @@ -19324,7 +20144,7 @@ const level = { spawn.randomLevelBoss(1840, 675) }, movers() { - simulation.makeTextLog(`movers by ryanbear`); + simulation.inGameConsole(`movers by ryanbear`); level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); @@ -19477,7 +20297,7 @@ const level = { powerUps.addResearchToLevel() //needs to run after mobs are spawned }, downpour() { - simulation.makeTextLog(`Downpour by DesBoot`); + simulation.inGameConsole(`Downpour by DesBoot`); let mobsspawned = 0 const laser = level.hazard(7492, -2612, 10, 500, 0.3) //laserintro @@ -19512,7 +20332,7 @@ const level = { // color.map = "#444" //custom map color - //simulation.makeTextLog(stopcycle) + //simulation.inGameConsole(stopcycle) level.custom = () => { do { @@ -19539,17 +20359,17 @@ const level = { // } // if (rainCount > 12) { // rainCount = 1 - // simulation.makeTextLog(rainCount) + // simulation.inGameConsole(rainCount) // } else { // rainCount = rainCount + 1 - // simulation.makeTextLog(rainCount) + // simulation.inGameConsole(rainCount) // } } while (Math.random() < 0.8); - //simulation.makeTextLog(stopcycle) - //simulation.makeTextLog(m.cycle) + //simulation.inGameConsole(stopcycle) + //simulation.inGameConsole(m.cycle) // ctx.fillStyle = "rgba(228,255,0,0.8)" - // //simulation.makeTextLog(stopcycle) + // //simulation.inGameConsole(stopcycle) // ctx.fillRect(50.4, -1210.0, 100, 100) // stopcycle = m.cycle + Math.random * 600; //stopcycle = m.cycles + Math.random * 600 @@ -21081,7 +21901,7 @@ const level = { if (m.alive) { // tech.damage *= 2; let text = ""; - if (!tech.isSuperDeterminism) { text += `
${tech.isCancelTech ? "?" : "✕"}
`; }; + if (!tech.isSuperDeterminism) { text += `
${(tech.isCancelTech && tech.cancelTechCount === 0) ? "?" : "✕"}
`; }; text += `

Blessing Of Sal

`; text += `
  Speed Boost
Increase speed by 5%
`; text += `
  Defense Boost
Reduce damage by 5%
`; @@ -21095,8 +21915,8 @@ const level = { }, choose(index) { if (index == 1) { - tech.squirrelFx += 0.25; - tech.squirrelJump += 0.1; + m.squirrelFx += 0.25; + m.squirrelJump += 0.1; m.setMovement(); powerUps.endDraft("buff"); } else if (index == 2) { @@ -22344,7 +23164,7 @@ const level = { Object.assign(spawn, obj); //ez }, superNgonBros() { - simulation.makeTextLog(`Super N-gon Bros by DesBoot`); + simulation.inGameConsole(`Super N-gon Bros by DesBoot`); let bowserKilled = 0 let flagY = -750 @@ -22468,19 +23288,19 @@ const level = { me.onDeath = function () { if (Math.random() < 0.1) { spawn.randomSmallMob(me.position.x, me.position.y - 75); - simulation.makeTextLog('mob') + simulation.inGameConsole('mob') } else { if (Math.random() < 0.07) { powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "tech", true); - simulation.makeTextLog('tech') + simulation.inGameConsole('tech') } else { if (Math.random() < 0.4) { powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "heal", true); - simulation.makeTextLog('heal') + simulation.inGameConsole('heal') } else { //if (Math.random() < 0.8){ powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "ammo", true); - simulation.makeTextLog('ammo') + simulation.inGameConsole('ammo') //} } } @@ -22528,7 +23348,7 @@ const level = { firstElevatorY -= 5 } - //simulation.makeTextLog(firstElevatorY) + //simulation.inGameConsole(firstElevatorY) elevator1.move(); elevator2.move(); if (player.position.x > 0 && player.position.y < -9000 && player.position.y > -10000) { @@ -22544,7 +23364,7 @@ const level = { portal[3].query() portal2[2].query() portal2[3].query() - //simulation.makeTextLog(firstBlockBroken) + //simulation.inGameConsole(firstBlockBroken) level.exit.drawAndCheck(); if (player.position.x > 4100 && secondMobsReached == 0) { secondMobsSpawned = 1 @@ -22587,7 +23407,7 @@ const level = { level.enter.draw(); if (finalRoomReached == 0 && player.position.x > 21150) { finalRoomReached = 1 - simulation.makeTextLog('Thank you M, but our techs are in another castle!') + simulation.inGameConsole('Thank you M, but our techs are in another castle!') } //mobs if (firstMobsSpawned == 1 && firstMobsReached == 0) { @@ -22863,13 +23683,14 @@ const level = { powerUps.addResearchToLevel() //needs to run after mobs are spawned }, underpass() { - simulation.makeTextLog(`underpass by Richard0820`); + simulation.inGameConsole(`underpass by Richard0820`); let key = false; const door = level.door(2650, -825, 50, 250, 250, 10); const elevator = level.elevator(-11050, -650, 450, 75, -2975, 0.003, { up: 0.1, down: 0.1 }) const slimePit = level.hazard(-4775, -350, 1975, 175); - const boost = level.boost(137.5, -600, 75, 25); + const boost = level.boost(137.5, -600, 75); + let base = Matter.Bodies.rectangle(-4375, -1000, 100, 100, { density: 0.05, isNotHoldable: true, @@ -24126,64 +24947,6 @@ const level = { ctx.setLineDash([]); } }; - // if(powerUps.pass == undefined) { - // let pass = {pass:true, activated:false}; - // Object.assign(powerUps, pass) - // } - // const loadOut = { - // loadOut: { - // name: "loadOut", - // color: "#000000", //"hsl(248,100%,65%)", - // size() { return 40 }, - // effect() { - // if(m.alive) { - // // tech.damage *= 2; - // let text = ""; - // if (!tech.isSuperDeterminism) { text += `
${tech.isCancelTech ? "?":"✕"}
`; }; - // text += `

Blessing Of Sal

`; - // text += `
  Speed Boost
Increase speed by 5%
`; - // text += `
  Defense Boost
Reduce damage by 5%
`; - // text += `
  Damage Boost
Increase damage by 10%
`; - // if(powerUps.pass == true) { - // text += `
  Blade of Sal
Press Shift to summon the Mythical Las Slayer
Drains Energy
`; - // } - // document.getElementById("choose-grid").innerHTML = text; - // powerUps.showDraft();//no known bugs ig idk, im keep this as it is - // } - // }, - // choose(index) { - // if(index == 1) { - // tech.squirrelFx += 0.25; - // tech.squirrelJump += 0.1; - // m.setMovement(); - // powerUps.endDraft("buff"); - // } else if(index == 2) { - // simulation.dmgScale *= 0.95; - // powerUps.endDraft("buff"); - // } else if(index == 3) { - // m.dmgScale *= 1.1; - // powerUps.endDraft("buff"); - // } else if(index == 4) { //sword! - // powerUps.pass = false; - // addEventListener("keydown", function(event) { - // if(event.key == "Shift" && powerUps.activated == false) { - // sword() - // powerUps.activated = true; - // } else if(event.key == "Shift" && powerUps.activated == true) { - // for(let i = 0; i < mob.length; i++) { - // if(mob[i].isSword) { - // mob[i].death() - // } - // powerUps.activated = false; - // } - // } - // }) - // powerUps.endDraft("buff"); - // } - // } - // } - // } - // Object.assign(powerUps, loadOut) const restoreBoss = function (x, y, radius = 30) { //ATTENTION LANDGREEN: RESTOREBOSS WILL NOT DROP ANY TECH, NOR WILL THERE BE ANY IN THE MAP. DO NOT ADD ANY TECH TO MY MAP mobs.spawn(x, y, 8, radius, 'transparent'); let me = mob[mob.length - 1]; @@ -25801,7 +26564,7 @@ const level = { }, cantilever() { // made by Eclipse#7932 on discord, (TheSpudguy)(@PurpleSunsetGames on github) // simulation.enableConstructMode(); - simulation.makeTextLog(`underpass by Eclipse#7932`); + simulation.inGameConsole(`underpass by Eclipse#7932`); level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 5500; @@ -25864,7 +26627,7 @@ const level = { powerUps.addResearchToLevel(); //needs to run after mobs are spawned }, tlinat() { // _Destined_ formerly Richard0820#2652 - simulation.makeTextLog(`tlinat by Richard0820`); + simulation.inGameConsole(`tlinat by Richard0820`); simulation.fallHeight = 1 / 0, level.setPosToSpawn(0, -1e3), level.exit.x = 5100, level.exit.y = 3770, spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20), spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20), level.defaultZoom = 3000, simulation.zoomTransition(level.defaultZoom), document.body.style.backgroundColor = "#d8dadf"; let e = 0, t = 0; @@ -26004,7 +26767,7 @@ const level = { n(o[l], e + 250 * l - Math.abs(1.5 * e), t) } } - simulation.makeTextLog(`
Look up
Walk right to tp to maze
Exit is at the bottom left`), Matter.Body.scale(player.parts[3], 2, 2), level.custom = () => { + simulation.inGameConsole(`
Look up
Walk right to tp to maze
Exit is at the bottom left`), Matter.Body.scale(player.parts[3], 2, 2), level.custom = () => { if (level.exit.drawAndCheck(), level.enter.draw(), player.position.y > 1e5 && Matter.Body.setPosition(player, { x: 5100, y: -5925 @@ -26014,7 +26777,7 @@ const level = { y: -5925 }), e++; for (let e = 0; e < map.length; e++) Math.random() < .75 && ghoster(map[e].position.x, map[e].position.y); - simulation.makeTextLog("Watch out for ghosters
Peace ✌️") + simulation.inGameConsole("Watch out for ghosters
Peace ✌️") } 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 - 0 && player.velocity.y < .15 && 0 == t && (t++, Matter.Body.scale(player.parts[3], .5, .5)) }, level.customTopLayer = () => { @@ -26087,7 +26850,7 @@ const level = { }, ruins() { // by SiddhUPe // simulation.enableConstructMode() - simulation.makeTextLog(`ruins by SiddhUPe`); + simulation.inGameConsole(`ruins by SiddhUPe`); level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 19531; @@ -27167,14 +27930,13 @@ const level = { spawn.mapRect(-100, 0, 1000, 100); powerUps.addResearchToLevel() //needs to run after mobs are spawned }, - ace() { //join us at discord.gg/Q8gY4WeUcm - simulation.makeTextLog(`ace by Richard0820`); + ace() { + simulation.inGameConsole(`ace by Richard0820`); let isDestroyed = false; const ace = { spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { if (Math.random() < chance) { - // simulation.difficulty = 50 - const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 + const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) const offSet = 6.28 * Math.random() for (let i = 0; i < len; i++) ace.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed) @@ -27224,16 +27986,15 @@ const level = { } }; }, - shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) { + shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance()) { if (this.allowShields && Math.random() < chance) { mobs.spawn(x, y, 9, target.radius + 30, "rgba(255,255,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(0,0,0)"; Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.shield = true; - me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.05 me.isUnblockable = true - me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; consBB[consBB.length] = Constraint.create({ @@ -27255,6 +28016,12 @@ const level = { me.shieldTargetID = target.id target.isShielded = true; + if (target.shieldCount > 0) { + target.shieldCount++ + } else { + target.shieldCount = 1 + } + me.shieldCount = target.shieldCount //used with "bubble fusion" target.shieldID = me.id me.onDeath = function () { //clear isShielded status from target @@ -27281,7 +28048,7 @@ const level = { Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.frictionAir = 0; me.shield = true; - me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.075 me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; for (let i = 0; i < nodes; ++i) { @@ -27706,7 +28473,7 @@ const level = { me.isBoss = true; me.isSlashBoss = true; me.showHealthBar = false; - me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.1 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.frictionAir = 0.02 @@ -28437,12 +29204,11 @@ const level = { } }, crimsonTowers() { - simulation.makeTextLog(`crimsonTowers by Richard0820. Thank you desboot for the video: Source`) + simulation.inGameConsole(`crimsonTowers by Richard0820. Thank you desboot for the video: Source`) const ace = { spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { if (Math.random() < chance) { - // simulation.difficulty = 50 - const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 + const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) const offSet = 6.28 * Math.random() for (let i = 0; i < len; i++) ace.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed) @@ -28492,16 +29258,15 @@ const level = { } }; }, - shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) { + shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance()) { if (this.allowShields && Math.random() < chance) { mobs.spawn(x, y, 9, target.radius + 30, "rgba(255,255,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(0,0,0)"; Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.shield = true; - me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.05 me.isUnblockable = true - me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; consBB[consBB.length] = Constraint.create({ @@ -28523,6 +29288,12 @@ const level = { me.shieldTargetID = target.id target.isShielded = true; + if (target.shieldCount > 0) { + target.shieldCount++ + } else { + target.shieldCount = 1 + } + me.shieldCount = target.shieldCount //used with "bubble fusion" target.shieldID = me.id me.onDeath = function () { //clear isShielded status from target @@ -28549,7 +29320,7 @@ const level = { Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.frictionAir = 0; me.shield = true; - me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.075 me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; for (let i = 0; i < nodes; ++i) { @@ -28974,7 +29745,7 @@ const level = { me.isBoss = true; me.isSlashBoss = true; me.showHealthBar = false; - me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.1 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.frictionAir = 0.02 @@ -29568,8 +30339,8 @@ const level = { level.exit.x = map[272].position.x; }, LaunchSite() { - simulation.makeTextLog(`Launch Site by Des Boot`); - simulation.makeTextLog(`The rain stopped...`); + simulation.inGameConsole(`Launch Site by Des Boot`); + simulation.inGameConsole(`The rain stopped...`); level.setPosToSpawn(0, -50); //normal spawn const elevatortoggle = level.toggle(13650, 3000) let newMobsSpawned = false; @@ -29984,7 +30755,7 @@ const level = { powerUps.addResearchToLevel() //needs to run after mobs are spawned }, shipwreck() { - simulation.makeTextLog(`shipwreck by 3xionDev`); + simulation.inGameConsole(`shipwreck by 3xionDev`); level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 1500; level.exit.y = -1875; @@ -30295,9 +31066,10 @@ const level = { level.enter.draw(); }; + level.customTopLayer = () => { }; }, unchartedCave() { - simulation.makeTextLog(`unchartedCave by 3xionDev`); + simulation.inGameConsole(`unchartedCave by 3xionDev`); level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 20985; level.exit.y = 2816; @@ -30705,9 +31477,10 @@ const level = { level.enter.draw(); }; + level.customTopLayer = () => { }; }, dojo() { // By weird_pusheen - simulation.makeTextLog(`dojo by werid_pusheen, fixed by Cornbread 2100`) + simulation.inGameConsole(`dojo by werid_pusheen, fixed by Cornbread 2100`) const vanishes = []; const smoofes = []; const leftRotor = level.rotor(-550, 900, 950, 25); @@ -31001,7 +31774,7 @@ const level = { */ var boss = mob[mob.length - 1]; boss.isBoss = true; - boss.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + boss.damageReduction = 0.2 boss.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y); level.exit.x = 2560; @@ -31157,7 +31930,687 @@ const level = { powerUps.addResearchToLevel() //needs to run after mobs are spawned }, arena() { - simulation.makeTextLog(`arena by Richard0820`) + simulation.inGameConsole(`arena by Whyisthisnotavalable`) + let genisis, genisisJumpSensor, genisisBody, genisisHead, genisisHeadSensor, genisisBodySensor; + let control = { left: false, right: false, up: false, down: false }; + const g = { + spawn() { + //load genisis in matter.js physic engine + // let vector = Vertices.fromPath("0 40 50 40 50 115 0 115 30 130 20 130"); //genisis as a series of vertices + let vertices = Vertices.fromPath("0,40, 50,40, 50,115, 30,130, 20,130, 0,115, 0,40"); //genisis as a series of vertices + genisisBody = Bodies.fromVertices(0, 0, vertices); + genisisJumpSensor = Bodies.rectangle(0, 46, 36, 6, { + //this sensor check if the genisis is on the ground to enable jumping + sleepThreshold: 99999999999, + isSensor: true + }); + vertices = Vertices.fromPath("16 -82 2 -66 2 -37 43 -37 43 -66 30 -82"); + genisisHead = Bodies.fromVertices(0, -55, vertices); //this part of the genisis lowers on crouch + genisisHeadSensor = Bodies.rectangle(0, -57, 48, 45, { + //senses if the genisis's head is empty and can return after crouching + sleepThreshold: 99999999999, + isSensor: true + }); + genisisBodySensor = Bodies.rectangle(0, 0, 70, 45, { + sleepThreshold: 99999999999, + isSensor: true + }); + genisis = Body.create({ + //combine genisisJumpSensor and genisisBody + parts: [genisisBody, genisisHead, genisisJumpSensor, genisisHeadSensor, genisisBodySensor], + inertia: Infinity, //prevents genisis rotation + friction: 0.002, + frictionAir: 0.001, + //frictionStatic: 0.5, + restitution: 0, + sleepThreshold: Infinity, + collisionFilter: { + group: 0, + category: cat.mob, + mask: cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield | cat.player | cat.bullet + }, + // death() { + // g.death(); + // } + }); + Matter.Body.setMass(genisis, g.mass); + Composite.add(engine.world, [genisis]); + }, + health: 1000, + maxHealth: 1000, //set in simulation.reset() + cycle: 600, //starts at 600 cycles instead of 0 to prevent bugs with g.history + lastKillCycle: 0, + lastHarmCycle: 0, + width: 50, + radius: 30, + eyeFillColor: null, + fillColor: null, //set by setFillColors + fillColorDark: null, //set by setFillColors + bodyGradient: null, //set by setFillColors + color: { + hue: 0, + sat: 0, + light: 50, + }, + setFillColors() { + g.fillColor = `hsl(${g.color.hue},${g.color.sat}%,${g.color.light}%)` + g.fillColorDark = `hsl(${g.color.hue},${g.color.sat}%,${g.color.light - 25}%)` + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, g.fillColorDark); + grd.addColorStop(1, g.fillColor); + g.bodyGradient = grd + }, + setFillColorsAlpha(alpha = 0.5) { + g.fillColor = `hsla(${g.color.hue},${g.color.sat}%,${g.color.light}%,${alpha})` + g.fillColorDark = `hsla(${g.color.hue},${g.color.sat}%,${g.color.light - 25}%,${alpha})` + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, g.fillColorDark); + grd.addColorStop(1, g.fillColor); + g.bodyGradient = grd + }, + height: 42, + yOffWhen: { + crouch: 22, + stand: 49, + jump: 70 + }, + defaultMass: 5, + mass: 5, + FxNotHolding: 0.015, + Fx: 0.016, //run Force on ground // + jumpForce: 0.64, + setMovement() { + // g.Fx = 0.08 / mass * tech.squirrelFx + // g.FxAir = 0.4 / mass / mass + g.Fx = tech.baseFx * g.fieldFx * tech.squirrelFx * (tech.isFastTime ? 1.5 : 1) / genisis.mass //base genisis mass is 5 + g.jumpForce = tech.baseJumpForce * g.fieldJump * tech.squirrelJump * (tech.isFastTime ? 1.13 : 1) / genisis.mass / genisis.mass //base genisis mass is 5 + }, + FxAir: 0.032, // 0.4/5/5 run Force in Air + yOff: 70, + yOffGoal: 70, + onGround: false, //checks if on ground or in air + lastOnGroundCycle: 0, //use to calculate coyote time + standingOn: undefined, + numTouching: 0, + crouch: false, + // isHeadClear: true, + spawnPos: { + x: 0, + y: 0 + }, + spawnVel: { + x: 0, + y: 0 + }, + pos: { + x: 0, + y: 0 + }, + yPosDifference: 24.2859, //genisis.position.y - g.pos.y //24.285923217549026 + // yPosDifferenceCrouched: -2.7140767824453604, + Sy: 0, //adds a smoothing effect to vertical only + Vx: 0, + Vy: 0, + friction: { + ground: 0.01, + air: 0.0025 + }, + airSpeedLimit: 125, // 125/mass/mass = 5 + angle: 0, + walk_cycle: 0, + stepSize: 0, + flipLegs: -1, + hip: { + x: 12, + y: 24 + }, + knee: { + x: 0, + y: 0, + x2: 0, + y2: 0 + }, + foot: { + x: 0, + y: 0 + }, + legLength1: 55, + legLength2: 45, + transX: 0, + transY: 0, + history: new Array(600), //[], //tracks the last second of genisis position + rewindCount: 0, //used with CPT + resetHistory() { + const set = { + position: { + x: genisis.position.x, + y: genisis.position.y, + }, + velocity: { + x: genisis.velocity.x, + y: genisis.velocity.y + }, + yOff: g.yOff, + angle: g.angle, + health: g.health, + energy: g.energy, + activeGun: b.activeGun + } + for (let i = 0; i < 600; i++) { //reset history + g.history[i] = set + } + }, + move() { + g.pos.x = genisis.position.x; + g.pos.y = genisisBody.position.y - g.yOff; + g.Vx = genisis.velocity.x; + g.Vy = genisis.velocity.y; + + //tracks the last 10s of genisis information + g.history.splice(g.cycle % 600, 1, { + position: { + x: genisis.position.x, + y: genisis.position.y, + }, + velocity: { + x: genisis.velocity.x, + y: genisis.velocity.y + }, + yOff: g.yOff, + angle: g.angle, + health: g.health, + energy: g.energy, + activeGun: b.activeGun + }); + // const back = 59 // 59 looks at 1 second ago //29 looks at 1/2 a second ago + // historyIndex = (g.cycle - back) % 600 + }, + targetX: 0, + targetY: 0, + transSmoothX: 0, + transSmoothY: 0, + lastGroundedPositionY: 0, + // mouseZoom: 0, + lookSmoothing: 0.07, //1 is instant jerky, 0.001 is slow smooth zoom, 0.07 is standard + look() { }, //set to lookDefault() + lookDefault() { + g.angle = Math.atan2( + g.targetY - g.pos.y, + g.targetX - g.pos.x + ); + // //smoothed mouse look translations + const scale = 0.8; + g.transSmoothX = canvas.width2 - g.pos.x - (simulation.mouse.x - canvas.width2) * scale; + g.transSmoothY = canvas.height2 - g.pos.y - (simulation.mouse.y - canvas.height2) * scale; + + g.transX += (g.transSmoothX - g.transX) * g.lookSmoothing; + g.transY += (g.transSmoothY - g.transY) * g.lookSmoothing; + }, + doCrouch() { + if (!g.crouch) { + g.crouch = true; + g.yOffGoal = g.yOffWhen.crouch; + if ((genisisHead.position.y - genisis.position.y) < 0) { + Matter.Body.setPosition(genisisHead, { + x: genisis.position.x, + y: genisis.position.y + 9.1740767 + }) + } + } + }, + undoCrouch() { + if (g.crouch) { + g.crouch = false; + g.yOffGoal = g.yOffWhen.stand; + if ((genisisHead.position.y - genisis.position.y) > 0) { + Matter.Body.setPosition(genisisHead, { + x: genisis.position.x, + y: genisis.position.y - 30.28592321 + }) + } + } + }, + hardLandCD: 0, + checkHeadClear() { + if (Matter.Query.collides(headSensor, map).length > 0) { + return false + } else { + return true + } + }, + buttonCD_jump: 0, //cool down for genisis buttons + jump() { + // if (!g.onGround) g.lastOnGroundCycle = 0 //g.cycle - tech.coyoteTime + g.buttonCD_jump = g.cycle + 20; //can't jump again until 20 cycles pass + //apply a fraction of the jump force to the body the genisis is jumping off of + Matter.Body.applyForce(g.standingOn, g.pos, { + x: 0, + y: g.jumpForce * 0.12 * Math.min(g.standingOn.mass, 5) + }); + + genisis.force.y = -g.jumpForce; //genisis jump force + Matter.Body.setVelocity(genisis, { //zero genisis y-velocity for consistent jumps + x: genisis.velocity.x, + y: Math.max(-10, Math.min(g.standingOn.velocity.y, 10)) //cap velocity contribution from blocks you are standing on to 10 in the vertical + }); + }, + groundControl() { + //check for crouch or jump + if (g.crouch) { + if (!(control.down) && g.checkHeadClear() && g.hardLandCD < g.cycle) g.undoCrouch(); + } else if (control.down || g.hardLandCD > g.cycle) { + g.doCrouch(); //on ground && not crouched and pressing s or down + } else if (control.up && g.buttonCD_jump + 20 < g.cycle && g.yOffWhen.stand > 23) { + g.jump() + } + + if (control.left) { + if (genisis.velocity.x > -2) { + genisis.force.x -= g.Fx * 1.5 + } else { + genisis.force.x -= g.Fx + } + // } + } else if (control.right) { + if (genisis.velocity.x < 2) { + genisis.force.x += g.Fx * 1.5 + } else { + genisis.force.x += g.Fx + } + } else { + const stoppingFriction = 0.92; //come to a stop if no move key is pressed + Matter.Body.setVelocity(genisis, { + x: genisis.velocity.x * stoppingFriction, + y: genisis.velocity.y * stoppingFriction + }); + } + //come to a stop if fast + if (genisis.speed > 4) { + const stoppingFriction = (g.crouch) ? 0.65 : 0.89; // this controls speed when crouched + Matter.Body.setVelocity(genisis, { + x: genisis.velocity.x * stoppingFriction, + y: genisis.velocity.y * stoppingFriction + }); + } + }, + airControl() { + //check for coyote time jump + // if (control.up && g.buttonCD_jump + 20 + tech.coyoteTime < g.cycle && g.yOffWhen.stand > 23 && g.lastOnGroundCycle + tech.coyoteTime > g.cycle) g.jump() + if (control.up && g.buttonCD_jump + 20 < g.cycle && g.yOffWhen.stand > 23 && g.lastOnGroundCycle + 5 > g.cycle) g.jump() + + //check for short jumps //moving up //recently pressed jump //but not pressing jump key now + if (g.buttonCD_jump + 60 > g.cycle && !(control.up) && g.Vy < 0) { + Matter.Body.setVelocity(genisis, { + //reduce genisis y-velocity every cycle + x: genisis.velocity.x, + y: genisis.velocity.y * 0.94 + }); + } + + if (control.left) { + if (genisis.velocity.x > -g.airSpeedLimit / genisis.mass / genisis.mass) genisis.force.x -= g.FxAir; // move genisis left / a + } else if (control.right) { + if (genisis.velocity.x < g.airSpeedLimit / genisis.mass / genisis.mass) genisis.force.x += g.FxAir; //move genisis right / d + } + }, + alive: true, + dmgScale: 1, //scales all damage, but not raw .dmg //set in levels.setDifficulty + defaultFPSCycle: 0, //tracks when to return to normal fps + immuneCycle: 0, //used in engine + collisionImmuneCycles: 30, + buttonCD: 0, //cool down for genisis buttons + drawLeg(stroke) { + // if (simulation.mouseInGame.x > g.pos.x) { + if (g.angle > -Math.PI / 2 && g.angle < Math.PI / 2) { + g.flipLegs = 1; + } else { + g.flipLegs = -1; + } + ctx.save(); + ctx.scale(g.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(g.hip.x, g.hip.y); + ctx.lineTo(g.knee.x, g.knee.y); + ctx.lineTo(g.foot.x, g.foot.y); + ctx.strokeStyle = stroke; + ctx.lineWidth = 7; + ctx.stroke(); + + //toe lines + ctx.beginPath(); + ctx.moveTo(g.foot.x, g.foot.y); + ctx.lineTo(g.foot.x - 15, g.foot.y + 5); + ctx.moveTo(g.foot.x, g.foot.y); + ctx.lineTo(g.foot.x + 15, g.foot.y + 5); + ctx.lineWidth = 4; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(g.hip.x, g.hip.y, 11, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(g.knee.x + 7, g.knee.y); + ctx.arc(g.knee.x, g.knee.y, 7, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(g.foot.x + 6, g.foot.y); + ctx.arc(g.foot.x, g.foot.y, 6, 0, 2 * Math.PI); + ctx.fillStyle = g.fillColor; + ctx.fill(); + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + }, + calcLeg(cycle_offset, offset) { + g.hip.x = 12 + offset; + g.hip.y = 24 + offset; + //stepSize goes to zero if Vx is zero or not on ground (make m transition cleaner) + g.stepSize = 0.8 * g.stepSize + 0.2 * (7 * Math.sqrt(Math.min(9, Math.abs(g.Vx))) * g.onGround); + //changes to stepsize are smoothed by adding only a percent of the new value each cycle + const stepAngle = 0.034 * g.walk_cycle + cycle_offset; + g.foot.x = 2.2 * g.stepSize * Math.cos(stepAngle) + offset; + g.foot.y = offset + 1.2 * g.stepSize * Math.sin(stepAngle) + g.yOff + g.height; + const Ymax = g.yOff + g.height; + if (g.foot.y > Ymax) g.foot.y = Ymax; + + //calculate knee position as intersection of circle from hip and foot + const d = Math.sqrt((g.hip.x - g.foot.x) * (g.hip.x - g.foot.x) + (g.hip.y - g.foot.y) * (g.hip.y - g.foot.y)); + const l = (g.legLength1 * g.legLength1 - g.legLength2 * g.legLength2 + d * d) / (2 * d); + const h = Math.sqrt(g.legLength1 * g.legLength1 - l * l); + g.knee.x = (l / d) * (g.foot.x - g.hip.x) - (h / d) * (g.foot.y - g.hip.y) + g.hip.x + offset; + g.knee.y = (l / d) * (g.foot.y - g.hip.y) + (h / d) * (g.foot.x - g.hip.x) + g.hip.y; + }, + draw() { }, + drawDefault() { + ctx.fillStyle = g.fillColor; + g.walk_cycle += g.flipLegs * g.Vx; + ctx.save(); + ctx.globalAlpha = (g.immuneCycle < g.cycle) ? 1 : 0.5 //|| (g.cycle % 40 > 20) + ctx.translate(g.pos.x, g.pos.y); + g.calcLeg(Math.PI, -3); + g.drawLeg("#FFFFFF"); + g.calcLeg(0, 0); + g.drawLeg("#FFFFFF"); + ctx.rotate(g.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = g.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#FFFFFF"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + g.yOff = g.yOff * 0.85 + g.yOffGoal * 0.15; //smoothly move leg height towards height goal + }, + drawDamage() { + ctx.fillStyle = "red"; + g.walk_cycle += g.flipLegs * g.Vx; + ctx.save(); + ctx.globalAlpha = 0.7; + ctx.translate(g.pos.x, g.pos.y); + g.calcLeg(Math.PI, -3); + g.drawLeg("#FF0000"); + g.calcLeg(0, 0); + g.drawLeg("#FF0000"); + ctx.rotate(g.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = g.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#FF0000"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + g.yOff = g.yOff * 0.85 + g.yOffGoal * 0.15; //smoothly move leg height towards height goal + }, + damage(dmg) { + g.health -= dmg; + }, + rebirth() { + g.health = g.maxHealth; + genisis.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield | cat.player | cat.bullet + }, + lastHealth: 1000, + drawHealth() { + let interpolated = this.lastHealth + (g.health - this.lastHealth) * 0.1; + + ctx.save(); + ctx.setTransform(1, 0, 0.2, 1, 0, 0); //slanted + ctx.fillStyle = "rgba(250, 100, 100, 0.3)"; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30); + const grad = ctx.createLinearGradient(0, 0, canvas.width2, 0); + grad.addColorStop(0, "lightblue"); + grad.addColorStop(1, "crimson"); + + ctx.fillStyle = grad; + ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * interpolated / 1000, 30); + ctx.restore(); + + this.lastHealth = interpolated; + }, + genisisOnGroundCheck(event) { + //runs on collisions events + function enter() { + g.numTouching++; + if (!g.onGround) { + g.onGround = true; + if (g.crouch) { + if (g.checkHeadClear()) { + g.undoCrouch(); + } else { + g.yOffGoal = g.yOffWhen.crouch; + } + } else { + //sets a hard land where genisis stays in a crouch for a bit and can't jump + //crouch is forced in groundControl below + const momentum = genisis.velocity.y * genisis.mass //genisis mass is 5 so this triggers at 26 down velocity, unless the genisis is holding something + if (momentum > 130) { + g.doCrouch(); + g.yOff = g.yOffWhen.jump; + g.hardLandCD = g.cycle + Math.min(momentum / 6.5 - 6, 40) + //falling damage + if (tech.isFallingDamage && g.immuneCycle < g.cycle && momentum > 150) { + g.damage(Math.min(Math.sqrt(momentum - 133) * 0.01, 0.25)); + if (g.immuneCycle < g.cycle + g.collisionImmuneCycles) g.immuneCycle = g.cycle + g.collisionImmuneCycles; //genisis is immune to damage for 30 cycles + } + } else { + g.yOffGoal = g.yOffWhen.stand; + } + } + } + } + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; ++i) { + let pair = pairs[i]; + if (pair.bodyA === genisisJumpSensor) { + g.standingOn = pair.bodyB; //keeping track to correctly provide recoil on jump + if (g.standingOn.alive !== true) enter(); + } else if (pair.bodyB === genisisJumpSensor) { + g.standingOn = pair.bodyA; //keeping track to correctly provide recoil on jump + if (g.standingOn.alive !== true) enter(); + } + } + g.numTouching = 0; + }, + genisisOffGroundCheck(event) { + //runs on collisions events + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; ++i) { + if (pairs[i].bodyA === genisisJumpSensor || pairs[i].bodyB === genisisJumpSensor) { + if (g.onGround && g.numTouching === 0) { + g.onGround = false; + g.lastOnGroundCycle = g.cycle; + g.hardLandCD = 0 // disable hard landing + if (g.checkHeadClear()) { + if (g.crouch) { + g.undoCrouch(); + } + g.yOffGoal = g.yOffWhen.jump; + } + } + } + } + } + }; + function GenisisCollisionChecks(event) { + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; i++) { + for (let k = 0; k < bullet.length; k++) { + if (pairs[i].bodyA === bullet[k]) { + collideBullet(pairs[i].bodyB); + break; + } else if (pairs[i].bodyB === bullet[k]) { + collideBullet(pairs[i].bodyA); + break; + } + + function collideBullet(obj) { + if ( + g.immuneCycle < g.cycle && + (obj === genisisBody || obj === genisisHead) + ) { + let dmg = Math.sqrt(Math.abs(0.000025 * Math.sqrt((bullet[k].mass + Math.sqrt(Vector.magnitude(bullet[k].velocity)) * 0.0000125)))); + g.damage(dmg); + return; + } + } + } + for (let k = 0; k < mob.length; k++) { + if (mob[k].alive) { + if (pairs[i].bodyA === mob[k]) { + collideMob(pairs[i].bodyB); + break; + } else if (pairs[i].bodyB === mob[k]) { + collideMob(pairs[i].bodyA); + break; + } + + function collideMob(obj) { + //genisis + mob collision + if ( + g.immuneCycle < g.cycle && + (obj === genisisBody || obj === genisisHead) && + !mob[k].isSlowed && !mob[k].isStunned + ) { + let dmg = Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05); + // if (g.isCloak) dmg *= 0.5 + if (tech.isRewindAvoidDeath && g.energy > 0.85 * Math.min(1, g.maxEnergy) && dmg > 0.01) { //CPT reversal runs in g.damage, but it stops the rest of the collision code here too + g.damage(dmg); + return + } + g.damage(dmg); + + if (tech.isCollisionRealitySwitch && g.alive) { + g.switchWorlds() + simulation.trails() + simulation.inGameConsole(`simulation.amplitude = ${Math.random()}`); + } + if (tech.isPiezo) g.energy += 20.48; + if (tech.isCouplingNoHit && g.coupling > 0) { + g.couplingChange(-3) + + const unit = Vector.rotate({ x: 1, y: 0 }, 6.28 * Math.random()) + let where = Vector.add(g.pos, Vector.mult(unit, 17)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 22, + color: 'rgba(0, 171, 238, 0.33)', + time: 8 + }); + where = Vector.add(g.pos, Vector.mult(unit, 60)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 18, + color: 'rgba(0, 171, 238, 0.5)', + time: 16 + }); + where = Vector.add(g.pos, Vector.mult(unit, 100)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 14, + color: 'rgba(0, 171, 238, 0.6)', + time: 24 + }); + where = Vector.add(g.pos, Vector.mult(unit, 135)) + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 10, + color: 'rgba(0, 171, 238, 0.7)', + time: 32 + }); + // simulation.drawList.push({ //add dmg to draw queue + // x: g.pos.x, + // y: g.pos.y, + // radius: 150, + // color: 'rgba(0, 171, 238, 0.33)', + // time: 6 + // }); + // simulation.drawList.push({ //add dmg to draw queue + // x: g.pos.x, + // y: g.pos.y, + // radius: 75, + // color: 'rgba(0, 171, 238, 0.5)', + // time: 16 + // }); + // simulation.drawList.push({ //add dmg to draw queue + // x: g.pos.x, + // y: g.pos.y, + // radius: 25, + // color: 'rgba(0, 171, 238, 0.75)', + // time: 25 + // }); + } + if (mob[k].onHit) mob[k].onHit(); + if (g.immuneCycle < g.cycle + g.collisionImmuneCycles) g.immuneCycle = g.cycle + g.collisionImmuneCycles; //genisis is immune to damage for 30 cycles + //extra kick between genisis and mob //this section would be better with forces but they don't work... + let angle = Math.atan2(genisis.position.y - mob[k].position.y, genisis.position.x - mob[k].position.x); + Matter.Body.setVelocity(genisis, { + x: genisis.velocity.x + 8 * Math.cos(angle), + y: genisis.velocity.y + 8 * Math.sin(angle) + }); + Matter.Body.setVelocity(mob[k], { + x: mob[k].velocity.x - 8 * Math.cos(angle), + y: mob[k].velocity.y - 8 * Math.sin(angle) + }); + + if (tech.isAnnihilation && !mob[k].shield && !mob[k].isShielded && !mob[k].isBoss && mob[k].isDropPowerUp && g.energy > 0.1 && mob[k].damageReduction > 0) { + g.energy -= 0.1 //* Math.max(g.maxEnergy, g.energy) //0.33 * g.energy + if (g.immuneCycle === g.cycle + g.collisionImmuneCycles) g.immuneCycle = 0; //genisis doesn't go immune to collision damage + mob[k].death(); + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.sqrt(dmg) * 500, + color: "rgba(255,0,255,0.2)", + time: simulation.drawTime + }); + } else { + simulation.drawList.push({ //add dmg to draw queue + x: pairs[i].activeContacts[0].vertex.x, + y: pairs[i].activeContacts[0].vertex.y, + radius: Math.sqrt(dmg) * 200, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + } + // return; + // } + } + } + } + } + } + } + g.spawn(); + Matter.Body.setPosition(genisis, { + x: 7875, + y: -2530 + }) let isUsingSwordMod = false; for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].name === 'size-weight illusion') { //to detect if the player is using the sword mod so that it won't mess up their mod. The sword mod adds this tech so if it is detected then the sword won't be removed from the gun array //Landgreen, don't add a tech with the same name @@ -31165,40 +32618,47 @@ const level = { } } if (!isUsingSwordMod) { - (function () { - const e = { - name: "sword", - descriptionFunction() { return `swing a sword that lifesteals health
drains health instead of ammunition
doesn't use ammo` }, - ammo: Infinity, - ammoPack: Infinity, - defaultAmmoPack: Infinity, - have: false, - cycle: 0, - sword: undefined, - bladeSegments: undefined, - bladeTrails: [], - angle: 0, - constraint: undefined, - do() { - if (input.fire && m.fireCDcycle > m.cycle) { + const e = { + name: "sword", + descriptionFunction() { return `swing a sword that lifesteals health
drains health instead of ammunition
doesn't use ammo` }, + ammo: Infinity, + ammoPack: Infinity, + defaultAmmoPack: Infinity, + have: false, + cycle: 0, + sword: undefined, + swordArray: [], + bladeSegments: undefined, + bladeTrails: [], + angle: 0, + constraint: undefined, + charge: 0, + angle2: 0, + fire() { }, + do() { + if (this.sword && this.cycle < 1) { + this.angle2 = Math.atan2(this.sword.position.y - m.pos.y, this.sword.position.x - m.pos.x); + } + if (this.sword) { + this.cycle++; + } + this.normalFire(); + this.renderDefault(); + this.collision(); + }, + normalFire() { + if (this.constraint) { + this.constraint.pointA = player.position; + } + if (tech.isStabSword && !m.crouch && this.cycle > 0 && this.stabStatus) { + if (this.sword) { + this.stabStatus = false; if (tech.isEnergyHealth) { - m.energy -= 0.004; - } else { - m.health -= 0.001; - m.displayHealth(); - } - } - if (b.activeGun !== null && input.fire && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { - if (!this.sword && b.guns[b.activeGun].name === 'sword') { - Matter.Body.setMass(player, 1000); - ({ sword: this.sword, bladeSegments: this.bladeSegments } = this.createAndSwingSword()); - this.angle = m.angle; + m.energy = 0.01; + m.immuneCycle = m.cycle + 30; } - } - if (this.sword && !input.fire) { this.cycle = 0; Matter.Body.setAngularVelocity(this.sword, 0); - Matter.Body.setMass(player, 5) Composite.remove(engine.world, this.sword); this.sword.parts.forEach(part => { Composite.remove(engine.world, part); @@ -31213,136 +32673,273 @@ const level = { this.constraint = undefined; } this.bladeTrails = []; - m.fireCDcycle = m.cycle + 10; + m.fireCDcycle = 0; + } + } + + if (input.fire && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { + if (tech.isEnergyHealth) { + m.energy -= 0.004; } else { - if (this.sword && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { - let handle; - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].customName == "handle") { - handle = bullet[i]; - } - } - if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { - Matter.Body.setAngularVelocity(this.sword, -Math.PI * 0.1); - } else { - Matter.Body.setAngularVelocity(this.sword, Math.PI * 0.1); + m.health -= 0.001; + m.displayHealth(); + } + } + if (input.fire && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { + if (!this.sword && b.guns[b.activeGun].name === 'sword') { + ({ sword: this.sword, bladeSegments: this.bladeSegments } = this.createAndSwingSword()); + this.angle = m.angle; + } + } + if (this.sword && !input.fire) { + this.cycle = 0; + Matter.Body.setAngularVelocity(this.sword, 0); + player.force.x *= 0.01; + player.force.y *= 0.01; + Composite.remove(engine.world, this.sword); + this.sword.parts.forEach(part => { + Composite.remove(engine.world, part); + const index = bullet.indexOf(part); + if (index !== -1) { + bullet.splice(index, 1); + } + }); + this.sword = undefined; + if (this.constraint) { + Composite.remove(engine.world, this.constraint); + this.constraint = undefined; + } + this.bladeTrails = []; + m.fireCDcycle = m.cycle + 10; + } else { + if (this.sword && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { + let handle; + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].customName == "handle") { + handle = bullet[i]; } - if (!this.constraint && (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) { - this.constraint = Constraint.create({ - bodyA: player, - bodyB: this.sword, - pointB: { x: -9, y: ((handle.position.y - this.sword.position.y)) }, - stiffness: 0.1, - damping: 0.0001815, - length: 0, + } + if (tech.infinityEdge) { + const newSize = Math.sqrt(0.5 * m.health) + 1; + Matter.Body.scale(this.sword, newSize * (1 / (this.sword.scale == undefined ? 1 : this.sword.scale)), newSize * (1 / (this.sword.scale == undefined ? 1 : this.sword.scale)), handle.position); + this.sword.scale = newSize; + } + if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { + Matter.Body.setAngularVelocity(this.sword, -Math.PI * 0.1); + } else { + Matter.Body.setAngularVelocity(this.sword, Math.PI * 0.1); + } + if (tech.sizeIllusion) { + player.force.x += Math.cos(m.angle) * player.mass / 500; + player.force.y += Math.sin(m.angle) * player.mass / 500; + } + if (!this.constraint && (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) { + this.constraint = Constraint.create({ + pointA: player.position, + bodyB: this.sword, + pointB: { x: -9, y: ((handle.position.y - this.sword.position.y)) }, + stiffness: (tech.infinityEdge ? 0.05 : 0.1), + damping: 0.001815, + length: 0, - }); - Composite.add(engine.world, this.constraint); - } else if (!this.constraint) { - this.constraint = Constraint.create({ - bodyA: player, - bodyB: this.sword, - pointB: { x: 9, y: ((handle.position.y - this.sword.position.y)) }, - stiffness: 0.1, - damping: 0.0001815, - length: 0, - }); - Composite.add(engine.world, this.constraint); - } - } else if (this.sword) { - if (tech.isEnergyHealth) { - m.energy = 0.01; - m.immuneCycle = m.cycle + 30; - } - this.cycle = 0; - Matter.Body.setAngularVelocity(this.sword, 0); - Matter.Body.setMass(player, 5) - Composite.remove(engine.world, this.sword); - this.sword.parts.forEach(part => { - Composite.remove(engine.world, part); - const index = bullet.indexOf(part); - if (index !== -1) { - bullet.splice(index, 1); - } }); - this.sword = undefined; - if (this.constraint) { - Composite.remove(engine.world, this.constraint); - this.constraint = undefined; + Composite.add(engine.world, this.constraint); + } else if (!this.constraint) { + this.constraint = Constraint.create({ + pointA: player.position, + bodyB: this.sword, + pointB: { x: 9, y: ((handle.position.y - this.sword.position.y)) }, + stiffness: (tech.infinityEdge ? 0.05 : 0.1), + damping: 0.001815, + length: 0, + }); + Composite.add(engine.world, this.constraint); + } + } else if (this.sword) { + if (tech.isEnergyHealth) { + m.energy = 0.01; + m.immuneCycle = m.cycle + 30; + } + this.cycle = 0; + Matter.Body.setAngularVelocity(this.sword, 0); + player.force.x *= 0.01; + player.force.y *= 0.01; + Composite.remove(engine.world, this.sword); + this.sword.parts.forEach(part => { + Composite.remove(engine.world, part); + const index = bullet.indexOf(part); + if (index !== -1) { + bullet.splice(index, 1); } - this.bladeTrails = []; - m.fireCDcycle = 0; + }); + this.sword = undefined; + if (this.constraint) { + Composite.remove(engine.world, this.constraint); + this.constraint = undefined; } + this.bladeTrails = []; + m.fireCDcycle = 0; } - if (this.sword) { - for (let i = 0; i < this.bladeSegments.length; i++) { - const blade = this.bladeSegments[i]; - const trail = this.bladeTrails[i] || []; - const vertices = blade.vertices.map(vertex => ({ x: vertex.x, y: vertex.y })); - trail.push(vertices); - if (trail.length > 10) { - trail.shift(); - } - this.bladeTrails[i] = trail; + } + }, + createAndSwingSword(x = player.position.x, y = player.position.y, angle = m.angle) { + const handleWidth = 20; + const handleHeight = 150; + const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); + bullet[bullet.length] = handle; + handle.customName = "handle"; + bullet[bullet.length - 1].do = () => { }; + const pommelWidth = 30; + const pommelHeight = 40; + const pommelVertices = [ + { x: x, y: y + handleHeight / 2 + pommelHeight / 2 }, + { x: x + pommelWidth / 2, y: y + handleHeight / 2 }, + { x: x, y: y + handleHeight / 2 - pommelHeight / 2 }, + { x: x - pommelWidth / 2, y: y + handleHeight / 2 }, + ]; + const pommel = Bodies.fromVertices(x, y + handleHeight / 2, pommelVertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = pommel; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; + } + // Blade setup + const bladeWidth = 100 * (tech.soundSword ? 3 : 1); + const bladeHeight = 20 * (tech.soundSword ? 3 : 1); + const numBlades = 15; + const extensionFactor = 5; + const bladeSegments = []; + + if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const bladeX = x + i * (bladeWidth / 20); + const bladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; + + const vertices = [ + { x: bladeX, y: bladeY - bladeHeight / 2 }, + { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, + ]; + + const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = blade; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; + } + Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 270) * 15)); + bladeSegments.push(blade); + } + } else { + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const mirroredBladeX = x - i * (bladeWidth / 20); + const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; + const mirroredVertices = [ + { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, + { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, + { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, + { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, + ]; + const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = mirroredBlade; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; } + Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); + bladeSegments.push(mirroredBlade); + } + } + bladeSegments.push(pommel); + const sword = Body.create({ + parts: [handle, ...bladeSegments], + }); - for (let i = 0; i < this.bladeTrails.length; i++) { - const trail = this.bladeTrails[i]; + Composite.add(engine.world, sword); + Matter.Body.setPosition(sword, { x, y }); - const alphaStep = 1 / trail.length; - let alpha = 0; + sword.collisionFilter.category = cat.bullet; + sword.collisionFilter.mask = cat.mobBullet | cat.powerup | cat.mob; + Body.scale(sword, -1, 1, { x, y }); - for (let j = 0; j < trail.length; j++) { - const vertices = trail[j]; - ctx.beginPath(); - ctx.moveTo(vertices[0].x, vertices[0].y); + return { sword, bladeSegments }; + }, + renderDefault() { + if (this.sword) { + for (let i = 0; i < this.bladeSegments.length; i++) { + const blade = this.bladeSegments[i]; + const trail = this.bladeTrails[i] || []; + const vertices = blade.vertices.map(vertex => ({ x: vertex.x, y: vertex.y })); + trail.push(vertices); + if (trail.length > 10) { + trail.shift(); + } + this.bladeTrails[i] = trail; + } - for (let k = 1; k < vertices.length; k++) { - ctx.lineTo(vertices[k].x, vertices[k].y); - }; + for (let i = 0; i < this.bladeTrails.length; i++) { + const trail = this.bladeTrails[i]; - alpha += alphaStep; - ctx.closePath(); - if (tech.isEnergyHealth) { - const eyeColor = m.fieldMeterColor; - const r = eyeColor[1]; - const g = eyeColor[2]; - const b = eyeColor[3]; - const color = `#${r}${r}${g}${g}${b}${b}${Math.round(alpha * 255).toString(16).padStart(2, '0')}`; - ctx.fillStyle = color; - } else { - ctx.fillStyle = `rgba(220, 20, 60, ${alpha})`; - } - ctx.fill(); - } - } - for (let i = 0; i < this.bladeSegments.length; i++) { + const alphaStep = 1 / trail.length; + let alpha = 0; + + for (let j = 0; j < trail.length; j++) { + const vertices = trail[j]; ctx.beginPath(); - ctx.lineJoin = "miter"; - ctx.miterLimit = 100; - ctx.strokeStyle = tech.isEnergyHealth ? m.fieldMeterColor : tech.isAmmoSword ? "#c0c0c0" : "crimson"; - ctx.lineWidth = 5; - ctx.fillStyle = "black"; - ctx.moveTo(this.bladeSegments[i].vertices[0].x, this.bladeSegments[i].vertices[0].y); - for (let j = 0; j < this.bladeSegments[i].vertices.length; j++) { - ctx.lineTo(this.bladeSegments[i].vertices[j].x, this.bladeSegments[i].vertices[j].y) + ctx.moveTo(vertices[0].x, vertices[0].y); + + for (let k = 1; k < vertices.length; k++) { + ctx.lineTo(vertices[k].x, vertices[k].y); }; + + alpha += alphaStep; ctx.closePath(); - ctx.stroke(); + if (tech.isEnergyHealth) { + const eyeColor = m.fieldMeterColor; + const r = eyeColor[1]; + const g = eyeColor[2]; + const b = eyeColor[3]; + const color = `#${r}${r}${g}${g}${b}${b}${Math.round(alpha * 255).toString(16).padStart(2, '0')}`; + ctx.fillStyle = color; + } else { + ctx.fillStyle = `rgba(220, 20, 60, ${alpha})`; + } ctx.fill(); - ctx.lineJoin = "round"; - ctx.miterLimit = 10; } } - if (this.sword) { - for (let i = 0; i < mob.length; i++) { - if (Matter.Query.collides(this.sword, [mob[i]]).length > 0) { - const dmg = m.dmgScale * 6 * Math.sqrt(this.sword.speed); - if (m.health < 0.9) { + for (let i = 0; i < this.bladeSegments.length; i++) { + ctx.beginPath(); + ctx.lineJoin = "miter"; + ctx.miterLimit = 100; + ctx.strokeStyle = tech.isEnergyHealth ? m.fieldMeterColor : tech.isAmmoSword ? "#c0c0c0" : "crimson"; + ctx.lineWidth = 5; + ctx.fillStyle = "black"; + ctx.moveTo(this.bladeSegments[i].vertices[0].x, this.bladeSegments[i].vertices[0].y); + for (let j = 0; j < this.bladeSegments[i].vertices.length; j++) { + ctx.lineTo(this.bladeSegments[i].vertices[j].x, this.bladeSegments[i].vertices[j].y) + }; + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + ctx.lineJoin = "round"; + ctx.miterLimit = 10; + } + } + }, + collision() { + if (this.sword) { + for (let i = 0; i < mob.length; i++) { + if (Matter.Query.collides(this.sword, [mob[i]]).length > 0) { + const dmg = m.dmgScale * Math.sqrt(this.sword.speed) * (tech.sizeIllusion ? 1.1 : 1) * (tech.isStabSword ? 1.5 : 1) * (tech.infinityEdge ? 1.1 : 1); + if (!tech.soundSword) { + if (m.health < m.maxHealth) { if (tech.isEnergyHealth) { m.energy += 0.04; } else { - m.health += 0.001 * (dmg - mob[i].health); + m.health += 0.01 * (dmg - mob[i].health); m.displayHealth(); } } else { @@ -31353,100 +32950,35 @@ const level = { m.displayHealth(); } } - mob[i].damage(dmg, true); - simulation.drawList.push({ - x: mob[i].position.x, - y: mob[i].position.y, - radius: Math.sqrt(dmg / this.sword.speed) * 50, - color: simulation.mobDmgColor, - time: simulation.drawTime - }); - const angle = Math.atan2(mob[i].position.y - this.sword.position.y, mob[i].position.x - this.sword.position.x); - this.sword.force.x -= Math.cos(angle) * 5; - this.sword.force.y -= Math.sin(angle) * 5; - break } + mob[i].damage(dmg, true); + simulation.drawList.push({ + x: mob[i].position.x, + y: mob[i].position.y, + radius: Math.abs(Math.log(dmg * this.sword.speed) * 40 * mob[i].damageReduction + 3), + color: (tech.soundSword ? "rgba(0, 0, 0, 0.3)" : simulation.mobDmgColor), + time: simulation.drawTime + }); + break } } - }, - createAndSwingSword(x = player.position.x, y = player.position.y, angle = m.angle) { - if (this.cycle < m.cycle) { - this.cycle = Infinity; - m.fireCDcycle = Infinity; - const handleWidth = 20; - const handleHeight = 150; - const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); - bullet[bullet.length] = handle; - handle.customName = "handle"; - bullet[bullet.length - 1].do = () => { }; - const bladeWidth = 100; - const bladeHeight = 20; - const numBlades = 15; - const extensionFactor = 5; - const bladeSegments = []; - if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { - for (let i = 0; i < numBlades; i++) { - const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; - const bladeX = x + i * (bladeWidth / 20); - const bladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; - - const vertices = [ - { x: bladeX, y: bladeY - bladeHeight / 2 }, - { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, - { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, - { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, - ]; - - const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); - bullet[bullet.length] = blade; - bullet[bullet.length - 1].do = () => { }; - Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 270) * 15)); - bladeSegments.push(blade); - } - } else { - for (let i = 0; i < numBlades; i++) { - const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; - const mirroredBladeX = x - i * (bladeWidth / 20); - const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; - const mirroredVertices = [ - { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, - { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, - { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, - { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, - ]; - const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); - bullet[bullet.length] = mirroredBlade; - bullet[bullet.length - 1].do = () => { }; - Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); - bladeSegments.push(mirroredBlade); - } - } - const sword = Body.create({ - parts: [handle, ...bladeSegments], - }); - - Composite.add(engine.world, sword); - Matter.Body.setPosition(sword, { x, y }); - - sword.collisionFilter.category = cat.bullet; - sword.collisionFilter.mask = cat.mobBullet | cat.mob; - Body.scale(sword, -1, 1, { x, y }); - // sword.frictionAir -= 0.01; - - return { sword, bladeSegments }; + if (Matter.Query.collides(this.sword, [genisis]).length > 0) { + m.damage(-0.0142) //balanced! } - }, - fire() { } - }; - b.guns.push(e); - const gunArray = b.guns.filter( - (obj, index, self) => - index === self.findIndex((item) => item.name === obj.name) - ); - b.guns = gunArray; - })(); + } + } + }; + b.guns.push(e); + const gunArray = b.guns.filter( + (obj, index, self) => + index === self.findIndex((item) => item.name === obj.name) + ); + b.guns = gunArray; + } else { + simulation.inGameConsole(`Thank you for using my sword mod
I'll save you the trouble of killing genisis
g.damage(Infinity)
`); + g.damage(Infinity); } - simulation.makeTextLog(`arena by Richard0820`); + simulation.inGameConsole(`arena by Richard0820`); let index = 0; let index2 = 0; let { sword: sword, bladeSegments: bladeSegments } = createSword(); @@ -31457,21 +32989,13 @@ const level = { level.exit.y = -2530; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit - level.defaultZoom = 8000 + level.defaultZoom = 2000 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#987654"; color.map = "#765432" //custom map color color.block = "#876543"; door.isClosing = true; door2.isClosing = true; - spawnStuff(1000, -3100, 4450, 3125, 50 / simulation.difficultyMode); - spawnStuff(5400, -2425, 200, 2250, 5 / simulation.difficultyMode); - spawnStuff(5625, -2425, 2000, 275, 5 / simulation.difficultyMode); - spawnStuff(5625, -2125, 850, 1125, 5 / simulation.difficultyMode); - spawnStuff(6500, -2150, 475, 650, 5 / simulation.difficultyMode); - spawnStuff(7000, -2125, 325, 275, 5 / simulation.difficultyMode); - spawnStuff(5650, -950, 300, 450, 5 / simulation.difficultyMode); - spawn.randomLevelBoss(4225, -575); for (let i = 0; i < 5; i++) { powerUps.spawn(-6075, -2000, "heal"); } @@ -31511,7 +33035,7 @@ const level = { this.maxLife = this.life; } - draw(ctx) { + draw() { ctx.beginPath(); ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`; ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); @@ -31570,7 +33094,10 @@ const level = { system.update(); } function draw() { - system.particles.forEach(particle => particle.draw(ctx)); + system.particles.forEach(particle => particle.draw()); + } + for (let i = 0; i < system.particles.length; i++) { + system.particles[i].life = 0; } level.custom = () => { update(); @@ -31640,7 +33167,7 @@ const level = { } } index2++; - simulation.makeTextLog(`If you want to keep this sword, visit https://github.com/Whyisthisnotavalable/n-scythe. The sword is there.`) + setTimeout(() => { simulation.inGameConsole(`If you want to keep this sword, visit https://github.com/Whyisthisnotavalable/n-scythe. The sword is there.`) }, 1000) } for (let i = 0; i < bladeSegments.length; i++) { const blade = bladeSegments[i]; @@ -31718,7 +33245,419 @@ const level = { } }; level.customTopLayer = () => { }; + simulation.ephemera.push({ + name: "genesis", + death: false, + pwuspawn: 0, + do() { + if (this.death === true) { + b.explosion(g.pos, 200 * Math.random(), "#000000") + setTimeout(() => { + if (this.pwuspawn === 0) { + powerUps.spawnBossPowerUp(g.pos.x, g.pos.y) + this.pwuspawn++; + } + simulation.removeEphemera(this.name); + simulation.removeEphemera("genisisScythe"); + }, 1000); + } + if (g.health >= 0) { + if (g.health < g.maxHealth) { + g.health++; + } + const dist = Matter.Vector.magnitudeSquared(Matter.Vector.sub(genisis.position, player.position)); + const time = Math.sqrt(dist) / 60; + g.alive = true; + g.targetX = m.pos.x + player.velocity.x * time; + g.targetY = m.pos.y + player.velocity.y * time; + } else { + this.death = true; + g.alive = false; + } + if (g.alive) { + g.cycle++; + g.move() + g.lookDefault(); + g.drawDefault(); + g.drawHealth(); + genisis.force.y += g.mass * simulation.g + g.setFillColors(); + control.right = g.angle > -Math.PI * 2 / 5 && g.angle < Math.PI * 2 / 5; + control.left = g.angle > Math.PI * 3 / 5 || g.angle < -Math.PI * 3 / 5; + control.down = g.angle > Math.PI * 3 / 10 && g.angle < Math.PI * 7 / 10; + if (Matter.Query.collides(genisis, body).length || Matter.Query.collides(genisisHead, map).length || Matter.Query.collides(genisisBodySensor, map).length && !control.down) { + if (g.buttonCD_jump < g.cycle) { + g.jump(); + } + } + control.up = g.angle > -Math.PI * 6 / 10 && g.angle < -Math.PI * 4 / 10; + + if (g.onGround) { + g.groundControl() + } else { + g.airControl() + } + + if (g.pos.y > simulation.fallHeight) { + Matter.Body.setPosition(genisis, { + x: level.exit.x, + y: level.exit.y + }) + } + } else { + genisis.collisionFilter.mask = cat.map | cat.body; + Matter.Body.setPosition(genisis, { + x: 0, + y: 0 + }) + } + + if (simulation.testing) { + ctx.beginPath(); + let bodyDraw = genisisJumpSensor.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(255, 0, 0, 0.5)"; + ctx.fill(); + + ctx.beginPath(); + bodyDraw = genisisBody.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(0, 255, 255, 0.25)"; + ctx.fill(); + + ctx.beginPath(); + bodyDraw = genisisHead.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(255, 255, 0, 0.4)"; + ctx.fill(); + + ctx.beginPath(); + bodyDraw = genisisHeadSensor.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(0, 0, 255, 0.25)"; + ctx.fill(); + + ctx.beginPath(); + bodyDraw = genisisBodySensor.vertices; + ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); + for (let j = 1; j < bodyDraw.length; ++j) { + ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); + } + ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); + ctx.fillStyle = "rgba(255, 0, 255, 0.25)"; + ctx.fill(); + } + + Events.on(engine, "collisionStart", function (event) { + g.genisisOnGroundCheck(event); + GenisisCollisionChecks(event); + }); + Events.on(engine, "collisionActive", function (event) { + g.genisisOnGroundCheck(event); + }); + Events.on(engine, "collisionEnd", function (event) { + g.genisisOffGroundCheck(event); + }); + } + }) + let evo = { + isLongBlade: true, + isScytheRange: true, + scytheRange: 3, + isScytheRad: false, + scytheRad: 0, + isDoubleScythe: false, + isPhaseScythe: false, + isMeleeScythe: false, + isStunScythe: false, + }; + simulation.ephemera.push({ + name: "genisisScythe", + cycle: 0, + scythe: undefined, + bladeSegments: undefined, + bladeTrails: [], + angle: 0, + constraint: undefined, + fireCD: 0, + do() { + if (isOwned) { + if (g.health < 500 && g.health > 200) { + evo = { + isLongBlade: true, + isScytheRange: true, + scytheRange: 3, + isScytheRad: false, + scytheRad: 1, + isDoubleScythe: true, + isPhaseScythe: false, + isMeleeScythe: false, + isStunScythe: false, + }; + } else if (g.health < 200 && g.health > 50) { + evo = { + isLongBlade: true, + isScytheRange: true, + scytheRange: 3, + isScytheRad: true, + scytheRad: 1, + isDoubleScythe: true, + isPhaseScythe: true, + isMeleeScythe: true, + isStunScythe: true, + }; + } else if (g.health < 50) { + evo = { + isLongBlade: true, + isScytheRange: true, + scytheRange: 9, + isScytheRad: true, + scytheRad: 6, + isDoubleScythe: true, + isPhaseScythe: true, + isMeleeScythe: true, + isStunScythe: true, + }; + } + if (g.cycle > this.fireCD && !this.scythe) { + this.fireCD = g.cycle + 30; + if (!this.scythe) { + ({ scythe: this.scythe, bladeSegments: this.bladeSegments } = this.createAndSwingScythe()); + this.angle = g.angle; + } + } + if (this.scythe && g.cycle > this.cycle + 30 || !g.alive && this.scythe) { + Composite.remove(engine.world, this.scythe); + this.scythe.parts.forEach(part => { + Composite.remove(engine.world, part); + const index = bullet.indexOf(part); + if (index !== -1) { + bullet.splice(index, 1); + } + }); + this.scythe = undefined; + this.bladeTrails = []; + } else { + if (this.scythe && !evo.isMeleeScythe) { + if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { + Matter.Body.setAngularVelocity(this.scythe, -Math.PI * 0.15 - (evo.scytheRad ? evo.scytheRad * 0.1 : 0)); + } else { + Matter.Body.setAngularVelocity(this.scythe, Math.PI * 0.15 + (evo.scytheRad ? evo.scytheRad * 0.1 : 0)); + } + Matter.Body.setVelocity(this.scythe, { + x: Math.cos(this.angle) * 30, + y: Math.sin(this.angle) * 30 + }); + } else if (this.scythe && evo.isMeleeScythe) { + if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { + Matter.Body.setAngularVelocity(this.scythe, -Math.PI * 0.1 + (evo.isStunScythe ? 0.1 : 0)); + } else { + Matter.Body.setAngularVelocity(this.scythe, Math.PI * 0.1 - (evo.isStunScythe ? 0.1 : 0)); + } + Matter.Body.setPosition(this.scythe, genisis.position); + } + } + if (this.scythe) { + for (let i = 0; i < this.bladeSegments.length; i++) { + const blade = this.bladeSegments[i]; + const trail = this.bladeTrails[i] || []; + const vertices = blade.vertices.map(vertex => ({ x: vertex.x, y: vertex.y })); + trail.push(vertices); + if (trail.length > 10) { + trail.shift(); + } + this.bladeTrails[i] = trail; + } + for (let i = 0; i < this.bladeTrails.length; i++) { + const trail = this.bladeTrails[i]; + + const alphaStep = 1 / trail.length; + let alpha = 0; + + for (let j = 0; j < trail.length; j++) { + const vertices = trail[j]; + ctx.beginPath(); + ctx.moveTo(vertices[0].x, vertices[0].y); + + for (let k = 1; k < vertices.length; k++) { + ctx.lineTo(vertices[k].x, vertices[k].y); + }; + + alpha += alphaStep; + ctx.closePath(); + ctx.fillStyle = `rgba(100, 20, 255, ${alpha})`; + ctx.fill(); + } + } + for (let i = 0; i < this.bladeSegments.length; i++) { + ctx.beginPath(); + ctx.lineJoin = "miter"; + ctx.miterLimit = 100; + ctx.strokeStyle = `rgb(100, 20, 255)`; + ctx.lineWidth = 5; + ctx.fillStyle = "black"; + ctx.moveTo(this.bladeSegments[i].vertices[0].x, this.bladeSegments[i].vertices[0].y); + for (let j = 0; j < this.bladeSegments[i].vertices.length; j++) { + ctx.lineTo(this.bladeSegments[i].vertices[j].x, this.bladeSegments[i].vertices[j].y) + }; + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + ctx.lineJoin = "round"; + ctx.miterLimit = 10; + } + } + if (this.scythe) { + for (let i = 0; i < mob.length; i++) { + if (Matter.Query.collides(this.scythe, [mob[i]]).length > 0) { + const dmg = m.dmgScale * 0.12 * 2.73 * (evo.isLongBlade ? 1.3 : 1) * (evo.scytheRange ? evo.scytheRange * 1.15 : 1) * (evo.isDoubleScythe ? 0.9 : 1) * (evo.scytheRad ? evo.scytheRad * 1.5 : 1); + mob[i].damage(dmg, true); + simulation.drawList.push({ + x: mob[i].position.x, + y: mob[i].position.y, + radius: Math.sqrt(dmg) * 50, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + if (!evo.isMeleeScythe) { + const angle = Math.atan2(mob[i].position.y - this.scythe.position.y, mob[i].position.x - this.scythe.position.x); + this.scythe.force.x += Math.cos(angle) * 2; + this.scythe.force.y += Math.sin(angle) * 2; + } + if (evo.isStunScythe) { + mobs.statusStun(mob[i], 90); + } + break + } + } + if (Matter.Query.collides(this.scythe, [player]).length > 0 && m.immuneCycle < m.cycle) { + const dmg = 0.02 * (evo.isLongBlade ? 1.3 : 1) * (evo.scytheRange ? evo.scytheRange * 1.15 : 1) * (evo.isDoubleScythe ? 0.9 : 1) * (evo.scytheRad ? evo.scytheRad * 1.5 : 1); // actual scythe scallings one tap the player so this is nerfed for genisis + m.damage(dmg); + m.immuneCycle = m.cycle + 10; + simulation.drawList.push({ + x: player.position.x, + y: player.position.y, + radius: Math.sqrt(dmg) * 50, + color: simulation.mobDmgColor, + time: simulation.drawTime + }); + if (!evo.isMeleeScythe) { + const angle = Math.atan2(player.position.y - this.scythe.position.y, player.position.x - this.scythe.position.x); + this.scythe.force.x += Math.cos(angle) * 2; + this.scythe.force.y += Math.sin(angle) * 2; + } + } + } + } + }, + createAndSwingScythe(x = genisis.position.x, y = genisis.position.y, angle = g.angle) { + this.cycle = g.cycle + 60 + (evo.scytheRange * 6); + const handleWidth = 20; + const handleHeight = 200 + (evo.isLongBlade ? 30 : 0) + (evo.isMeleeScythe ? 200 : 0); + const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); + bullet[bullet.length] = handle; + bullet[bullet.length - 1].do = () => { }; + const bladeWidth = 100; + const bladeHeight = 20; + const numBlades = 10 + (evo.isLongBlade ? 1 : 0) + (evo.isMeleeScythe ? 3 : 0); + const extensionFactor = 5.5; + const bladeSegments = []; + if (!evo.isDoubleScythe) { + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const bladeX = x - handleWidth / 2 + i * (bladeWidth / 2) - extensionFactorFraction * (bladeWidth / 2); + const bladeY = y + handleHeight / 2 - i * (bladeHeight / (3 ** i)); + + const vertices = [ + { x: bladeX, y: bladeY - bladeHeight / 2 }, + { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, + ]; + + const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = blade; + bullet[bullet.length - 1].do = () => { }; + Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 180) * 5)); + bladeSegments.push(blade); + } + } else { + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const bladeX = x - handleWidth / 2 + i * (bladeWidth / 2) - extensionFactorFraction * (bladeWidth / 2); + const bladeY = y + handleHeight / 2 - i * (bladeHeight / (3 ** i)); + + const vertices = [ + { x: bladeX, y: bladeY - bladeHeight / 2 }, + { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, + ]; + + const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = blade; + bullet[bullet.length - 1].do = () => { }; + Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 180) * 5)); + bladeSegments.push(blade); + } + + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const bladeX = x + handleWidth / 2 - i * (bladeWidth / 2) + extensionFactorFraction * (bladeWidth / 2); + const bladeY = y - handleHeight / 2 - i * (bladeHeight / (3 ** i)); + + const vertices = [ + { x: bladeX, y: bladeY - bladeHeight / 2 }, + { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, + { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, + ]; + + const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = blade; + bullet[bullet.length - 1].do = () => { }; + Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 180) * 5) + Math.PI); + bladeSegments.push(blade); + } + } + const scythe = Body.create({ + parts: [handle, ...bladeSegments], + }); + Composite.add(engine.world, scythe); + Matter.Body.setPosition(scythe, { x, y }); + scythe.collisionFilter.category = cat.body; + scythe.collisionFilter.mask = cat.mobBullet | cat.player; + if (!evo.isMeleeScythe) { + setTimeout(() => { + scythe.collisionFilter.mask = cat.mobBullet | cat.mob | cat.player; + }, 1000) + } + if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { + Body.scale(scythe, -1, 1, { x, y }); + } + + scythe.frictionAir -= 0.01; + + return { scythe, bladeSegments }; + }, + }) spawn.mapRect(-10000, 0, 20000, 2000); spawn.mapRect(-10000, -10000, 2000, 10000); spawn.mapRect(8000, -10000, 2000, 10000); @@ -31730,56 +33669,54 @@ const level = { spawn.mapRect(4000, -10, 100, 20); spawn.mapRect(-1000, -10000, 2000, 8000); - spawn.mapRect(-500, -10000, 1000, 9700); - function createSword(x = 0, y = 0, angle = 0) { //sword asthetic + // spawn.mapRect(-500, -10000, 1000, 9700); + function createSword(x = 0, y = 0) { //sword asthetic const handleWidth = 20; const handleHeight = 150; const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); bullet[bullet.length] = handle; handle.customName = "handle"; bullet[bullet.length - 1].do = () => { }; - const bladeWidth = 100; - const bladeHeight = 20; + const pommelWidth = 30; + const pommelHeight = 40; + const pommelVertices = [ + { x: x, y: y + handleHeight / 2 + pommelHeight / 2 }, + { x: x + pommelWidth / 2, y: y + handleHeight / 2 }, + { x: x, y: y + handleHeight / 2 - pommelHeight / 2 }, + { x: x - pommelWidth / 2, y: y + handleHeight / 2 }, + ]; + const pommel = Bodies.fromVertices(x, y + handleHeight / 2, pommelVertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = pommel; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; + } + // Blade setup + const bladeWidth = 100 * (tech.soundSword ? 3 : 1); + const bladeHeight = 20 * (tech.soundSword ? 3 : 1); const numBlades = 15; const extensionFactor = 5; const bladeSegments = []; - if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { - for (let i = 0; i < numBlades; i++) { - const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; - const bladeX = x + i * (bladeWidth / 20); - const bladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; - - const vertices = [ - { x: bladeX, y: bladeY - bladeHeight / 2 }, - { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, - { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, - { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, - ]; - - const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); - bullet[bullet.length] = blade; - bullet[bullet.length - 1].do = () => { }; - Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 270) * 15)); - bladeSegments.push(blade); - } - } else { - for (let i = 0; i < numBlades; i++) { - const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; - const mirroredBladeX = x - i * (bladeWidth / 20); - const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; - const mirroredVertices = [ - { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, - { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, - { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, - { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, - ]; - const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); - bullet[bullet.length] = mirroredBlade; - bullet[bullet.length - 1].do = () => { }; - Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); - bladeSegments.push(mirroredBlade); + for (let i = 0; i < numBlades; i++) { + const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; + const mirroredBladeX = x - i * (bladeWidth / 20); + const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; + const mirroredVertices = [ + { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, + { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, + { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, + { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, + ]; + const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); + bullet[bullet.length] = mirroredBlade; + bullet[bullet.length - 1].do = () => { }; + if (tech.soundSword) { + bullet[bullet.length - 1].draw = () => { }; } + Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); + bladeSegments.push(mirroredBlade); } + bladeSegments.push(pommel); const sword = Body.create({ parts: [handle, ...bladeSegments], }); @@ -31788,28 +33725,948 @@ const level = { Matter.Body.setPosition(sword, { x, y }); sword.collisionFilter.category = cat.bullet; - sword.collisionFilter.mask = cat.bullet; + sword.collisionFilter.mask = cat.mobBullet | cat.powerup | cat.mob; Body.scale(sword, -1, 1, { x, y }); - Body.rotate(sword, Math.PI / 1.05) - sword.frictionAir = -0.01; + Body.rotate(sword, Math.PI + Math.PI / 15) return { sword, bladeSegments }; } - function spawnStuff(x, y, width, height, num) { - for (let i = 0; i < num; i++) { - randomMob(x + width * Math.random(), y + height * Math.random(), Infinity) + }, + soft() { + simulation.inGameConsole(``); + simulation.inGameConsole(`soft by Richard0820`); + simulation.inGameConsole("The lasers deal less damage the higher level you are") + const portals = []; + portals.push(level.portal({ + x: -1525, + y: -250 + }, Math.PI / 2, { + x: 1100, + y: -1025 + }, Math.PI / 2)) + const soft = { + createCloth(x, y, radius, width, height, attachToPlayer = false, stayStill = false, options, touchPlayer = true, constrictionStrength = 0.001) { + const bodies = []; + const constraints = []; + const otherCons = []; + const bodyWidth = radius; + const bodyHeight = radius; + const numRows = Math.ceil(height / bodyHeight); + const numCols = Math.ceil(width / bodyWidth); + + for (let i = 0; i < numRows; i++) { + for (let j = 0; j < numCols; j++) { + const posX = x + j * bodyWidth + bodyWidth / 2; + const posY = y + i * bodyHeight + bodyHeight / 2; + + const rect = Matter.Bodies.circle(posX, posY, (bodyWidth + bodyHeight) / 4, options); + rect.collisionFilter.category = cat.body; + rect.collisionFilter.mask = (touchPlayer ? cat.player | cat.body | cat.bullet | cat.mob | cat.mobBullet : cat.body | cat.bullet | cat.mob | cat.mobBullet); + rect.classType = "body"; + + Composite.add(engine.world, rect); + + bodies.push(rect); + } + } + + for (let i = 0; i < numRows; i++) { + for (let j = 0; j < numCols; j++) { + const bodyIndexA = i * numCols + j; + if (j < numCols - 1) { + const bodyIndexB = i * numCols + (j + 1); + const constraint = Constraint.create({ + bodyA: bodies[bodyIndexA], + bodyB: bodies[bodyIndexB], + stiffness: 0.06, + damping: 0.001 + }); + Composite.add(engine.world, constraint); + constraints.push(constraint); + } + if (i < numRows - 1) { + const bodyIndexB = (i + 1) * numCols + j; + const constraint = Constraint.create({ + bodyA: bodies[bodyIndexA], + bodyB: bodies[bodyIndexB], + stiffness: 0.06, + damping: 0.001 + }); + Composite.add(engine.world, constraint); + constraints.push(constraint); + } + } + } + + for (let i = 0; i < numRows - 1; i++) { + for (let j = 0; j < numCols - 1; j++) { + const bodyA = bodies[i * numCols + j]; + const bodyB = bodies[(i + 1) * numCols + j + 1]; + const constraint = Constraint.create({ + bodyA: bodyA, + bodyB: bodyB, + stiffness: 0.02 + }); + constraints.push(constraint); + } + } + + for (let i = 0; i < numRows - 1; i++) { + for (let j = 1; j < numCols; j++) { + const bodyA = bodies[i * numCols + j]; + const bodyB = bodies[(i + 1) * numCols + j - 1]; + const constraint = Constraint.create({ + bodyA: bodyA, + bodyB: bodyB, + stiffness: 0.02 + }); + constraints.push(constraint); + } + } + if (stayStill) { + for (let i = 0; i < bodies.length; i++) { + const by = bodies[i]; + const spawnX = by.position.x + bodyWidth / 2; + const spawnY = by.position.y + bodyHeight / 2; + const isLastColumn = (i + 1) % numCols === 0; + const isFirstColumn = i % numCols === 0; + const stiffness = constrictionStrength * (isLastColumn || isFirstColumn ? 100 : 1); // Apply extra stiffness to first and last columns + + const cost = Constraint.create({ + bodyA: by, + pointB: { x: spawnX, y: spawnY }, + stiffness: stiffness, + length: 0 + }); + + Composite.add(engine.world, cost); + otherCons.push(cost); + } + } + if (attachToPlayer) { + for (let i = 0; i < bodies.length; i++) { + const cost = Constraint.create({ + bodyA: bodies[i], + pointB: player.position, + stiffness: 0.0005, + length: 0 + }); + Composite.add(engine.world, cost); + } + } + + return { bodies, constraints, otherCons }; + }, + clothOptions: { + frictionAir: 0.005, + }, + isOuterBoundary(body, bodies) { //unused + const neighbors = [ + { x: body.position.x + 1, y: body.position.y }, + { x: body.position.x - 1, y: body.position.y }, + { x: body.position.x, y: body.position.y + 1 }, + { x: body.position.x, y: body.position.y - 1 } + ]; + + for (let i = 0; i < neighbors.length; i++) { + const neighbor = neighbors[i]; + const isNeighbor = bodies.some(b => b.position.x === neighbor.x && b.position.y === neighbor.y); + if (!isNeighbor) { + return true; + } + } + return false; + }, + draw(cloth) { + ctx.beginPath(); + ctx.lineWidth = 2; + ctx.strokeStyle = "rgba(0,0,0,0.3)"; + ctx.fillStyle = "black"; + for (let i = 0, len = cloth.constraints.length; i < len; ++i) { + const constraint = cloth.constraints[i]; + ctx.moveTo(constraint.bodyA.position.x, constraint.bodyA.position.y); + ctx.lineTo(constraint.bodyB.position.x, constraint.bodyB.position.y); + } + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + }, + addGravity(bodies, magnitude) { + for (var i = 0; i < bodies.length; i++) { + bodies[i].force.y += bodies[i].mass * magnitude; + } + }, + gravity(cloth) { + this.addGravity(cloth.bodies, simulation.g); + }, + breaker(cloth, percentage = 0.5) { + const totalConstraints = cloth.constraints.length; + const constraintsToRemove = Math.ceil(totalConstraints * percentage); + + for (let i = 0; i < constraintsToRemove; i++) { + const randomIndex = Math.floor(Math.random() * cloth.constraints.length); + + let removedConstraint = cloth.constraints.splice(randomIndex, 1)[0]; + Composite.remove(engine.world, removedConstraint); + } + }, + destroyer(cloth, percentage = 0.99999) { + const otherCons = cloth.otherCons.length; + const otherCons2Remove = Math.ceil(otherCons * percentage); + + for (let i = 0; i < otherCons2Remove; i++) { + const randomIndex = Math.floor(Math.random() * cloth.otherCons.length); + + let removedConstraint = cloth.otherCons.splice(randomIndex, 1)[0]; + Composite.remove(engine.world, removedConstraint); + } + }, + annihilate(cloth) { + const totalBodies = cloth.bodies.length; + for (let i = 0; i < totalBodies; i++) { + const removeBody = cloth.bodies[i]; + Composite.remove(engine.world, removeBody); + } + cloth.bodies.length = 0; // Clear the bodies array after removal } } - function randomMob(x, y, chance = 1) { - if (spawn.spawnChance(chance) || chance === Infinity) { - const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; - spawn[pick](x, y); + const clothArray = []; + clothArray.push(soft.createCloth(-100, 0, 50, 1000, 300, false, true, soft.clothOptions, true)) + clothArray.push(soft.createCloth(-2000, 2375, 50, 1525, 200, false, true, soft.clothOptions, true)) + clothArray.push(soft.createCloth(-3950, 125, 50, 1800, 125, false, true, soft.clothOptions, true)) + const annoyingStuff = { + lasers(where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + }; + + const seeRange = 7000; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: where.x + seeRange * Math.cos(angle), + y: where.y + seeRange * Math.sin(angle) + }; + // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + vertexCollision(where, look, body); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + const dmg = 0.5 / simulation.dmgScale; + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: dmg * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + //draw beam + if (best.dist2 === Infinity) best = look; + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + }, + laserBoss(x, y, radius = 30) { + mobs.spawn(x, y, 6, radius, "#f00"); + let me = mob[mob.length - 1]; + + setTimeout(() => { //fix mob in place, but allow rotation + me.constraint = Constraint.create({ + pointA: { + x: me.position.x, + y: me.position.y + }, + bodyB: me, + stiffness: 1, + damping: 1 + }); + Composite.add(engine.world, me.constraint); + }, 2000); //add in a delay in case the level gets flipped left right + me.count = 0; + me.frictionAir = 0.03; + // me.torque -= me.inertia * 0.002 + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.25 + me.isBoss = true; + // spawn.shield(me, x, y, 1); //not working, not sure why + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.rotateVelocity = -Math.min(0.0045, 0.0015 * simulation.accelScale * simulation.accelScale) * (level.levelsCleared > 8 ? 1 : -1) + me.do = function () { + this.fill = '#' + Math.random().toString(16).substr(-6); //flash colors + this.checkStatus(); + + if (!this.isStunned) { + //check if slowed + let slowed = false + for (let i = 0; i < this.status.length; i++) { + if (this.status[i].type === "slow") { + slowed = true + break + } + } + if (!slowed) { + this.count++ + Matter.Body.setAngle(this, this.count * this.rotateVelocity) + Matter.Body.setAngularVelocity(this, 0) + } + + ctx.beginPath(); + for (let i = 0; i < this.vertices.length; i++) { + if (Math.sin((2 * Math.PI * simulation.cycle) / (50 + i)) > 0) { + this.lasers(this.vertices[i], Math.atan2(this.vertices[i].y - this.position.y, this.vertices[i].x - this.position.x)); + } + } + ctx.strokeStyle = "#50f"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(80,0,255,0.07)"; + ctx.stroke(); // Draw it + } + }; + me.lasers = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2) best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + }; + + const seeRange = 7000; + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const look = { + x: where.x + seeRange * Math.cos(angle), + y: where.y + seeRange * Math.sin(angle) + }; + // vertexCollision(where, look, mob); + vertexCollision(where, look, map); + vertexCollision(where, look, body); + if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); + if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second + const dmg = 0.5 / simulation.dmgScale; + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: best.x, + y: best.y, + radius: dmg * 1500, + color: "rgba(80,0,255,0.5)", + time: 20 + }); + } + //draw beam + if (best.dist2 === Infinity) best = look; + ctx.moveTo(where.x, where.y); + ctx.lineTo(best.x, best.y); + } } - if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { - const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; - spawn[pick](x, y); + } + level.setPosToSpawn(-350, 0); + level.exit.x = 1075; + level.exit.y = 20; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#aaFFFF55"; + spawn.mapRect(900, 50, 425, 250); + // spawn.mapRect(900, -1050, 0.1, 1350); + spawn.mapRect(-475, 2375, 1800, 250); + spawn.mapRect(-4400, 2375, 2475, 250); + spawn.mapRect(-4400, -450, 250, 3075); + spawn.mapRect(-4400, -450, 2225, 250); + spawn.mapRect(-2425, -1300, 250, 1100); + spawn.mapRect(-2425, -1300, 3825, 250); + spawn.mapRect(1325, -1300, 250, 3925); + spawn.mapRect(-875, -1300, 250, 1375); + spawn.mapRect(-725, 50, 675, 250); + spawn.mapRect(-875, 175, 175, 125); + for (let i = 0; i < 6; i++) { + spawn.mapRect(-4175, 2000 - i * 375, 50, 125); + } + spawn.mapRect(-3925, 162.5, 50, 125); + spawn.mapRect(-2175, 162.5, 50, 125); + spawn.mapRect(300, 2025, 250, 600); + spawn.mapRect(-2150, 175, 50, 25); + spawn.mapRect(-2150, 250, 50, 25); + spawn.mapRect(-900, 175, 50, 25); + spawn.mapRect(-900, 250, 50, 25); + spawn.mapRect(-1600, 175, 50, 25); + spawn.mapRect(-1500, 175, 50, 25); + spawn.mapRect(-1600, 250, 50, 25); + spawn.mapRect(-1500, 250, 50, 25); + spawn.mapRect(-1925, 175, 50, 25); + spawn.mapRect(-1925, 250, 50, 25); + spawn.mapRect(-1200, 175, 50, 25); + spawn.mapRect(-1200, 250, 50, 25); + spawn.bodyRect(-2125, 200, 1250, 50); + spawn.debris(425, 200, 50); + spawn.debris(-650, 2100, 50); + spawn.debris(-3000, 1925, 50); + spawn.debris(-3825, 1550, 50); + spawn.debris(-2475, -50, 50); + + const bouncyBody = body[body.length - 1]; + bouncyBody.restitution = 0.9; + spawn.mapVertex(-2175 + 1300 / 2, -1050 + 1225 / 2, "0 -400 -100 -300 -100 0 100 0 100 -300"); + + spawn.mapVertex(-4150 + 1975 / 2, -200 + 2575 / 2, "0 -800 -200 -600 -200 0 0 200 200 0 200 -600 0 200"); + const mapWithVertex = map[map.length - 1]; + let index1 = 0; + level.custom = () => { + level.exit.drawAndCheck(); + + level.enter.draw(); + + if (player.position.x > 425 && index1 === 0) { + soft.breaker(clothArray[0], 0.7); + soft.destroyer(clothArray[0]); + index1++; + } + if (player.position.y > 1300 && index1 === 1) { + setTimeout(() => { + soft.breaker(clothArray[0], 1); + soft.annihilate(clothArray[0]); + clothArray.splice(0, 1); + }, 1000); //prevents bugs + simulation.inGameConsole("Couldn't be so simple, could it?", 2000 * Math.random()); + index1++; + } + }; + level.customTopLayer = () => { + for (let i = 0; i < portals.length; i++) { + portals[i][2].query(); + portals[i][3].query(); + portals[i][0].draw(); + portals[i][1].draw(); + portals[i][2].draw(); + portals[i][3].draw(); + } + ctx.beginPath(); + if (Math.sin((2 * Math.PI * simulation.cycle) / (50)) > 0) { + annoyingStuff.lasers(mapWithVertex.vertices[0], Math.atan2(mapWithVertex.vertices[0].y - mapWithVertex.position.y, mapWithVertex.vertices[0].x - mapWithVertex.position.x)); + annoyingStuff.lasers(mapWithVertex.vertices[3], Math.atan2(mapWithVertex.vertices[3].y - mapWithVertex.position.y, mapWithVertex.vertices[3].x - mapWithVertex.position.x)); + } + if (Math.sin((2 * Math.PI * simulation.cycle) / (51)) > 0) { + annoyingStuff.lasers(mapWithVertex.vertices[1], Math.atan2(mapWithVertex.vertices[1].y - mapWithVertex.position.y, mapWithVertex.vertices[1].x - mapWithVertex.position.x)); + annoyingStuff.lasers(mapWithVertex.vertices[4], Math.atan2(mapWithVertex.vertices[4].y - mapWithVertex.position.y, mapWithVertex.vertices[4].x - mapWithVertex.position.x)); + } + if (Math.sin((2 * Math.PI * simulation.cycle) / (52)) > 0) { + annoyingStuff.lasers(mapWithVertex.vertices[2], Math.atan2(mapWithVertex.vertices[2].y - mapWithVertex.position.y, mapWithVertex.vertices[2].x - mapWithVertex.position.x)); + annoyingStuff.lasers(mapWithVertex.vertices[5], Math.atan2(mapWithVertex.vertices[5].y - mapWithVertex.position.y, mapWithVertex.vertices[5].x - mapWithVertex.position.x)); + } + + ctx.strokeStyle = "#000"; + ctx.lineWidth = 1.5; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.stroke(); // Draw it + ctx.setLineDash([]); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(0,0,0,0.07)"; + ctx.stroke(); // Draw it + + for (let i = 0; i < clothArray.length; i++) { + soft.draw(clothArray[i]); + soft.gravity(clothArray[i]); + } + + ctx.beginPath(); + ctx.fillStyle = "rgba(69, 69, 69, 0.1)"; + ctx.rect(-475, 175, 425, 2300); + ctx.rect(900, 175, 425, 2300); + ctx.rect(-875, 175, 400, 10000); + ctx.rect(-4200, -250, 2025, 2775); + ctx.fill(); + + ctx.beginPath(); + ctx.fillStyle = (m.pos.x < -725 && m.pos.y < 175) ? `rgba(68, 68, 68, ${Math.max(0.3, Math.min((-775 - m.pos.x) / 100, 0.99))})` : color.map; + ctx.rect(-875, 50, 175, 150); + ctx.fill(); + + }; + annoyingStuff.laserBoss(-1525, 1025); + spawn.pulsar(-1525, -850); + spawn.pulsar(1125, 1600); + spawn.pulsar(-250, 1600); + spawn.pulsar(-1450, 1600); + spawn.pulsar(-2950, 1750); + spawn.pulsar(-3375, 1750); + spawn.pulsar(-3825, 1300); + spawn.pulsar(-3825, 850); + spawn.pulsar(-3450, 50); + spawn.pulsar(-2925, 50); + spawn.pulsar(-1900, -400); + spawn.pulsar(-1200, -400); + + powerUps.addResearchToLevel() + powerUps.directSpawn(-775, 125, "tech"); + if (!level.isNextLevelPowerUps && powerUp[powerUp.length - 1]) powerUp[powerUp.length - 1].collisionFilter.mask = cat.map | cat.body | cat.powerUp + spawn.bodyRect(-875, 75, 25, 100); + let hardBody = body[body.length - 1]; + hardBody.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.powerUp + }, + flappyGon() { //community map by digin + level.announceMobTypes(); + simulation.inGameConsole(`flappy n-gon by Digin`); + setTimeout(() => { simulation.inGameConsole("gravity is a choice"); }, 1000); + setTimeout(() => { simulation.inGameConsole("everyone will fly"); }, 2000); + setTimeout(() => { simulation.inGameConsole("jump from the post and find out"); }, 3000); + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 8600; + level.exit.y = -1100; + level.defaultZoom = 1800; + simulation.zoomTransition(level.defaultZoom); + document.body.style.backgroundColor = "#55FF55"; + + var slimey = level.hazard(-200, -10, 9000, 10); + + // allow "flight" + + const old_playerOffGroundCheck = playerOffGroundCheck; + + playerOffGroundCheck = (event) => { + old_playerOffGroundCheck(event); + if (player.position.y < -300) { + m.onGround = true; + } + }; + + const oldNextLevel = level.nextLevel; + level.nextLevel = () => { // clear the flappy effects, because apparently there's no established api for this + playerOffGroundCheck = old_playerOffGroundCheck; + + level.nextLevel = oldNextLevel; + oldNextLevel(); + }; + + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); // standard bumps + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + spawn.mapRect(level.exit.x - 100, level.exit.y + 40, 200, 100); + + // room basis + spawn.mapRect(-200, 0, 9000, 100); + spawn.mapRect(-200, -1500, 9000, 100); + spawn.mapRect(-200, -1500, 100, 1500); + spawn.mapRect(8700, -1500, 100, 1500); + + // somewhat randomized flappy pylons + const pylon = 1500; // height of the entire pylon assembly + for (var i = 0; i < 10; i++) { + var xbasis = 700 + i * 750; + var window = 300 + (10 - i) * 50; + var toph = pylon - window - 400 + (Math.random() - 0.5) * 400 - i * 50; + if (i == 0) { // on the first one, the lower pile will always have a height of 300 + toph = pylon - window - 300; + } + spawn.mapRect(xbasis, -1500, 100, toph); + spawn.mapRect(xbasis, toph + window - pylon, 100, pylon - toph - window); + if (i < 9) { + spawn.randomMob(xbasis + 300, Math.random() * -1400); + } + else { + spawn.randomLevelBoss(xbasis + 300, Math.random() * -1400); } + if (i == 5) { + spawn.secondaryBossChance(xbasis + 300, Math.random() * -1400); + } + } + + level.custom = () => { + level.exit.drawAndCheck(); + player.onGround = true; + level.enter.draw(); + }; + const slimeRise = 0.15; + level.customTopLayer = () => { + slimey.height += slimeRise; + slimey.min.y -= slimeRise; + slimey.query(); + }; + powerUps.addResearchToLevel(); + }, + rings() { + level.announceMobTypes(); + simulation.inGameConsole(`rings by ThatLittleFrog`); + setTimeout(() => { + simulation.inGameConsole("go up"); + }, 2000); + level.setPosToSpawn(0, -2000); // spawn high up so you can go to the bottom of the lowest ring without tripping the too-low reset + level.exit.x = 0; + level.exit.y = -6400; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800; + simulation.zoomTransition(level.defaultZoom); + document.body.style.backgroundColor = "#d8dadf"; + + function mkrect(x, y, w, h) { + let who = body[body.length] = Bodies.rectangle(x, y, w, h, { + collisionFilter: { + category: cat.map, + mask: cat.body | cat.player | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + inertia: Infinity, //prevents rotation + isNotHoldable: true, + friction: 1, + frictionStatic: 1, + restitution: 0, + frictionAir: 1, + isStatic: true + }); + Composite.add(engine.world, who); + who.classType = "body"; + return body[body.length - 1]; + } + + function makeRing(x, y, linegth, thicc = 200) { // I don't feel like doing trigonometry, so linegth is slightly different from the radius + var _shape = [undefined, undefined, undefined, undefined]; + _shape[0] = mkrect(x - linegth / 2 - thicc, y - linegth / 2 - thicc, linegth, thicc); + _shape[1] = mkrect(x - linegth / 2 - thicc, y - linegth / 2, thicc, linegth); + _shape[2] = mkrect(x - linegth / 2, y + linegth / 2 - thicc, linegth, thicc); + _shape[3] = mkrect(x + linegth / 2 - thicc, y - linegth / 2 - thicc, thicc, linegth - thicc * 2); + let ret = { + shape: _shape, + x: x, + y: y, + r: 0, + rot(ang) { + this.r = ang; + let offs = 0; + for (let shape of this.shape) { + offs += Math.PI / 2; + Matter.Body.setAngle(shape, ang); + if (shape == this.shape[3]) { + Matter.Body.setPosition(shape, { + x: this.x + Math.cos(ang + offs) * (linegth / 2 - thicc / 2) - Math.cos(ang + offs + Math.PI / 2) * thicc, + y: this.y + Math.sin(ang + offs) * (linegth / 2 - thicc / 2) - Math.sin(ang + offs + Math.PI / 2) * thicc + }); + } + else { + Matter.Body.setPosition(shape, { + x: this.x + Math.cos(ang + offs) * (linegth / 2 - thicc / 2), + y: this.y + Math.sin(ang + offs) * (linegth / 2 - thicc / 2) + }); + } + } + }, + rotBy(ang) { + this.rot(this.r + ang); + } + }; + ret.rot(0); + return ret; + } + + var inner = makeRing(level.enter.x, level.enter.y, 1000); + var mid = makeRing(level.enter.x, level.enter.y, 2500); + var mid2 = makeRing(level.enter.x, level.enter.y, 4000); + var out = makeRing(level.enter.x, level.enter.y, 6000); + + spawn.randomMob(level.enter.x + 250, level.enter.y); + + spawn.randomMob(level.enter.x + 1250, level.enter.y); + spawn.randomMob(level.enter.x - 1250, level.enter.y); + spawn.randomMob(level.enter.x, level.enter.y + 1250); + spawn.randomMob(level.enter.x, level.enter.y - 1250); + spawn.randomMob(level.enter.x + 1250, level.enter.y + 500); + spawn.randomMob(level.enter.x - 1250, level.enter.y + 500); + spawn.randomMob(level.enter.x + 500, level.enter.y + 1250); + spawn.randomMob(level.enter.x + 500, level.enter.y - 1250); + + spawn.randomMob(level.enter.x + 2750, level.enter.y); + spawn.randomMob(level.enter.x - 2750, level.enter.y); + spawn.randomMob(level.enter.x, level.enter.y + 2750); + spawn.randomMob(level.enter.x, level.enter.y - 2750); + spawn.randomMob(level.enter.x + 2750, level.enter.y + 500); + spawn.randomMob(level.enter.x - 2750, level.enter.y + 500); + spawn.randomMob(level.enter.x + 500, level.enter.y + 2750); + spawn.randomMob(level.enter.x + 500, level.enter.y - 2750); + + spawn.randomLevelBoss(level.enter.x, level.enter.y - 4250); + spawn.secondaryBossChance(level.enter.x, level.enter.y + 4250); + + level.custom = () => { + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + inner.rotBy(0.01); + mid.rotBy(-0.005); + mid2.rotBy(0.003); + out.rotBy(-0.002); + }; + + powerUps.addResearchToLevel(); + }, + trial() { // trial, collab between Cirryn and Tarantula Hawk + simulation.inGameConsole(`trial by Cirryn and Tarantula Hawk`); + level.setPosToSpawn(0, -50); + level.exit.x = 4150; + level.exit.y = -30; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800; + simulation.zoomTransition(level.defaultZoom); + document.body.style.backgroundColor = "#d8dadf"; + const button = level.button(2000, 0); + const door = level.door(3930, -300, 40, 300, 300, 10); + door.isClosing = false; + var didTrialBegin = false; + + const customMob = { + assassin(x, y) { // modified slasher + mobs.spawn(x, y, 3, 30, "black"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.0008 * simulation.accelScale; + me.torqueMagnitude = 0.00002 * me.inertia * (Math.random() > 0.5 ? -1 : 1); + me.frictionStatic = 0; + me.frictionAir = 0.08; + me.delay = 120 * simulation.CDScale; + me.cd = 0; + spawn.shield(me, x, y); + me.damageReduction = 0; + const start = window.performance.now(); // they only last ~ten seconds + const lifespan = 15000 + 700 * (Math.random() - 0.5); + me.onDamage = function () { + Matter.Body.setAngularVelocity(me, me.angularVelocity + 1); + }; + me.do = function () { + this.checkStatus(); + this.alwaysSeePlayer(); + this.attraction(); + this.health = 1 - (window.performance.now() - start) / lifespan; + if (this.health < 0) { + this.death(); + } + Matter.Body.setAngularVelocity(me, me.angularVelocity + 0.05); + }; + }, + mercenary(x, y) { // fast boi + mobs.spawn(x, y, 3, 60, "white"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.accelMag = 0.001 * simulation.accelScale; + me.torqueMagnitude = 0.00001 * me.inertia * (Math.random() > 0.5 ? -1 : 1); + me.frictionStatic = 0; + me.frictionAir = 0.03; + me.delay = 120 * simulation.CDScale; + me.cd = 0; + spawn.shield(me, x, y); + me.damageReduction = 0; + const start = window.performance.now(); // they only last ~ten seconds + const lifespan = 25000 + 700 * (Math.random() - 0.5); + me.onDamage = function () { + Matter.Body.setAngularVelocity(me, me.angularVelocity + 1); + }; + me.do = function () { + this.checkStatus(); + this.attraction(); + this.health = 1 - (window.performance.now() - start) / lifespan; + if (this.health < 0) { + this.death(); + } + this.alwaysSeePlayer(); + }; + } + // eventually maybe add more custom mob types + }; + + function randomWave(count, source) { // generates a wave list from a source + // checks in spawn first, then customMob, for the sources + var ret = []; + for (var i = 0; i < count; i++) { + var pick = source[Math.floor(Math.random() * source.length)]; + if (spawn[pick]) { + ret.push(spawn[pick]); + } + else if (customMob[pick]) { + ret.push(customMob[pick]); + } + } + return ret; + } + + function wave(mobs) { // takes a list of functions that accept x,y coordinates to spawn a mob and spawns them in the ceiling + for (var i = 0; i < mobs.length; i++) { + var x = 1000 + 2400 * i / mobs.length + 200 * (Math.random() - 0.5); + var y = -950 - 100 * Math.random(); + mobs[i](x, y); + } + const ammoCount = Math.random() * (10 - simulation.difficulty / 4); + for (var i = 0; i < ammoCount; i++) { + powerUps.spawn(3300, -1000, "ammo"); + } + } + + level.custom = () => { + door.openClose(); + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + button.query(); + button.draw(); + door.draw(); + if (!button.isUp && !didTrialBegin) { + didTrialBegin = true; + simulation.inGameConsole('The Trial has begun.'); + + setTimeout(() => { + simulation.inGameConsole('first wave (domitable)'); + wave(randomWave(2 + simulation.difficulty * 0.1, spawn.fullPickList)); + }, 3000); + + setTimeout(() => { + simulation.inGameConsole('second wave (domitable)'); + wave(randomWave(2 + simulation.difficulty * 0.1, spawn.fullPickList)); + }, 13000); + + setTimeout(() => { + simulation.inGameConsole('third wave (indomitable)'); + wave(randomWave(4, ["assassin"])); + }, 23000); + + setTimeout(() => { + simulation.inGameConsole('fourth wave (domitable)'); + wave(randomWave(4 + simulation.difficulty / 2, spawn.fullPickList)); + }, 39000); + + setTimeout(() => { + simulation.inGameConsole('fifth wave (domitable)'); + wave(randomWave(4 + simulation.difficulty / 2, spawn.fullPickList)); + }, 49000); + + setTimeout(() => { + simulation.inGameConsole('sixth wave (indomitable)'); + wave(randomWave(7, ["mercenary"])); + }, 59000); + + setTimeout(() => { + simulation.inGameConsole('seventh wave (boss)'); + spawn.randomLevelBoss(700, -1000); + var mainBoss = mob[mob.length - 1]; + mainBoss.oldOnDeath = mainBoss.onDeath; + mainBoss.onDeath = () => { + door.isClosing = false; + powerUps.spawn(4150, -30, "tech"); + powerUps.spawn(4150, -30, "tech"); + mainBoss.oldOnDeath(); + } + spawn.secondaryBossChance(3500, -1000); + }, 86000); + + door.isClosing = true; + } + }; + + spawn.mapRect(-100, 0, 10000, 10000); // the left half of the room + spawn.mapRect(-10000, -300, 9900, 10000); + spawn.mapRect(-100, -300, 400, 100); + spawn.mapRect(200, -800, 100, 500); + spawn.mapRect(200, -800, 500, 100); + spawn.mapRect(600, -1000, 100, 200); + + spawn.mapRect(600, -1100, 3000, 100); // the ceiling + + spawn.mapRect(3500, -1000, 100, 200); // the right half of the room + spawn.mapRect(3500, -800, 500, 100); + spawn.mapRect(3900, -800, 100, 500); + spawn.mapRect(3900, -300, 400, 100); + spawn.mapRect(4300, -300, 10000, 10000); + + for (var i = 0; i < 4; i++) { // "door" at the entrance + spawn.bodyRect(200, -200 + i * 50, 20, 50); + } + + for (var i = 0; i < 5; i++) { // some random rubble in the first half of the room + spawn.bodyRect(400 + Math.random() * 1000, -200, 40 + Math.random() * 40, 40 + Math.random() * 40); } + + powerUps.addResearchToLevel(); //needs to run after mobs are spawneds }, // ******************************************************************************************************** // ******************************************************************************************************** @@ -32226,27 +35083,27 @@ const level = { const buttonDoor = level.button(400, 0) let instruction = 0 - level.trainingText(`use your field to pick up the gun power up`) + level.trainingText(`use your field to pick up ${powerUps.orb.gun()}`) level.custom = () => { if (instruction === 0 && simulation.isChoosing) { instruction++ - level.trainingText(`use your field to pick up the gun power up + level.trainingText(`use your field to pick up ${powerUps.orb.gun()}
choose a gun`) } else if (instruction === 1 && !simulation.isChoosing) { instruction++ - level.trainingText(`use your field to pick up the gun power up + level.trainingText(`use your field to pick up ${powerUps.orb.gun()}
choose a gun

use the left mouse button to shoot the mobs`) } else if (instruction === 2 && mob.length === 0) { instruction++ - level.trainingText(`use your field to pick up the gun power up + level.trainingText(`use your field to pick up ${powerUps.orb.gun()}
choose a gun
use the left mouse button to shoot the mobs

drop a block on the red button to open the door`) } else if (instruction === 3 && !door.isClosing) { instruction++ - level.trainingText(`use your field to pick up the gun power up + level.trainingText(`use your field to pick up ${powerUps.orb.gun()}
choose a gun
use the left mouse button to shoot the mobs
put a block on the red button to open the door
`) @@ -32448,16 +35305,15 @@ const level = { spawn.mapRect(1375, -16, 50, 50); spawn.mapRect(1400, -8, 50, 25); spawn.mapRect(750, -24, 650, 100); - powerUps.directSpawn(875, -40, "heal", false, null, 15); - powerUps.directSpawn(1075, -50, "heal", false, null, 25); - powerUps.directSpawn(1275, -65, "heal", false, null, 35); + powerUps.directSpawn(875, -40, "heal", false, 15); + powerUps.directSpawn(1075, -50, "heal", false, 25); + powerUps.directSpawn(1275, -65, "heal", false, 35); const door = level.door(1612.5, -175, 25, 190, 185, 3) spawn.mapRect(1600, -1200, 500, 850); //exit roof spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, nailGun() { - level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; @@ -32467,7 +35323,7 @@ const level = { level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor - b.removeAllGuns(); + b.resetAllGuns(); b.giveGuns("nail gun") b.guns[b.activeGun].ammo = 0 simulation.updateGunHUD(); @@ -32552,7 +35408,6 @@ const level = { spawn.mapRect(1600, -600, 425, 250); }, shotGun() { - level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; @@ -32562,7 +35417,7 @@ const level = { level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor - b.removeAllGuns(); + b.resetAllGuns(); b.giveGuns("shotgun") // b.guns[b.activeGun].ammo = 0 // simulation.updateGunHUD(); @@ -32634,7 +35489,6 @@ const level = { spawn.mapRect(1600, -600, 425, 250); }, superBall() { - level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; @@ -32644,7 +35498,7 @@ const level = { level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor - b.removeAllGuns(); + b.resetAllGuns(); b.giveGuns("super balls") // b.guns[b.activeGun].ammo = 0 // simulation.updateGunHUD(); @@ -32718,7 +35572,6 @@ const level = { spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, matterWave() { //fire wave through the map to kill mosb - level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; @@ -32728,7 +35581,7 @@ const level = { level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor - b.removeAllGuns(); + b.resetAllGuns(); b.giveGuns("wave") // b.guns[b.activeGun].ammo = 0 // simulation.updateGunHUD(); @@ -32805,7 +35658,6 @@ const level = { spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, missile() { //fire a missile to kill mobs and trigger button - level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; @@ -32815,7 +35667,7 @@ const level = { level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor - b.removeAllGuns(); + b.resetAllGuns(); b.giveGuns("missiles") // b.guns[b.activeGun].ammo = 0 // simulation.updateGunHUD(); @@ -32894,7 +35746,6 @@ const level = { spawn.mapRect(2025, -2800, 1075, 2450); }, stack() { //stack blocks to get to exit - level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; @@ -32904,7 +35755,7 @@ const level = { level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor - b.removeAllGuns(); + b.resetAllGuns(); let instruction = 0 level.trainingText(`use your field to stack the blocks`) @@ -32946,7 +35797,6 @@ const level = { spawn.mapVertex(1300, 0, "400 0 -500 0 -300 -125 400 -125"); //base }, mine() { //kill mobs and tack their bodies - level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(300, -50); //normal spawn spawn.mapRect(250, -10, 100, 20); //small platform for player level.exit.x = 1775; @@ -32956,7 +35806,7 @@ const level = { level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor - b.removeAllGuns(); + b.resetAllGuns(); b.giveGuns("mine") let instruction = 0 @@ -33030,7 +35880,6 @@ const level = { spawn.mapVertex(1300, 0, "400 0 -600 0 -300 -125 400 -125"); //base }, grenades() { //jump at the top of the elevator's path to go extra high - level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(0, -50); //normal spawn spawn.mapRect(-50, -10, 100, 20); //small platform for player level.exit.x = 1900; @@ -33040,7 +35889,7 @@ const level = { level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor - b.removeAllGuns(); + b.resetAllGuns(); b.giveGuns("grenades") const elevator1 = level.elevator(550, -100, 180, 25, -840, 0.003, { @@ -33150,7 +35999,6 @@ const level = { spawn.nodeGroup(1200, -1500, "grenadier", 7); }, harpoon() { //jump at the top of the elevator's path to go extra high - level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(0, -50); //normal spawn spawn.mapRect(-50, -10, 100, 20); //small platform for player level.exit.x = 1900; @@ -33160,7 +36008,7 @@ const level = { level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor - b.removeAllGuns(); + b.resetAllGuns(); b.giveGuns("harpoon") let instruction = 0 diff --git a/js/lore.js b/js/lore.js index ff5372e5..16bccb33 100644 --- a/js/lore.js +++ b/js/lore.js @@ -38,7 +38,7 @@ const lore = { if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" // document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" - simulation.makeTextLog(`lore.unlockTesting()`, Infinity); + simulation.inGameConsole(`lore.unlockTesting()`, Infinity); sound.portamento(50) sound.portamento(83.333) @@ -48,7 +48,7 @@ const lore = { color: "#f20", voice: undefined, text: function (say) { - simulation.makeTextLog(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); + simulation.inGameConsole(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); lore.talkingColor = this.color const utterance = new SpeechSynthesisUtterance(say); utterance.lang = "en-AU" //"en-IN"; //de-DE en-GB fr-FR en-US en-AU @@ -61,7 +61,7 @@ const lore = { voice: undefined, text: function (say) { if (level.levels[level.onLevel] === undefined) { //only talk if on the lore level (which is undefined because it is popped out of the level.levels array) - simulation.makeTextLog(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); + simulation.inGameConsole(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); lore.talkingColor = this.color if (lore.isSpeech) { const utterance = new SpeechSynthesisUtterance(say); @@ -95,7 +95,7 @@ const lore = { color: "#f20", text: function (say) { if (level.levels[level.onLevel] === undefined) { //only talk if on the lore level (which is undefined because it is popped out of the level.levels array) - simulation.makeTextLog(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); + simulation.inGameConsole(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); lore.talkingColor = this.color if (lore.isSpeech) { utterance = new SpeechSynthesisUtterance(say); @@ -358,7 +358,7 @@ const lore = { setInterval(() => { if (Math.random() < 0.5) { spawn[spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]](1000 * (Math.random() - 0.5), -500 + 200 * (Math.random() - 0.5)); - level.difficultyIncrease(simulation.difficultyMode) + // level.difficultyIncrease(simulation.difficultyMode) } else { spawn.randomLevelBoss(500 * (Math.random() - 0.5), -500 + 200 * (Math.random() - 0.5)) } @@ -622,8 +622,8 @@ const lore = { console.log(`https://www.openstreetmap.org/#map=18/${latitude}/${longitude}`) console.log(`Latitude: ${latitude} °, Longitude: ${longitude} °`) lore.miriam.text("We tracked the location down to this Latitude and Longitude:") - simulation.makeTextLog(`Latitude: ${latitude} °, Longitude: ${longitude} °`, Infinity); - simulation.makeTextLog(`https://www.openstreetmap.org/#map=18/${latitude}/${longitude}`, Infinity); + simulation.inGameConsole(`Latitude: ${latitude} °, Longitude: ${longitude} °`, Infinity); + simulation.inGameConsole(`https://www.openstreetmap.org/#map=18/${latitude}/${longitude}`, Infinity); } function error() { @@ -693,9 +693,9 @@ const lore = { setInterval(() => { spawn[spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]](1000 * (Math.random() - 0.5), -500 + 200 * (Math.random() - 0.5)); }, 500); //every 1/2 seconds - setInterval(() => { - level.difficultyIncrease(simulation.difficultyMode) - }, 5000); //every 5 seconds + // setInterval(() => { + // level.difficultyIncrease(simulation.difficultyMode) + // }, 5000); //every 5 seconds }, () => { lore.talkingColor = "#dff"; @@ -769,13 +769,13 @@ const lore = { }, () => { lore.miriam.text("We can spawn power ups with this command:") - simulation.makeTextLog(`powerUps.spawn(player.position.x, player.position.y - 100, "heal")`, Infinity); + simulation.inGameConsole(`powerUps.spawn(player.position.x, player.position.y - 100, "heal")`, Infinity); powerUps.spawn(player.position.x, player.position.y - 100, "heal") }, () => { setTimeout(() => { lore.miriam.text("or we can make a bunch of them:") - simulation.makeTextLog(`for (let i = 0; i < 100; i++) powerUps.spawn(0, 500, "coupling")`, Infinity); + simulation.inGameConsole(`for (let i = 0; i < 100; i++) powerUps.spawn(0, 500, "coupling")`, Infinity); for (let i = 0; i < 100; i++) powerUps.spawn(5 - 10 * Math.random(), -500 * Math.random(), "coupling") }, 2000); }, @@ -798,9 +798,9 @@ const lore = { count++ if (mob.length === 0 || count > 3600 + 900 * mob.length) { lore.miriam.text("I'll spawn some more power ups for you.") - simulation.makeTextLog(`for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100, "heal")`, Infinity); + simulation.inGameConsole(`for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100, "heal")`, Infinity); for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100 - i * 20, "heal") - simulation.makeTextLog(`for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100, "ammo")`, Infinity); + simulation.inGameConsole(`for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100, "ammo")`, Infinity); for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100 - i * 20, "ammo") spawn.dragonFlyBoss(-1400, -300); spawn.dragonFlyBoss(1400, -300); @@ -820,9 +820,9 @@ const lore = { count++ if (mob.length === 0 || count > 3600 + 900 * mob.length) { lore.anand.text("DragonFlyBoss is my favorite.") - simulation.makeTextLog(`for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100, "heal")`, Infinity); + simulation.inGameConsole(`for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100, "heal")`, Infinity); for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100 - i * 20, "heal") - simulation.makeTextLog(`for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100, "ammo")`, Infinity); + simulation.inGameConsole(`for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100, "ammo")`, Infinity); for (let i = 0; i < 10; i++) powerUps.spawn(player.position.x, player.position.y - 100 - i * 20, "ammo") spawn.historyBoss(0, -400); spawn.powerUpBossBaby(-1500, -100); @@ -841,7 +841,7 @@ const lore = { count++ if (mob.length === 0 || count > 3600 + 900 * mob.length) { lore.miriam.text("Here are some extra tech.") - simulation.makeTextLog(`for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100, "tech")`, Infinity); + simulation.inGameConsole(`for (let i = 0; i < 6; i++) powerUps.spawn(player.position.x, player.position.y - 100, "tech")`, Infinity); for (let i = 0; i < 6; i++) powerUps.spawn(0, -200 - i * 40, "tech") spawn.historyBoss(0, -400); spawn.blinkBoss(-1400, -300); @@ -902,7 +902,7 @@ const lore = { x: 0, y: -500 }) - simulation.makeTextLog(`Matter.Body.setPosition(player, { x: 0, y: -500 })`, 180); + simulation.inGameConsole(`Matter.Body.setPosition(player, { x: 0, y: -500 })`, 180); }, 1500); } else if (m.alive) { @@ -1180,7 +1180,7 @@ const lore = { // return product_Range(r + 1, n) / product_Range(1, n - r); // } // } - // simulation.makeTextLog(`n = ${combinations(tech.tech.length + b.guns.length + m.fieldUpgrades.length, 50).toExponential(10)}`, Infinity); + // simulation.inGameConsole(`n = ${combinations(tech.tech.length + b.guns.length + m.fieldUpgrades.length, 50).toExponential(10)}`, Infinity); // lore.miriam.text(`There are roughly 5 times 10 to the 60 possible combinations. `) // }, // () => { lore.miriam.text("Even if each simulation took 1 nano-second,") }, diff --git a/js/mob.js b/js/mob.js index 349d6875..01a7f049 100644 --- a/js/mob.js +++ b/js/mob.js @@ -794,27 +794,6 @@ const mobs = { } } }, - // invulnerability() { - // if (this.isInvulnerable) { - // if (this.invulnerabilityCountDown > 0) { - // this.invulnerabilityCountDown-- - // //graphics //draw a super shield? - // ctx.beginPath(); - // let vertices = this.vertices; - // ctx.moveTo(vertices[0].x, vertices[0].y); - // for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); - // ctx.lineTo(vertices[0].x, vertices[0].y); - // ctx.lineWidth = 20; - // // ctx.fillStyle = `rgba(${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},0.5)` - // // ctx.fill(); - // ctx.strokeStyle = "rgba(255,255,255,0.4)"; - // ctx.stroke(); - // } else { - // this.isInvulnerable = false - // this.damageReduction = this.startingDamageReduction - // } - // } - // }, grow() { if (this.seePlayer.recall) { if (this.radius < 80) { @@ -926,10 +905,7 @@ const mobs = { spawn.bomb(this.position.x, this.position.y + this.radius * 0.7, 9 + Math.ceil(this.radius / 15), 5); //add spin and speed Matter.Body.setAngularVelocity(mob[mob.length - 1], (Math.random() - 0.5) * 0.5); - Matter.Body.setVelocity(mob[mob.length - 1], { - x: this.velocity.x, - y: this.velocity.y - }); + Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x, y: this.velocity.y }); //spin for mob as well Matter.Body.setAngularVelocity(this, (Math.random() - 0.5) * 0.25); } @@ -1078,7 +1054,7 @@ const mobs = { }) } } else if (tech.isMobLowHealth && this.health < 0.25) { - dmg *= 3.22 + dmg *= 3 simulation.ephemera.push({ name: "damage outline", @@ -1109,7 +1085,7 @@ const mobs = { if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000 dmg *= this.damageReduction //energy and heal drain should be calculated after damage boosts - if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon + if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon * level.isReducedRegen dmg /= Math.sqrt(this.mass) } @@ -1161,13 +1137,26 @@ const mobs = { this.onDeath(this); //custom death effects this.removeConsBB(); this.alive = false; //triggers mob removal in mob[i].replace(i) + // console.log(this.shieldCount) if (this.isDropPowerUp) { - // if (true) { - // //killing a mob heals for the last damage you took - - - // } + if (level.isMobDeathHeal) { + for (let i = 0; i < mob.length; i++) { + if (Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) < 500000 && mob[i].alive) { //700 + if (mob[i].health < 1) { + mob[i].health += 0.33 + this.isBoss + if (mob[i].health > 1) mob[i].health = 1 + simulation.drawList.push({ + x: mob[i].position.x, + y: mob[i].position.y, + radius: mob[i].radius + 20, + color: "rgba(0,255,100,0.5)", + time: 10 + }); + } + } + } + } if (this.isSoonZombie) { //spawn zombie on death this.leaveBody = false; let count = 5 //delay spawn cycles @@ -1197,6 +1186,34 @@ const mobs = { }); } } + if (level.isMobRespawn && !this.isBoss && 0.33 > Math.random()) { + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: 30, + color: `#fff`, + time: 20 + }); + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: 20, + color: `#fff`, + time: 40 + }); + simulation.drawList.push({ + x: this.position.x, + y: this.position.y, + radius: 10, + color: `#fff`, + time: 60 + }); + setTimeout(() => { + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + const size = 16 + Math.ceil(Math.random() * 15) + spawn[pick](this.position.x, this.position.y, size); + }, 1000); + } if (tech.healSpawn && Math.random() < tech.healSpawn) { powerUps.spawn(this.position.x + 20 * (Math.random() - 0.5), this.position.y + 20 * (Math.random() - 0.5), "heal"); simulation.drawList.push({ @@ -1222,13 +1239,16 @@ const mobs = { }); } - if (tech.deathSkipTime && !m.isBodiesAsleep) { + if (tech.isVerlet && !m.isBodiesAsleep) { requestAnimationFrame(() => { - simulation.timePlayerSkip((this.isBoss ? 45 : 25) * tech.deathSkipTime) + simulation.timePlayerSkip(this.isBoss ? 60 : 30) simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations }); //wrapping in animation frame prevents errors, probably } - if (tech.isEnergyLoss) m.energy *= 0.8; + if (tech.isEnergyLoss) { + m.energy -= 0.05; + if (m.energy < 0) m.energy = 0 + } powerUps.spawnRandomPowerUp(this.position.x, this.position.y); m.lastKillCycle = m.cycle; //tracks the last time a kill was made, mostly used in simulation.checks() mobs.mobDeaths++ @@ -1263,6 +1283,10 @@ const mobs = { bullet[bullet.length - 1].endCycle = simulation.cycle + 900 //15 seconds this.leaveBody = false; // no body since it turned into the bot } + if (tech.isMobDeathImmunity) { + const immuneTime = 360 + if (m.immuneCycle < m.cycle + immuneTime) m.immuneCycle = m.cycle + immuneTime; //player is immune to damage + } if (tech.isAddRemoveMaxHealth) { if (!this.isBoss) { const amount = 0.0025 @@ -1276,34 +1300,12 @@ const mobs = { m.setMaxHealth(); } } - - // if (this.isBoss && this.isDropPowerUp) { - // powerUps.spawn(this.position.x + 20, this.position.y, "tech", false) - // powerUps.spawn(this.position.x - 20, this.position.y, "research", false) - // powerUps.spawn(this.position.x - 40, this.position.y, "research", false) - // powerUps.spawn(this.position.x + 40, this.position.y, "research", false) - // powerUps.spawn(this.position.x, this.position.y + 20, "research", false) - // powerUps.spawn(this.position.x, this.position.y - 20, "heal", false) - // powerUps.spawn(this.position.x, this.position.y + 40, "heal", false) - // powerUps.spawn(this.position.x, this.position.y - 40, "heal", false) - // } else { - // const amount = 0.005 - // if (tech.isEnergyHealth) { - // if (m.maxEnergy > amount) { - // tech.healMaxEnergyBonus -= amount - // m.setMaxEnergy(); - // } - // } else if (m.maxHealth > amount) { - // tech.extraMaxHealth -= amount //decrease max health - // m.setMaxHealth(); - // } - // } } if (tech.cloakDuplication && !this.isBoss) { tech.cloakDuplication -= 0.01 powerUps.setPowerUpMode(); //needed after adjusting duplication chance } - } else if (tech.isShieldAmmo && this.shield && !this.isExtraShield && this.isDropPowerUp) { + } else if (tech.isShieldAmmo && this.shield && this.shieldCount === 1) { let type = tech.isEnergyNoAmmo ? "heal" : "ammo" if (Math.random() < 0.4) { type = "heal" diff --git a/js/player.js b/js/player.js index e65d5af4..9bd7a427 100644 --- a/js/player.js +++ b/js/player.js @@ -8,7 +8,7 @@ const m = { // let vector = Vertices.fromPath("0 40 50 40 50 115 0 115 30 130 20 130"); //player as a series of vertices let vertices = Vertices.fromPath("0,40, 50,40, 50,115, 30,130, 20,130, 0,115, 0,40"); //player as a series of vertices playerBody = Bodies.fromVertices(0, 0, vertices); - jumpSensor = Bodies.rectangle(0, 46, 36, 6, { + jumpSensor = Bodies.rectangle(0, 46, 36, 6, { //(0, 46, 50, 6, { //for wall jumping //this sensor check if the player is on the ground to enable jumping sleepThreshold: 99999999999, isSensor: true @@ -83,16 +83,21 @@ const m = { Fx: 0.016, //run Force on ground // jumpForce: 0.42, setMovement() { - // m.Fx = 0.08 / mass * tech.squirrelFx + // console.log(player.mass) // m.FxAir = 0.4 / mass / mass - m.Fx = tech.baseFx * m.fieldFx * tech.squirrelFx * (tech.isFastTime ? 1.5 : 1) / player.mass //base player mass is 5 - m.jumpForce = tech.baseJumpForce * m.fieldJump * tech.squirrelJump * (tech.isFastTime ? 1.13 : 1) / player.mass / player.mass //base player mass is 5 + m.Fx = tech.baseFx * m.fieldFx * m.squirrelFx * (tech.isFastTime ? 1.5 : 1) / player.mass //base player mass is 5 + m.jumpForce = tech.baseJumpForce * m.fieldJump * m.squirrelJump * (tech.isFastTime ? 1.13 : 1) / player.mass / player.mass //base player mass is 5 }, FxAir: 0.016, // 0.4/5/5 run Force in Air yOff: 70, yOffGoal: 70, onGround: false, //checks if on ground or in air lastOnGroundCycle: 0, //use to calculate coyote time + coyoteCycles: 5, + hardLanding: 130, + squirrelFx: 1, + squirrelJump: 1, + velocitySmooth: { x: 0, y: 0 },//use for drawing skin's velocity gel tail standingOn: undefined, numTouching: 0, crouch: false, @@ -210,10 +215,7 @@ const m = { m.crouch = true; m.yOffGoal = m.yOffWhen.crouch; if ((playerHead.position.y - player.position.y) < 0) { - Matter.Body.setPosition(playerHead, { - x: player.position.x, - y: player.position.y + 9.1740767 - }) + Matter.Body.setPosition(playerHead, { x: player.position.x, y: player.position.y + 9.1740767 }) } } }, @@ -222,14 +224,12 @@ const m = { m.crouch = false; m.yOffGoal = m.yOffWhen.stand; if ((playerHead.position.y - player.position.y) > 0) { - Matter.Body.setPosition(playerHead, { - x: player.position.x, - y: player.position.y - 30.28592321 - }) + Matter.Body.setPosition(playerHead, { x: player.position.x, y: player.position.y - 30.28592321 }) } } }, hardLandCD: 0, + hardLandCDScale: 1, checkHeadClear() { if (Matter.Query.collides(headSensor, map).length > 0) { return false @@ -239,14 +239,9 @@ const m = { }, buttonCD_jump: 0, //cool down for player buttons jump() { - // if (!m.onGround) m.lastOnGroundCycle = 0 //m.cycle - tech.coyoteTime m.buttonCD_jump = m.cycle; //can't jump again until 20 cycles pass //apply a fraction of the jump force to the body the player is jumping off of - Matter.Body.applyForce(m.standingOn, m.pos, { - x: 0, - y: m.jumpForce * 0.12 * Math.min(m.standingOn.mass, 5) - }); - + Matter.Body.applyForce(m.standingOn, m.pos, { x: 0, y: m.jumpForce * 0.12 * Math.min(m.standingOn.mass, 5) }); player.force.y = -m.jumpForce; //player jump force Matter.Body.setVelocity(player, { //zero player y-velocity for consistent jumps x: player.velocity.x, @@ -260,7 +255,7 @@ const m = { if (!(input.down) && m.checkHeadClear() && m.hardLandCD < m.cycle) m.undoCrouch(); } else if (input.down || m.hardLandCD > m.cycle) { m.doCrouch(); //on ground && not crouched and pressing s or down - } else if (input.up && m.buttonCD_jump + 20 < m.cycle && m.yOffWhen.stand > 23) { + } else if (input.up && m.buttonCD_jump + 20 < m.cycle) { m.jump() } const moveX = player.velocity.x - m.moverX //account for mover platforms @@ -290,8 +285,7 @@ const m = { }, airControl() { //check for coyote time jump - // if (input.up && m.buttonCD_jump + 20 + tech.coyoteTime < m.cycle && m.yOffWhen.stand > 23 && m.lastOnGroundCycle + tech.coyoteTime > m.cycle) m.jump() - if (input.up && m.buttonCD_jump + 20 < m.cycle && m.yOffWhen.stand > 23 && m.lastOnGroundCycle + 5 > m.cycle) m.jump() + if (input.up && m.buttonCD_jump + 20 < m.cycle && m.lastOnGroundCycle + m.coyoteCycles > m.cycle) m.jump() //check for short jumps //moving up //recently pressed jump //but not pressing jump key now if (m.buttonCD_jump + 60 > m.cycle && !(input.up) && m.Vy < 0) { @@ -348,7 +342,7 @@ const m = { } if ( !tech.tech[i].isNonRefundable && - !tech.tech[i].isFromAppliedScience && + // !tech.tech[i].isFromAppliedScience && !tech.tech[i].isAltRealityTech ) { totalTech += tech.tech[i].count @@ -366,6 +360,7 @@ const m = { tech.duplication = 0; tech.extraMaxHealth = 0; tech.totalCount = 0; + tech.removeCount = 0; const randomBotCount = b.totalBots() b.zeroBotCount() //remove all bullets, respawn bots @@ -384,14 +379,17 @@ const m = { 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; + if (b.guns[i].ammo !== Infinity) { + b.guns[i].ammo = 0; + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; + } } //give random guns for (let i = 0; i < totalGuns; i++) b.giveGuns() //randomize ammo based on ammo/ammoPack count for (let i = 0, len = b.inventory.length; i < len; i++) { - if (b.guns[b.inventory[i]].ammo !== Infinity) b.guns[b.inventory[i]].ammo = Math.max(0, Math.floor(ammoCount / b.inventory.length * b.guns[b.inventory[i]].ammoPack * (1.15 + 0.3 * (Math.random() - 0.5)))) + if (b.guns[b.inventory[i]].ammo !== Infinity) b.guns[b.inventory[i]].ammo = Math.max(0, Math.floor(ammoCount / b.inventory.length * b.guns[b.inventory[i]].ammoPack * (2.5 + 0.3 * (Math.random() - 0.5)))) } // console.log(b.activeGun) //randomize tech @@ -414,7 +412,7 @@ const m = { m.drop(); if (simulation.paused) build.pauseGrid() //update the build when paused }, - dmgScale: null, //scales all damage, but not raw .dmg //set in levels.setDifficulty + dmgScale: null, //scales all damage, but not raw .dmg death() { if (tech.isImmortal) { //if player has the immortality buff, spawn on the same level with randomized damage //remove immortality tech @@ -445,7 +443,7 @@ const m = { simulation.clearNow = true; //triggers a map reset m.switchWorlds() simulation.isTextLogOpen = true; - simulation.makeTextLog(`simulation.amplitude = 0.${len - i - 1}`, swapPeriod); + simulation.inGameConsole(`simulation.amplitude = 0.${len - i - 1}`, swapPeriod); simulation.isTextLogOpen = false; simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = `rgba(255,255,255,${(i + 1) * (i + 1) * 0.006})`; @@ -458,7 +456,7 @@ const m = { ctx.clearRect(0, 0, canvas.width, canvas.height); } simulation.isTextLogOpen = true; - simulation.makeTextLog("simulation.amplitude = null"); + simulation.inGameConsole("simulation.amplitude = null"); tech.isImmortal = false //disable future immortality }, 6 * swapPeriod); } else if (m.alive) { //normal death code here @@ -472,13 +470,10 @@ const m = { m.displayHealth(); document.getElementById("text-log").style.display = "none" document.getElementById("fade-out").style.opacity = 0.9; //slowly fade to 90% white on top of canvas - // build.shareURL(false) setTimeout(function () { Composite.clear(engine.world); Engine.clear(engine); simulation.splashReturn(); - //if you die after clearing fewer than 4 levels the difficulty settings automatically opens - if ((level.levelsCleared < 4 || level.levelsCleared > 12) && !simulation.isTraining && !simulation.isCheating) document.getElementById("settings-details").open = true; }, 5000); } }, @@ -488,7 +483,7 @@ const m = { for (i = 0, len = b.inventory.length; i < len; i++) gunList.push(b.inventory[i]) const techList = [] //store tech names for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) techList.push(i) + if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) techList.push(tech.tech[i].name) } if (techList.length) { localSettings.entanglement = { @@ -541,9 +536,15 @@ const m = { }, baseHealth: 1, setMaxHealth(isMessage) { - m.maxHealth = m.baseHealth + tech.extraMaxHealth + 3 * tech.isFallingDamage + 4 * tech.isFlipFlop * tech.isFlipFlopOn * tech.isFlipFlopHealth + m.maxHealth = m.baseHealth + tech.extraMaxHealth + 4 * tech.isFallingDamage + if (level.isReducedHealth) { + level.reducedHealthLost = Math.max(0, m.health - m.maxHealth * 0.5) + m.maxHealth *= 0.5 + } document.getElementById("health-bg").style.width = `${Math.floor(300 * m.maxHealth)}px` - if (isMessage) simulation.makeTextLog(`m.maxHealth = ${m.maxHealth.toFixed(2)}`) + document.getElementById("defense-bar").style.width = Math.floor(300 * m.maxHealth * (1 - m.defense())) + "px"; + + if (isMessage) simulation.inGameConsole(`m.maxHealth = ${m.maxHealth.toFixed(2)}`) if (m.health > m.maxHealth) m.health = m.maxHealth; m.displayHealth(); }, @@ -553,29 +554,29 @@ const m = { lastCalculatedDefense: 0, //used to decided if defense bar needs to be redrawn (in simulation.checks) defense() { let dmg = 1 - dmg *= m.fieldHarmReduction - // if (!tech.isFlipFlopOn && tech.isFlipFlopHealth) dmg *= 0.5 - // 1.25 + Math.sin(m.cycle * 0.01) - if (tech.isDiaphragm) dmg *= 0.56 + 0.36 * Math.sin(m.cycle * 0.0075); + if (powerUps.boost.isDefense && powerUps.boost.endCycle > simulation.cycle) dmg *= 0.3 + if (tech.isMaxHealthDefense && m.health === m.maxHealth) dmg *= 0.3 + if (tech.isDiaphragm) dmg *= 0.55 + 0.35 * Math.sin(m.cycle * 0.0075); if (tech.isZeno) dmg *= 0.15 - if (tech.isFieldHarmReduction) dmg *= 0.65 - if (tech.isHarmMACHO) dmg *= 0.4 + if (tech.isFieldHarmReduction) dmg *= 0.6 + if (tech.isHarmDarkMatter) dmg *= (tech.isMoveDarkMatter || tech.isNotDarkMatter) ? 0.25 : 0.4 if (tech.isImmortal) dmg *= 0.7 - if (tech.energyRegen === 0) dmg *= 0.34 if (m.fieldMode === 0 || m.fieldMode === 3) dmg *= 0.973 ** m.coupling - if (tech.isLowHealthDefense) dmg *= 1 - Math.max(0, 1 - m.health) * 0.8 - if (tech.isHarmReduceNoKill && m.lastKillCycle + 300 < m.cycle) dmg *= 0.26 - if (tech.squirrelFx !== 1) dmg *= 0.78//Math.pow(0.78, (tech.squirrelFx - 1) / 0.4) + if (tech.isHarmReduceNoKill && m.lastKillCycle + 300 < m.cycle) dmg *= 0.3 if (tech.isAddBlockMass && m.isHolding) dmg *= 0.1 - if (tech.isSpeedHarm && player.speed > 0.1) dmg *= 1 - Math.min(player.speed * 0.0193, 0.88) //capped at speed of 55 - if (tech.isHarmReduce && input.field) dmg *= 0.12 + if (tech.isSpeedHarm && (tech.speedAdded + player.speed) > 0.1) dmg *= 1 - Math.min((tech.speedAdded + player.speed) * 0.01583, 0.95) //capped at speed of 55 + if (tech.isHarmReduce && input.field) dmg *= 0.1 if (tech.isNeutronium && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.05 - if (tech.isBotArmor) dmg *= 0.94 ** b.totalBots() - if (tech.isHarmArmor && m.lastHarmCycle + 600 > m.cycle) dmg *= 0.33; - if (tech.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.27 - if (tech.isTurret && m.crouch) dmg *= 0.34; + if (tech.isBotArmor) dmg *= 0.96 ** b.totalBots() + if (tech.isHarmArmor && m.lastHarmCycle + 600 > m.cycle) dmg *= 0.3; + if (tech.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.3 + if (tech.isTurret && m.crouch) dmg *= 0.3; if (tech.isFirstDer && b.inventory[0] === b.activeGun) dmg *= 0.85 ** b.inventory.length - return tech.isEnergyHealth ? Math.pow(dmg, 0.5) : dmg //defense has less effect + // if (tech.isLowHealthDefense) dmg *= Math.pow(0.3, Math.max(0, (tech.isEnergyHealth ? m.maxEnergy - m.energy : m.maxHealth - m.health))) + if (tech.isLowHealthDefense) dmg *= Math.pow(0.2, Math.max(0, 1 - (tech.isEnergyHealth ? m.energy / m.maxEnergy : m.health / m.maxHealth))) + // return tech.isEnergyHealth ? Math.pow(dmg, 0.7) : dmg //defense has less effect + // dmg *= m.fieldHarmReduction + return dmg * m.fieldHarmReduction }, rewind(steps) { // m.rewind(Math.floor(Math.min(599, 137 * m.energy))) if (tech.isRewindGrenade) { @@ -685,13 +686,13 @@ const m = { damage(dmg) { if (tech.isRewindAvoidDeath && (m.energy + 0.05) > Math.min(0.95, m.maxEnergy) && dmg > 0.01) { const steps = Math.floor(Math.min(299, 150 * m.energy)) - simulation.makeTextLog(`m.rewind(${steps})`) + simulation.inGameConsole(`m.rewind(${steps})`) m.rewind(steps) return } m.lastHarmCycle = m.cycle if (tech.isDroneOnDamage && bullet.length < 150) { //chance to build a drone on damage from tech - const len = Math.min((dmg - 0.06 * Math.random()) * 40, 40) / tech.droneEnergyReduction * (tech.isEnergyHealth ? 0.5 : 1) + const len = Math.min((dmg - 0.06 * Math.random()) * 80, 60) / tech.droneEnergyReduction * (tech.isEnergyHealth ? 0.5 : 1) for (let i = 0; i < len; i++) { if (Math.random() < 0.5) b.drone({ x: m.pos.x + 30 * Math.cos(m.angle) + 100 * (Math.random() - 0.5), @@ -705,8 +706,8 @@ const m = { if (tech.isDeathAvoid && powerUps.research.count && !tech.isDeathAvoidedThisLevel) { tech.isDeathAvoidedThisLevel = true powerUps.research.changeRerolls(-1) - simulation.makeTextLog(`m.research--
${powerUps.research.count}`) - for (let i = 0; i < 16; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); + simulation.inGameConsole(`m.research--
${powerUps.research.count}`) + for (let i = 0; i < 22; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); m.energy = m.maxEnergy + 0.1 if (m.immuneCycle < m.cycle + 300) m.immuneCycle = m.cycle + 300 //disable this.immuneCycle bonus seconds simulation.wipe = function () { //set wipe to have trails @@ -714,7 +715,6 @@ const m = { ctx.fillRect(0, 0, canvas.width, canvas.height); } setTimeout(function () { - tech.maxDuplicationEvent() simulation.wipe = function () { //set wipe to normal ctx.clearRect(0, 0, canvas.width, canvas.height); } @@ -734,7 +734,7 @@ const m = { tech.isDeathAvoidedThisLevel = true m.health = 0.05 powerUps.research.changeRerolls(-1) - simulation.makeTextLog(`m.research-- + simulation.inGameConsole(`m.research--
${powerUps.research.count}`) for (let i = 0; i < 16; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); if (m.immuneCycle < m.cycle + 300) m.immuneCycle = m.cycle + 300 //disable this.immuneCycle bonus seconds @@ -743,7 +743,6 @@ const m = { ctx.fillRect(0, 0, canvas.width, canvas.height); } setTimeout(function () { - tech.maxDuplicationEvent() simulation.wipe = function () { //set wipe to normal ctx.clearRect(0, 0, canvas.width, canvas.height); } @@ -787,6 +786,21 @@ const m = { simulation.fpsInterval = 1000 / simulation.fpsCap; } m.defaultFPSCycle = m.cycle + if (level.isMobHealPlayerDamage) { + for (let i = 0; i < mob.length; i++) { + if (mob[i].health < 1 && mob[i].isDropPowerUp && mob[i].alive) { + simulation.drawList.push({ + x: mob[i].position.x, + y: mob[i].position.y, + radius: mob[i].radius + 20, + color: "rgba(0,255,100,0.5)", + time: 10 + }); + mob[i].health += dmg * 7 + if (mob[i].health > 1) mob[i].health = 1 + } + } + } // if (tech.isSlowFPS) { // slow game // simulation.fpsCap = 30 //new fps // simulation.fpsInterval = 1000 / simulation.fpsCap; @@ -839,12 +853,22 @@ const m = { }, draw() { }, isAltSkin: false, + drawBoost() { + + }, resetSkin() { simulation.isAutoZoom = true; + m.hardLandCDScale = 1 m.yOffWhen.jump = 70 m.yOffWhen.stand = 49 m.yOffWhen.crouch = 22 m.isAltSkin = false + m.coyoteCycles = 5 + m.hardLanding = 130 + m.squirrelFx = 1; + m.squirrelJump = 1; + m.velocitySmooth = { x: 0, y: 0 } + requestAnimationFrame(() => { m.setMovement() }) m.color = { hue: 0, sat: 0, @@ -888,15 +912,21 @@ const m = { ctx.lineTo(m.knee.x, m.knee.y); ctx.lineTo(m.foot.x, m.foot.y); ctx.strokeStyle = stroke; - ctx.lineWidth = 6; + ctx.lineWidth = 5; ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 14, m.foot.y + 5); - ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 14, m.foot.y + 5); + if (m.onGround) { + ctx.lineTo(m.foot.x - 14, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 14, m.foot.y + 5); + } else { + ctx.lineTo(m.foot.x - 12, m.foot.y + 8); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 12, m.foot.y + 8); + } ctx.lineWidth = 4; ctx.stroke(); @@ -925,11 +955,6 @@ const m = { m.yOffWhen.stand = 49 m.yOffWhen.crouch = 22 m.isAltSkin = false - m.color = { - hue: 0, - sat: 0, - light: 100, - } m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 10}%)` @@ -966,14 +991,38 @@ const m = { m.isAltSkin = true m.yOffWhen.stand = 52 m.yOffWhen.jump = 72 - // m.yOffWhen.crouch = 22 - // m.color = { - // hue: 184, - // sat: 0, - // light: 55, - // } - // m.setFillColors(); + m.coyoteCycles = 11 + m.hardLandCDScale = 0.5 + m.hardLanding = 160 + m.squirrelFx = 1.4; + m.squirrelJump = 1.16; + m.setMovement() + m.draw = function () { + if (powerUps.boost.endCycle > simulation.cycle) { + //gel that acts as if the wind is blowing it when player moves + ctx.save(); + ctx.translate(m.pos.x, m.pos.y); + m.velocitySmooth = Vector.add(Vector.mult(m.velocitySmooth, 0.8), Vector.mult(player.velocity, 0.2)) + ctx.rotate(Math.atan2(m.velocitySmooth.y, m.velocitySmooth.x)) + ctx.beginPath(); + const radius = 39 + const mag = 14 * Vector.magnitude(m.velocitySmooth) + radius + ctx.arc(0, 0, radius, -Math.PI / 2, Math.PI / 2); + ctx.bezierCurveTo(-radius, radius, -radius, 0, -mag, 0); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + ctx.bezierCurveTo(-radius, 0, -radius, -radius, 0, -radius); + + // const time = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration + const time = Math.min(0.5, (powerUps.boost.endCycle - simulation.cycle) / powerUps.boost.duration) + + ctx.fillStyle = `rgba(0,0,0,${0.04 + 0.3 * time})` + ctx.fill() + // ctx.strokeStyle = "#333" + // ctx.lineWidth = 1 + // ctx.stroke(); + ctx.restore(); + } + m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) @@ -992,8 +1041,7 @@ const m = { ctx.lineWidth = 2; ctx.stroke(); ctx.restore(); - m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal - powerUps.boost.draw() + m.yOff = m.yOff * 0.75 + m.yOffGoal * 0.25; //smoothly move leg height towards height goal } m.drawLeg = function (stroke) { if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { @@ -1052,11 +1100,6 @@ const m = { }, polar() { m.isAltSkin = true - m.color = { - hue: 0, - sat: 0, - light: 100, - } // m.setFillColors(); m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 35}%)` @@ -1074,12 +1117,10 @@ const m = { ctx.translate(m.pos.x, m.pos.y); m.calcLeg(Math.PI, -3); - const diff = (m.lastKillCycle - m.cycle + tech.isDamageCooldownTime) / tech.isDamageCooldownTime const color = diff < 0 ? "#fff" : "#aaa" const hue = 220 + 20 * Math.sin(0.01 * m.cycle) const colorInverse = diff < 0 ? `hsl(${hue}, 80%, 40%)` : "#fff" - // const colorInverseFade = diff < 0 ? "#ccc" : "#fff" m.drawLeg(color, colorInverse); m.calcLeg(0, 0); m.drawLeg(color, colorInverse); @@ -1093,21 +1134,14 @@ const m = { ctx.beginPath(); ctx.moveTo(15, 0) ctx.lineTo(28, 0) - // ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = colorInverse; ctx.lineWidth = 4; ctx.stroke(); ctx.restore(); - - // const scale = diff>0.3 - // console.log(diff.toFixed(3), scale.toFixed(3)) ctx.beginPath(); ctx.ellipse(m.pos.x, m.pos.y, 24, 18, 3.14 * Math.random(), 0, 2 * Math.PI) - // `rgba(0,0,${100 + 30 * Math.sin(0.1 * m.cycle)},0.8)` ctx.fillStyle = diff < 0 ? `hsl(${hue}, 80%, 40%)` : `rgba(255,255,255,${Math.min(Math.max(0, diff + 0.3), 1)})` - // ctx.fillStyle = colorInverse - // ctx.fillStyle = `rgba(0,0,0,${scale})` ctx.fill(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal @@ -1127,15 +1161,21 @@ const m = { ctx.lineTo(m.knee.x, m.knee.y); ctx.lineTo(m.foot.x, m.foot.y); ctx.strokeStyle = stroke; - ctx.lineWidth = 6; + ctx.lineWidth = 5; ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 15, m.foot.y + 5); - ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + if (m.onGround) { + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + } else { + ctx.lineTo(m.foot.x - 13, m.foot.y + 8); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 13, m.foot.y + 8); + } ctx.lineWidth = 3; ctx.stroke(); @@ -1159,23 +1199,46 @@ const m = { m.isAltSkin = true m.yOffWhen.stand = 52 m.yOffWhen.jump = 72 - // m.yOffWhen.crouch = 22 - // m.color = { - // hue: 184, - // sat: 0, - // light: 55, - // } - // m.setFillColors(); + // m.speedSmooth = 0 + // m.smoothAngle = 0 m.draw = function () { + if (powerUps.boost.endCycle > simulation.cycle) { + //gel that acts as if the wind is blowing it when player moves + ctx.save(); + ctx.translate(m.pos.x, m.pos.y); + m.velocitySmooth = Vector.add(Vector.mult(m.velocitySmooth, 0.8), Vector.mult(player.velocity, 0.2)) + ctx.rotate(Math.atan2(m.velocitySmooth.y, m.velocitySmooth.x)) + ctx.beginPath(); + const radius = 40 + const mag = 9 * Vector.magnitude(m.velocitySmooth) + radius + ctx.arc(0, 0, radius, -Math.PI / 2, Math.PI / 2); + ctx.bezierCurveTo(-radius, radius, -radius, 0, -mag, 0); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + ctx.bezierCurveTo(-radius, 0, -radius, -radius, 0, -radius); + + // const time = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration + const time = Math.min(0.5, (powerUps.boost.endCycle - simulation.cycle) / powerUps.boost.duration) + + // ctx.fillStyle = `rgba(255,0,200,${0.4 * time})` + // ctx.fill() + // ctx.strokeStyle = "#f09" + + ctx.fillStyle = `rgba(255,255,255,${0.3 + time})`; + ctx.fill() + ctx.strokeStyle = "#446" + ctx.lineWidth = 0.2 + 4 * time + // ctx.lineWidth = 1 + ctx.stroke(); + ctx.restore(); + } + m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) ctx.translate(m.pos.x, m.pos.y); m.calcLeg(Math.PI, -1.25); - m.drawLeg("#606070"); + m.drawLeg("#606080"); m.calcLeg(0, 0); - m.drawLeg("#445"); - + m.drawLeg("#446"); ctx.rotate(m.angle); ctx.beginPath(); @@ -1186,22 +1249,27 @@ const m = { ctx.beginPath(); const arc = 0.7 + 0.17 * Math.sin(m.cycle * 0.012) ctx.arc(0, 0, 30, -arc, arc, true); //- Math.PI / 2 - ctx.strokeStyle = "#445"; + ctx.strokeStyle = "#446"; ctx.lineWidth = 2; ctx.stroke(); + //fire outline directed opposite player look direction + // ctx.beginPath(); + // const radius = 40 + // const extend = -50 + // ctx.arc(0, 0, radius, -Math.PI / 2, Math.PI / 2); + // ctx.bezierCurveTo(extend, radius, extend, 0, -100, 0); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + // ctx.bezierCurveTo(extend, 0, extend, -radius, 0, -radius); + // ctx.fillStyle = "rgba(255,0,255,0.3)"; + // ctx.fill() ctx.beginPath(); ctx.moveTo(13, 0) ctx.lineTo(20, 0) - // ctx.beginPath(); - // ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.lineWidth = 5; - ctx.strokeStyle = "#445"; ctx.stroke(); ctx.restore(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal - powerUps.boost.draw() } m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { @@ -1223,9 +1291,15 @@ const m = { //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 14, m.foot.y + 5); - ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 14, m.foot.y + 5); + if (m.onGround) { + ctx.lineTo(m.foot.x - 14, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 14, m.foot.y + 5); + } else { + ctx.lineTo(m.foot.x - 12, m.foot.y + 8); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 12, m.foot.y + 8); + } ctx.lineWidth = 4; ctx.stroke(); @@ -1254,6 +1328,30 @@ const m = { } m.setFillColors(); m.draw = function () { + if (powerUps.boost.endCycle > simulation.cycle) { + //gel that acts as if the wind is blowing it when player moves + ctx.save(); + ctx.translate(m.pos.x, m.pos.y); + m.velocitySmooth = Vector.add(Vector.mult(m.velocitySmooth, 0.8), Vector.mult(player.velocity, 0.2)) + ctx.rotate(Math.atan2(m.velocitySmooth.y, m.velocitySmooth.x)) + ctx.beginPath(); + const radius = 40 + const mag = 10 * Vector.magnitude(m.velocitySmooth) + radius + ctx.arc(0, 0, radius, -Math.PI / 2, Math.PI / 2); + ctx.bezierCurveTo(-radius, radius, -radius, 0, -mag, 0); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + ctx.bezierCurveTo(-radius, 0, -radius, -radius, 0, -radius); + + // const time = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration + const time = Math.min(0.5, (powerUps.boost.endCycle - simulation.cycle) / powerUps.boost.duration) + + ctx.fillStyle = `hsla(184,100%,70%,${0.1 + 1.5 * time})` + ctx.fill() + ctx.strokeStyle = "#035"//"hsl(184,100%,70%)" + ctx.lineWidth = 0.2 + 3 * time + ctx.stroke(); + ctx.restore(); + } + ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1266,8 +1364,8 @@ const m = { ctx.rotate(m.angle); ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); - ctx.strokeStyle = "rgba(0,255,255,0.22)"; - ctx.lineWidth = 12; + ctx.strokeStyle = "rgba(0,255,255,0.25)"; + ctx.lineWidth = 15; ctx.stroke(); ctx.fillStyle = 'hsl(184,100%,85%)' //m.fillColor; //"#9ff" //m.bodyGradient ctx.fill(); @@ -1276,10 +1374,9 @@ const m = { ctx.arc(17, 0, 5.5, 0, 2 * Math.PI); ctx.fillStyle = "#357" ctx.fill(); - ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal - powerUps.boost.draw() } m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { @@ -1295,16 +1392,22 @@ const m = { ctx.lineTo(m.knee.x, m.knee.y); ctx.lineTo(m.foot.x, m.foot.y); ctx.strokeStyle = stroke; - ctx.lineWidth = 6; + ctx.lineWidth = 5; ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 14, m.foot.y + 5); - ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 14, m.foot.y + 5); - ctx.lineWidth = 4; + if (m.onGround) { + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + } else { + ctx.lineTo(m.foot.x - 13, m.foot.y + 8); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 13, m.foot.y + 8); + } + ctx.lineWidth = 3; ctx.stroke(); //hip joint @@ -1317,7 +1420,7 @@ const m = { ctx.moveTo(m.foot.x + 5, m.foot.y); ctx.arc(m.foot.x, m.foot.y + 1, 5, 0, 2 * Math.PI); ctx.strokeStyle = "rgba(0,255,255,0.25)"; - ctx.lineWidth = 5; + ctx.lineWidth = 6; ctx.stroke(); ctx.fillStyle = m.fillColor; ctx.fill(); @@ -1325,6 +1428,9 @@ const m = { } }, tungsten() { + m.hardLandCDScale = 2 + m.hardLanding = 60 + m.coyoteCycles = 0 m.isAltSkin = true m.color = { hue: 210, @@ -1384,7 +1490,7 @@ const m = { ctx.fill(); ctx.restore(); - m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + m.yOff = m.yOff * 0.9 + m.yOffGoal * 0.1; //smoothly move leg height towards height goal powerUps.boost.draw() } m.drawLeg = function (stroke) { @@ -1469,6 +1575,26 @@ const m = { m.bodyGradient = grd m.draw = function () { + if (powerUps.boost.endCycle > simulation.cycle) { + //gel that acts as if the wind is blowing it when player moves + ctx.save(); + ctx.translate(m.pos.x, m.pos.y); + m.velocitySmooth = Vector.add(Vector.mult(m.velocitySmooth, 0.8), Vector.mult(player.velocity, 0.2)) + ctx.rotate(Math.atan2(m.velocitySmooth.y, m.velocitySmooth.x)) + ctx.beginPath(); + const radius = 40 + const mag = 12 * Vector.magnitude(m.velocitySmooth) + radius + ctx.arc(0, 0, radius, -Math.PI / 2, Math.PI / 2); + ctx.bezierCurveTo(-radius, radius, -radius, 0, -mag, 0); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + ctx.bezierCurveTo(-radius, 0, -radius, -radius, 0, -radius); + // const time = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration + const time = Math.min(0.5, (powerUps.boost.endCycle - simulation.cycle) / powerUps.boost.duration) + + ctx.fillStyle = `hsla(${simulation.cycle},100%,70%,${0.1 + 2 * time})` + ctx.fill() + ctx.restore(); + } + ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1489,7 +1615,6 @@ const m = { // ctx.stroke(); ctx.restore(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal - powerUps.boost.draw() } m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { @@ -1511,9 +1636,15 @@ const m = { //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 15, m.foot.y + 5); - ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + if (m.onGround) { + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + } else { + ctx.lineTo(m.foot.x - 13, m.foot.y + 8); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 13, m.foot.y + 8); + } ctx.lineWidth = 4; ctx.stroke(); @@ -1542,7 +1673,7 @@ const m = { m.isAltSkin = true simulation.isAutoZoom = false; m.draw = function () { - const amplitude = 8 + 4 * Math.sin(m.cycle * 0.0075) + const amplitude = 8 + 4 * Math.sin(m.cycle * 0.01) ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1568,18 +1699,38 @@ const m = { ctx.stroke(); ctx.restore(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal - powerUps.boost.draw() - //zoom camera in and out + if (powerUps.boost.endCycle > simulation.cycle) { + //gel that acts as if the wind is blowing it when player moves + ctx.save(); + ctx.translate(m.pos.x, m.pos.y); + m.velocitySmooth = Vector.add(Vector.mult(m.velocitySmooth, 0.8), Vector.mult(player.velocity, 0.2)) + ctx.rotate(Math.atan2(m.velocitySmooth.y, m.velocitySmooth.x)) + ctx.beginPath(); + const radius = 39 + const mag = 6 * Vector.magnitude(m.velocitySmooth) + radius + ctx.arc(0, 0, radius, -Math.PI / 2, Math.PI / 2); + ctx.bezierCurveTo(-radius, radius, -radius, 0, -mag, 0); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + ctx.bezierCurveTo(-radius, 0, -radius, -radius, 0, -radius); + // ctx.fillStyle = `hsla(${simulation.cycle * 0.5},100%,70%,0.4)` + // ctx.fill() + ctx.strokeStyle = "#345" + // const time = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration + const time = Math.min(0.5, (powerUps.boost.endCycle - simulation.cycle) / powerUps.boost.duration) + + ctx.lineWidth = 0.2 + 4 * time + ctx.stroke(); + ctx.restore(); + } - // console.log(simulation.zoomScale) - simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075)) + //zoom camera in and out + simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.01)) } }, dilate2() { m.isAltSkin = true m.draw = function () { - const amplitude = Math.sin(m.cycle * 0.0075) + const amplitude = Math.sin(m.cycle * 0.01) ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; @@ -1596,7 +1747,7 @@ const m = { ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.strokeStyle = "#345"; - ctx.lineWidth = 3 + 3 * Math.sin(m.cycle * 0.0075 + Math.PI); + ctx.lineWidth = 3 + 3 * Math.sin(m.cycle * 0.01 + Math.PI); ctx.stroke(); // ctx.arc(12, 0, 8 + 4 * amplitude, 0, 2 * Math.PI); //big eye ctx.beginPath(); @@ -1608,8 +1759,31 @@ const m = { // ctx.stroke(); ctx.restore(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal - powerUps.boost.draw() - simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075)) + + if (powerUps.boost.endCycle > simulation.cycle) { + //gel that acts as if the wind is blowing it when player moves + ctx.save(); + ctx.translate(m.pos.x, m.pos.y); + m.velocitySmooth = Vector.add(Vector.mult(m.velocitySmooth, 0.8), Vector.mult(player.velocity, 0.2)) + ctx.rotate(Math.atan2(m.velocitySmooth.y, m.velocitySmooth.x)) + ctx.beginPath(); + const radius = 39 + const mag = 6 * Vector.magnitude(m.velocitySmooth) + radius + ctx.arc(0, 0, radius, -Math.PI / 2, Math.PI / 2); + ctx.bezierCurveTo(-radius, radius, -radius, 0, -mag, 0); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + ctx.bezierCurveTo(-radius, 0, -radius, -radius, 0, -radius); + // ctx.fillStyle = `hsla(${simulation.cycle * 0.5},100%,70%,0.4)` + // ctx.fill() + const time = Math.min(0.5, (powerUps.boost.endCycle - simulation.cycle) / powerUps.boost.duration) + + ctx.strokeStyle = "#345" + ctx.lineWidth = 0.2 + 7 * time + // ctx.lineWidth = (4 + 3 * Math.sin(m.cycle * 0.01 + Math.PI)) * time; + ctx.stroke(); + ctx.restore(); + } + + simulation.setZoom(1800 + 400 * amplitude) } m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { @@ -1625,15 +1799,21 @@ const m = { ctx.lineTo(m.knee.x, m.knee.y); ctx.lineTo(m.foot.x, m.foot.y); ctx.strokeStyle = stroke; - ctx.lineWidth = 6 + 2 * Math.sin(m.cycle * 0.0075 + Math.PI); + ctx.lineWidth = 6 + 2 * Math.sin(m.cycle * 0.01 + Math.PI); ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 15, m.foot.y + 5); - ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + if (m.onGround) { + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + } else { + ctx.lineTo(m.foot.x - 13, m.foot.y + 8); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 13, m.foot.y + 8); + } ctx.lineWidth = 4; ctx.stroke(); @@ -1648,19 +1828,14 @@ const m = { ctx.arc(m.foot.x, m.foot.y, 6, 0, 2 * Math.PI); ctx.fillStyle = "#345"; ctx.fill(); - ctx.lineWidth = 3 + 3 * Math.sin(m.cycle * 0.0075 + Math.PI); + ctx.lineWidth = 3 + 3 * Math.sin(m.cycle * 0.01 + Math.PI); ctx.stroke(); ctx.restore(); } }, CPT() { m.isAltSkin = true - m.color = { - hue: 0, - sat: 0, - light: 100, - } - // m.setFillColors(); + m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 35}%)` let grd = ctx.createLinearGradient(-20, 0, 15, 0); @@ -1668,7 +1843,6 @@ const m = { grd.addColorStop(1, m.fillColor); // grd.addColorStop(1, m.fillColor); m.bodyGradient = grd - m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; @@ -1708,15 +1882,21 @@ const m = { ctx.lineTo(m.knee.x, m.knee.y); ctx.lineTo(m.foot.x, m.foot.y); ctx.strokeStyle = stroke; - ctx.lineWidth = 6; + ctx.lineWidth = 5; ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 15, m.foot.y + 5); - ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + if (m.onGround) { + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + } else { + ctx.lineTo(m.foot.x - 13, m.foot.y + 8); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 13, m.foot.y + 8); + } ctx.lineWidth = 3; ctx.stroke(); @@ -1736,50 +1916,44 @@ const m = { ctx.restore(); } }, - hexagon() { + verlet() { m.isAltSkin = true - m.color = { - hue: 0, - sat: 0, - light: 100, - } - // m.setFillColors(); - m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` - m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 35}%)` - let grd = ctx.createLinearGradient(-30, 0, 30, 0); - grd.addColorStop(0, m.fillColorDark); - grd.addColorStop(0.7, m.fillColor); - // grd.addColorStop(1, m.fillColor); - m.bodyGradient = grd - m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) ctx.translate(m.pos.x, m.pos.y); - m.calcLeg(Math.PI, -3); + m.calcLeg(Math.PI, -2); m.drawLeg("#4a4a4a"); m.calcLeg(0, 0); m.drawLeg("#333"); - ctx.rotate(m.angle); - const size = 32 ctx.beginPath(); - ctx.lineTo(size * 1, size * 0) - ctx.lineTo(size * 0.5, size * 0.866) - ctx.lineTo(size * -0.5, size * 0.866) - ctx.lineTo(size * -1, size * 0) - ctx.lineTo(size * -0.5, size * -0.866) - ctx.lineTo(size * 0.5, size * -0.866) - ctx.lineTo(size * 1, size * 0) + ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); - ctx.arc(15, 0, 4, 0, 2 * Math.PI); - ctx.strokeStyle = "#333"; - ctx.lineWidth = 2; + const rate = 0.09 + ctx.strokeStyle = "#000"; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.arc(0, 0, rate * (simulation.cycle + 0) % 30, 0, 2 * Math.PI); ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, rate * (simulation.cycle + 15 / rate) % 30, 0, 2 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.stroke(); + + ctx.globalCompositeOperation = "difference"; + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(21, 0, 8, 0, 2 * Math.PI); + ctx.fillStyle = input.fire ? "#0ff" : input.field ? "#d30" : `#fff` + ctx.fill(); ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } @@ -1797,27 +1971,28 @@ const m = { ctx.lineTo(m.knee.x, m.knee.y); ctx.lineTo(m.foot.x, m.foot.y); ctx.strokeStyle = stroke; - ctx.lineWidth = 6; + ctx.lineWidth = 1; ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + const footDrop = m.onGround ? 5 : 10 + ctx.lineTo(m.foot.x - 15, m.foot.y + footDrop); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 15, m.foot.y + 5); - ctx.lineWidth = 3; + ctx.lineTo(m.foot.x + 15, m.foot.y + footDrop); + ctx.lineWidth = 1; ctx.stroke(); //hip joint ctx.beginPath(); - ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + ctx.arc(m.hip.x, m.hip.y, 9, 0, 2 * Math.PI); //knee joint ctx.moveTo(m.knee.x + 5, m.knee.y); - ctx.arc(m.knee.x, m.knee.y, 5, 0, 2 * Math.PI); + ctx.arc(m.knee.x, m.knee.y, 3, 0, 2 * Math.PI); //foot joint ctx.moveTo(m.foot.x + 5, m.foot.y); - ctx.arc(m.foot.x, m.foot.y, 5, 0, 2 * Math.PI); + ctx.arc(m.foot.x, m.foot.y, 4, 0, 2 * Math.PI); ctx.fillStyle = "#000"; ctx.fill(); // ctx.lineWidth = 2; @@ -1892,6 +2067,7 @@ const m = { ctx.stroke(); ctx.restore(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() } }, diegesis() { @@ -1919,10 +2095,12 @@ const m = { ctx.stroke(); ctx.restore(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() } }, cat() { m.isAltSkin = true + m.coyoteCycles = 10 m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; @@ -1931,8 +2109,6 @@ const m = { ctx.translate(m.pos.x, m.pos.y); m.calcLeg(Math.PI, -3); m.drawLeg("#4a4a4a"); - - if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) { ctx.scale(1, -1); ctx.rotate(Math.PI); @@ -1987,6 +2163,7 @@ const m = { ctx.restore(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() } }, pareidolia() { @@ -2042,6 +2219,7 @@ const m = { ctx.stroke(); ctx.restore(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; + powerUps.boost.draw() } }, flipFlop() { @@ -2079,7 +2257,97 @@ const m = { m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } - } + }, + hexagon() { + m.isAltSkin = true + + m.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` + m.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 35}%)` + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, m.fillColorDark); + grd.addColorStop(0.7, m.fillColor); + // grd.addColorStop(1, m.fillColor); + m.bodyGradient = grd + + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + 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); + + const size = 32 + ctx.beginPath(); + ctx.lineTo(size * 1, size * 0) + ctx.lineTo(size * 0.5, size * 0.866) + ctx.lineTo(size * -0.5, size * 0.866) + ctx.lineTo(size * -1, size * 0) + ctx.lineTo(size * -0.5, size * -0.866) + ctx.lineTo(size * 0.5, size * -0.866) + ctx.lineTo(size * 1, size * 0) + ctx.fillStyle = m.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + powerUps.boost.draw() + } + m.drawLeg = function (stroke) { + // if (simulation.mouseInGame.x > m.pos.x) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(m.hip.x, m.hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(m.foot.x, m.foot.y); + ctx.strokeStyle = stroke; + ctx.lineWidth = 6; + ctx.stroke(); + + //toe lines + ctx.beginPath(); + ctx.moveTo(m.foot.x, m.foot.y); + if (m.onGround) { + ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + } else { + ctx.lineTo(m.foot.x - 13, m.foot.y + 8); + ctx.moveTo(m.foot.x, m.foot.y); + ctx.lineTo(m.foot.x + 13, m.foot.y + 8); + } + ctx.lineWidth = 3; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(m.knee.x + 5, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 5, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(m.foot.x + 5, m.foot.y); + ctx.arc(m.foot.x, m.foot.y, 5, 0, 2 * Math.PI); + ctx.fillStyle = "#000"; + ctx.fill(); + // ctx.lineWidth = 2; + // ctx.stroke(); + ctx.restore(); + } + }, }, // ********************************************* // **************** fields ********************* @@ -2106,7 +2374,7 @@ const m = { isFieldActive: false, fieldRange: 155, fieldShieldingScale: 1, - // fieldDamage: 1, + fieldDamage: 1, isSneakAttack: false, lastHit: 0, //stores value of last damage player took above a threshold, in m.damage sneakAttackCycle: 0, @@ -2143,8 +2411,8 @@ const m = { m.eyeFillColor = m.fieldMeterColor m.fieldShieldingScale = 1; m.fieldBlockCD = 10; + m.fieldDamage = 1 m.fieldHarmReduction = 1; - m.lastHit = 0 m.isSneakAttack = false m.duplicateChance = 0 m.grabPowerUpRange2 = 200000; @@ -2176,8 +2444,9 @@ const m = { } }, setMaxEnergy(isMessage = true) { - m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2.66 * tech.isGroundState + 3 * tech.isRelay * tech.isFlipFlopOn * tech.isRelayEnergy + 1.5 * (m.fieldMode === 1) + (m.fieldMode === 0 || m.fieldMode === 1) * 0.05 * m.coupling + 0.77 * tech.isStandingWaveExpand - if (isMessage) simulation.makeTextLog(`m.maxEnergy = ${(m.maxEnergy.toFixed(2))}`) + m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 3 * tech.isGroundState + 1.5 * (m.fieldMode === 1) + (m.fieldMode === 0 || m.fieldMode === 1) * 0.05 * m.coupling + 0.77 * tech.isStandingWaveExpand + if (level.isReducedEnergy) m.maxEnergy *= 0.5 + if (isMessage) simulation.inGameConsole(`m.maxEnergy = ${(m.maxEnergy.toFixed(2))}`) }, fieldMeterColor: "#0cf", drawRegenEnergy(bgColor = "rgba(0, 0, 0, 0.4)", range = 60) { @@ -2227,12 +2496,14 @@ const m = { m.fieldRegen = 0.001667 //10 energy per second plasma torch } else if (m.fieldMode === 8) { m.fieldRegen = 0.001667 //10 energy per second pilot wave + } else if (m.fieldMode === 9) { + m.fieldRegen = 0.00117 //7 energy per second wormhole } else if (m.fieldMode === 10) { m.fieldRegen = 0.0015 //9 energy per second grappling hook } else { m.fieldRegen = 0.001 //6 energy per second } - if (m.fieldMode === 0 || m.fieldMode === 4) m.fieldRegen += 0.000133 * m.coupling //return `generate ${(6*couple).toFixed(0)} energy per second` + if (m.fieldMode === 0 || m.fieldMode === 4) m.fieldRegen += 0.0001 * m.coupling if (tech.isTimeCrystals) { m.fieldRegen *= 2.5 } else if (tech.isGroundState) { @@ -2240,21 +2511,18 @@ const m = { } }, regenEnergy() { //used in drawRegenEnergy // rewritten by some tech - if (m.immuneCycle < m.cycle && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen; + if (m.immuneCycle < m.cycle && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen * level.isReducedRegen; if (m.energy < 0) m.energy = 0 }, regenEnergyDefault() { - if (m.immuneCycle < m.cycle && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen; + if (m.immuneCycle < m.cycle && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen * level.isReducedRegen; if (m.energy < 0) m.energy = 0 }, lookingAt(who) { //calculate a vector from body to player and make it length 1 const diff = Vector.normalise(Vector.sub(who.position, m.pos)); //make a vector for the player's direction of length 1 - const dir = { - x: Math.cos(m.angle), - y: Math.sin(m.angle) - }; + const dir = { x: Math.cos(m.angle), y: Math.sin(m.angle) }; //the dot product of diff and dir will return how much over lap between the vectors if (Vector.dot(dir, diff) > m.fieldThreshold) { return true; @@ -2278,8 +2546,6 @@ const m = { Matter.Body.setMass(player, mass); //reduce air and ground move forces m.setMovement() - // m.Fx = 0.08 / mass * tech.squirrelFx //base player mass is 5 - // m.FxAir = 0.4 / mass / mass //base player mass is 5 //make player stand a bit lower when holding heavy masses m.yOffWhen.stand = Math.max(m.yOffWhen.crouch, Math.min(49, 49 - (mass - 5) * 6)) if (m.onGround && !m.crouch) m.yOffGoal = m.yOffWhen.stand; @@ -2358,6 +2624,30 @@ const m = { ctx.lineTo(m.holdingTarget.vertices[i + 1].x, m.holdingTarget.vertices[i + 1].y); ctx.fill(); } + if (tech.isTokamakFly && m.throwCharge > 4 && m.energy > 0.01) { + player.force.y -= 0.5 * player.mass * simulation.g; //add some reduced gravity + // const mass = (player.mass + 10) / 3 * simulation.g //this makes it so you fly slower with larger blocks + let isDrain = false + const thrust = player.mass * simulation.g * Math.pow(5 / player.mass, 0.1) + if (input.down) { + isDrain = true + player.force.y += 0.9 * thrust; + } else if (input.up) { + isDrain = true + player.force.y -= 0.9 * thrust + } + if (!m.onGround) { + if (input.left) { + isDrain = true + player.force.x -= 0.4 * thrust + } else if (input.right) { + isDrain = true + player.force.x += 0.4 * thrust + } + if (isDrain) m.energy -= 0.0017; + } + + } } else { //draw charge const x = m.pos.x + 15 * Math.cos(m.angle); @@ -2408,7 +2698,7 @@ const m = { if (tech.isTokamak && m.throwCharge > 4) { //remove the block body and pulse in the direction you are facing //m.throwCharge > 5 seems to be when the field full colors in a block you are holding m.throwCycle = m.cycle + 180 //used to detect if a block was thrown in the last 3 seconds - if (m.immuneCycle < m.cycle) m.energy += 0.25 * Math.sqrt(m.holdingTarget.mass) * Math.min(5, m.throwCharge) + if (m.immuneCycle < m.cycle) m.energy += 0.25 * Math.sqrt(m.holdingTarget.mass) * Math.min(5, m.throwCharge) * level.isReducedRegen m.throwCharge = 0; m.definePlayerMass() //return to normal player mass //remove block before pulse, so it doesn't get in the way @@ -2419,6 +2709,12 @@ const m = { } } b.pulse(60 * Math.pow(m.holdingTarget.mass, 0.25), m.angle) + if (tech.isTokamakHeal && tech.tokamakHealCount < 5) { + tech.tokamakHealCount++ + let massScale = Math.min(65 * Math.sqrt(m.maxHealth), 14 * Math.pow(m.holdingTarget.mass, 0.4)) + if (powerUps.healGiveMaxEnergy) massScale = powerUps["heal"].size() + powerUps.spawn(m.pos.x, m.pos.y, "heal", true, massScale * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1))) // spawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { + } } else { //normal throw //bullet-like collisions m.holdingTarget.collisionFilter.category = cat.bullet @@ -2462,6 +2758,7 @@ const m = { }); m.definePlayerMass() //return to normal player mass + if (tech.isStaticBlock) m.holdingTarget.isStatic = true if (tech.isAddBlockMass) { const expand = function (that, massLimit) { if (that.mass < massLimit) { @@ -2756,7 +3053,7 @@ const m = { m.pushMass(mob[i]); if (tech.deflectEnergy && !mob[i].isInvulnerable && !mob[i].isShielded) { - m.energy += tech.deflectEnergy + m.energy += tech.deflectEnergy * level.isReducedRegen } } } @@ -2853,7 +3150,7 @@ const m = { couplingDescription(couple = m.coupling) { switch (m.fieldMode) { case 0: //field emitter - return `all other field effects` + return `all effects` case 1: //standing wave // return `deflecting condenses +${couple.toFixed(1)} ice IX` return `+${(couple * 5).toFixed(0)} maximum energy` @@ -2861,25 +3158,25 @@ const m = { return `deflecting condenses ${(0.1 * couple).toFixed(2)} ice IX` // return `invulnerable +${2*couple} seconds post collision` case 3: //negative mass - return `+${(100 * (1 - 0.973 ** couple)).toFixed(1)}% defense` + return `${(0.973 ** couple).toFixed(2)}x damage taken` case 4: //assembler - return `generate ${(0.8 * couple).toFixed(1)} energy per second` + return `+${(0.6 * couple).toFixed(1)} energy per second` case 5: //plasma - return `+${(1.5 * couple).toFixed(1)}% damage` + return `${(1 + 0.015 * couple).toFixed(3)}x damage` case 6: //time dilation - return `+${(5 * couple).toFixed(0)}% longer stopped time` //movement, jumping, and + return `+${(1 + 0.05 * couple).toFixed(2)}x longer stopped time` //movement, jumping, and case 7: //cloaking - return `+${(3.3 * couple).toFixed(1)}% ambush damage` + return `${(1 + 3.3 * couple).toFixed(3)}x ambush damage` case 8: //pilot wave - return `+${(4 * couple).toFixed(0)}% block collision damage` + return `${(1 + 0.05 * couple).toFixed(2)}x block collision damage` case 9: //wormhole return `after eating blocks +${(2 * couple).toFixed(0)} energy` case 10: //grappling hook - return `${powerUps.orb.ammo(1)} give ${(4 * couple).toFixed(0)}% more ammo` + return `${powerUps.orb.ammo(1)} give ${(4 * couple).toFixed(0)}% more ammo` } }, couplingChange(change = 0) { - if (change > 0 && level.onLevel !== -1) simulation.makeTextLog(`m.coupling += ${change}`, 60); //level.onLevel !== -1 means not on lore level + if (change > 0 && level.onLevel !== -1) simulation.inGameConsole(`
m.coupling += ${change}`, 60); //level.onLevel !== -1 means not on lore level m.coupling += change if (m.coupling < 0) { //look for coupling power ups on this level and remove them to prevent exploiting tech ejections @@ -2917,13 +3214,13 @@ const m = { document.getElementById("field").innerHTML = m.fieldUpgrades[index].name m.setHoldDefaults(); m.fieldUpgrades[index].effect(); - simulation.makeTextLog(`m.setField("${m.fieldUpgrades[m.fieldMode].name}")`); + simulation.inGameConsole(`
  m.setField("${m.fieldUpgrades[m.fieldMode].name}")
input.key.field: ["MouseRight"]`); }, fieldUpgrades: [{ name: "field emitter", imageNumber: Math.floor(Math.random() * 26), //pick one of the 25 field emitter image files at random description: `initial field
use energy to deflect mobs and throw blocks -
generate 4 energy per second`, //
100 max energy +
4 energy per second`, //
100 max energy effect: () => { m.hold = function () { if (m.isHolding) { @@ -2952,11 +3249,11 @@ const m = { //deflecting protects you in every direction description: `3 oscillating shields are permanently active
+150 max energy -
generate 6 energy per second`, +
6 energy per second`, drainCD: 0, effect: () => { m.fieldBlockCD = 0; - m.blockingRecoil = 1.5 //4 is normal + m.blockingRecoil = 1 //4 is normal m.fieldRange = 185 m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2)) // m.fieldHarmReduction = 0.66; //33% reduction @@ -3054,10 +3351,7 @@ const m = { }, { name: "perfect diamagnetism", - description: "deflecting does not drain energy
maintains functionality while inactive
generate 5 energy per second", - //
attract power ups from far away - // description: "attract power ups from far away
deflecting doesn't drain energy
thrown blocks have", - // description: "gain energy when blocking
no recoil when blocking", + description: `deflecting does not drain energy
shield maintains functionality while inactive
5 energy per second`, effect: () => { m.fieldMeterColor = "#48f" //"#0c5" m.eyeFillColor = m.fieldMeterColor @@ -3292,14 +3586,14 @@ const m = { { name: "negative mass", //
hold blocks as if they have a lower mass - description: "use energy to nullify  gravity
+55% defense
generate 6 energy per second", + description: `use energy to nullify  gravity
0.4x damage taken
6 energy per second`, fieldDrawRadius: 0, effect: () => { m.fieldFire = true; m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping m.fieldMeterColor = "#333" m.eyeFillColor = m.fieldMeterColor - m.fieldHarmReduction = 0.45; //55% reduction + m.fieldHarmReduction = 0.4; m.fieldDrawRadius = 0; m.hold = function () { @@ -3480,11 +3774,11 @@ const m = { }, { name: "molecular assembler", - description: `excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
generate 12 energy per second`, - // simulation.molecularMode: Math.floor(4 * Math.random()), //0 spores, 1 missile, 2 ice IX, 3 drones + description: `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second`, setDescription() { - return `excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
generate 12 energy per second` + return `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second` }, + effect: () => { m.fieldMeterColor = "#ff0" m.eyeFillColor = m.fieldMeterColor @@ -3538,7 +3832,7 @@ const m = { bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) // b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) } else if (simulation.molecularMode === 2) { - m.energy -= 0.045; + m.energy -= 0.044; b.iceIX(1) } else if (simulation.molecularMode === 3) { if (tech.isDroneRadioactive) { @@ -3600,9 +3894,10 @@ const m = { }, { name: "plasma torch", - description: "use energy to emit short range plasma
damages and pushes mobs away
generate 10 energy per second", + description: "use energy to emit short range plasma
1.5x damage
10 energy per second", set() { b.isExtruderOn = false + m.fieldDamage = 1.5 // m.fieldCDcycleAlternate = 0 if (m.plasmaBall) { @@ -4056,7 +4351,7 @@ const m = { }, { name: "time dilation", - description: "use energy to stop time
+20% movement and fire rate
generate 12 energy per second", + description: `use energy to stop time
1.2x movement and fire rate
12 energy per second`, set() { // m.fieldMeterColor = "#0fc" // m.fieldMeterColor = "#ff0" @@ -4067,7 +4362,7 @@ const m = { m.setMovement(); b.setFireCD() const timeStop = () => { - m.immuneCycle = m.cycle + 10; //immune to harm while time is stopped, this also disables regen + m.immuneCycle = m.cycle + 10; //invulnerable to harm while time is stopped, this also disables regen //draw field everywhere ctx.globalCompositeOperation = "saturation" ctx.fillStyle = "#ccc"; @@ -4246,7 +4541,7 @@ const m = { }, effect() { if (tech.isTimeStop) { - m.fieldHarmReduction = 0.66; //33% reduction + m.fieldHarmReduction = 0.6; } else { m.fieldHarmReduction = 1; } @@ -4255,7 +4550,7 @@ const m = { }, { name: "metamaterial cloaking", - description: "+66% defense while cloaked
after decloaking +333% damage for 2 s
generate 6 energy per second", + description: `0.3x damage taken while cloaked
after decloaking 4.5x damage for 2 s
6 energy per second`, effect: () => { m.fieldFire = true; m.fieldMeterColor = "#333"; @@ -4306,7 +4601,7 @@ const m = { if (!m.isCloak) { //&& m.energy > drain + 0.03 // m.energy -= drain m.isCloak = true //enter cloak - m.fieldHarmReduction = 0.33; //66% reduction + m.fieldHarmReduction = 0.3; m.enterCloakCycle = m.cycle if (tech.isCloakHealLastHit && m.lastHit > 0) { const heal = Math.min(0.75 * m.lastHit, m.energy) @@ -4398,26 +4693,25 @@ const m = { } this.drawRegenEnergyCloaking() if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) { //show sneak attack status + m.fieldDamage = 4.5 * (1 + 0.033 * m.coupling) const timeLeft = (m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) - m.cycle) * 0.5 ctx.beginPath(); ctx.arc(m.pos.x, m.pos.y, 32, 0, 2 * Math.PI); ctx.strokeStyle = "rgba(180,30,70,0.5)";//"rgba(0,0,0,0.7)";//"rgba(255,255,255,0.7)";//"rgba(255,0,100,0.7)"; ctx.lineWidth = Math.max(Math.min(10, timeLeft), 3); ctx.stroke(); - // ctx.globalCompositeOperation = "multiply"; // m.drawCloakedM() // ctx.globalCompositeOperation = "source-over"; + } else { + m.fieldDamage = 1 } } } }, { name: "pilot wave", - //
blocks can't collide with intangible mobs - //field radius decreases out of line of sight - //unlock tech from other fields - description: "use energy to guide blocks
tech, fields, and guns have +2 choices
generate 10 energy per second", + description: `use energy to guide blocks
,
, and
have +3 choices
10 energy per second`, effect: () => { m.fieldMeterColor = "#333" m.eyeFillColor = m.fieldMeterColor @@ -4646,7 +4940,7 @@ const m = { { name: "wormhole", //wormholes attract blocks and power ups
- description: "use energy to tunnel through a wormhole
+7% chance to duplicate spawned power ups
generate 6 energy per second", //
bullets may also traverse wormholes + description: "use energy to tunnel through a wormhole
+7% chance to duplicate spawned power ups
7 energy per second", //
bullets may also traverse wormholes drain: 0, effect: function () { m.fieldMeterColor = "#bbf" //"#0c5" @@ -4757,7 +5051,7 @@ const m = { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); m.fieldRange *= 0.8 - if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling + if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling * level.isReducedRegen if (tech.isWormholeWorms) { //pandimensional spermia b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10)); @@ -4779,10 +5073,8 @@ const m = { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); m.fieldRange *= 0.8 - // if (tech.isWormholeEnergy && m.energy < m.maxEnergy * 2) m.energy = m.maxEnergy * 2 - // if (tech.isWormholeEnergy && m.immuneCycle < m.cycle) m.energy += 0.5 - if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling - if (m.fieldMode === 0 || m.fieldMode === 9) m.energy += 0.02 * m.coupling + if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling * level.isReducedRegen + if (m.fieldMode === 0 || m.fieldMode === 9) m.energy += 0.02 * m.coupling * level.isReducedRegen if (tech.isWormholeWorms) { //pandimensional spermia b.worm(Vector.add(m.hole.pos1, Vector.rotate({ x: m.fieldRange * 0.4, @@ -5194,15 +5486,14 @@ const m = { }, { name: "grappling hook", - // description: `use energy to pull yourself towards the map
generate 6 energy per second`, - description: `use energy to fire a hook that pulls player
damages mobs and grabs blocks
generate 9 energy per second`, + description: `use energy to fire a hook that pulls you
0.5x damage taken
9 energy per second`, effect: () => { m.fieldFire = true; // m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping // m.fieldMeterColor = "#789"//"#456" m.eyeFillColor = m.fieldMeterColor + m.fieldHarmReduction = 0.5; //40% reduction m.grabPowerUpRange2 = 300000 //m.grabPowerUpRange2 = 200000; - // m.fieldHarmReduction = 0.45; //55% reduction m.hold = function () { if (m.isHolding) { @@ -5379,8 +5670,8 @@ const m = { y: drag * player.velocity.y }); if (input.up) { //forward thrust - player.force.x += thrust * Math.cos(m.angle) * tech.squirrelJump - player.force.y += thrust * Math.sin(m.angle) * tech.squirrelJump + player.force.x += thrust * Math.cos(m.angle) * m.squirrelJump + player.force.y += thrust * Math.sin(m.angle) * m.squirrelJump } else if (input.down) { player.force.x -= 0.6 * thrust * Math.cos(m.angle) player.force.y -= 0.6 * thrust * Math.sin(m.angle) @@ -5534,7 +5825,7 @@ const m = { return } m.damage(dmg); - if (tech.isPiezo) m.energy += 20.48; + if (tech.isPiezo) m.energy += 20.48 * level.isReducedRegen; if (tech.isStimulatedEmission) powerUps.ejectTech() if (mob[k].onHit) mob[k].onHit(); if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles @@ -5597,7 +5888,7 @@ const m = { let dmg = tech.blockDamage * m.dmgScale * v * obj.mass * (tech.isMobBlockFling ? 2 : 1); if (mob[k].isShielded) dmg *= 0.7 mob[k].damage(dmg, true); - if (tech.isBlockPowerUps && !mob[k].alive && mob[k].isDropPowerUp && m.throwCycle > m.cycle) { + if (tech.isBlockPowerUps && !mob[k].alive && mob[k].isDropPowerUp && Math.random() < 0.5) { let type = tech.isEnergyNoAmmo ? "heal" : "ammo" if (Math.random() < 0.4) { type = "heal" diff --git a/js/powerup.js b/js/powerup.js index d70b30ae..4f3af638 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -34,48 +34,10 @@ const powerUps = { healGiveMaxEnergy: false, //for tech 1st ionization energy orb: { research(num = 1) { - switch (num) { - case 1: - return `
` - case 2: - return ` -
-
-
       ` - case 3: - return ` -
-
-
-
          ` - case 4: - return ` -
-
-
-
-
            ` - case 5: - return ` -
-
-
-
-
-
              ` - case 6: - return ` -
-
-
-
-
-
-
                ` - } + if (num === 1) return `
` let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
    ' for (let i = 0; i < num; i++) { @@ -90,7 +52,7 @@ const powerUps = { } let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
    ' for (let i = 0; i < num; i++) { @@ -100,37 +62,43 @@ const powerUps = { }, heal(num = 1) { if (powerUps.healGiveMaxEnergy) { - switch (num) { - case 1: - return `
` - } + if (num === 1) return `
` + let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
    ' - for (let i = 0; i < num; i++) { - text += '  ' - } + for (let i = 0; i < num; i++) text += '  ' return text } else { - switch (num) { - case 1: - return `
` - } + if (num === 1) return `
` + let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
    ' - for (let i = 0; i < num; i++) { - text += '  ' - } + for (let i = 0; i < num; i++) text += '  ' return text } }, tech(num = 1) { - return `
` + return `
tech
` + }, + field(num = 1) { + return `
field
` + }, + gun(num = 1) { + return `
gun
` + }, + gunTech(num = 1) { + return `
guntech
+
guntech
` + }, + fieldTech(num = 1) { + return `
fieldtech
+
fieldtech
` }, coupling(num = 1) { switch (num) { @@ -139,7 +107,7 @@ const powerUps = { } let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
   ' for (let i = 0; i < num; i++) { @@ -189,7 +157,6 @@ const powerUps = { } else { powerUps.do = () => powerUps.draw(); } - tech.maxDuplicationEvent() //check to see if hitting 100% duplication } else { powerUps.draw = powerUps.drawCircle if (tech.isHealAttract) { @@ -247,12 +214,14 @@ const powerUps = { if (powerUp[i].isDuplicated) { if (Math.random() < 0.003 && !m.isBodiesAsleep) { // (1-0.003)^240 = chance to be removed after 4 seconds, 240 = 4 seconds * 60 cycles per second b.explosion(powerUp[i].position, 175 + (11 + 3 * Math.random()) * powerUp[i].size); - Matter.Composite.remove(engine.world, powerUp[i]); - powerUp.splice(i, 1); + if (powerUp[i]) { + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + } break } if (Math.random() < 0.3) { //draw electricity - const mag = 4 + powerUp[i].size / 5 + const mag = Math.max(1, 4 + powerUp[i].size / 5) let unit = Vector.rotate({ x: mag, y: mag }, 2 * Math.PI * Math.random()) let path = { x: powerUp[i].position.x + unit.x, y: powerUp[i].position.y + unit.y } ctx.beginPath(); @@ -272,17 +241,17 @@ const powerUps = { choose(type, index) { if (type === "gun") { b.giveGuns(index) - let text = `b.giveGuns("${b.guns[index].name}")` + let text = `
  b.giveGuns("${b.guns[index].name}")` if (b.inventory.length === 1) text += `
input.key.gun: ["MouseLeft"]` if (b.inventory.length === 2) text += `
input.key.nextGun: ["${input.key.nextGun}","MouseWheel"]
input.key.previousGun: ["${input.key.previousGun}","MouseWheel"]` - simulation.makeTextLog(text); + simulation.inGameConsole(text); } else if (type === "field") { m.setField(index) } else if (type === "tech") { - // if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false - simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}")`); + // if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false + simulation.inGameConsole(`
  tech.giveTech("${tech.tech[index].name}")`); tech.giveTech(index) } powerUps.endDraft(type); @@ -313,26 +282,18 @@ const powerUps = { ctx.fillStyle = `rgba(150,150,150,0.9)`; //`rgba(221,221,221,0.6)`; ctx.fillRect(0, 0, canvas.width, canvas.height); }); - // document.getElementById("pause-grid-right").style.opacity = "0.7" - // document.getElementById("pause-grid-left").style.opacity = "0.7" } - // build.pauseGrid() }, endDraft(type, isCanceled = false) { //type should be a gun, tech, or field if (isCanceled) { - if (tech.isCancelTech && Math.random() < 0.85 && type !== "entanglement") { - // powerUps.research.use('tech') - powerUps[type].effect(); - return - } if (tech.isCancelDuplication) { - tech.duplication += 0.05 - tech.maxDuplicationEvent() - simulation.makeTextLog(`tech.duplicationChance() += ${0.043}`) - simulation.circleFlare(0.043); + const value = 0.05 + tech.duplication += value + simulation.inGameConsole(`tech.duplicationChance() += ${value}`) + simulation.circleFlare(value); } if (tech.isCancelRerolls) { - for (let i = 0, len = 10 + 4 * Math.random(); i < len; i++) { + for (let i = 0, len = 8 + 4 * Math.random(); i < len; i++) { let spawnType if (Math.random() < 0.4 && !tech.isEnergyNoAmmo) { spawnType = "ammo" @@ -345,21 +306,14 @@ const powerUps = { } } if (tech.isCancelCouple) powerUps.spawnDelay("coupling", 8) - // if (tech.isCancelTech && Math.random() < 0.3) { - // powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "tech", false); - // simulation.makeTextLog(`options exchange: returns 1 tech`) - // } - // if (tech.isBanish && type === 'tech') { // banish researched tech by adding them to the list of banished tech - // const banishLength = tech.isDeterminism ? 1 : 3 + tech.extraChoices * 2 - // for (let i = 0; i < banishLength; i++) { - // const index = powerUps.tech.choiceLog.length - i - 1 - // if (powerUps.tech.choiceLog[index] && tech.tech[powerUps.tech.choiceLog[index]]) { - // tech.tech[powerUps.tech.choiceLog[index]].isBanished = true - // } - // } - // simulation.makeTextLog(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - banishLength)}`) - // } + if (tech.isCancelTech && tech.cancelTechCount === 0 && type !== "entanglement") { + tech.cancelTechCount++ + // powerUps.research.use('tech') + powerUps[type].effect(); + return + } } + if (tech.isAnsatz && powerUps.research.count < 1) { for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); } @@ -386,7 +340,7 @@ const powerUps = { if (this.count < 5) simulation.removeEphemera(this.name) ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, this.count, 0, 2 * Math.PI); + ctx.arc(m.pos.x, m.pos.y, Math.max(3, this.count), 0, 2 * Math.PI); ctx.fillStyle = color ctx.fill(); // ctx.strokeStyle = "hsla(200,50%,61%,0.18)"; @@ -395,6 +349,125 @@ const powerUps = { }) }, + difficulty: { + name: "difficulty", + color: "#000", + size() { + return 80 / Math.pow(localSettings.difficultyMode, 1.5); + }, + effect() { + const initialDifficultyMode = simulation.difficultyMode + requestAnimationFrame(() => { //add a background behind the power up menu + ctx.fillStyle = `rgba(150,150,150,0.9)`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }); + powerUps.animatePowerUpGrab('rgba(0, 0, 0,0.6)') + + if (!simulation.paused) { + simulation.paused = true; + simulation.isChoosing = true; //stops p from un pausing on key down + document.body.style.cursor = "auto"; + document.getElementById("choose-grid").style.pointerEvents = "auto"; + document.getElementById("choose-grid").style.transitionDuration = "0s"; + } + //build level info + document.getElementById("choose-grid").classList.add('choose-grid-no-images'); + document.getElementById("choose-grid").classList.remove('choose-grid'); + document.getElementById("choose-grid").style.gridTemplateColumns = "505px" //adjust this to increase the width of the whole menu, but mostly the center column + + let text = `
+
+
+ + + + + + + + + +
+
+
0.87x damage, 1.22x damage taken per level
+1 boss on each level
+
more mob per level
faster mobs per level
+
0.87x damage, 1.22x damage taken per level
+1 random constraint on each level
+
+1 boss on each level
bosses spawn 1 fewer ${powerUps.orb.tech()}
+
0.87x damage, 1.22x damage taken per level
+1 random constraint on each level
+
0.5x initial damage
2x initial damage taken
+
+
+
${localSettings.difficultyCompleted[1] ? "⚆" : " "}
+
${localSettings.difficultyCompleted[2] ? "⚆" : " "}
+
${localSettings.difficultyCompleted[3] ? "⚆" : " "}
+
${localSettings.difficultyCompleted[4] ? "⚆" : " "}
+
${localSettings.difficultyCompleted[5] ? "⚇" : " "}
+
${localSettings.difficultyCompleted[6] ? "⚇" : " "}
+
+
+
+ confirm difficulty parameters +
+
` + document.getElementById("choose-grid").innerHTML = text + //show level info + document.getElementById("choose-grid").style.opacity = "1" + document.getElementById("choose-grid").style.transitionDuration = "0.3s"; //how long is the fade in on + document.getElementById("choose-grid").style.visibility = "visible" + document.getElementById("choose-difficulty").addEventListener("click", () => { + level.unPause() + document.body.style.cursor = "none"; + //reset hide image style + if (localSettings.isHideImages) { + document.getElementById("choose-grid").classList.add('choose-grid-no-images'); + document.getElementById("choose-grid").classList.remove('choose-grid'); + } else { + document.getElementById("choose-grid").classList.add('choose-grid'); + document.getElementById("choose-grid").classList.remove('choose-grid-no-images'); + } + if (level.levelsCleared === 0 && initialDifficultyMode !== simulation.difficultyMode) { + //remove and respawn all power ups if difficulty mode was changed + for (let i = 0; i < powerUp.length; ++i) Matter.Composite.remove(engine.world, powerUp[i]); + powerUp = []; + level.initialPowerUps() + simulation.trails(30) + } + }); + + let setDifficultyText = function (isReset = true) { + for (let i = 1; i < 7; i++) { + const id = document.getElementById("constraint-" + i) + if (simulation.difficultyMode < i) { + id.style.opacity = "0.15" + } else { + id.style.opacity = "1" + } + } + if (isReset) { + lore.setTechGoal() + localSettings.difficultyMode = simulation.difficultyMode + localSettings.levelsClearedLastGame = 0 //after changing difficulty, reset run history + localSettings.entanglement = undefined //after changing difficulty, reset stored tech + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + } + setDifficultyText(false) + document.getElementById("difficulty-slider").value = simulation.difficultyMode + document.getElementById("difficulty-slider").addEventListener("input", () => { + simulation.difficultyMode = document.getElementById("difficulty-slider").value + setDifficultyText() + level.setConstraints() + }); + for (let i = 1; i < 7; i++) { + document.getElementById("constraint-" + i).addEventListener("click", () => { + simulation.difficultyMode = i + document.getElementById("difficulty-slider").value = simulation.difficultyMode + setDifficultyText() + level.setConstraints() + }); + } + }, + }, coupling: { name: "coupling", color: "#0ae", //"#0cf", @@ -430,19 +503,41 @@ const powerUps = { endCycle: 0, duration: null, //set by "tech: band gap" damage: null, //set by "tech: band gap" + isDefense: false, effect() { powerUps.animatePowerUpGrab('rgba(255, 0, 0, 0.5)') - powerUps.boost.endCycle = m.cycle + Math.floor(Math.max(0, powerUps.boost.endCycle - m.cycle) * 0.6) + powerUps.boost.duration //duration+seconds plus 2/3 of current time left + powerUps.boost.endCycle = simulation.cycle + Math.floor(Math.max(0, powerUps.boost.endCycle - simulation.cycle) * 0.6) + powerUps.boost.duration //duration+seconds plus 2/3 of current time left }, draw() { // console.log(this.endCycle) - if (powerUps.boost.endCycle > m.cycle) { - ctx.strokeStyle = "rgba(255,0,0,0.8)" //m.fieldMeterColor; //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` + // if (powerUps.boost.endCycle > m.cycle) { + // ctx.strokeStyle = "rgba(255,0,0,0.8)" //m.fieldMeterColor; //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` + // ctx.beginPath(); + // const arc = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration + // ctx.arc(m.pos.x, m.pos.y, 28, m.angle - Math.PI * arc, m.angle + Math.PI * arc); //- Math.PI / 2 + // ctx.lineWidth = 4 + // ctx.stroke(); + // } + + if (powerUps.boost.endCycle > simulation.cycle) { + //gel that acts as if the wind is blowing it when player moves + ctx.save(); + ctx.translate(m.pos.x, m.pos.y); + m.velocitySmooth = Vector.add(Vector.mult(m.velocitySmooth, 0.8), Vector.mult(player.velocity, 0.2)) + ctx.rotate(Math.atan2(m.velocitySmooth.y, m.velocitySmooth.x)) ctx.beginPath(); - const arc = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration - ctx.arc(m.pos.x, m.pos.y, 28, m.angle - Math.PI * arc, m.angle + Math.PI * arc); //- Math.PI / 2 - ctx.lineWidth = 4 + const radius = 40 + const mag = 8 * Vector.magnitude(m.velocitySmooth) + radius + ctx.arc(0, 0, radius, -Math.PI / 2, Math.PI / 2); + ctx.bezierCurveTo(-radius, radius, -radius, 0, -mag, 0); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + ctx.bezierCurveTo(-radius, 0, -radius, -radius, 0, -radius); + const time = Math.min(0.5, (powerUps.boost.endCycle - simulation.cycle) / powerUps.boost.duration) + ctx.fillStyle = `rgba(255,0,200,${time})` + ctx.fill() + ctx.strokeStyle = "#f09" + ctx.lineWidth = 0.3 + 4 * time ctx.stroke(); + ctx.restore(); } }, }, @@ -462,7 +557,7 @@ const powerUps = { if (amount !== 0) powerUps.research.count += amount if (tech.isRerollBots && !this.isMakingBots) { let cycle = () => { - const cost = 2 + Math.floor(0.2 * b.totalBots()) + const cost = 2 + Math.floor(0.25 * b.totalBots()) if (m.alive && powerUps.research.count >= cost) { requestAnimationFrame(cycle); this.isMakingBots = true @@ -494,7 +589,7 @@ const powerUps = { } if (tech.isRerollHaste) { if (powerUps.research.count === 0) { - tech.researchHaste = 0.66; + tech.researchHaste = 0.5; b.setFireCD(); } else { tech.researchHaste = 1; @@ -504,11 +599,16 @@ const powerUps = { }, currentRerollCount: 0, use(type) { //runs when you actually research a list of selections, type can be field, gun, or tech - if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { - tech.addJunkTechToPool(tech.junkResearchNumber * 0.01) + if (tech.isJunkResearch && powerUps.research.currentRerollCount < 2) { + tech.addJunkTechToPool(0.01) } else { powerUps.research.changeRerolls(-1) } + if (tech.isResearchDamage) { + tech.damage *= 1.05 + simulation.inGameConsole(`tech.damage *= ${1.05} //peer review`); + tech.addJunkTechToPool(0.01) + } powerUps.research.currentRerollCount++ // if (tech.isBanish && type === 'tech') { // banish researched tech // const banishLength = tech.isDeterminism ? 1 : 3 + tech.extraChoices * 2 @@ -516,12 +616,12 @@ const powerUps = { // const index = powerUps.tech.choiceLog.length - i - 1 // if (powerUps.tech.choiceLog[index] && tech.tech[powerUps.tech.choiceLog[index]]) tech.tech[powerUps.tech.choiceLog[index]].isBanished = true // } - // simulation.makeTextLog(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - banishLength)}`) + // simulation.inGameConsole(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - banishLength)}`) // } if (tech.isResearchReality) { m.switchWorlds() simulation.trails() - simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`); + simulation.inGameConsole(`simulation.amplitude = ${Math.random()}`); } powerUps[type].effect(); }, @@ -530,7 +630,7 @@ const powerUps = { name: "heal", color: "#0eb", size() { - return Math.sqrt(0.1 + 0.25) * 40 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)) * (tech.isFlipFlopOn && tech.isFlipFlopHealth ? Math.sqrt(2) : 1); //(simulation.healScale ** 0.25) gives a smaller radius as heal scale goes down + return Math.sqrt(0.1 + 0.25) * 40 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)); //(simulation.healScale ** 0.25) gives a smaller radius as heal scale goes down }, effect() { if (!tech.isEnergyHealth && m.alive) { @@ -540,30 +640,42 @@ const powerUps = { let overHeal = m.health + heal * simulation.healScale - m.maxHealth //used with tech.isOverHeal const healOutput = Math.min(m.maxHealth - m.health, heal) * simulation.healScale m.addHealth(heal); - if (healOutput > 0) simulation.makeTextLog(`m.health += ${(healOutput).toFixed(3)}`) //
${m.health.toFixed(3)} + if (healOutput > 0) simulation.inGameConsole(`
  m.health += ${(healOutput).toFixed(3)}`) //
${m.health.toFixed(3)} if (tech.isOverHeal && overHeal > 0) { //tech quenching - overHeal *= 2 //double the over heal converted to max health - //make sure overHeal doesn't kill player - if (m.health - overHeal * m.defense() < 0) overHeal = m.health - 0.01 - tech.extraMaxHealth += overHeal //increase max health + tech.extraMaxHealth += 0.3 * overHeal //increase max health m.setMaxHealth(); - m.damage(overHeal); - overHeal *= m.defense() // account for defense after m.damage() so the text log is accurate - simulation.makeTextLog(`m.health -= ${(overHeal).toFixed(3)}`) //
${m.health.toFixed(3)} + simulation.inGameConsole(`
  m.maxHealth += ${(0.3 * overHeal).toFixed(3)}`) simulation.drawList.push({ //add dmg to draw queue x: m.pos.x, y: m.pos.y, - radius: overHeal * 500 * simulation.healScale, - color: simulation.mobDmgColor, + radius: overHeal * 100 * simulation.healScale, + color: "#0eb", time: simulation.drawTime }); + + // overHeal *= 2 //double the over heal converted to max health + // //make sure overHeal doesn't kill player + // if (m.health - overHeal * m.defense() < 0) overHeal = m.health - 0.01 + // if (overHeal > m.maxHealth) overHeal = m.maxHealth //just in case overHeal gets too big + // tech.extraMaxHealth += overHeal //increase max health + // m.setMaxHealth(); + // m.damage(overHeal); + // overHeal *= m.defense() // account for defense after m.damage() so the text log is accurate + // simulation.inGameConsole(`
  m.health -= ${(overHeal).toFixed(3)}`) //
${m.health.toFixed(3)} + // simulation.drawList.push({ //add dmg to draw queue + // x: m.pos.x, + // y: m.pos.y, + // radius: overHeal * 500 * simulation.healScale, + // color: simulation.mobDmgColor, + // time: simulation.drawTime + // }); } else if (overHeal > 0.13) { //if leftover heals spawn a new spammer heal power up requestAnimationFrame(() => { - powerUps.directSpawn(this.position.x, this.position.y, "heal", true, null, overHeal * 40 * (simulation.healScale ** 0.25))// directSpawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { + powerUps.directSpawn(this.position.x, this.position.y, "heal", true, Math.min(1, overHeal) * 40 * (simulation.healScale ** 0.25))// directSpawn(x, y, name, moving = true, mode = null, size = powerUps[name].size()) { }); } if (tech.isHealBrake) { - const totalTime = 900 + const totalTime = 1020 //check if you already have this effect let foundActiveEffect = false for (let i = 0; i < simulation.ephemera.length; i++) { @@ -606,14 +718,14 @@ const powerUps = { } } if (powerUps.healGiveMaxEnergy) { - tech.healMaxEnergyBonus += 0.11 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1) + tech.healMaxEnergyBonus += 0.14 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1) m.setMaxEnergy(); } }, spawn(x, y, size) { //used to spawn a heal with a specific size / heal amount, not normally used - powerUps.directSpawn(x, y, "heal", false, null, size) - if (Math.random() < tech.duplicationChance()) { - powerUps.directSpawn(x, y, "heal", false, null, size) + powerUps.directSpawn(x, y, "heal", false, size) + if (!level.isNextLevelPowerUps && Math.random() < tech.duplicationChance()) { + powerUps.directSpawn(x, y, "heal", false, size) powerUp[powerUp.length - 1].isDuplicated = true } } @@ -629,64 +741,35 @@ const powerUps = { if (b.inventory.length > 0) { powerUps.animatePowerUpGrab('rgba(68, 102, 119,0.25)') if (tech.isAmmoForGun && b.activeGun !== null) { //give extra ammo to one gun only with tech logistics - const target = b.guns[b.activeGun] - if (target.ammo !== Infinity) { + const name = b.guns[b.activeGun] + if (name.ammo !== Infinity) { if (tech.ammoCap) { - const ammoAdded = Math.ceil(target.ammoPack * 0.7 * tech.ammoCap * 0.8 * couplingExtraAmmo) //0.7 is average - target.ammo = ammoAdded - // simulation.makeTextLog(`${target.name}.ammo = ${ammoAdded}`) + name.ammo = Math.ceil(2 * name.ammoPack * tech.ammoCap * couplingExtraAmmo) } else { - const ammoAdded = Math.ceil((0.7 * Math.random() + 0.7 * Math.random()) * target.ammoPack * 0.8 * couplingExtraAmmo) - target.ammo += ammoAdded - // simulation.makeTextLog(`${target.name}.ammo += ${ammoAdded}`) + name.ammo += Math.ceil(2 * (Math.random() + Math.random()) * name.ammoPack * couplingExtraAmmo) } } } else { //give ammo to all guns in inventory - // let textLog = "" for (let i = 0, len = b.inventory.length; i < len; i++) { - const target = b.guns[b.inventory[i]] - if (target.ammo !== Infinity) { + const name = b.guns[b.inventory[i]] + if (name.ammo !== Infinity) { if (tech.ammoCap) { - const ammoAdded = Math.ceil(target.ammoPack * 0.45 * tech.ammoCap * couplingExtraAmmo) //0.45 is average - target.ammo = ammoAdded - // textLog += `${target.name}.ammo = ${ammoAdded}
` + name.ammo = Math.ceil(name.ammoPack * tech.ammoCap * couplingExtraAmmo) } else { //default ammo behavior - const ammoAdded = Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack * couplingExtraAmmo) //Math.ceil(Math.random() * target.ammoPack) - // console.log(ammoAdded, Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack)) - target.ammo += ammoAdded - // textLog += `${target.name}.ammo += ${ammoAdded}
` + name.ammo += Math.ceil((Math.random() + Math.random()) * name.ammoPack * couplingExtraAmmo) } } } - // simulation.makeTextLog(textLog) } - // } else { //give ammo to all guns in inventory - // for (let i = 0, len = b.inventory.length; i < len; i++) { - // const target = b.guns[b.inventory[i]] - // if (target.ammo !== Infinity) { - // if (tech.ammoCap) { - // const ammoAdded = Math.ceil(target.ammoPack * 0.45 * tech.ammoCap) //0.45 is average - // target.ammo = ammoAdded - // simulation.makeTextLog(`${target.name}.ammo = ${ammoAdded}`) - // } else { - // const ammoAdded = Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack) //Math.ceil(Math.random() * target.ammoPack) - // target.ammo += ammoAdded - // simulation.makeTextLog(`${target.name}.ammo += ${ammoAdded}`) - // } - // } - // } - // } simulation.updateGunHUD(); } } }, cancelText(type) { - // if (localSettings.isHideImages) { } - if (tech.isSuperDeterminism) { return `
` - } else if (tech.isCancelTech) { - return `
randomize
` + } else if (tech.isCancelTech && tech.cancelTechCount === 0) { + return `
randomize
` } else if (level.levelsCleared === 0 && localSettings.isTrainingNotAttempted && b.inventory.length === 0) { //don't show cancel if on initial level and haven't done tutorial return `` } else { @@ -697,13 +780,10 @@ const powerUps = { let text = "" if (type === "entanglement") { text += `
entanglement
` - } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { + } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 2) { text += `
` // style = "margin-left: 192px; margin-right: -192px;" - tech.junkResearchNumber = Math.ceil(3 * Math.random()) text += `
` - for (let i = 0; i < tech.junkResearchNumber; i++) { - text += `
` - } + text += `
` text += `
  pseudoscience
` } else if (powerUps.research.count > 0) { text += `
` // style = "margin-left: 192px; margin-right: -192px;" @@ -719,19 +799,16 @@ const powerUps = { let text = `
` if (type === "entanglement") { text += `entanglement` //‌ - } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { + } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 2) { text += `` // style = "margin-left: 192px; margin-right: -192px;" - tech.junkResearchNumber = Math.ceil(3 * Math.random()) text += `
` - for (let i = 0, len = tech.junkResearchNumber; i < len; i++) { - text += `
` - } + text += `
` text += `
  ${tech.isResearchReality ? "alternate reality" : "research"}
` } else if (powerUps.research.count > 0) { text += `` // style = "margin-left: 192px; margin-right: -192px;" text += `
` let researchCap = 18 - if (tech.isCancelTech) researchCap -= 2 + if (tech.isCancelTech && tech.cancelTechCount === 0) researchCap -= 2 if (canvas.width < 1951) researchCap -= 3 if (canvas.width < 1711) researchCap -= 4 for (let i = 0, len = Math.min(powerUps.research.count, researchCap); i < len; i++) { @@ -743,8 +820,8 @@ const powerUps = { } if (tech.isSuperDeterminism) { text += `cancel` - } else if (tech.isCancelTech) { - text += `randomize` + } else if (tech.isCancelTech && tech.cancelTechCount === 0) { + text += `randomize` } else if (level.levelsCleared === 0 && localSettings.isTrainingNotAttempted && b.inventory.length === 0) { text += `` //don't show cancel if on initial level and haven't done tutorial } else { @@ -787,31 +864,13 @@ const powerUps = { } return text }, - // researchAndCancelText(type) { - // let text = "
" - // if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { - // text += `
` // style = "margin-left: 192px; margin-right: -192px;" - // tech.junkResearchNumber = Math.ceil(4 * Math.random()) - // text += `
` - // for (let i = 0; i < tech.junkResearchNumber; i++) text += `
` - // text += `
  pseudoscience
` - // } else if (powerUps.research.count > 0) { - // text += `
` // style = "margin-left: 192px; margin-right: -192px;" - // text += `
` - // for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += `
` - // text += `
  ${tech.isResearchReality?"alternate reality": "research"}
` - // } else { - // text += `
` - // } - // return text + '
' - // }, hideStyle: `style="height:auto; border: none; background-color: transparent;"`, gunText(choose, click) { const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/gun/${b.guns[choose].name}.webp');"` return `
-
-
  ${b.guns[choose].name}
- ${b.guns[choose].descriptionFunction()}
` +
+
  ${b.guns[choose].name}
+ ${b.guns[choose].descriptionFunction()}
` }, fieldText(choose, click) { const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/field/${m.fieldUpgrades[choose].name}${choose === 0 ? Math.floor(Math.random() * 10) : ""}.webp');"` @@ -828,6 +887,15 @@ const powerUps = {
  ${tech.tech[choose].name} ${techCountText}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` }, + instantTechText(choose, click) { + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + const style = localSettings.isHideImages || tech.tech[choose].isLore ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` + //
+ return `
+
+
  ${tech.tech[choose].name} ${techCountText}
+ ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + }, skinTechText(choose, click) { const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` @@ -838,7 +906,7 @@ const powerUps = {
-           ${tech.tech[choose].name} ${techCountText}
+         ${tech.tech[choose].name} ${techCountText}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` }, fieldTechText(choose, click) { @@ -867,36 +935,101 @@ const powerUps = {           ${tech.tech[choose].name} ${techCountText} ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}` }, + // junkTechText(choose, click) { //old code with yahoo images + // const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + // const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-size: contain;background-repeat: no-repeat;background-image: url('img/junk.webp');"` + // if (!localSettings.isHideImages) { + // setTimeout(() => { //delay so that the html element exists + // if (tech.tech[choose].url === undefined) { //if on url has been set yet + // const url = "https://images.search.yahoo.com/search/images?p=" + tech.tech[choose].name; + // fetch(url, { signal: AbortSignal.timeout(1000) }) //give up if it takes over 1 second + // .then((response) => response.text()) + // .then((html) => { + // const parser = new DOMParser(); + // const doc = parser.parseFromString(html, "text/html"); + // const elements = doc.getElementsByClassName("ld"); + // // console.log(i, elements[i].getAttribute("data"), JSON.parse(elements[i].getAttribute("data")).iurl) + // const index = Math.floor(Math.random() * 4) //randomly choose from the first 4 images + // if (parseInt(JSON.parse(elements[index].getAttribute("data")).s.slice(0, -2)) < 500) { //make sure it isn't too big + // tech.tech[choose].url = JSON.parse(elements[index].getAttribute("data")).iurl //store the url + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` //make the url the background image + // } else if (parseInt(JSON.parse(elements[index + 1].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller + // tech.tech[choose].url = JSON.parse(elements[index + 1].getAttribute("data")).iurl + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + // } else if (parseInt(JSON.parse(elements[index + 2].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller + // tech.tech[choose].url = JSON.parse(elements[index + 2].getAttribute("data")).iurl + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + // } + // }); + // } else { + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + // } + // }, 1); + // } + // return `
+ //
+ //
  ${tech.tech[choose].name} ${techCountText}
+ // ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + // }, + // junkTechText(choose, click) { + // const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + // const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-size: contain;background-repeat: no-repeat;background-image: url('img/junk.webp');"` + // if (!localSettings.isHideImages) { + // setTimeout(() => { //delay so that the html element exists + // if (tech.tech[choose].url === undefined) { //if on url has been set yet + // const url = "https://images.search.yahoo.com/search/images?p=" + tech.tech[choose].name; + // fetch(url, { signal: AbortSignal.timeout(1000) }) //give up if it takes over 1 second + // .then((response) => response.text()) + // .then((html) => { + // const parser = new DOMParser(); + // const doc = parser.parseFromString(html, "text/html"); + // const elements = doc.getElementsByClassName("ld"); + // // console.log(i, elements[i].getAttribute("data"), JSON.parse(elements[i].getAttribute("data")).iurl) + // const index = Math.floor(Math.random() * 4) //randomly choose from the first 4 images + // if (parseInt(JSON.parse(elements[index].getAttribute("data")).s.slice(0, -2)) < 500) { //make sure it isn't too big + // tech.tech[choose].url = JSON.parse(elements[index].getAttribute("data")).iurl //store the url + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` //make the url the background image + // } else if (parseInt(JSON.parse(elements[index + 1].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller + // tech.tech[choose].url = JSON.parse(elements[index + 1].getAttribute("data")).iurl + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + // } else if (parseInt(JSON.parse(elements[index + 2].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller + // tech.tech[choose].url = JSON.parse(elements[index + 2].getAttribute("data")).iurl + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + // } + // }); + // } else { + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + // } + // }, 1); + // } + // return `
+ //
+ //
  ${tech.tech[choose].name} ${techCountText}
+ // ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + // }, junkTechText(choose, click) { const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-size: contain;background-repeat: no-repeat;background-image: url('img/junk.webp');"` if (!localSettings.isHideImages) { - setTimeout(() => { //delay so that the html element exists - if (tech.tech[choose].url === undefined) { //if on url has been set yet - const url = "https://images.search.yahoo.com/search/images?p=" + tech.tech[choose].name; - fetch(url, { signal: AbortSignal.timeout(1000) }) //give up if it takes over 1 second - .then((response) => response.text()) - .then((html) => { - const parser = new DOMParser(); - const doc = parser.parseFromString(html, "text/html"); - const elements = doc.getElementsByClassName("ld"); - // console.log(i, elements[i].getAttribute("data"), JSON.parse(elements[i].getAttribute("data")).iurl) - const index = Math.floor(Math.random() * 4) //randomly choose from the first 4 images - if (parseInt(JSON.parse(elements[index].getAttribute("data")).s.slice(0, -2)) < 500) { //make sure it isn't too big - tech.tech[choose].url = JSON.parse(elements[index].getAttribute("data")).iurl //store the url - document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` //make the url the background image - } else if (parseInt(JSON.parse(elements[index + 1].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller - tech.tech[choose].url = JSON.parse(elements[index + 1].getAttribute("data")).iurl - document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` - } else if (parseInt(JSON.parse(elements[index + 2].getAttribute("data")).s.slice(0, -2)) < 500) { //try a different images and see if it is smaller - tech.tech[choose].url = JSON.parse(elements[index + 2].getAttribute("data")).iurl - document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` - } - }); - } else { - document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` - } - }, 1); + // setTimeout(() => { //delay so that the html element exists + // if (tech.tech[choose].url === undefined) { //if on url has been set yet + // const url = `https://api.openverse.engineering/v1/images/?q=${tech.tech[choose].name}`; + // fetch(url, { signal: AbortSignal.timeout(1000) }) //give up if it takes over 1 second + // .then((response) => response.json()) + // .then((responseJson) => { + // if (responseJson.results.length > 0) { + // const index = Math.floor(Math.random() * responseJson.results.length) //randomly choose from the images + // tech.tech[choose].url = responseJson.results[index].url //store the url + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` //make the url the background image + // } + // }); + // } else { + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + // } + // }, 1); + // setTimeout(() => { //delay so that the html element exists + // document.getElementById(`junk-${choose}`).style.backgroundImage = `url('${tech.tech[choose].url}')` + // }, 1); } return `
@@ -925,8 +1058,13 @@ const powerUps = { } // console.log(options.length) if (options.length > 0 || !tech.isSuperDeterminism) { - let totalChoices = Math.min(options.length, (tech.isDeterminism ? 1 : 2 + tech.extraChoices + 2 * (m.fieldMode === 8))) - if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay + let totalChoices = 2 + tech.extraChoices + 3 * (m.fieldMode === 8) - level.fewerChoices + if (tech.isCancelTech && tech.cancelTechCount === 1) { + totalChoices *= 3 + tech.cancelTechCount++ + } + if (tech.isDeterminism) totalChoices = 1 + totalChoices = Math.min(totalChoices, options.length) function removeOption(index) { for (let i = 0; i < options.length; i++) { if (options[i] === index) { @@ -987,9 +1125,13 @@ const powerUps = { for (let i = 1; i < m.fieldUpgrades.length; i++) { //skip field emitter if (i !== m.fieldMode) options.push(i); } - let totalChoices = Math.min(options.length, (tech.isDeterminism ? 1 : 2 + tech.extraChoices + 2 * (m.fieldMode === 8))) - if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay - + let totalChoices = 2 + tech.extraChoices + 3 * (m.fieldMode === 8) - level.fewerChoices + if (tech.isCancelTech && tech.cancelTechCount === 1) { + totalChoices *= 3 + tech.cancelTechCount++ + } + if (tech.isDeterminism) totalChoices = 1 + totalChoices = Math.max(1, Math.min(totalChoices, options.length)) function removeOption(index) { for (let i = 0; i < options.length; i++) { if (options[i] === index) { @@ -1063,16 +1205,21 @@ const powerUps = { } } //set total choices - let totalChoices = (tech.isDeterminism ? 1 : 3 + tech.extraChoices + 2 * (m.fieldMode === 8)) - if (tech.isFlipFlopChoices) totalChoices += tech.isRelay ? (tech.isFlipFlopOn ? -1 : 7) : (tech.isFlipFlopOn ? 7 : -1) //flip the order for relay + let totalChoices = 3 + tech.extraChoices + 3 * (m.fieldMode === 8) - level.fewerChoices + if (tech.isCancelTech && tech.cancelTechCount === 1) { + totalChoices *= 3 + tech.cancelTechCount++ + } + if (tech.isDeterminism) totalChoices = 1 + totalChoices = Math.max(1, Math.min(totalChoices, options.length)) + if (optionLengthNoDuplicates < totalChoices + 1) { //if not enough options for all the choices totalChoices = optionLengthNoDuplicates if (tech.isBanish) { //when you run out of options eject banish for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].name === "decoherence") powerUps.ejectTech(i, true) } - simulation.makeTextLog(`decoherence tech ejected`) - simulation.makeTextLog(`options reset`) + simulation.inGameConsole(`decoherence tech ejected
options reset`) } } if (tech.tooManyTechChoices) { @@ -1097,11 +1244,11 @@ const powerUps = { const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options if (tech.isBanish) { tech.tech[choose].isBanished = true - if (i === 0) simulation.makeTextLog(`options.length = ${optionLengthNoDuplicates} //tech removed from pool by decoherence`) + if (i === 0) simulation.inGameConsole(`options.length = ${optionLengthNoDuplicates} //removed from pool by decoherence`) } removeOption(choose) //move from future options pool to avoid repeats on this selection tech.tech[choose].isRecentlyShown = true //this flag prevents this option from being shown the next time you pick up a tech power up - if (Math.random() < tech.junkChance) { // choose is set to a random JUNK tech + if (Math.random() < tech.junkChance + level.junkAdded) { // choose is set to a random JUNK tech const list = [] for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].isJunk) list.push(i) @@ -1117,6 +1264,8 @@ const powerUps = { text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`) } else if (tech.tech[choose].isSkin) { text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isInstant) { + text += powerUps.instantTechText(choose, `powerUps.choose('tech',${choose})`) } else { //normal tech text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) } @@ -1183,18 +1332,8 @@ const powerUps = { text += powerUps.fieldText(pick, `powerUps.choose('field',${pick})`) } } - // if (tech.isMicroTransactions && powerUps.research.count > 0) { - // const skins = [] //find skins - // for (let i = 0; i < tech.tech.length; i++) { - // if (tech.tech[i].isSkin) skins.push(i) - // } - // const choose = skins[Math.floor(Math.seededRandom(0, skins.length))] //pick an element from the array of options - - // text += `
microtransaction: ${tech.tech[choose].name}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` - // } if (tech.isBrainstorm && !tech.isBrainstormActive && !simulation.isChoosing) { tech.isBrainstormActive = true - let count = 1 let timeStart = performance.now() const cycle = (timestamp) => { @@ -1214,33 +1353,9 @@ const powerUps = { } } requestAnimationFrame(cycle); - - // count++ - // if (count < tech.brainStormDelay * 5 && simulation.isChoosing) { - // if (!(count % tech.brainStormDelay)) { - // powerUps.tech.effect(); - // document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay - // document.body.style.cursor = "auto"; - // document.getElementById("choose-grid").style.transitionDuration = "0s"; - // } - // requestAnimationFrame(cycle); - // } else { - // tech.isBrainstormActive = false - // } } - // if (localSettings.isHideImages) text += powerUps.researchText('tech') document.getElementById("choose-grid").innerHTML = text powerUps.showDraft(); - - //fade in all circles - // requestAnimationFrame(() => { - // var elements = document.getElementsByClassName('circle-grid'); - // for (var i in elements) { - // if (elements.hasOwnProperty(i)) { - // elements[i].style.opacity = '1'; - // } - // } - // }); } } }, @@ -1276,11 +1391,18 @@ const powerUps = { if (!alreadyHasGun) text += powerUps.gunText(choose, `powerUps.choose('gun',${choose})`) } for (let i = 0; i < localSettings.entanglement.techIndexes.length; i++) { //add tech - let choose = localSettings.entanglement.techIndexes[i] - if (tech.tech[choose]) { + let found = false; + let choose = undefined + for (let j = 0; j < tech.tech.length; j++) { + if (localSettings.entanglement.techIndexes[i] === tech.tech[j].name) { + choose = j; + found = true; + break; + } + } + if (found && tech.tech[choose]) { const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; if (choose === null || tech.tech[choose].count + 1 > tech.tech[choose].maxCount || !tech.tech[choose].allowed()) { - // text += `
${tech.tech[choose].name} - incoherent
` text += powerUps.incoherentTechText(choose) } else { if (tech.tech[choose].isFieldTech) { @@ -1293,13 +1415,14 @@ const powerUps = { text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`) } else if (tech.tech[choose].isSkin) { text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isInstant) { + text += powerUps.instantTechText(choose, `powerUps.choose('tech',${choose})`) } else { //normal tech text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) } } } } - // document.getElementById("choose-grid").classList.add("flipX"); document.getElementById("choose-grid").innerHTML = text powerUps.showDraft(); localSettings.entanglement = undefined @@ -1307,18 +1430,17 @@ const powerUps = { } }, }, - spawnDelay(type, num) { - let count = num + spawnDelay(type, count, delay = 2) { + count *= delay let cycle = () => { if (count > 0) { if (m.alive) requestAnimationFrame(cycle); - if (!simulation.paused && !simulation.isChoosing) { //&& !(simulation.cycle % 2) + if (!simulation.paused && !simulation.isChoosing && powerUp.length < 300) { //&& !(simulation.cycle % 2) count-- - const where = { - x: m.pos.x + 50 * (Math.random() - 0.5), - y: m.pos.y + 50 * (Math.random() - 0.5) + if (!(count % delay)) { + const where = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) } + powerUps.spawn(where.x, where.y, type); } - powerUps.spawn(where.x, where.y, type); } } } @@ -1327,7 +1449,7 @@ const powerUps = { onPickUp(who) { powerUps.research.currentRerollCount = 0 if (tech.isTechDamage && who.name === "tech") m.damage(0.1) - if (tech.isMassEnergy) m.energy += 2; + if (tech.isMassEnergy) m.energy += 2 * level.isReducedRegen; if (tech.isMineDrop && bullet.length < 150 && Math.random() < 0.5) { if (tech.isLaserMine && input.down) { b.laserMine(who.position) @@ -1335,18 +1457,7 @@ const powerUps = { b.mine(who.position, { x: 0, y: 0 }, 0) } } - if (tech.isRelay) { - if (tech.isFlipFlopOn) { - tech.isFlipFlopOn = false - if (document.getElementById("tech-switch")) document.getElementById("tech-switch").innerHTML = ` = OFF` - m.eyeFillColor = 'transparent' - } else { - tech.isFlipFlopOn = true //immune to damage this hit, lose immunity for next hit - if (document.getElementById("tech-switch")) document.getElementById("tech-switch").innerHTML = ` = ON` - m.eyeFillColor = m.fieldMeterColor //'#0cf' - } - if (tech.isRelayEnergy) m.setMaxEnergy(); - } + if (level.isNoDamage) level.noDamageCycle = m.cycle }, spawnRandomPowerUp(x, y) { //mostly used after mob dies, doesn't always return a power up if (!tech.isEnergyHealth && (Math.random() * Math.random() - 0.3 > Math.sqrt(m.health)) || Math.random() < 0.04) { //spawn heal chance is higher at low health @@ -1361,12 +1472,7 @@ const powerUps = { powerUps.spawn(x, y, "gun"); return; } - // if (Math.random() < 0.0027 * (22 - tech.totalCount)) { //a new tech has a low chance for each not acquired tech up to 25 - if (Math.random() < 0.005 * (10 - level.levelsCleared)) { //a new tech has a low chance that decreases in later levels - powerUps.spawn(x, y, "tech"); - return; - } - if (Math.random() < 0.0015) { + if (Math.random() < 0.0016) { powerUps.spawn(x, y, "field"); return; } @@ -1374,36 +1480,28 @@ const powerUps = { powerUps.spawn(x, y, "coupling"); return; } - if (tech.isBoostPowerUps && Math.random() < 0.14) { + if (Math.random() < 0.02 || (tech.isBoostPowerUps && Math.random() < 0.14)) { powerUps.spawn(x, y, "boost"); return; } - // if (Math.random() < 0.01) { - // powerUps.spawn(x, y, "research"); - // return; - // } }, randomPowerUpCounter: 0, isFieldSpawned: false, //makes it so a field spawns once but not more times spawnBossPowerUp(x, y) { //boss spawns field and gun tech upgrades if (level.levels[level.onLevel] !== "final") { - // if (level.levelsCleared === 1) powerUps.spawn(x, y, "field") - // if (m.fieldMode === 0 && !m.coupling) { if (!powerUps.isFieldSpawned) { powerUps.isFieldSpawned = true powerUps.spawn(x, y, "field") } else { - powerUps.randomPowerUpCounter++; - powerUpChance(Math.max(level.levelsCleared, 10) * 0.1) + powerUpChance() } - if (!(simulation.difficulty > spawn.secondaryBossThreshold)) { - powerUps.randomPowerUpCounter += 0.6; - powerUpChance(Math.max(level.levelsCleared, 6) * 0.1) + if (simulation.difficultyMode < 4) {//don't spawn second power up on difficulties with a second boss + powerUpChance() } - - function powerUpChance(chanceToFail) { - if (Math.random() * chanceToFail < powerUps.randomPowerUpCounter) { - powerUps.randomPowerUpCounter = 0; + function powerUpChance() { + powerUps.randomPowerUpCounter++ + if (powerUps.randomPowerUpCounter > Math.max(level.levelsCleared, 9) * 0.1 * Math.random()) { + powerUps.randomPowerUpCounter = 0; //reset odds if (Math.random() < 0.97) { powerUps.spawn(x, y, "tech") } else { @@ -1419,8 +1517,6 @@ const powerUps = { } } } - - if (tech.isAddRemoveMaxHealth) { powerUps.spawn(x + 20, y, "tech", false) powerUps.spawn(x - 20, y, "research", false) @@ -1430,20 +1526,11 @@ const powerUps = { powerUps.spawn(x, y - 20, "heal", false) powerUps.spawn(x, y + 40, "heal", false) powerUps.spawn(x, y - 40, "heal", false) - // if (this.isBoss && this.isDropPowerUp) { - // } else { - // const amount = 0.005 - // if (tech.isEnergyHealth) { - // if (m.maxEnergy > amount) { - // tech.healMaxEnergyBonus -= amount - // m.setMaxEnergy(); - // } - // } else if (m.maxHealth > amount) { - // tech.extraMaxHealth -= amount //decrease max health - // m.setMaxHealth(); - // } - // } } + if (tech.isResearchReality) powerUps.spawnDelay("research", 6) + if (tech.isBanish) powerUps.spawnDelay("research", 2) + if (tech.isCouplingNoHit) powerUps.spawnDelay("coupling", 9) + // if (tech.isRerollDamage) powerUps.spawnDelay("research", 1) } }, chooseRandomPowerUp(x, y) { //100% chance to drop a random power up //used in spawn.debris @@ -1453,15 +1540,16 @@ const powerUps = { powerUps.spawn(x, y, "ammo", false); } }, - addResearchToLevel() { //add a random power up to a location that has a mob, mostly used to give each level one randomly placed research - if (mob.length && Math.random() < 0.45 - 0.3 * (simulation.difficultyMode > 4)) { //lower chance on why difficulty + addResearchToLevel() { //add a random power up to a location that has a mob, mostly used to give each level a research + // if (simulation.difficultyMode < 4 && mob.length) { //don't spawn on higher difficulty settings + if ((level.levelsCleared < 17 - simulation.difficultyMode * 3) && mob.length) { //don't spawn late game const index = Math.floor(Math.random() * mob.length) powerUps.spawn(mob[index].position.x, mob[index].position.y, "research"); } }, spawnStartingPowerUps(x, y) { //used for map specific power ups, mostly to give player a starting gun if (level.levelsCleared < 4) { //runs on first 4 levels on all difficulties - if (level.levelsCleared > 1) powerUps.spawn(x, y, "tech") + if (level.levelsCleared > 1 && simulation.difficultyMode < 6) powerUps.spawn(x, y, "tech") if (b.inventory.length === 0) { powerUps.spawn(x, y, "gun", false); //first gun } else if (tech.totalCount === 0) { //first tech @@ -1481,12 +1569,12 @@ const powerUps = { }, ejectTech(choose = 'random', isOverride = false) { if (!simulation.isChoosing || isOverride) { - // console.log(tech.tech[choose].name, tech.tech[choose].count, tech.tech[choose].isNonRefundable) + // console.log(tech.tech[choose].name, tech.tech[choose].count, tech.tech[choose].isInstant) //find which tech you have if (choose === 'random') { const have = [] for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + if (tech.tech[i].count > 0 && !tech.tech[i].isInstant) have.push(i) } // if (have.length === 0) { // for (let i = 0; i < tech.tech.length; i++) { @@ -1496,14 +1584,15 @@ const powerUps = { if (have.length) { choose = have[Math.floor(Math.random() * have.length)] - simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`) + simulation.inGameConsole(`tech.remove("${tech.tech[choose].name}")`) for (let i = 0; i < tech.tech[choose].count; i++) { powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); - // powerUp[powerUp.length - 1].isDuplicated = true } // remove a random tech from the list of tech you have + tech.removeCount += tech.tech[choose].count tech.tech[choose].remove(); + tech.totalCount -= tech.tech[choose].count tech.tech[choose].count = 0; tech.tech[choose].isLost = true; simulation.updateTechHUD(); @@ -1512,15 +1601,16 @@ const powerUps = { } else { return false } - } else if (tech.tech[choose].count && !tech.tech[choose].isNonRefundable) { - simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`) + } else if (tech.tech[choose].count && !tech.tech[choose].isInstant) { + simulation.inGameConsole(`tech.remove("${tech.tech[choose].name}")`) for (let i = 0; i < tech.tech[choose].count; i++) { powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); - powerUp[powerUp.length - 1].isDuplicated = true } // remove a random tech from the list of tech you have tech.tech[choose].remove(); + tech.totalCount -= tech.tech[choose].count + tech.removeCount += tech.tech[choose].count tech.tech[choose].count = 0; tech.tech[choose].isLost = true; simulation.updateTechHUD(); @@ -1532,35 +1622,20 @@ const powerUps = { } }, pauseEjectTech(index) { - if ((tech.isPauseEjectTech || simulation.testing) && !simulation.isChoosing && !tech.tech[index].isNonRefundable) { - if (Math.random() < 0.03 || tech.tech[index].isFromAppliedScience || (tech.tech[index].bonusResearch !== undefined && tech.tech[index].bonusResearch > powerUps.research.count)) { - tech.removeTech(index) - } else { - powerUps.ejectTech(index) - m.damage(0.04) - } + if ((tech.isPauseEjectTech || simulation.testing) && !simulation.isChoosing && !tech.tech[index].isInstant) { + // if (tech.tech[index].bonusResearch !== undefined && tech.tech[index].bonusResearch > powerUps.research.count) { + // tech.removeTech(index) + // } else { + // } + tech.tech[index].frequency = 0 //banish tech + powerUps.ejectTech(index) + if (m.immuneCycle < m.cycle) m.damage(tech.pauseEjectTech * 0.01) + tech.pauseEjectTech *= 1.3 document.getElementById(`${index}-pause-tech`).style.textDecoration = "line-through" document.getElementById(`${index}-pause-tech`).style.animation = "" document.getElementById(`${index}-pause-tech`).onclick = null } }, - // removeRandomTech() { - // const have = [] //find which tech you have - // for (let i = 0; i < tech.tech.length; i++) { - // if (tech.tech[i].count > 0) have.push(i) - // } - // if (have.length) { - // const choose = have[Math.floor(Math.random() * have.length)] - // simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`) - // const totalRemoved = tech.tech[choose].count - // tech.tech[choose].count = 0; - // tech.tech[choose].remove(); // remove a random tech form the list of tech you have - // tech.tech[choose].isLost = true - // simulation.updateTechHUD(); - // return totalRemoved - // } - // return 0 - // }, randomize(where) { //makes a random power up convert into a random different power up //put 10 power ups close together const len = Math.min(10, powerUp.length) @@ -1583,15 +1658,9 @@ const powerUps = { smallIndexes.push(i) } } - if (bigIndexes.length > 0) { - // console.log("at least 1 big will always spilt") - const index = bigIndexes[Math.floor(Math.random() * bigIndexes.length)] - for (let i = 0; i < 3; i++) powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false) - Matter.Composite.remove(engine.world, powerUp[index]); - powerUp.splice(index, 1); - } else if (smallIndexes.length > 2 && Math.random() < 0.33) { - // console.log("no big, at least 3 small can combine") + + if (smallIndexes.length > 2 && Math.random() < 0.66) { // console.log("no big, at least 3 small can combine") for (let j = 0; j < 3; j++) { for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "heal" || powerUp[i].name === "research" || powerUp[i].name === "ammo" || powerUp[i].name === "coupling" || powerUp[i].name === "boost") { @@ -1604,8 +1673,13 @@ const powerUps = { options = ["tech", "gun", "field"] powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false) - } else if (smallIndexes.length > 0) { - // console.log("no big, at least 1 small will swap flavors") + } else if (bigIndexes.length > 0 && Math.random() < 0.5) { // console.log("at least 1 big can spilt") + const index = bigIndexes[Math.floor(Math.random() * bigIndexes.length)] + for (let i = 0; i < 3; i++) powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false) + + Matter.Composite.remove(engine.world, powerUp[index]); + powerUp.splice(index, 1); + } else if (smallIndexes.length > 0) { // console.log("no big, at least 1 small will swap flavors") const index = Math.floor(Math.random() * powerUp.length) options = options.filter(e => e !== powerUp[index].name); //don't repeat the current power up type powerUps.directSpawn(where.x, where.y, options[Math.floor(Math.random() * options.length)], false) @@ -1613,7 +1687,28 @@ const powerUps = { powerUp.splice(index, 1); } }, - directSpawn(x, y, target, moving = true, mode = null, size = powerUps[target].size(), isDuplicated = false) { + spawn(x, y, name, moving = true, size = powerUps[name].size()) { + if ( + (!tech.isSuperDeterminism || (name !== 'research')) && + !(tech.isEnergyNoAmmo && name === 'ammo') + ) { + if (tech.isBoostReplaceAmmo && name === 'ammo') { + name = 'boost' + size = powerUps[name].size() + } + powerUps.directSpawn(x, y, name, moving, size) + if (!level.isNextLevelPowerUps && Math.random() < tech.duplicationChance()) { + powerUps.directSpawn(x, y, name, moving, size, true) + powerUp[powerUp.length - 1].isDuplicated = true + if (tech.isDupEnergy) m.energy *= 2 + } + } + }, + directSpawn(x, y, name, moving = true, size = powerUps[name].size(), isDuplicated = false) { + if (level.isNextLevelPowerUps) { + powerUps.powerUpStorage.push({ name: name, size: size }) + return + } let index = powerUp.length; let properties = { density: 0.001, @@ -1624,9 +1719,9 @@ const powerUps = { category: cat.powerUp, mask: cat.map | cat.powerUp }, - color: powerUps[target].color, - effect: powerUps[target].effect, - name: powerUps[target].name, + color: powerUps[name].color, + effect: powerUps[name].effect, + name: powerUps[name].name, size: size } let polygonSides @@ -1635,25 +1730,11 @@ const powerUps = { properties.isDuplicated = true } else { properties.inertia = Infinity //prevents rotation for circles only - polygonSides = 0 + polygonSides = 12 } powerUp[index] = Matter.Bodies.polygon(x, y, polygonSides, size, properties); - if (mode) powerUp[index].mode = mode if (moving) Matter.Body.setVelocity(powerUp[index], { x: (Math.random() - 0.5) * 15, y: Math.random() * -9 - 3 }); Composite.add(engine.world, powerUp[index]); }, - spawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { - if ( - (!tech.isSuperDeterminism || (target !== 'research')) && - !(tech.isEnergyNoAmmo && target === 'ammo') - ) { - if (tech.isBoostReplaceAmmo && target === 'ammo') target = 'boost' - powerUps.directSpawn(x, y, target, moving, mode, size) - if (Math.random() < tech.duplicationChance()) { - powerUps.directSpawn(x, y, target, moving, mode, size, true) - powerUp[powerUp.length - 1].isDuplicated = true - // if (tech.isPowerUpsVanish) powerUp[powerUp.length - 1].endCycle = simulation.cycle + 300 - } - } - }, + powerUpStorage: [],//used when power ups are sent to the next level (for the constraint, level.isNextLevelPowerUps) }; \ No newline at end of file diff --git a/js/simulation.js b/js/simulation.js index 70d5bb47..86dc5e99 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -184,7 +184,7 @@ const simulation = { fpsCapDefault: 72, //use to change fpsCap back to normal after a hit from a mob isCommunityMaps: false, cyclePaused: 0, - fallHeight: 6000, //below this y position the player dies + fallHeight: 6000, //below this y position the player will teleport to start, take damage, or teleport to the sky based on the value of level.fallMode lastTimeStamp: 0, //tracks time stamps for measuring delta delta: 1000 / 60, //speed of game engine //looks like it has to be 16.6666 to match player input buttonCD: 0, @@ -192,10 +192,11 @@ const simulation = { levelsCleared: 0, difficultyMode: 2, //normal difficulty is 2 difficulty: 0, - dmgScale: null, //set in levels.setDifficulty + constraint: 0, + dmgScale: null, healScale: 1, - accelScale: null, //set in levels.setDifficulty - CDScale: null, //set in levels.setDifficulty + accelScale: null, + CDScale: null, molecularMode: Math.floor(4 * Math.random()), //0 spores, 1 missile, 2 ice IX, 3 drones //randomize molecular assembler field type // dropFPS(cap = 40, time = 15) { // simulation.fpsCap = cap @@ -418,39 +419,64 @@ const simulation = { simulation.boldActiveGunHUD(); }, updateTechHUD() { - - // tech.tech.sort((a, b) => { - // console.log(a.cycle, b.cycle) - // if (a.cycle === undefined && b.cycle !== undefined) return -1; - // if (a.cycle !== undefined && b.cycle === undefined) return 1; - // if (a.cycle === undefined && b.cycle === undefined) return 0; - // if (a.cycle !== b.cycle) return a.cycle - b.cycle; - // }); - let text = "" for (let i = 0, len = tech.tech.length; i < len; i++) { //add tech if (tech.tech[i].isLost) { if (text) text += "
" //add a new line, but not on the first line text += `${tech.tech[i].name}` - } else if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) { + } else if (tech.tech[i].count > 0 && !tech.tech[i].isInstant) { if (text) text += "
" //add a new line, but not on the first line text += tech.tech[i].name - if (tech.tech[i].nameInfo) { - text += tech.tech[i].nameInfo - tech.tech[i].addNameInfo(); - } if (tech.tech[i].count > 1) text += ` (${tech.tech[i].count}x)` } } - document.getElementById("tech").innerHTML = text + document.getElementById("right-HUD").innerHTML = text + + + // let text = "" + // if (simulation.difficultyMode > 2 && level.constraintDescription1) { + // text += `${level.constraintDescription1}` + // // text += `${level.constraintDescription1}` + // } + // if (simulation.difficultyMode > 4 && level.constraintDescription2) { + // text += `
${level.constraintDescription2}` + // } + // for (let i = 0, len = tech.tech.length; i < len; i++) { //add tech + // if (tech.tech[i].isLost) { + // if (text) text += "
" //add a new line, but not on the first line + // text += `${tech.tech[i].name}` + // } else if (tech.tech[i].count > 0 && !tech.tech[i].isInstant) { + // if (text) text += "
" //add a new line, but not on the first line + // text += tech.tech[i].name + // if (tech.tech[i].count > 1) text += ` (${tech.tech[i].count}x)` + // } + // } + // document.getElementById("right-HUD").innerHTML = text + + // let constraints = "" + // if (simulation.difficultyMode > 2 && level.constraintDescription1) { + // constraints += `${level.constraintDescription1}` + // // text += `${level.constraintDescription1}` + // } + // if (simulation.difficultyMode > 4 && level.constraintDescription2) { + // constraints += `
${level.constraintDescription2}` + // } + // let text = "" + // for (let i = 0, len = tech.tech.length; i < len; i++) { //add tech + // if (tech.tech[i].isLost) { + // if (text) text += "
" //add a new line, but not on the first line + // text += `${tech.tech[i].name}` + // } else if (tech.tech[i].count > 0 && !tech.tech[i].isInstant) { + // if (text) text += "
" //add a new line, but not on the first line + // text += tech.tech[i].name + // if (tech.tech[i].count > 1) text += ` (${tech.tech[i].count}x)` + // } + // } + // document.getElementById("right-HUD").innerHTML = constraints + `
` + text + `
` }, lastLogTime: 0, isTextLogOpen: true, - // - // SVGleftMouse: ' ', - // SVGrightMouse: ' ', - makeTextLog(text, time = 240) { + inGameConsole(text, time = 240) { if (!localSettings.isHideHUD && simulation.isTextLogOpen && !build.isExperimentSelection) { if (simulation.lastLogTime > m.cycle) { //if there is an older message document.getElementById("text-log").innerHTML = document.getElementById("text-log").innerHTML + '
' + text; @@ -625,8 +651,8 @@ const simulation = { restoreCamera() { ctx.restore(); }, - trails() { - const swapPeriod = 150 + trails(swapPeriod = 150) { + // const swapPeriod = 150 const len = 30 for (let i = 0; i < len; i++) { setTimeout(function () { @@ -756,11 +782,11 @@ const simulation = { document.getElementById("health").style.display = "inline" document.getElementById("health-bg").style.display = "inline"; if (!localSettings.isHideHUD) { - document.getElementById("tech").style.display = "inline" + document.getElementById("right-HUD").style.display = "inline" document.getElementById("defense-bar").style.display = "inline" document.getElementById("damage-bar").style.display = "inline" } else { - document.getElementById("tech").style.display = "none" + document.getElementById("right-HUD").style.display = "none" document.getElementById("defense-bar").style.display = "none" document.getElementById("damage-bar").style.display = "none" } @@ -786,12 +812,13 @@ const simulation = { } else { Composite.add(engine.world, [player]) } + shuffle(level.constraint) level.populateLevels() - input.endKeySensing(); simulation.ephemera = [] + powerUps.powerUpStorage = [] tech.setupAllTech(); //sets tech to default values - b.removeAllGuns(); + b.resetAllGuns(); tech.duplication = 0; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() @@ -813,7 +840,6 @@ const simulation = { simulation.isChoosing = false; b.setFireMethod() b.setFireCD(); - // simulation.updateTechHUD(); for (let i = 0; i < b.guns.length; i++) b.guns[i].isRecentlyShown = false //reset recently shown back to zero for (let i = 0; i < m.fieldUpgrades.length; i++) m.fieldUpgrades[i].isRecentlyShown = false //reset recently shown back to zero for (let i = 0; i < tech.tech.length; i++) tech.tech[i].isRecentlyShown = false //reset recently shown back to zero @@ -826,17 +852,13 @@ const simulation = { powerUps.boost.endCycle = 0 powerUps.isFieldSpawned = false m.setFillColors(); - // m.maxHealth = 1 - // m.maxEnergy = 1 - // m.energy = 1 input.isPauseKeyReady = true simulation.wipe = function () { //set wipe to normal ctx.clearRect(0, 0, canvas.width, canvas.height); } + m.lastHit = 0 m.hole.isOn = false simulation.paused = false; - // simulation.cycle = 0 - // m.cycle = 0 engine.timing.timeScale = 1; simulation.fpsCap = simulation.fpsCapDefault; simulation.isAutoZoom = true; @@ -846,27 +868,18 @@ const simulation = { level.onLevel = 0; level.levelsCleared = 0; - //resetting difficulty - // simulation.difficulty = 0; - level.setDifficulty() - simulation.difficultyMode = Number(document.getElementById("difficulty-select").value) + level.updateDifficulty() + // level.setConstraints() simulation.clearNow = true; document.getElementById("text-log").style.display = "none" document.getElementById("fade-out").style.opacity = 0; document.title = "n-gon"; - // simulation.makeTextLog(`input.key.up: ["${input.key.up}", "ArrowUp"]`); - // simulation.makeTextLog(`input.key.left: ["${input.key.left}", "ArrowLeft"]`); - // simulation.makeTextLog(`input.key.down: ["${input.key.down}", "ArrowDown"]`); - // simulation.makeTextLog(`input.key.right: ["${input.key.right}", "ArrowRight"]`); - simulation.makeTextLog(`Math.seed = ${Math.initialSeed}`); - simulation.makeTextLog(`const engine = Engine.create(); //simulation begin`); - simulation.makeTextLog(`engine.timing.timeScale = 1`); - // simulation.makeTextLog(`input.key.field: ["${input.key.field}", "MouseRight"]`); - - // document.getElementById("health").style.display = "inline" - // document.getElementById("health-bg").style.display = "inline" + simulation.inGameConsole(`Math.seed = ${Math.initialSeed}`); + simulation.inGameConsole(`const engine = Engine.create(); //simulation begin`); + simulation.inGameConsole(`engine.timing.timeScale = 1`); m.alive = true; + m.definePlayerMass(); m.onGround = false m.lastOnGroundCycle = 0 m.health = 0; @@ -876,23 +889,10 @@ const simulation = { //set to default field tech.healMaxEnergyBonus = 0 - // m.setMaxEnergy(); m.energy = 0 m.immuneCycle = 0; - // simulation.makeTextLog(`${simulation.SVGrightMouse} ${m.fieldUpgrades[m.fieldMode].name}

${m.fieldUpgrades[m.fieldMode].description}`, 600); - // 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}" - // `, 800); m.coupling = 0 m.setField(0) //this calls m.couplingChange(), which sets max health and max energy - // m.energy = 0; //exit testing if (simulation.testing) { simulation.testing = false; @@ -933,22 +933,8 @@ const simulation = { m.energy = m.maxEnergy + (m.energy - m.maxEnergy) * tech.overfillDrain //every second energy above max energy loses 25% if (m.energy > 1000000) m.energy = 1000000 } - if (tech.isFlipFlopEnergy && m.immuneCycle < m.cycle) { - if (tech.isFlipFlopOn) { - if (m.immuneCycle < m.cycle) m.energy += 0.2; - } else { - m.energy -= 0.01; - if (m.energy < 0) m.energy = 0 - } - } - if (tech.relayIce && tech.isFlipFlopOn) { - for (let j = 0; j < tech.relayIce; j++) { - for (let i = 0, len = 3 + Math.ceil(9 * Math.random()); i < len; i++) b.iceIX(2) - } - } - if (m.pos.y > simulation.fallHeight) { // if 4000px deep - if (level.isEndlessFall) { + if (level.fallMode === "start") { //infinite falling. teleport to sky after falling simulation.ephemera.push({ @@ -957,7 +943,6 @@ const simulation = { do() { this.count-- if (this.count < 0 || m.onGround) simulation.removeEphemera(this.name) - // console.log(player.velocity.y) if (player.velocity.y > 70) Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.99 }); if (player.velocity.y > 90) Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.99 }); }, @@ -976,13 +961,36 @@ const simulation = { m.angle = Math.atan2(simulation.mouseInGame.y - m.pos.y, simulation.mouseInGame.x - m.pos.x); // move bots for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType) { - Matter.Body.setPosition(bullet[i], Vector.sub(bullet[i].position, change)); - // Matter.Body.setPosition(bullet[i], Vector.add(player.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); - // Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); - } + if (bullet[i].botType) Matter.Body.setPosition(bullet[i], Vector.sub(bullet[i].position, change)); } - } else { + } else if (level.fallMode === "position") { //fall and stay in the same horizontal position + simulation.ephemera.push({ + name: "slow player", + count: 180, //cycles before it self removes + do() { + this.count-- + if (this.count < 0 || m.onGround) simulation.removeEphemera(this.name) + if (player.velocity.y > 70) Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.99 }); + if (player.velocity.y > 90) Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.99 }); + }, + }) + const before = { x: player.position.x, y: player.position.y, } + const posXClamped = Math.min(Math.max(level.fallModeBounds.left, player.position.x), level.fallModeBounds.right) + Matter.Body.setPosition(player, { x: posXClamped, y: level.enter.y - 4000 }); + + // translate camera smoothly to preserve illusion to endless fall + const change = { x: before.x - posXClamped, y: before.y - player.position.y } + m.transX += change.x + m.transY += change.y + simulation.mouseInGame.x = (simulation.mouse.x - canvas.width2) / simulation.zoom * simulation.edgeZoomOutSmooth + canvas.width2 - m.transX; + simulation.mouseInGame.y = (simulation.mouse.y - canvas.height2) / simulation.zoom * simulation.edgeZoomOutSmooth + canvas.height2 - m.transY; + m.angle = Math.atan2(simulation.mouseInGame.y - m.pos.y, simulation.mouseInGame.x - m.pos.x); + + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) Matter.Body.setPosition(bullet[i], Vector.sub(bullet[i].position, change)); + } + } else { //get hurt and go to start Matter.Body.setVelocity(player, { x: 0, y: 0 }); Matter.Body.setPosition(player, { x: level.enter.x + 50, y: level.enter.y - 20 }); // m.damage(0.02 * simulation.difficultyMode); @@ -1004,7 +1012,7 @@ const simulation = { if (isNaN(player.position.x)) m.death(); if (m.lastKillCycle + 300 > m.cycle) { //effects active for 5 seconds after killing a mob if (tech.isEnergyRecovery && m.immuneCycle < m.cycle) { - m.energy += m.maxEnergy * 0.05 + m.energy += m.maxEnergy * 0.05 * level.isReducedRegen simulation.drawList.push({ //add dmg to draw queue x: m.pos.x, y: m.pos.y - 45, @@ -1016,7 +1024,7 @@ const simulation = { if (tech.isHealthRecovery) { if (tech.isEnergyHealth) { if (m.immuneCycle < m.cycle) { - m.energy += m.maxEnergy * 0.005 + m.energy += m.maxEnergy * 0.005 * level.isReducedRegen simulation.drawList.push({ //add dmg to draw queue x: m.pos.x, y: m.pos.y, @@ -1049,7 +1057,7 @@ const simulation = { do() { if (Matter.Query.point(map, m.pos).length > 0 || Matter.Query.point(map, player.position).length > 0) { this.count-- - // console.log('halp, stuck in map!', Matter.Query.point(map, m.pos)) + if (this.count < 0) { simulation.removeEphemera(this.name) Matter.Body.setVelocity(player, { x: 0, y: 0 }); @@ -1061,7 +1069,6 @@ const simulation = { }, }) } - if (tech.isZeno) { if (tech.isEnergyHealth) { m.energy *= 0.95 @@ -1073,28 +1080,41 @@ const simulation = { } if (tech.cyclicImmunity && m.immuneCycle < m.cycle + tech.cyclicImmunity) m.immuneCycle = m.cycle + tech.cyclicImmunity; //player is immune to damage for 60 cycles - fallCheck = function (who, save = false) { - let i = who.length; - while (i--) { - if (who[i].position.y > simulation.fallHeight) { - if (save) { - Matter.Body.setVelocity(who[i], { x: 0, y: 0 }); - Matter.Body.setPosition(who[i], { - x: level.exit.x + 30 * (Math.random() - 0.5), - y: level.exit.y + 30 * (Math.random() - 0.5) - }); - } else { - Matter.Composite.remove(engine.world, who[i]); - who.splice(i, 1); - } + + + let i = body.length; + while (i--) { + if (body[i].position.y > simulation.fallHeight) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + } + } + i = powerUp.length + while (i--) { + if (powerUp[i].position.y > simulation.fallHeight) { + Matter.Body.setVelocity(powerUp[i], { x: 0, y: 0 }); + if (level.fallMode === "position") { + const posXClamped = Math.min(Math.max(level.fallModeBounds.left, powerUp[i].position.x), level.fallModeBounds.right) + Matter.Body.setPosition(powerUp[i], { x: posXClamped, y: level.enter.y - 3000 }); + } else { + Matter.Body.setPosition(powerUp[i], { + x: level.exit.x + 30 * (Math.random() - 0.5), + y: level.exit.y + 30 * (Math.random() - 0.5) + }); } } - }; - fallCheck(body); - fallCheck(powerUp, true); - let i = mob.length; + } + i = mob.length; while (i--) { - if (mob[i].position.y > simulation.fallHeight) mob[i].death(); + if (mob[i].position.y > simulation.fallHeight) { + if (mob[i].isBoss && level.fallMode === "position") { + Matter.Body.setVelocity(mob[i], { x: 0, y: 0 }); + const posXClamped = Math.min(Math.max(level.fallModeBounds.left, mob[i].position.x), level.fallModeBounds.right) + Matter.Body.setPosition(mob[i], { x: posXClamped, y: level.enter.y - 3000 }); + } else { + mob[i].death(); + } + } } } @@ -1116,7 +1136,7 @@ const simulation = { clearNow: false, clearMap() { level.isProcedural = false; - level.isEndlessFall = false; + level.fallMode = ""; ctx.setTransform(1, 0, 0, 1, 0, 0); if (m.alive) { if (tech.isLongitudinal) b.guns[3].waves = []; //empty array of wave bullets @@ -1135,15 +1155,6 @@ const simulation = { simulation.updateGunHUD(); } - // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun is mine - // if (b.guns[i].name === "mine") { - // b.guns[i].ammo += count - // if (tech.ammoCap) b.guns[i].ammo = Math.min(tech.ammoCap, b.guns[i].ammo) - // simulation.updateGunHUD(); - // break; - // } - // } - if (tech.isMutualism && !tech.isEnergyHealth) { for (let i = 0; i < bullet.length; i++) { if (bullet[i].isMutualismActive) { @@ -1227,7 +1238,7 @@ const simulation = { } //respawn drones in animation frame - requestAnimationFrame(() => { b.delayDrones({ x: level.enter.x + 50, y: level.enter.y - 60 }, droneCount) }); + requestAnimationFrame(() => { b.delayDrones({ x: level.enter.x + 50, y: level.enter.y - 60 }, droneCount, deliveryCount) }); //respawn spores in animation frame let respawnSpores = () => { @@ -1296,7 +1307,7 @@ const simulation = { for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].isDropPowerUp && mob[i].alive) count++ } - count *= 0.25 //to fake the 25% chance, this makes it not random, and more predictable + count *= 0.3 //to fake the 25% chance, this makes it not random, and more predictable let cycle = () => { //run after waiting a cycle for the map to be cleared const types = ["heal", "ammo", "heal", "ammo", "research", "coupling", "boost", "tech", "gun", "field"] for (let i = 0; i < count; i++) powerUps.spawnDelay(types[Math.floor(Math.random() * types.length)], 1) @@ -1370,7 +1381,6 @@ const simulation = { // window.getSelection().addRange(range); // document.execCommand("copy"); // window.getSelection().removeAllRanges(); - // console.log(`spawn.mapRect(${simulation.getCoords.pos1.x}, ${simulation.getCoords.pos1.y}, ${simulation.getCoords.pos2.x - simulation.getCoords.pos1.x}, ${simulation.getCoords.pos2.y - simulation.getCoords.pos1.y}); //`); // } // } // }, @@ -1544,15 +1554,9 @@ const simulation = { const circleCollisions = []; for (const line of outerCollisions) { for (const vertex of line) { - // console.log('hi') const distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2) const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); - // const queryPoint = { - // x: Math.cos(angle) * (distance - 1) + pos.x, - // y: Math.sin(angle) * (distance - 1) + pos.y - // } const queryPoint = { x: Math.cos(angle + Math.PI) + vertex.x, y: Math.sin(angle + Math.PI) + vertex.y } - if (Math.abs(distance - radius) < 1 && Matter.Query.ray(map, pos, queryPoint).length == 0) circleCollisions.push(vertex) } } @@ -1917,7 +1921,6 @@ const simulation = { const y = round(simulation.constructMouseDownPosition.y) const dx = Math.max(25, round(simulation.mouseInGame.x) - x) const dy = Math.max(25, round(simulation.mouseInGame.y) - y) - // console.log(e.button) if (e.button === 1) { if (level.isProcedural) { simulation.outputMapString(`spawn.randomMob(x+${x}, ${y}, 0);\n`); @@ -1964,9 +1967,20 @@ const simulation = { } }); + document.body.addEventListener("wheel", (e) => { + if (e.deltaY > 0) { + simulation.setZoom(simulation.zoomScale / 0.9) + } else { + simulation.setZoom(simulation.zoomScale * 0.9) + } + }); + + + + //undo last element added after you press z - document.body.addEventListener("keydown", (e) => { // e.keyCode z=90 m=77 b=66 shift = 16 c = 67 - if (simulation.testing && e.keyCode === 90 && simulation.constructMapString.length) { + document.body.addEventListener("keydown", (event) => { // e.keyCode z=90 m=77 b=66 shift = 16 c = 67 + if (simulation.testing && event.code === "KeyZ" && simulation.constructMapString.length) { if (simulation.constructMapString[simulation.constructMapString.length - 1][6] === 'm') { //remove map from current level const index = map.length - 1 Matter.Composite.remove(engine.world, map[index]); diff --git a/js/spawn.js b/js/spawn.js index 691729b1..b0f42072 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -6,8 +6,8 @@ const spawn = { "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "powerUpBossBaby", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss", "snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "slashBoss", "shieldingBoss", - "timeSkipBoss", "dragonFlyBoss", "beetleBoss", "sneakBoss", "mantisBoss", - "laserLayerBoss" + "timeSkipBoss", "dragonFlyBoss", "beetleBoss", "sneakBoss", "mantisBoss", "laserLayerBoss", + "snakeBoss" ], bossTypeSpawnOrder: [], //preset list of boss names calculated at the start of a run by the randomSeed bossTypeSpawnIndex: 0, //increases as the boss type cycles @@ -48,7 +48,8 @@ const spawn = { // spawn.pickList.push(spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]); }, spawnChance(chance) { - return Math.random() < chance + 0.07 * simulation.difficulty && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1) + const difficultyChance = simulation.difficultyMode === 1 ? 1 : simulation.difficulty + return (Math.random() < chance + 0.07 * difficultyChance) && (mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1)) }, randomMob(x, y, chance = 1) { if (spawn.spawnChance(chance) || chance === Infinity) { @@ -112,32 +113,18 @@ const spawn = { } } }, - secondaryBossThreshold: 22, secondaryBossChance(x, y) { - // if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { - // tech.isScaleMobsWithDuplication = true - // spawn.randomLevelBoss(x, y); - // tech.isScaleMobsWithDuplication = false - // return true - // } else if (tech.isResearchBoss) { - // if (powerUps.research.count > 2) { - // powerUps.research.changeRerolls(-3) - // simulation.makeTextLog(`m.research -= 3
${powerUps.research.count}`) - // } else { - // tech.addJunkTechToPool(0.49) - // } - // spawn.randomLevelBoss(x, y); - // return true - // } - if (simulation.difficulty > spawn.secondaryBossThreshold) { //starts on hard mode level 6, level 12 on easy, level 4 on why? + if (simulation.difficultyMode > 3 && level.levelsCleared > 1) { spawn.randomLevelBoss(x, y); + powerUps.directSpawn(x - 30, y, "ammo"); + powerUps.directSpawn(x + 30, y, "ammo"); } else { return false } }, //mob templates ********************************************************************************************* //*********************************************************************************************************** - MACHO(x = m.pos.x, y = m.pos.y) { //immortal mob that follows player //if you have the tech it spawns at start of every level at the player + darkMatter(x = m.pos.x, y = m.pos.y) { //immortal mob that follows player //if you have the tech it spawns at start of every level at the player mobs.spawn(x, y, 3, 0.1, "transparent"); let me = mob[mob.length - 1]; me.stroke = "transparent" @@ -150,46 +137,57 @@ const spawn = { me.collisionFilter.category = 0; me.collisionFilter.mask = 0; //cat.player //| cat.body me.chaseSpeed = 3.3 - me.isMACHO = true; + me.isDarkMatter = true; me.frictionAir = 0.006 me.onDeath = function () { - tech.isHarmMACHO = false; + tech.isHarmDarkMatter = false; } me.do = function () { if (!simulation.isTimeSkipping) { + const scale = ((tech.isMoveDarkMatter || tech.isNotDarkMatter) ? 1.6 : 1) * level.isReducedRegen const sine = Math.sin(simulation.cycle * 0.015) - this.radius = 55 * tech.isDarkStar + 370 * (1 + 0.1 * sine) + this.radius = 111 * tech.isDarkStar + 370 * (1 + 0.1 * sine) //chase player const sub = Vector.sub(player.position, this.position) const mag = Vector.magnitude(sub) // follow physics - // Matter.Body.setVelocity(this, { x: 0, y: 0 }); - // const where = Vector.add(this.position, Vector.mult(Vector.normalise(sub), this.chaseSpeed)) - // if (mag > 10) Matter.Body.setPosition(this, { x: where.x, y: where.y }); + if (tech.isMoveDarkMatter && m.crouch && input.down) { + Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.97), Vector.mult(player.velocity, 0.03))) + Matter.Body.setPosition(this, Vector.add(Vector.mult(this.position, 0.95), Vector.mult(player.position, 0.05))) + } - //realistic physics - const force = Vector.mult(Vector.normalise(sub), 0.000000003) + const force = Vector.mult(Vector.normalise(sub), 0.000000003 * (this.distanceToPlayer() > 4000 ? 3 : 1)) this.force.x += force.x this.force.y += force.y - - if (mag < this.radius) { //buff to player when inside radius - tech.isHarmMACHO = true; - - //draw halo - ctx.strokeStyle = "rgba(80,120,200,0.2)" //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, 36, 0, 2 * Math.PI); - ctx.lineWidth = 10; - ctx.stroke(); - // ctx.strokeStyle = "rgba(255,255,0,0.17)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` - // ctx.beginPath(); - // ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI); - // ctx.lineWidth = 30; - // ctx.stroke(); + if (tech.isNotDarkMatter) { + if (mag < this.radius) { //buff to player when inside radius + tech.isHarmDarkMatter = false; + } else { + tech.isHarmDarkMatter = true; + //draw halo + ctx.strokeStyle = "rgba(80,120,200,0.2)" //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 36, 0, 2 * Math.PI); + ctx.lineWidth = 10; + ctx.stroke(); + if (tech.isDarkEnergy) m.energy += 0.0017 * scale + } } else { - tech.isHarmMACHO = false; + if (mag < this.radius) { //buff to player when inside radius + tech.isHarmDarkMatter = true; + //draw halo + ctx.strokeStyle = "rgba(80,120,200,0.2)" //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 36, 0, 2 * Math.PI); + ctx.lineWidth = 10; + ctx.stroke(); + if (tech.isDarkEnergy) m.energy += 0.0017 * scale + } else { + tech.isHarmDarkMatter = false; + } } + //draw outline ctx.beginPath(); ctx.arc(this.position.x, this.position.y, this.radius + 15, 0, 2 * Math.PI); @@ -203,14 +201,13 @@ const spawn = { for (let i = 0, len = mob.length; i < len; ++i) { if (mob[i].alive && !mob[i].isShielded) { if (Vector.magnitude(Vector.sub(this.position, mob[i].position)) - mob[i].radius < this.radius) { - mob[i].damage(0.025 * m.dmgScale); - // mob[i].locatePlayer();// - + const dmg = 0.03 * m.dmgScale * scale + mob[i].damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: mob[i].position.x, y: mob[i].position.y, radius: mob[i].radius + 8, - color: `rgba(10,0,40,0.1)`, // random hue, but not red + color: `rgba(10,0,40,0.1)`, time: 4 }); } @@ -254,12 +251,12 @@ const spawn = { //aoe damage to player if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(player.position, this.position)) < this.radius) { - const DRAIN = tech.isRadioactiveResistance ? 0.05 * 0.25 : 0.05 + const DRAIN = tech.isRadioactiveResistance ? 0.05 * 0.2 : 0.05 if (m.energy > DRAIN) { if (m.immuneCycle < m.cycle) m.energy -= DRAIN } else { m.energy = 0; - m.damage((tech.isRadioactiveResistance ? 0.005 * 0.25 : 0.005) * simulation.dmgScale) + m.damage((tech.isRadioactiveResistance ? 0.005 * 0.2 : 0.005) * simulation.dmgScale) simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, @@ -927,6 +924,12 @@ const spawn = { me.onDeath = function () { if (!this.hasRunDeathScript) { this.hasRunDeathScript = true + //record win on this difficulty level to show up in the difficulty settings as a + if (!simulation.isCheating) { + localSettings.difficultyCompleted[simulation.difficultyMode] = true + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + //make a block body to replace this one //this body is too big to leave behind in the normal way mobs.replace() const len = body.length; @@ -960,9 +963,9 @@ const spawn = { //add lore level as next level if player took lore tech earlier in the game if (lore.techCount > (lore.techGoal - 1) && !simulation.isCheating) { - simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`, 360); + simulation.inGameConsole(`undefined = ${lore.techCount}/${lore.techGoal}`, 360); setTimeout(function () { - simulation.makeTextLog(`level.levels.push("null")`, 720); + simulation.inGameConsole(`level.levels.push("null")`, 720); unlockExit() level.levels.push("null") }, 4000); @@ -974,14 +977,14 @@ const spawn = { if (!simulation.paused && !simulation.onTitlePage) { count++ if (count < 660) { - if (count === 1) simulation.makeTextLog(`//enter testing mode to set level.levels.length to Infinite`); - if (!(count % 60)) simulation.makeTextLog(`simulation.analysis = ${((count / 60 - Math.random()) * 0.1).toFixed(3)}`); + if (count === 1 && simulation.difficultyMode < 5) simulation.inGameConsole(`//enter testing mode to set level.levels.length to Infinite`); + if (!(count % 60)) simulation.inGameConsole(`simulation.analysis = ${((count / 60 - Math.random()) * 0.1).toFixed(3)}`); } else if (count === 660) { - simulation.makeTextLog(`simulation.analysis = 1 //analysis complete`); + simulation.inGameConsole(`simulation.analysis = 1 //analysis complete`); } else if (count === 780) { - simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`) + simulation.inGameConsole(`undefined = ${lore.techCount}/${lore.techGoal}`) } else if (count === 1020) { - simulation.makeTextLog(`Engine.clear(engine) //simulation successful`); + simulation.inGameConsole(`Engine.clear(engine) //simulation successful`); } else if (count === 1260) { // tech.isImmortal = false; // m.death() @@ -1011,10 +1014,10 @@ const spawn = { return } } - if (simulation.testing) { + if (simulation.testing || simulation.difficultyMode > 5) { unlockExit() setTimeout(function () { - simulation.makeTextLog(`level.levels.length = Infinite`); + simulation.inGameConsole(`level.levels.length = Infinite`); }, 1500); } else { if (!simulation.onTitlePage) requestAnimationFrame(loop); @@ -1022,8 +1025,6 @@ const spawn = { } requestAnimationFrame(loop); } - // for (let i = 0; i < 3; i++) - level.difficultyIncrease(simulation.difficultyMode) //ramp up damage //remove power Ups, to avoid spamming console function removeAll(array) { for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]); @@ -1267,7 +1268,7 @@ const spawn = { const me = mob[mob.length - 1]; me.isBoss = true; Matter.Body.setDensity(me, 0.002); //normal density even though its a boss - me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); //extra reduction for a boss, because normal density + me.damageReduction = 0.04 me.frictionAir = 0.01; me.accelMag = 0.0002; me.onDeath = function () { @@ -1431,9 +1432,9 @@ const spawn = { me.seePlayerFreq = Math.floor(11 + 7 * Math.random()) me.seeAtDistance2 = 1400000; me.cellMassMax = 70 - me.collisionFilter.mask = cat.player | cat.bullet //| cat.body | cat.map - Matter.Body.setDensity(me, 0.0001 + 0.00002 * simulation.difficulty) // normal density is 0.001 - me.damageReduction = 0.17 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); //me.damageReductionGoal + me.collisionFilter.mask = cat.player | cat.bullet | cat.body// | cat.map + Matter.Body.setDensity(me, 0.00012 + 0.000008 * simulation.difficulty) // normal density is 0.001 + me.damageReduction = 0.17 const k = 642 //k=r^2/m me.split = function () { @@ -1518,7 +1519,7 @@ const spawn = { me.onHit = function () { //run this function on hitting player this.explode(); }; - me.damageReduction = 0.14 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.damageReduction = 0.14 me.doAwake = function () { this.alwaysSeePlayer(); this.checkStatus(); @@ -1633,7 +1634,7 @@ const spawn = { powerUps.spawnRandomPowerUp(this.position.x, this.position.y) // manual power up spawn to avoid spawning too many tech with "symbiosis" } } - me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.2 //required setup for invulnerable // me.isInvulnerable = false me.invulnerabilityCountDown = 0 @@ -1691,14 +1692,15 @@ const spawn = { powerUps.spawnBossPowerUp(me.position.x, me.position.y) powerUps.spawn(me.position.x, me.position.y, "heal"); powerUps.spawn(me.position.x, me.position.y, "ammo"); + powerUps.spawn(me.position.x, me.position.y, "ammo"); } else if (!m.isCloak) { me.foundPlayer(); } - me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.21 me.isInvulnerable = true me.startingDamageReduction = me.damageReduction me.damageReduction = 0 - me.invulnerabilityCountDown = 25 + simulation.difficulty + me.invulnerabilityCountDown = 30 + simulation.difficulty me.onHit = function () { //run this function on hitting player if (powerUps.ejectTech()) { powerUps.ejectGraphic("150, 138, 255"); @@ -1713,10 +1715,7 @@ const spawn = { if (vertices > 3) { this.isDropPowerUp = false; spawn.powerUpBossBaby(this.position.x, this.position.y, vertices - 1) - Matter.Body.setVelocity(mob[mob.length - 1], { - x: this.velocity.x, - y: this.velocity.y - }) + Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x, y: this.velocity.y }) } for (let i = 0; i < powerUp.length; i++) powerUp[i].collisionFilter.mask = cat.map | cat.powerUp }; @@ -1761,7 +1760,7 @@ const spawn = { me.seeAtDistance2 = 1000000; me.accelMag = 0.0002 + 0.0004 * simulation.accelScale; Matter.Body.setDensity(me, 0.0003); //normal is 0.001 - me.collisionFilter.mask = cat.bullet | cat.player //| cat.body + me.collisionFilter.mask = cat.bullet | cat.player | cat.body me.memory = Infinity; me.seePlayerFreq = 30 me.lockedOn = null; @@ -1770,15 +1769,16 @@ const spawn = { powerUps.spawnBossPowerUp(me.position.x, me.position.y) powerUps.spawn(me.position.x, me.position.y, "heal"); powerUps.spawn(me.position.x, me.position.y, "ammo"); + powerUps.spawn(me.position.x, me.position.y, "ammo"); } else if (!m.isCloak) { me.foundPlayer(); } - me.damageReduction = 0.15 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - // me.isInvulnerable = true - // me.startingDamageReduction = me.damageReduction - // me.damageReduction = 0 - // me.invulnerabilityCountDown = 60 + simulation.difficulty * 2 + me.damageReduction = 0.23 + me.isInvulnerable = true + me.startingDamageReduction = me.damageReduction + me.damageReduction = 0 + me.invulnerabilityCountDown = 30 + simulation.difficulty me.onHit = function () { //run this function on hitting player if (powerUps.ejectTech()) { @@ -1794,77 +1794,41 @@ const spawn = { if (vertices > 3) { this.isDropPowerUp = false; spawn.powerUpBoss(this.position.x, this.position.y, vertices - 1) - Matter.Body.setVelocity(mob[mob.length - 1], { - x: this.velocity.x, - y: this.velocity.y - }) + Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x, y: this.velocity.y }) } for (let i = 0; i < powerUp.length; i++) powerUp[i].collisionFilter.mask = cat.map | cat.powerUp + for (let i = 0; i < 40; i++) this.colors(); }; - - //steal all power ups - // for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { - // powerUp[i].collisionFilter.mask = 0 - // Matter.Body.setPosition(powerUp[i], this.vertices[i]) - // Matter.Body.setVelocity(powerUp[i], { - // x: 0, - // y: 0 - // }) - // } - // me.powerUpList = [] - // me.constrainPowerUps = function() { - // for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { - // //remove other constraints on power up - // for (let i = 0, len = cons.length; i < len; ++i) { - // if (cons[i].bodyB === powerUp[i] || cons[i].bodyA === powerUp[i]) { - // Matter.Composite.remove(engine.world, cons[i]); - // cons.splice(i, 1); - // break; - // } - // } - - // //add to list - // this.powerUpList.push(powerUp[i]) - // //position and stop - // powerUp[i].collisionFilter.mask = 0 - // Matter.Body.setPosition(powerUp[i], this.vertices[i]) - // Matter.Body.setVelocity(powerUp[i], { x: 0, y: 0 }) - // //add constraint - // cons[cons.length] = Constraint.create({ - // pointA: this.vertices[i], - // bodyB: powerUp[i], - // stiffness: 1, - // damping: 1 - // }); - // Composite.add(engine.world, cons[cons.length - 1]); - // } - // for (let i = 0; i < this.powerUpList.length; i++) {} - // } - // me.constrainPowerUps() + me.colors = function () { + const unit = Vector.rotate({ x: 1, y: 0 }, Math.random() * 6.28) + const where = Vector.add(this.position, Vector.mult(unit, 700 - 500 * Math.random() * Math.random())) + const colors = ["#0ae", "#f55", "#f7b", "#0eb", "#467", "hsl(246,100%,77%)", "#0cf", "#26a"] + simulation.drawList.push({ //add dmg to draw queue + x: where.x, + y: where.y, + radius: 5 + 10 * Math.random(), + color: colors[Math.floor(Math.random() * colors.length)], //#0cf + time: 17//4 + Math.floor(15 * Math.random()) + }); + } me.do = function () { this.stroke = `hsl(0,0%,${80 + 25 * Math.sin(simulation.cycle * 0.01)}%)` - // if (this.isInvulnerable) { - // if (this.invulnerabilityCountDown > 0) { - // this.invulnerabilityCountDown-- - // ctx.beginPath(); - // let vertices = this.vertices; - // ctx.moveTo(vertices[0].x, vertices[0].y); - // for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); - // ctx.lineTo(vertices[0].x, vertices[0].y); - // ctx.lineWidth = 20; - // ctx.strokeStyle = "rgba(255,255,255,0.7)"; - // ctx.stroke(); - // } else { - // this.isInvulnerable = false - // this.damageReduction = this.startingDamageReduction - // } - // } - //steal all power ups - // for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { - // powerUp[i].collisionFilter.mask = 0 - // Matter.Body.setPosition(powerUp[i], this.vertices[i]) - // Matter.Body.setVelocity(powerUp[i], { x: 0, y: 0 }) - // } + if (this.isInvulnerable) { + if (this.invulnerabilityCountDown > 0) { + this.invulnerabilityCountDown-- + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } else { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + } + } if (this.alive) { for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { powerUp[i].collisionFilter.mask = 0 @@ -1876,6 +1840,9 @@ const spawn = { this.seePlayerCheckByDistance(); this.attraction(); this.checkStatus(); + + //aura around boss so it can bee seen more easily even when inside walls + if (!(simulation.cycle % 5)) this.colors(); }; }, @@ -2089,8 +2056,8 @@ const spawn = { this.death(); //hit player if (Vector.magnitude(Vector.sub(this.position, player.position)) < this.explodeRange && m.immuneCycle < m.cycle) { - m.damage(0.01 * simulation.dmgScale * (tech.isRadioactiveResistance ? 0.25 : 1)); - m.energy -= 0.1 * (tech.isRadioactiveResistance ? 0.25 : 1) + m.damage(0.01 * simulation.dmgScale * (tech.isRadioactiveResistance ? 0.2 : 1)); + m.energy -= 0.1 * (tech.isRadioactiveResistance ? 0.2 : 1) if (m.energy < 0) m.energy = 0 } const range = this.explodeRange + 50 //mines get a slightly larger range to explode @@ -2154,7 +2121,7 @@ const spawn = { mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); let me = mob[mob.length - 1]; me.isBoss = true; - me.damageReduction = 0.09 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.12 me.accelMag = 0.05; //jump height me.g = 0.003; //required if using this.gravity me.frictionAir = 0.01; @@ -2204,7 +2171,7 @@ const spawn = { // powerUps.spawnBossPowerUp(this.position.x, this.position.y) // }; // me.lastSpeed = me.speed - // me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.damageReduction = 0.25 // me.do = function() { // // this.armor(); // this.gravity(); @@ -2276,10 +2243,7 @@ const spawn = { me.fill = "#28b"; me.rememberFill = me.fill; me.cd = 0; - me.burstDir = { - x: 0, - y: 0 - }; + me.burstDir = { x: 0, y: 0 }; me.frictionAir = 0.022; me.lookTorque = 0.0000014; me.restitution = 0; @@ -2325,7 +2289,7 @@ const spawn = { me.seeAtDistance2 = (me.eventHorizon + 400) * (me.eventHorizon + 400); //vision limit is event horizon me.accelMag = 0.00012 * simulation.accelScale; me.frictionAir = 0.025; - me.collisionFilter.mask = cat.player | cat.bullet //| cat.body + me.collisionFilter.mask = cat.player | cat.bullet | cat.body me.memory = Infinity; Matter.Body.setDensity(me, 0.015); //extra dense //normal is 0.001 //makes effective life much larger me.do = function () { @@ -2383,6 +2347,7 @@ const spawn = { ctx.fill(); } } + if (level.isMobShields) spawn.shield(me, x, y); }, // timeBoss(x, y, radius = 25) { // mobs.spawn(x, y, 12, radius, "#000"); @@ -2400,7 +2365,7 @@ const spawn = { // me.onDeath = function() { // powerUps.spawnBossPowerUp(this.position.x, this.position.y) // }; - // me.damageReduction = 0.23 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.damageReduction = 0.23 // me.do = function() { // //keep it slow, to stop issues from explosion knock backs // if (!(simulation.cycle % this.seePlayerFreq)) { @@ -2480,7 +2445,7 @@ const spawn = { // toMe(bullet, this.position, this.eventHorizon)) } }; - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.27 me.do = function () { //keep it slow, to stop issues from explosion knock backs if (this.speed > 1) { @@ -2583,7 +2548,7 @@ const spawn = { let me = mob[mob.length - 1]; Matter.Body.setDensity(me, 0.003); //extra dense //normal is 0.001 //makes effective life much larger and damage on collision me.isBoss = true; - me.damageReduction = 0.13 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) //normal is 1, most bosses have 0.25 + me.damageReduction = 0.13 //normal is 1, most bosses have 0.25 targets.push(me.id) //add to shield protection me.friction = 0; @@ -2672,7 +2637,7 @@ const spawn = { let me = mob[mob.length - 1]; me.babyList = [] //list of mobs that are apart of this boss Matter.Body.setDensity(me, 0.0015); //extra dense //normal is 0.001 //makes effective life much larger and damage on collision - me.damageReduction = 0.13 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) //normal is 1, most bosses have 0.25 + me.damageReduction = 0.14 //normal is 1, most bosses have 0.25 me.isBoss = true; me.friction = 0; @@ -2835,7 +2800,7 @@ const spawn = { for (let i = 0; i < b.inventory.length; i++) { const gun = b.guns[b.inventory[i]] if (gun.ammo > 0 && gun.ammo !== Infinity) { - gun.ammo -= Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * gun.ammoPack) //Math.ceil(Math.random() * target.ammoPack) + gun.ammo -= Math.ceil((Math.random() + Math.random()) * gun.ammoPack) if (gun.ammo < 0) gun.ammo = 0 isRemovedAmmo = true } @@ -2988,7 +2953,7 @@ const spawn = { me.laserRange = 350; me.seeAtDistance2 = 2000000; me.isBoss = true; - me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) // me.damageReductionGoal + me.damageReduction = 0.38 // me.damageReductionGoal me.showHealthBar = false; //drawn in this.awake me.delayLimit = 60 + Math.floor(30 * Math.random()); @@ -3159,7 +3124,7 @@ const spawn = { mobs.spawn(x, y, 7, radius, '#16576b'); let me = mob[mob.length - 1]; Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger - // me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.damageReduction = 0.04 me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front Matter.Body.rotate(me, Math.random() * Math.PI * 2); @@ -3262,7 +3227,7 @@ const spawn = { mobs.spawn(x, y, 7, radius, color); let me = mob[mob.length - 1]; Matter.Body.setDensity(me, 0.0025); //extra dense //normal is 0.001 //makes effective life much larger - // me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.damageReduction = 0.04 me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front Matter.Body.rotate(me, Math.random() * Math.PI * 2); @@ -3317,7 +3282,7 @@ const spawn = { this.wing(this.angle - 2.1 - flapArc * Math.sin(simulation.cycle * this.flapRate), this.flapRadius) const seeRange = 550 + 35 * simulation.difficultyMode; - if (this.distanceToPlayer() < seeRange) { + if (this.distanceToPlayer() < 1000) { best = { x: null, y: null, @@ -3413,7 +3378,7 @@ const spawn = { let me = mob[mob.length - 1]; me.isBoss = true; Matter.Body.setDensity(me, 0.005); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.07 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.08 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.nextHealthThreshold = 0.75 @@ -3604,7 +3569,7 @@ const spawn = { let me = mob[mob.length - 1]; me.isBoss = true; Matter.Body.setDensity(me, 0.004); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.12 me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front Matter.Body.rotate(me, Math.random() * Math.PI * 2); @@ -3696,7 +3661,7 @@ const spawn = { mobs.spawn(x, y, 3, radius, "rgb(0,235,255)"); let me = mob[mob.length - 1]; me.isBoss = true; - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.27 me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front Matter.Body.rotate(me, Math.random() * Math.PI * 2); @@ -3802,7 +3767,7 @@ const spawn = { Matter.Body.rotate(me, Math.PI * 0.1); Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger me.isBoss = true; - me.damageReduction = 0.034 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.038 me.frictionStatic = 0; me.friction = 0; @@ -3860,6 +3825,229 @@ const spawn = { this.checkStatus(); }; }, + snakeBoss(x, y) { + mobs.spawn(x, y, 0, 25, `rgba(255,0,200)`); //"rgb(221,102,119)" + let me = mob[mob.length - 1]; + me.stroke = "transparent"; //used for drawGhost + me.isUnblockable = true; + Matter.Body.setDensity(me, 0.033); //extra dense //normal is 0.001 //makes effective life much larger + me.isBoss = true; + me.damageReduction = 0.5 + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.nextHealthThreshold = 0.75 + me.invulnerableCount = 0 + me.history = [] + for (let i = 0; i < 20; i++) { + me.history.push({ x: me.position.x + i, y: me.position.y }) + } + me.frictionStatic = 0; + me.friction = 0; + me.memory = 240 + me.seePlayerFreq = 55 + me.delay = 5 + 2 * simulation.CDScale;//8 + 3 * simulation.CDScale; + me.nextBlinkCycle = me.delay; + me.JumpDistance = 0//set in redMode() + // spawn.shield(me, x, y, 1); + me.collisionFilter.mask = cat.bullet | cat.map //| cat.body //cat.player | + me.powerUpNames = [] + me.redMode = function () { + this.color = `rgba(255,0,200,` + this.fill = this.color + '1)' + this.JumpDistance = 20 + let cycle = () => { + if (this.radius < 25) { + if (m.alive && this.JumpDistance === 20) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { + const scale = 1.01; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + } + } + } + requestAnimationFrame(cycle); + } + me.redMode(); + me.blueMode = function () { + this.color = `rgba(0,0,255,`//`rgba(255,0,200,` + this.fill = this.color + '1)' + this.JumpDistance = 37 //adjust this number in the IF below + let cycle = () => { + if (this.radius > 14) { + if (m.alive && this.JumpDistance === 37) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { + const scale = 0.96; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + } + } + } + requestAnimationFrame(cycle); + } + me.onDamage = function () { + if (this.health < this.nextHealthThreshold) { + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 + this.invulnerableCount = 300 + this.isInvulnerable = true + this.damageReduction = 0 + if (this.history.length < 200) for (let i = 0; i < 11; i++) this.history.unshift(this.history[0]) + this.blueMode() + } + }; + me.onDeath = function () { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + + //respawn all eaten power ups + let i = 0 + let cycle = () => { + if (i < this.powerUpNames.length) { + if (m.alive) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing && powerUp.length < 300) { + const index = Math.floor(Math.random() * this.history.length) //random segment of tail + const where = { x: this.history[index].x + 25 * (Math.random() - 0.5), y: this.history[index].y + 25 * (Math.random() - 0.5) } + powerUps.spawn(where.x, where.y, this.powerUpNames[i]); + i++ + } + } + } + requestAnimationFrame(cycle); + } + me.do = function () { + const color = this.color + (0.35 + 0.25 * Math.random()) + ')' + //check for player collisions in between each segment + if (m.immuneCycle < m.cycle) { + for (let i = 0; i < this.history.length - 1; i++) { + if (Matter.Query.ray([player], this.history[i], this.history[i + 1], 10).length > 0) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60 + const dmg = 0.15 * simulation.dmgScale + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x, + y: m.pos.y, + radius: dmg * 1500,//30, + color: color, + time: 20 + }); + + //reset tail length for a sec to prevent repeat damage + for (let i = 0, len = this.history.length; i < len; i++) { + this.history[i] = { x: this.position.x, y: this.position.y } + } + break + } + } + } + + if (this.nextBlinkCycle < simulation.cycle) { //teleport towards the player + this.nextBlinkCycle = simulation.cycle + this.delay; + if (this.isSlowed) this.nextBlinkCycle += this.delay + if (this.isStunned) this.nextBlinkCycle += this.delay * 3 + + //custom see player by history code + let move = (target = this.seePlayer.position) => { + const dist = Vector.sub(target, this.position); + + Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), this.JumpDistance)); + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + // Matter.Body.setAngle(this, 0); + Matter.Body.setAngularVelocity(this, 0) + //track previous locations for the tail + this.history.push({ x: this.position.x, y: this.position.y }) //add newest to end + this.history.shift() //remove first (oldest) + } + //look for close power ups in line of sight + let close = { + dist: Infinity, + targetPos: null, + index: null, + } + for (let i = 0; i < powerUp.length; i++) { + if (Matter.Query.ray(map, this.position, powerUp[i].position).length === 0) { + const dist = Vector.magnitude(Vector.sub(this.position, powerUp[i].position)) + if (dist < close.dist) { + close = { + dist: dist, + target: powerUp[i], + index: i, + } + } + } + } + if (close.dist < 3000) { //chase power ups if they are near + move(close.target.position) + + //check if close to power up and eat it + if (close.dist < this.JumpDistance + 2 * this.radius) { + this.powerUpNames.push(close.target.name) //save name to return power ups after this mob dies + Matter.Composite.remove(engine.world, close.target); + powerUp.splice(close.index, 1); + this.health = 1 //heal to full + //add more segments to tail + if (this.history.length < 200) for (let i = 0; i < 4; i++) this.history.unshift(this.history[0]) + //draw pickup for a single cycle + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(close.target.position.x, close.target.position.y); + ctx.strokeStyle = "#000" + ctx.lineWidth = 4 + ctx.stroke(); + } + + //go eat blocks to heal? + // } else if (this.health < 0.6) { + + } else if (Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && !m.isCloak) { //chase player + this.seePlayer.yes = true; + this.locatePlayer(); + if (!this.seePlayer.yes) this.seePlayer.yes = true; + move() + } else if (this.seePlayer.recall) { //chase player's history + this.lostPlayer(); + if (!m.isCloak) { + for (let i = 0; i < 50; i++) { //if lost player lock onto a player location in history + let history = m.history[(m.cycle - 10 * i) % 600] + if (Matter.Query.ray(map, this.position, history.position).length === 0) { + this.seePlayer.recall = this.memory + Math.round(this.memory * Math.random()); //cycles before mob falls a sleep + this.seePlayer.position.x = history.position.x; + this.seePlayer.position.y = history.position.y; + this.seePlayer.yes = true; + move() + break + } + } + } + } + + } + this.checkStatus(); + if (this.isInvulnerable) { + this.invulnerableCount-- + if (this.invulnerableCount < 0) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + this.redMode() + } + //draw invulnerable + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 13 + 5 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; + ctx.stroke(); + } + //draw history + ctx.beginPath(); + for (let i = 0, len = this.history.length; i < len; i++) { + ctx.lineTo(this.history[i].x, this.history[i].y) + } + ctx.lineWidth = this.radius * 2; + ctx.strokeStyle = color //"rgba(0,235,255,0.5)"; + ctx.stroke(); + }; + }, pulsarBoss(x, y, radius = 90, isNonCollide = false) { mobs.spawn(x, y, 3, radius, "#a0f"); let me = mob[mob.length - 1]; @@ -3900,7 +4088,7 @@ const spawn = { me.do = function () { if (player.speed > 5) this.do = this.fire //don't attack until player moves } - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.29 me.fire = function () { // this.armor(); this.checkStatus(); @@ -4001,6 +4189,7 @@ const spawn = { me.pulseRadius = Math.min(400, 170 + simulation.difficulty * 3) me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5) me.isFiring = false + spawn.shield(me, x, y); me.onHit = function () { }; me.canSeeTarget = function () { const angle = this.angle + Math.PI / 2; @@ -4121,7 +4310,7 @@ const spawn = { } Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.accelMag = 0.0002 * simulation.accelScale; - // me.frictionAir = 1 + spawn.shield(me, x, y); me.lasers = [] //keeps track of static laser beams me.laserLimit = simulation.difficultyMode < 3 ? 1 : 2 @@ -4177,7 +4366,7 @@ const spawn = { if (this.lasers[i].fade > 0.99) { if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { // hitting player m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage after getting hit - const dmg = 0.05 * simulation.dmgScale; + const dmg = 0.03 * simulation.dmgScale; m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: best.x, @@ -4267,14 +4456,12 @@ const spawn = { } Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.accelMag = 0.0001 * simulation.accelScale; - - me.isBoss = true; me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.36 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.nextHealthThreshold = 0.75 @@ -4283,18 +4470,14 @@ const spawn = { if (this.health < this.nextHealthThreshold) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 - this.invulnerableCount = 120 + this.invulnerableCount = 90 this.isInvulnerable = true this.damageReduction = 0 - - // if (this.health > 0.74) this.laserLimit += 2 //increase total lasers once player gets into combat - // this.addLaser() this.laserDelay = 130 } }; me.lasers = [] //keeps track of static laser beams - // me.laserLimit = 1 + 2 * (simulation.difficultyMode < 3 ? 1 : 2) - me.laserLimit = 2 + (simulation.difficultyMode < 3 ? 1 : 2) + me.laserLimit = 2 + (simulation.difficultyMode > 2) + (simulation.difficultyMode > 4) me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5) me.cycle = 0 me.laserDelay = 210 @@ -4346,7 +4529,7 @@ const spawn = { if (this.lasers[i].fade > 0.99) { if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { // hitting player m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage after getting hit - const dmg = 0.05 * simulation.dmgScale; + const dmg = 0.03 * simulation.dmgScale; m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: best.x, @@ -4409,7 +4592,6 @@ const spawn = { this.force.y += force.y; } else { //drift const force = Vector.mult(Vector.normalise(Vector.sub(this.driftGoal, this.position)), 0.00001 * this.mass) - // const force = Vector.mult(this.driftGoal, 0.0001 * this.mass) this.force.x += force.x; this.force.y += force.y; } @@ -4449,6 +4631,7 @@ const spawn = { Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.accelMag = 0.0001 * simulation.accelScale; me.laserInterval = 100 + spawn.shield(me, x, y); me.onHit = function () { //run this function on hitting player this.explode(); @@ -4526,7 +4709,7 @@ const spawn = { // me.torque -= me.inertia * 0.002 spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.25 me.isBoss = true; // spawn.shield(me, x, y, 1); //not working, not sure why me.onDeath = function () { @@ -4740,7 +4923,7 @@ const spawn = { // spawn.shield(me, x, y, 1); Matter.Body.setDensity(me, 0.005); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.12 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.13 me.isBoss = true; me.onDamage = function () { }; me.onDeath = function () { @@ -4835,8 +5018,8 @@ const spawn = { me.frictionAir = 0; me.restitution = 1 // spawn.spawnOrbitals(me, radius + 50 + 125 * Math.random(), 1) - Matter.Body.setDensity(me, 0.002 + 0.0001 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.09 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + Matter.Body.setDensity(me, 0.002 + 0.00005 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.11 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.nextHealthThreshold = 0.75 @@ -4932,7 +5115,7 @@ const spawn = { me.isBoss = true; me.isReactorBoss = true; Matter.Body.setDensity(me, 0.001); //normal is 0.001 - me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.04 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.nextHealthThreshold = 0.75 @@ -5060,8 +5243,8 @@ const spawn = { this.death(); //hit player if (Vector.magnitude(Vector.sub(this.position, player.position)) < this.explodeRange && m.immuneCycle < m.cycle) { - m.damage(0.02 * simulation.dmgScale * (tech.isRadioactiveResistance ? 0.25 : 1)); - m.energy -= 0.2 * (tech.isRadioactiveResistance ? 0.25 : 1) + m.damage(0.02 * simulation.dmgScale * (tech.isRadioactiveResistance ? 0.2 : 1)); + m.energy -= 0.2 * (tech.isRadioactiveResistance ? 0.2 : 1) if (m.energy < 0) m.energy = 0 } // mob[i].isInvulnerable = false //make mineBoss not invulnerable ? @@ -5089,7 +5272,7 @@ const spawn = { me.isBoss = true; me.isReactorBoss = true; Matter.Body.setDensity(me, 0.003); //normal is 0.001 - me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.1 me.startingDamageReduction = me.damageReduction me.inertia = Infinity; me.isInvulnerable = false @@ -5182,7 +5365,7 @@ const spawn = { // me.skipRate = 1 + Math.floor(simulation.difficulty*0.02) // spawn.shield(me, x, y, 1); Matter.Body.setDensity(me, 0.001); //normal is 0.001 - me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.05 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.onDamage = function () { @@ -5289,7 +5472,7 @@ const spawn = { let me = mob[mob.length - 1]; Matter.Body.rotate(me, 2 * Math.PI * Math.random()); me.isBoss = true; - me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.11 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.frictionAir = 0.02 @@ -5304,7 +5487,7 @@ const spawn = { me.torqueMagnitude = 0.00024 * me.inertia * (Math.random() > 0.5 ? -1 : 1); me.delay = 70 + 70 * simulation.CDScale; me.cd = 0; - me.swordRadius = 0; + me.swordRadius = 50; me.swordVertex = 1 me.swordRadiusMax = 1100 + 20 * simulation.difficulty; me.swordRadiusGrowRate = me.swordRadiusMax * (0.005 + 0.0003 * simulation.difficulty) @@ -5338,15 +5521,16 @@ const spawn = { Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 ) { //find vertex farthest away from player - let dist = 0 - for (let i = 0, len = this.vertices.length; i < len; i++) { - const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) - if (D > dist) { - dist = D - this.swordVertex = i - } - } - this.laserAngle = this.swordVertex / 5 * 2 * Math.PI + 0.6283 + // let dist = 0 + // for (let i = 0, len = this.vertices.length; i < len; i++) { + // const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) + // if (D > dist) { + // dist = D + // this.swordVertex = i + // } + // } + // this.laserAngle = this.swordVertex / 5 * 2 * Math.PI + 0.6283 + this.sword = this.swordGrow Matter.Body.setVelocity(this, { x: 0, y: 0 }); Matter.Body.setAngularVelocity(this, 0) @@ -5355,6 +5539,7 @@ const spawn = { this.isInvulnerable = true this.frictionAir = 1 } + this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); //always see the tip of the sword } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing me.swordGrow = function () { @@ -5380,7 +5565,7 @@ const spawn = { this.spinCount++ if (this.spinCount > 80) { this.sword = this.swordWaiting - this.swordRadius = 0 + this.swordRadius = 50 this.accelMag = 0.001 * simulation.accelScale; this.cd = simulation.cycle + this.delay; this.damageReduction = this.startingDamageReduction @@ -5735,9 +5920,9 @@ const spawn = { sneakBoss(x, y, radius = 70) { mobs.spawn(x, y, 5, radius, "transparent"); let me = mob[mob.length - 1]; - Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 0.001); //extra dense //normal is 0.001 //makes effective life much larger me.isBoss = true; - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.17 me.accelMag = 0.0017 * Math.sqrt(simulation.accelScale); me.frictionAir = 0.01; @@ -5752,6 +5937,7 @@ const spawn = { me.vanishesLeft = Math.ceil(1 + simulation.difficultyMode * 0.5) me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) + for (let i = 0; i < simulation.difficultyMode / 2 - 0.5; i++) spawn.sneaker(this.position.x + 10 * Math.random(), this.position.y + 10 * Math.random()) }; me.onDamage = function () { if (this.vanishesLeft > 0 && this.health < 0.1) { //if health is below 10% teleport to a random spot on player history, heal, and cloak @@ -5776,6 +5962,7 @@ const spawn = { this.seePlayer.recall = 0 this.cloak(); this.health = 1; + for (let i = 0; i < simulation.difficultyMode / 2 - 0.5; i++) spawn.sneaker(this.position.x + 10 * Math.random(), this.position.y + 10 * Math.random()) } }; me.cloak = function () { @@ -5784,12 +5971,12 @@ const spawn = { this.isCloaked = true; this.isBadTarget = true; this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player - this.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + this.damageReduction = 0.04 } } me.deCloak = function () { if (this.isCloaked) { - this.damageReduction = 0.4 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + this.damageReduction = 0.4 this.isCloaked = false; this.isBadTarget = false; this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player @@ -5797,7 +5984,7 @@ const spawn = { } me.do = function () { if (this.damageReduction === 0) { - this.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + this.damageReduction = 0.04 let i = this.status.length //clear bad status effects while (i--) { if (this.status[i].type === "stun" || this.status[i].type === "dot") this.status.splice(i, 1); @@ -5807,7 +5994,7 @@ const spawn = { this.gravity(); this.seePlayerByHistory(55); this.checkStatus(); - this.attraction(); + if (this.alpha > 0.8) this.attraction(); //draw if (this.seePlayer.recall) { if (this.alpha < 1) this.alpha += 0.005 + 0.003 / simulation.CDScale; @@ -5847,6 +6034,8 @@ const spawn = { me.showHealthBar = false; me.memory = 240; me.isVanished = false; + // spawn.shield(me, x, y); //makes it too hard to stealth + me.onDamage = function () { if (!this.isVanished && this.health < 0.1 && !this.isStunned && !this.isSlowed) { //if health is below 10% teleport to a random spot on player history, heal, and cloak this.health = 1; @@ -5912,7 +6101,7 @@ const spawn = { ghoster(x, y, radius = 50 + Math.ceil(Math.random() * 90)) { mobs.spawn(x, y, 7, radius, "transparent"); let me = mob[mob.length - 1]; - me.seeAtDistance2 = 300000; + me.seeAtDistance2 = 500000; me.accelMag = 0.00007 + 0.0001 * simulation.accelScale; if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search Matter.Body.setDensity(me, 0.0002); //normal is 0.001 @@ -5921,10 +6110,9 @@ const spawn = { me.alpha = 1; //used in drawGhost me.isNotCloaked = false; //used in drawGhost me.isBadTarget = true; - // me.leaveBody = false; - me.collisionFilter.mask = cat.bullet //| cat.body + me.collisionFilter.mask = cat.bullet | cat.body me.showHealthBar = false; - me.memory = 600; + me.memory = 900; me.delay = 60 me.cd = 0; me.onHit = function () { @@ -5938,7 +6126,7 @@ const spawn = { for (let i = 0; i < b.inventory.length; i++) { const gun = b.guns[b.inventory[i]] if (gun.ammo > 0 && gun.ammo !== Infinity) { - gun.ammo -= Math.ceil((0.6 * Math.random() + 0.6 * Math.random()) * gun.ammoPack) //Math.ceil(Math.random() * target.ammoPack) + gun.ammo -= Math.ceil(1.1 * (Math.random() + Math.random()) * gun.ammoPack) if (gun.ammo < 0) gun.ammo = 0 isRemovedAmmo = true } @@ -5952,6 +6140,9 @@ const spawn = { } } }; + me.onDamage = function () { + if (this.health < 0.8) me.seeAtDistance2 = 2000000; + } me.do = function () { if (this.speed > 7) Matter.Body.setVelocity(this, { x: this.velocity.x * 0.8, y: this.velocity.y * 0.8 }); //cap max speed to avoid getting launched by deflection, explosion this.seePlayerCheckByDistance(); @@ -5961,8 +6152,8 @@ const spawn = { //draw if (this.distanceToPlayer2() < this.seeAtDistance2) { if (this.alpha < 1) this.alpha += 0.011 * simulation.CDScale; //near player go solid - } else { - if (this.alpha > 0) this.alpha -= 0.05; ///away from player, hide + } else if (this.alpha > 0) { + this.alpha -= 0.05; ///away from player, hide } if (this.alpha > 0) { if (this.alpha > 0.7 && this.seePlayer.recall) { @@ -5970,7 +6161,7 @@ const spawn = { if (!this.isNotCloaked) { this.isNotCloaked = true; this.isBadTarget = false; - this.collisionFilter.mask = cat.player | cat.bullet + this.collisionFilter.mask = cat.player | cat.bullet | cat.body } } //draw body @@ -5985,7 +6176,7 @@ const spawn = { } else if (this.isNotCloaked) { this.isNotCloaked = false; this.isBadTarget = true; - this.collisionFilter.mask = cat.bullet; //can't touch player or walls + this.collisionFilter.mask = cat.bullet | cat.body; //can't touch player or walls } }; }, @@ -6039,7 +6230,7 @@ const spawn = { let me = mob[mob.length - 1]; me.isBoss = true; - Matter.Body.setDensity(me, 0.0025 + 0.00013 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 0.0025 + 0.00009 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger me.stroke = "transparent"; //used for drawGhost me.seeAtDistance2 = 1500000; @@ -6056,7 +6247,7 @@ const spawn = { me.collisionFilter.mask = cat.player | cat.bullet //| cat.body spawn.shield(me, x, y, 1); - const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 + const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) const speed = (0.007 + 0.003 * Math.random() + 0.004 * Math.sqrt(simulation.difficulty)) let radiusOrbitals = radius + 125 + 350 * Math.random() for (let i = 0; i < len; i++) spawn.orbital(me, radiusOrbitals, i / len * 2 * Math.PI, speed) @@ -6066,7 +6257,7 @@ const spawn = { me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; - me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.22 me.do = function () { // this.armor(); this.seePlayerCheckByDistance(); @@ -6099,7 +6290,7 @@ const spawn = { me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } - // spawn.shield(me, x, y); + spawn.shield(me, x, y); me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); @@ -6123,8 +6314,8 @@ const spawn = { }, 2000); //add in a delay in case the level gets flipped left right me.isBoss = true; - Matter.Body.setDensity(me, 0.01 + 0.0004 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + Matter.Body.setDensity(me, 0.01 + 0.0003 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.22 me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front me.isVerticesChange = true @@ -6286,14 +6477,11 @@ const spawn = { me.accelMag = 0.0005 * simulation.accelScale; me.frictionAir = 0.05; me.torque = 0.0001 * me.inertia; - me.fireDir = { - x: 0, - y: 0 - }; + me.fireDir = { x: 0, y: 0 }; me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } - // spawn.shield(me, x, y); + spawn.shield(me, x, y); me.do = function () { // this.seePlayerByLookingAt(); this.seePlayerCheck(); @@ -6484,8 +6672,8 @@ const spawn = { mobs.spawn(x, y, 6, radius, "rgb(150,150,255)"); let me = mob[mob.length - 1]; me.isBoss = true; - Matter.Body.setDensity(me, 0.0022 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + Matter.Body.setDensity(me, 0.0022 + 0.00015 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.22 me.accelMag = 0.0001 * simulation.accelScale; me.fireFreq = Math.floor(330 * simulation.CDScale) @@ -6528,7 +6716,7 @@ const spawn = { // let me = mob[mob.length - 1]; // me.isBoss = true; // Matter.Body.setDensity(me, 0.0022 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - // me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.damageReduction = 0.2 // me.fireFreq = Math.floor(60 * simulation.CDScale) // me.seePlayerFreq = 15 @@ -6629,7 +6817,7 @@ const spawn = { // let me = mob[mob.length - 1]; // me.isBoss = true; // Matter.Body.setDensity(me, 0.0022 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - // me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.damageReduction = 0.2 // me.accelMag = 0.0001 * simulation.accelScale; // me.fireFreq = Math.floor(180 * simulation.CDScale) @@ -6696,7 +6884,7 @@ const spawn = { spawn.spawnOrbitals(me, radius + 50, 1); spawn.spawnOrbitals(me, radius + 125, 1); spawn.spawnOrbitals(me, radius + 200, 1); - Matter.Body.setDensity(me, 0.004 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 0.004 + 0.00015 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger me.onDeath = function () { //helps collisions functions work better after vertex have been changed setTimeout(() => { //fix mob in place, but allow rotation for (let i = 0, len = 6; i < len; i++) { @@ -6725,7 +6913,7 @@ const spawn = { }); } }; - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.27 me.do = function () { // this.armor(); if (this.grenadeLimiter > 1) this.grenadeLimiter-- @@ -6750,16 +6938,13 @@ const spawn = { me.accelMag = 0.0006 * simulation.accelScale; me.frictionAir = 0.05; me.torque = 0.0001 * me.inertia * (Math.random() > 0.5 ? -1 : 1) - me.fireDir = { - x: 0, - y: 0 - }; + me.fireDir = { x: 0, y: 0 }; me.onDeath = function () { //helps collisions functions work better after vertex have been changed spawn.grenade(this.position.x, this.position.y, 200 * simulation.CDScale); // mob[mob.length - 1].collisionFilter.category = 0 mob[mob.length - 1].collisionFilter.mask = cat.player | cat.map; } - // spawn.shield(me, x, y); + spawn.shield(me, x, y); me.do = function () { this.seePlayerCheck(); this.checkStatus(); @@ -6890,7 +7075,7 @@ const spawn = { me.onDamage = function () { this.cycle = 0 }; - me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.39 me.do = function () { Matter.Body.rotate(this, 0.003) //gently spin around this.checkStatus(); @@ -6898,9 +7083,7 @@ const spawn = { ctx.moveTo(this.vertices[this.vertices.length - 1].x, this.vertices[this.vertices.length - 1].y) const phase = (this.vertices.length + 1) * this.cycle / this.maxCycles if (phase > 1) ctx.lineTo(this.vertices[0].x, this.vertices[0].y) - for (let i = 1; i < phase - 1; i++) { - ctx.lineTo(this.vertices[i].x, this.vertices[i].y) - } + for (let i = 1; i < phase - 1; i++) ctx.lineTo(this.vertices[i].x, this.vertices[i].y) ctx.lineWidth = 5 ctx.strokeStyle = "rgb(255,255,255)" ctx.stroke(); @@ -6914,8 +7097,8 @@ const spawn = { ctx.moveTo(this.position.x, this.position.y) ctx.lineTo(mob[i].position.x, mob[i].position.y) spawn.shield(mob[i], mob[i].position.x, mob[i].position.y, 1, true); - // me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - mob[mob.length - 1].damageReduction = 0.5 * 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) //shields are extra strong + // me.damageReduction = 0.075 + mob[mob.length - 1].damageReduction = 0.5 * 0.075 //shields are extra strong } } if (!this.isShielded && this.alive) spawn.shield(this, this.position.x, this.position.y, 1, true); @@ -6938,7 +7121,7 @@ const spawn = { spawn.spawnOrbitals(me, radius + 50 + 100 * Math.random()) Matter.Body.setDensity(me, 0.0025); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.07 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.07 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.onDeath = function () { @@ -7028,7 +7211,7 @@ const spawn = { // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed }; me.onDamage = function () { }; - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.28 me.do = function () { // this.armor(); this.seePlayerCheck(); @@ -7162,49 +7345,6 @@ const spawn = { // this.attraction(); }; }, - // exploder(x, y, radius = 40 + Math.ceil(Math.random() * 50)) { - // mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); - // let me = mob[mob.length - 1]; - // me.onHit = function() { //run this function on hitting player - // this.explode(); - // }; - // me.g = 0.0003; //required if using this.gravity - // me.seePlayerFreq = 50 + Math.floor(Math.random() * 20) - // me.do = function() { - // this.gravity(); - // if (!(simulation.cycle % this.seePlayerFreq)) { - // if ( - // this.distanceToPlayer2() < this.seeAtDistance2 && - // Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && - // Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 && - // !m.isCloak - // ) { - // this.foundPlayer(); - // } else if (this.seePlayer.recall) { - // for (let i = 0; i < 20; i++) { - // let history = m.history[(m.cycle - 30 * i) % 600] - // if (Matter.Query.ray(map, this.position, history.position).length === 0) { - // this.seePlayer.recall = this.memory + Math.round(this.memory * Math.random()); //seconds before mob falls a sleep - // this.seePlayer.position.x = history.position.x; - // this.seePlayer.position.y = history.position.y; - - // ctx.beginPath(); - // ctx.moveTo(this.position.x, this.position.y); - // ctx.lineTo(history.position.x, history.position.y); - // ctx.lineWidth = 5; - // ctx.strokeStyle = "#000"; - // ctx.stroke(); - - // break - // } - // } - // this.lostPlayer(); - // } - // } - // this.checkStatus(); - // this.attraction(); - // }; - // }, exploder(x, y, radius = 40 + Math.ceil(Math.random() * 50)) { mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); let me = mob[mob.length - 1]; @@ -7213,6 +7353,7 @@ const spawn = { this.explode(); }; me.g = 0.0004; //required if using this.gravity + spawn.shield(me, x, y); me.do = function () { this.gravity(); this.seePlayerCheck(); @@ -7233,7 +7374,7 @@ const spawn = { me.memory = 250; me.laserRange = 500; Matter.Body.setDensity(me, 0.0022 + 0.00022 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.startingDamageReduction = 0.14 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = 0.14 me.damageReduction = 0 me.isInvulnerable = true @@ -7362,7 +7503,7 @@ const spawn = { let me = mob[mob.length - 1]; me.isBoss = true; Matter.Body.setDensity(me, 0.00165 + 0.00011 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.startingDamageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = 0.1 me.damageReduction = 0 me.isInvulnerable = true @@ -7451,8 +7592,8 @@ const spawn = { snakeBody(x, y, radius = 10) { mobs.spawn(x, y, 8, radius, "rgba(0,180,180,0.4)"); let me = mob[mob.length - 1]; - me.collisionFilter.mask = cat.bullet | cat.player //| cat.mob //| cat.body - me.damageReduction = 0.028 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body //| cat.mob + me.damageReduction = 0.031 Matter.Body.setDensity(me, 0.0001); //normal is 0.001 // me.accelMag = 0.0007 * simulation.accelScale; @@ -7491,8 +7632,8 @@ const spawn = { mobs.spawn(x, y, 8, radius, "rgb(0,60,80)"); let me = mob[mob.length - 1]; me.isBoss = true; - Matter.Body.setDensity(me, 0.0005 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + Matter.Body.setDensity(me, 0.0006 + 0.0001 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.27 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.nextHealthThreshold = 0.75 @@ -7584,16 +7725,16 @@ const spawn = { } }; }, - shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) { + //chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance() + shield(target, x, y, chance = (level.isMobShields ? 4 : 1) * Math.min(0.02 + simulation.difficulty * 0.005, 0.2)) { if (this.allowShields && Math.random() < chance) { mobs.spawn(x, y, 9, target.radius + 30, "rgba(220,220,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(220,220,255)"; Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.shield = true; - me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.05 me.isUnblockable = true - me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; consBB[consBB.length] = Constraint.create({ @@ -7615,7 +7756,14 @@ const spawn = { me.shieldTargetID = target.id target.isShielded = true; + if (target.shieldCount > 0) { + target.shieldCount++ + } else { + target.shieldCount = 1 + } + me.shieldCount = target.shieldCount //used with "bubble fusion" target.shieldID = me.id + me.onDeath = function () { //clear isShielded status from target for (let i = 0, len = mob.length; i < len; i++) { @@ -7641,7 +7789,7 @@ const spawn = { Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.frictionAir = 0; me.shield = true; - me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.075 me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; for (let i = 0; i < nodes; ++i) { @@ -7678,8 +7826,7 @@ const spawn = { }, spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { if (Math.random() < chance) { - // simulation.difficulty = 50 - const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 + const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) const offSet = 6.28 * Math.random() for (let i = 0; i < len; i++) spawn.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed) @@ -7735,8 +7882,8 @@ const spawn = { mobs.spawn(x, y, nodes, radius, "rgb(255,0,150)"); let me = mob[mob.length - 1]; me.isBoss = true; - Matter.Body.setDensity(me, 0.0017 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + Matter.Body.setDensity(me, 0.0018 + 0.00015 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + me.damageReduction = 0.11 me.stroke = "transparent"; //used for drawGhost me.seeAtDistance2 = 2000000; @@ -7939,19 +8086,24 @@ const spawn = { if (this.freeOfWires) { this.gravity(); } else { - if (m.pos.x > breakingPoint) { + if (m.pos.x > breakingPoint || simulation.isCheating) { this.freeOfWires = true; this.fill = "#000" this.force.x += -0.003; player.force.x += 0.06; // player.force.y -= 0.15; + //remove difficulty power up if the wire breaks to prevent getting power ups and then making the difficulty harder + for (let i = 0; i < powerUp.length; ++i) { + if (powerUp[i].name === "difficulty") { + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + } + } + } //player is extra heavy from wires - Matter.Body.setVelocity(player, { - x: player.velocity.x, - y: player.velocity.y + 0.3 - }) + Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 0.3 }) //player friction from the wires if (m.pos.x > 700 && player.velocity.x > -2) { @@ -7963,10 +8115,7 @@ const spawn = { }) } //move to player - Matter.Body.setPosition(this, { - x: m.pos.x + (42 * Math.cos(m.angle + Math.PI)), - y: m.pos.y + (42 * Math.sin(m.angle + Math.PI)) - }) + Matter.Body.setPosition(this, { x: m.pos.x + (42 * Math.cos(m.angle + Math.PI)), y: m.pos.y + (42 * Math.sin(m.angle + Math.PI)) }) } //draw wire ctx.beginPath(); @@ -8009,7 +8158,7 @@ const spawn = { if (this.freeOfWires) { this.gravity(); } else { - if (m.pos.x > breakingPoint) { + if (m.pos.x > breakingPoint || simulation.isCheating) { this.freeOfWires = true; this.force.x -= 0.0004; this.fill = "#222"; @@ -8061,7 +8210,7 @@ const spawn = { if (this.freeOfWires) { this.gravity(); } else { - if (m.pos.x > breakingPoint) { + if (m.pos.x > breakingPoint || simulation.isCheating) { this.freeOfWires = true; this.force.x += -0.0003; this.fill = "#333"; @@ -8112,7 +8261,7 @@ const spawn = { if (this.freeOfWires) { this.gravity(); } else { - if (m.pos.x > breakingPoint) { + if (m.pos.x > breakingPoint || simulation.isCheating) { this.freeOfWires = true; this.force.x += -0.0006; this.fill = "#111"; @@ -8163,7 +8312,7 @@ const spawn = { if (this.freeOfWires) { this.gravity(); } else { - if (m.pos.x > breakingPoint) { + if (m.pos.x > breakingPoint || simulation.isCheating) { this.freeOfWires = true; this.force.x += -0.0005; this.fill = "#222"; @@ -8210,12 +8359,6 @@ const spawn = { } } }, - // bodyRect(x, y, width, height, chance = 1, properties = { friction: 0.05, frictionAir: 0.001 }) { - // if (Math.random() < chance) body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties); - // }, - // bodyVertex(x, y, vector, properties) { //adds shape to body array - // body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(vector), properties); - // }, bodyRect(x, y, width, height, chance = 1, properties = { friction: 0.05, frictionAir: 0.001 }) { //this is the command that adds blocks to the world in the middle of a level if (Math.random() < chance) { body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, properties); diff --git a/js/tech.js b/js/tech.js index 533ba529..fc7ca793 100644 --- a/js/tech.js +++ b/js/tech.js @@ -1,7 +1,7 @@ const tech = { totalCount: null, + removeCount: 0, setupAllTech() { - tech.damage = 1 for (let i = 0, len = tech.tech.length; i < len; i++) { tech.tech[i].isLost = false tech.tech[i].isBanished = false @@ -14,9 +14,12 @@ const tech = { } else { tech.tech[i].frequency = 2 } + if (tech.tech[i].name === "heals" || tech.tech[i].name === "ammo" || tech.tech[i].name === "research") tech.tech[i].value = tech.tech[i].defaultValue } //remove lore if it's your first time playing since it's confusing //also remove lore if cheating + tech.removeCount = 0; + tech.pauseEjectTech = 1; //used in paradigm shift lore.techCount = 0; if (simulation.isCheating || localSettings.runCount < 1) { //simulation.isCommunityMaps || for (let i = 0, len = tech.tech.length; i < len; i++) { @@ -26,9 +29,8 @@ const tech = { } } } - // tech.removeJunkTechFromPool(); - // tech.removeLoreTechFromPool(); - // tech.addLoreTechToPool(); + + tech.damage = 1 tech.junkChance = 0; tech.extraMaxHealth = 0; tech.totalCount = 0; @@ -39,7 +41,7 @@ const tech = { if (index === 'random') { const have = [] //find which tech you have for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + if (tech.tech[i].count > 0 && !tech.tech[i].isInstant) have.push(i) } if (have.length) { index = have[Math.floor(Math.random() * have.length)] @@ -59,8 +61,9 @@ const tech = { } if (tech.tech[index].count === 0) return 0 const totalRemoved = tech.tech[index].count - simulation.makeTextLog(`tech.removeTech("${tech.tech[index].name}")`, 360) + simulation.inGameConsole(`tech.removeTech("${tech.tech[index].name}")`, 360) tech.tech[index].remove(); + tech.removeCount += totalRemoved tech.tech[index].count = 0; tech.totalCount -= totalRemoved simulation.updateTechHUD(); @@ -68,23 +71,15 @@ const tech = { simulation.updateTechHUD(); return totalRemoved //return the total number of tech removed }, - // onclick="tech.removeTechPaused(${i}, this)" //add this to tech elements in pause menu - // removeTechPaused(index, who) { - // tech.tech[index].remove(); - // tech.tech[index].count = 0; - // simulation.updateTechHUD(); - // who.innerHTML = "removed" - // // who.style.display = "none" - // }, - // removeLoreTechFromPool() { - // 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) - // } - // }, junkChance: 0, addJunkTechToPool(percent) { //percent is number between 0-1 - tech.junkChance += (1 - tech.junkChance) * percent + tech.junkChance += percent + if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 + if (tech.junkChance > 1) tech.junkChance = 1 + simulation.inGameConsole(`+${(100 * percent).toFixed(0)}% JUNKtech chance (${(100 * tech.junkChance).toFixed(0)} total chance)`) + // tech.junkChance += (1 - tech.junkChance) * percent return percent + //make an array for possible junk tech to add // let options = []; // for (let i = 0; i < tech.tech.length; i++) { @@ -97,7 +92,7 @@ const tech = { // } // const num = Math.ceil(percent * countNonJunk) //scale number added // for (let i = 0; i < num; i++) tech.tech[options[Math.floor(Math.random() * options.length)]].frequency++ //add random array options to tech pool - // simulation.makeTextLog(`tech.tech.push(${num.toFixed(0)} JUNK)`) + // simulation.inGameConsole(`tech.tech.push(${num.toFixed(0)} JUNK)`) // return num // } else { // return 0 @@ -112,25 +107,16 @@ const tech = { // } // } // } - if (percent > 0) { - tech.junkChance = (tech.junkChance - percent) / (1 - percent) - if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 - } - }, - giveRandomJUNK() { - const list = [] - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].isJunk) list.push(tech.tech[i].name) - } - let name = list[Math.floor(Math.random() * list.length)] - tech.giveTech(name) - simulation.makeTextLog(`tech.giveTech("${name}")`); + + // if (percent > 0) { + // tech.junkChance = (tech.junkChance - percent) / (1 - percent) + // if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 + // } + tech.junkChance -= percent + if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 + if (tech.junkChance > 1) tech.junkChance = 1 }, giveTech(index = 'random') { - // if (Math.random() < tech.junkChance) { - // tech.giveRandomJUNK(); - // return - // } if (index === 'random') { let options = []; for (let i = 0; i < tech.tech.length; i++) { @@ -139,8 +125,8 @@ const tech = { // give a random tech from the tech I don't have if (options.length > 0) { let newTech = options[Math.floor(Math.random() * options.length)] + simulation.inGameConsole(`tech.giveTech("${tech.tech[newTech].name}") //random tech`); tech.giveTech(newTech) - simulation.makeTextLog(`tech.giveTech("${tech.tech[newTech].name}") //random tech`); } } else { if (isNaN(index)) { //find index by name @@ -155,7 +141,7 @@ const tech = { if (!found) return //if name not found don't give any tech } if (tech.isMetaAnalysis && tech.tech[index].isJunk) { - simulation.makeTextLog(`//tech: meta-analysis replaced junk tech with random tech`); + simulation.inGameConsole(`//tech: meta-analysis replaced junk tech with random tech`); tech.giveTech('random') for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 40 * Math.random(), m.pos.y + 40 * Math.random(), "research"); return @@ -163,26 +149,92 @@ const tech = { if (tech.tech[index].isLost) tech.tech[index].isLost = false; //give specific tech if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false //stops the bug where you can't gets stacks of tech you take with decoherence, I think + if (tech.isDamageFieldTech && tech.tech[index].isFieldTech) { + tech.damage *= 1.15 + // simulation.inGameConsole(`damage *= ${1.05}`) + simulation.inGameConsole(`tech.damage *= ${1.1} //hidden-variable theory`); + } tech.tech[index].effect(); //give specific tech tech.tech[index].count++ - // tech.tech[index].cycle = m.cycle - console.log(tech.tech[index].cycle) - tech.totalCount++ //used in power up randomization - //move new tech to the top of the tech list - if (index > 0) { - // Remove the element from the array - const [item] = tech.tech.splice(index, 1); - // Add the element to the front of the array - tech.tech.unshift(item); + if (!tech.tech[index].isInstant) tech.totalCount++ //used in power up randomization + if (tech.isWiki) { + async function getWikipediaIntro(subject) { + // const searchEndpoint = `https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&titles=${encodeURIComponent(subject).replace(/' /g, '%27')}&format=json&origin=*`; + const searchEndpoint = `https://en.wikipedia.org/w/api.php?action=opensearch&search=${encodeURIComponent(subject).replace(/' /g, '%27')}&limit=1&namespace=0&format=json&origin=*`; + try { + // Perform a search to get the closest matching title + const searchResponse = await fetch(searchEndpoint); + const searchData = await searchResponse.json(); + if (searchData[1].length === 0) throw new Error('No matching pages found'); + const closestTitle = searchData[1][0]; + // Use the closest matching title to get the page content + const contentEndpoint = `https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&titles=${encodeURIComponent(closestTitle)}&format=json&origin=*`; + const contentResponse = await fetch(contentEndpoint); + const contentData = await contentResponse.json(); + const pages = contentData.query.pages; + const pageId = Object.keys(pages)[0]; + return pages[pageId].extract + } catch (error) { + console.error('Error fetching Wikipedia intro:', error); + } + } + const subject = tech.tech[index].name + getWikipediaIntro(subject).then(intro => { + let tab = window.open(`https://en.wikipedia.org/w/index.php?search=${encodeURIComponent(subject).replace(/' /g, '%27')}&title=Special:Search`, "_blank"); + if (tab) { + let checkInterval = setInterval(() => { + if (tab.closed) { + clearInterval(checkInterval); // Stop checking once the tab is closed + + const introArray = intro.split(" ") + const wordLimit = 7 + const wordNumber = Math.ceil(Math.random() * wordLimit) + const answer = prompt(`On the wikipedia page for ${subject} what is word ${wordNumber + 1}?`) + console.log(introArray[wordNumber]) + if (introArray[wordNumber]) { + if (answer && answer.toLowerCase() === introArray[wordNumber].toLowerCase().replace(/[^a-zA-Z]/g, '')) { + powerUps.spawnDelay("research", 4) + simulation.inGameConsole(`correct!`, 360) + } else { + simulation.inGameConsole(`${answer} is wrong, it was ${introArray[wordNumber]}`, 360) + } + let text = `"` + for (let i = 0; i < wordLimit + 3; i++) { + if (i === wordNumber) { + text += `${introArray[i]} ` + } else { + text += `${introArray[i]} ` + } + } + simulation.inGameConsole(text + `..."`, 360) + } else { + simulation.inGameConsole(`hmmm I'm not sure the answer, so I'll say it's correct!`, 360) + powerUps.spawnDelay("research", 3) + } + } + }, 1000); // Check every 1 second + setTimeout(() => { + tab.close(); + }, 7000); // Close the tab after 7 seconds + } + }); } - simulation.updateTechHUD(); + + //move new tech to the top of the tech list + requestAnimationFrame(() => { + if (index > 0 && !build.isExperimentSelection) { + const [item] = tech.tech.splice(index, 1); // Remove the element from the array + tech.tech.unshift(item); // Add the element to the front of the array + } + simulation.updateTechHUD(); + }) } }, setCheating() { if (!simulation.isCheating) { simulation.isCheating = true; - level.levelAnnounce(); + document.title = "n-gon:" + level.levelAnnounce(); lore.techCount = 0; for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isLore) { @@ -223,66 +275,46 @@ const tech = { }, damage: 1, //used for tech changes to player damage that don't have complex conditions damageFromTech() { - let dmg = tech.damage //m.fieldDamage - // if (tech.isDivisor) { - // for (let i = 0; i < b.inventory.length; i++) { - // if (b.guns[b.inventory[i]].ammo % 3 === 0) { - // dmg *= 1.44 - // break - // } - // } - // } - if (tech.isPowerUpDamage) dmg *= 1 + 0.05 * powerUp.length - if (tech.isDamageCooldown) dmg *= m.lastKillCycle + tech.isDamageCooldownTime > m.cycle ? 0.45 : 4.33 - if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.93 - if (tech.isDivisor && b.activeGun !== undefined && b.activeGun !== null && b.guns[b.activeGun].ammo % 3 === 0) dmg *= 1.77 - if (tech.isNoGroundDamage) dmg *= m.onGround ? 0.85 : 2 - if (tech.isDilate) dmg *= 1.5 + 0.6 * Math.sin(m.cycle * 0.0075) - if (tech.isGunChoice && tech.buffedGun === b.inventoryGun) dmg *= 1 + 0.31 * b.inventory.length - if (powerUps.boost.endCycle > m.cycle) dmg *= 1 + powerUps.boost.damage + let dmg = tech.damage * m.fieldDamage + if (level.isNoDamage && (m.cycle - 180 < level.noDamageCycle)) dmg *= 0.1 + if (tech.isMaxHealthDamage && m.health === m.maxHealth) dmg *= 1.5 + if (tech.noDefenseSettingDamage && m.defense() === 1) dmg *= 2 + if (tech.isImmunityDamage && m.immuneCycle > m.cycle) dmg *= 3 + if (tech.isPowerUpDamage) dmg *= 1 + 0.07 * powerUp.length + if (tech.isDamageCooldown) dmg *= m.lastKillCycle + tech.isDamageCooldownTime > m.cycle ? 0.4 : 4 + if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 2 + if (tech.isDivisor && b.activeGun !== undefined && b.activeGun !== null && b.guns[b.activeGun].ammo % 3 === 0) dmg *= 1.9 + if (tech.isNoGroundDamage) dmg *= m.onGround ? 0.9 : 2 + if (tech.isDilate) dmg *= 1.9 + 1.1 * Math.sin(m.cycle * 0.01) + if (tech.isGunChoice && tech.buffedGun === b.inventoryGun) dmg *= 1 + 0.3 * b.inventory.length + if (powerUps.boost.endCycle > simulation.cycle) dmg *= 1 + powerUps.boost.damage if (m.coupling && (m.fieldMode === 0 || m.fieldMode === 5)) dmg *= 1 + 0.015 * m.coupling - if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) dmg *= 4.33 * (1 + 0.033 * m.coupling) - if (tech.deathSkipTime) dmg *= 1 + 0.6 * tech.deathSkipTime - if (tech.isTechDebt) dmg *= tech.totalCount > 20 ? Math.pow(0.85, tech.totalCount - 20) : 4 - 0.15 * tech.totalCount // if (tech.isTechDebt) dmg *= Math.min(Math.pow(0.85, tech.totalCount - 20), 4 - 0.15 * tech.totalCount) - if (tech.isFlipFlopDamage && tech.isFlipFlopOn) dmg *= 1.555 - if (tech.isAnthropicDamage && tech.isDeathAvoidedThisLevel) dmg *= 2.3703599 + if (tech.isVerlet) dmg *= 3 + if (tech.isTechDebt) dmg *= tech.totalCount > 20 ? Math.pow(0.85, tech.totalCount - 20) : 4 - 0.15 * tech.totalCount + if (tech.isAnthropicDamage && tech.isDeathAvoidedThisLevel) dmg *= 2.71828 if (tech.isDupDamage) dmg *= 1 + Math.min(1, tech.duplicationChance()) if (tech.isDamageForGuns) dmg *= 1 + 0.22 * Math.max(0, b.inventory.length - 1) - if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.25 + if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.3 if (tech.isAcidDmg && m.health > 1) dmg *= 1.35; - if (tech.isRerollDamage) dmg *= 1 + Math.max(0, 0.03 * powerUps.research.count) - if (tech.isBotDamage) dmg *= 1 + 0.06 * b.totalBots() + if (tech.isRerollDamage) dmg *= 1 + Math.max(0, 0.05 * powerUps.research.count) + if (tech.isBotDamage) dmg *= 1 + 0.04 * b.totalBots() if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage - if (tech.isLowEnergyDamage) dmg *= 1 + 0.7 * Math.max(0, 1 - m.energy) + if (tech.isLowEnergyDamage) dmg *= 1 + 0.5 * Math.max(0, m.maxEnergy - m.energy) if (tech.energyDamage) dmg *= 1 + m.energy * 0.23 * tech.energyDamage; - if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.007 - if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2.11 - if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.88, player.speed * 0.0193) - if (tech.isAxion && tech.isHarmMACHO) dmg *= 2 - m.defense() + if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.01 + if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2 + if (tech.isSpeedDamage) dmg *= 1 + Math.min(2, ((tech.speedAdded + player.speed) * 0.033))//1 + Math.min(1, (tech.speedAdded + player.speed) * 0.0193) + if (tech.isAxion && tech.isHarmDarkMatter) dmg *= ((tech.isMoveDarkMatter || tech.isNotDarkMatter) ? 3.2 : 2) if (tech.isHarmDamage && m.lastHarmCycle + 480 > m.cycle) dmg *= 3; - if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit * (2 - m.defense()) // if (!simulation.paused) m.lastHit = 0 - if (tech.isLowHealthDmg) dmg *= 1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health)) - if (tech.isJunkDNA) dmg *= 1 + 2 * tech.junkChance + if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit + // if (tech.isLowHealthDmg) dmg *= 1 + 0.6 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health)) + if (tech.isLowHealthDmg) dmg *= 1 + 0.6 * Math.max(0, (tech.isEnergyHealth ? m.maxEnergy - m.energy : m.maxHealth - m.health)) + if (tech.isJunkDNA) dmg *= 1 + 2 * (tech.junkChance + level.junkAdded) return dmg }, duplicationChance() { - return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.17 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + 0.08 * tech.isDuplicateMobs + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0))) - }, - isScaleMobsWithDuplication: false, - maxDuplicationEvent() { - if (tech.is100Duplicate && tech.duplicationChance() > 0.99) { - tech.is100Duplicate = false - const range = 1300 - tech.isScaleMobsWithDuplication = true - for (let i = 0, len = 9; i < len; i++) { - const angle = 2 * Math.PI * i / len - spawn.randomLevelBoss(m.pos.x + range * Math.cos(angle), m.pos.y + range * Math.sin(angle), spawn.nonCollideBossList); - } - spawn.historyBoss(0, 0) - spawn.pulsarBoss(level.exit.x, level.exit.y, 70, true) - spawn.blockBoss(level.enter.x, level.enter.y) - tech.isScaleMobsWithDuplication = false - } + if (level.isNoDuplicate) return 0 + return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.2 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + 0.08 * tech.isDuplicateMobs + 0.03 * tech.isMassProduction + 0.04 * tech.isHealAttract + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.6 : 0) + 0.06 * tech.isDupEnergy)) }, setTechFrequency(name, frequency) { for (let i = 0, len = tech.tech.length; i < len; i++) { @@ -317,7 +349,7 @@ const tech = { }, tech: [{ name: "tungsten carbide", - description: "+300 maximum health
lose health after hard landings", + description: "+400 maximum health
lose health after hard landings", maxCount: 1, count: 0, frequency: 1, @@ -328,14 +360,12 @@ const tech = { }, requires: "not skin", effect() { - tech.hardLanding = 70 tech.isFallingDamage = true; m.setMaxHealth(); m.addHealth(3 / simulation.healScale) m.skin.tungsten() }, remove() { - tech.hardLanding = 130 tech.isFallingDamage = false; m.setMaxHealth(); if (this.count) m.resetSkin(); @@ -343,7 +373,7 @@ const tech = { }, { name: "nitinol", - description: "+33% movement and jumping
+22% defense", + description: "1.3x movement and jumping
0.17 seconds of coyote time", maxCount: 1, count: 0, frequency: 1, @@ -355,22 +385,15 @@ const tech = { requires: "not skinned", effect() { m.skin.mech(); - tech.hardLanding = 110 - tech.squirrelFx += 0.4; - tech.squirrelJump += 0.16; m.setMovement() }, remove() { - tech.hardLanding = 130 - tech.squirrelFx = 1; - tech.squirrelJump = 1; - m.setMovement() if (this.count) m.resetSkin(); } }, { name: "Higgs mechanism", - description: "+77% fire rate
while firing your position is fixed", + description: "4x fire rate
while firing your position is fixed", maxCount: 1, count: 0, frequency: 1, @@ -387,17 +410,38 @@ const tech = { m.skin.strokeGap(); }, remove() { + tech.isFireMoveLock = false if (tech.isFireMoveLock) { - tech.isFireMoveLock = false b.setFireCD(); b.setFireMethod(); if (this.count) m.resetSkin(); } } }, + { + name: "Verlet integration", + description: "3x damage
after mobs die advance time 0.5 seconds", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin + }, + requires: "not skinned", + effect() { + tech.isVerlet = true + m.skin.verlet(); + }, + remove() { + tech.isVerlet = false + if (this.count) m.resetSkin(); + } + }, { name: "Hilbert space", - description: "+300% damage
after a collision enter an alternate reality", + description: "4x damage
after a collision enter an alternate reality", maxCount: 1, count: 0, frequency: 1, @@ -408,20 +452,23 @@ const tech = { return !m.isAltSkin && !tech.isResearchReality && !tech.isSwitchReality }, requires: "not skinned, Ψ(t) collapse, many-worlds", - damage: 4, //1+300% + damage: 4, effect() { m.skin.anodize(); tech.damage *= this.damage tech.isCollisionRealitySwitch = true; }, remove() { - if (this.count) tech.damage /= this.damage tech.isCollisionRealitySwitch = false; + if (this.count && m.alive) { + tech.damage /= this.damage + m.resetSkin(); + } } }, { name: "aperture", - description: "every 6 seconds your damage cycles
between -10% and +110% damage", + description: "every 4 seconds your damage cycles
between 0.8x and 3x damage", maxCount: 1, count: 0, frequency: 1, @@ -442,7 +489,7 @@ const tech = { }, { name: "diaphragm", - description: "every 6 seconds your defense cycles
between +8% and +80% defense", + description: "every 4 seconds your damage taken cycles
between 0.9x and 0.2x damage taken", maxCount: 1, count: 0, frequency: 2, @@ -464,8 +511,7 @@ const tech = { }, { name: "mass-energy equivalence", - // description: "energy protects you instead of health
√ of defense reduction reduces max energy", - description: `energy protects you instead of health
defensive upgrades reduced by about 50%`, + description: `energy protects you instead of health`, maxCount: 1, count: 0, frequency: 1, @@ -487,7 +533,7 @@ const tech = { m.skin.energy(); }, remove() { - if (tech.isEnergyHealth) { + if (this.count > 0) { tech.isEnergyHealth = false; document.getElementById("health").style.display = "inline" document.getElementById("health-bg").style.display = "inline" @@ -504,10 +550,8 @@ const tech = { { name: "1st ionization energy", link: `1st ionization energy`, - // description: `after you collect ${powerUps.orb.heal()}
+${0.1 * tech.largerHeals} maximum energy`, - // descriptionFunction: `convert current and future ${powerUps.orb.heal()} into

give +${10 * tech.largerHeals} maximum energy`, descriptionFunction() { - return `convert current and future
into

give +${11 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)} maximum energy` + return `convert current and future
into

give +${14 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)} maximum energy` }, maxCount: 1, count: 0, @@ -536,10 +580,7 @@ const tech = { { name: "depolarization", descriptionFunction() { - // return `+300% damage or -50% damage
if a mob has died in the last 5 seconds` - // return `+333% damage if no mobs died in the last ${(tech.isDamageCooldownTime / 60).toFixed(1)} seconds
-55% damage if a mob died in the last ${(tech.isDamageCooldownTime / 60).toFixed(0)} seconds
` - // return `-55% damage if a mob died in the last ${(tech.isDamageCooldownTime / 60).toFixed(1)} seconds
otherwise do +333% damage
` - return `-55% damage for ${(tech.isDamageCooldownTime / 60).toFixed(1)} seconds after a mob dies
+333% damage otherwise
` + return `4x damage, but if a mob dies
0.4x damage for ${(tech.isDamageCooldownTime / 60).toFixed(1)} seconds instead` }, maxCount: 1, count: 0, @@ -560,9 +601,9 @@ const tech = { } }, { - name: "hyperpolarization", + name: "repolarization", descriptionFunction() { - return `the damage from depolarization
resets 1.25 seconds sooner after a mob dies` + return `the damage from depolarization
resets 1.25 seconds sooner after a mob dies` }, maxCount: 3, count: 0, @@ -581,8 +622,6 @@ const tech = { }, { name: "CPT symmetry", - // description: "charge, parity, and time invert to undo defense
rewind (1.5—5) seconds for (66—220) energy", - // description: "after losing health, if you have full energy
rewind time for 44 energy per second", descriptionFunction() { return `after losing health, if you have above ${(85 * Math.min(100, m.maxEnergy)).toFixed(0)} energy
rewind time for 20 energy per second` }, @@ -646,7 +685,7 @@ const tech = { { name: "ternary", //"divisor", descriptionFunction() { - return `+77% damage while your current gun
has ammo divisible by 3` + return `1.9x damage while your ammo
is evenly divisible by 3(${((b.activeGun !== undefined && b.activeGun !== null && b.guns[b.activeGun].ammo % 3 === 0) ? "1.9" : "1")}x)` //if (tech.isDivisor && b.activeGun !== undefined && b.activeGun !== null && b.guns[b.activeGun].ammo % 3 === 0) dmg *= 1.9 }, maxCount: 1, count: 0, @@ -663,150 +702,54 @@ const tech = { } }, { - name: "ordnance", - description: "double the frequency of finding guntech
spawn a gun and +6% JUNK to tech pool", + name: "integrated armament", + link: `integrated armament`, + description: `1.3x damage, but new ${powerUps.orb.gun()} replace
current ${powerUps.orb.gun()} and convert your ${powerUps.orb.gunTech()}
`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed: () => true, - requires: "", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2 - } - this.refundAmount += tech.addJunkTechToPool(0.06) - }, - refundAmount: 0, - remove() { - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "ad hoc", - descriptionFunction() { - return `spawn a ${powerUps.orb.heal()}, ${powerUps.orb.research(1)}, ${powerUps.orb.ammo(1)}, field, gun, or tech
for each of your guns` - }, - maxCount: 1, //random power up - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, allowed() { - return b.inventory.length > 1 + return b.inventory.length === 1 }, - requires: "at least 2 guns", + requires: "only 1 gun", effect() { - for (let i = 0; i < b.inventory.length; i++) { - if (Math.random() < 1 / 6) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); - } else if (Math.random() < 1 / 5) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "tech"); - } else if (Math.random() < 1 / 4) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); - } else if (Math.random() < 1 / 3) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "heal"); - } else if (Math.random() < 1 / 2) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); - } else { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "research"); - } - } + tech.isOneGun = true; }, - remove() { } + remove() { + tech.isOneGun = false; + } }, { - name: "supply chain", - descriptionFunction() { - return `double your current ammo for all your guns
triple the frequency of finding applied science` - }, - maxCount: 9, + name: "ordnance", + description: `spawn ${powerUps.orb.gun()} and get 2x frequency for ${powerUps.orb.gunTech()}
+6% JUNK choices`, + maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - allowed() { - return true - }, + isInstant: true, + isBadRandomOption: true, + allowed: () => tech.junkChance < 1, requires: "", effect() { - for (let i = 0; i < b.guns.length; i++) { - if (b.guns[i].have) b.guns[i].ammo = Math.floor(2 * b.guns[i].ammo) - } - simulation.makeGunHUD(); + powerUps.spawn(m.pos.x, m.pos.y, "gun"); for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].name === "applied science") tech.tech[i].frequency *= 3 + if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2 } + this.refundAmount += tech.addJunkTechToPool(0.06) }, + refundAmount: 0, remove() { - if (this.count) { - for (let j = 0; j < this.count; j++) { - for (let i = 0; i < b.guns.length; i++) { - if (b.guns[i].have) b.guns[i].ammo = Math.floor(0.5 * b.guns[i].ammo) - } - } - simulation.makeGunHUD(); - - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].name === "applied science") tech.tech[i].frequency = 2 - } + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 } } }, - { - name: "applied science", - description: `get a random guntech
for each of your guns`, //spawn ${powerUps.orb.research(1)} and - maxCount: 9, - count: 0, - isNonRefundable: true, - frequency: 2, - frequencyDefault: 2, - allowed() { - return b.inventory.length > 1 - }, - requires: "at least 2 guns", - effect() { - for (let i = b.inventory.length - 1; i > -1; i--) { //backwards because some tech can remove or add guns - const gunTechPool = [] //find gun tech for this gun - for (let j = 0, len = tech.tech.length; j < len; j++) { - // console.log(j, tech.tech[j].isGunTech, tech.tech[j].allowed(), !tech.tech[j].isJunk, !tech.tech[j].isBadRandomOption, tech.tech[j].count < tech.tech[j].maxCount) - const originalActiveGunIndex = b.activeGun //set current gun to active so allowed works - b.activeGun = b.inventory[i] //to make the .allowed work for guns that aren't active - if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { - const regex = tech.tech[j].requires.search(b.guns[b.inventory[i]].name) //get string index of gun name - const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' - if (regex !== -1 && (not === -1 || not > regex)) gunTechPool.push(j) //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' - } - b.activeGun = originalActiveGunIndex - if (!b.guns[b.activeGun].have) { - if (b.inventory.length === 0) { - b.activeGun = null - } else { - b.activeGun = b.inventory[0] - } - b.inventoryGun = 0; - } - } - if (gunTechPool.length) { - const index = Math.floor(Math.random() * gunTechPool.length) - tech.giveTech(gunTechPool[index]) // choose from the gun pool - tech.tech[gunTechPool[index]].isFromAppliedScience = true //makes it not remove properly under paradigm shift - simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360) - } - } - simulation.boldActiveGunHUD(); - }, - remove() { } - }, { name: "arsenal", descriptionFunction() { - return `+22% damage per unequipped gun (${(22 * Math.max(0, b.inventory.length - 1)).toFixed(0)}%)` + return `for each unused ${powerUps.orb.gun()} in your inventory
1.25x damage (${(1 + 0.25 * Math.max(0, b.inventory.length - 1)).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -824,8 +767,8 @@ const tech = { { name: "active cooling", descriptionFunction() { - return `+28% fire rate per unequipped gun (${(28 * Math.max(0, b.inventory.length - 1)).toFixed(0)}%)` - }, //
but not including your equipped gun` }, + return `for each unused ${powerUps.orb.gun()} in your inventory
1.25x fire rate (${(1 / Math.pow(0.8, Math.max(0, b.inventory.length - 1))).toFixed(2)}x)` + }, maxCount: 1, count: 0, frequency: 1, @@ -847,11 +790,9 @@ const tech = { let info = "" if (this.count > 0 && Number.isInteger(tech.buffedGun) && b.inventory.length) { let gun = b.guns[b.inventory[tech.buffedGun]].name - info = `
this level: +${(31 * Math.max(0, b.inventory.length)).toFixed(0)}% damage for ${gun}` + info = `
this level: ${(1.3 * Math.max(0, b.inventory.length)).toFixed(2)}x damage for ${gun}` } - return ` - a new gun is chosen to be improved each level -
+31% damage per gun for the chosen gun${info}` + return `a new ${powerUps.orb.gun()} in your inventory is chosen each level
if it's equipped, 1.3x damage per ${powerUps.orb.gun()} in your inventory${info}
` }, maxCount: 1, count: 0, @@ -872,15 +813,15 @@ const tech = { }, { name: "generalist", - description: "spawn 7 guns, but you can't switch guns
your equipped gun cycles after each level", + description: `spawn 7 ${powerUps.orb.gun()}, but you can't switch ${powerUps.orb.gun()}
your equipped ${powerUps.orb.gun()} cycles after each level`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isNonRefundable: true, + isInstant: true, isBadRandomOption: true, allowed() { - return b.inventory.length < b.guns.length - 5 && b.inventory.length > 1 + return (b.inventory.length < b.guns.length - 5) && (b.inventory.length > 1) }, requires: "at least 2 guns, at least 5 unclaimed guns", effect() { @@ -888,245 +829,313 @@ const tech = { for (let i = 0; i < 7; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); }, remove() { - // if (!this.count) tech.isGunCycle = false; // only set to false if you don't have this tech - // if (tech.isGunCycle) { - // for (let i = 0; i < 8; i++) { - // if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun - // } - // tech.isGunCycle = false; - // } } }, { - name: "logistics", - description: `${powerUps.orb.ammo()} give 80% more ammo, but
it's only added to your current gun`, - maxCount: 1, + name: "ad hoc", + descriptionFunction() { + return `spawn a ${powerUps.orb.heal()}, ${powerUps.orb.research()}, ${powerUps.orb.ammo()}, ${powerUps.orb.tech()}, ${powerUps.orb.gun()}, or ${powerUps.orb.field()}
for each ${powerUps.orb.gun()} in your inventory` + }, + maxCount: 1, //random power up count: 0, frequency: 1, frequencyDefault: 1, + isInstant: true, allowed() { - return !tech.isEnergyNoAmmo + return b.inventory.length > 1 }, - requires: "not non-renewables", + requires: "at least 2 guns", effect() { - tech.isAmmoForGun = true; + for (let i = 0; i < b.inventory.length; i++) { + if (Math.random() < 1 / 6) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); + } else if (Math.random() < 1 / 5) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "tech"); + } else if (Math.random() < 1 / 4) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); + } else if (Math.random() < 1 / 3) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "heal"); + } else if (Math.random() < 1 / 2) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); + } else { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "research"); + } + } }, - remove() { - tech.isAmmoForGun = false; - } + remove() { } }, { - name: "cache", - link: `cache`, - description: `${powerUps.orb.ammo()} give 1500% more ammo, but
you can't store any more ammo than that`, - // ammo powerups always max out your gun, - // but the maximum ammo ti limited - // description: `${powerUps.orb.ammo()} give 13x more ammo, but
you can't store any more ammo than that`, + name: "applied science", + description: `get a random ${powerUps.orb.gunTech()}
for each ${powerUps.orb.gun()} in your inventory`, //spawn ${powerUps.orb.research(1)} and maxCount: 1, count: 0, - frequency: 1, - frequencyDefault: 1, + isInstant: true, + frequency: 2, + frequencyDefault: 2, allowed() { - return !tech.isEnergyNoAmmo + return b.inventory.length > 1 }, - requires: "not non-renewables", + requires: "at least 2 guns", effect() { - tech.ammoCap = 15; - powerUps.ammo.effect() + const delay = 20 + let i = (b.inventory.length) * delay + let gunIndex = -1 + let cycle = () => { + if (i > 0) { + if (m.alive) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { + i-- + if (!(i % delay)) { + gunIndex++ + //find gun tech for this gun + const gunTechPool = [] + for (let j = 0, len = tech.tech.length; j < len; j++) { + const originalActiveGunIndex = b.activeGun //set current gun to active so allowed works + b.activeGun = b.inventory[gunIndex] //to make the .allowed work for guns that aren't active + if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { + const regex = tech.tech[j].requires.search(b.guns[b.inventory[gunIndex]].name) //get string index of gun name + const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' + if (regex !== -1 && (not === -1 || not > regex)) gunTechPool.push(j) //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' + } + b.activeGun = originalActiveGunIndex + if (!b.guns[b.activeGun].have) { + if (b.inventory.length === 0) { + b.activeGun = null + } else { + b.activeGun = b.inventory[0] + } + b.inventoryGun = 0; + } + } + + //give the tech that was found for this gun + if (gunTechPool.length) { + const index = Math.floor(Math.random() * gunTechPool.length) + simulation.inGameConsole(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360) + tech.giveTech(gunTechPool[index]) // choose from the gun pool + simulation.boldActiveGunHUD(); + } + + } + } + } + } + requestAnimationFrame(cycle); + + // for (let i = b.inventory.length - 1; i > -1; i--) { //backwards because some tech can remove or add guns + // const gunTechPool = [] //find gun tech for this gun + // for (let j = 0, len = tech.tech.length; j < len; j++) { + // // console.log(j, tech.tech[j].isGunTech, tech.tech[j].allowed(), !tech.tech[j].isJunk, !tech.tech[j].isBadRandomOption, tech.tech[j].count < tech.tech[j].maxCount) + // const originalActiveGunIndex = b.activeGun //set current gun to active so allowed works + // b.activeGun = b.inventory[i] //to make the .allowed work for guns that aren't active + // if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { + // const regex = tech.tech[j].requires.search(b.guns[b.inventory[i]].name) //get string index of gun name + // const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' + // if (regex !== -1 && (not === -1 || not > regex)) gunTechPool.push(j) //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' + // } + // b.activeGun = originalActiveGunIndex + // if (!b.guns[b.activeGun].have) { + // if (b.inventory.length === 0) { + // b.activeGun = null + // } else { + // b.activeGun = b.inventory[0] + // } + // b.inventoryGun = 0; + // } + // } + // if (gunTechPool.length) { + // const index = Math.floor(Math.random() * gunTechPool.length) + // // console.log(gunTechPool, index, gunTechPool[index], tech.tech[gunTechPool[index]].name) + // simulation.inGameConsole(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360) + // // tech.tech[gunTechPool[index]].isInstant = true //makes it not remove properly under paradigm shift + // tech.giveTech(gunTechPool[index]) // choose from the gun pool + // // console.log(gunTechPool, index, gunTechPool[index], tech.tech[gunTechPool[index]].name) + // // tech.tech[gunTechPool[index]].isFromAppliedScience = true //makes it not remove properly under paradigm shift + // } + // } + // simulation.boldActiveGunHUD(); }, - remove() { - tech.ammoCap = 0; - } + remove() { } }, { - name: "catabolism", + name: "supply chain", descriptionFunction() { - return `if you fire while out of ammo
spawn ${powerUps.orb.ammo(4)} and ${tech.isEnergyHealth ? "–4 maximum energy" : "–2 maximum health"}` + return `spawn ${powerUps.orb.gun()} ${powerUps.orb.ammo(10)}` }, - maxCount: 1, + maxCount: 9, count: 0, + isInstant: true, frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isEnergyNoAmmo + return true }, - requires: "not non-renewables", + requires: "", effect() { - tech.isAmmoFromHealth = true; + // let ammoCount = 0 //count ammo + // if (b.activeGun && b.activeGun !== undefined && b.guns[b.activeGun].have && b.guns[b.activeGun].ammo !== Infinity) { + // ammoCount += b.guns[b.activeGun].ammo / b.guns[b.activeGun].ammoPack + // } + powerUps.spawnDelay("ammo", 10) + powerUps.spawn(m.pos.x, m.pos.y, "gun"); }, - remove() { - tech.isAmmoFromHealth = false; - } + remove() { } }, { - name: "non-renewables", - description: `+97% damage
${powerUps.orb.ammo()} can't spawn`, - maxCount: 1, + name: "marginal utility", + descriptionFunction() { + if (this.count === 0) this.gun = Math.floor(Math.random() * (b.guns.length - 1)) //don't pick laser + return `2x ammo per ${powerUps.orb.ammo(1)} for ${b.guns[this.gun].name}` + }, + maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, - allowed() { - return !tech.isAmmoFromHealth && !tech.isBoostReplaceAmmo - }, - requires: "not catabolism, quasiparticles", - damage: 1.97, + allowed() { return true }, + requires: "", + gun: undefined, effect() { - tech.damage *= this.damage - tech.isEnergyNoAmmo = true; + if (this.gun === undefined) this.gun = Math.floor(Math.random() * (b.guns.length - 1)) //don't pick laser + + simulation.inGameConsole(`${b.guns[this.gun].ammoPack} → ${2 * b.guns[this.gun].ammoPack} average ammo per ${powerUps.orb.ammo(1)} for ${b.guns[this.gun].name}`) + b.guns[this.gun].ammoPack *= 2 + // simulation.inGameConsole(`${(tech.interestRate * 100).toFixed(0)}% interest on health = ${h > 20 ? h + powerUps.orb.heal(1) : powerUps.orb.heal(h)}`) + + // for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); }, remove() { - if (this.count) tech.damage /= this.damage - tech.isEnergyNoAmmo = false; + if (this.count) { + b.guns[this.gun].ammoPack /= 2 + } } }, { - name: "desublimated ammunition", - description: `if crouching
alternating shots use no ammo`, + name: "Pareto efficiency", + descriptionFunction() { + return `for each ${powerUps.orb.gun()} in your inventory
randomly get 5x or 0.2x ammo per ${powerUps.orb.ammo(1)}` + }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - allowed: () => true, - requires: "", + isBadRandomOption: true, + allowed() { + return b.inventory.length > 2 + }, + requires: "at least 3 guns", effect() { - tech.crouchAmmoCount = true + + let options = [] + for (let i = 0; i < b.inventory.length; i++) options.push(b.inventory[i]) + options = shuffle(options) + for (let i = 0; i < options.length; i++) { + const index = options[i] + const scale = (i < options.length / 2) ? 4 : 0.25 + simulation.inGameConsole(`${(b.guns[index].ammoPack).toFixed(1)} ${(b.guns[index].ammoPack * scale).toFixed(1)} average ammo per ${powerUps.orb.ammo(1)} for ${b.guns[index].name}`, Infinity) + b.guns[index].ammoPack *= scale + } }, - remove() { - tech.crouchAmmoCount = false; - } + remove() { } }, { - name: "gun turret", - description: "if crouching
+66% defense ", + name: "logistics", + description: `2x ammo per ${powerUps.orb.ammo()}, but
ammo is only added to your current ${powerUps.orb.gun()}`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return true + return !tech.isEnergyNoAmmo }, - requires: "", + requires: "not non-renewables", effect() { - tech.isTurret = true + tech.isAmmoForGun = true; }, remove() { - tech.isTurret = false; + tech.isAmmoForGun = false; } }, { - name: "dead reckoning", - description: "if your speed is 0
+50% damage", - maxCount: 9, + name: "cache", + link: `cache`, + description: `17x ammo per ${powerUps.orb.ammo()}, but
you can't store additional ammo`, + maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return true + return !tech.isEnergyNoAmmo }, - requires: "", + requires: "not non-renewables", effect() { - tech.restDamage += 0.5 + tech.ammoCap = 17; + powerUps.ammo.effect() }, remove() { - tech.restDamage = 1; + tech.ammoCap = 0; } }, { - name: "kinetic bombardment", - description: "far away mobs take more damage
up to +33% damage at 3000 displacement", + name: "catabolism", + descriptionFunction() { + return `if you fire while out of ammo
spawn ${powerUps.orb.ammo(4)} and ${tech.isEnergyHealth ? "–4 maximum energy" : "–2 maximum health"}` + }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return true + return !tech.isEnergyNoAmmo }, - requires: "", + requires: "not non-renewables", effect() { - tech.isFarAwayDmg = true; //used in mob.damage() + tech.isAmmoFromHealth = true; }, remove() { - tech.isFarAwayDmg = false; + tech.isAmmoFromHealth = false; } }, { - name: "integrated armament", - link: `integrated armament`, - description: `+25% damage, but new guns replace
your current gun and convert guntech
`, + name: "non-renewables", + description: `2x damage
${powerUps.orb.ammo()} can't spawn`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return b.inventory.length === 1 + return !tech.isAmmoFromHealth && !tech.isBoostReplaceAmmo }, - requires: "only 1 gun", + requires: "not catabolism, quasiparticles", + damage: 2, effect() { - tech.isOneGun = true; + tech.damage *= this.damage + tech.isEnergyNoAmmo = true; }, remove() { - tech.isOneGun = false; + if (this.count && m.alive) tech.damage /= this.damage + tech.isEnergyNoAmmo = false; } }, { - name: "mechatronics", - descriptionFunction() { - let damageTotal = 1 - for (let i = 0; i < this.damageSoFar.length; i++) damageTotal *= this.damageSoFar[i] - let currentDamage = "" - if (this.count) currentDamage = `
(+${(100 * (damageTotal - 1)).toFixed(0)}%)` - return `gain between +7% and +13% damage` + currentDamage - }, - maxCount: 9, + name: "desublimated ammunition", + description: `if crouching
alternating shots use no ammo`, + maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - allowed() { return true }, + allowed: () => true, requires: "", - damage: 1.1, - damageSoFar: [], //tracks the random damage upgrades so it can be removed and in descriptionFunction effect() { - const damage = (Math.floor((Math.random() * 0.07 + 0.07 + 1) * 100)) / 100 - tech.damage *= damage - this.damageSoFar.push(damage) + tech.crouchAmmoCount = true }, remove() { - for (let i = 0; i < this.damageSoFar.length; i++) tech.damage /= this.damageSoFar[i] - this.damageSoFar.length = 0 + tech.crouchAmmoCount = false; } }, - // { - // name: "coyote", - // description: "", - // maxCount: 1, - // count: 0, - // frequency: 1, - // frequencyDefault: 1, - // allowed() { return true }, - // requires: "", - // effect() { // good with melee builds, content skipping builds - // tech.coyoteTime = 120 - // // simulation.gravity = function() { - // // function addGravity(bodies, magnitude) { - // // for (var i = 0; i < bodies.length; i++) { - // // bodies[i].force.y += bodies[i].mass * magnitude; - // // } - // // } - // // if (!m.isBodiesAsleep) { - // // addGravity(powerUp, simulation.g); - // // addGravity(body, simulation.g); - // // } - // // player.force.y += player.mass * simulation.g - // // } - // }, - // remove() { - // tech.coyoteTime = 5 - // } - // }, { - name: "Newtons 1st law", - description: "defense is proportional to your speed
up to +88% defense at 55 speed", + name: "gun turret", + description: "if crouching
0.3x damage taken", maxCount: 1, count: 0, frequency: 1, @@ -1136,15 +1145,33 @@ const tech = { }, requires: "", effect() { - tech.isSpeedHarm = true //max at speed = 40 + tech.isTurret = true }, remove() { - tech.isSpeedHarm = false + tech.isTurret = false; } }, { - name: "Newtons 2nd law", - description: "damage is proportional to your speed
up to +88% damage at 55 speed", + name: "dead reckoning", + description: `if your speed is 0
1.5x damage`, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.restDamage += 0.5 + }, + remove() { + tech.restDamage = 1; + } + }, + { + name: "kinetic bombardment", + description: "far away mobs take more damage
up to 1.3x damage at 3000 displacement", maxCount: 1, count: 0, frequency: 1, @@ -1154,34 +1181,42 @@ const tech = { }, requires: "", effect() { - tech.isSpeedDamage = true //max at speed = 40 + tech.isFarAwayDmg = true; }, remove() { - tech.isSpeedDamage = false + tech.isFarAwayDmg = false; } }, { name: "microstates", link: `microstates`, - description: "for each active bullet or bot
+0.7% damage", + descriptionFunction() { + return `use ${powerUps.orb.research(3)}
1.01x damage per bullet or bot (${(1 + bullet.length * 0.01).toFixed(2)}x)` + }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return true + return powerUps.research.count > 2 || build.isExperimentSelection }, requires: "", effect() { tech.isDamageFromBulletCount = true + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } }, remove() { tech.isDamageFromBulletCount = false + if (this.count > 0) { + powerUps.research.changeRerolls(3) + } } }, { name: "regression", - description: "bullet collisions increase vulnerability to
damage by +5% for mobs (+0.25% for bosses)", + description: "bullet collisions increase vulnerability to
damage by 1.05x for mobs and +1.025x for bosses", maxCount: 1, count: 0, frequency: 1, @@ -1199,7 +1234,7 @@ const tech = { }, { name: "simulated annealing", - description: "+20% damage
–20% fire rate", + description: "1.2x damage
0.8x fire rate", maxCount: 1, count: 0, frequency: 1, @@ -1210,18 +1245,84 @@ const tech = { damage: 1.2, effect() { tech.damage *= this.damage - tech.slowFire = 1.2 + tech.slowFire = 1.25 b.setFireCD(); }, remove() { - if (this.count) tech.damage /= this.damage + if (this.count && m.alive) tech.damage /= this.damage tech.slowFire = 1; b.setFireCD(); } }, + { + name: "heuristics", + descriptionFunction() { + let totalRate = 1 + for (let i = 0; i < this.totalRate.length; i++) totalRate *= this.totalRate[i] + let currentRate = "" + if (this.count) currentRate = `(${(totalRate).toFixed(2)}x)` + return `randomly gain between 1x and 2x fire rate
+5% JUNK choices` + currentRate + }, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.junkChance < 1 + }, + requires: "", + totalRate: [], //tracks the random damage upgrades so it can be removed and in descriptionFunction + effect() { + const rate = (Math.floor((Math.random() + 1) * 100)) / 100 + tech.fireRate /= rate + this.totalRate.push(rate) + b.setFireCD(); + simulation.inGameConsole(`tech.fireRate *= ${rate} //heuristics`); + this.refundAmount += tech.addJunkTechToPool(0.05) + }, + refundAmount: 0, + remove() { + if (this.count && m.alive) { + for (let i = 0; i < this.totalRate.length; i++) tech.fireRate *= this.totalRate[i] + if (this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + this.totalRate.length = 0 + b.setFireCD(); + } + }, + { + name: "mechatronics", + descriptionFunction() { + let damageTotal = 1 + for (let i = 0; i < this.damageSoFar.length; i++) damageTotal *= this.damageSoFar[i] + let currentDamage = "" + if (this.count) currentDamage = `
(${(damageTotal).toFixed(2)}x)` + return `randomly gain between 1x and 1.3x damage` + currentDamage + }, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + damageSoFar: [], //tracks the random damage upgrades so it can be removed and in descriptionFunction + effect() { + const damage = (Math.floor((Math.random() * 0.3 + 1) * 100)) / 100 + tech.damage *= damage + this.damageSoFar.push(damage) + simulation.inGameConsole(`tech.damage *= ${damage} //mechatronics`); + }, + remove() { + if (this.count && m.alive) for (let i = 0; i < this.damageSoFar.length; i++) tech.damage /= this.damageSoFar[i] + this.damageSoFar.length = 0 + } + }, { name: "dynamical systems", - description: `use ${powerUps.orb.research(2)}
+30% damage`, + description: `use ${powerUps.orb.research(2)}
1.3x damage`, // isFieldTech: true, maxCount: 1, count: 0, @@ -1245,37 +1346,16 @@ const tech = { }, remove() { tech.isCloakingDamage = false - if (this.count > 0) { + if (this.count && m.alive) { tech.damage /= this.damage powerUps.research.changeRerolls(2) } } }, - { - name: "heuristics", - description: "+22% fire rate
spawn a gun", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.fireRate *= 0.78 - b.setFireCD(); - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - }, - remove() { - tech.fireRate = 1; - b.setFireCD(); - } - }, { name: "anti-shear topology", link: `anti-shear topology`, - description: "your bullets last +30% longer", //
drone spore worm flea missile foam wave neutron ice", + description: "your bullets last 1.3x longer", //
drone spore worm flea missile foam wave neutron ice", maxCount: 3, count: 0, frequency: 1, @@ -1291,7 +1371,7 @@ const tech = { }, { name: "fracture analysis", - description: "if a mob is stunned it takes
+400% damage from bullet impacts", + description: "if a mob is stunned it takes
5x damage from bullet impacts", maxCount: 1, count: 0, frequency: 2, @@ -1327,7 +1407,7 @@ const tech = { }, { name: "thermal runaway", - description: "after mobs die
they explode", + description: "after mobs die they explode", maxCount: 1, count: 0, frequency: 1, @@ -1347,7 +1427,7 @@ const tech = { name: "zoospore vector", link: `zoospore vector`, descriptionFunction() { - return `after mobs die there is a +10% chance
they grow ${b.guns[6].nameString('s')}` + return `after mobs die there is a 10% chance
they grow ${b.guns[6].nameString('s')}` }, // description: "after mobs die
they have a +10% chance to grow spores", maxCount: 9, @@ -1371,26 +1451,71 @@ const tech = { } }, { - name: "propagator", - description: "after mobs die advance time 0.5 seconds
+60% damage", - maxCount: 3, + name: "exciton", + descriptionFunction() { + return `after mobs die they have a +14% chance to
spawn ${powerUps.orb.boost(1)} that give ${(1 + powerUps.boost.damage).toFixed(2)}x damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds
` + }, + maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "", effect() { - tech.deathSkipTime++ + tech.isBoostPowerUps = true + }, + remove() { + tech.isBoostPowerUps = false + } + }, + { + name: "band gap", + descriptionFunction() { + // return `${powerUps.orb.boost(1)} give ${(1 + powerUps.boost.damage).toFixed(2)}x ${(1 + powerUps.boost.damage + 0.77).toFixed(2)}x damage
but their duration is reduced by 1 second` + // const predict = this.count === 0 ? `${(1 + powerUps.boost.damage).toFixed(2)}x` : `` + return `${powerUps.orb.boost(1)} give an additional ${(1 + 0.75).toFixed(2)}x damage
but their duration is reduced by 1 second` + }, + maxCount: 9, + count: 1, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isBoostPowerUps || tech.isBoostReplaceAmmo + }, + requires: "exciton, quasiparticles", + effect() { + powerUps.boost.duration -= 60 + powerUps.boost.damage += 0.75 + }, + remove() { + powerUps.boost.duration = 600 + powerUps.boost.damage = 1.25 + } + }, + { + name: "polariton", + descriptionFunction() { + return `${powerUps.orb.boost(1)} also give 0.3x damage taken
for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` + }, + maxCount: 9, + count: 1, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isBoostPowerUps || tech.isBoostReplaceAmmo + }, + requires: "exciton, quasiparticles", + effect() { + powerUps.boost.isDefense = true }, remove() { - tech.deathSkipTime = 0 + powerUps.boost.isDefense = false } }, { name: "collider", descriptionFunction() { - return `after mobs die power ups
randomly collide to form different power ups` - // return `after mobs die there is a +33% chance to convert
${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, ${powerUps.orb.research(1)}, tech, field, gun into other types` + return `after mobs die existing power ups
collide to form new power ups` }, maxCount: 1, @@ -1409,7 +1534,7 @@ const tech = { { name: "bubble fusion", descriptionFunction() { - return `after destroying a mob's natural shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` + return `after destroying a mob's shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)} (once per mob)` }, maxCount: 1, count: 0, @@ -1430,7 +1555,7 @@ const tech = { { name: "enthalpy", descriptionFunction() { - return `after mobs die
they have a +8% chance to spawn ${powerUps.orb.heal(1)}` + return `after mobs die
they have an 8% chance to spawn ${powerUps.orb.heal(1)}` }, maxCount: 9, count: 0, @@ -1448,27 +1573,9 @@ const tech = { tech.healSpawn = 0; } }, - // { - // name: "yield stress", - // description: "+55% damage
to mobs at maximum health", - // maxCount: 1, - // count: 0, - // frequency: 1, - // frequencyDefault: 1, - // allowed() { - // return m.fieldMode !== 7 && tech.mobSpawnWithHealth === 0 - // }, - // requires: "not cloaking, reaction inhibitor", - // effect() { - // tech.isMobFullHealth = true - // }, - // remove() { - // tech.isMobFullHealth = false - // } - // }, { name: "cascading failure", - description: "+222% damage
to mobs below 25% health", + description: "3x damage to mobs below 25% durability", maxCount: 1, count: 0, frequency: 3, @@ -1486,7 +1593,7 @@ const tech = { }, { name: "reaction inhibitor", - description: "mobs spawn with -12% initial health", + description: "mobs spawn with 0.88x initial durability", maxCount: 3, count: 0, frequency: 1, @@ -1498,7 +1605,6 @@ const tech = { effect() { tech.mobSpawnWithHealth++ mobs.setMobSpawnHealth() - //set all mobs at full health to 0.85 for (let i = 0; i < mob.length; i++) { if (mob.health > mobs.mobSpawnWithHealth) mob.health = mobs.mobSpawnWithHealth } @@ -1511,7 +1617,7 @@ const tech = { { name: "scrap bots", link: `scrap bots`, - description: "after mobs die you have a +33% chance
to build scrap bots that operate for 15 seconds", + description: "after mobs die you have a 33% chance
to construct scrap bots that operate for 15 seconds", maxCount: 3, count: 0, frequency: 1, @@ -1551,7 +1657,7 @@ const tech = { { name: "nail-bot", link: `nail-bot`, - description: "a bot fires nails at mobs in line of sight", + description: "construct a bot fires nails at mobs in line of sight", maxCount: 9, count: 0, frequency: 1, @@ -1577,7 +1683,7 @@ const tech = { { name: "nail-bot upgrade", link: `nail-bot upgrade`, - description: "convert your bots to nail-bots
+500% fire rate and +40% nail velocity", + description: "convert your bots to nail-bots
4x fire rate and 1.4x nail velocity", maxCount: 1, count: 0, frequency: 3, @@ -1609,7 +1715,7 @@ const tech = { { name: "foam-bot", link: `foam-bot`, - description: "a bot sprays sticky foam at nearby mobs", + description: "construct a bot sprays sticky foam at nearby mobs", maxCount: 9, count: 0, frequency: 1, @@ -1635,7 +1741,7 @@ const tech = { { name: "foam-bot upgrade", link: `foam-bot upgrade`, - description: "convert your bots to foam-bots
+300% foam size and fire rate", + description: "convert your bots to foam-bots
2.5x foam size and fire rate", maxCount: 1, count: 0, frequency: 3, @@ -1667,7 +1773,7 @@ const tech = { { name: "sound-bot", link: `sound-bot`, - description: "a bot emits expanding arcs
aimed towards nearby mobs", + description: "construct a bot emits expanding
arcs of sound aimed towards nearby mobs", maxCount: 9, count: 0, frequency: 1, @@ -1691,7 +1797,7 @@ const tech = { { name: "sound-bot upgrade", link: `sound-bot upgrade`, - description: "convert your bots to sound-bots
+150% wave fire rate and +150% damage", + description: "convert your bots to sound-bots
6x wave damage", maxCount: 1, count: 0, frequency: 3, @@ -1723,7 +1829,7 @@ const tech = { { name: "boom-bot", link: `boom-bot`, - description: "a bot defends the space around you
ignites an explosion after hitting a mob", + description: "construct a bot defends the space around you
ignites an explosion after hitting a mob", maxCount: 9, count: 0, frequency: 1, @@ -1749,7 +1855,7 @@ const tech = { { name: "boom-bot upgrade", link: `boom-bot upgrade`, - description: "convert your bots to boom-bots
+300% explosion damage and size", + description: "convert your bots to boom-bots
4x explosion damage and size", maxCount: 1, count: 0, frequency: 3, @@ -1781,7 +1887,7 @@ const tech = { { name: "laser-bot", link: `laser-bot`, - description: "a bot uses energy to emit a laser beam
that targets nearby mobs", + description: "construct a bot uses energy to emit a laser
that targets nearby mobs", maxCount: 9, count: 0, frequency: 1, @@ -1807,7 +1913,7 @@ const tech = { { name: "laser-bot upgrade", link: `laser-bot upgrade`, - description: "convert your bots to laser-bots
+100% damage, efficiency, and range", + description: "convert your bots to laser-bots
2x damage, efficiency, and range", maxCount: 1, count: 0, frequency: 3, @@ -1839,7 +1945,7 @@ const tech = { { name: "orbital-bot", link: `orbital-bot`, - description: "a bot is locked in orbit around you
stuns and damages mobs on contact", + description: "construct a bot is locked in orbit around you
stuns and damages mobs on contact", maxCount: 9, count: 0, frequency: 1, @@ -1865,7 +1971,7 @@ const tech = { { name: "orbital-bot upgrade", link: `orbital-bot upgrade`, - description: "convert your bots to orbital-bots
+300% orbital damage and +50% radius", + description: "convert your bots to orbital-bots
4x orbital damage and 2x radius", maxCount: 1, count: 0, frequency: 3, @@ -1906,7 +2012,7 @@ const tech = { { name: "dynamo-bot", link: `dynamo-bot`, - description: "a bot damages mobs while it traces your path
when it's near generate +8 energy per second", + description: "construct a bot damages mobs it touches
+8 energy per second when nearby", maxCount: 9, count: 0, frequency: 1, @@ -1932,7 +2038,7 @@ const tech = { { name: "dynamo-bot upgrade", link: `dynamo-bot upgrade`, - description: "convert your bots to dynamo-bots
when it's near generate +24 energy per second", + description: "convert your bots to dynamo-bots
+24 energy per second when nearby", maxCount: 1, count: 0, frequency: 3, @@ -1963,7 +2069,9 @@ const tech = { }, { name: "perimeter defense", - description: "for each permanent bot
+6% defense", + descriptionFunction() { + return `for each permanent bot
0.96x damage taken(${(0.96 ** b.totalBots()).toFixed(2)}x)` + }, maxCount: 1, count: 0, frequency: 2, @@ -1982,7 +2090,9 @@ const tech = { }, { name: "network effect", - description: "for each permanent bot
+6% damage", + descriptionFunction() { + return `for each permanent bot
1.04x damage(${(1 + 0.04 * b.totalBots()).toFixed(2)}x)` + }, maxCount: 1, count: 0, frequency: 2, @@ -2003,7 +2113,7 @@ const tech = { name: "bot fabrication", link: `bot fabrication`, descriptionFunction() { - return `after you collect ${powerUps.orb.research(2 + Math.floor(0.1666 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` + return `after you collect ${powerUps.orb.research(2 + Math.floor(0.25 * b.totalBots()))}use them
to construct a random bot (+1 cost every 4 bots)` }, // description: `if you collect ${powerUps.orb.research(2)}use them to build a
random bot (+1 cost every 5 bots)`, maxCount: 1, @@ -2018,23 +2128,50 @@ const tech = { effect() { tech.isRerollBots = true; powerUps.research.changeRerolls(0) - simulation.makeTextLog(`m.research = 0`) + simulation.inGameConsole(`m.research = 0`) }, remove() { tech.isRerollBots = false; // this.description = `if you collect ${powerUps.orb.research(2 + Math.floor(0.2 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` } }, + { + name: "open-source", + description: `${powerUps.orb.tech()}, ${powerUps.orb.field()}, and ${powerUps.orb.gun()} have +1 bot choice
3x frequency for ${powerUps.orb.tech()} with bots`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + allowed() { + return b.totalBots() > 1 && !tech.isDeterminism + }, + requires: "at least 2 bots", + effect() { + tech.isExtraBotOption = true + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 3 + } + }, + remove() { + if (this.count > 0) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 3) + } + } + tech.isExtraBotOption = false + } + }, { name: "ersatz bots", link: `ersatz bots`, - description: "double your current permanent bots
remove all guns in your inventory", + description: `double your current permanent bots
remove all ${powerUps.orb.gun()} in your inventory`, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, isBotTech: true, - // isNonRefundable: true, + isInstant: true, isBadRandomOption: true, numberOfGunsLost: 0, allowed() { @@ -2043,7 +2180,17 @@ const tech = { requires: "NOT EXPERIMENT MODE, at least 4 bots", effect() { this.numberOfGunsLost = b.inventory.length - b.removeAllGuns(); + b.inventory = []; //removes guns and ammo + for (let i = 0, len = b.guns.length; i < len; ++i) { + b.guns[i].count = 0; + b.guns[i].have = false; + if (b.guns[i].ammo != Infinity) b.guns[i].ammo = 0; + } + tech.buffedGun = 0 + b.activeGun = null; + b.inventoryGun = 0; + simulation.drawCursor = simulation.drawCursorBasic //set cross hairs + simulation.makeGunHUD(); //double bots for (let i = 0; i < tech.nailBotCount; i++) b.nailBot(); @@ -2066,114 +2213,155 @@ const tech = { tech.missileBotCount *= 2 }, remove() { - if (this.count) { - //return guns - for (let i = 0; i < this.numberOfGunsLost; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); - this.numberOfGunsLost = 0; + // if (this.count) { + // //return guns + // for (let i = 0; i < this.numberOfGunsLost; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + // this.numberOfGunsLost = 0; - //half all current guns - tech.nailBotCount = Math.round(tech.nailBotCount / 2) - tech.laserBotCount = Math.round(tech.laserBotCount / 2) - tech.foamBotCount = Math.round(tech.foamBotCount / 2) - tech.soundBotCount = Math.round(tech.soundBotCount / 2) - tech.boomBotCount = Math.round(tech.boomBotCount / 2) - tech.orbitBotCount = Math.round(tech.orbitBotCount / 2) - tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2) - tech.plasmaBotCount = Math.round(tech.plasmaBotCount / 2) - tech.missileBotCount = Math.round(tech.missileBotCount / 2) - b.clearPermanentBots(); - b.respawnBots(); - } + // //half all current bots + // tech.nailBotCount = Math.round(tech.nailBotCount / 2) + // tech.laserBotCount = Math.round(tech.laserBotCount / 2) + // tech.foamBotCount = Math.round(tech.foamBotCount / 2) + // tech.soundBotCount = Math.round(tech.soundBotCount / 2) + // tech.boomBotCount = Math.round(tech.boomBotCount / 2) + // tech.orbitBotCount = Math.round(tech.orbitBotCount / 2) + // tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2) + // tech.plasmaBotCount = Math.round(tech.plasmaBotCount / 2) + // tech.missileBotCount = Math.round(tech.missileBotCount / 2) + // b.clearPermanentBots(); + // b.respawnBots(); + // } } }, - // { - // name: "robotics", - // description: `spawn 2 random bots
quadruple the frequency of finding bot tech`, - // maxCount: 1, - // count: 0, - // frequency: 1, - // frequencyDefault: 1, - // isBotTech: true, - // allowed() { - // return b.totalBots() > 1 || build.isExperimentSelection - // }, - // requires: "at least 2 bots", - // effect: () => { - // b.randomBot() - // b.randomBot() - // for (let i = 0, len = tech.tech.length; i < len; i++) { - // if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 4 - // } - // }, - // remove() { - // if (this.count > 0) { - // b.removeBot() - // b.removeBot() - // b.clearPermanentBots(); - // b.respawnBots(); - // for (let i = 0, len = tech.tech.length; i < len; i++) { - // if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 4) - // } - // } - // } - // }, { name: "robotics", - description: `spawn 2 random bots
tech, fields, and guns have +1 bot choice`, //tech have an extra bot tech option + description: `construct 2 random bots`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBotTech: true, + isInstant: true, allowed() { - return b.totalBots() > 1 && !tech.isDeterminism + return b.totalBots() > 2 }, - requires: "at least 2 bots", + requires: "at least 3 bots", effect() { - tech.isExtraBotOption = true for (let i = 0; i < 2; i++) b.randomBot() }, remove() { - if (this.count > 0) { - for (let i = 0; i < 2; i++) b.removeBot() - b.clearPermanentBots(); - b.respawnBots(); - } - tech.isExtraBotOption = false } }, { - name: "open-source", //digital fabricator - description: `spawn 3 random bots
triple the frequency of finding bot tech`, + name: "bot manufacturing", + description: `use ${powerUps.orb.research(2)} to construct
3 random bots`, maxCount: 1, count: 0, - frequency: 0, - frequencyDefault: 0, + frequency: 1, + frequencyDefault: 1, isBotTech: true, + isInstant: true, allowed() { - return tech.isExtraBotOption + return b.totalBots() > 3 && powerUps.research.count > 1 }, - requires: "robotics", + requires: "at least 4 bots", effect() { - for (let i = 0; i < 3; i++) b.randomBot() - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 3 + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } + // m.energy = 0.01; + b.randomBot() + b.randomBot() + b.randomBot() }, - remove() { - if (this.count > 0) { - for (let i = 0; i < 3; i++) b.removeBot() - b.clearPermanentBots(); - b.respawnBots(); - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 3) + remove() { } + }, + { + name: "bot prototypes", + description: `use ${powerUps.orb.research(3)}to build 2 random bots
and upgrade all bots to a random type`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + isInstant: true, + allowed() { + return b.totalBots() > 5 && powerUps.research.count > 2 + }, + requires: "at least 6 bots", + effect() { + requestAnimationFrame(() => { + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } - } - } + //fill array of available bots + const notUpgradedBots = [] + const num = 2 + notUpgradedBots.push(() => { + tech.giveTech("nail-bot upgrade") + for (let i = 0; i < num; i++) { + b.nailBot() + tech.nailBotCount++; + } + simulation.inGameConsole(`tech.isNailBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("foam-bot upgrade") + for (let i = 0; i < num; i++) { + b.foamBot() + tech.foamBotCount++; + } + simulation.inGameConsole(`tech.isFoamBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("sound-bot upgrade") + for (let i = 0; i < num; i++) { + b.soundBot() + tech.soundBotCount++; + } + simulation.inGameConsole(`tech.isSoundBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("boom-bot upgrade") + for (let i = 0; i < num; i++) { + b.boomBot() + tech.boomBotCount++; + } + simulation.inGameConsole(`tech.isBoomBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("laser-bot upgrade") + for (let i = 0; i < num; i++) { + b.laserBot() + tech.laserBotCount++; + } + simulation.inGameConsole(`tech.isLaserBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("orbital-bot upgrade") + for (let i = 0; i < num; i++) { + b.orbitBot() + tech.orbitBotCount++; + } + simulation.inGameConsole(`tech.isOrbitalBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("dynamo-bot upgrade") + for (let i = 0; i < num; i++) { + b.dynamoBot() + tech.dynamoBotCount++; + } + simulation.inGameConsole(`tech.isDynamoBotUpgrade = true`) + }) + + notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() //choose random function from the array and run it + }) + }, + remove() { } }, { name: "decorrelation", - description: "if your gun or field are unused for 2 seconds
+77% defense", + description: `if your ${powerUps.orb.gun()} and ${powerUps.orb.field()} keys are unused for 2 seconds
0.3x damage taken`, maxCount: 1, count: 0, frequency: 1, @@ -2191,7 +2379,7 @@ const tech = { }, { name: "anticorrelation", - description: "if your gun or field are unused for 2 seconds
+111% damage", + description: `if your ${powerUps.orb.gun()} and ${powerUps.orb.field()} keys are unused for 2 seconds
2x damage`, maxCount: 1, count: 0, frequency: 1, @@ -2209,7 +2397,7 @@ const tech = { }, { name: "mass driver", - description: "+300% block collision damage", + description: "4x block collision damage", maxCount: 1, count: 0, frequency: 1, @@ -2228,7 +2416,7 @@ const tech = { { name: "inflation", link: `inflation`, - description: "if holding a block +90% defense
after throwing a block it expands 200%", + description: "if holding a block 0.1x damage taken
after throwing a block it expands 3x", maxCount: 1, count: 0, frequency: 3, @@ -2246,7 +2434,7 @@ const tech = { }, { name: "restitution", - description: "+150% block collision damage
after throwing a block it becomes very bouncy", + description: "2.5x block collision damage
after throwing a block it becomes very bouncy", maxCount: 1, count: 0, frequency: 3, @@ -2264,7 +2452,7 @@ const tech = { }, { name: "flywheel", - description: "+150% block collision damage
after a mob dies its block is flung at mobs", + description: "2.5x block collision damage
after a mob dies its block is flung at mobs", maxCount: 1, count: 0, frequency: 3, @@ -2280,28 +2468,10 @@ const tech = { tech.isMobBlockFling = false } }, - // { - // name: "fermions", - // description: "blocks thrown by you or pilot wave will
collide with intangible mobs, but not you", - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return (tech.blockDamage > 0.075 || m.fieldMode === 8) && !tech.isTokamak - // }, - // requires: "mass driver or pilot wave, not tokamak", - // effect() { - // tech.isBlockBullets = true - // }, - // remove() { - // tech.isBlockBullets = false - // } - // }, { name: "buckling", descriptionFunction() { - return `if a block you threw kills a mob
spawn either ${powerUps.orb.coupling(1)}, ${powerUps.orb.boost(1)}, ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` + return `if a block kills a mob there's a 50% chance
to spawn either ${powerUps.orb.coupling(1)}, ${powerUps.orb.boost(1)}, ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` }, maxCount: 1, count: 0, @@ -2318,357 +2488,140 @@ const tech = { tech.isBlockPowerUps = false } }, + { - name: "NOR gate", - description: "if flip-flop is OFF
become invulnerable to your next collision", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isFlipFlop - }, - requires: "flip-flop", - effect() { - tech.isFlipFlopHarm = true //do you have this tech - }, - remove() { - tech.isFlipFlopHarm = false - } - }, - { - name: "shape-memory alloy", + name: "first derivative", descriptionFunction() { - return `if flip-flop is ON
+400 maximum health and +100% ${powerUps.orb.heal()} effect` - }, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isFlipFlop - }, - requires: "flip-flop", - effect() { - tech.isFlipFlopHealth = true; - m.setMaxHealth(); - for (let i = 0; i < powerUp.length; i++) { - if (powerUp[i].name === "heal") { - const oldSize = powerUp[i].size - powerUp[i].size = powerUps.heal.size() //update current heals - const scale = powerUp[i].size / oldSize - Matter.Body.scale(powerUp[i], scale, scale); //grow - } - } - }, - remove() { - tech.isFlipFlopHealth = false; - m.setMaxHealth(); - for (let i = 0; i < powerUp.length; i++) { - if (powerUp[i].name === "heal") { - const oldSize = powerUp[i].size - powerUp[i].size = powerUps.heal.size() //update current heals - const scale = powerUp[i].size / oldSize - Matter.Body.scale(powerUp[i], scale, scale); //grow - } - } - } - }, - { - name: "flip-flop", - link: `flip-flop`, - description: `toggle ON and OFF after a collision
unlock advanced tech that runs if ON`, - nameInfo: "", - addNameInfo() { - setTimeout(function () { - if (document.getElementById("tech-flip-flop")) { - if (tech.isFlipFlopOn) { - document.getElementById("tech-flip-flop").innerHTML = ` = ON` - m.eyeFillColor = m.fieldMeterColor //'#5af' - } else { - document.getElementById("tech-flip-flop").innerHTML = ` = OFF` - m.eyeFillColor = "transparent" - } - } - }, 100); + return `0.85x damage taken per ${powerUps.orb.gun()} in your inventory
while the 1st ${powerUps.orb.gun()} in your inventory is equipped(${(0.85 ** b.inventory.length).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isRelay - }, - requires: "not relay switch", - effect() { - tech.isFlipFlop = true //do you have this tech? - if (!tech.isFlipFlopOn) { - tech.isFlipFlopOn = true //what is the state of flip-Flop? - } - // if (!m.isShipMode) { - // m.skin.flipFlop() - // } - }, - remove() { - tech.isFlipFlop = false - if (tech.isFlipFlopOn) { - tech.isFlipFlopOn = false //what is the state of flip-Flop? - } - m.eyeFillColor = 'transparent' - // m.resetSkin(); - } - }, - { - name: "NAND gate", - description: "if ON
+55.5% damage", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isFlipFlop || tech.isRelay - }, - requires: "ON/OFF tech", - effect() { - tech.isFlipFlopDamage = true; - }, - remove() { - tech.isFlipFlopDamage = false; - } - }, - { - name: "integrated circuit", - description: "if ON +7 power up choices
if OFF -1 power up choices", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (tech.isFlipFlop || tech.isRelay) && !tech.isDeterminism - }, - requires: "ON/OFF tech, not determinism", - effect() { - tech.isFlipFlopChoices = true //do you have this tech - }, - remove() { - tech.isFlipFlopChoices = false - } - }, - { - name: "transistor", - description: "if ON generate +20 energy per second
if OFF drain -1 energy per second", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isFlipFlop || tech.isRelay + return true }, - requires: "ON/OFF tech", + requires: "", effect() { - tech.isFlipFlopEnergy = true; + tech.isFirstDer = true }, remove() { - tech.isFlipFlopEnergy = false; + tech.isFirstDer = false; } }, - // { - // name: "decoupling", - // link: `decoupling`, - // descriptionFunction() { - // //(${ m.couplingDescription(this.bonus)}) - // return `if ON +5 coupling
if OFF a dangerous particle slowly chases you` - // }, - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // bonus: 5, //coupling given - // allowed() { - // return tech.isFlipFlop || tech.isRelay - // }, - // requires: "ON/OFF tech", - // effect() { - // tech.isFlipFlopCoupling = true; - // if (tech.isFlipFlopOn) { - // m.couplingChange(this.bonus) - // } else { - // for (let i = 0; i < mob.length; i++) { - // if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP - // } - // spawn.WIMP() - // mob[mob.length - 1].isDecoupling = true //so you can find it to remove - // } - // }, - // remove() { - // tech.isFlipFlopCoupling = false; - // if (this.count) { - // if (tech.isFlipFlop || tech.isRelay) { - // if (tech.isFlipFlopOn) { - // m.couplingChange(-this.bonus) - // } else { - // for (let i = 0; i < mob.length; i++) { - // if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP - // } - // } - // } - // } - // } - // }, { - name: "relay switch", - description: `toggle ON and OFF after picking up a power up
unlock advanced tech that runs if ON`, - nameInfo: "", - addNameInfo() { - setTimeout(function () { - if (document.getElementById("tech-switch")) { - if (tech.isFlipFlopOn) { - document.getElementById("tech-switch").innerHTML = ` = ON` - m.eyeFillColor = m.fieldMeterColor //'#5af' - } else { - document.getElementById("tech-switch").innerHTML = ` = OFF` - m.eyeFillColor = "transparent" - } - } - }, 100); + name: "dark matter", + //a MAssive Compact Halo Object follows you
+ descriptionFunction() { + return `dark matter slowly gravitates towards you
0.4x damage taken ${tech.isNotDarkMatter ? "outside" : "inside"} dark matter` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isFlipFlop + return true }, - requires: "not flip-flop", + requires: "", effect() { - m.isAltSkin = true - tech.isRelay = true //do you have this tech? - if (!tech.isFlipFlopOn) { - tech.isFlipFlopOn = true //what is the state of flip-Flop? - } - // if (!m.isShipMode) { - // m.skin.flipFlop() - // } + tech.isDarkMatter = true; //this harm reduction comes from the particle toggling tech.isHarmDarkMatter + spawn.darkMatter() }, remove() { - tech.isRelay = false - if (tech.isFlipFlopOn) { - tech.isFlipFlopOn = false //what is the state of flip-Flop? + tech.isDarkMatter = false; + tech.isHarmDarkMatter = false; + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isDarkMatter) mob[i].alive = false; } - m.eyeFillColor = 'transparent' - // m.resetSkin(); } }, { - name: "lithium-ion", - description: "if relay switch is ON
+300 maximum energy", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isRelay - }, - requires: "relay switch", - effect() { - tech.isRelayEnergy = true - m.setMaxEnergy() + name: "axion", + descriptionFunction() { + return `while ${tech.isNotDarkMatter ? "outside" : "inside"} dark matter
2x damage` }, - remove() { - tech.isRelayEnergy = false - m.setMaxEnergy() - } - }, - { - name: "thermocouple", - description: "if relay switch is ON
condense 4-13 ice IX crystals per second", - maxCount: 9, + maxCount: 1, count: 0, - frequency: 3, - frequencyDefault: 3, + frequency: 2, + frequencyDefault: 2, allowed() { - return tech.isRelay + return tech.isDarkMatter }, - requires: "relay switch", + requires: "dark matter", effect() { - tech.relayIce++ + tech.isAxion = true }, remove() { - tech.relayIce = 0 + tech.isAxion = false } }, { - name: "first derivative", + name: "dark energy", descriptionFunction() { - return `while your first gun is equipped
+15% defense per gun (${(100 * (1 - 0.85 ** b.inventory.length)).toFixed(0)}%)` + return `while ${tech.isNotDarkMatter ? "outside" : "inside"} dark matter
generate 10 energy per second` }, maxCount: 1, count: 0, - frequency: 1, - frequencyDefault: 1, + frequency: 2, + frequencyDefault: 2, allowed() { - return true + return tech.isDarkMatter }, - requires: "", + requires: "dark matter", effect() { - tech.isFirstDer = true + tech.isDarkEnergy = true }, remove() { - tech.isFirstDer = false; + tech.isDarkEnergy = false } }, { name: "MACHO", - description: "a massive compact halo object follows you
+60% defense while you are inside the MACHO", + description: "dark matter's effects are only active outside it's range
1.6x to all dark matter effects
", maxCount: 1, count: 0, - frequency: 1, - frequencyDefault: 1, + frequency: 2, + frequencyDefault: 2, allowed() { - return true + return tech.isDarkMatter && !tech.isMoveDarkMatter && !tech.isDarkStar }, - requires: "", + requires: "dark matter, not entropic gravity, dark star", effect() { - tech.isMACHO = true; //this harm reduction comes from the particle toggling tech.isHarmMACHO - spawn.MACHO() + tech.isNotDarkMatter = true }, remove() { - tech.isMACHO = false; - tech.isHarmMACHO = false; - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].isMACHO) mob[i].alive = false; - } + tech.isNotDarkMatter = false } }, { - name: "axion", - description: "while inside the MACHO
defense increases damage", + name: "entropic gravity", + description: "crouching pulls dark matter towards you
1.6x to all dark matter effects", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.isMACHO + return tech.isDarkMatter && !tech.isNotDarkMatter }, - requires: "MACHO", + requires: "dark matter, not MACHO", effect() { - tech.isAxion = true + tech.isMoveDarkMatter = true }, remove() { - tech.isAxion = false + tech.isMoveDarkMatter = false } }, { name: "dark star", - description: "mobs inside the MACHO are damaged
increase MACHO radius by 15%", + description: `mobs inside dark matter are damaged
1.3x dark matter radius`, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.isMACHO + return tech.isDarkMatter && !tech.isNotDarkMatter }, - requires: "MACHO", + requires: "dark matter, not MACHO", effect() { tech.isDarkStar = true }, @@ -2678,7 +2631,9 @@ const tech = { }, { name: "ablative drones", - description: "after losing health there is a chance
to rebuild your broken parts as drones", + descriptionFunction() { + return `after losing ${tech.isEnergyHealth ? "energy" : "health"} there is a chance
to rebuild your broken parts as drones` + }, maxCount: 1, count: 0, frequency: 1, @@ -2696,7 +2651,7 @@ const tech = { { name: "non-Newtonian armor", link: `non-Newtonian armor`, - description: "after mob collisions
+66% defense for 10 seconds", + description: "after mob collisions
0.3x damage taken for 10 seconds", maxCount: 1, count: 0, frequency: 1, @@ -2714,9 +2669,7 @@ const tech = { }, { name: "tessellation", - description: `use ${powerUps.orb.research(2)}
+35% defense`, - // description: "use 4 research
reduce defense by 50%", - // isFieldTech: true, + description: `use ${powerUps.orb.research(2)}
0.6x damage taken`, maxCount: 1, count: 0, frequency: 1, @@ -2725,10 +2678,6 @@ const tech = { return powerUps.research.count > 1 || build.isExperimentSelection }, requires: "", - // allowed() { - // return (m.fieldMode === 8 || m.fieldMode === 2 || m.fieldMode === 3 || m.fieldMode === 10) && (build.isExperimentSelection || powerUps.research.count > 3) - // }, - // requires: "perfect diamagnetism, negative mass, grappling hook, pilot wave", effect() { tech.isFieldHarmReduction = true for (let i = 0; i < 2; i++) { @@ -2742,7 +2691,7 @@ const tech = { }, { name: "Pauli exclusion", - description: `after mob collisions
become invulnerable for +3.5 seconds`, + description: `for 7 seconds after mob collisions
become invulnerable and inhibit energy regen`, maxCount: 9, count: 0, frequency: 1, @@ -2752,7 +2701,7 @@ const tech = { }, requires: "", effect() { - m.collisionImmuneCycles += 210; + m.collisionImmuneCycles += 480; if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage }, remove() { @@ -2760,27 +2709,63 @@ const tech = { } }, { - name: "spin–statistics theorem", - description: `every 7 seconds
become invulnerable for +1.9 seconds`, - maxCount: 3, + name: "spin-statistics theorem", + description: `for 1.9 seconds out of every 7 seconds
become invulnerable and inhibit energy regen`, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true //m.collisionImmuneCycles > 30 + }, + requires: "", + effect() { + tech.cyclicImmunity += 114; + }, + remove() { + tech.cyclicImmunity = 0; + } + }, + { + name: "fermion", + description: `for 6 seconds after mobs die
become invulnerable and inhibit energy regen`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isMobDeathImmunity = true; + }, + remove() { + tech.isMobDeathImmunity = false; + } + }, + { + name: "abelian group", + description: `3x damage while invulnerable`, + maxCount: 1, count: 0, - frequency: 1, - frequencyDefault: 1, + frequency: 2, + frequencyDefault: 2, allowed() { - return true //m.collisionImmuneCycles > 30 + return tech.isMobDeathImmunity || tech.cyclicImmunity || m.collisionImmuneCycles > 30 }, - requires: "", + requires: "invincibility tech", effect() { - tech.cyclicImmunity += 114; + tech.isImmunityDamage = true; }, remove() { - tech.cyclicImmunity = 0; + tech.isImmunityDamage = false; } }, { name: "refrigerant", descriptionFunction() { - return `after losing at least 5% ${tech.isEnergyHealth ? "energy" : "health"}
freeze all mobs for 7 seconds` + return `after losing at least 5 ${tech.isEnergyHealth ? "energy" : "health"}
freeze all mobs for 7 seconds` }, maxCount: 1, count: 0, @@ -2797,28 +2782,9 @@ const tech = { tech.isHarmFreeze = false; } }, - // { - // name: "clock gating", - // description: `after losing health slow time by 50%
+20% defense`, - // maxCount: 1, - // count: 0, - // frequency: 1, - // frequencyDefault: 1, - // allowed() { - // return simulation.fpsCapDefault > 45 - // }, - // requires: "FPS above 45", - // effect() { - // tech.isSlowFPS = true; - // }, - // remove() { - // tech.isSlowFPS = false; - // } - // }, - { name: "piezoelectricity", - description: "if you collide with a mob
generate +2048 energy", //
reduce defense by 15% + description: "if you collide with a mob
generate +2048 energy", maxCount: 1, count: 0, frequency: 1, @@ -2829,7 +2795,6 @@ const tech = { requires: "not mass-energy", effect() { tech.isPiezo = true; - // if (simulation.isTextLogOpen) m.energy += 20.48; }, remove() { tech.isPiezo = false; @@ -2837,8 +2802,7 @@ const tech = { }, { name: "ground state", - description: "+266 maximum energy
–33% passive energy generation", - // description: "reduce defense by 66%
you no longer passively regenerate energy", + description: "+300 maximum energy
0.66x passive energy generation", maxCount: 1, count: 0, frequency: 1, @@ -2860,7 +2824,7 @@ const tech = { }, { name: "heat engine", - description: `+40% damage
–50 maximum energy`, + description: `1.4x damage
–50 maximum energy`, maxCount: 1, count: 0, frequency: 1, @@ -2874,14 +2838,14 @@ const tech = { m.setMaxEnergy() }, remove() { - if (this.count) tech.damage /= this.damage + if (this.count && m.alive) tech.damage /= this.damage tech.isMaxEnergyTech = false; m.setMaxEnergy() } }, { name: "exothermic process", - description: "+50% damage
after mobs die –20% energy", + description: "1.6x damage
after mobs die –5 energy", maxCount: 1, count: 0, frequency: 1, @@ -2890,24 +2854,28 @@ const tech = { return true }, requires: "", - damage: 1.55, + damage: 1.6, effect() { tech.damage *= this.damage tech.isEnergyLoss = true; }, remove() { - if (this.count) tech.damage /= this.damage + if (this.count && m.alive) tech.damage /= this.damage tech.isEnergyLoss = false; } }, { name: "Gibbs free energy", - description: `for each energy below 100
+0.7% damage`, + descriptionFunction() { + return `1.005x damage for each missing energy
(${(1 + 0.5 * Math.max(0, m.maxEnergy - m.energy)).toFixed(2)}x)` + }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - allowed: () => true, + allowed() { + return true + }, requires: "", effect() { tech.isLowEnergyDamage = true; @@ -2918,19 +2886,19 @@ const tech = { }, { name: "overcharge", - description: "+88 maximum energy
+5% JUNK to tech pool", + description: "+88 maximum energy
+4% JUNK choices", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return true + return tech.junkChance < 1 }, requires: "", effect() { - tech.bonusEnergy += 0.66 + tech.bonusEnergy += 0.88 m.setMaxEnergy() - this.refundAmount += tech.addJunkTechToPool(0.05) + this.refundAmount += tech.addJunkTechToPool(0.04) }, refundAmount: 0, remove() { @@ -2944,13 +2912,13 @@ const tech = { }, { name: "Maxwells demon", - description: "energy above max decays by 30% 1% per second
+5% JUNK to tech pool", + description: "energy above maximum decays 30x slower
+5% JUNK choices", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return m.energy > m.maxEnergy || build.isExperimentSelection + return (m.energy > m.maxEnergy || build.isExperimentSelection) && tech.junkChance < 1 }, requires: "energy above your max", effect() { @@ -2968,7 +2936,7 @@ const tech = { }, { name: "inductive charging", - description: "if crouched +600% passive energy generation
if not crouched energy generation is disabled", + description: "if crouched 7x passive energy generation
otherwise 0x passive energy generation", maxCount: 1, count: 0, frequency: 1, @@ -2980,7 +2948,7 @@ const tech = { effect() { tech.isCrouchRegen = true; //only used to check for requirements m.regenEnergy = function () { - if (m.immuneCycle < m.cycle && m.crouch && m.fieldCDcycle < m.cycle) m.energy += 7 * m.fieldRegen; + if (m.immuneCycle < m.cycle && m.crouch && m.fieldCDcycle < m.cycle) m.energy += 7 * m.fieldRegen * level.isReducedRegen; if (m.energy < 0) m.energy = 0 } }, @@ -2991,7 +2959,7 @@ const tech = { }, { name: "energy conservation", - description: "4% of damage done recovered as energy", + description: "doing damage to mobs generates energy", maxCount: 9, count: 0, frequency: 1, @@ -3009,7 +2977,7 @@ const tech = { }, { name: "parasitism", - description: "if a mob has died in the last 5 seconds
+93% damage, no passive energy generation", + description: "if a mob has died in the last 5 seconds
2x damage, no passive energy generation", maxCount: 1, count: 0, frequency: 1, @@ -3021,7 +2989,7 @@ const tech = { effect() { tech.isDamageAfterKillNoRegen = true; m.regenEnergy = function () { - if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle) && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen; + if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle) && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen * level.isReducedRegen; if (m.energy < 0) m.energy = 0 } }, @@ -3032,7 +3000,7 @@ const tech = { }, { name: "waste heat recovery", - description: "if a mob has died in the last 5 seconds
generate 5% of max energy per second", + description: "if a mob has died in the last 5 seconds
generate 0.05x maximum energy every second", maxCount: 1, count: 0, frequency: 1, @@ -3051,7 +3019,7 @@ const tech = { { name: "recycling", descriptionFunction() { - return `if a mob has died in the last 5 seconds
recover 0.5% of max ${tech.isEnergyHealth ? "energy" : "health"} per second` + return `if a mob has died in the last 5 seconds
recover 0.005x maximum ${tech.isEnergyHealth ? "energy" : "health"} every second` }, description: "", maxCount: 1, @@ -3072,7 +3040,7 @@ const tech = { }, { name: "torpor", - description: "if a mob has not died in the last 5 seconds
+74% defense", + description: "if a mob has not died in the last 5 seconds
0.3x damage taken", maxCount: 1, count: 0, frequency: 1, @@ -3088,17 +3056,79 @@ const tech = { tech.isHarmReduceNoKill = false; } }, + { + name: "stability", + descriptionFunction() { + return `0.3x damage taken
while your health is at maximum` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isMaxHealthDefense = true; + }, + remove() { + tech.isMaxHealthDefense = false; + } + }, + { + name: "instability", + descriptionFunction() { + return `2x damage while your damage taken is 1.00x
(current damage taken = ${(m.defense()).toFixed(2)}x)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.noDefenseSettingDamage = true; + }, + remove() { + tech.noDefenseSettingDamage = false; + } + }, + { + name: "control theory", + descriptionFunction() { + return `1.5x damage
while your health is at maximum` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isMaxHealthDamage = true; + }, + remove() { + tech.isMaxHealthDamage = false; + } + }, { name: "homeostasis", descriptionFunction() { - return `for each health below 100
+0.8% defense (${(100 * (Math.max(0, 1 - m.health) * 0.8)).toFixed(0)}%)` + // return `0.9x damage taken for each ${name} missing
(${(Math.pow(0.1 * max, Math.max(0, max - h))).toFixed(2)}x)` + const scale = 0.2 //adjust this to control the strength of this effect + return `missing ${tech.isEnergyHealth ? "energy" : "health"} reduces damage taken
down to a limit of ${scale}x at 0 ${tech.isEnergyHealth ? "energy" : "health"}(${(Math.pow(scale, Math.max(0, 1 - (tech.isEnergyHealth ? m.energy / m.maxEnergy : m.health / m.maxHealth)))).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return m.health < 0.6 || build.isExperimentSelection + return m.health < 0.9 || build.isExperimentSelection }, requires: "health below 60", effect() { @@ -3111,16 +3141,16 @@ const tech = { { name: "negative feedback", descriptionFunction() { - return `for each ${tech.isEnergyHealth ? "energy" : "health"} below 100
+0.7% damage (${(70 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(0)}%)` + return `1.006x damage for each missing ${tech.isEnergyHealth ? "energy" : "health"}
(${(1 + 0.6 * Math.max(0, (tech.isEnergyHealth ? m.maxEnergy - m.energy : m.maxHealth - m.health))).toFixed(2)}x)` //1 + 0.6 * Math.max(0, (tech.isEnergyHealth ? m.maxEnergy - m.energy : m.maxHealth - m.health)) }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return m.health < 0.6 || build.isExperimentSelection + return m.health < 0.9 || build.isExperimentSelection }, - requires: "health below 60", + requires: "health below 90", effect() { tech.isLowHealthDmg = true; //used in mob.damage() }, @@ -3131,10 +3161,8 @@ const tech = { { name: "Zenos paradox", descriptionFunction() { - return `+85% defense
–5% of current ${tech.isEnergyHealth ? "energy" : "health"} every 5 seconds` + return `0.15x damage taken
–5% of current ${tech.isEnergyHealth ? "energy" : "health"} every 5 seconds` }, - // description: "+85% defense
–5% of current health every 5 seconds", - // description: "every 5 seconds remove 1/10 of your health
reduce defense by 90%", maxCount: 1, count: 0, frequency: 1, @@ -3153,31 +3181,31 @@ const tech = { { name: "antiscience", descriptionFunction() { - return `–10 ${tech.isEnergyHealth ? "energy" : "health"} after picking up a tech
+66% damage` + return `–10 ${tech.isEnergyHealth ? "energy" : "health"} after picking up ${powerUps.orb.tech()}
1.7x damage` }, - // description: "+66% damage
–10 health after picking up a tech", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, + isBadRandomOption: true, allowed() { return true }, requires: "", - damage: 1.66, + damage: 1.7, effect() { tech.damage *= this.damage tech.isTechDamage = true; }, remove() { - if (this.count) tech.damage /= this.damage + if (this.count && m.alive) tech.damage /= this.damage tech.isTechDamage = false; } }, { name: "ergodicity", descriptionFunction() { - return `${powerUps.orb.heal()} have -50% effect
+66% damage` + return `0.50x healing from ${powerUps.orb.heal()}
1.7x damage` }, maxCount: 1, count: 0, @@ -3187,7 +3215,7 @@ const tech = { return true }, requires: "", - damage: 1.66, + damage: 1.7, effect() { tech.damage *= this.damage tech.isHalfHeals = true; @@ -3200,7 +3228,7 @@ const tech = { } }, remove() { - if (this.count) { + if (this.count && m.alive) { tech.damage /= this.damage for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "heal") { @@ -3215,7 +3243,7 @@ const tech = { }, { name: "fluoroantimonic acid", - description: "if your health is above 100
+35% damage", + description: "if your health is above 100
1.35x damage", maxCount: 1, count: 0, frequency: 2, @@ -3223,7 +3251,7 @@ const tech = { allowed() { return m.maxHealth > 1; }, - requires: "max health above 100", + requires: "maximum health above 100", effect() { tech.isAcidDmg = true; }, @@ -3234,9 +3262,8 @@ const tech = { { name: "induction brake", descriptionFunction() { - return `after using ${powerUps.orb.heal()} slow nearby mobs for 15 seconds
spawn ${powerUps.orb.heal(4)}` + return `after using ${powerUps.orb.heal()}
slow nearby mobs for 17 seconds` }, - // description: `after using ${powerUps.orb.heal()} slow nearby mobs for 15 seconds
spawn ${powerUps.orb.heal(4)}`, maxCount: 1, count: 0, frequency: 1, @@ -3247,7 +3274,6 @@ const tech = { requires: "not eddy current brake", effect() { tech.isHealBrake = true; - for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal"); }, remove() { tech.isHealBrake = false; @@ -3256,7 +3282,7 @@ const tech = { { name: "adiabatic healing", descriptionFunction() { - return `${powerUps.orb.heal()} have +100% effect
+4% JUNK to tech pool` + return `2x healing from ${powerUps.orb.heal()}
+4% JUNK choices` }, maxCount: 3, count: 0, @@ -3264,7 +3290,7 @@ const tech = { frequencyDefault: 1, isHealTech: true, allowed() { - return (m.health / m.maxHealth) < 0.7 || build.isExperimentSelection + return ((m.health / m.maxHealth) < 0.7 || build.isExperimentSelection) && tech.junkChance < 1 }, requires: "under 70% health", effect() { @@ -3299,7 +3325,7 @@ const tech = { { name: "quenching", descriptionFunction() { - return `${powerUps.orb.heal()} over healing results in 200% health loss
and 200% max health increase` + return `0.3x of ${powerUps.orb.heal()} overhealing
is added to maximum health` }, maxCount: 1, count: 0, @@ -3320,9 +3346,8 @@ const tech = { { name: "accretion", descriptionFunction() { - return `${powerUps.orb.heal(1)} follow you, even between levels
spawn ${powerUps.orb.heal(7)}` + return `${powerUps.orb.heal(1)} follow you, even between levels
+4% chance to duplicate spawned power ups` }, - // description: `${powerUps.orb.heal(1)} follow you, even between levels
spawn ${powerUps.orb.heal(5)}`, maxCount: 1, count: 0, frequency: 1, @@ -3335,7 +3360,7 @@ const tech = { effect() { tech.isHealAttract = true powerUps.setPowerUpMode(); - for (let i = 0; i < 7; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal"); + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.04); }, remove() { tech.isHealAttract = false @@ -3345,7 +3370,7 @@ const tech = { { name: "accretion disk", descriptionFunction() { - return `+5% damage (${5 * powerUp.length}%)
for each power up that exists on this level` + return `1.07x damage for each power up on this level
+5% JUNK choices (${(1 + 0.07 * powerUp.length).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -3358,15 +3383,44 @@ const tech = { requires: "accretion", effect() { tech.isPowerUpDamage = true + this.refundAmount += tech.addJunkTechToPool(0.05) }, + refundAmount: 0, remove() { tech.isPowerUpDamage = false + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + }, + }, + { + name: "maintenance", + descriptionFunction() { + return `2x frequency for ${powerUps.orb.tech()} with healing
spawn ${powerUps.orb.heal(13)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isInstant: true, + isBadRandomOption: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 13; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal"); + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 2 + } }, + remove() { } }, { name: "self-assembly", descriptionFunction() { - return `at the start of each level
for every 25% missing ${tech.isEnergyHealth ? "energy" : "health"} spawn ${powerUps.orb.heal()}` + return `at the start of each level
for every 20 missing ${tech.isEnergyHealth ? "energy" : "health"} spawn ${powerUps.orb.heal()}` }, maxCount: 1, count: 0, @@ -3385,38 +3439,36 @@ const tech = { } }, { - name: "maintenance", + name: "interest", descriptionFunction() { - return `
double
the frequency of finding healing tech
spawn ${powerUps.orb.heal(13)}` + return `at the start of each level spawn
${(100 * this.rate).toFixed(0)}% of your ${powerUps.orb.research(1)}, ${powerUps.orb.ammo(1)}, ${powerUps.orb.coupling(1)}, and ${powerUps.orb.heal(1)} (rounded up)` }, - maxCount: 1, + maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, allowed() { return true }, requires: "", + rate: 0.06, effect() { - for (let i = 0; i < 13; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal"); - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 2 - } + tech.interestRate += this.rate; }, - remove() { } + remove() { + tech.interestRate = 0; + } }, { name: "anthropic principle", - nameInfo: "", - addNameInfo() { - setTimeout(function () { - powerUps.research.changeRerolls(0) - }, 1000); - }, + // nameInfo: "", + // addNameInfo() { + // setTimeout(function () { + // powerUps.research.changeRerolls(0) + // }, 1000); + // }, descriptionFunction() { - return `once per level, instead of dying
use ${powerUps.orb.research(1)} and spawn ${powerUps.orb.heal(16)}` + return `once per level, instead of dying use ${powerUps.orb.research(1)} and
spawn ${powerUps.orb.heal(22)}` }, maxCount: 1, count: 0, @@ -3440,7 +3492,7 @@ const tech = { }, { name: "weak anthropic principle", - description: "after anthropic principle prevents your death
+50% duplication chance for that level", + description: "after anthropic principle prevents your death
+60% duplication chance for that level", maxCount: 1, count: 0, frequency: 3, @@ -3460,7 +3512,7 @@ const tech = { }, { name: "strong anthropic principle", - description: "after anthropic principle prevents your death
+137.03599% damage for that level", + description: "after anthropic principle prevents your death
2.71828x damage for that level", maxCount: 1, count: 0, frequency: 3, @@ -3478,7 +3530,7 @@ const tech = { }, { name: "quantum immortality", - description: "+30% defense
after dying, continue in an alternate reality", + description: "0.7x damage taken
after dying, continue in an alternate reality", maxCount: 1, count: 0, frequency: 1, @@ -3496,8 +3548,7 @@ const tech = { }, { name: "many-worlds", - // description: "each level is an alternate reality, where you
find a tech at the start of each level", - description: `on each new level spawn a tech power up
and enter an alternate reality`, + description: `at the start of each level spawn ${powerUps.orb.tech()} ${powerUps.orb.coupling(3)}
and enter an alternate reality`, maxCount: 1, count: 0, frequency: 1, @@ -3517,7 +3568,7 @@ const tech = { { name: "Ψ(t) collapse", link: `Ψ(t) collapse`, - description: `spawn ${powerUps.orb.research(21)}
after you research enter an alternate reality`, + description: `after a boss dies spawn ${powerUps.orb.research(6)}
if you research enter an alternate reality`, maxCount: 1, count: 0, frequency: 1, @@ -3530,16 +3581,16 @@ const tech = { bonusResearch: 21, effect() { tech.isResearchReality = true; - for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + Math.random() * 60, m.pos.y + Math.random() * 60, "research", false); + // for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + Math.random() * 60, m.pos.y + Math.random() * 60, "research", false); }, remove() { tech.isResearchReality = false; - if (this.count > 0) powerUps.research.changeRerolls(-this.bonusResearch) + // if (this.count > 0) powerUps.research.changeRerolls(-this.bonusResearch) } }, { name: "decoherence", - description: `tech options you don't choose won't reoccur
spawn ${powerUps.orb.research(7)}`, + description: `after a boss dies spawn ${powerUps.orb.research(2)}
${powerUps.orb.tech()} options you don't choose won't reoccur`, maxCount: 1, count: 0, frequency: 1, @@ -3551,7 +3602,6 @@ const tech = { bonusResearch: 7, effect() { tech.isBanish = true - for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); }, remove() { if (tech.isBanish) { @@ -3560,20 +3610,55 @@ const tech = { for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].isBanished) tech.tech[i].isBanished = false } - powerUps.research.changeRerolls(-this.bonusResearch) } tech.isBanish = false } }, + { + name: "peer review", + description: `after you research gain 1.05x damage
and +1% JUNK choices`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (powerUps.research.count > 0 || build.isExperimentSelection) && !tech.isSuperDeterminism + }, + requires: "at least 1 research, not superdeterminism", + effect() { + tech.isResearchDamage = true; + }, + remove() { + tech.isResearchDamage = false; + } + }, + { + name: "pseudoscience", + description: "research 2 times for free, but
+1% JUNK choices each time", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isResearchReality && !tech.isSuperDeterminism + }, + requires: "not Ψ(t) collapse, superdeterminism", + effect() { + tech.isJunkResearch = true; + }, + remove() { + tech.isJunkResearch = false; + } + }, { name: "renormalization", - description: `47% chance to spawn ${powerUps.orb.research(1)} after consuming ${powerUps.orb.research(1)}
+5% JUNK to tech pool`, + description: `47% chance to spawn ${powerUps.orb.research(1)} after consuming ${powerUps.orb.research(1)}
+5% JUNK choices`, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return (powerUps.research.count > 3 || build.isExperimentSelection) && !tech.isSuperDeterminism + return (powerUps.research.count > 3 || build.isExperimentSelection) && !tech.isSuperDeterminism && tech.junkChance < 1 }, requires: "at least 4 research, not superdeterminism", effect() { @@ -3592,10 +3677,7 @@ const tech = { }, { name: "perturbation theory", - description: `if you have no ${powerUps.orb.research(1)} in your inventory
+60% fire rate`, - // descriptionFunction() { - // return `+40% damage, but -10% damage
for each ${powerUps.orb.research(1)} in your inventory (${40 - 10 * powerUps.research.count}% damage)` - // }, + description: `if you have no ${powerUps.orb.research(1)} in your inventory
2x fire rate`, maxCount: 1, count: 0, frequency: 1, @@ -3606,7 +3688,7 @@ const tech = { requires: "no research", effect() { tech.isRerollHaste = true; - tech.researchHaste = 0.4; //+60% fire rate + powerUps.research.changeRerolls(0) b.setFireCD(); }, remove() { @@ -3615,29 +3697,10 @@ const tech = { b.setFireCD(); } }, - { - name: "ansatz", - description: `after choosing a field, tech, or gun
if you have no ${powerUps.orb.research(1)} in your inventory spawn ${powerUps.orb.research(3)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return powerUps.research.count < 1 && !tech.isSuperDeterminism && !tech.isRerollHaste && !tech.isResearchReality - }, - requires: "no research, not superdeterminism, Ψ(t) collapse, perturbation theory", - effect() { - tech.isAnsatz = true; - }, - remove() { - tech.isAnsatz = false; - } - }, { name: "Bayesian statistics", - // description: `for each ${powerUps.orb.research(1)} in your inventory
+3.8% damage`, descriptionFunction() { - return `spawn ${powerUps.orb.research(this.bonusResearch)}
+3% damage per ${powerUps.orb.research(1)} in your inventory (${(3 * powerUps.research.count).toFixed(0)}%)` + return `1.05x damage per ${powerUps.orb.research(1)} in your inventory
(${(1 + Math.max(0, 0.05 * powerUps.research.count)).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -3647,121 +3710,81 @@ const tech = { return powerUps.research.count > 1 || build.isExperimentSelection }, requires: "at least 2 research", - bonusResearch: 6, effect() { - powerUps.spawnDelay("research", this.bonusResearch) tech.isRerollDamage = true; }, remove() { tech.isRerollDamage = false; - if (this.count) { - powerUps.research.changeRerolls(-this.bonusResearch) - } } }, { - name: "mass production", - descriptionFunction() { - return `spawn ${powerUps.orb.ammo(3)}${powerUps.orb.heal(3)}  ${powerUps.orb.research(2)}
tech have extra choices to spawn ${powerUps.orb.ammo(1)},  ${powerUps.orb.heal(1)},  or  ${powerUps.orb.research(1)}` - }, - // description: `< strong class='color-m' > tech
always have < strong > +3
choices to spawn < br > ${ powerUps.orb.ammo(8) } ${ powerUps.orb.heal(8) } & nbsp;& nbsp; or ${ powerUps.orb.research(5) } `, + name: "ansatz", + description: `after choosing ${powerUps.orb.field()}, ${powerUps.orb.tech()}, or ${powerUps.orb.gun()}
if you have no ${powerUps.orb.research(1)} in your inventory spawn ${powerUps.orb.research(3)}`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - allowed() { return true }, - requires: "", + allowed() { + return powerUps.research.count < 1 && !tech.isSuperDeterminism && !tech.isRerollHaste + }, + requires: "no research, not superdeterminism, Ψ(t) collapse, perturbation theory", effect() { - tech.isMassProduction = true - powerUps.spawnDelay("ammo", 4); - powerUps.spawnDelay("heal", 4); - powerUps.spawnDelay("research", 2); + tech.isAnsatz = true; }, remove() { - tech.isMassProduction = false + tech.isAnsatz = false; } }, { - name: "research", - descriptionFunction() { - return `spawn ${this.value > 36 ? this.value + powerUps.orb.research(1) : powerUps.orb.research(this.value)}
next time this effect is improved by ${powerUps.orb.research(5)}` - }, - maxCount: 9, - count: 0, - frequency: 0, - frequencyDefault: 0, - isNonRefundable: true, - isMassProduction: true, - allowed() { return true }, - requires: "", - value: 8, - effect() { - powerUps.spawnDelay("research", this.value); - this.value += 5 - }, - remove() { } - }, - { - name: "ammo", - descriptionFunction() { - return `spawn ${this.value > 33 ? this.value + powerUps.orb.ammo(1) : powerUps.orb.ammo(this.value)}
next time this effect is improved by ${powerUps.orb.ammo(7)}` - }, - maxCount: 9, + name: "unified field theory", + description: `when paused you can click to change your ${powerUps.orb.field()}
2x frequency for ${powerUps.orb.fieldTech()}`, + maxCount: 1, count: 0, - frequency: 0, - frequencyDefault: 0, - isNonRefundable: true, - isMassProduction: true, - allowed() { return true }, - requires: "", - value: 10, - effect() { - powerUps.spawnDelay("ammo", this.value); - this.value += 7 - }, - remove() { } - }, - { - name: "heals", - descriptionFunction() { - return `spawn ${this.value > 30 ? this.value + powerUps.orb.heal(1) : powerUps.orb.heal(this.value)}
next time this effect is improved by ${powerUps.orb.heal(7)}` + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism && !tech.isNoDraftPause }, - maxCount: 9, - count: 0, - frequency: 0, - frequencyDefault: 0, - isNonRefundable: true, - isMassProduction: true, - allowed() { return true }, - requires: "", - value: 10, + requires: "not superdeterminism, eternalism", effect() { - powerUps.spawnDelay("heal", this.value); - this.value += 7 + tech.isPauseSwitchField = true; + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 2 + } }, - remove() { } + remove() { + tech.isPauseSwitchField = false; + if (this.count > 1) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 2 + } + } + } }, { - name: "pseudoscience", - description: "when selecting a power up, research 3 times
for free, but add 1-3% JUNK to the tech pool", + name: "eternalism", + description: `1.25x damage
time can't be paused (time can still be dilated)`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isResearchReality && !tech.isSuperDeterminism //tech.isResearchBoss || tech.isMetaAnalysis || tech.isRerollBots || tech.isDeathAvoid || tech.isRerollDamage || build.isExperimentSelection + return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause }, - requires: "not Ψ(t) collapse, superdeterminism", //"abiogenesis, meta-analysis, bot fabrication, anthropic principle, or Bayesian statistics, not Ψ(t) collapse", + requires: "not unified field theory, paradigm shift, invariant", + damage: 1.25, effect() { - tech.isJunkResearch = true; + tech.damage *= this.damage + tech.isNoDraftPause = true }, remove() { - tech.isJunkResearch = false; + if (this.count && m.alive) tech.damage /= this.damage + tech.isNoDraftPause = false } }, { name: "brainstorming", - description: "tech choices randomize
every 1.5 seconds for 10 seconds", + description: `randomize ${powerUps.orb.tech()} choices
every 1.5 seconds for 10 seconds`, maxCount: 1, count: 0, frequency: 1, @@ -3782,7 +3805,7 @@ const tech = { }, { name: "cross-disciplinary", - description: "tech have an extra field or gun choice
+5% chance to duplicate spawned power ups", + description: `${powerUps.orb.tech()} have an extra ${powerUps.orb.field()} or ${powerUps.orb.gun()} choice
+5% chance to duplicate spawned power ups`, maxCount: 1, count: 0, frequency: 1, @@ -3793,15 +3816,17 @@ const tech = { requires: "not determinism", effect() { tech.isExtraGunField = true; + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.05); }, remove() { tech.isExtraGunField = false; + if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "emergence", - description: "tech, fields, and guns have +1 choice
+8% damage", - // description: "tech, fields, and guns have +2 choices
+3% JUNK to tech pool", + description: `${powerUps.orb.field()}, ${powerUps.orb.tech()}, and ${powerUps.orb.gun()} have +1 choice
1.1x damage`, maxCount: 9, count: 0, frequency: 1, @@ -3810,39 +3835,35 @@ const tech = { return !tech.isDeterminism }, requires: "not determinism", - damage: 1.08, + damage: 1.1, effect() { tech.extraChoices += 1; tech.damage *= this.damage - // this.refundAmount += tech.addJunkTechToPool(0.03) }, refundAmount: 0, remove() { tech.extraChoices = 0; - if (this.count > 0) { - tech.damage /= this.damage - // if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) - } + if (this.count && m.alive) tech.damage /= this.damage } }, { name: "path integral", link: `path integral`, - description: "your next tech choice has all possible options
+5% JUNK to tech pool", + description: `your next ${powerUps.orb.tech()} has all possible choices
+4% JUNK choices`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isNonRefundable: true, + isInstant: true, // isJunk: true, allowed() { - return !tech.isDeterminism && !tech.isBrainstorm + return !tech.isDeterminism && !tech.isBrainstorm && tech.junkChance < 1 }, requires: "not determinism, brainstorm", effect() { tech.tooManyTechChoices = 1 // for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); - this.refundAmount += tech.addJunkTechToPool(0.05) + this.refundAmount += tech.addJunkTechToPool(0.04) }, refundAmount: 0, remove() { @@ -3855,15 +3876,15 @@ const tech = { }, { name: "determinism", - description: "spawn 5 tech
only 1 choice for tech, fields, and guns", + description: `spawn ${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}
${powerUps.orb.field()}, ${powerUps.orb.tech()}, and ${powerUps.orb.gun()} have only 1 choice`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, - isNonRefundable: true, + isInstant: true, allowed() { - return !tech.extraChoices && !tech.isExtraGunField && !tech.isFlipFlopChoices && !tech.isExtraBotOption + return !tech.extraChoices && !tech.isExtraGunField && !tech.isExtraBotOption }, requires: "not emergence, cross-disciplinary, integrated circuit", effect() { @@ -3877,13 +3898,13 @@ const tech = { }, { name: "superdeterminism", - description: `spawn 5 tech
you can't cancel and ${powerUps.orb.research(1)} no longer spawn`, + description: `spawn ${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}
you can't cancel and ${powerUps.orb.research(1)} no longer spawn`, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBadRandomOption: true, - isNonRefundable: true, + isInstant: true, allowed() { return tech.isDeterminism && !tech.isAnsatz && !tech.isJunkResearch && !tech.isBrainstorm }, @@ -3900,13 +3921,13 @@ const tech = { { name: "technical debt", descriptionFunction() { - // return `+300% damage –15% damage
for each tech you have learned (+${(Math.floor(100 * (Math.min(Math.pow(0.85, tech.totalCount - 20), 4 - 0.15 * tech.totalCount))) - 100)}%)` - return `+300% damage –15% damage
for each tech you have learned (${(tech.totalCount > 20 ? 100 * (Math.pow(0.85, tech.totalCount - 20) - 1) : 100 * (3 - 0.15 * tech.totalCount)).toFixed(0)}%)` + return `increase damage by 4x, but reduce damage
by 0.15x for each ${powerUps.orb.tech()} you have(${(tech.totalCount > 20 ? (Math.pow(0.85, tech.totalCount - 20)) : (4 - 0.15 * tech.totalCount)).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, + isBadRandomOption: true, allowed() { return true }, @@ -3918,34 +3939,15 @@ const tech = { tech.isTechDebt = false; } }, - // { - // name: "abiogenesis", - // // description: `use ${powerUps.orb.research(4)}(or 49% JUNK to the tech pool if you can't) to add a 2nd boss to each level`, - // description: `as a level begins spawn a 2nd boss using ${powerUps.orb.research(3)}
(+49% JUNK to the tech pool if you can't pay)
`, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return (build.isExperimentSelection || powerUps.research.count > 2) && !tech.isDuplicateMobs - // }, - // requires: "at least 3 research, not parthenogenesis", - // effect() { - // tech.isResearchBoss = true; - // }, - // remove() { - // tech.isResearchBoss = false; - // } - // }, { name: "meta-analysis", - description: `if you choose a JUNKtech you instead get a
random normal tech and spawn ${powerUps.orb.research(2)}`, + description: `if you choose JUNK
you get a random choice and ${powerUps.orb.research(2)} instead`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return tech.junkChance > 0 + return tech.junkChance > 0.1 }, requires: "some JUNK tech", effect() { @@ -3957,23 +3959,23 @@ const tech = { }, { name: "dark patterns", - description: "+22% damage
+11% JUNK to tech pool", + description: "1.3x damage
+15% JUNK choices", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return true + return tech.junkChance < 1 }, requires: "", - damage: 1.22, + damage: 1.3, effect() { tech.damage *= this.damage - this.refundAmount += tech.addJunkTechToPool(0.11) + this.refundAmount += tech.addJunkTechToPool(0.15) }, refundAmount: 0, remove() { - if (this.count > 0) { + if (this.count && m.alive) { tech.damage /= this.damage if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) } @@ -3982,7 +3984,7 @@ const tech = { { name: "junk DNA", descriptionFunction() { - return `increase damage by twice the
JUNK tech pool percent (${(200 * tech.junkChance).toFixed(0)}%)` + return `increase damage by twice the
JUNK chance (${(1 + 2 * (tech.junkChance + level.junkAdded)).toFixed(2)}x)` }, maxCount: 1, count: 0, @@ -4000,124 +4002,100 @@ const tech = { } }, { - name: "exciton", + name: "mass production", descriptionFunction() { - return `after mobs die they have a 14% chance to
spawn ${powerUps.orb.boost(1)} that give +${(powerUps.boost.damage * 100).toFixed(0)}% damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds
` + return `${powerUps.orb.tech()} have extra choices to spawn ${powerUps.orb.ammo(1)},  ${powerUps.orb.heal(1)},  or  ${powerUps.orb.research(1)}
+3% chance to duplicate spawned power ups` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - allowed: () => true, + allowed() { return true }, requires: "", effect() { - tech.isBoostPowerUps = true + tech.isMassProduction = true + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.03); }, remove() { - tech.isBoostPowerUps = false + tech.isMassProduction = false + if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance + } }, { - name: "band gap", + name: "research", descriptionFunction() { - return `${powerUps.orb.boost(1)} give +77% damage
but their duration is reduced by 1 second` + return `spawn ${this.value > 36 ? this.value + powerUps.orb.research(1) : powerUps.orb.research(this.value)}
next time increase amount spawned by +5${powerUps.orb.research(1)}` }, maxCount: 9, - count: 1, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isBoostPowerUps || tech.isBoostReplaceAmmo - }, - requires: "exciton, quasiparticles", - effect() { - powerUps.boost.duration -= 60 - powerUps.boost.damage += 0.77 - }, - remove() { - powerUps.boost.duration = 600 - powerUps.boost.damage = 1.25 - } - }, - { - name: "eternalism", - description: "+24% damage
time can't be paused (time can be dilated)", - maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - allowed() { - return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause - }, - requires: "not unified field theory, paradigm shift, invariant", - damage: 1.24, + isInstant: true, + isMassProduction: true, + allowed() { return true }, + requires: "", + value: 8, + defaultValue: 8, effect() { - tech.damage *= this.damage - tech.isNoDraftPause = true + powerUps.spawnDelay("research", this.value); + this.value += 5 }, - remove() { - if (this.count) tech.damage /= this.damage - tech.isNoDraftPause = false - } + remove() { } }, { - name: "paradigm shift", - // description: `when paused clicking a tech ejects it
with a 20% chance to remove without ejecting`, - // description: `when paused clicking a tech ejects it
and a 20% chance to remove without ejecting`, + name: "ammo", descriptionFunction() { - return `when paused clicking a tech ejects it
–4 ${tech.isEnergyHealth ? "energy" : "health"} each time and a 3% chance to fail` + return `spawn ${this.value > 33 ? this.value + powerUps.orb.ammo(1) : powerUps.orb.ammo(this.value)}
next time increase amount spawned by +7${powerUps.orb.ammo(1)}` }, - maxCount: 1, + maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism && !tech.isNoDraftPause - }, - requires: "not superdeterminism, eternalism", + isInstant: true, + isMassProduction: true, + allowed() { return true }, + requires: "", + value: 10, + defaultValue: 10, effect() { - tech.isPauseEjectTech = true; + powerUps.spawnDelay("ammo", this.value); + this.value += 7 }, - remove() { - tech.isPauseEjectTech = false; - } + remove() { } }, { - name: "unified field theory", - description: `in the pause menu click to switch fields
double the frequency of finding fieldtech`, - maxCount: 1, + name: "heals", + descriptionFunction() { + return `spawn ${this.value > 30 ? this.value + powerUps.orb.heal(1) : powerUps.orb.heal(this.value)}
next time increase amount spawned by +7${powerUps.orb.heal(1)}` + }, + maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism && !tech.isNoDraftPause - }, - requires: "not superdeterminism, eternalism", + isInstant: true, + isMassProduction: true, + allowed() { return true }, + requires: "", + value: 10, + defaultValue: 10, effect() { - tech.isPauseSwitchField = true; - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 2 - } + powerUps.spawnDelay("heal", this.value); + this.value += 7 }, - remove() { - tech.isPauseSwitchField = false; - if (this.count > 1) { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 2 - } - } - } + remove() { } }, { name: "field coupling", descriptionFunction() { - // return `spawn ${powerUps.orb.coupling(10)}
that each give +0.1 coupling` //
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} return `spawn ${powerUps.orb.coupling(10)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` }, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, + isInstant: true, allowed() { return true }, @@ -4126,7 +4104,7 @@ const tech = { powerUps.spawnDelay("coupling", 10) }, remove() { - if (this.count) m.couplingChange(-this.count * 10) + // if (this.count) m.couplingChange(-this.count * 10) } }, { @@ -4141,7 +4119,6 @@ const tech = { orbText = powerUps.orb.coupling(converted) } return `convert ${this.researchUsed} ${powerUps.orb.research(1)} into ${orbText}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` - } else { let converted = powerUps.research.count * this.couplingToResearch let orbText @@ -4152,13 +4129,12 @@ const tech = { } return `convert ${powerUps.research.count} ${powerUps.orb.research(1)} into ${orbText}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` } - - }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, + isInstant: true, allowed() { return powerUps.research.count > 3 }, @@ -4218,29 +4194,28 @@ const tech = { name: "fine-structure constant", descriptionFunction() { // return `spawn ${this.value} ${powerUps.orb.coupling(1)} that each give +0.1 coupling
-0.5 coupling after mob collisions`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} - return `spawn ${this.value} ${powerUps.orb.coupling(1)}, but lose ${powerUps.orb.coupling(5)} after mob collisions
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` + return `after a boss dies spawn ${powerUps.orb.coupling(9)}
lose ${powerUps.orb.coupling(3)} after mob collisions`//
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)} }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isNonRefundable: true, + // isInstant: true, allowed: () => true, requires: "", value: 60, effect() { tech.isCouplingNoHit = true - powerUps.spawnDelay("coupling", this.value) + // powerUps.spawnDelay("coupling", this.value) }, remove() { - if (this.count) m.couplingChange(-this.value) + // if (this.count) m.couplingChange(-this.value) tech.isCouplingNoHit = false } }, { name: "residual dipolar coupling", descriptionFunction() { - // return `clicking cancel for a field, tech, or gun
spawns ${powerUps.orb.coupling(5)}that each give +0.1 coupling`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} return `clicking cancel spawns ${powerUps.orb.coupling(8)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` }, maxCount: 1, @@ -4261,7 +4236,7 @@ const tech = { { name: "commodities exchange", descriptionFunction() { - return `clicking cancel for a field, tech, or gun
spawns 10-14 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` + return `clicking cancel for ${powerUps.orb.field()}, ${powerUps.orb.tech()}, or ${powerUps.orb.gun()}
spawns 8-12 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` }, maxCount: 1, count: 0, @@ -4281,28 +4256,27 @@ const tech = { { name: "options exchange", link: `options exchange`, - description: `clicking cancel for a field, tech, or gun
has a 85% chance to randomize choices`, + description: `clicking cancel for ${powerUps.orb.field()}, ${powerUps.orb.tech()}, or ${powerUps.orb.gun()}
will randomize with 3x choices, once a level`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isSuperDeterminism //&& (tech.isCancelRerolls || tech.isCancelDuplication) + return !tech.isSuperDeterminism //&& (tech.isCancelRerolls || tech.isCancelDuplication || tech.isCancelCouple) }, - requires: "not superdeterminism", //futures exchange, commodities exchange, + requires: "not superdeterminism", //residual dipolar coupling, commodities exchange, futures exchange, effect() { tech.isCancelTech = true + tech.cancelTechCount = 0 }, remove() { tech.isCancelTech = false + tech.cancelTechCount = 0 } }, { name: "futures exchange", - description: "clicking cancel for a field, tech, or gun
gives +5% power up duplication chance", - // descriptionFunction() { - // return `clicking × to cancel a field, tech, or gun
gives +${4.9 - 0.15*simulation.difficultyMode}% power up duplication chance` - // }, + description: `clicking cancel for ${powerUps.orb.field()}, ${powerUps.orb.tech()}, or ${powerUps.orb.gun()}
gives +5% power up duplication chance`, maxCount: 1, count: 0, frequency: 1, @@ -4317,25 +4291,25 @@ const tech = { }, remove() { tech.isCancelDuplication = false - powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "replication", - description: "+10% chance to duplicate spawned power ups
+22% JUNK to tech pool", + description: "+10% chance to duplicate spawned power ups
+15% JUNK choices", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return tech.duplicationChance() < 1. + return tech.duplicationChance() < 1 && tech.junkChance < 1 }, requires: "below 100% duplication chance", effect() { tech.duplicateChance += 0.1 powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.1); - this.refundAmount += tech.addJunkTechToPool(0.22) + this.refundAmount += tech.addJunkTechToPool(0.15) }, refundAmount: 0, remove() { @@ -4347,27 +4321,6 @@ const tech = { } } }, - { - name: "stimulated emission", - description: "+19% chance to duplicate spawned power ups,
but after a collision eject 1 tech", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() < 1 - }, - requires: "below 1% duplication chance", - effect() { - tech.isStimulatedEmission = true - powerUps.setPowerUpMode(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.15); - }, - remove() { - tech.isStimulatedEmission = false - powerUps.setPowerUpMode(); //needed after adjusting duplication chance - } - }, { name: "metastability", description: "+13% chance to duplicate spawned power ups
duplicates explode with a 4 second half-life", @@ -4386,20 +4339,22 @@ const tech = { }, remove() { tech.isPowerUpsVanish = false - powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } } }, { name: "correlated damage", - description: "duplication increases damage", + descriptionFunction() { + return `duplication increases damage
(${(1 + Math.min(1, tech.duplicationChance())).toFixed(2)}x)` + }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return tech.duplicationChance() > 0.15 + return tech.duplicationChance() > 0.03 }, - requires: "duplication chance > 15%", + requires: "duplication chance > 3%", effect() { tech.isDupDamage = true; }, @@ -4415,41 +4370,44 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return tech.duplicationChance() > 0// && !tech.isResearchBoss + return tech.duplicationChance() > 0 }, requires: "some duplication chance", effect() { tech.isDuplicateMobs = true; powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.08); }, remove() { tech.isDuplicateMobs = false; + if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { - name: "apomixis", - description: `when you reach 100% duplication
spawn 11 bosses with 100% more durability`, + name: "stimulated emission", + description: `+20% chance to duplicate spawned power ups,
collisions eject a random ${powerUps.orb.tech()}`, maxCount: 1, count: 0, - frequency: 3, - frequencyDefault: 3, - isNonRefundable: true, + frequency: 1, + frequencyDefault: 1, allowed() { - return tech.duplicationChance() > 0.5 + return tech.duplicationChance() < 1 }, - requires: "duplication chance above 50%", + requires: "below 1% duplication chance", effect() { - tech.is100Duplicate = true; - tech.maxDuplicationEvent() + tech.isStimulatedEmission = true + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.20); }, remove() { - tech.is100Duplicate = false; + tech.isStimulatedEmission = false + if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "strange attractor", descriptionFunction() { - return `+7% damage
removing this increases duplication by +11%` + return `1.1x damage
removing this increases duplication by +11%` }, maxCount: 1, count: 0, @@ -4460,14 +4418,16 @@ const tech = { return true }, requires: "", - damage: 1.07, + damage: 1.1, effect() { tech.damage *= this.damage }, + isRemoveBenefit: true, remove() { if (this.count > 0 && m.alive) { tech.duplication += 0.11 powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); tech.damage /= this.damage this.frequency = 0 } @@ -4475,8 +4435,7 @@ const tech = { }, { name: "strange loop", - // description: `+11% damage
removing this doubles it's damage if you take it again`, - description: `+9% damage
removing this gives strange attractor and null hypothesis`, + description: `1.1x damage
removing this gives a random ${powerUps.orb.tech()} with remove`, maxCount: 1, count: 0, frequency: 1, @@ -4486,24 +4445,28 @@ const tech = { return true }, requires: "", - damage: 1.09, + damage: 1.1, effect() { tech.damage *= this.damage }, + isRemoveBenefit: true, remove() { if (this.count > 0 && m.alive) { tech.damage /= this.damage this.frequency = 0 + requestAnimationFrame(() => { - tech.giveTech("null hypothesis") - tech.giveTech("strange attractor") + const options = [] + for (let i = 0, len = tech.tech.length; i < len; i++) if (tech.tech[i].isRemoveBenefit && tech.tech[i].count === 0) options.push(i) + const index = options[Math.floor(Math.random() * options.length)] + tech.giveTech(tech.tech[index].name) }); } } }, { name: "null hypothesis", - description: `+8% damage
removing this spawns ${powerUps.orb.research(15)}`, + description: `1.1x damage
removing this spawns ${powerUps.orb.research(15)}`, maxCount: 1, count: 0, frequency: 1, @@ -4513,14 +4476,15 @@ const tech = { return true }, requires: "", - damage: 1.08, + damage: 1.1, effect() { tech.damage *= this.damage }, + isRemoveBenefit: true, remove() { if (this.count > 0 && m.alive) { tech.damage /= this.damage - powerUps.spawnDelay("research", 15) + requestAnimationFrame(() => { powerUps.spawnDelay("research", 15) }); this.frequency = 0 } } @@ -4528,7 +4492,7 @@ const tech = { { name: "martingale", descriptionFunction() { - return `+${(100 * this.damage).toFixed(0)}% damage. After removing this there is a 50%
chance to get it back with double its damage
` + return `${(1 + this.damage).toFixed(1)}x damage. removing this has a 50%
chance return with 2x its damage (${(1 + this.damage).toFixed(1)}x→${(1 + 2 * this.damage).toFixed(1)}x)
` }, maxCount: 1, count: 0, @@ -4539,51 +4503,161 @@ const tech = { return true }, requires: "", - damage: 0.11, + damage: 0.1, effect() { tech.damage *= 1 + this.damage }, + isRemoveBenefit: true, + remove() { + if (this.count > 0 && m.alive) { + tech.damage /= 1 + this.damage + if (Math.random() < 0.5) { + this.damage *= 2 + requestAnimationFrame(() => { tech.giveTech("martingale") }); + } + this.frequency = 0 + } + } + }, + { + name: "externality", + descriptionFunction() { + return `1.1x damage
removing this spawns ${this.ammo} ${powerUps.orb.ammo()}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBadRandomOption: true, + allowed() { + return true + }, + requires: "", + damage: 1.1, + ammo: 50, + effect() { + tech.damage *= this.damage + }, + isRemoveBenefit: true, + remove() { + if (this.count > 0 && m.alive) { + tech.damage /= this.damage + this.frequency = 0 + requestAnimationFrame(() => { powerUps.spawnDelay("ammo", this.ammo) }); + } + } + }, + { + name: "deprecated", + scale: 0.08, + descriptionFunction() { + return `after removing this gain
${1 + this.scale}x damage per removed ${powerUps.orb.tech()}(${(1 + this.scale * ((this.frequency === 0 ? 0 : 1) + tech.removeCount)).toFixed(2)}x)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBadRandomOption: true, + allowed() { + return true + }, + requires: "", + damage: 1.1, + effect() { + }, + isRemoveBenefit: true, + remove() { + if (this.count > 0 && m.alive) { + tech.damage *= 1 + this.scale * (1 + tech.removeCount) + this.frequency = 0 + } + } + }, + { + name: "planned obsolescence", + descriptionFunction() { + return `at the start of each level eject your oldest ${powerUps.orb.tech()}
and gain 1.1x damage each time` //(${(tech.isEjectOld).toFixed(2)}x) + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBadRandomOption: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.isEjectOld = true + }, + remove() { + tech.isEjectOld = false + } + }, + { + name: "paradigm shift", + descriptionFunction() { + return `when paused clicking your ${powerUps.orb.tech()} ejects them
costs ${tech.pauseEjectTech.toFixed(1)} ${tech.isEnergyHealth ? "energy" : "health"} (1.3x cost each use)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism && !tech.isNoDraftPause + }, + requires: "not superdeterminism, eternalism", + effect() { + tech.isPauseEjectTech = true; + }, remove() { - if (this.count > 0 && m.alive) { - tech.damage /= 1 + this.damage - if (Math.random() < 0.5) { - this.damage *= 2 - requestAnimationFrame(() => { tech.giveTech("chaos theory") }); - } - this.frequency = 0 - } + tech.isPauseEjectTech = false; + } }, { name: "Born rule", - description: "remove all current tech
spawn new tech to replace them", + description: `eject all your ${powerUps.orb.tech()}`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isNonRefundable: true, + isInstant: true, isBadRandomOption: true, allowed() { return (tech.totalCount > 6) }, requires: "more than 6 tech", effect() { - //remove active bullets //to get rid of bots - for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); - bullet = []; - let count = 1 //count tech + // //remove active bullets //to get rid of bots + // for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); + // bullet = []; + // let count = 1 //count tech + // for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups + // if (!tech.tech[i].isInstant) count += tech.tech[i].count + // } + // if (tech.isDeterminism) count -= 4 //remove the bonus tech + // if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech + // const removeCount = tech.removeCount + // tech.setupAllTech(); // remove all tech + // tech.removeCount = removeCount + // if (simulation.isCheating) tech.setCheating(); + // lore.techCount = 0; + // // tech.addLoreTechToPool(); + // for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups + // //have state is checked in m.death() + + + let count = 0 //count tech for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups - if (!tech.tech[i].isNonRefundable) count += tech.tech[i].count + if (!tech.tech[i].isInstant && tech.tech[i].count) { + count += tech.tech[i].count + tech.removeTech(i) + // powerUps.ejectTech(index) + } } - if (tech.isDeterminism) count -= 4 //remove the bonus tech - if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech - - tech.setupAllTech(); // remove all tech - if (simulation.isCheating) tech.setCheating(); - lore.techCount = 0; - // tech.addLoreTechToPool(); - for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups - //have state is checked in m.death() + powerUps.spawnDelay("tech", count); + // for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups }, remove() { } }, @@ -4591,44 +4665,44 @@ const tech = { { name: "Occams razor", descriptionFunction() { - return `randomly remove half your tech
for each removed +${this.damagePerRemoved * 100}% damage (~${(this.count === 0) ? this.damagePerRemoved * 50 * tech.totalCount : this.damage * 100}%)` + return `randomly remove half your ${powerUps.orb.tech()}
for each removed ${(1 + this.damagePerRemoved).toFixed(2)}x damage (~${((this.count === 0) ? 1 + this.damagePerRemoved * 0.5 * tech.totalCount : this.damage).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isNonRefundable: true, + isInstant: true, isBadRandomOption: true, allowed() { return (tech.totalCount > 6) }, requires: "more than 6 tech", - // removePercent: 0.5, damagePerRemoved: 0.5, damage: null, effect() { let pool = [] for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups - if (tech.tech[i].count && !tech.tech[i].isNonRefundable && !tech.tech[i].isFromAppliedScience) pool.push(i) + if (tech.tech[i].count && !tech.tech[i].isInstant) pool.push(i) } pool = shuffle(pool); //shuffles order of maps let removeCount = 0 - for (let i = 0, len = pool.length * this.damagePerRemoved; i < len; i++) removeCount += tech.removeTech(pool[i]) + for (let i = 0, len = pool.length * 0.5; i < len; i++) removeCount += tech.removeTech(pool[i]) this.damage = this.damagePerRemoved * removeCount tech.damage *= (1 + this.damage) + simulation.inGameConsole(`tech.damage *= ${(1 + this.damage).toFixed(2)} //from Occam's razor`); }, remove() { - if (this.count) tech.damage /= (1 + this.damage) + if (this.count && m.alive) tech.damage /= (1 + this.damage) } }, { name: "exchange symmetry", - description: "remove 1 random tech
spawn 2 new guns", + description: `remove a random ${powerUps.orb.tech()}
spawn ${powerUps.orb.gun()}${powerUps.orb.gun()}`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isNonRefundable: true, + isInstant: true, isBadRandomOption: true, allowed() { return (tech.totalCount > 3) && !tech.isSuperDeterminism @@ -4637,30 +4711,25 @@ const tech = { effect() { const have = [] //find which tech you have for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + if (tech.tech[i].count > 0 && !tech.tech[i].isInstant) have.push(i) } const choose = have[Math.floor(Math.random() * have.length)] - simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`, 360) for (let i = 0; i < tech.tech[choose].count; i++) { powerUps.spawn(m.pos.x, m.pos.y, "gun"); } powerUps.spawn(m.pos.x, m.pos.y, "gun"); - // powerUps.spawn(m.pos.x, m.pos.y, "gun"); - tech.tech[choose].remove(); // remove a random tech form the list of tech you have - // tech.tech[choose].count = 0; - tech.tech[choose].isLost = true - simulation.updateTechHUD(); + tech.removeTech(choose) }, remove() { } }, { name: "Monte Carlo method", - description: "remove 1 random tech
spawn 2 tech", + description: `remove a random ${powerUps.orb.tech()}
spawn ${powerUps.orb.tech()}${powerUps.orb.tech()}`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isNonRefundable: true, + isInstant: true, isBadRandomOption: true, allowed() { return (tech.totalCount > 3) && tech.duplicationChance() > 0 && !tech.isSuperDeterminism @@ -4672,50 +4741,6 @@ const tech = { }, remove() { } }, - // { - // name: "backward induction", - // descriptionFunction() { - // if (build.isExperimentSelection || powerUps.tech.choiceLog.length < 10) return `use ${powerUps.orb.research(2)} to choose all the unchosen tech
from your last selection` - - // text = `` - // let num = 3 - // if (tech.extraChoices) num = 5 - // if (tech.isDeterminism) num = 1 - // for (let i = 0; i < num; i++) { - // const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] - // if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { - // text += `${tech.tech[index].name}, ` - // } - // } - // text = text.slice(0, -2); - // return `use ${powerUps.orb.research(2)}to choose the unchosen
tech from your previous selection:
${text}` - // }, - // // description: `use ${powerUps.orb.research(2)}to choose all the unchosen
tech from your previous tech selection`, - // maxCount: 1, - // count: 0, - // frequency: 100, - // frequencyDefault: 100, - // isNonRefundable: true, - // isBadRandomOption: true, - // allowed() { - // return powerUps.tech.choiceLog.length > 10 && !tech.isDeterminism && powerUps.research.count > 1 - // }, - // requires: "NOT EXPERIMENT MODE, rejected an option in the last tech selection, at least 2 research, not determinism", - // effect: () => { - // powerUps.research.changeRerolls(-2) - // let num = 3 - // if (tech.extraChoices) num = 5 - // if (tech.isDeterminism) num = 1 - // for (let i = 0; i < num; i++) { - // const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] - // if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { - // tech.giveTech(index) - // simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //backward induction`); - // } - // } - // }, - // remove() {} - // }, //************************************************** //************************************************** gun //************************************************** tech @@ -4741,7 +4766,7 @@ const tech = { }, { name: "nanowires", - description: `needles tunnel through blocks and map
+20% needle damage`, + description: `needles tunnel through blocks and map
1.2x needle damage`, isGunTech: true, maxCount: 1, count: 0, @@ -4793,22 +4818,23 @@ const tech = { tech.isNeedles = true for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "nail gun") { - b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 3); - b.guns[i].ammoPack = Math.ceil(b.guns[i].defaultAmmoPack / 3); + b.guns[i].ammo = Math.ceil(b.guns[i].ammo / this.ammoScale); + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack / this.ammoScale; b.guns[i].chooseFireMethod() simulation.updateGunHUD(); break } } }, + ammoScale: 3, remove() { if (tech.isNeedles) { tech.isNeedles = false for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "nail gun") { b.guns[i].chooseFireMethod() - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 3); - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * this.ammoScale); + b.guns[i].ammoPack = b.guns[i].ammo * this.ammoScale; simulation.updateGunHUD(); break } @@ -4818,7 +4844,7 @@ const tech = { }, { name: "stress concentration", - description: "mobs below 50% durability die after you shoot
them near their center with needles or rivets", + description: "mobs below half durability die after you shoot
them near their center with needles or rivets", isGunTech: true, maxCount: 1, count: 0, @@ -4871,7 +4897,7 @@ const tech = { }, { name: "pneumatic actuator", - description: "nail gun takes no time to ramp up
to its fastest fire rate", + description: "nail gun takes no time to ramp up
to its fastest fire rate", isGunTech: true, maxCount: 1, count: 0, @@ -4899,7 +4925,7 @@ const tech = { { name: "ice crystal nucleation", link: `ice crystal nucleation`, - description: "nail gun uses energy to condense
unlimited freezing ice shards", + description: "nail gun uses energy instead of ammo
to condense freezing ice nails", isGunTech: true, maxCount: 1, count: 0, @@ -4912,7 +4938,7 @@ const tech = { effect() { tech.isIceCrystals = true; b.guns[0].ammoPack = Infinity - b.guns[0].recordedAmmo = b.guns[i].ammo + b.guns[0].recordedAmmo = b.guns[0].ammo b.guns[0].ammo = Infinity simulation.updateGunHUD(); }, @@ -4922,7 +4948,6 @@ const tech = { b.guns[0].ammoPack = b.guns[0].defaultAmmoPack; if (b.guns[0].recordedAmmo) b.guns[0].ammo = b.guns[0].recordedAmmo simulation.updateGunHUD(); - if (this.count) requestAnimationFrame(() => { simulation.updateGunHUD(); }); } tech.isIceCrystals = false; @@ -4931,7 +4956,7 @@ const tech = { }, { name: "rotary cannon", - description: "nail gun has increased muzzle speed,
maximum fire rate, accuracy, and recoil", + description: `nail gun has increased muzzle speed,
maximum fire rate, accuracy, and recoil`, isGunTech: true, maxCount: 1, count: 0, @@ -4958,7 +4983,7 @@ const tech = { }, { name: "gauge", - description: `rivets, needles, super balls, and nails
have +30% mass and physical damage`, + description: `rivets, needles, super balls, and nails
have 1.3x mass and physical damage`, isGunTech: true, maxCount: 9, count: 0, @@ -4977,7 +5002,7 @@ const tech = { }, { name: "supercritical fission", - description: "if nails, needles, or rivets strike mobs
near their center they can explode", + description: "if nails, needles, or rivets strike mobs
near their center they trigger an explosion", isGunTech: true, maxCount: 1, count: 0, @@ -4997,7 +5022,7 @@ const tech = { { name: "irradiated nails", link: `irradiated nails`, - description: "nails, needles, and rivets are radioactive
+90% radioactive damage over 3 seconds", + description: "nails, needles, and rivets are radioactive
2x radioactive damage over 3 seconds", isGunTech: true, maxCount: 1, count: 0, @@ -5017,7 +5042,7 @@ const tech = { { name: "6s half-life", link: `6s half-life`, - description: "nails, needles, rivets are made of plutonium-238
radioactive damage lasts +3 seconds", + description: "nails, needles, rivets are made of plutonium-238
radioactive damage lasts +3 seconds", isGunTech: true, maxCount: 1, count: 0, @@ -5037,7 +5062,7 @@ const tech = { { name: "1s half-life", link: `1s half-life`, - description: "nails, needles, rivets are made of lithium-8
+300% radioactive damage for 1 second
", + description: "nails, needles, rivets are made of lithium-8
4x radioactive damage for 1 second
", isGunTech: true, maxCount: 1, count: 0, @@ -5057,7 +5082,7 @@ const tech = { { name: "spin-statistics", link: `spin-statistics`, - description: "after firing the shotgun you are invulnerable
shotgun has 50% fewer shots", + description: `after firing the shotgun you are invulnerable
shotgun has 0.7x bullets per ${powerUps.orb.ammo(1)}`, isGunTech: true, maxCount: 1, count: 0, @@ -5073,30 +5098,30 @@ const tech = { //cut current ammo by 1/2 for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "shotgun") { - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.5); - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.5 + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.7); + b.guns[i].ammoPack *= 0.7 break; } } simulation.updateGunHUD(); }, remove() { - if (tech.isShotgunImmune) { - tech.isShotgunImmune = false; + tech.isShotgunImmune = false; + if (this.count > 0) { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "shotgun") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 2); + b.guns[i].ammoPack /= 0.7 + b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 0.7); + simulation.updateGunHUD(); break; } } - simulation.updateGunHUD(); } } }, { name: "Newtons 3rd law", - description: "+66% shotgun fire rate and recoil", + description: "1.7x shotgun fire rate and recoil", isGunTech: true, maxCount: 1, count: 0, @@ -5116,7 +5141,7 @@ const tech = { { name: "Noether violation", link: `Noether violation`, - description: "+50% shotgun damage
shotgun recoil is reversed", + description: "1.5x shotgun damage
shotgun recoil is reversed", isGunTech: true, maxCount: 1, count: 0, @@ -5135,7 +5160,7 @@ const tech = { }, { name: "repeater", - description: "shotgun immediately fires again for no ammo
-50% shotgun fire rate", + description: "shotgun immediately fires again for no ammo
reduced 0.5x shotgun fire rate", isGunTech: true, maxCount: 9, count: 0, @@ -5195,7 +5220,7 @@ const tech = { { name: "ice-shot", link: `ice-shot`, - description: "shotgun grows freezing ice IX crystals", + description: "shotgun condenses ice IX crystals
that freeze mobs", isGunTech: true, maxCount: 1, count: 0, @@ -5221,7 +5246,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) + return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.isNeedleIce || (m.coupling && (m.fieldMode === 2 || m.fieldMode === 0)) }, requires: "a freeze effect", effect() { @@ -5240,7 +5265,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) + return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.isNeedleIce || (m.coupling && (m.fieldMode === 2 || m.fieldMode === 0)) }, requires: "a freeze effect", effect() { @@ -5259,7 +5284,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return (tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0))) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob + return (tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.isNeedleIce || (m.coupling && (m.fieldMode === 2 || m.fieldMode === 0))) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob }, requires: "a localized freeze effect, no other mob death tech", effect() { @@ -5278,7 +5303,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) || tech.iceIXOnDeath || tech.isIceShot + return (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isNeedleIce || (m.coupling && (m.fieldMode === 2 || m.fieldMode === 0)) || tech.iceIXOnDeath || tech.isIceShot }, requires: "ice IX", effect() { @@ -5290,14 +5315,14 @@ const tech = { }, { name: "superfluidity", - description: "freeze effects are applied
to a small area around the target", + description: "freeze effects are applied
to a small area around the target", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.relayIce || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) || tech.iceIXOnDeath || tech.isIceShot + return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isNeedleIce || (m.coupling && (m.fieldMode === 2 || m.fieldMode === 0)) || tech.iceIXOnDeath || tech.isIceShot }, requires: "a localized freeze effect", effect() { @@ -5307,6 +5332,33 @@ const tech = { tech.isAoESlow = false } }, + { + name: "triple point", + descriptionFunction() { + return `+5 second freeze duration` + }, + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + // allowed() { + // return (m.fieldMode === 2 && m.coupling > 0) || (tech.haveGunCheck("shotgun") && tech.isIceShot) + // }, + // requires: "perfect diamagnetism", + allowed() { + return (tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.isNeedleIce || (m.coupling && (m.fieldMode === 2 || m.fieldMode === 0))) + }, + requires: "a localized freeze effect", + effect() { + tech.iceIXFreezeTime += 5 * 60 + // powerUps.spawnDelay("coupling", 10) + }, + remove() { + tech.iceIXFreezeTime = 150 + // if (this.count) m.couplingChange(-10 * this.count) + } + }, { name: "incendiary ammunition", description: "shotgun, rivets, super balls, and drones
are loaded with explosives", @@ -5328,7 +5380,7 @@ const tech = { }, { name: "rebound", - description: `after they collide with a mob, super balls
gain speed, duration, and +33% damage`, + description: `after they collide with a mob, super balls
gain speed, duration, and 1.3x damage`, isGunTech: true, maxCount: 1, count: 0, @@ -5347,7 +5399,7 @@ const tech = { }, { name: "Zectron", - description: `+90% super ball density and damage, but
after colliding with super balls -25% energy`, + description: `2x super ball damage, but
after you collide with super balls they stop`, isGunTech: true, maxCount: 1, count: 0, @@ -5385,7 +5437,7 @@ const tech = { }, { name: "autocannon", - description: "fire +1 extra super ball
balls are quickly released in same direction", + description: "fire +2 super balls in a line
1.4x super ball velocity and gravity", isGunTech: true, maxCount: 1, count: 0, @@ -5458,7 +5510,7 @@ const tech = { }, { name: "phase velocity", - description: "wave particles propagate faster as solids
+40% wave damage", + description: "wave particles propagate faster as solids
1.4x wave damage", isGunTech: true, maxCount: 1, count: 0, @@ -5477,7 +5529,7 @@ const tech = { }, { name: "amplitude", - description: "+37% wave damage
+37% wave particle amplitude", + description: "1.4x wave damage
1.4x wave bullet amplitude", isGunTech: true, maxCount: 3, count: 0, @@ -5489,16 +5541,16 @@ const tech = { requires: "wave", effect() { tech.waveFrequency *= 0.66 - tech.wavePacketDamage *= 1.37 + tech.wavePacketDamage *= 1.4 }, remove() { - tech.waveFrequency = 0.2 + tech.waveFrequency = 0.2 //adjust this to make the waves much larger tech.wavePacketDamage = 1 } }, { name: "propagation", - description: "–25% wave packet propagation speed
+41% wave damage", + description: `0.75x wave propagation speed
1.4x wave damage`, isGunTech: true, maxCount: 9, count: 0, @@ -5510,16 +5562,16 @@ const tech = { requires: "wave", effect() { tech.waveBeamSpeed *= 0.75; - tech.waveBeamDamage += 0.27 * 0.41 //this sets base wave damage + tech.waveBeamDamage += 0.3 * 0.4 //this sets base wave damage }, remove() { tech.waveBeamSpeed = 11; - tech.waveBeamDamage = 0.27 //this sets base wave damage + tech.waveBeamDamage = 0.3 //this sets base wave damage } }, { name: "bound state", - description: "wave packets reflect backwards 2 times
–33% range", + description: "wave packets reflect backwards 2 times
0.7x wave range", isGunTech: true, maxCount: 9, count: 0, @@ -5538,7 +5590,7 @@ const tech = { }, { name: "frequency", - description: `wave has unlimited ammo
-25% wave damage`, + description: `wave has unlimited ammo
0.75x wave damage`, isGunTech: true, maxCount: 1, count: 0, @@ -5579,7 +5631,7 @@ const tech = { effect() { tech.isLongitudinal = true; b.guns[3].chooseFireMethod() - b.guns[3].ammoPack = b.guns[3].defaultAmmoPack / this.ammoScale + b.guns[3].ammoPack /= this.ammoScale if (tech.isInfiniteWaveAmmo) { b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo / this.ammoScale); //used with low frequency } else { @@ -5588,10 +5640,10 @@ const tech = { simulation.updateGunHUD(); }, remove() { - if (tech.isLongitudinal) { - tech.isLongitudinal = false; + tech.isLongitudinal = false; + if (this.count > 0) { b.guns[3].chooseFireMethod() - b.guns[3].ammoPack = b.guns[3].defaultAmmoPack + b.guns[3].ammoPack *= this.ammoScale if (tech.isInfiniteWaveAmmo) { b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo * this.ammoScale); //used with low frequency } else { @@ -5599,12 +5651,11 @@ const tech = { } simulation.updateGunHUD(); } - tech.isLongitudinal = false; } }, { name: "isotropic", - description: "waves expand in all directions
–40% range and +50% damage", + description: "waves expand in all directions
0.6x range and 1.5x damage", isGunTech: true, maxCount: 1, count: 0, @@ -5661,9 +5712,44 @@ const tech = { tech.isPhononWave = false } }, + { + name: "missile-bot", + link: `missile-bot`, + description: `use ${powerUps.orb.research(1)}to trade your missile ${powerUps.orb.gun()}
for a bot that fires missiles`, + // isGunTech: true, + isRemoveGun: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + isInstant: true, + allowed() { + return tech.haveGunCheck("missiles", false) && tech.missileFireCD === 45 && (build.isExperimentSelection || powerUps.research.count > 0) + }, + requires: "missiles, not launch system", + effect() { + tech.missileBotCount++; + b.missileBot(); + if (tech.haveGunCheck("missiles", false)) b.removeGun("missiles") //remove your last gun + for (let i = 0; i < 1; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + // if (this.count) { + // tech.missileBotCount = 0; + // b.clearPermanentBots(); + // b.respawnBots(); + // if (!tech.haveGunCheck("missiles", false)) b.giveGuns("missiles") + // powerUps.research.changeRerolls(1) + // } + } + }, { name: "cruise missile", - description: "+100% missile explosive damage, radius
–50% missile speed", + description: "2x missile explosive damage, radius
0.5x missile speed", isGunTech: true, maxCount: 1, count: 0, @@ -5682,7 +5768,7 @@ const tech = { }, { name: "ICBM", - description: "+75% missile explosive damage, radius
–50% missile speed", + description: "1.75x missile explosive damage, radius
0.5x missile speed", isGunTech: true, maxCount: 1, count: 0, @@ -5701,7 +5787,7 @@ const tech = { }, { name: "launch system", - description: `+500% missile gun fire rate
+20% missile ammo per ${powerUps.orb.ammo(1)}`, + description: `5x missile fire rate
1.2x missile ammo per ${powerUps.orb.ammo(1)}`, isGunTech: true, maxCount: 1, count: 0, @@ -5724,11 +5810,11 @@ const tech = { } }, remove() { - if (tech.missileFireCD !== 45) { - tech.missileFireCD = 45; + tech.missileFireCD = 45; + if (this.count > 0) { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "missiles") { - b.guns[i].ammoPack = 5; + b.guns[i].ammoPack /= this.ammoBonus; b.guns[i].ammo = Math.ceil(b.guns[i].ammo / this.ammoBonus); simulation.updateGunHUD(); break @@ -5737,43 +5823,9 @@ const tech = { } } }, - { - name: "missile-bot", - link: `missile-bot`, - description: `use ${powerUps.orb.research(1)}to trade your missile gun
for a bot that fires missiles`, - isGunTech: true, - isRemoveGun: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { - return tech.haveGunCheck("missiles", false) && tech.missileFireCD === 45 && (build.isExperimentSelection || powerUps.research.count > 0) - }, - requires: "missiles, not launch system", - effect() { - tech.missileBotCount++; - b.missileBot(); - if (tech.haveGunCheck("missiles", false)) b.removeGun("missiles") //remove your last gun - for (let i = 0; i < 1; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - if (this.count) { - tech.missileBotCount = 0; - b.clearPermanentBots(); - b.respawnBots(); - if (!tech.haveGunCheck("missiles", false)) b.giveGuns("missiles") - powerUps.research.changeRerolls(1) - } - } - }, { name: "iridium-192", - description: "explosions release gamma radiation
+100% explosion damage over 4 seconds", + description: "explosions release gamma radiation
2x explosion damage over 4 seconds", isGunTech: true, maxCount: 1, count: 0, @@ -5811,7 +5863,7 @@ const tech = { }, { name: "ammonium nitrate", - description: "+24% explosive damage, radius", + description: "1.25x explosive damage, radius", isGunTech: true, maxCount: 9, count: 0, @@ -5822,7 +5874,7 @@ const tech = { }, requires: "an explosive damage source, not iridium-192", effect() { - tech.explosiveRadius += 0.24; + tech.explosiveRadius += 0.25; }, remove() { tech.explosiveRadius = 1; @@ -5830,7 +5882,7 @@ const tech = { }, { name: "nitroglycerin", - description: "+66% explosive damage
–33% explosive radius", + description: "1.7x explosive damage
0.7x smaller explosive radius", isGunTech: true, maxCount: 1, count: 0, @@ -5849,7 +5901,7 @@ const tech = { }, { name: "acetone peroxide", - description: "+70% explosive radius
–33% explosive defense", + description: "1.7x explosive radius
1.4x explosive damage taken", isGunTech: true, maxCount: 1, count: 0, @@ -5909,29 +5961,9 @@ const tech = { if (this.count > 0) powerUps.research.changeRerolls(3) } }, - // { - // name: "electric armor", - // // description: "explosions do no defense
while your energy is above 98%", - // description: "instead of causing health loss, explosions
drain 12 energy and have more knockback", - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return !tech.isSmartRadius && !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() - // }, - // requires: "an explosive damage source, not iridium-192", - // effect() { - // tech.isImmuneExplosion = true; - // }, - // remove() { - // tech.isImmuneExplosion = false; - // } - // }, { name: "MIRV", - description: "fire +1 missile or grenade per shot
–12% explosion damage and radius", + description: "fire +1 missile or grenade per shot
0.88x explosion damage and radius", isGunTech: true, maxCount: 9, count: 0, @@ -5994,7 +6026,7 @@ const tech = { }, { name: "chain reaction", - description: "+33% grenade radius and damage
blocks caught in explosions also explode", + description: "1.3x grenade radius and damage
blocks caught in explosions also explode", isGunTech: true, maxCount: 1, count: 0, @@ -6013,7 +6045,7 @@ const tech = { }, { name: "flame test", - description: "after grenades detonate they release
a colorful cluster of small explosions", + description: "after grenades detonate they trigger
a colorful cluster of small explosions", isGunTech: true, maxCount: 1, count: 0, @@ -6032,7 +6064,7 @@ const tech = { }, { name: "pyrotechnics", - description: "after grenades detonate they release
a colorful circle of explosions", + description: "after grenades detonate they trigger
a colorful circle of explosions", isGunTech: true, maxCount: 1, count: 0, @@ -6051,7 +6083,7 @@ const tech = { }, { name: "fireworks", - description: "after grenades detonate they release
colorful petals of explosions", + description: "after grenades detonate they trigger
colorful petals of explosions", isGunTech: true, maxCount: 1, count: 0, @@ -6091,7 +6123,7 @@ const tech = { }, { name: "vacuum permittivity", - description: "+20% radioactive range
objects in range of the bomb are slowed", + description: "1.2x radioactive range
objects in range of the bomb are slowed", isGunTech: true, maxCount: 1, count: 0, @@ -6129,7 +6161,7 @@ const tech = { }, { name: "nuclear transmutation", - description: "+47% radiation damage
nail, drone, neutron bomb, iridium, cosmic string, deflect", + description: "1.5x radiation damage
nail, drone, neutron bomb, iridium, cosmic string, deflect", isGunTech: true, maxCount: 9, count: 0, @@ -6140,7 +6172,7 @@ const tech = { }, requires: "radiation damage source", effect() { - tech.radioactiveDamage += 1.47 + tech.radioactiveDamage += 1.5 }, remove() { tech.radioactiveDamage = 1 @@ -6149,7 +6181,7 @@ const tech = { { name: "water shielding", link: `water shielding`, - description: "radioactive effects on you are reduced by 75%
neutron bomb, drones, explosions, slime", + description: "reduce radioactive effects on you by 0.8x
neutron bomb, drones, explosions, slime", isGunTech: true, maxCount: 1, count: 0, @@ -6168,7 +6200,7 @@ const tech = { }, { name: "ricochet", - description: "after nails hit a mob they rebound towards
a new mob with +180% damage per bounce", + description: "after nails hit a mob they rebound towards
a new mob with 2.8x damage per bounce", isGunTech: true, maxCount: 1, count: 0, @@ -6189,20 +6221,20 @@ const tech = { }, { name: "booby trap", - description: "50% chance to drop a mine from power ups
+30% JUNK to tech pool", + description: "50% chance to drop a mine from power ups
+15% JUNK choices", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("mine") + return tech.haveGunCheck("mine") && tech.junkChance < 1 }, requires: "mines", effect() { tech.isMineDrop = true; if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0) - this.refundAmount += tech.addJunkTechToPool(0.30) + this.refundAmount += tech.addJunkTechToPool(0.15) }, refundAmount: 0, remove() { @@ -6235,7 +6267,7 @@ const tech = { { name: "blast ball", descriptionFunction() { - return `instead of nails mines fire bouncy balls` + return `mines fire bouncy balls instead of nails` }, isGunTech: true, maxCount: 1, @@ -6276,7 +6308,7 @@ const tech = { { name: "sentry", descriptionFunction() { - return `mines fire one ${b.guns[10].nameString()} at a time
mines fire 50% more ${b.guns[10].nameString('s')}` + return `mines fire one ${b.guns[10].nameString()} at a time
mines fire 1.5x more ${b.guns[10].nameString('s')}` }, isGunTech: true, maxCount: 1, @@ -6297,7 +6329,7 @@ const tech = { { name: "extended magazine", descriptionFunction() { - return `sentry mines fire 50% more ${b.guns[10].nameString('s')}` + return `sentry mines fire 1.5x more ${b.guns[10].nameString('s')}` }, isGunTech: true, maxCount: 9, @@ -6318,7 +6350,7 @@ const tech = { { name: "mycelial fragmentation", link: `mycelial fragmentation`, - description: "during their growth phase
+70% sporangium discharge", + description: "during their growth phase
1.7x sporangium discharge", isGunTech: true, maxCount: 1, count: 0, @@ -6359,7 +6391,7 @@ const tech = { }, { name: "colony", - description: "+50% sporangium discharge
40% chance to discharge something different", + description: "1.6x sporangium discharge
33% chance to discharge something different", link: `colony`, isGunTech: true, maxCount: 1, @@ -6380,7 +6412,7 @@ const tech = { { name: "cryodesiccation", descriptionFunction() { - return `+25% sporangium discharge
${b.guns[6].nameString('s')} freeze mobs for 1.5 second` + return `1.25x sporangium discharge
${b.guns[6].nameString('s')} freeze mobs for 1.5 second` }, // description: "+25% sporangium discharge
spores freeze mobs for 1.5 second", isGunTech: true, @@ -6402,9 +6434,8 @@ const tech = { { name: "flagella", descriptionFunction() { - return `+50% ${b.guns[6].nameString()} acceleration
if they can't find a target ${b.guns[6].nameString('s')} follow you` + return `2x ${b.guns[6].nameString()} acceleration
if they can't find a target ${b.guns[6].nameString('s')} follow you` }, - // description: "+50% spore acceleration
if they can't find a target spores follow you", isGunTech: true, maxCount: 1, count: 0, @@ -6421,38 +6452,11 @@ const tech = { tech.isSporeFollow = false } }, - // { - // name: "junk DNA", - // //increase damage by 10% for each JUNK tech percent in the tech pool, remove all JUNK tech, - // descriptionFunction() { return `+50% ${b.guns[6].nameString()} damage
+15% JUNK to tech pool` }, - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // allowed() { - // return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea - // }, - // requires: "spores", - // effect() { - // tech.isSporeWorm = true - // this.refundAmount += tech.addJunkTechToPool(0.15) - // }, - // refundAmount: 0, - // remove() { - // tech.isSporeWorm = false - // if (this.count > 0 && this.refundAmount > 0) { - // tech.removeJunkTechFromPool(this.refundAmount) - // this.refundAmount = 0 - // } - // } - // }, { name: "mutualism", descriptionFunction() { - return `+200% ${b.guns[6].nameString()} damage
${b.guns[6].nameString('s')} borrow 1 health until they die` + return `3x ${b.guns[6].nameString()} damage
${b.guns[6].nameString('s')} borrow 1 health until they die` }, - // description: `+150% ${b.guns[6].name()} damage
spores borrow 0.5 health until they die`, isGunTech: true, maxCount: 1, count: 0, @@ -6529,7 +6533,7 @@ const tech = { }, { name: "K-selection", - description: "+37% worm and flea damage", + description: "1.37x worm and flea damage", isGunTech: true, maxCount: 3, count: 0, @@ -6567,12 +6571,58 @@ const tech = { tech.isDronesTravel = false } }, + { + name: "fault tolerance", + description: `use ${powerUps.orb.research(2)}to trade your drone ${powerUps.orb.gun()}
for 5 drones that last forever`, + // isGunTech: true, + isRemoveGun: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isInstant: true, + allowed() { + return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.bulletsLastLonger === 1 && !tech.isDronesTravel && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "drones, not drone repair, anti-shear topology, autonomous navigation", + effect() { + const num = 5 + tech.isForeverDrones += num + if (tech.haveGunCheck("drones", false)) b.removeGun("drones") + //spawn drones + if (tech.isDroneRadioactive) { + for (let i = 0; i < num * 0.25; i++) { + b.droneRadioactive({ + x: m.pos.x + 30 * (Math.random() - 0.5), + y: m.pos.y + 30 * (Math.random() - 0.5) + }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } else { + for (let i = 0; i < num; i++) { + b.drone({ + x: m.pos.x + 30 * (Math.random() - 0.5), + y: m.pos.y + 30 * (Math.random() - 0.5) + }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isForeverDrones = 0 + // if (this.count && !tech.haveGunCheck("drones", false)) b.giveGuns("drones") + // if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, { name: "reduced tolerances", link: `reduced tolerances`, - description: `+66% drones per ${powerUps.orb.ammo()} and energy
–40% drone duration`, + description: `2x drones per ${powerUps.orb.ammo()} and energy
0.6x drone duration`, isGunTech: true, - maxCount: 3, + maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, @@ -6581,20 +6631,19 @@ const tech = { }, requires: "drones, not irradiated drones", effect() { - tech.droneCycleReduction = Math.pow(0.6, 1 + this.count) - tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count) + tech.droneCycleReduction = 0.6 + tech.droneEnergyReduction = 0.3 for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") { - const scale = Math.pow(3, this.count + 1) - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * scale - } + if (b.guns[i].name === "drones") b.guns[i].ammoPack *= 2 } }, remove() { tech.droneCycleReduction = 1 tech.droneEnergyReduction = 1 - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + if (this.count > 0) { + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") b.guns[i].ammoPack /= 2 + } } } }, @@ -6619,15 +6668,14 @@ const tech = { }, { name: "von Neumann probe", //"drone repair", - description: "after a drone expires
it will harvest a nearby block to replicate itself", - // description: "broken drones repair if the drone gun is active
repairing has a 25% chance to use 1 drone", + description: "after a drone expires it will use -4 energy
and a nearby block to replicate itself", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("drones") + return tech.haveGunCheck("drones") || (m.fieldMode === 4 && simulation.molecularMode === 3) }, requires: "drones", effect() { @@ -6639,7 +6687,7 @@ const tech = { }, { name: "brushless motor", - description: "drones rapidly rush towards their target
+33% drone collision damage", + description: "drones rapidly rush towards their target
1.33x drone collision damage", isGunTech: true, maxCount: 1, count: 0, @@ -6658,7 +6706,7 @@ const tech = { }, { name: "axial flux motor", - description: "+66% drones rush frequency
+44% drone collision damage", + description: "1.66x drones rush frequency
1.44x drone collision damage", isGunTech: true, maxCount: 1, count: 0, @@ -6678,7 +6726,7 @@ const tech = { { name: "irradiated drones", link: `irradiated drones`, - description: `the space around drones is irradiated
–75% drones per ${powerUps.orb.ammo()} and energy`, + description: `the space around drones is irradiated
0.25x drones per ${powerUps.orb.ammo()} and energy`, isGunTech: true, maxCount: 1, count: 0, @@ -6692,18 +6740,18 @@ const tech = { tech.isDroneRadioactive = true for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "drones") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.25 + b.guns[i].ammoPack *= 0.25 b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25) simulation.makeGunHUD(); } } }, remove() { - if (tech.isDroneRadioactive) { - tech.isDroneRadioactive = false + tech.isDroneRadioactive = false + if (this.count > 0) { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "drones") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + b.guns[i].ammoPack /= 0.25 b.guns[i].ammo = b.guns[i].ammo * 4 simulation.makeGunHUD(); } @@ -6713,27 +6761,7 @@ const tech = { }, { name: "beta radiation", //"control rod ejection", - description: "–50% drone duration
+100% drone radiation damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isDroneRadioactive - }, - requires: "drones, irradiated drones", - effect() { - tech.droneRadioDamage = 2 - }, - remove() { - tech.droneRadioDamage = 1 - } - }, - { - name: "orthocyclic winding", - link: `orthocyclic winding`, - description: "+66% drone acceleration
+33% radiation damage", + description: "0.5x drone duration
2x drone radiation damage", isGunTech: true, maxCount: 1, count: 0, @@ -6744,92 +6772,30 @@ const tech = { }, requires: "drones, irradiated drones", effect() { - tech.isFastDrones = true - }, - remove() { - tech.isFastDrones = false - } - }, - { - name: "fault tolerance", - description: `use ${powerUps.orb.research(2)}to trade your drone gun
for 5 drones that last forever`, - isGunTech: true, - isRemoveGun: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.bulletsLastLonger === 1 && !tech.isDronesTravel && (build.isExperimentSelection || powerUps.research.count > 1) - }, - requires: "drones, not drone repair, anti-shear topology, autonomous navigation", - effect() { - const num = 5 - tech.isForeverDrones += num - if (tech.haveGunCheck("drones", false)) b.removeGun("drones") - //spawn drones - if (tech.isDroneRadioactive) { - for (let i = 0; i < num * 0.25; i++) { - b.droneRadioactive({ - x: m.pos.x + 30 * (Math.random() - 0.5), - y: m.pos.y + 30 * (Math.random() - 0.5) - }, 5) - bullet[bullet.length - 1].endCycle = Infinity - } - } else { - for (let i = 0; i < num; i++) { - b.drone({ - x: m.pos.x + 30 * (Math.random() - 0.5), - y: m.pos.y + 30 * (Math.random() - 0.5) - }, 5) - bullet[bullet.length - 1].endCycle = Infinity - } - } - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } + tech.droneRadioDamage = 2 }, remove() { - tech.isForeverDrones = 0 - if (this.count && !tech.haveGunCheck("drones", false)) b.giveGuns("drones") - if (this.count > 0) powerUps.research.changeRerolls(2) + tech.droneRadioDamage = 1 } }, { - name: "surfactant", - description: `use ${powerUps.orb.research(2)}to trade your foam gun
for 2 foam-bots and foam-bot upgrade`, + name: "orthocyclic winding", + link: `orthocyclic winding`, + description: "1.66x drone acceleration
1.33x radiation damage", isGunTech: true, - isRemoveGun: true, maxCount: 1, count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - isNonRefundable: true, - requires: "foam gun, not bot upgrades, fractionation, pressure vessel", + frequency: 2, + frequencyDefault: 2, allowed() { - return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() && !tech.isAmmoFoamSize && !tech.isFoamPressure && (build.isExperimentSelection || powerUps.research.count > 1) + return tech.isDroneRadioactive }, + requires: "drones, irradiated drones", effect() { - tech.giveTech("foam-bot upgrade") - for (let i = 0; i < 2; i++) { - b.foamBot() - tech.foamBotCount++; - } - simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) - if (tech.haveGunCheck("foam", false)) b.removeGun("foam") - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } + tech.isFastDrones = true }, remove() { - // if (this.count) { - // b.clearPermanentBots(); - // b.respawnBots(); - // if (!tech.haveGunCheck("foam")) b.giveGuns("foam") - // } - // if (this.count > 0) powerUps.research.changeRerolls(2) + tech.isFastDrones = false } }, { @@ -6853,7 +6819,7 @@ const tech = { }, { name: "uncertainty principle", - description: "foam, wave, and super ball positions are erratic
+53% foam, wave, and super ball damage", + description: "foam, wave, and super ball positions are erratic
1.5x foam, wave, and super ball damage", isGunTech: true, maxCount: 1, count: 0, @@ -6870,9 +6836,47 @@ const tech = { tech.isBulletTeleport = false; } }, + { + name: "surfactant", + description: `use ${powerUps.orb.research(2)}to trade your foam ${powerUps.orb.gun()}
for 2 foam-bots and foam-bot upgrade`, + // isGunTech: true, + isRemoveGun: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + isInstant: true, + requires: "foam gun, not bot upgrades, fractionation, pressure vessel", + allowed() { + return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() && !tech.isAmmoFoamSize && !tech.isFoamPressure && (build.isExperimentSelection || powerUps.research.count > 1) + }, + effect() { + + requestAnimationFrame(() => { tech.giveTech("foam-bot upgrade") }) + for (let i = 0; i < 2; i++) { + b.foamBot() + tech.foamBotCount++; + } + simulation.inGameConsole(`tech.isFoamBotUpgrade = true`) + if (tech.haveGunCheck("foam", false)) b.removeGun("foam") + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + // if (this.count) { + // b.clearPermanentBots(); + // b.respawnBots(); + // if (!tech.haveGunCheck("foam")) b.giveGuns("foam") + // } + // if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, { name: "aerogel", - description: "–50% foam duration and foam bubbles float
+180% foam damage", + description: "foam bubbles float with 0.5x foam duration
2.8x foam damage", isGunTech: true, maxCount: 1, count: 0, @@ -6893,7 +6897,7 @@ const tech = { }, { name: "surface tension", - description: "+43% foam damage", + description: "1.4x foam damage", isGunTech: true, maxCount: 9, count: 0, @@ -6904,7 +6908,7 @@ const tech = { }, requires: "foam", effect() { - tech.foamDamage += 0.01 * 0.43 + tech.foamDamage += 0.01 * 0.4 }, remove() { tech.foamDamage = 0.01; @@ -6912,7 +6916,7 @@ const tech = { }, { name: "cavitation", - description: "25% chance to discharge a huge foam bubble
increase foam recoil by 100%", + description: "25% chance to discharge a huge foam bubble
2x foam gun recoil", isGunTech: true, maxCount: 1, count: 0, @@ -6933,7 +6937,7 @@ const tech = { }, { name: "foam fractionation", - description: "if you have below 300 ammo
+100% foam gun bubble size", + description: "if you have below 300 ammo
2x foam bubble size", isGunTech: true, maxCount: 1, count: 0, @@ -6952,7 +6956,7 @@ const tech = { }, { name: "ideal gas law", - description: `remove all current foam ammo
+1200% foam ammo per ${powerUps.orb.ammo(1)}`, + description: `6x foam ammo per ${powerUps.orb.ammo(1)}`, //remove all current foam ammo
isGunTech: true, maxCount: 1, count: 0, @@ -6962,24 +6966,24 @@ const tech = { return tech.haveGunCheck("foam") && !tech.isEnergyNoAmmo }, requires: "foam, not non-renewables", - ammoLost: 0, + // ammoLost: 0, effect() { - b.guns[8].ammoPack = b.guns[8].ammoPack * 12; - this.ammoLost = b.guns[8].ammo - b.guns[8].ammo = 0 + b.guns[8].ammoPack *= 6; + // this.ammoLost = b.guns[8].ammo + // b.guns[8].ammo = 0 simulation.updateGunHUD() }, remove() { - b.guns[8].ammoPack = 24 if (this.count) { - b.guns[8].ammo += this.ammoLost + b.guns[8].ammoPack /= 8 + // b.guns[8].ammo += this.ammoLost simulation.updateGunHUD() } } }, { name: "pressure vessel", - description: "build up charge while firing foam gun
after firing discharge foam bubbles", + description: "build up charge while firing foam
after firing discharge foam bubbles", isGunTech: true, maxCount: 1, count: 0, @@ -7023,7 +7027,7 @@ const tech = { { name: "Bitter electromagnet", descriptionFunction() { - return `railgun charges +33% slower
+100% harpoon density and damage` + return `0.66x railgun charge rate
2x harpoon density and damage` }, isGunTech: true, maxCount: 3, @@ -7045,7 +7049,7 @@ const tech = { }, { name: "railgun", - description: `hold fire to charge harpoon and release to launch
harpoons can't retract`, + description: `hold and release fire to launch harpoons
but, harpoons can't retract`, // description: `+900% harpoon ammo, but it can't retract
+50% harpoon density and damage`, isGunTech: true, maxCount: 1, @@ -7061,70 +7065,24 @@ const tech = { tech.isRailGun = true; tech.harpoonDensity = tech.isRailGun ? 0.007 : 0.004 b.guns[9].chooseFireMethod() - b.guns[9].ammoPack = 5; + b.guns[9].ammoPack *= 3; b.guns[9].ammo = b.guns[9].ammo * 6; simulation.updateGunHUD(); }, remove() { - if (tech.isRailGun) { - tech.isRailGun = false; + tech.isRailGun = false; + if (this.count > 0) { tech.harpoonDensity = tech.isRailGun ? 0.007 : 0.004 b.guns[9].chooseFireMethod() - b.guns[9].ammoPack = 1.7; + b.guns[9].ammoPack /= 3; b.guns[9].ammo = Math.ceil(b.guns[9].ammo / 6); simulation.updateGunHUD(); } } }, - // { - // name: "grappling hook", - // description: `harpoons attach to the map and pull you
your rope extends while holding fire`, - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isRailGun && !tech.isFireMoveLock - // }, - // requires: "harpoon, not railgun, UHMWPE, induction furnace, Higgs mechanism", - // effect() { - // tech.isGrapple = true; - // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - // if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() - // } - // }, - // remove() { - // if (tech.isGrapple) { - // tech.isGrapple = false; - // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - // if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() - // } - // } - // } - // }, - // { - // name: "bulk modulus", - // description: `while grappling become invulnerable
drain energy`, - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return tech.haveGunCheck("harpoon") && !tech.isRailEnergy - // }, - // requires: "not alternator", - // effect() { - // tech.isImmuneGrapple = true; - // }, - // remove() { - // tech.isImmuneGrapple = false - // } - // }, { name: "alternator", - description: "harpoon no longer uses any energy", + description: "0.05x harpoon energy cost", isGunTech: true, maxCount: 1, count: 0, @@ -7163,7 +7121,7 @@ const tech = { { name: "Bessemer process", descriptionFunction() { - return `+${(10 * Math.sqrt(b.guns[9].ammo)).toFixed(0)}% harpoon size and damage
(effect scales by 1/10 √ harpoon ammo)` + return `${(1 + 0.1 * Math.sqrt(b.guns[9].ammo)).toFixed(2)}x harpoon size and damage
(effect scales by 1/10 √ harpoon ammo)` }, isGunTech: true, maxCount: 1, @@ -7229,7 +7187,7 @@ const tech = { { name: "UHMWPE", descriptionFunction() { - return `+${(b.guns[9].ammo * 1.25).toFixed(0)}% harpoon rope length
(effect scales by 1/80 of harpoon ammo)` + return `${(1 + b.guns[9].ammo * 0.0125).toFixed(2)}x harpoon rope length
(effect scales by 1/80 of harpoon ammo)` }, isGunTech: true, maxCount: 1, @@ -7249,7 +7207,7 @@ const tech = { }, { name: "induction furnace", - description: "after using harpoon/grapple to collect power ups
+77% harpoon or grapple damage for 8 seconds", + description: "after using harpoon/grapple to collect power ups
1.8x harpoon or grapple damage for 8 seconds", isGunTech: true, maxCount: 1, count: 0, @@ -7269,7 +7227,7 @@ const tech = { }, { name: "brittle", - description: "+111% harpoon/grapple damage
to mobs at maximum health", + description: "2.2x harpoon/grapple damage
to mobs at maximum durability", isGunTech: true, maxCount: 1, count: 0, @@ -7289,7 +7247,7 @@ const tech = { { name: "quasiparticles", descriptionFunction() { - return `convert current and future ${powerUps.orb.ammo(1)} into ${powerUps.orb.boost(1)} which
give +${(powerUps.boost.damage * 100).toFixed(0)}% damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` + return `convert current and future ${powerUps.orb.ammo(1)} into ${powerUps.orb.boost(1)}
that give ${(1 + powerUps.boost.damage).toFixed(2)}x damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` }, isGunTech: true, maxCount: 1, @@ -7297,7 +7255,7 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return ((tech.haveGunCheck("wave") && !tech.isInfiniteWaveAmmo) || tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun)) && !tech.isEnergyNoAmmo + return ((tech.haveGunCheck("wave") && tech.isInfiniteWaveAmmo) || tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun)) && !tech.isEnergyNoAmmo }, requires: "harpoon, laser, wave, frequency, not railgun, non-renewables", effect() { @@ -7317,61 +7275,63 @@ const tech = { }, { name: "optical amplifier", - description: "gain 3 random laser guntech
laser only turns off if you have no energy", - isGunTech: true, + description: `gain 3 random laser ${powerUps.orb.tech()}
laser only turns off if you have no energy`, + // isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, - isNonRefundable: true, + isInstant: true, allowed() { return tech.haveGunCheck("laser") && !tech.isPulseLaser }, requires: "laser gun, not pulse", effect() { - let techGiven = 0 - for (let j = 0; j < 3; j++) { - const names = ["quasiparticles", "lens", "compound lens", "arc length", "infrared diode", "free-electron laser", "dye laser", "relativistic momentum", "specular reflection", "diffraction grating", "diffuse beam", "output coupler", "slow light", "laser-bot", "laser-bot upgrade"] - //convert names into indexes - const options = [] - for (let i = 0; i < names.length; i++) { - for (let k = 0; k < tech.tech.length; k++) { - if (tech.tech[k].name === names[i]) { - options.push(k) - break + requestAnimationFrame(() => { + let techGiven = 0 + for (let j = 0; j < 3; j++) { + const names = ["quasiparticles", "lens", "compound lens", "arc length", "infrared diode", "free-electron laser", "dye laser", "relativistic momentum", "specular reflection", "diffraction grating", "diffuse beam", "output coupler", "slow light", "laser-bot", "laser-bot upgrade"] + //convert names into indexes + const options = [] + for (let i = 0; i < names.length; i++) { + for (let k = 0; k < tech.tech.length; k++) { + if (tech.tech[k].name === names[i]) { + options.push(k) + break + } } } - } - //remove options that don't meet requirements - for (let i = options.length - 1; i > -1; i--) { - const index = options[i] - if (!(tech.tech[index].count < tech.tech[index].maxCount) || !tech.tech[index].allowed()) { - options.splice(i, 1); + //remove options that don't meet requirements + for (let i = options.length - 1; i > -1; i--) { + const index = options[i] + if (!(tech.tech[index].count < tech.tech[index].maxCount) || !tech.tech[index].allowed()) { + options.splice(i, 1); + } + } + //pick one option + if (options.length) { + const index = options[Math.floor(Math.random() * options.length)] + simulation.inGameConsole(`tech.giveTech("${tech.tech[index].name}") //optical amplifier`, 360); + tech.giveTech(index) + techGiven++ } } - //pick one option - if (options.length) { - const index = options[Math.floor(Math.random() * options.length)] - simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //optical amplifier`, 360); - tech.giveTech(index) - techGiven++ - } - } - if (techGiven > 0) { - tech.isStuckOn = true - } else { //eject if none found - simulation.makeTextLog(`0 tech found //optical amplifier`); - const loop = () => { - if (!simulation.paused && m.alive) { - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].name === this.name) powerUps.ejectTech(i) + if (techGiven > 0) { + tech.isStuckOn = true + } else { //eject if none found + simulation.inGameConsole(`0 tech found //optical amplifier`); + const loop = () => { + if (!simulation.paused && m.alive) { + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].name === this.name) powerUps.ejectTech(i) + } + return } - return + requestAnimationFrame(loop); } requestAnimationFrame(loop); } - requestAnimationFrame(loop); - } + }); }, remove() { tech.isStuckOn = false @@ -7398,8 +7358,7 @@ const tech = { }, { name: "iridescence", - // description: "if a laser hits a mob at a low angle of illumination
+66% laser damage", - description: "if laser beams hit mobs near their center
+100% laser damage", + description: "if laser beams hit mobs near their center
2x laser damage", isGunTech: true, maxCount: 9, count: 0, @@ -7418,7 +7377,7 @@ const tech = { }, { name: "lens", - description: "+150% laser gun damage if it passes
through a revolving 90° arc circular lens", //π / 2
+ description: "2.5x laser damage if it passes
through a revolving 90° arc circular lens", //π / 2
isGunTech: true, maxCount: 1, count: 0, @@ -7443,7 +7402,7 @@ const tech = { }, { name: "compound lens", - description: "+40% laser lens damage
+25° lens arc", + description: "1.4x laser lens damage
+25° lens arc", isGunTech: true, maxCount: 9, count: 0, @@ -7483,7 +7442,7 @@ const tech = { }, { name: "diffraction grating", - description: `+1 diverging laser gun beam`, + description: `+1 diverging laser beam`, isGunTech: true, maxCount: 9, count: 0, @@ -7507,7 +7466,7 @@ const tech = { { name: "diffuse beam", link: `diffuse beam`, - description: "laser gun beam is wider and doesn't reflect
+220% laser damage", + description: "your laser beam is wider, but it doesn't reflect
3.2x laser damage", isGunTech: true, maxCount: 1, count: 0, @@ -7532,7 +7491,7 @@ const tech = { }, { name: "output coupler", - description: "+30% laser gun beam width
+30% laser damage", + description: "1.3x laser beam width
1.3x laser damage", isGunTech: true, maxCount: 9, count: 0, @@ -7556,19 +7515,18 @@ const tech = { } }, { - name: "slow light", - description: "laser gun beam is spread into your recent past
+300% total beam damage", + name: "delayed-choice", + description: "your laser fires a 0.4 second delayed beam
that does 0.7x damage", isGunTech: true, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isWideLaser + return tech.haveGunCheck("laser") && !tech.beamSplitter && !tech.isWideLaser }, - requires: "laser gun, not specular reflection, diffraction grating, diffuse beam", + requires: "laser gun, diffraction grating, diffuse beam", effect() { - // this.description = `add 5 more laser beams into into your past` tech.historyLaser++ b.guns[11].chooseFireMethod() }, @@ -7581,14 +7539,14 @@ const tech = { }, { name: "infrared diode", - description: "+60% laser energy efficiency
infrared light is outside visual perception", + description: "0.4x laser energy cost
infrared light is outside visual perception", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserBotUpgrade || tech.isLaserMine || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.0018 + return (tech.haveGunCheck("laser") || tech.isLaserBotUpgrade || tech.isLaserMine || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.003 }, requires: "laser, not free-electron, pulse", effect() { @@ -7597,21 +7555,21 @@ const tech = { // tech.laserColorAlpha = "rgba(255,0,20,0.05)" }, remove() { - tech.laserDrain = 0.0018; + tech.laserDrain = 0.003; tech.laserColor = "#f02" tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" } }, { name: "dye laser", - description: "+25% laser energy efficiency
+25% laser damage", + description: "0.75x laser energy cost
1.25x laser damage", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.0018 + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.003 }, requires: "laser, not pulse, infrared diode", effect() { @@ -7621,7 +7579,7 @@ const tech = { tech.laserColorAlpha = "rgba(0, 11, 255,0.5)" }, remove() { - tech.laserDrain = 0.0018; + tech.laserDrain = 0.003; tech.laserDamage = 0.18; //used in check on pulse and diode: tech.laserDamage === 0.18 tech.laserColor = "#f00" tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" @@ -7629,24 +7587,24 @@ const tech = { }, { name: "free-electron laser", - description: "–250% laser energy efficiency
+200% laser damage", + description: "3x laser energy cost
3x laser damage", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.0018 + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.003 }, requires: "laser, not pulse, infrared diode", effect() { - tech.laserDrain *= 1 + 2.5 //250% more drain - tech.laserDamage *= 1 + 2 //190% more damage + tech.laserDrain *= 3 + tech.laserDamage *= 3 tech.laserColor = "#83f" tech.laserColorAlpha = "rgba(136, 51, 255,0.5)" }, remove() { - tech.laserDrain = 0.0018; + tech.laserDrain = 0.003; tech.laserDamage = 0.18; //used in check on pulse and diode: tech.laserDamage === 0.18 tech.laserColor = "#f00" tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" @@ -7661,7 +7619,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && tech.laserDrain === 0.0018 && !tech.isStuckOn + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && tech.laserDrain === 0.003 && !tech.isStuckOn }, requires: "laser gun, not specular reflection, diffuse, free-electron laser, optical amplifier", effect() { @@ -7681,7 +7639,7 @@ const tech = { //************************************************** { name: "spherical harmonics", - description: "+50% standing wave deflection efficiency
shield deflection radius holds it's max range", //standing wave oscillates in a 3rd dimension
+ description: "1.5x standing wave deflection energy efficiency
shield deflection radius is stable", //standing wave oscillates in a 3rd dimension
isFieldTech: true, maxCount: 9, count: 0, @@ -7748,7 +7706,7 @@ const tech = { }, { name: "expansion", - description: "using standing wave field expands its radius
+77 maximum energy", + description: "using standing wave expands its radius
+77 maximum energy", isFieldTech: true, maxCount: 1, count: 0, @@ -7771,9 +7729,13 @@ const tech = { } }, { + + // descriptionFunction() { + // return `use ${powerUps.orb.research(2)}
1.01x damage per energy below maximum (${(1 + Math.max(0, m.maxEnergy - m.energy)).toFixed(2)}x)` + // }, name: "electronegativity", descriptionFunction() { - return `+0.23% damage per current stored energy
(+${(27 * m.maxEnergy).toFixed(0)}% damage at max energy)` + return `1.0023x damage per energy
(${(1 + 0.23 * m.energy).toFixed(2)} at current energy, ${(1 + 0.23 * m.maxEnergy).toFixed(2)}x at maximum energy)` }, // description: "+1% damage per 8 stored energy", isFieldTech: true, @@ -7813,7 +7775,7 @@ const tech = { }, { name: "cherenkov radiation", //deflecting and blocks - description: "bremsstrahlung's effects are radioactive
+250% damage over 3 seconds", + description: "bremsstrahlung's effects are radioactive
3.5x damage over 3 seconds", isFieldTech: true, maxCount: 1, count: 0, @@ -7832,7 +7794,7 @@ const tech = { }, { name: "flux pinning", - description: "after deflecting a mob
it is stunned for up to 4 seconds", + description: `mobs deflected by your ${powerUps.orb.field()}
are stunned for 4 seconds`, isFieldTech: true, maxCount: 9, count: 0, @@ -7849,29 +7811,6 @@ const tech = { tech.isStunField = 0; } }, - { - name: "triple point", - descriptionFunction() { - return `+1.5 second ice IX freeze and spawn ${powerUps.orb.coupling(10)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` - }, - isFieldTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldMode === 2 - }, - requires: "perfect diamagnetism", - effect() { - tech.iceIXFreezeTime += 90 - powerUps.spawnDelay("coupling", 10) - }, - remove() { - tech.iceIXFreezeTime = 150 - if (this.count) m.couplingChange(-10 * this.count) - } - }, { name: "eddy current brake", description: "perfect diamagnetism slows nearby mobs
effect radius scales with stored energy", @@ -7893,7 +7832,7 @@ const tech = { }, { name: "Meissner effect", - description: "+55% perfect diamagnetism radius
+22° perfect diamagnetism circular arc", + description: "1.55x perfect diamagnetism radius
+22° perfect diamagnetism circular arc", isFieldTech: true, maxCount: 1, count: 0, @@ -7913,9 +7852,8 @@ const tech = { { name: "radiative equilibrium", descriptionFunction() { - return `after losing ${tech.isEnergyHealth ? "energy" : "health"}
+200% damage for 8 seconds` + return `after losing ${tech.isEnergyHealth ? "energy" : "health"}
3x damage for 8 seconds` }, - // description: `after losing ${tech.isEnergyHealth ? "energy" : "health"}
+200% damage for 8 seconds`, isFieldTech: true, maxCount: 1, count: 0, @@ -7935,21 +7873,19 @@ const tech = { { name: "dynamic equilibrium", descriptionFunction() { - // return `increase damage by your defense and
5% of your last ${tech.isEnergyHealth ? "energy" : "health"} loss   (+${(100 * Math.max(5, tech.lastHitDamage) * m.lastHit * (2 - m.defense())).toFixed(0)}% damage)` - return `increase damage by your last ${tech.isEnergyHealth ? "energy" : "health"} loss
scales with defense   (+${(100 * Math.max(5, tech.lastHitDamage) * m.lastHit * (2 - m.defense())).toFixed(0)}% damage)` - }, // = +${10*m.defense()}% - // descriptionFunction() { return `increase damage by your last ${tech.isEnergyHealth ? "energy" : "health"} loss
(${(tech.lastHitDamage).toFixed(0)}%)(${(100*m.lastHit).toFixed(0)} ${tech.isEnergyHealth ? "energy" : "health"})(${2 - m.defense()} defense) = ${(100*tech.lastHitDamage * m.lastHit * (2 - m.defense())).toFixed(0)}% damage ` }, // = +${10*m.defense()}% + return `increase damage by your most recent ${tech.isEnergyHealth ? "energy" : "health"} loss
(${(1 + (tech.lastHitDamage === 0 ? 6 : tech.lastHitDamage) * m.lastHit).toFixed(2)}x)` + }, isFieldTech: true, - maxCount: 9, + maxCount: 3, count: 0, frequency: 2, - frequencyDefault: 2, + frequencyDefault: 200, allowed() { return m.fieldMode === 8 || m.fieldMode === 3 }, requires: "negative mass, pilot wave", effect() { - tech.lastHitDamage += 4; + tech.lastHitDamage += 6; }, remove() { tech.lastHitDamage = 0; @@ -7957,7 +7893,7 @@ const tech = { }, { name: "neutronium", - description: `move and jump 20% slower
if your field is active +95% defense`, + description: `0.8x move and jump, but
while your ${powerUps.orb.field()} is active 0.05x damage taken`, isFieldTech: true, maxCount: 1, count: 0, @@ -7969,8 +7905,8 @@ const tech = { requires: "negative mass", effect() { tech.isNeutronium = true - tech.baseFx *= 0.8 - tech.baseJumpForce *= 0.8 + tech.baseFx *= 0.86 + tech.baseJumpForce *= 0.87 m.setMovement() }, //also removed in m.setHoldDefaults() if player switches into a bad field @@ -7985,7 +7921,9 @@ const tech = { }, { name: "aerostat", - description: `+100% damage while off the ground
-15% damage while on the ground`, + descriptionFunction() { + return `2x damage while off the ground
0.9x damage while on the ground(${(m.onGround ? 0.9 : 2).toFixed(1)}x)` + }, isFieldTech: true, maxCount: 1, count: 0, @@ -8039,219 +7977,72 @@ const tech = { remove() { tech.isFlyFaster = false; } - }, - // { - // name: "Bose Einstein condensate", - // description: "use energy to freeze mobs in your field
pilot wave, negative mass, time dilation", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return m.fieldMode === 8 || m.fieldMode === 3 || (m.fieldMode === 6 && !tech.isRewindField) - // }, - // requires: "pilot wave, negative mass, time dilation, not retrocausality", - // effect() { - // tech.isFreezeMobs = true - // }, - // remove() { - // tech.isFreezeMobs = false - // } - // }, - { - name: "bot manufacturing", - description: `use ${powerUps.orb.research(2)} to build
3 random bots`, + }, { + name: "Newtons 1st law", + descriptionFunction() { + return `damage taken reduces as your speed increases
up to 0.05x damage taken at 60 speed (${(1 - Math.min((tech.speedAdded + player.speed) * 0.01583, 0.95)).toFixed(2)}x)` + }, isFieldTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isBotTech: true, - isNonRefundable: true, allowed() { - return powerUps.research.count > 1 && (m.fieldMode === 4 || m.fieldMode === 10 || m.fieldMode === 8) + return m.fieldMode === 3 || m.fieldMode === 10 }, - requires: "molecular assembler, grappling hook, pilot wave", + requires: "negative mass, grappling hook", effect() { - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - m.energy = 0.01; - b.randomBot() - b.randomBot() - b.randomBot() + tech.isSpeedHarm = true //max at speed = 40 }, - remove() { } + remove() { + tech.isSpeedHarm = false + } }, { - name: "bot prototypes", - description: `use ${powerUps.orb.research(3)}to build 2 random bots
and upgrade all bots to that type`, + name: "Newtons 2nd law", + descriptionFunction() { + return `damage increases proportional to your speed
up to 3x damage at 60 speed (${(1 + Math.min(2, ((tech.speedAdded + player.speed) * 0.033))).toFixed(2)}x)` + }, isFieldTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, - isBotTech: true, - isNonRefundable: true, allowed() { - return powerUps.research.count > 2 && (m.fieldMode === 4 || m.fieldMode === 10 || m.fieldMode === 8) + return m.fieldMode === 3 || m.fieldMode === 10 }, - requires: "molecular assembler, grappling hook, pilot wave", + requires: "negative mass, grappling hook", effect() { - for (let i = 0; i < 3; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - //fill array of available bots - const notUpgradedBots = [] - const num = 2 - notUpgradedBots.push(() => { - tech.giveTech("nail-bot upgrade") - for (let i = 0; i < num; i++) { - b.nailBot() - tech.nailBotCount++; - } - simulation.makeTextLog(`tech.isNailBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("foam-bot upgrade") - for (let i = 0; i < num; i++) { - b.foamBot() - tech.foamBotCount++; - } - simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("sound-bot upgrade") - for (let i = 0; i < num; i++) { - b.soundBot() - tech.soundBotCount++; - } - simulation.makeTextLog(`tech.isSoundBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("boom-bot upgrade") - for (let i = 0; i < num; i++) { - b.boomBot() - tech.boomBotCount++; - } - simulation.makeTextLog(`tech.isBoomBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("laser-bot upgrade") - for (let i = 0; i < num; i++) { - b.laserBot() - tech.laserBotCount++; - } - simulation.makeTextLog(`tech.isLaserBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("orbital-bot upgrade") - for (let i = 0; i < num; i++) { - b.orbitBot() - tech.orbitBotCount++; - } - simulation.makeTextLog(`tech.isOrbitalBotUpgrade = true`) - }) - for (let i = 0; i < 2; i++) { //double chance for dynamo-bot, since it's very good for assembler - notUpgradedBots.push(() => { - tech.giveTech("dynamo-bot upgrade") - for (let i = 0; i < num; i++) { - b.dynamoBot() - tech.dynamoBotCount++; - } - simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) - }) - } - notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() //choose random function from the array and run it + tech.isSpeedDamage = true //max at speed = 40 }, - remove() { } - }, - // { - // name: "mycelium manufacturing", - // link: `mycelium manufacturing`, - // // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow spores`, - // descriptionFunction() { return `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow ${b.guns[6].nameString('s')}` }, - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // allowed() { - // return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldMode === 4 && !(tech.isMissileField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) - // }, - // requires: "molecular assembler, no other manufacturing, no drone tech", - // effect() { - // if (!build.isExperimentSelection) { - // for (let i = 0; i < 1; i++) { - // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - // } - // } - // tech.isSporeField = true; - // }, - // remove() { - // tech.isSporeField = false; - // if (this.count > 0) powerUps.research.changeRerolls(1) - // } - // }, - // { - // name: "missile manufacturing", - // link: `missile manufacturing`, - // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to construct missiles`, - // // description: "use 3 research to repurpose assembler
excess energy used to construct missiles", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // allowed() { - // return (build.isExperimentSelection || powerUps.research.count > 0) && m.maxEnergy > 0.5 && m.fieldMode === 4 && !(tech.isSporeField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel) - // }, - // requires: "molecular assembler, no other manufacturing, no drone tech", - // effect() { - // if (!build.isExperimentSelection) { - // for (let i = 0; i < 1; i++) { - // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - // } - // } - // tech.isMissileField = true; - // }, - // remove() { - // tech.isMissileField = false; - // if (this.count > 0) powerUps.research.changeRerolls(1) - // } - // }, - // { - // name: "ice IX manufacturing", - // link: `ice IX manufacturing`, - // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to condense ice IX`, - // // description: "use 3 research to repurpose assembler
excess energy used to condense ice IX", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // allowed() { - // return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldMode === 4 && !(tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel) - // }, - // requires: "molecular assembler, no other manufacturing, no drone tech", - // effect() { - // if (!build.isExperimentSelection) { - // for (let i = 0; i < 1; i++) { - // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - // } - // } - // tech.isIceField = true; - // }, - // remove() { - // tech.isIceField = false; - // if (this.count > 0) powerUps.research.changeRerolls(1) - // } - // }, + remove() { + tech.isSpeedDamage = false + } + }, + { + name: "MOND", + descriptionFunction() { + return `your speed counts as +20 higher
(for Newton's 1st and 2nd laws)` + }, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isSpeedDamage || tech.isSpeedHarm + }, + requires: "Newtons 1st or 2nd law", + effect() { + tech.speedAdded = 20 + }, + remove() { + tech.speedAdded = 0 + } + }, { name: "additive manufacturing", - description: "hold crouch and use your field to print a block
with +80% density, damage, and launch speed", - // description: "simultaneously fire and activate your field to make
molecular assembler print a throwable block
+80% block throwing speed", + description: `hold crouch and use ${powerUps.orb.field()} to print a block
with 1.8x density, damage, and launch speed`, isFieldTech: true, maxCount: 1, count: 0, @@ -8269,6 +8060,86 @@ const tech = { tech.isPrinter = false; } }, + { + name: "working mass", + // description: "molecular assembler prints one block
to jump off while midair", + descriptionFunction() { + const fieldName = m.fieldMode === 8 ? "pilot wave" : "molecular assembler" + return `pressing jump in midair
will print a block to jump off` + // return `${fieldName} prints a block
to jump off while midair` + }, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldMode === 4 || m.fieldMode === 8) + }, + requires: "molecular assembler, pilot wave", + effect() { + simulation.ephemera.push({ + name: "blockJump", + blockJumpPhase: 0, + do() { + if (m.onGround && m.buttonCD_jump + 10 < m.cycle && !(m.lastOnGroundCycle + m.coyoteCycles > m.cycle)) this.blockJumpPhase = 0 //reset after touching ground or block + if (this.blockJumpPhase === 0 && !m.onGround && !input.up && m.buttonCD_jump + 10 < m.cycle) { //not pressing jump + this.blockJumpPhase = 1 + } else if (this.blockJumpPhase === 1 && input.up && m.buttonCD_jump + 10 < m.cycle) { //2nd jump + this.blockJumpPhase = 2 + let horizontalVelocity = 8 * (- input.left + input.right) //ive player and block horizontal momentum + + const radius = 25 + Math.floor(15 * Math.random()) + body[body.length] = Matter.Bodies.polygon(m.pos.x, m.pos.y + 60 + radius, 4, radius, { + friction: 0.05, + frictionAir: 0.001, + collisionFilter: { + category: cat.body, + mask: cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + }, + classType: "body", + }); + const block = body[body.length - 1] + //mess with the block shape (this code is horrible) + Composite.add(engine.world, block); //add to world + const r1 = radius * (1 + 0.4 * Math.random()) + const r2 = radius * (1 + 0.4 * Math.random()) + let angle = Math.PI / 4 + const vertices = [] + for (let i = 0, len = block.vertices.length; i < len; i++) { + angle += 2 * Math.PI / len + vertices.push({ x: block.position.x + r1 * Math.cos(angle), y: block.position.y + r2 * Math.sin(angle) }) + } + Matter.Body.setVertices(block, vertices) + // Matter.Body.setAngle(block, Math.PI / 4) + Matter.Body.setVelocity(block, { x: 0.9 * player.velocity.x - horizontalVelocity, y: 10 }); + Matter.Body.applyForce(block, m.pos, { x: 0, y: m.jumpForce * 0.12 * Math.min(m.standingOn.mass, 5) }); + if (tech.isBlockRestitution) { + block.restitution = 0.999 //extra bouncy + block.friction = block.frictionStatic = block.frictionAir = 0.001 + } + if (tech.isAddBlockMass) { + const expand = function (that, massLimit) { + if (that.mass < massLimit) { + const scale = 1.04; + Matter.Body.scale(that, scale, scale); + setTimeout(expand, 20, that, massLimit); + } + }; + expand(block, Math.min(20, block.mass * 3)) + } + //jump + m.buttonCD_jump = m.cycle; //can't jump again until 20 cycles pass + Matter.Body.setVelocity(player, { x: player.velocity.x + horizontalVelocity, y: -7.5 + 0.25 * player.velocity.y }); + player.force.y = -m.jumpForce; //player jump force + } + }, + }) + }, + remove() { + if (this.count) simulation.removeEphemera("blockJump") + } + }, { name: "pair production", description: "after picking up a power up
+200 energy", @@ -8283,7 +8154,7 @@ const tech = { requires: "molecular assembler, pilot wave, standing wave", effect() { tech.isMassEnergy = true // used in m.grabPowerUp - m.energy += 2 + m.energy += 2 * level.isReducedRegen }, remove() { tech.isMassEnergy = false; @@ -8310,7 +8181,7 @@ const tech = { }, { name: "combinatorial optimization", - description: "+35% damage
–35% fire rate", + description: "1.4x damage
0.7x fire rate", isFieldTech: true, maxCount: 1, count: 0, @@ -8320,21 +8191,21 @@ const tech = { return m.fieldMode === 6 || m.fieldMode === 7 || m.fieldMode === 8 }, requires: "time dilation, cloaking, pilot wave", - damage: 1.35, + damage: 1.4, effect() { tech.damage *= this.damage - tech.aimDamage = 1.35 + tech.aimDamage = 1.42 b.setFireCD(); }, remove() { - if (this.count) tech.damage /= this.damage + if (this.count && m.alive) tech.damage /= this.damage tech.aimDamage = 1 b.setFireCD(); } }, { name: "tokamak", - description: "throwing a block converts it into energy
and a pulsed fusion explosion", + description: "tokamak converts thrown blocks into energy
and a pulsed fusion explosion", isFieldTech: true, maxCount: 1, count: 0, @@ -8351,16 +8222,57 @@ const tech = { tech.isTokamak = false; } }, + { + name: "stellarator", + descriptionFunction() { + return `the first 5 blocks detonated by tokamak
spawn ${powerUps.orb.heal(1)} proportional to block size` + }, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isTokamak && (m.fieldMode === 5 || m.fieldMode === 4 || m.fieldMode === 10) + }, + requires: "tokamak", + effect() { + tech.isTokamakHeal = true; + tech.tokamakHealCount = 0 + }, + remove() { + tech.isTokamakHeal = false; + } + }, + { + name: "inertial confinement", + description: "while holding a block charged with tokamak
you can use energy to fly", //and invulnerable? + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isTokamak && (m.fieldMode === 5 || m.fieldMode === 4 || m.fieldMode === 10) + }, + requires: "tokamak", + effect() { + tech.isTokamakFly = true; + }, + remove() { + tech.isTokamakFly = false; + } + }, { name: "degenerate matter", - description: "if your field is active
+88% defense", + description: `if your ${powerUps.orb.field()} is active
0.1x damage taken`, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return (m.fieldMode === 10 || m.fieldMode === 4 || m.fieldMode === 8) + return (m.fieldMode === 10 || m.fieldMode === 5 || m.fieldMode === 8) }, requires: "plasma torch, grappling hook, pilot wave", effect() { @@ -8373,8 +8285,9 @@ const tech = { { name: "plasma-bot", link: `plasma-bot`, - description: `use ${powerUps.orb.research(2)}to trade your field
for a bot that uses energy to emit plasma`, - isFieldTech: true, + description: `use ${powerUps.orb.research(2)}to trade your ${powerUps.orb.field()}
for a bot that uses energy to emit plasma`, + // isFieldTech: true, + isInstant: true, maxCount: 1, count: 0, frequency: 3, @@ -8416,30 +8329,30 @@ const tech = { { name: "plasma jet", link: `plasma jet`, - description: `use ${powerUps.orb.research(2)}
+50% plasma torch range`, + description: `use ${powerUps.orb.research(1)}
1.5x plasma torch range`, isFieldTech: true, - maxCount: 1, + maxCount: 3, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return (tech.plasmaBotCount || m.fieldMode === 5) && (build.isExperimentSelection || powerUps.research.count > 1) && !tech.isPlasmaBall + return (tech.plasmaBotCount || m.fieldMode === 5) && (build.isExperimentSelection || powerUps.research.count > 0) && !tech.isPlasmaBall }, requires: "plasma torch, not plasma ball", effect() { tech.isPlasmaRange += 0.5; - for (let i = 0; i < 2; i++) { + for (let i = 0; i < 1; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, remove() { tech.isPlasmaRange = 1; - if (this.count > 0) powerUps.research.changeRerolls(this.count * 2) + if (this.count > 0) powerUps.research.changeRerolls(this.count) } }, { name: "extruder", - description: "extrude a thin hot wire of plasma
increases damage and energy drain", + description: "extrude a thin hot wire of plasma
increases damage and energy cost", isFieldTech: true, maxCount: 1, count: 0, @@ -8479,7 +8392,7 @@ const tech = { }, { name: "plasma ball", - description: "grow an expanding ball of plasma
increases damage and energy drain", + description: "grow an expanding ball of plasma
increases damage and energy cost", isFieldTech: true, maxCount: 1, count: 0, @@ -8541,7 +8454,7 @@ const tech = { }, { name: "frame-dragging", //"non-inertial frame", - description: "when not moving time dilation stops time
+33% defense", + description: "when not moving time dilation stops time
0.6x damage taken", isFieldTech: true, maxCount: 1, count: 0, @@ -8562,7 +8475,7 @@ const tech = { }, { name: "Lorentz transformation", - description: `use ${powerUps.orb.research(3)}
+50% movement, jumping, and fire rate`, + description: `use ${powerUps.orb.research(3)}
1.5x movement, jumping, and fire rate`, isFieldTech: true, maxCount: 1, count: 0, @@ -8589,9 +8502,8 @@ const tech = { }, { name: "time crystals", - // description: "+150% passive energy generation
${}", descriptionFunction() { - return `+150% passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` + return `2.5x passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` }, isFieldTech: true, maxCount: 1, @@ -8606,20 +8518,19 @@ const tech = { tech.isTimeCrystals = true m.setFieldRegen() this.descriptionFunction = function () { - return `+150% passive energy generation
(+${(60 * m.fieldRegen * 60).toFixed(1)} energy per second)` + return `2.5x passive energy generation
(+${(60 * m.fieldRegen * 60).toFixed(1)} energy per second)` } }, remove() { tech.isTimeCrystals = false m.setFieldRegen() this.descriptionFunction = function () { - return `+150% passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` + return `2.5x passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` } } }, { name: "no-cloning theorem", - // descriptionFunction() { return `+45% chance to duplicate spawned power ups
after a mob dies –2% duplication (${tech.duplicationChance()})` }, description: `+40% chance to duplicate spawned power ups
after a mob dies –1% duplication`, isFieldTech: true, maxCount: 1, @@ -8637,17 +8548,14 @@ const tech = { }, remove() { tech.cloakDuplication = 0 - powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "metamaterial absorber", //quantum eraser descriptionFunction() { - return `for each mob left alive after you exit a level
there is a 25% chance to spawn a random power up` + return `for each mob left alive after you exit a level
there is a 30% chance to spawn a random power up` }, - // descriptionFunction() { - // return `for each mob left alive after you exit a level
` - // }, isFieldTech: true, maxCount: 1, count: 0, @@ -8667,7 +8575,7 @@ const tech = { { name: "symbiosis", descriptionFunction() { - return `after a boss dies spawn ${powerUps.orb.research(4)}${powerUps.orb.heal(3)} and a tech
after a mob dies –0.25 maximum ${tech.isEnergyHealth ? "energy" : "health"}` + return `after a boss dies spawn ${powerUps.orb.research(4)}${powerUps.orb.heal(3)}${powerUps.orb.tech()}
after a mob dies –0.25 maximum ${tech.isEnergyHealth ? "energy" : "health"}` }, isFieldTech: true, maxCount: 1, @@ -8711,7 +8619,7 @@ const tech = { { name: "patch", link: `patch`, - description: "after cloaking recover 75%
of your last health lost", + description: "after cloaking recover 0.75x
of your last health lost", isFieldTech: true, maxCount: 1, count: 0, @@ -8731,7 +8639,7 @@ const tech = { { name: "dazzler", link: `dazzler`, - description: "after decloaking
stun nearby mobs for 2 second", + description: "after decloaking
stun nearby mobs for 2 seconds", isFieldTech: true, maxCount: 1, count: 0, @@ -8750,7 +8658,7 @@ const tech = { }, { name: "topological defect", - description: "+111% damage
to mobs at maximum health", + description: "2.1x damage
to mobs at maximum durability", isFieldTech: true, maxCount: 1, count: 0, @@ -8767,37 +8675,37 @@ const tech = { tech.isMobFullHealthCloak = false } }, - // { - // name: "ambush", - // description: "metamaterial cloaking field damage effect
is increased from 333% to 555%", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return m.fieldMode === 7 - // }, - // requires: "metamaterial cloaking", - // effect() { - // tech.sneakAttackDmg = 6.55 //555% + 100% - // }, - // remove() { - // tech.sneakAttackDmg = 4.33 //333% + 100% - // } - // }, + { + name: "hidden-variable theory", + description: `1.15x damage each time you choose ${powerUps.orb.fieldTech()}`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldMode === 8 + }, + requires: "pilot wave", + effect() { + tech.isDamageFieldTech = true + }, + remove() { + tech.isDamageFieldTech = false + } + }, { name: "WIMPs", - description: `at the end of each level spawn ${powerUps.orb.research(4)}
and a dangerous particle that slowly chases you`, + description: `at each level's exit, spawn ${powerUps.orb.research(4)}
and a dangerous particle that slowly chases you`, isFieldTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return m.fieldMode === 9 || m.fieldMode === 8 || m.fieldMode === 6 + return m.fieldMode === 9 || m.fieldMode === 8 }, - requires: "wormhole, pilot wave, time dilation", + requires: "wormhole, pilot wave", effect() { tech.wimpCount++ spawn.WIMP() @@ -8829,29 +8737,10 @@ const tech = { }, remove() { tech.fieldDuplicate = 0 - powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (this.count > 0) powerUps.research.changeRerolls(3) } }, - // { - // name: "Penrose process", - // description: "after a block falls into a wormhole
+50 energy", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return m.fieldMode === 9 - // }, - // requires: "wormhole", - // effect() { - // tech.isWormholeEnergy = true - // }, - // remove() { - // tech.isWormholeEnergy = false - // } - // }, { name: "transdimensional worms", link: `transdimensional worms`, @@ -8872,9 +8761,33 @@ const tech = { tech.isWormholeWorms = false } }, + { + name: "anyon", + descriptionFunction() { + return `2x energy after duplicating a power up
+6% chance to duplicate spawned power ups` + }, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldMode === 9 || m.fieldMode === 1 + }, + requires: "wormhole, standing wave", + effect() { + tech.isDupEnergy = true; + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.06); + }, + remove() { + tech.isDupEnergy = false; + if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } + } + }, { name: "geodesics", - description: `your bullets can traverse wormholes
spawn 2 guns and ${powerUps.orb.ammo(4)}`, + description: `your bullets can traverse wormholes
1.5x damage`, isFieldTech: true, maxCount: 1, count: 0, @@ -8884,18 +8797,21 @@ const tech = { return m.fieldMode === 9 }, requires: "wormhole", + damage: 1.5, effect() { + tech.damage *= this.damage tech.isWormHoleBullets = true - for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "gun"); - for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "ammo"); + // for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "gun"); + // for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "ammo"); }, remove() { - if (tech.isWormHoleBullets) { - for (let i = 0; i < 2; i++) { - if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun - } - tech.isWormHoleBullets = false; - } + // if (tech.isWormHoleBullets) { + // for (let i = 0; i < 2; i++) { + // if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun + // } + // } + if (this.count && m.alive) tech.damage /= this.damage + tech.isWormHoleBullets = false; } }, { @@ -8939,7 +8855,7 @@ const tech = { }, { name: "charmed baryons", - description: `–33% movement and jumping
wormholes drain zero energy`, + description: `0.8x movement and jumping
wormholes cost zero energy`, isFieldTech: true, maxCount: 1, count: 0, @@ -8951,8 +8867,8 @@ const tech = { requires: "wormhole, not affine connection", effect() { tech.isFreeWormHole = true - tech.baseFx *= 0.66 - tech.baseJumpForce *= 0.66 + tech.baseFx *= 0.8 + tech.baseJumpForce *= 0.8 m.setMovement() }, //also removed in m.setHoldDefaults() if player switches into a bad field @@ -8967,7 +8883,7 @@ const tech = { }, { name: "affine connection", - description: "wormholes can tunnel through anything
for +200% energy drain", + description: "wormholes can tunnel through anything
for 2x energy cost", isFieldTech: true, maxCount: 1, count: 0, @@ -9025,7 +8941,7 @@ const tech = { }, { name: "reel", - description: "+400% block collision damage
up to +100 energy after reeling in blocks", + description: "5x block collision damage
up to +100 energy after reeling in blocks", isFieldTech: true, maxCount: 1, count: 0, @@ -9092,7 +9008,7 @@ const tech = { // maxCount: 1, // count: 0, // frequency: 0, - // isNonRefundable: true, + // isInstant: true, // isBadRandomOption: true, // isExperimentalMode: true, // allowed() { @@ -9243,7 +9159,7 @@ const tech = { // maxCount: 9, // count: 0, // frequency: 0, - // isNonRefundable: true, + // isInstant: true, // isJunk: true, // allowed() { // return true @@ -9256,12 +9172,12 @@ const tech = { // }, { name: "swap meet", - description: "normal tech become JUNK
and JUNK become normal tech", + description: `normal ${powerUps.orb.tech()} become JUNK
and JUNK become normal`, maxCount: 1, count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed() { return true }, @@ -9287,7 +9203,7 @@ const tech = { // count: 0, // frequency: 0, // isJunk: true, - // isNonRefundable: true, + // isInstant: true, // allowed() { // return true // }, @@ -9306,7 +9222,7 @@ const tech = { const loop = () => { if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { const dmg = Math.floor(27 * Math.random()) * 0.01 - this.text = `+${(dmg * 100).toFixed(0).padStart(2, '0')}% damage` + this.text = `+${(1 + dmg).toFixed(2).padStart(2, '0')}x damage` this.damage = 1 + dmg if (document.getElementById(`damage-JUNK-id${this.id}`)) document.getElementById(`damage-JUNK-id${this.id}`).innerHTML = this.text setTimeout(() => { @@ -9333,7 +9249,7 @@ const tech = { tech.damage *= this.damage }, remove() { - if (this.count > 0) tech.damage /= this.damage + if (this.count && m.alive) tech.damage /= this.damage } }, { @@ -9342,7 +9258,7 @@ const tech = { count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed() { return !build.isExperimentSelection }, @@ -9362,7 +9278,7 @@ const tech = { count += 4.5 const waves = 2 * Math.sin(count * 0.0133) + Math.sin(count * 0.013) + 0.5 * Math.sin(count * 0.031) + 0.33 * Math.sin(count * 0.03) this.spawnCount = Math.floor(100 * Math.abs(waves)) - this.text = `spawn ${this.spawnCount.toLocaleString(undefined, { minimumIntegerDigits: 3 })} ${powerUps.orb.boost(1)}
that give +${(powerUps.boost.damage * 100).toFixed(0)}% damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` + this.text = `spawn ${this.spawnCount.toLocaleString(undefined, { minimumIntegerDigits: 3 })} ${powerUps.orb.boost(1)}
that give ${(1 + powerUps.boost.damage).toFixed(2)}x damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` if (document.getElementById(`boost-JUNK-id${this.id}`)) document.getElementById(`boost-JUNK-id${this.id}`).innerHTML = this.text setTimeout(() => { loop() @@ -9378,7 +9294,7 @@ const tech = { }, { name: "placebo", - description: "+777% damage
+777% defense", + description: "7.77x damage", maxCount: 1, count: 0, frequency: 0, @@ -9386,7 +9302,7 @@ const tech = { allowed: () => true, requires: "", effect() { - if (Math.random() < 0.1) tech.damage *= 8.77 + if (Math.random() < 0.07) tech.damage *= 7.77 }, remove() { } }, @@ -9405,7 +9321,7 @@ const tech = { remove() { } }, { - name: "planned obsolescence", + name: "defunct", description: "build 100 scrap bots
bots might last for 30 seconds", maxCount: 1, count: 0, @@ -9439,16 +9355,15 @@ const tech = { // }, { name: "return", - description: "return to the start of the game
reduce combat difficulty by 2 levels", + description: "return to the start of the game", maxCount: 1, count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed: () => true, requires: "", effect() { - level.difficultyDecrease(simulation.difficultyMode * 2) level.onLevel = 0 simulation.clearNow = true //end current level }, @@ -9461,7 +9376,7 @@ const tech = { count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed: () => true, requires: "", effect() { @@ -9485,7 +9400,7 @@ const tech = { count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed: () => true, requires: "", effect() { @@ -9519,7 +9434,7 @@ const tech = { }, { name: "reinforcement learning", - description: "+1000% frequency of finding copies of current tech
", + description: `10x frequency for current ${powerUps.orb.tech()}`, maxCount: 1, count: 0, frequency: 1, @@ -9548,13 +9463,13 @@ const tech = { count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed: () => true, requires: "", effect() { // tech.damage *= 1.33 setInterval(() => { - if (powerUps.boost.endCycle < m.cycle && !simulation.paused && m.alive) { + if (powerUps.boost.endCycle < simulation.cycle && !simulation.paused && m.alive) { for (let i = 0; i < mob.length; i++) { if (mob[i].distanceToPlayer2() < 400000) { //650 canvas.requestPointerLock(); @@ -9569,12 +9484,12 @@ const tech = { }, { name: "closed timelike curve", - description: "spawn 5 field power ups, but every 12 seconds
teleport a second into your future or past", + description: `spawn ${powerUps.orb.field()}${powerUps.orb.field()}${powerUps.orb.field()}${powerUps.orb.field()}${powerUps.orb.field()}, but every 12 seconds
teleport a second into your future or past`, maxCount: 1, count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed: () => true, requires: "", effect() { @@ -9586,7 +9501,7 @@ const tech = { requestAnimationFrame(() => { if ((simulation.cycle % 1440) > 720) { //kinda alternate between each option m.rewind(60) - m.energy += 0.4 //to make up for lost energy + m.energy += 0.4 * level.isReducedRegen//to make up for lost energy } else { simulation.timePlayerSkip(60) } @@ -9606,7 +9521,7 @@ const tech = { // count: 0, // frequency: 0, // isJunk: true, - // isNonRefundable: true, + // isInstant: true, // allowed() { // return true // }, @@ -9646,18 +9561,24 @@ const tech = { // }, { name: "discount", - description: "get 3 random JUNKtech for the price of 1!", + description: `get 3 random JUNK${powerUps.orb.tech()} for the price of 1!`, maxCount: 1, count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed: () => true, requires: "", effect() { - tech.giveRandomJUNK() - tech.giveRandomJUNK() - tech.giveRandomJUNK() + for (let i = 0; i < 3; i++) { + const list = [] + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].isJunk) list.push(tech.tech[i].name) + } + let name = list[Math.floor(Math.random() * list.length)] + simulation.inGameConsole(`tech.giveTech("${name}")`); + tech.giveTech(name) + } }, remove() { } }, @@ -9667,7 +9588,7 @@ const tech = { // maxCount: 1, // count: 0, // frequency: 0, - // isNonRefundable: true, + // isInstant: true, // isJunk: true, // allowed() { // return true @@ -9681,13 +9602,13 @@ const tech = { // }, { name: "Higgs phase transition", - description: "instantly spawn 5 tech, but add a chance to
remove everything with a 5 minute half-life", + description: `instantly spawn ${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}, but add a chance to
remove everything with a 5 minute half-life`, maxCount: 1, count: 0, frequency: 0, frequencyDefault: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed: () => true, requires: "", effect() { @@ -9721,7 +9642,7 @@ const tech = { frequency: 0, frequencyDefault: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed: () => true, requires: "", effect() { @@ -9731,18 +9652,12 @@ const tech = { mob[i].death(); } } - // for (let i = powerUp.length - 1; i > -1; i--) { - // if (powerUp[i].name !== "ammo") { - // Matter.Composite.remove(engine.world, powerUp[i]); - // powerUp.splice(i, 1); - // } - // } }, remove() { } }, { name: "brainstorm", - description: "the tech choice menu randomizes
every 0.5 seconds for 10 seconds", + description: `${powerUps.orb.tech()} choices randomize
every 0.5 seconds for 10 seconds`, maxCount: 1, count: 0, frequency: 0, @@ -9766,7 +9681,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return !tech.isFallingDamage && !tech.isOverHeal && !tech.isEnergyHealth @@ -9786,7 +9701,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - // isNonRefundable: true, + // isInstant: true, isJunk: true, allowed() { return true @@ -9817,7 +9732,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - // isNonRefundable: true, + // isInstant: true, isJunk: true, allowed() { return true @@ -9850,7 +9765,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -9984,7 +9899,7 @@ const tech = { count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed() { return true }, @@ -10060,7 +9975,7 @@ const tech = { count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed() { return true }, @@ -10179,7 +10094,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10199,7 +10114,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10226,26 +10141,45 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return m.fieldMode !== 8 && m.fieldMode !== 9 && !tech.isTokamak }, requires: "not pilot wave, tokamak, wormhole", effect() { - + m.throwBlock = m.throwSelf }, remove() { m.throwBlock = m.throwBlockDefault } }, + { + name: "stationary", + description: "thrown blocks can't move,
but somehow they still have momentum...", + maxCount: 1, + count: 0, + frequency: 0, + // isInstant: true, + isJunk: true, + allowed() { + return m.fieldMode !== 8 && m.fieldMode !== 9 && !tech.isTokamak + }, + requires: "not pilot wave, tokamak, wormhole", + effect() { + tech.isStaticBlock = true + }, + remove() { + tech.isStaticBlock = false + } + }, { name: "spinor", description: "the direction you aim is determined by your position", maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return !m.isShipMode @@ -10274,7 +10208,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true }, requires: "", @@ -10296,7 +10230,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return tech.deathSpawns === 0 @@ -10315,7 +10249,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10340,7 +10274,7 @@ const tech = { // count: 0, // frequency: 0, // isExperimentHide: true, - // isNonRefundable: true, + // isInstant: true, // isJunk: true, // allowed() { // return !m.isShipMode @@ -10385,11 +10319,11 @@ const tech = { }, { name: "disintegrated armament", - description: "spawn a gun
remove your active gun", + description: `spawn ${powerUps.orb.gun()}
remove your active ${powerUps.orb.gun()}`, maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return b.inventory.length > 0 @@ -10404,11 +10338,11 @@ const tech = { }, { name: "probability", - description: "increase the frequency
of one random tech by 100", + description: `100x frequency for
a random ${powerUps.orb.tech()}`, maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10435,11 +10369,11 @@ const tech = { }, { name: "encryption", - description: "secure tech information", + description: `secure information`, maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10469,7 +10403,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10489,7 +10423,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10498,7 +10432,7 @@ const tech = { effect() { setInterval(() => { let score = Math.ceil(1000 * Math.random() * Math.random() * Math.random() * Math.random() * Math.random()) - simulation.makeTextLog(`simulation.score = ${score.toFixed(0)}`); + simulation.inGameConsole(`simulation.score = ${score.toFixed(0)}`); }, 10000); //every 10 seconds }, remove() { } @@ -10509,7 +10443,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10528,7 +10462,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10545,7 +10479,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10575,35 +10509,29 @@ const tech = { }, { name: "repartitioning", - description: "set the frequency of finding normal tech to 0
spawn 5 tech", + description: `set the JUNK chance to 100%
spawn ${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}${powerUps.orb.tech()}`, maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isJunk) { - tech.tech[i].frequency = 2 - } else { - tech.tech[i].frequency = 0 - } - } + tech.addJunkTechToPool(1) for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); }, remove() { } }, { name: "defragment", - description: "set the frequency of finding JUNKtech to zero", + description: "set the frequency of finding JUNK to zero", maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10620,7 +10548,7 @@ const tech = { // maxCount: 9, // count: 0, // frequency: 0, - // isNonRefundable: true, + // isInstant: true, // isExperimentHide: true, // isJunk: true, // allowed() { @@ -10641,7 +10569,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10660,7 +10588,7 @@ const tech = { // maxCount: 1, // count: 0, // frequency: 0, - // isNonRefundable: true, + // isInstant: true, // isJunk: true, // allowed() { return true }, // requires: "", @@ -10724,7 +10652,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10742,7 +10670,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10759,7 +10687,7 @@ const tech = { maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10768,7 +10696,7 @@ const tech = { effect() { setInterval(() => { m.rewind(120) - m.energy += 0.4 + m.energy += 0.4 * level.isReducedRegen }, 10000); // for (let i = 0; i < 24; i++) { // setTimeout(() => { m.rewind(120) }, i * 5000); @@ -10782,7 +10710,7 @@ const tech = { maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10791,7 +10719,7 @@ const tech = { effect() { setInterval(() => { m.rewind(30) - m.energy += 0.2 + m.energy += 0.2 * level.isReducedRegen }, 4000); }, remove() { } @@ -10802,7 +10730,7 @@ const tech = { maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10826,7 +10754,7 @@ const tech = { maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10843,7 +10771,7 @@ const tech = { maxCount: 3, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10865,19 +10793,19 @@ const tech = { }, { name: "expert system", - description: "spawn a tech power up
+64% JUNK to tech pool", + description: `spawn ${powerUps.orb.tech()}
+50% JUNK choices`, maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { - return true + return tech.junkChance < 1 }, requires: "", effect() { powerUps.spawn(m.pos.x, m.pos.y, "tech"); - tech.addJunkTechToPool(0.64) + tech.addJunkTechToPool(0.5) }, remove() { } }, @@ -10887,7 +10815,7 @@ const tech = { maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10912,7 +10840,7 @@ const tech = { maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10937,7 +10865,7 @@ const tech = { maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -10961,24 +10889,25 @@ const tech = { remove() { } }, { - name: "stubs", + name: "wall jump", description: "no knees or toes are drawn on the player
you can wall climb though", maxCount: 1, count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed() { return !m.isShipMode }, requires: "", effect() { m.skin.stubs() - Matter.Body.scale(player.parts[3], 2, 2); + jumpSensor.vertices[0].x += -22 + jumpSensor.vertices[3].x += -22 + jumpSensor.vertices[1].x += 22 + jumpSensor.vertices[2].x += 22 }, - remove() { - // if (this.count) m.resetSkin(); - } + remove() { } }, { name: "Sleipnir", @@ -11058,7 +10987,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return !m.isShipMode @@ -11096,7 +11025,7 @@ const tech = { count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed() { return !m.isShipMode }, @@ -11115,7 +11044,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -11134,31 +11063,13 @@ const tech = { }, remove() { } }, - // { - // name: "microtransactions", - // description: `when you choose a tech you can
use ${powerUps.orb.research(1)} to buy a free in game skin`, - // maxCount: 1, - // count: 0, - // frequency: 0, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // tech.isMicroTransactions = true - // }, - // remove() { - // tech.isMicroTransactions = false - // } - // }, { name: "ship", - description: "fly around with no legs
reduce combat difficulty by 1 level", + description: "fly around with no legs", maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return !m.isShipMode && !m.isAltSkin && m.fieldUpgrades[m.fieldMode].name !== "negative mass" @@ -11167,7 +11078,6 @@ const tech = { effect() { m.isAltSkin = true m.shipMode() - level.difficultyDecrease(simulation.difficultyMode) //unlock relativistic rotation for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].name === "relativistic rotation") tech.tech[i].frequency = 10 @@ -11177,11 +11087,11 @@ const tech = { }, { name: "circular symmetry", - description: "turning the ship rotates the universe instead
+200% damage", + description: "turning the ship rotates the universe instead
2x damage", maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return m.isShipMode @@ -11212,7 +11122,7 @@ const tech = { count: 0, frequency: 0, isBotTech: true, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return b.totalBots() > 2 @@ -11268,30 +11178,13 @@ const tech = { }, remove() { } }, - { - name: "growth hacking", - description: "increase combat difficulty by 1 level", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - level.difficultyIncrease(simulation.difficultyMode) - }, - remove() { } - }, { name: "stun", description: "stun all mobs for up to 8 seconds", maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -11304,11 +11197,11 @@ const tech = { }, { name: "translucent", - description: "spawn 3 gun power ups
your bullets and bots are transparent", + description: `spawn ${powerUps.orb.gun()}${powerUps.orb.gun()}${powerUps.orb.gun()}
your bullets and bots are transparent`, maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -11330,13 +11223,30 @@ const tech = { }, remove() { } }, + { + name: "difficulty", + description: "spawn a power up that lets you
adjust the simulation difficulty parameters", + maxCount: 1, + count: 0, + frequency: 0, + isInstant: true, + isJunk: true, + allowed() { + return (level.levelsCleared < 5) + }, + requires: "before level 5", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "difficulty"); + }, + remove() { } + }, { name: "re-research", description: `eject all your ${powerUps.orb.research(1)}`, maxCount: 9, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return powerUps.research.count > 3 @@ -11354,7 +11264,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return powerUps.research.count > 3 @@ -11364,7 +11274,32 @@ const tech = { m.energy = 0 spawn.suckerBoss(m.pos.x, m.pos.y - 700) powerUps.research.changeRerolls(-4) - simulation.makeTextLog(`m.research --
${powerUps.research.count}`) + simulation.inGameConsole(`m.research --
${powerUps.research.count}`) + }, + remove() { } + }, + { + name: "apomixis", + description: `spawn 11 bosses`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isInstant: true, + isJunk: true, + allowed() { + return tech.duplicationChance() > 0.99 + }, + requires: "duplication chance above 99%", + effect() { + const range = 1300 + for (let i = 0, len = 9; i < len; i++) { + const angle = 2 * Math.PI * i / len + spawn.randomLevelBoss(m.pos.x + range * Math.cos(angle), m.pos.y + range * Math.sin(angle), spawn.nonCollideBossList); + } + spawn.historyBoss(0, 0) + spawn.pulsarBoss(level.exit.x, level.exit.y, 70, true) + spawn.blockBoss(level.enter.x, level.enter.y) }, remove() { } }, @@ -11377,7 +11312,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true }, requires: "", @@ -11387,7 +11322,7 @@ const tech = { for (let i = 0; i < 20; i++) { spawn[this.mobType](m.pos.x, m.pos.y - 700) } - simulation.makeTextLog(`spawn.${this.mobType}(x,y)`) + simulation.inGameConsole(`spawn.${this.mobType}(x,y)`) }, remove() { } @@ -11398,7 +11333,7 @@ const tech = { maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -11562,13 +11497,31 @@ const tech = { return text }, }, + { + name: "wikipedia", + description: `After you get ${powerUps.orb.tech()} you have 7 seconds to study for a quiz. If you ace the quiz you get ${powerUps.orb.research(4)}`, + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.isWiki = true; + }, + remove() { + tech.isWiki = false; + } + }, { name: "cosmogonic myth", description: `open a portal to a primordial version of reality
in 5 minutes close the portal, spawn 1 of each power up
`, maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -11603,7 +11556,7 @@ const tech = { }, { name: "beforeunload", - description: "75% of the time when you attempt to exit n-gon
you are prompted to cancel or continue.
Each time you cancel gain +25% damage.", + description: "75% of the time when you attempt to exit n-gon
you are prompted to cancel or continue.
Each time you cancel gain 1.25x damage.", maxCount: 1, count: 0, frequency: 1, @@ -11623,11 +11576,11 @@ const tech = { }, { name: "planetesimals", - description: `play planetesimals (an asteroids-like game)
clear levels in planetesimals to spawn tech
if you die in planetesimals you die in n-gon`, + description: `play planetesimals (an asteroids-like game)
clear levels in planetesimals to spawn ${powerUps.orb.tech()}
if you die in planetesimals you die in n-gon`, maxCount: 1, count: 0, frequency: 0, - isNonRefundable: true, + isInstant: true, isJunk: true, allowed() { return true @@ -11658,13 +11611,13 @@ const tech = { }, { name: "tinker", - description: "permanently unlock JUNKtech in experiment mode
this effect is stored for future visits", + description: `permanently unlock JUNK${powerUps.orb.tech()} in experiment mode
this effect is stored for future visits`, maxCount: 1, count: 0, frequency: 0, frequencyDefault: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed() { return !localSettings.isJunkExperiment }, @@ -11684,7 +11637,7 @@ const tech = { count: 0, frequency: 0, isJunk: true, - isNonRefundable: true, + isInstant: true, allowed: () => true, requires: "", effect() { @@ -11807,10 +11760,6 @@ const tech = { this.description = `null is open at level.final()
 ` } else { this.frequency += lore.techGoal * 2 - // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech - // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` - // } - // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() this.description = `uncaught error:
${Math.max(0, lore.techGoal - lore.techCount)} more required for access to null` } }, 1); @@ -11822,46 +11771,8 @@ const tech = { } } ], - // addLoreTechToPool() { //adds lore tech to tech pool - // if (!simulation.isCheating) { - // tech.tech.push({ - // name: `undefined`, - // description: `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`, - // maxCount: 1, - // count: 0, - // frequency: 2, - // isLore: true, - // isNonRefundable: true, - // isExperimentHide: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // setTimeout(() => { //a short delay, I can't remember why - // lore.techCount++ - // if (lore.techCount > lore.techGoal - 1) { - // // tech.removeLoreTechFromPool(); - // 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) - // } - // } else { - // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech - // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` - // } - // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() - // } - // }, 1); - // }, - // remove() {} - // }) - // } - // }, - // junk: [ - - // ], //variables use for gun tech upgrades - fireRate: null, + fireRate: 1, //initializes to 1 bulletSize: null, energySiphon: null, healSpawn: null, @@ -11875,7 +11786,6 @@ const tech = { isAcidDmg: null, isAnnihilation: null, largerHeals: null, - squirrelFx: null, isCrit: null, isLowHealthDmg: null, isLowHealthDefense: null, @@ -11914,7 +11824,6 @@ const tech = { isBlockStun: null, isStunField: null, isHarmDamage: null, - energyRegen: null, isVacuumBomb: null, renormalization: null, fragments: null, @@ -11940,7 +11849,6 @@ const tech = { isNailShot: null, slowFire: null, fastTime: null, - squirrelJump: null, isFastRadiation: null, isAmmoForGun: null, isRapidPulse: null, @@ -12012,11 +11920,13 @@ const tech = { historyLaser: null, isSpeedHarm: null, isSpeedDamage: null, + speedAdded: null, isTimeSkip: null, isCancelDuplication: null, duplication: null, isCancelRerolls: null, isCancelTech: null, + cancelTechCount: null, isBotDamage: null, isBanish: null, isMaxEnergyTech: null, @@ -12032,6 +11942,7 @@ const tech = { isAmmoFoamSize: null, isIceIX: null, isDupDamage: null, + isDupEnergy: null, isFireRateForGuns: null, cyclicImmunity: null, isTechDamage: null, @@ -12042,9 +11953,9 @@ const tech = { isExplodeRadio: null, isPauseSwitchField: null, isPauseEjectTech: null, + pauseEjectTech: null, isShieldPierce: null, isDuplicateMobs: null, - is100Duplicate: null, isDynamoBotUpgrade: null, isBlockPowerUps: null, isDamageAfterKillNoRegen: null, @@ -12052,15 +11963,6 @@ const tech = { isSwitchReality: null, isResearchReality: null, isAnthropicDamage: null, - isFlipFlop: null, - isFlipFlopHarm: null, - isFlipFlopOn: null, - // isFlipFlopLevelReset: null, - isFlipFlopDamage: null, - isFlipFlopEnergy: null, - isFlipFlopChoices: null, - isRelay: null, - relayIce: null, isMetaAnalysis: null, isFoamAttract: null, droneCycleReduction: null, @@ -12070,7 +11972,6 @@ const tech = { isDroneRespawn: null, deathSpawns: null, isMobBlockFling: null, - // blockingIce: null, isPhaseVelocity: null, waveBeamSpeed: null, wavePacketAmplitude: null, @@ -12078,13 +11979,18 @@ const tech = { iceIXOnDeath: null, wimpCount: null, isAddBlockMass: null, - isMACHO: null, - isHarmMACHO: null, + isDarkMatter: null, + isHarmDarkMatter: null, + isMoveDarkMatter: null, + isNotDarkMatter: null, isSneakAttack: null, isFallingDamage: null, harmonics: null, isStandingWaveExpand: null, isTokamak: null, + isTokamakHeal: null, + tokamakHealCount: null, + isTokamakFly: null, deflectEnergy: null, superBallDelay: null, isBlockExplode: null, @@ -12094,9 +12000,7 @@ const tech = { isDroneTeleport: null, isDroneFastLook: null, isBulletTeleport: null, - isResearchBoss: null, isJunkResearch: null, - junkResearchNumber: null, laserColor: null, laserColorAlpha: null, isLongitudinal: null, @@ -12136,6 +12040,7 @@ const tech = { isRewindField: null, isCrouchRegen: null, isAxion: null, + isDarkEnergy: null, isDarkStar: null, isWormholeMapIgnore: null, isLessDamageReduction: null, @@ -12148,15 +12053,10 @@ const tech = { isTimeCrystals: null, isGroundState: null, isRailGun: null, - // isGrapple: null, - // isImmuneGrapple: null, isDronesTravel: null, isTechDebt: null, isPlasmaBall: null, plasmaDischarge: null, - isFlipFlopHealth: null, - isRelayEnergy: null, - coyoteTime: null, missileFireCD: null, isBotField: null, isFoamBall: null, @@ -12166,7 +12066,7 @@ const tech = { isClusterExplode: null, isCircleExplode: null, isPetalsExplode: null, - deathSkipTime: null, + isVerlet: null, isIceMaxHealthLoss: null, isIceKill: null, isCritKill: null, @@ -12196,7 +12096,6 @@ const tech = { collidePowerUps: null, isDilate: null, isDiaphragm: null, - hardLanding: null, isNoGroundDamage: null, isSuperBounce: null, isDivisor: null, @@ -12206,7 +12105,6 @@ const tech = { isHealBrake: null, isMassProduction: null, isPrinter: null, - // isHookWire: null, isHookDefense: null, hookNails: null, isHarpoonDefense: null, @@ -12219,4 +12117,15 @@ const tech = { isDamageCooldownTime: null, isPowerUpDamage: null, isExitPrompt: null, + isResearchDamage: null, + interestRate: null, + isImmunityDamage: null, + isMobDeathImmunity: null, + isMaxHealthDefense: null, + noDefenseSettingDamage: null, + isMaxHealthDamage: null, + isEjectOld: null, + isWiki: null, + isStaticBlock: null, + isDamageFieldTech: null, } \ No newline at end of file diff --git a/style.css b/style.css index 216d5f92..965a2fbd 100644 --- a/style.css +++ b/style.css @@ -9,6 +9,7 @@ body { margin: 0; overflow: hidden; cursor: auto; + /* filter: grayscale(1); */ /* transition: background-color 0.2s ease-in-out; */ } @@ -184,17 +185,11 @@ summary { scrollbar-width: none; } - - - .choose-grid-no-images { border-radius: 8px; border: 10px solid #444; gap: 10px; background-color: #444; - /* padding: 10px 1px; */ - - position: absolute; top: 50%; left: 50%; @@ -212,6 +207,7 @@ summary { overflow: auto; -ms-overflow-style: none; scrollbar-width: none; + /* box-shadow: 7px 7px 6px rgba(0, 0, 0, 0.13); */ } @@ -223,9 +219,6 @@ summary { line-height: 160%; background-color: var(--card-color); font-size: 0.75em; - /* transform-style: preserve-3d; - transition: transform 2s; - transform: rotateX(180deg); */ } .choose-grid-module:hover { @@ -260,9 +253,7 @@ summary { overflow: auto; max-height: 100vh; -ms-overflow-style: none; - /* IE and Edge */ scrollbar-width: none; - /* Firefox */ padding: 1px; } @@ -277,15 +268,7 @@ summary { overflow: auto; max-height: 100vh; -ms-overflow-style: none; - /* IE and Edge */ scrollbar-width: none; - /* Firefox */ -} - -.pause-console { - padding: 10px; - margin: 5px; - border-radius: 10px; } #pause-grid-left::-webkit-scrollbar { @@ -303,12 +286,12 @@ summary { border: 1px solid #444; /* min-height: 88px; */ line-height: 100%; - background-color: #fff; + background-color: var(--card-color); } .sort-button { border: 1px #333 solid; - border-radius: 0.5em; + border-radius: 0.4em; background-color: #fff; font-size: 0.5em; /* padding: 0.3em; */ @@ -371,7 +354,6 @@ summary { .experiment-start-box { background-color: var(--card-color); - /* font-size: 1em; */ position: sticky; top: 0; z-index: 10; @@ -390,7 +372,8 @@ summary { border-top-right-radius: 0px; padding: 0.2em 0px; /* height: 210px; */ - box-shadow: 8px 8px 7px rgba(0, 0, 50, 0.15); + box-shadow: 8px 8px 6px rgba(0, 0, 50, 0.15); + /* opacity: 0.1; */ } .card-background { @@ -403,7 +386,7 @@ summary { } .card-text { - background-color: rgba(255, 255, 255, 1); + background-color: var(--card-color); padding: 0.5em 1em; border: 1px solid #444; margin-top: -1px; @@ -519,38 +502,6 @@ summary { border-radius: 5px; } -/* keeps 4 columns at 1440px */ -/* @media (1500px <= width < 1950px) { - .experiment-grid-module, .choose-grid-module, .pause-grid-module { - line-height: 150%; - font-size: 0.72em; - } - #experiment-grid, #choose-grid, .pause-grid{ - grid-template-columns: repeat(auto-fit, 360px); - } - .card-background{ - height:315px; - } - .card-text { - min-height: 82px; - } -} -@media (width < 1500px) { - .experiment-grid-module, .choose-grid-module, .pause-grid-module { - line-height: 143%; - font-size: 0.68em; - } - #experiment-grid, #choose-grid, .pause-grid{ - grid-template-columns: repeat(auto-fit, 340px); - } - .card-background{ - height:290px; - } - .card-text { - min-height: 75px; - } -} */ - .experiment-grid-module:hover .card-text { background-color: var(--hover-card-color); } @@ -610,15 +561,20 @@ summary { z-index: 12; font-size: 1.5em; transition: opacity 5s ease-in; + + /* border: 1.5px #333 solid; */ + /* border-radius: 8px; */ + /* background-color: #fff; */ } .details-div { padding: 10px; border-radius: 8px; border: 2px #333 solid; - background-color: #fff; + background-color: var(--card-color); } + #dmg { position: absolute; z-index: 2; @@ -695,77 +651,6 @@ summary { display: none; } -/* #damage-bar { - position: absolute; - top: 0px; - left: 15px; - height: 6px; - width: 0px; - transition: width 0.3s linear; - opacity: 1; - z-index: 2; - pointer-events: none; - background-color: #f03; - border: 1px solid #701; - display: none; -} - -#defense-bar { - position: absolute; - top: 9px; - left: 15px; - height: 5px; - width: 0px; - transition: width 0.3s linear; - opacity: 1; - z-index: 2; - pointer-events: none; - background-color: #fff; - border: 1px solid #333; - display: none; -} -#health { - position: absolute; - top: 16px; - left: 15px; - height: 20px; - width: 0px; - transition: width 1s ease-out; z-index: 2; - pointer-events: none; - background-color: #0fa; - border: 2px solid rgb(51, 162, 125); - border-radius: 8px; - display: none; -} -#health-bg { - position: absolute; - top: 18px; - left: 15px; - height: 20px; - width: 0px; - transition: width 1s ease-out; - background-color: #000; - opacity: 0.1; - z-index: 1; - pointer-events: none; - border-radius: 8px; - display: none; -} */ - -/* .low-health { - animation: blink 250ms infinite alternate; -} - -@keyframes blink { - from { - opacity: 1; - } - - to { - opacity: 0.6; - } -} */ - /* background on title page */ #fade-out { position: absolute; @@ -785,14 +670,12 @@ summary { z-index: 2; font-size: 23px; color: #222; - /* background-color: rgba(255, 255, 255, 0.4); */ background-color: rgba(255, 255, 255, 0.2); line-height: 120%; user-select: none; pointer-events: none; padding: 0px 5px 0px 5px; border-radius: 5px; - /*border: 2px solid rgba(0, 0, 0, 0.4);*/ } #field { @@ -804,31 +687,51 @@ summary { color: #000; text-align: right; opacity: 0.7; - /* line-height: 140%; */ background-color: rgba(190, 210, 245, 0.25); user-select: none; pointer-events: none; padding: 0px 5px 0px 5px; border-radius: 5px; - /*border: 2px solid rgba(0, 0, 0, 0.4);*/ } -#tech { +#right-HUD { position: absolute; top: 15px; right: 15px; z-index: 2; font-size: 20px; - color: #222; text-align: right; - opacity: 0.35; line-height: 120%; - background-color: rgba(255, 255, 255, 0.4); user-select: none; pointer-events: none; + /*border: 2px solid rgba(0, 0, 0, 0.4);*/ + color: #222; padding: 0px 5px 0px 5px; border-radius: 5px; - /*border: 2px solid rgba(0, 0, 0, 0.4);*/ + opacity: 0.35; + background-color: rgba(255, 255, 255, 0.4); +} + +#right-HUD-constraint { + position: absolute; + top: 12px; + right: 15px; + z-index: 2; + font-size: 20px; + text-align: right; + line-height: 120%; + user-select: none; + pointer-events: none; + padding: 5px 10px 5px 10px; + border-radius: 8px; + color: #624; + opacity: 0.35; + background-color: rgba(255, 215, 241, 0.4); + /* color: rgb(141, 23, 88); + opacity: 0.35; + background-color: rgba(141, 23, 88, 0.1); */ + /* font-weight: 400; */ + transition: all 0.5s linear; } #text-log { @@ -842,17 +745,15 @@ summary { font-size: 1.15em; color: #555; background-color: rgba(255, 255, 255, 0.5); - /* transition: opacity 0.15s; */ pointer-events: none; user-select: none; } -/* color for in game console output */ -/* .ammo-flash { - color: #f33; - transition: color 2s; -} */ - +.constraint { + color: rgb(141, 23, 88); + /* font-weight: 100; */ + /* text-decoration: underline; */ +} .color-text { color: #000; @@ -920,12 +821,81 @@ summary { color: #356; } +.color-choice { + display: inline-block; +} + +.color-choice span { + display: inline-block; + animation: bounce 3s infinite; + transform-origin: bottom; + color: #555; +} + +.color-choice span:nth-child(1) { + animation-delay: 0s; +} + +.color-choice span:nth-child(2) { + animation-delay: 1s; +} + +.color-choice span:nth-child(3) { + animation-delay: 2s; +} + +@keyframes bounce { + 0% { + transform: translateY(0); + } + + 15% { + transform: translateY(-2px); + color: #aaa; + } + + 30% { + transform: translateY(0); + } + + 100% { + transform: translateY(0); + } +} + +.color-invulnerable { + color: #fff; + text-shadow: 0px 0px 7px #000; +} + +.color-dark-matter { + color: #246; + text-shadow: 0px 0px 7px #246; +} + +.color-tokamak { + color: #5c3ab8; + font-weight: 100; + text-shadow: 0px -5px 9px rgb(255, 0, 204); +} + + + .color-dup { font-variant: small-caps; letter-spacing: 1px; text-shadow: 1.5px -1.5px hsla(243, 100%, 38%, 0.2); } +.color-remove { + text-decoration: line-through; + /* letter-spacing: 2px; */ + border-radius: 3px; + padding: 0px 3px; + background-color: hsl(255, 40%, 71%); + color: #fff; +} + .color-cloaked { letter-spacing: 2px; animation: cloak 6s linear infinite alternate; @@ -969,6 +939,11 @@ summary { text-shadow: 1px 0px 2px #234; } +.color-print { + color: #567; + font-family: 'Courier New', Courier, monospace; +} + .color-defense { background-color: hsla(227, 9%, 71%, 0.279); padding: 2px; @@ -977,9 +952,24 @@ summary { font-weight: 100; } +.color-bot { + font-variant: small-caps; + background-color: rgba(75, 125, 150, 0.06); + border: 0.1em solid #146; + color: #146; + padding: 0em 0.3em 0.08em 0.3em; + font-weight: 400; +} + +.no-box { + border: none; + background-color: transparent; + padding: 0; +} + .color-block { - background-color: rgba(0, 0, 0, 0.04); - border: 1px solid rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 0.1); + border: 0.1em solid #333; padding: 0.5px; font-weight: 100; } @@ -1021,13 +1011,22 @@ summary { } .circle-grid { - width: 1.35em; - height: 1.35em; + width: 1.32em; + height: 1.32em; border-radius: 50%; display: inline-block; margin-bottom: -0.3em; } +.circle-grid-instant { + width: 1.1em; + height: 1.1em; + border-radius: 50%; + border: 0.15em solid hsl(255, 100%, 71%); + display: inline-block; + margin-bottom: -0.35em; +} + .circle-grid-skin { width: 1.25em; height: 1.25em; @@ -1039,7 +1038,6 @@ summary { position: absolute; top: -0.05em; left: 0em; - margin-bottom: -0.3em; } .circle-grid-skin-eye { @@ -1052,7 +1050,6 @@ summary { position: absolute; top: 0.45em; left: 0.9em; - margin-bottom: -0.3em; } .junk { @@ -1090,7 +1087,7 @@ summary { background-color: #0d9; border: 0.05em #fff solid; opacity: 0.85; - margin-bottom: -3px; + margin-bottom: -2px; } .heal-circle-energy { @@ -1101,7 +1098,7 @@ summary { background-color: #ff0; border: 0.05em #000 solid; opacity: 0.85; - margin-bottom: -3px; + margin-bottom: -2px; } .coupling-circle { @@ -1155,6 +1152,44 @@ summary { background-color: #f7b; } +.color-speed { + display: inline-block; + transform: skew(-27deg); + /* font-weight: 400; */ + color: #038; +} + +.color-randomize { + display: inline-block; + animation: randomize 4s linear infinite alternate; + transform-origin: bottom; + color: #555; +} + +@keyframes randomize { + + 0%, + 100% { + font-family: Arial; + } + + 20% { + font-family: Times New Roman; + } + + 40% { + font-family: Verdana; + } + + 60% { + font-family: Georgia; + } + + 80% { + font-family: Courier New; + } +} + .alt { animation: alt 8s linear infinite alternate; font-weight: 400; @@ -1502,4 +1537,163 @@ summary { stroke-dasharray: 300; stroke-dashoffset: 300; animation: dash 2.5s ease-in forwards; +} + + + +.grid-container { + display: grid; + grid-template-columns: 0fr 5fr 0fr; + line-height: 150%; + background-color: #fff; + padding: 15px 0 14px 0; + border-radius: 5px; +} + +.far-right-column { + display: grid; + grid-template-rows: repeat(6, 1fr); + font-size: 2rem; + font-family: monospace; + align-self: center; + justify-self: center; + width: 22px; +} + +.far-right-column>div { + padding-top: 50px; +} + +#difficulty-slider { + margin-top: 2.3rem; + height: 26.3rem; + width: 2rem; + writing-mode: vertical-lr; + direction: ltr; +} + +.left-column { + grid-row: 1 / span 6 +} + +.right-column { + display: grid; + grid-template-rows: repeat(6, 1fr); + /* to adjust the width of this column edit the "gridTemplateColumns" in the difficulty power up js code*/ +} + +.row { + transition: opacity 0.4s ease-out; + /* border: 2px #444 solid; */ + /* margin-top: -2px; */ + font-size: 1.2rem; + padding: 10px; +} + +.pause-difficulty-row { + line-height: 130%; + font-size: 1em; + padding: 7px 0px 7px 0px; + /* top | right | bottom | left */ + margin: -5px 0; +} + +#constraint-1 { + background-color: hsl(240, 18%, 95%); + border-radius: 7px 7px 0 0; +} + +#constraint-2 { + background-color: hsl(240, 18%, 93%); +} + +#constraint-3 { + background-color: hsl(240, 18%, 89%); +} + +#constraint-4 { + background-color: hsl(240, 18%, 85%); +} + +#constraint-5 { + background-color: hsl(240, 18%, 81%); +} + +#constraint-6 { + background-color: hsl(240, 18%, 77%); + border-radius: 0 0 7px 7px; +} + +#constraint-1:hover { + background-color: hsl(240, 18%, 93%); + border: 1px #444 solid; + padding: 9px; +} + +#constraint-2:hover { + background-color: hsl(240, 18%, 91%); + border: 1px #444 solid; + padding: 9px; +} + +#constraint-3:hover { + background-color: hsl(240, 18%, 87%); + border: 1px #444 solid; + padding: 9px; +} + +#constraint-4:hover { + background-color: hsl(240, 18%, 83%); + border: 1px #444 solid; + padding: 9px; +} + +#constraint-5:hover { + background-color: hsl(240, 18%, 79%); + border: 1px #444 solid; + padding: 9px; +} + +#constraint-6:hover { + background-color: hsl(240, 18%, 75%); + border: 1px #444 solid; + padding: 9px; +} + +#choose-difficulty { + text-align: center; + font-size: 1.1em; + padding: 5px; + margin-top: 10px; + color: #333; + border-radius: 5px; +} + +.pause-details { + background-color: #e2e9ec; + border: 1px solid #333; + border-radius: 5px; + padding: 7px; + margin-bottom: 10px; + /* box-shadow: 4px 4px 4px hsla(240, 0%, 17%, 0.14); */ +} + +.tooltip {} + +.tooltip .tooltiptext { + visibility: hidden; + background-color: #fff; + border-radius: 0.3em; + padding: 0.2em 0.3em; + border: solid #000 1px; + + position: relative; + z-index: 1; + bottom: 100%; + left: 0%; + margin-left: -50%; +} + +.tooltip:hover .tooltiptext { + visibility: visible; } \ No newline at end of file diff --git a/todo.txt b/todo.txt index 651378c2..e90b56d7 100644 --- a/todo.txt +++ b/todo.txt @@ -1,40 +1,269 @@ ******************************************************** NEXT PATCH ************************************************** -new tech are sorted to the top of the tech list - pause menu tech list matches the tech list in game -JUNK tech icons are properly displayed in pause -Sort interface works with enter key -bug fix: you can no longer pause or enter testing in experiment selection on text input -pressing P in power up selection menu brings up the pause menu - so you can see your tech and stats -blocks stuck in vertical portals have a bit of randomness added to their velocity to help them escape - -JUNK tech chance is a raw percent chance to display a random JUNK - it was previously a specific JUNK tech directly added to the pool - this might cause bugs -JUNK tech: beforeunload - asks if you want to cancel if you exit game, if you cancel gain 25% damage, but there is a 25% chance to exit anyways -JUNK tech: what the block? - trying to throw a block, throws you instead -reinforcement learning converted into a JUNK tech -JUNK DNA - scale damage by 100->200% of JUNK pool tech percent -dark patterns 33->22% JUNK -replication 33->22% JUNK - -overcharge 66->88 max energy -residual dipolar coupling 6->8 coupling per cancel -futures exchange 4.7->5% duplication per cancel -hyperpolarization reduces polarization time by 1->1.25 seconds -reel +75->100 energy - -updated physics engine to matter.js 0.19 (from 0.18) +tech: hidden-variable theory - after choosing a fieldtech gain 1.15x damage + for pilot wave only +polariton - boosts also give 0.3x damage taken + +constraints + removed + full damage taken after boss dies + wording is too unclear + new + 0.5x energy regen from all sources + balanced + slow bots + bots have roughly 15% reduction in damage in addition to a slow follow speed + mob death heals mobs + has 1000->700 range and 1->0.33 healing + periodically spawn WIMPs + has a 30s delay and a 15->6s spawn rate + 50->40% JUNK chance + + +heuristics gives (1-1.5x)->(1-2x) fire rate and +5% JUNK +autonomous defense harpoon now scale from Bessemer process + but at half the rate since there are 6 harpoons +Bessemer process and rail gun scale at 0.1->0.07 + +bugs + crash with training level "heal" and power ups + set difficulty mode level 2 for training -*********************************************************** TODO ***************************************************** +******************************************************** BUGS ******************************************************** +figure out why seeded random isn't making runs the same: + shuffle is being used for a wide variety of things that don't need a seeded random + make two shuffle functions? + check which of these is working + level order + is flipped + constraint order + mob type order + boss order + gun, field, tech options + make field options offered precalculated so it doesn't depend on player choices? + generate all constraints at the start of the game + doesn't seem to be determined by the seed... + display future constraints in pause menu? + +at start of level game go stuck in pause state + no crash or bug report + occur level 8 + after 2 levels of sending power ups to next level + auto drones grabbed a power up and game froze while in power up mode + running code to exit pause kinda worked + +ants marching outline on splash screen doesn't sync right on safari -List of ways to break the game - CPT + high energy regen - research->bot fabrication-> ersatz bots -> various bot upgrades - duplication 100% - electronegativity and high energy? +player can become crouched while not touching the ground if they exit the ground while crouched + +*********************************************************** TODO ***************************************************** + +procedural animation + https://www.youtube.com/watch?v=qlfh_rv6khY + +maybe no constraints on final boss and reactor? + +constraints balance + 50% JUNK chance + 4x shielded mob chance + power ups are sent to next level + +33% chance for mobs to respawn + -1 choice + 2x ammo costs + maybe nerf... + 0 duplication + 50% max energy + 50% max health + bots follow slow + 0.1x damage after a power up + mob death heals mobs + mobs heal for your lost health + periodically spawn WIMPs + 0.5x energy regen + +each difficulty setting adds a chance for a random effect + make some effects only possible on certain levels, or with certain bosses? + not implemented random constraint ideas________________________ + mob death spawns something + mob bullets + bosses heals nearby mobs + ammo power ups give 0.7x ammo + 2x research costs + mobs slowly regen health + exit door takes 10x time to open, + and while door is opening (ghosters, suckers) attack? + can't pause while choosing tech, gun, field similar to eternalism + can't have more then 15 bullets + bots do 0.5x damage + remove 2 random tech and return them next level + too niche + player damage is 0.25x while player is invulnerable + all hazards: lasers and slime do 3x damage + player lasers and radiation do 0.5x damage + explosions do 0.5x damage + freeze effects last 0.25x time + +tech: - lower damage taken over 10s to 0x but after taking damage increase damage taken to 1x + isn't this just CPT skin with less steps? + maybe skin + +tech: - freezing grenades/explosions + +tech: - randomize constraints somehow + in pause interface or power up selection menu? + each time you research the current constraints also randomize? + only allowed if difficulty is high enough + +tech: - when you get a bot, get a second bot + +tech: - boost power ups also give 0.1x damage taken + +tech: - tech have +3 choices to eject your tech + tech name + no description? + +copy negative-player as a boss? or a JUNK tech? + code is in community map (not sure which) + +harpoon tech: - after firing 4 harpoons your next harpoon has 4x damage + need to track number fired + +tech: - after killing a Boss + heal to full + gain 3x damage for the rest of the level + +tech: clicking on this tech in the pause menu will teleport you to the next level + +tech: - instead of reeling in grappling hook teleport to the hook after releasing field button + this might need another buff? + give damage immunity after teleport for 1+ seconds to balance + +new level - rework testChamber + +skin with wheel instead of legs + +Boss (or mob) that quickly moves towards player, but they moves perpendicularly to player, like dodging + could respond to when player presses fire key or to when it takes damage + +new snakeBoss type that eats mobs + each time it eats: + heal? + get shield if full? + make boss longer (if snake) + get shield + get orbitals + spawn spawns (little red square mobs) + when boss dies + spawn eaten mobs? + spawn generic mobs? + nothing + make snake new colors, width + +Boss mob - takes a snapshot of the positions of all mobs, player, blocks, power ups. Then 3 seconds later it teleports everything back to those spots. + after snap shot is stored draw outline of body positions for a second to show the change + immune after snapshot? or immune after teleport? +Boss mob - records the position of mobs every few cycles + makes a ghost copy of the mob that is delayed by a few seconds + ghost can damage player, but doesn't have a matter.js object + gives mobs short snake tails, like snakeBoss + brings 1 mob back to life every few seconds + +tech: atomic pile - lose 1 health if you are above the maximum energy + generate energy for each nearby mob? + do damage? + plasma torch tech? + +field tech: molecular assembler - every time you spawn a drone/spore/... become immune to damage for time + scales with how much energy was used to spawn drone/... + +figure out how to put instructions for controls in background on initial level + mouse smooth makes the text position jitter when it moves sub pixels + hide the jitter with artificial jitter to make it seem intentional + make it look like the instructions are on a fuzzy TV screen + when player presses move buttons highlight the box/letter for those buttons + +make player mass an adjustable var in the skin + does this mess with jump height or air control? +increase mass and movement speed at the same time + increase jump differently because it scales extra with mass + m.defaultMass = 4.5 + m.definePlayerMass() + possible player.mass bad interactions + grapple + +JUNK tech - player takes damage from block collisions + is this gonna contribute to lag? + +bullets should trigger shrinking platforms level element? + +level element - player activated elevators + could be fast and throw player + could just rise up slow (slow might have a bad jerky animation) + +rework energy and health HUD + make both diegetic? + how? not sure there is a good way to do this... + should health be red or green? + +flip player upside down + how + rotate player in matter.js + make sure floor sensor works + flip player crouch direction + redraw legs, orb + flip gravity + when to use? + fieldTech: negative mass? + effect in level + +tech - after a power up is duplicated + update text to random effect after choosing tech, or after each trigger, or on first display of tech + pick 1 effect at random + become immune to damage taken for 5 seconds + summon JUNK bots for 10 seconds + 2x current energy + gain 1.01x damage permanently + cool name: + +field tech: negative mass - quickly pull/teleport in all nearby blocks and then fire them away from player + how does player triggers effect? + picking up a block pulls in all nearby blocks, throwing block fires all nearby blocks + taking damage + auto aim 50% of blocks at mobs + +after picking up heals gain ____ + 0.1x damage taken for 12s +after picking up ammo gain ____ + 4x fire rate for 12s +after picking up research gain ____ + +10 choices for 12s + +tech: Energy generation increases with you velocity + Newtons' 3rd law? + +tech - getting caught in an explosion gives you _____ + damage for 10 seconds? + heals + +tech - limit total number of tech to like 10? + +tech - destroying blocks gives _____ + +removeJunkTechFromPool doesn't seem to remove the correct amount + +wormholes that end inside wall check to see if the space before the wall is safe and end there instead + can use laser code + +tech: - (1 + 0.01)x for each tech, gun, field you've picked up + scales with born rule, duplication, remove tech + +explosions have a chance to spawn spores + infinite feedback loop with spores that explode? + +make sure healing isn't effected by simulation.healScale + instead heal orb size should be scaled + but the ratio between size and heal shouldn't be effected boss - tracks the position, velocity, angle of power ups, blocks, and bullets it fires reactor only? @@ -58,10 +287,6 @@ tech - getting a new gun also gives you 2 random tech for that gun or a field? can these guntech tech be converted into a player choice? -tech: interest - research and ammo increases by 10% at the start of each level? - spawn research and ammo at start of new level - extend to current health? - how to reduce the number of clicks and keypresses auto fire mode player shoots at whatever is nearby @@ -370,7 +595,7 @@ add small SVG pics to in-game console when? one for each field? use stuff on physics notes: -simulation.makeTextLog(` +simulation.inGameConsole(` @@ -691,11 +916,6 @@ intro map: diegeticly draw a mouse with field highlighted also indicate space? dynamically adjust drawing after picking up a gun -increase mass and movement speed at the same time - increase jump differently because it scales extra with mass - m.defaultMass = 4.5 - m.definePlayerMass() - give history boss legs? field tech - disable blocking, but does high damage to mobs inside field @@ -881,77 +1101,6 @@ n-gon outreach ideas hacker news - show hacker news post twitch - lets play -******************************************************** BUGS ******************************************************** - -bug: maybe I can put in an event listener to reset inputs to false when you tab out to prevent key sticking - -bug - url sharing still broken sometimes - -tech upgrade to anthropic principle to make it trigger at 50% life and 0% once per map - -bug? cloaking field doesn't show energy over max - -run more profiles of n-gon to fix performance issues - -bug - death while paused crashes game? - -bug: possibly clearing away all bullets causes a problem - bullet.js 255 (.do() is missing) - I died and quantum immortality triggered (I had needles and ice-IX) - game crashed but recovered - -vanish element bug, crashes on touching element, happens for 1 person maybe with junk tech? - -safari issues - once: can't pick up blocks - fixed on new map - cloaking field - once: after damage, locked into slow time mode - fixed on damage - 3 times player head graphics not rotating - left/right leg flip broke - walk leg direction, legs are walking backwards - happened maybe after power up selection menu?? - cloaking field(at least once) - aiming still works - fixed on new map, although flip still broken (is flip a separate issue?) - flip fixed on new game - -sharing builds as html doesn't work for long lists... - it shouldn't be sharing undefined at all - probably some other problems too - (this might be fixed...) - -blocks on buttons teleport into the button endlessly if they are being slowly floated away - maybe add a cooldown? - can't reproduce - -ants marching outline doesn't sync right on safari anymore. - -door to exit in level: vats does nothing - did I do that? - -death while in power up selection menu doesn't reset properly - of course it's not possible to die in this menu unless you use testing and shift+X - -player can become crouched while not touching the ground if they exit the ground while crouched - -a couple times people have reported the final boss dropping extra bodies on death - -blue triangle boss can move backwards and aim away from you if set up properly - issues with dot product probably, but might not be worth fixing - -mouse event e.which is deprecated - -fix door.isClosing actually meaning isClosed? - -make it so that when you are immune to harm you can either jump on mobs or you pass through them - -is there a way to check if the player is stuck inside the map or block - trigger a short term non-collide if that occurs - -(intermittent, but almost every time) bug - capping the fps causes random slow downs, that can be fixed with pause - ******************************************************** LEVELS ******************************************************** map: observatory @@ -1099,67 +1248,7 @@ add sounds // tone(445.50) // tone(495) - -******************************************************** LORE ******************************************************** - -look up sci-fi science ideas here https://projectrho.com/public_html/rocket/ - -possible names for tech - sidereal - with respect to the stars (an extra rotation for time keeping) - holonomy - parallel transport of a vector leads to movement (applies to curved space) - holographic - 2-D surface can predict the 3-D space behind it? I think - hypergolic - A hypergolic propellant combination used in a rocket engine is one whose components spontaneously ignite when they come into contact with each other. - swarm intelligence - for a drone tech - genetic algorithm - metaheuristic - is a higher-level procedure or heuristic designed to find, generate, or select a heuristic (partial search algorithm) that may provide a sufficiently good solution to an optimization problem, especially with incomplete or imperfect information or limited computation capacity - stochastic optimization - electrostatic discharge - Gödel's incompleteness - quantum zeno effect (perturbation of a system prevents some systems from evolving because it scrambles coherence) (apply to lasers, fields) - counterfactual - something false - regression to the mean - phlogiston theory is a superseded scientific theory that postulated the existence of a fire-like element called phlogiston - Laplace's demon was a notable published articulation of causal determinism on a scientific basis by Pierre-Simon Laplace in 1814.[1] According to determinism, if someone (the demon) knows the precise location and momentum of every atom in the universe, their past and future values for any given time are entailed; they can be calculated from the laws of classical mechanics. - evolutionary cosmology - eternal inflation - hypergraph - SQUID (for superconducting quantum interference device) is a very sensitive magnetometer used to measure extremely subtle magnetic fields, based on superconducting loops containing Josephson junctions. - Josephson junction - superconducting junction used in SQUIDS and quantum computers - nuclear pasta - hard matter in neutron star - nonlocal, nonlocality: maybe use for pilot wave - fine-tuned universe - nonperturbative - D-branes - instanton or pseudoparticle - soliton (wave packet) - hall effect thrusters - spaghettification - particle accelerator - superluminal signalling - NP-complete - lenticular lens: is an array of lenses, designed so that when viewed from slightly different angles, different parts of the image underneath are shown. - p-hacking JUNK tech - https://en.wikipedia.org/wiki/High-entropy_alloys high yield strength and low ductility, high temp resistance - https://en.wikipedia.org/wiki/Refractory_metals hard, high temp resistance - https://en.wikipedia.org/wiki/Upper-atmospheric_lightning#Elves - prion quine - self replicating protein - Unitarity - https://en.wikipedia.org/wiki/Unitarity_(physics) - all probabilities add up to 1, calculations work the same forward and backwards in time - this is violated by expansion of the universe - https://en.wikipedia.org/wiki/Cosmic_censorship_hypothesis - black holes can't leak - Alcubierre warp drive (FTL with negative mass) - Spherules - A spherule is a small sphere or spherical body. It can also refer to a thick-walled spherical structure that contains endospores and occurs in the parasitic form of fungi - negative entropy - memetics - magnetorquers - produce spin by pushing on earth's magnetic field - Josephson junction - superconducting junction - Pyroelectricity - voltage from temp changes - upgrade from piezoelectricity - Unruh effect - accelerating makes heat/thermal particles - configuration space - holds the position of everything - stress–energy tensor - radioisotope thermoelectric generator - - - -******************************************************** CARS IMAGES ******************************************************** +******************************************************** IMAGES ******************************************************** process: discord midjourney prompts -> "pixelmator pro" adjust color, repair, scale to 384x256, export PNG -> webP? -> place in /img folder make n-gon a progressive web app to manage image downloads, cache @@ -1226,5 +1315,104 @@ if pause is pressed while selecting power ups, display pause menu on top of sele boost, coupling power ups tech - cyan electron orbiting a black nucleus electric field as bas-relief //(by Kazumasa Nagai) radioactive - volumetric atomic nucleus diagram by Paul Catherall +******************************************************** LORE ******************************************************** +look up sci-fi science ideas here https://projectrho.com/public_html/rocket/ +possible names for tech + sidereal - with respect to the stars (an extra rotation for time keeping) + holonomy - parallel transport of a vector leads to movement (applies to curved space) + holographic - 2-D surface can predict the 3-D space behind it? I think + entropic gravity - gravity is emergent in a holographic way + (maybe a field tech for negative mass) + conformal - similar rules for small and big scales linked to holographic principle + hypergolic - A hypergolic propellant combination used in a rocket engine is one whose components spontaneously ignite when they come into contact with each other. + swarm intelligence - for a drone tech + metaheuristic - is a higher-level procedure or heuristic designed to find, generate, or select a heuristic (partial search algorithm) that may provide a sufficiently good solution to an optimization problem, especially with incomplete or imperfect information or limited computation capacity + stochastic optimization + electrostatic discharge + Gödel's incompleteness + quantum zeno effect (perturbation of a system prevents some systems from evolving because it scrambles coherence) (apply to lasers, fields) + counterfactual - something false + regression to the mean + phlogiston theory is a superseded scientific theory that postulated the existence of a fire-like element called phlogiston + Laplace's demon was a notable published articulation of causal determinism on a scientific basis by Pierre-Simon Laplace in 1814.[1] According to determinism, if someone (the demon) knows the precise location and momentum of every atom in the universe, their past and future values for any given time are entailed; they can be calculated from the laws of classical mechanics. + evolutionary cosmology + eternal inflation + hypergraph + SQUID (for superconducting quantum interference device) is a very sensitive magnetometer used to measure extremely subtle magnetic fields, based on superconducting loops containing Josephson junctions. + Josephson junction - superconducting junction used in SQUIDS and quantum computers + nuclear pasta - hard matter in neutron star + nonlocal, nonlocality: maybe use for pilot wave + fine-tuned universe + nonperturbative + D-branes + instanton or pseudoparticle + soliton (wave packet) + hall effect thrusters + spaghettification + particle accelerator + superluminal signalling + NP-complete + lenticular lens: is an array of lenses, designed so that when viewed from slightly different angles, different parts of the image underneath are shown. + p-hacking JUNK tech + https://en.wikipedia.org/wiki/High-entropy_alloys high yield strength and low ductility, high temp resistance + https://en.wikipedia.org/wiki/Refractory_metals hard, high temp resistance + https://en.wikipedia.org/wiki/Upper-atmospheric_lightning#Elves + prion quine - self replicating protein + Unitarity - https://en.wikipedia.org/wiki/Unitarity_(physics) - all probabilities add up to 1, calculations work the same forward and backwards in time + this is violated by expansion of the universe + https://en.wikipedia.org/wiki/Cosmic_censorship_hypothesis - black holes can't leak + Alcubierre warp drive (FTL with negative mass) + Spherules - A spherule is a small sphere or spherical body. It can also refer to a thick-walled spherical structure that contains endospores and occurs in the parasitic form of fungi + negative entropy + memetics + magnetorquers - produce spin by pushing on earth's magnetic field + Josephson junction - superconducting junction + Pyroelectricity - voltage from temp changes - upgrade from piezoelectricity + Unruh effect - accelerating makes heat/thermal particles + configuration space - holds the position of everything (related to fermions/bosons and particle interactions) + stress–energy tensor + radioisotope thermoelectric generator - + retrovirus: these things make JUNK DNA so link it somehow to that tech? + Upon infection with a retrovirus, a cell converts the retroviral RNA into DNA and sometimes the DNA will be passed on to progeny as JUNK DNA + amalgam, amalgamation - the action, process, or result of combining or uniting. + thermoplastic - the stuff in 3-D printers, use for molecular assembler tech + ergosphere - region of a spinning black hole that might allow FTL or alternate realities. + equivalence principle - gravity and acceleration are the same + Casimir effect - attractive force between two close conductive plates + difference engine - early calculator/computer + cyanoacrylate - superglue use for a slowing effect? + superposition - something with waves overlapping + math terms - integral, derivative, Laplace transform, inflection point + quasicrystals - something with low friction, maybe defense? + Coalescence - things merging together like clouds. maybe mergin power ups? + trihydrogen cation - common molecule in space, dark matter tech? + superradiance - laser tech + cryocoolers - freezing effects + metaphysics - maybe this changes something deep and universal about physics? not sure + cork - used as a heat shield for rockets + +******************************************************* DESIGN ****************************************************** + +priorities + synergies between tech + difficult to achieve synergies that feel so powerful they are game breaking / changing + randomized content that adds repeatability + bosses, mobs, levels, tech + graphical indicators of tech effects and quantity + subtle lore woven into unexpected places + +add more randomize sub level map content + left/right sides of lock + small lab rooms + +list of powerful synergies + CPT + high energy regen + research + bot fabrication + ersatz bots + various bot upgrades + harpoon + high fire rate + alternator + time dilation + duplication 100% + interest + coupling, research + (peer review? or Bayesian statistics) + electronegativity and high energy? + electronegativity + anyon + duplication + Maxwells demon + interest + pair production + chain reaction + invulnerable + Abelian group + parasitism = clear all mobs on level