diff --git a/.DS_Store b/.DS_Store
index 6ef377d..7a51ec2 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/js/bullet.js b/js/bullet.js
index b4403ea..68d2462 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -35,7 +35,7 @@ const b = {
if (m.health > 0.05) {
m.damage(0.05 / m.harmReduction()); // /m.harmReduction() undoes damage increase from difficulty
if (!(tech.isRewindAvoidDeath && m.energy > 0.66)) { //don't give ammo if CPT triggered
- for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x, m.pos.y, "ammo");
+ for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x, m.pos.y, "ammo");
}
}
} else {
@@ -66,7 +66,7 @@ const b = {
if (m.health > 0.05) {
m.damage(0.05 / m.harmReduction()); // /m.harmReduction() undoes damage increase from difficulty
if (!(tech.isRewindAvoidDeath && m.energy > 0.66)) { //don't give ammo if CPT triggered
- for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x, m.pos.y, "ammo");
+ for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x, m.pos.y, "ammo");
}
}
} else {
@@ -98,7 +98,7 @@ const b = {
if (m.health > 0.05) {
m.damage(0.05 / m.harmReduction()); // /m.harmReduction() undoes damage increase from difficulty
if (!(tech.isRewindAvoidDeath && m.energy > 0.66)) { //don't give ammo if CPT triggered
- for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x, m.pos.y, "ammo");
+ for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x, m.pos.y, "ammo");
}
}
} else {
@@ -2037,7 +2037,7 @@ const b = {
restitution: 1,
dmg: 0.24, //damage done in addition to the damage from momentum
lookFrequency: 80 + Math.floor(23 * Math.random()),
- endCycle: simulation.cycle + Math.floor((1100 + 420 * Math.random()) * tech.isBulletsLastLonger),
+ endCycle: simulation.cycle + Math.floor((1100 + 420 * Math.random()) * tech.isBulletsLastLonger * tech.droneCycleReduction),
classType: "bullet",
collisionFilter: {
category: cat.bullet,
@@ -2109,7 +2109,7 @@ const b = {
powerUp.splice(i, 1);
if (tech.isDroneGrab) {
this.isImproved = true;
- const SCALE = 3
+ const SCALE = 2.5
Matter.Body.scale(this, SCALE, SCALE);
this.lookFrequency = 30;
this.endCycle += 2500
@@ -3214,8 +3214,6 @@ const b = {
this.baseFire(m.angle + (Math.random() - 0.5) * (Math.random() - 0.5) * (m.crouch ? 1.35 : 3.2) / CD)
},
fireNeedles() {
- m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 33 : 16) * b.fireCD); // cool down
-
function makeNeedle(angle = m.angle) {
const me = bullet.length;
bullet[me] = Bodies.rectangle(m.pos.x + 40 * Math.cos(m.angle), m.pos.y + 40 * Math.sin(m.angle), 50, 1, b.fireAttributes(angle));
@@ -3226,34 +3224,36 @@ const b = {
bullet[me].do = function() {
const whom = Matter.Query.collides(this, mob)
if (whom.length && this.speed > 20) { //if touching a mob
- who = whom[whom.length - 1].bodyA
- if (who && who.mob) {
- let immune = false
- for (let i = 0; i < this.immuneList.length; i++) {
- if (this.immuneList[i] === who.id) {
- immune = true
- break
+ for (let i = 0, len = whom.length; i < len; i++) {
+ who = whom[i].bodyA
+ if (who && who.mob) {
+ let immune = false
+ for (let i = 0; i < this.immuneList.length; i++) { //check if this needle has hit this mob already
+ if (this.immuneList[i] === who.id) {
+ immune = true
+ break
+ }
}
- }
- if (!immune) {
- if (tech.isNailCrit && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.975) {
- b.explosion(this.position, 220 + 30 * Math.random()); //makes bullet do explosive damage at end
- }
- this.immuneList.push(who.id)
- who.foundPlayer();
- if (tech.isNailRadiation) {
- mobs.statusDoT(who, tech.isFastRadiation ? 8 : 2, tech.isSlowRadiation ? 240 : (tech.isFastRadiation ? 30 : 120)) // one tick every 30 cycles
- } else {
- let dmg = b.dmgScale * 3.25
- if (tech.isCrit && who.isStunned) dmg *= 4
- who.damage(dmg, tech.isNeedleShieldPierce);
- simulation.drawList.push({ //add dmg to draw queue
- x: this.position.x,
- y: this.position.y,
- radius: Math.log(2 * dmg + 1.1) * 40,
- color: simulation.playerDmgColor,
- time: simulation.drawTime
- });
+ if (!immune) {
+ if (tech.isNailCrit && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.975) {
+ b.explosion(this.position, 220 + 30 * Math.random()); //makes bullet do explosive damage at end
+ }
+ this.immuneList.push(who.id) //remember that this needle has hit this mob once already
+ who.foundPlayer();
+ if (tech.isNailRadiation) {
+ mobs.statusDoT(who, tech.isFastRadiation ? 9 : 2.25, tech.isSlowRadiation ? 240 : (tech.isFastRadiation ? 30 : 120)) // one tick every 30 cycles
+ } else {
+ let dmg = b.dmgScale * 3.5
+ if (tech.isCrit && who.isStunned) dmg *= 4
+ who.damage(dmg, tech.isNeedleShieldPierce);
+ simulation.drawList.push({ //add dmg to draw queue
+ x: this.position.x,
+ y: this.position.y,
+ radius: Math.log(2 * dmg + 1.1) * 40,
+ color: simulation.playerDmgColor,
+ time: simulation.drawTime
+ });
+ }
}
}
}
@@ -3277,10 +3277,26 @@ const b = {
Matter.Body.setDensity(bullet[me], 0.00001);
World.add(engine.world, bullet[me]); //add bullet to world
}
- const spread = (m.crouch ? 0.013 : 0.06)
- makeNeedle(m.angle + spread)
- makeNeedle()
- makeNeedle(m.angle - spread)
+
+ if (m.crouch) {
+ m.fireCDcycle = m.cycle + 50 * b.fireCD; // cool down
+ makeNeedle()
+ for (let i = 1; i < 4; i++) { //4 total needles
+ setTimeout(() => { if (!simulation.paused) makeNeedle() }, 40 * i);
+ }
+ } else {
+ m.fireCDcycle = m.cycle + 30 * b.fireCD; // cool down
+ makeNeedle()
+ for (let i = 1; i < 3; i++) { //3 total needles
+ setTimeout(() => { if (!simulation.paused) makeNeedle() }, 40 * i);
+ }
+ }
+
+
+ // const spread = (m.crouch ? 0.013 : 0.06)
+ // makeNeedle(m.angle + spread)
+ // makeNeedle()
+ // makeNeedle(m.angle - spread)
},
fireRivets() {
m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 30 : 25) * b.fireCD); // cool down
@@ -3553,8 +3569,7 @@ const b = {
}
}
}
- },
- {
+ }, {
name: "super balls",
description: "fire four balls in a wide arc
balls bounce with no momentum loss",
ammo: 0,
@@ -3618,8 +3633,7 @@ const b = {
}
}
}
- },
- {
+ }, {
name: "wave beam",
description: "emit a sine wave of oscillating particles
propagates through walls",
ammo: 0,
@@ -3729,8 +3743,7 @@ const b = {
const transverse = Vector.normalise(Vector.perp(bullet[me].velocity))
}
}
- },
- {
+ }, {
name: "missiles",
description: "launch homing missiles that explode
crouch to rapidly launch smaller missiles",
ammo: 0,
@@ -3846,8 +3859,7 @@ const b = {
// }
}
- },
- {
+ }, {
name: "grenades",
description: "lob a single bouncy projectile
explodes on contact or after one second",
ammo: 0,
@@ -3857,8 +3869,7 @@ const b = {
m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 40 : 30) * b.fireCD); // cool down
b.grenade()
},
- },
- {
+ }, {
name: "mine",
description: "toss a proximity mine that sticks to walls
fires nails at mobs within range",
ammo: 0,
@@ -3885,8 +3896,7 @@ const b = {
}
m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 50 : 25) * b.fireCD); // cool down
}
- },
- {
+ }, {
name: "spores",
description: "fire a sporangium that discharges spores
spores seek out nearby mobs",
ammo: 0,
@@ -3947,7 +3957,7 @@ const b = {
} else {
const bodyCollisions = Matter.Query.collides(this, body)
if (bodyCollisions.length) {
- if (!bodyCollisions[0].bodyA.isNotHoldable) {
+ if (!bodyCollisions[0].bodyA.isComposite) {
onCollide(this)
this.stuckTo = bodyCollisions[0].bodyA
//find the relative position for when the mob is at angle zero by undoing the mobs rotation
@@ -4015,12 +4025,12 @@ const b = {
}
}
}
- },
- {
+ }, {
name: "drones",
description: "deploy drones that crash into mobs
crashes reduce their lifespan by 1 second",
ammo: 0,
ammoPack: 14,
+ defaultAmmoPack: 14,
have: false,
fire() {
if (m.crouch) {
@@ -4086,8 +4096,7 @@ const b = {
b.foam(position, Vector.rotate(velocity, 0.5 * (Math.random() - 0.5)), radius)
}
}
- },
- {
+ }, {
name: "rail gun",
description: "use energy to launch a high-speed dense rod
hold left mouse to charge, release to fire",
ammo: 0,
@@ -4431,8 +4440,7 @@ const b = {
}
}
}
- },
- {
+ }, {
name: "laser",
description: "emit a beam of collimated coherent light
drains energy instead of ammunition",
ammo: 0,
diff --git a/js/level.js b/js/level.js
index f88289d..4557df8 100644
--- a/js/level.js
+++ b/js/level.js
@@ -16,11 +16,11 @@ const level = {
// simulation.zoomScale = 1000;
// simulation.setZoom();
// m.setField("nano-scale manufacturing")
- // b.giveGuns("foam")
+ // b.giveGuns("spores")
// tech.isExplodeRadio = true
// for (let i = 0; i < 1; i++) tech.giveTech("dynamo-bot")
- // tech.giveTech("supercritical fission")
- // tech.giveTech("micro-extruder")
+ // tech.giveTech("needle gun")
+ // tech.giveTech("ceramic needles")
// tech.giveTech("causality bombs")
// tech.giveTech("cardinality")
// tech.giveTech("Bayesian statistics")
@@ -424,12 +424,14 @@ const level = {
rotor(x, y, rotate = 0, radius = 800, width = 40, density = 0.0005) {
const rotor1 = Matter.Bodies.rectangle(x, y, width, radius, {
density: density,
- isNotHoldable: true
+ isNotHoldable: true,
+ isComposite: true
});
const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, {
angle: Math.PI / 2,
density: density,
- isNotHoldable: true
+ isNotHoldable: true,
+ isComposite: true
});
rotor = Body.create({ //combine rotor1 and rotor2
parts: [rotor1, rotor2],
@@ -1100,8 +1102,8 @@ const level = {
// simulation.difficulty = 30
// spawn.starter(1900, -500, 200) //big boy
- spawn.pulsar(1900, -500)
- spawn.pulsarBoss(1900, -500)
+ // spawn.pulsar(1900, -500)
+ // spawn.pulsarBoss(1900, -500)
// spawn.historyBoss(1900, -500)
// spawn.ghoster(2900, -500)
// spawn.launcherBoss(1200, -500)
@@ -1113,7 +1115,7 @@ const level = {
// spawn.streamBoss(1600, -500)
// spawn.orbitalBoss(1600, -500)
// spawn.cellBossCulture(1600, -500)
- // spawn.shieldingBoss(1600, -500)
+ spawn.shieldingBoss(1600, -500)
// spawn.beamer(1200, -500)
// spawn.shield(mob[mob.length - 1], 1800, -120, 1);
@@ -4956,13 +4958,13 @@ const level = {
document.body.style.backgroundColor = "#dcdcde";
//Level
level.setPosToSpawn(200, 50);
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 8950;
- level.exit.y = 200;
+ level.exit.y = 170;
+ spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
//Map
- spawn.mapRect(150, 90, 100, 100);
- spawn.mapRect(8950, 190, 100, 100);
spawn.mapRect(-100, -400, 100, 600);
spawn.mapRect(-100, 100, 700, 100);
spawn.mapRect(500, 100, 100, 1700);
@@ -5006,7 +5008,7 @@ const level = {
spawn.mapRect(8000, 1500, 300, 100);
spawn.mapRect(7120, -100, 300, 100);
spawn.mapRect(7000, 1500, 300, 100);
- spawn.mapRect(6500, 1000, 300, 2100);
+ spawn.mapRect(6500, 1000, 300, 1200);
spawn.mapRect(5800, 1100, 300, 100);
spawn.mapRect(5900, 1700, 300, 100);
spawn.mapRect(5300, 1400, 300, 100);
@@ -5063,11 +5065,11 @@ const level = {
spawn.randomMob(8650, -200, 0.9); //end guards
- //Boss Spawning
- spawn.randomLevelBoss(6000, 700, ["pulsarBoss", "laserTargetingBoss", "powerUpBoss", "bomberBoss", "historyBoss", "orbitalBoss"]);
- spawn.shieldingBoss(7200, 500);
- if (simulation.difficulty > 20) {
- spawn.randomLevelBoss(2000, 300, ["historyBoss", "shooterBoss"]);
+ //Boss Spawning
+ if (simulation.difficulty > 3) {
+ spawn.randomLevelBoss(6000, 700, ["pulsarBoss", "laserTargetingBoss", "powerUpBoss", "bomberBoss", "historyBoss", "orbitalBoss"]);
+ if (simulation.difficulty > 10) spawn.shieldingBoss(7200, 500);
+ if (simulation.difficulty > 20) spawn.randomLevelBoss(2000, 300, ["historyBoss", "shooterBoss"]);
}
//Blocks
diff --git a/js/player.js b/js/player.js
index 0821f09..6a995da 100644
--- a/js/player.js
+++ b/js/player.js
@@ -392,7 +392,7 @@ const m = {
simulation.makeGunHUD(); //update gun HUD
simulation.updateTechHUD();
simulation.isTextLogOpen = true;
- if (m.holdingTarget) m.drop();
+ m.drop();
if (simulation.paused) build.pauseGrid() //update the build when paused
},
death() {
@@ -498,7 +498,7 @@ const m = {
harmReduction() {
let dmg = 1
dmg *= m.fieldHarmReduction
- if (tech.isImmortal) dmg *= 0.84
+ if (tech.isImmortal) dmg *= 0.79
if (tech.isHarmReduceAfterKill) dmg *= (m.lastKillCycle + 300 > m.cycle) ? 0.25 : 1.25
if (tech.healthDrain) dmg *= 1 + 2.667 * tech.healthDrain //tech.healthDrain = 0.03 at one stack //cause more damage
if (tech.squirrelFx !== 1) dmg *= 1 + (tech.squirrelFx - 1) / 5 //cause more damage
@@ -631,7 +631,7 @@ const m = {
}
m.lastHarmCycle = m.cycle
if (tech.isDroneOnDamage) { //chance to build a drone on damage from tech
- const len = Math.min((dmg - 0.06 * Math.random()) * 40, 40)
+ const len = Math.min((dmg - 0.06 * Math.random()) * 40, 40) / tech.droneEnergyReduction
for (let i = 0; i < len; i++) {
if (Math.random() < 0.5) b.drone() //spawn drone
}
@@ -1002,11 +1002,11 @@ const m = {
m.isHolding = false;
m.throwCharge = 0;
m.definePlayerMass()
- if (m.holdingTarget) {
- m.holdingTarget.collisionFilter.category = cat.body;
- m.holdingTarget.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
- m.holdingTarget = null;
- }
+ }
+ if (m.holdingTarget) {
+ m.holdingTarget.collisionFilter.category = cat.body;
+ m.holdingTarget.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
+ m.holdingTarget = null;
}
},
definePlayerMass(mass = m.defaultMass) {
@@ -1376,7 +1376,7 @@ const m = {
if (tech.isFreezeMobs) {
for (let i = 0, len = mob.length; i < len; ++i) {
Matter.Sleeping.set(mob[i], false)
- mobs.statusSlow(mob[i], 90)
+ mobs.statusSlow(mob[i], 60)
}
} else {
wake(mob);
@@ -1566,7 +1566,7 @@ const m = {
m.energy -= 0.057;
b.iceIX(1)
} else {
- m.energy -= 0.45;
+ m.energy -= 0.45 * tech.droneEnergyReduction;
b.drone(1)
}
}
@@ -2297,15 +2297,246 @@ const m = {
name: "wormhole",
description: "use energy to tunnel through a wormhole
wormholes attract blocks and power ups
7% chance to duplicate spawned power ups", //
bullets may also traverse wormholes
effect: function() {
- m.drop();
m.duplicateChance = 0.07
simulation.draw.powerUp = simulation.draw.powerUpBonus //change power up draw
- // if (tech.isRewindGun) {
- // m.hold = this.rewind
- // } else {
- m.hold = this.teleport
- // }
+ m.hold = function() {
+ // m.hole = { //this is reset with each new field, but I'm leaving it here for reference
+ // isOn: false,
+ // isReady: true,
+ // pos1: {x: 0,y: 0},
+ // pos2: {x: 0,y: 0},
+ // angle: 0,
+ // unit:{x:0,y:0},
+ // }
+ if (m.hole.isOn) {
+ // draw holes
+ m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025))
+ const semiMajorAxis = m.fieldRange + 30
+ const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1)
+ const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1)
+ const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2)
+ const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2)
+ ctx.beginPath();
+ ctx.moveTo(edge1a.x, edge1a.y)
+ ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y);
+ ctx.lineTo(edge2b.x, edge2b.y)
+ ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y);
+ ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)"
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI)
+ ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI)
+ ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})`
+ ctx.fill();
+
+ //suck power ups
+ for (let i = 0, len = powerUp.length; i < len; ++i) {
+ //which hole is closer
+ const dxP1 = m.hole.pos1.x - powerUp[i].position.x;
+ const dyP1 = m.hole.pos1.y - powerUp[i].position.y;
+ const dxP2 = m.hole.pos2.x - powerUp[i].position.x;
+ const dyP2 = m.hole.pos2.y - powerUp[i].position.y;
+ let dxP, dyP, dist2
+ if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) {
+ dxP = dxP1
+ dyP = dyP1
+ } else {
+ dxP = dxP2
+ dyP = dyP2
+ }
+ dist2 = dxP * dxP + dyP * dyP;
+ if (dist2 < 600000 && !(m.health === m.maxHealth && powerUp[i].name === "heal")) {
+ powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole
+ powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity
+ Matter.Body.setVelocity(powerUp[i], { //extra friction
+ x: powerUp[i].velocity.x * 0.05,
+ y: powerUp[i].velocity.y * 0.05
+ });
+ if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough
+ m.fieldRange *= 0.8
+ powerUps.onPickUp(powerUp[i]);
+ powerUp[i].effect();
+ Matter.World.remove(engine.world, powerUp[i]);
+ powerUp.splice(i, 1);
+ break; //because the array order is messed up after splice
+ }
+ }
+ }
+ //suck and shrink blocks
+ const suckRange = 500
+ const shrinkRange = 100
+ const shrinkScale = 0.97;
+ const slowScale = 0.9
+ for (let i = 0, len = body.length; i < len; i++) {
+ if (!body[i].isNotHoldable) {
+ const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position))
+ const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position))
+ if (dist1 < dist2) {
+ if (dist1 < suckRange) {
+ const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1)
+ const slow = Vector.mult(body[i].velocity, slowScale)
+ Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
+ //shrink
+ if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) {
+ Matter.Body.scale(body[i], shrinkScale, shrinkScale);
+ if (body[i].mass < 0.05) {
+ Matter.World.remove(engine.world, body[i]);
+ body.splice(i, 1);
+ m.fieldRange *= 0.8
+ if (tech.isWormholeEnergy) m.energy += 0.63
+ if (tech.isWormSpores) { //pandimensionalspermia
+ for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) {
+ b.spore(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), -15));
+ }
+ }
+ break
+ }
+ }
+ }
+ } else if (dist2 < suckRange) {
+ const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1)
+ const slow = Vector.mult(body[i].velocity, slowScale)
+ Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
+ //shrink
+ if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) {
+ Matter.Body.scale(body[i], shrinkScale, shrinkScale);
+ if (body[i].mass < 0.05) {
+ Matter.World.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.energy += 0.63
+ if (tech.isWormSpores) { //pandimensionalspermia
+ for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) {
+ b.spore(Vector.add(m.hole.pos1, 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), 15));
+ }
+ }
+ break
+ }
+ }
+ }
+ }
+ }
+ if (tech.isWormBullets) {
+ //teleport bullets
+ for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2
+ if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots
+ if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1
+ Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position)));
+ m.fieldRange += 5
+ bullet[i].isInHole = true
+ } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1
+ Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position)));
+ m.fieldRange += 5
+ bullet[i].isInHole = true
+ }
+ }
+ }
+ // mobs get pushed away
+ for (let i = 0, len = mob.length; i < len; i++) {
+ if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) {
+ const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07)
+ Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
+ }
+ if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) {
+ const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07)
+ Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
+ }
+ }
+ }
+ }
+
+ if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
+ const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame)
+ const scale = 60
+ // console.log(Matter.Query.region(map, bounds))
+ if (m.hole.isReady &&
+ (
+ Matter.Query.region(map, {
+ min: {
+ x: simulation.mouseInGame.x - scale,
+ y: simulation.mouseInGame.y - scale
+ },
+ max: {
+ x: simulation.mouseInGame.x + scale,
+ y: simulation.mouseInGame.y + scale
+ }
+ }).length === 0 &&
+ Matter.Query.ray(map, m.pos, justPastMouse).length === 0
+ // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 &&
+ // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 &&
+ // Matter.Query.ray(map, player.position, justPastMouse).length === 0
+ )
+ ) {
+ const sub = Vector.sub(simulation.mouseInGame, m.pos)
+ const mag = Vector.magnitude(sub)
+ const drain = 0.03 + 0.005 * Math.sqrt(mag)
+ if (m.energy > drain && mag > 300) {
+ m.energy -= drain
+ m.hole.isReady = false;
+ m.fieldRange = 0
+ Matter.Body.setPosition(player, simulation.mouseInGame);
+ m.buttonCD_jump = 0 //this might fix a bug with jumping
+ const velocity = Vector.mult(Vector.normalise(sub), 18)
+ Matter.Body.setVelocity(player, {
+ x: velocity.x,
+ y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer
+ });
+ if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage
+ // move bots to player
+ for (let i = 0; i < bullet.length; i++) {
+ if (bullet[i].botType) {
+ 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
+ });
+ }
+ }
+
+ //set holes
+ m.hole.isOn = true;
+ m.hole.pos1.x = m.pos.x
+ m.hole.pos1.y = m.pos.y
+ m.hole.pos2.x = player.position.x
+ m.hole.pos2.y = player.position.y
+ m.hole.angle = Math.atan2(sub.y, sub.x)
+ m.hole.unit = Vector.perp(Vector.normalise(sub))
+
+ if (tech.isWormholeDamage) {
+ who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100)
+ for (let i = 0; i < who.length; i++) {
+ if (who[i].body.alive) {
+ mobs.statusDoT(who[i].body, 1, 420)
+ mobs.statusStun(who[i].body, 360)
+ }
+ }
+ }
+ } else {
+ m.grabPowerUp();
+ }
+ } else {
+ m.grabPowerUp();
+ }
+ // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
+ // m.pickUp();
+ } else {
+ m.hole.isReady = true;
+ }
+ m.drawFieldMeter()
+ }
},
rewindCount: 0,
// rewind: function() {
@@ -2392,244 +2623,6 @@ const m = {
// }
// m.drawFieldMeter()
// },
- teleport: function() {
- // m.hole = { //this is reset with each new field, but I'm leaving it here for reference
- // isOn: false,
- // isReady: true,
- // pos1: {x: 0,y: 0},
- // pos2: {x: 0,y: 0},
- // angle: 0,
- // unit:{x:0,y:0},
- // }
- if (m.hole.isOn) {
- // draw holes
- m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025))
- const semiMajorAxis = m.fieldRange + 30
- const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1)
- const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1)
- const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2)
- const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2)
- ctx.beginPath();
- ctx.moveTo(edge1a.x, edge1a.y)
- ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y);
- ctx.lineTo(edge2b.x, edge2b.y)
- ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y);
- ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)"
- ctx.fill();
- ctx.beginPath();
- ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI)
- ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI)
- ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})`
- ctx.fill();
-
- //suck power ups
- for (let i = 0, len = powerUp.length; i < len; ++i) {
- //which hole is closer
- const dxP1 = m.hole.pos1.x - powerUp[i].position.x;
- const dyP1 = m.hole.pos1.y - powerUp[i].position.y;
- const dxP2 = m.hole.pos2.x - powerUp[i].position.x;
- const dyP2 = m.hole.pos2.y - powerUp[i].position.y;
- let dxP, dyP, dist2
- if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) {
- dxP = dxP1
- dyP = dyP1
- } else {
- dxP = dxP2
- dyP = dyP2
- }
- dist2 = dxP * dxP + dyP * dyP;
- if (dist2 < 600000 && !(m.health === m.maxHealth && powerUp[i].name === "heal")) {
- powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole
- powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity
- Matter.Body.setVelocity(powerUp[i], { //extra friction
- x: powerUp[i].velocity.x * 0.05,
- y: powerUp[i].velocity.y * 0.05
- });
- if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough
- m.fieldRange *= 0.8
- powerUps.onPickUp(powerUp[i]);
- powerUp[i].effect();
- Matter.World.remove(engine.world, powerUp[i]);
- powerUp.splice(i, 1);
- break; //because the array order is messed up after splice
- }
- }
- }
- //suck and shrink blocks
- const suckRange = 500
- const shrinkRange = 100
- const shrinkScale = 0.97;
- const slowScale = 0.9
- for (let i = 0, len = body.length; i < len; i++) {
- if (!body[i].isNotHoldable) {
- const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position))
- const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position))
- if (dist1 < dist2) {
- if (dist1 < suckRange) {
- const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1)
- const slow = Vector.mult(body[i].velocity, slowScale)
- Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
- //shrink
- if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) {
- Matter.Body.scale(body[i], shrinkScale, shrinkScale);
- if (body[i].mass < 0.05) {
- Matter.World.remove(engine.world, body[i]);
- body.splice(i, 1);
- m.fieldRange *= 0.8
- if (tech.isWormholeEnergy) m.energy += 0.63
- if (tech.isWormSpores) { //pandimensionalspermia
- for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) {
- b.spore(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), -15));
- }
- }
- break
- }
- }
- }
- } else if (dist2 < suckRange) {
- const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1)
- const slow = Vector.mult(body[i].velocity, slowScale)
- Matter.Body.setVelocity(body[i], Vector.add(slow, pull));
- //shrink
- if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) {
- Matter.Body.scale(body[i], shrinkScale, shrinkScale);
- if (body[i].mass < 0.05) {
- Matter.World.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.energy += 0.63
- if (tech.isWormSpores) { //pandimensionalspermia
- for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) {
- b.spore(Vector.add(m.hole.pos1, 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), 15));
- }
- }
- break
- }
- }
- }
- }
- }
- if (tech.isWormBullets) {
- //teleport bullets
- for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2
- if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots
- if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1
- Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position)));
- m.fieldRange += 5
- bullet[i].isInHole = true
- } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1
- Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position)));
- m.fieldRange += 5
- bullet[i].isInHole = true
- }
- }
- }
- // mobs get pushed away
- for (let i = 0, len = mob.length; i < len; i++) {
- if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) {
- const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07)
- Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
- }
- if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) {
- const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07)
- Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull));
- }
- }
- }
- }
-
- if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
- const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame)
- const scale = 60
- // console.log(Matter.Query.region(map, bounds))
- if (m.hole.isReady &&
- (
- Matter.Query.region(map, {
- min: {
- x: simulation.mouseInGame.x - scale,
- y: simulation.mouseInGame.y - scale
- },
- max: {
- x: simulation.mouseInGame.x + scale,
- y: simulation.mouseInGame.y + scale
- }
- }).length === 0 &&
- Matter.Query.ray(map, m.pos, justPastMouse).length === 0
- // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 &&
- // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 &&
- // Matter.Query.ray(map, player.position, justPastMouse).length === 0
- )
- ) {
- const sub = Vector.sub(simulation.mouseInGame, m.pos)
- const mag = Vector.magnitude(sub)
- const drain = 0.03 + 0.005 * Math.sqrt(mag)
- if (m.energy > drain && mag > 300) {
- m.energy -= drain
- m.hole.isReady = false;
- m.fieldRange = 0
- Matter.Body.setPosition(player, simulation.mouseInGame);
- m.buttonCD_jump = 0 //this might fix a bug with jumping
- const velocity = Vector.mult(Vector.normalise(sub), 18)
- Matter.Body.setVelocity(player, {
- x: velocity.x,
- y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer
- });
- if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage
- // move bots to player
- for (let i = 0; i < bullet.length; i++) {
- if (bullet[i].botType) {
- 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
- });
- }
- }
-
- //set holes
- m.hole.isOn = true;
- m.hole.pos1.x = m.pos.x
- m.hole.pos1.y = m.pos.y
- m.hole.pos2.x = player.position.x
- m.hole.pos2.y = player.position.y
- m.hole.angle = Math.atan2(sub.y, sub.x)
- m.hole.unit = Vector.perp(Vector.normalise(sub))
-
- if (tech.isWormholeDamage) {
- who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100)
- for (let i = 0; i < who.length; i++) {
- if (who[i].body.alive) {
- mobs.statusDoT(who[i].body, 1, 420)
- mobs.statusStun(who[i].body, 360)
- }
- }
- }
- } else {
- m.grabPowerUp();
- }
- } else {
- m.grabPowerUp();
- }
- } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
- m.pickUp();
- } else {
- m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists)
- m.hole.isReady = true;
- }
- m.drawFieldMeter()
- },
},
],
isShipMode: false,
diff --git a/js/spawn.js b/js/spawn.js
index 3d6f380..a132e80 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -2566,7 +2566,7 @@ const spawn = {
this.cycle = 0
ctx.beginPath();
for (let i = 0; i < mob.length; i++) {
- if (!mob[i].isShielded && !mob[i].shield && mob[i].dropPowerUp) {
+ if (!mob[i].isShielded && !mob[i].shield && mob[i].dropPowerUp && mob[i].alive) {
ctx.moveTo(this.position.x, this.position.y)
ctx.lineTo(mob[i].position.x, mob[i].position.y)
diff --git a/js/tech.js b/js/tech.js
index 41782a0..50db686 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -124,7 +124,7 @@
damageFromTech() {
let dmg = m.fieldDamage
if (tech.isFlipFlopDamage && tech.isFlipFlopOn) dmg *= 1.555
- if (tech.isAnthropicDamage && tech.isDeathAvoidedThisLevel) dmg *= 2.37
+ if (tech.isAnthropicDamage && tech.isDeathAvoidedThisLevel) dmg *= 2.3703599
if (tech.isDamageAfterKill) dmg *= (m.lastKillCycle + 300 > m.cycle) ? 1.5 : 0.5
if (tech.isTechDamage) dmg *= 2
if (tech.isDupDamage) dmg *= 1 + Math.min(1, tech.duplicationChance())
@@ -270,8 +270,8 @@
}
},
{
- name: "gun technology",
- description: "double the frequency of finding gun tech
spawn a gun",
+ name: "gun sciences",
+ description: "spawn a gun and double the frequency
of finding tech for a specific gun",
maxCount: 1,
count: 0,
frequency: 1,
@@ -358,14 +358,14 @@
},
{
name: "catabolism",
- description: "when you fire while out of ammo
gain 3 ammo, but lose 5 health",
+ description: "when you fire while out of ammo
gain 4 ammo, but lose 5 health",
maxCount: 1,
count: 0,
frequency: 1,
allowed() {
- return !tech.isEnergyHealth && !tech.isEnergyNoAmmo
+ return m.harmReduction() < 1 && !tech.isEnergyHealth && !tech.isEnergyNoAmmo
},
- requires: "not mass-energy equivalence
not exciton-lattice",
+ requires: "some harm reduction, not mass-energy equivalence, exciton-lattice",
effect: () => {
tech.isAmmoFromHealth = true;
},
@@ -613,6 +613,58 @@
b.setFireCD();
}
},
+ {
+ name: "microstates",
+ description: "increase damage by 4%
for every 10 active bullets",
+ maxCount: 1,
+ count: 0,
+ frequency: 1,
+ allowed() {
+ return tech.isBulletsLastLonger > 1
+ },
+ requires: "anti-shear topology",
+ effect() {
+ tech.isDamageFromBulletCount = true
+ },
+ remove() {
+ tech.isDamageFromBulletCount = false
+ }
+ },
+ {
+ name: "anti-shear topology",
+ description: "some bullets last 30% longer
drones, spores, missiles, foam, wave, neutron",
+ // isGunTech: true,
+ maxCount: 3,
+ count: 0,
+ frequency: 1,
+ allowed() {
+ return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || tech.haveGunCheck("spores") || tech.haveGunCheck("drones") || tech.haveGunCheck("missiles") || tech.haveGunCheck("foam") || tech.haveGunCheck("wave beam") || tech.isNeutronBomb
+ },
+ requires: "drones, spores, missiles, foam
wave beam, neutron bomb",
+ effect() {
+ tech.isBulletsLastLonger += 0.3
+ },
+ remove() {
+ tech.isBulletsLastLonger = 1;
+ }
+ },
+ {
+ name: "radioactive contamination",
+ description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob",
+ maxCount: 1,
+ count: 0,
+ frequency: 1,
+ allowed() {
+ return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio
+ },
+ requires: "radiation damage source",
+ effect() {
+ tech.isRadioactive = true
+ },
+ remove() {
+ tech.isRadioactive = false
+ }
+ },
{
name: "iridium-192",
description: "explosions release gamma radiation
100% more damage, but over 4 seconds",
@@ -699,6 +751,40 @@
tech.isImmuneExplosion = false;
}
},
+ {
+ name: "incendiary ammunition",
+ description: "shotgun, super balls, and drones
are loaded with explosives",
+ maxCount: 1,
+ count: 0,
+ frequency: 1,
+ allowed() {
+ return ((m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)) || tech.haveGunCheck("drones") || tech.haveGunCheck("super balls") || tech.haveGunCheck("shotgun")) && !tech.isNailShot
+ },
+ requires: "drones, super balls, shotgun",
+ effect() {
+ tech.isIncendiary = true
+ },
+ remove() {
+ tech.isIncendiary = false;
+ }
+ },
+ {
+ name: "fragmentation",
+ description: "some detonations and collisions eject nails
blocks, rail gun, grenades, missiles, shotgun slugs",
+ maxCount: 9,
+ count: 0,
+ frequency: 1,
+ allowed() {
+ return (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || tech.haveGunCheck("rail gun") || (tech.haveGunCheck("shotgun") && tech.isSlugShot) || tech.throwChargeRate > 1
+ },
+ requires: "grenades, missiles, rail gun, shotgun slugs, or mass driver",
+ effect() {
+ tech.fragments++
+ },
+ remove() {
+ tech.fragments = 0
+ }
+ },
{
name: "thermal runaway",
description: "mobs explode when they die
be careful",
@@ -716,6 +802,23 @@
tech.isExplodeMob = false;
}
},
+ {
+ name: "impact shear",
+ description: "mobs release a nail when they die
nails target nearby mobs",
+ maxCount: 9,
+ count: 0,
+ frequency: 1,
+ allowed() {
+ return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.isBotSpawner
+ },
+ requires: "no other mob death tech",
+ effect: () => {
+ tech.nailsDeathMob++
+ },
+ remove() {
+ tech.nailsDeathMob = 0;
+ }
+ },
{
name: "zoospore vector",
description: "mobs produce spores when they die
9% chance",
@@ -736,23 +839,6 @@
tech.sporesOnDeath = 0;
}
},
- {
- name: "impact shear",
- description: "mobs release a nail when they die
nails target nearby mobs",
- maxCount: 9,
- count: 0,
- frequency: 1,
- allowed() {
- return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.isBotSpawner
- },
- requires: "no other mob death tech",
- effect: () => {
- tech.nailsDeathMob++
- },
- remove() {
- tech.nailsDeathMob = 0;
- }
- },
{
name: "reaction inhibitor",
description: "mobs spawn with 11% less health",
@@ -1492,7 +1578,25 @@
remove() {
tech.isFreezeHarmImmune = false;
}
- }, {
+ },
+ {
+ name: "superfluidity",
+ description: "freeze effects are applied to a small area",
+ maxCount: 1,
+ count: 0,
+ frequency: 1,
+ allowed() {
+ return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField
+ },
+ requires: "a localized freeze effect",
+ effect() {
+ tech.isAoESlow = true
+ },
+ remove() {
+ tech.isAoESlow = false
+ }
+ },
+ {
name: "ablative drones",
description: "rebuild your broken parts as drones
chance to occur after receiving harm",
maxCount: 1,
@@ -2025,7 +2129,7 @@
frequency: 1,
isHealTech: true,
allowed() {
- return (m.health < 0.75 || build.isExperimentSelection) && !tech.isEnergyHealth
+ return ((m.health / m.maxHealth) < 0.7 || build.isExperimentSelection) && !tech.isEnergyHealth
},
requires: "not mass-energy equivalence",
effect() {
@@ -2036,14 +2140,14 @@
}
},
{
- name: "healing technology",
+ name: "maintenance",
description: "double the frequency of finding healing tech
spawn 12 heals",
maxCount: 1,
count: 0,
frequency: 1,
isNonRefundable: true,
allowed() {
- return true
+ return ((m.health / m.maxHealth) < 0.7 || build.isExperimentSelection)
},
requires: "",
effect() {
@@ -2118,14 +2222,14 @@
}
}, {
name: "quantum immortality",
- description: "after dying, continue in an alternate reality
reduce harm by 16%", //spawn 4 research
+ description: "after dying, continue in an alternate reality
reduce harm by 23%", //spawn 4 research
maxCount: 1,
count: 0,
frequency: 1,
allowed() {
- return !tech.isSwitchReality && !tech.isResearchReality
+ return !tech.isSwitchReality && !tech.isResearchReality && tech.isDeathAvoid
},
- requires: "not many-worlds, perturbation theory",
+ requires: "anthropic principle, not many-worlds, perturbation theory",
effect() {
tech.isImmortal = true;
// for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + Math.random() * 10, m.pos.y + Math.random() * 10, "research", false);
@@ -2156,9 +2260,9 @@
count: 0,
frequency: 1,
allowed() {
- return !tech.isImmortal && !tech.isSwitchReality && (powerUps.research.count > 2 || build.isExperimentSelection)
+ return !tech.isImmortal && !tech.isSwitchReality
},
- requires: "at least 2 research, not quantum immortality, many-worlds",
+ requires: "not quantum immortality, many-worlds",
effect() {
tech.isResearchReality = true;
for (let i = 0; i < 11; i++) powerUps.spawn(m.pos.x + Math.random() * 10, m.pos.y + Math.random() * 10, "research", false);
@@ -2166,22 +2270,6 @@
remove() {
tech.isResearchReality = false;
}
- }, {
- name: "renormalization",
- description: "using a research for any purpose
has a 37% chance to spawn a research",
- maxCount: 1,
- count: 0,
- frequency: 1,
- allowed() {
- return (powerUps.research.count > 2 || build.isExperimentSelection) && !tech.isSuperDeterminism && !tech.isRerollHaste
- },
- requires: "not superdeterminism or Ψ(t) collapse
at least 3 research",
- effect() {
- tech.renormalization = true;
- },
- remove() {
- tech.renormalization = false;
- }
}, {
name: "decoherence",
description: "researched or canceled tech won't reoccur
spawn 5 research",
@@ -2200,6 +2288,22 @@
tech.isBanish = false
powerUps.tech.banishLog = [] //reset banish log
}
+ }, {
+ name: "renormalization",
+ description: "using a research for any purpose
has a 37% chance to spawn a research",
+ maxCount: 1,
+ count: 0,
+ frequency: 1,
+ allowed() {
+ return (powerUps.research.count > 3 || build.isExperimentSelection) && !tech.isSuperDeterminism && !tech.isRerollHaste
+ },
+ requires: "not superdeterminism or Ψ(t) collapse
at least 4 research",
+ effect() {
+ tech.renormalization = true;
+ },
+ remove() {
+ tech.renormalization = false;
+ }
}, {
name: "perturbation theory",
description: "66% decreased delay after firing
when you have no research in your inventory",
@@ -2319,7 +2423,7 @@
},
{
name: "meta-analysis",
- description: "if you choose a junk tech you instead get a
random non-junk tech and spawn 2 research",
+ description: "if you choose a JUNK tech you instead get a
random normal tech and 2 research",
maxCount: 1,
count: 0,
frequency: 2,
@@ -2336,7 +2440,7 @@
},
{
name: "replication",
- description: "7% chance to duplicate spawned power ups
add 12 junk tech to the potential pool",
+ description: "7% chance to duplicate spawned power ups
add 12 JUNK tech to the potential pool",
maxCount: 9,
count: 0,
frequency: 1,
@@ -2378,14 +2482,14 @@
},
{
name: "futures exchange",
- description: "clicking × to cancel a field, tech, or gun
adds 4.5% power up duplication chance",
+ description: "clicking × to cancel a field, tech, or gun
adds 4.5% power up duplication chance",
maxCount: 1,
count: 0,
frequency: 1,
allowed() {
- return tech.duplicationChance() < 1 && !tech.isDeterminism && (level.levelsCleared < 5 || Math.random() < 0.5)
+ return tech.duplicationChance() < 1 && !tech.isDeterminism && level.levelsCleared < 5
},
- requires: "below 100% duplication chance, not determinism",
+ requires: "below 100% duplication chance, below level 5, not determinism",
effect() {
tech.isCancelDuplication = true
tech.cancelCount = 0
@@ -2546,7 +2650,7 @@
}
}, {
name: "dark patterns",
- description: "reduce combat difficulty by 1 level
add 18 junk tech to the potential pool",
+ description: "reduce combat difficulty by 1 level
add 18 JUNK tech to the potential pool",
maxCount: 1,
count: 0,
frequency: 1,
@@ -2584,7 +2688,7 @@
}
},
{
- name: "field technology",
+ name: "vector fields",
description: "double the frequency of finding field tech
spawn a field",
maxCount: 1,
count: 0,
@@ -2646,7 +2750,7 @@
}
}, {
name: "determinism",
- description: "spawn 5 tech
tech, fields, and guns have only 1 choice",
+ description: "spawn 5 tech, but you have no cancel
and 1 choice for tech, fields, and guns",
maxCount: 1,
count: 0,
frequency: 1,
@@ -2666,7 +2770,7 @@
}
}, {
name: "superdeterminism",
- description: "spawn 7 tech
research, guns, and fields no longer spawn",
+ description: "spawn 5 tech
research, guns, and fields no longer spawn",
maxCount: 1,
count: 0,
frequency: 3,
@@ -2732,108 +2836,6 @@
tech.isRewindGun = false
}
}
- }, {
- name: "incendiary ammunition",
- description: "shotgun, super balls, and drones
are loaded with explosives",
- isGunTech: true,
- maxCount: 1,
- count: 0,
- frequency: 1,
- allowed() {
- return ((m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)) || tech.haveGunCheck("drones") || tech.haveGunCheck("super balls") || tech.haveGunCheck("shotgun")) && !tech.isNailShot
- },
- requires: "drones, super balls, shotgun",
- effect() {
- tech.isIncendiary = true
- },
- remove() {
- tech.isIncendiary = false;
- }
- }, {
- name: "fragmentation",
- description: "some detonations and collisions eject nails
blocks, rail gun, grenades, missiles, shotgun slugs",
- isGunTech: true,
- maxCount: 9,
- count: 0,
- frequency: 1,
- allowed() {
- return (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || tech.haveGunCheck("rail gun") || (tech.haveGunCheck("shotgun") && tech.isSlugShot) || tech.throwChargeRate > 1
- },
- requires: "grenades, missiles, rail gun, shotgun slugs, or mass driver",
- effect() {
- tech.fragments++
- },
- remove() {
- tech.fragments = 0
- }
- }, {
- name: "superfluidity",
- description: "freeze effects are applied to a small area",
- isGunTech: true,
- maxCount: 1,
- count: 0,
- frequency: 1,
- allowed() {
- return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField
- },
- requires: "a freeze effect",
- effect() {
- tech.isAoESlow = true
- },
- remove() {
- tech.isAoESlow = false
- }
- }, {
- name: "radioactive contamination",
- description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob",
- isGunTech: true,
- maxCount: 1,
- count: 0,
- frequency: 1,
- allowed() {
- return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio
- },
- requires: "radiation damage source",
- effect() {
- tech.isRadioactive = true
- },
- remove() {
- tech.isRadioactive = false
- }
- }, {
- name: "anti-shear topology",
- description: "some bullets last 30% longer
drones, spores, missiles, foam, wave, neutron",
- isGunTech: true,
- maxCount: 3,
- count: 0,
- frequency: 1,
- allowed() {
- return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || tech.haveGunCheck("spores") || tech.haveGunCheck("drones") || tech.haveGunCheck("missiles") || tech.haveGunCheck("foam") || tech.haveGunCheck("wave beam") || tech.isNeutronBomb
- },
- requires: "drones, spores, missiles, foam
wave beam, neutron bomb",
- effect() {
- tech.isBulletsLastLonger += 0.3
- },
- remove() {
- tech.isBulletsLastLonger = 1;
- }
- }, {
- name: "microstates",
- description: "increase damage by 4%
for every 10 active bullets",
- isGunTech: true,
- maxCount: 1,
- count: 0,
- frequency: 1,
- allowed() {
- return tech.isBulletsLastLonger > 1
- },
- requires: "anti-shear topology",
- effect() {
- tech.isDamageFromBulletCount = true
- },
- remove() {
- tech.isDamageFromBulletCount = false
- }
}, {
name: "needle gun",
description: "nail gun fires 3 mob piercing needles
requires 3 times more ammo",
@@ -2872,7 +2874,7 @@
}
}
}, {
- name: "ceramic needle",
+ name: "ceramic needles",
description: `your needles pierce shields
directly damaging shielded mobs`,
isGunTech: true,
maxCount: 1,
@@ -3600,6 +3602,31 @@
remove() {
tech.isDroneGrab = false
}
+ }, {
+ name: "planned obsolescence",
+ description: "reduce all drone production costs by 300%
reduce the average drone lifetime by 53%",
+ isGunTech: true,
+ maxCount: 3,
+ count: 0,
+ frequency: 1,
+ allowed() {
+ return tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))
+ },
+ requires: "drones",
+ effect() {
+ tech.droneCycleReduction = Math.pow(0.47, this.count)
+ tech.droneEnergyReduction = Math.pow(0.33, this.count)
+ 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 * Math.pow(3, this.count)
+ }
+ },
+ 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
+ }
+ }
}, {
name: "necrophoresis",
description: "foam bubbles grow and split into 3 copies
when the mob they are stuck to dies",
@@ -4845,7 +4872,7 @@
remove() {}
}, {
name: "defragment",
- description: "set the frequency of finding junk tech to zero",
+ description: "set the frequency of finding JUNK tech to zero",
maxCount: 1,
count: 0,
frequency: 0,
@@ -5021,7 +5048,7 @@
remove() {}
}, {
name: "expert system",
- description: "spawn a tech power up
add 64 junk tech to the potential pool",
+ description: "spawn a tech power up
add 64 JUNK tech to the potential pool",
maxCount: 9,
count: 0,
frequency: 0,
@@ -5795,5 +5822,7 @@
isFlipFlopDamage: null,
isFlipFlopEnergy: null,
isMetaAnalysis: null,
- isFoamAttract: null
+ isFoamAttract: null,
+ droneCycleReduction: null,
+ droneEnergyReduction: null
}
\ No newline at end of file
diff --git a/style.css b/style.css
index e3550e1..88ae013 100644
--- a/style.css
+++ b/style.css
@@ -604,6 +604,13 @@ summary {
font-weight: 100;
}
+.color-j {
+ letter-spacing: 1px;
+ /* font-weight: 100; */
+ font-family: Lucida Console, Courier, monospace;
+ /* transform: rotate(-90deg); */
+}
+
/* .color-rewind {
background-image: linear-gradient(to left, #fff, #bbb);
border-radius: 5px;
diff --git a/todo.txt b/todo.txt
index 72846d0..cc2209b 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,16 +1,19 @@
******************************************************** NEXT PATCH ********************************************************
-tech: electrostatic induction - foam bullets are attracted to nearby mobs
+bug fixes
+ strong anthropic principle does 0.03599% more damage
+ spores can stick to rotating blocks now
+ probably fixed the new wormhole field while targeting a block -> crouch lock bug
+ ceramic needles now correctly bypass shields
-portals on perplex map, now remove blocks that fall in
-new community map! coliseum by iNoobBoi
+needles fire 3 at a time with a short delay and no spread
-a few more tech can be refunded properly
-nonRefundable tech don't show up in the list of tech you have
+tech: planned obsolescence - 3x drone ammo, 1/3 drone nano-scale energy cost, but 53% reduced drone life span
******************************************************** BUGS ********************************************************
-fix issue where you have to press z once to get copy to work for simulation.enableConstructMode()
+you have to press z once to get copy to work for simulation.enableConstructMode() sometimes
+ not sure how to reproduce, but it happens often on the first draw
mouse event e.which is deprecated
@@ -22,9 +25,6 @@ fix door.isOpen actually meaning isClosed?
wasn't able to understand bug after extensive testing
had tech: complex spin statistics
-(a few times) wormhole teleportation can leave the player in a stuck jump state
- seems to be easily fixed, by porting, firing or something
-
(always) make it so that when you are immune to harm you can either jump on mobs or you pass through them
(always) is there a way to check if the player is stuck inside the map or block
@@ -41,6 +41,20 @@ fix door.isOpen actually meaning isClosed?
******************************************************** TODO ********************************************************
+mob vision: look at player history
+ build a new type of attraction for mobs
+ if mobs can't see player, they check to see if they can see where the player was in the history
+ if mobs can't see player, they could check to see if they can find player in the past
+ https://abitawake.com/news/articles/enemy-ai-chasing-a-player-without-navigation2d-or-a-star-pathfinding
+
+tech: chitin - take 50% less damage, reduce harm reduction by 5% after each collision
+
+Mob: "Tentacle": Sits on wall. Is a black blob. When you get near it, reaches out and grabs you, similar to wires. Does not deal damage.
+ maybe it could be immune to damage? but it is spawned by an actual mob
+
+wormhole, or CPT tech: after taking damage teleport in direction of mouse
+ after collision
+
mob sniper: draw aim graphics before fire
tech laser: photon - laser, but it can only move 100 pixels a cycle
@@ -153,12 +167,6 @@ tech pilot wave: antigravity - blocks have no gravity for a few seconds after ex
maybe they bounce too?
maybe they explode?
-mob vision: look at player history
- build a new type of attraction for mobs
- if mobs can't see player, they check to see if they can see where the player was in the history
- if mobs can't see player, they could check to see if they can find player in the past
- https://abitawake.com/news/articles/enemy-ai-chasing-a-player-without-navigation2d-or-a-star-pathfinding
-
wormhole - make it clear when the wormhole can and can't teleport to a location before the player clicks
flavor - your bullets destroy blocks