diff --git a/.DS_Store b/.DS_Store
index 95a0799..32b9bd6 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/js/bullet.js b/js/bullet.js
index 535b65f..beff0dd 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -583,7 +583,7 @@ const b = {
v1: null,
v2: null
};
- if (tech.isPulseAim) { //find mobs in line of sight
+ if (tech.isPulseAim && input.down) { //find mobs in line of sight
let dist = 2200
for (let i = 0, len = mob.length; i < len; i++) {
const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position))
@@ -2386,9 +2386,9 @@ const b = {
if (best.who.alive) {
best.who.locatePlayer();
if (best.who.damageReduction) {
- if ( //crit
+ if ( //iridescence
tech.laserCrit && !best.who.shield &&
- Vector.dot(Vector.normalise(Vector.sub(best.who.position, path[path.length - 1])), Vector.normalise(Vector.sub(path[path.length - 1], path[path.length - 2]))) > 0.99 - 0.6 / best.who.radius
+ Vector.dot(Vector.normalise(Vector.sub(best.who.position, path[path.length - 1])), Vector.normalise(Vector.sub(path[path.length - 1], path[path.length - 2]))) > 0.995 - 0.6 / best.who.radius
) {
damage *= 1 + tech.laserCrit
simulation.drawList.push({ //add dmg to draw queue
@@ -2663,8 +2663,7 @@ const b = {
!mob[i].isBadTarget &&
Vector.magnitude(Vector.sub(this.position, mob[i].position)) < 700 + 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 &&
- !mob[i].isInvulnerable
+ Matter.Query.ray(body, this.position, mob[i].position).length === 0
) {
if (tech.isStun) b.AoEStunEffect(this.position, 700 + mob[i].radius + random); //AoEStunEffect(where, range, cycles = 90 + 60 * Math.random()) {
if (tech.isMineSentry) {
@@ -2961,7 +2960,7 @@ const b = {
friction: 0,
frictionAir: 0.023,
restitution: 0.9,
- dmg: 1, //damage done in addition to the damage from momentum
+ dmg: 1.2, //damage done in addition to the damage from momentum
lookFrequency: 14 + Math.floor(8 * Math.random()),
endCycle: simulation.cycle + 100 * tech.isBulletsLastLonger + Math.floor(25 * Math.random()),
classType: "bullet",
@@ -3022,6 +3021,128 @@ const b = {
// y: m.Vy / 2 + speed * Math.sin(dir)
// });
},
+ flea(where, velocity, radius = 7 + 3 * Math.random()) {
+ const me = bullet.length;
+ bullet[me] = Bodies.polygon(where.x, where.y, 5, radius, {
+ isFlea: true,
+ angle: 0.5 * Math.random(),
+ friction: 1,
+ frictionStatic: 1,
+ frictionAir: 0, //0.01,
+ restitution: 0,
+ density: 0.0005, // 0.001 is normal density
+ dmg: 9 * (tech.isMutualism ? 2.5 : 1), //damage done in addition to the damage from momentum //spores do 7 dmg, worms do 18
+ lookFrequency: 19 + Math.floor(11 * Math.random()),
+ endCycle: simulation.cycle + Math.floor((780 * tech.isBulletsLastLonger + 360 * Math.random()) + Math.max(0, 150 - bullet.length)), // 13 - 19s
+ classType: "bullet",
+ collisionFilter: {
+ category: cat.bullet,
+ mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield
+ },
+ minDmgSpeed: 0,
+ lockedOn: null,
+ delay: 50,
+ cd: simulation.cycle + 10,
+ beforeDmg(who) {
+ // this.endCycle = 0
+ Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), 10 + 10 * Math.random())); //push away from target
+
+ this.endCycle -= 180
+ this.cd = simulation.cycle + this.delay;
+ // this.collisionFilter.mask = cat.map
+ if (tech.isSporeFreeze) mobs.statusSlow(who, 90)
+ if (tech.isSpawnBulletsOnDeath && who.alive && who.isDropPowerUp) {
+ setTimeout(() => {
+ if (!who.alive) {
+ for (let i = 0; i < 3; i++) { //spawn 3 more
+ const speed = 10 + 5 * Math.random()
+ const angle = 2 * Math.PI * Math.random()
+ b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) })
+ }
+ }
+ this.endCycle = 0;
+ }, 1);
+ }
+ },
+ onEnd() {},
+ gravity: 0.002 + 0.002 * tech.isSporeFollow,
+ do() {
+ // if (true && this.lockedOn && this.cd < simulation.cycle) { //blink towards mobs
+ // //needs it's own cooldown variables
+ // // this.cd = simulation.cycle + this.delay;
+
+ // const sub = Vector.sub(this.lockedOn.position, this.position);
+ // const distMag = Vector.magnitude(sub);
+ // if (distMag < 500) {
+ // const unit = Vector.normalise(sub)
+ // Matter.Body.setVelocity(this, Vector.mult(unit, Math.max(20, this.speed * 1.5)));
+ // ctx.beginPath();
+ // ctx.moveTo(this.position.x, this.position.y);
+ // Matter.Body.translate(this, Vector.mult(unit, Math.min(350, distMag - this.lockedOn.radius + 10)));
+ // ctx.lineTo(this.position.x, this.position.y);
+ // ctx.lineWidth = radius * 2;
+ // ctx.strokeStyle = "rgba(0,0,0,0.5)";
+ // ctx.stroke();
+ // }
+ // }
+
+ this.force.y += this.gravity * this.mass
+ if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { //if on the ground and not on jump cooldown //
+ // this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield
+
+ this.cd = simulation.cycle + this.delay;
+ this.lockedOn = null; //find a target
+ let closeDist = Infinity;
+ for (let i = 0, len = mob.length; i < len; ++i) {
+ if (
+ !mob[i].isBadTarget &&
+ !mob[i].isInvulnerable &&
+ mob[i].alive &&
+ this.position.y - mob[i].position.y < 1500 && //this is about how high fleas can jump with capMaxY = 0.12 + 0.04 * Math.random()
+ this.position.y - mob[i].position.y > -300 && //not too far below the flea (note that fleas should be on the ground most of the time when doing this check)
+ Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
+ Matter.Query.ray(body, this.position, mob[i].position).length === 0
+ ) {
+ const TARGET_VECTOR = Vector.sub(this.position, mob[i].position)
+ const DIST = Vector.magnitude(TARGET_VECTOR);
+ if (DIST < closeDist) {
+ closeDist = DIST;
+ this.lockedOn = mob[i]
+ }
+ }
+ }
+ 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 }
+ }
+ }
+ if (this.lockedOn) { //hop towards mob target
+ const where = Vector.add(this.lockedOn.position, Vector.mult(this.lockedOn.velocity, 5)) //estimate where the mob will be in 5 cycles
+ const Dy = Math.max(0, this.position.y - where.y) //can't be negative because you can't hop down
+ const Dx = this.position.x - where.x
+ const Vx = -0.06 * Dx / Math.sqrt(2 * Dy / this.gravity) //calibrated to hit target, don't mess with this
+ const Vy = 0.085 * Math.sqrt(this.gravity * Dy) //calibrated to hit target, don't mess with this
+ const capX = 0.07 + 0.02 * tech.isSporeFollow
+ const capMaxY = 0.12 + 0.04 * Math.random() + 0.05 * tech.isSporeFollow
+ const capMinY = closeDist > 500 ? 0.05 + 0.02 * Math.random() : 0.02 + 0.01 * Math.random() //don't jump super low, unless you are very close to mob target
+ this.force.x = Math.max(-capX, Math.min(capX, Vx)) * this.mass;
+ this.force.y = -Math.max(capMinY, Math.min(capMaxY, Vy)) * this.mass
+ } else { //random hops
+ if (Math.random() < 0.5) { //chance to continue in the same horizontal direction
+ this.force.x = (0.01 + 0.03 * Math.random()) * this.mass * (this.velocity.x > 0 ? 1 : -1); //random move
+ } else {
+ this.force.x = (0.01 + 0.03 * Math.random()) * this.mass * (Math.random() < 0.5 ? 1 : -1); //random move
+ }
+ this.force.y = -(0.03 + 0.08 * Math.random()) * this.mass
+ }
+ Matter.Body.setVelocity(this, { x: 0, y: 0 });
+ }
+ }
+ })
+ Composite.add(engine.world, bullet[me]); //add bullet to world
+ Matter.Body.setVelocity(bullet[me], velocity);
+ },
drone(where = { x: m.pos.x + 30 * Math.cos(m.angle) + 20 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 20 * (Math.random() - 0.5) }, speed = 1) {
const me = bullet.length;
const THRUST = 0.0015
@@ -3774,7 +3895,13 @@ const b = {
this.force.y += this.mass * tech.foamGravity; //gravity
if (tech.isFoamAttract) {
for (let i = 0, len = mob.length; i < len; i++) {
- if (!mob[i].isBadTarget && Vector.magnitude(Vector.sub(mob[i].position, this.position)) < 375 && mob[i].alive && Matter.Query.ray(map, this.position, mob[i].position).length === 0) {
+ if (
+ !mob[i].isBadTarget &&
+ Vector.magnitude(Vector.sub(mob[i].position, this.position)) < 375 &&
+ mob[i].alive &&
+ Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
+ !mob[i].isInvulnerable
+ ) {
this.force = Vector.mult(Vector.normalise(Vector.sub(mob[i].position, this.position)), this.mass * 0.004)
const slow = 0.9
Matter.Body.setVelocity(this, {
@@ -3800,7 +3927,7 @@ const b = {
targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) {
let closestMob, dist
for (let i = 0, len = mob.length; i < len; i++) {
- if (who !== mob[i] && !mob[i].isBadTarget) {
+ if (who !== mob[i] && !mob[i].isBadTarget && !mob[i].isInvulnerable) {
dist = Vector.magnitude(Vector.sub(who.position, mob[i].position));
if (dist < range && Matter.Query.ray(map, who.position, mob[i].position).length === 0) { //&& Matter.Query.ray(body, position, mob[i].position).length === 0
closestMob = mob[i]
@@ -4331,7 +4458,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 ? 21 : 110) * b.fireCDscale),
+ delay: Math.floor((tech.isNailBotUpgrade ? 20 : 105) * b.fireCDscale),
acceleration: 0.005 * (1 + 0.5 * Math.random()),
range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots(),
endCycle: Infinity,
@@ -4492,7 +4619,7 @@ const b = {
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 < 1000000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0) {
+ if (dist2 < 1000000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) {
this.cd = simulation.cycle + this.delay;
target = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60))
const radius = 6 + 7 * Math.random()
@@ -4523,10 +4650,7 @@ const b = {
restitution: 0.5 * (1 + 0.5 * Math.random()),
acceleration: 0.0015 * (1 + 0.3 * Math.random()),
playerRange: 140 + Math.floor(30 * Math.random()) + 2 * b.totalBots(),
- offPlayer: {
- x: 0,
- y: 0,
- },
+ offPlayer: { x: 0, y: 0, },
dmg: 0, //damage done in addition to the damage from momentum
minDmgSpeed: 2,
lookFrequency: 40 + Math.floor(7 * Math.random()) - 10 * tech.isLaserBotUpgrade,
@@ -4562,11 +4686,14 @@ const b = {
let closeDist = this.range;
for (let i = 0, len = mob.length; i < len; ++i) {
const DIST = Vector.magnitude(Vector.sub(this.vertices[0], mob[i].position));
- if (DIST - mob[i].radius < closeDist &&
+ if (
+ DIST - mob[i].radius < closeDist &&
!mob[i].isShielded &&
(!mob[i].isBadTarget || mob[i].isMobBullet) &&
Matter.Query.ray(map, this.vertices[0], mob[i].position).length === 0 &&
- Matter.Query.ray(body, this.vertices[0], mob[i].position).length === 0) {
+ Matter.Query.ray(body, this.vertices[0], mob[i].position).length === 0 &&
+ !mob[i].isInvulnerable
+ ) {
closeDist = DIST;
this.lockedOn = mob[i]
}
@@ -4686,10 +4813,13 @@ const b = {
let closeDist = this.range;
for (let i = 0, len = mob.length; i < len; ++i) {
const DIST = Vector.magnitude(Vector.sub(this.position, mob[i].position)) - mob[i].radius;
- if (DIST < closeDist &&
+ if (
+ DIST < closeDist &&
!mob[i].isBadTarget &&
Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
- Matter.Query.ray(body, this.position, mob[i].position).length === 0) {
+ Matter.Query.ray(body, this.position, mob[i].position).length === 0 &&
+ !mob[i].isInvulnerable
+ ) {
closeDist = DIST;
this.lockedOn = mob[i]
}
@@ -4842,7 +4972,7 @@ const b = {
y: best.y
};
if (best.who.alive) {
- const dmg = 0.6 * m.dmgScale; //********** SCALE DAMAGE HERE *********************
+ const dmg = 0.65 * m.dmgScale; //********** SCALE DAMAGE HERE *********************
best.who.damage(dmg);
best.who.locatePlayer();
//push mobs away
@@ -4948,7 +5078,7 @@ const b = {
})
for (let i = 0; i < q.length; i++) {
if (!q[i].isShielded) {
- mobs.statusStun(q[i], 180)
+ mobs.statusStun(q[i], 210)
const dmg = 0.4 * m.dmgScale * (this.isUpgraded ? 4 : 1) * (tech.isCrit ? 4 : 1)
q[i].damage(dmg);
if (q[i].alive) q[i].foundPlayer();
@@ -6273,12 +6403,17 @@ const b = {
this.stuck(); //runs different code based on what the bullet is stuck to
let scale = 1.01
if (tech.isSporeGrowth && !(simulation.cycle % 40)) { //release a spore
- if (tech.isSporeWorm) {
+ if (tech.isSporeFlea) {
+ if (!(simulation.cycle % 80)) {
+ const speed = 10 + 5 * Math.random()
+ const angle = 2 * Math.PI * Math.random()
+ b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) })
+ }
+ } else if (tech.isSporeWorm) {
if (!(simulation.cycle % 80)) b.worm(this.position)
} else {
b.spore(this.position)
}
- // this.totalSpores--
scale = 0.96
if (this.stuckTo && this.stuckTo.alive) scale = 0.9
Matter.Body.scale(this, scale, scale);
@@ -6289,18 +6424,21 @@ const b = {
this.radius *= scale
if (this.radius > this.maxRadius) this.endCycle = 0;
}
- // this.force.y += this.mass * 0.00045;
-
//draw green glow
ctx.fillStyle = "rgba(0,200,125,0.16)";
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI);
ctx.fill();
};
-
//spawn bullets on end
bullet[me].onEnd = function() {
- if (tech.isSporeWorm) {
+ if (tech.isSporeFlea) {
+ for (let i = 0, len = this.totalSpores * 0.5; i < len; i++) {
+ const speed = 10 + 5 * Math.random()
+ const angle = 2 * Math.PI * Math.random()
+ b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) })
+ }
+ } else if (tech.isSporeWorm) {
for (let i = 0, len = this.totalSpores * 0.5; i < len; i++) b.worm(this.position)
} else {
for (let i = 0; i < this.totalSpores; i++) b.spore(this.position)
@@ -6549,7 +6687,7 @@ const b = {
if (dot > 0.95 - Math.min(dist * 0.00015, 0.3)) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target
// if (this.ammo > -1) {
// this.ammo--
- b.harpoon(where, input.down ? mob[i] : null, angle, harpoonSize, false) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 })
+ b.harpoon(where, input.down ? null : mob[i], angle, harpoonSize, false) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 })
angle += SPREAD
targetCount++
if (targetCount > tech.extraHarpoons) break
@@ -6574,7 +6712,7 @@ const b = {
//look for closest mob in player's LoS
const dir = { x: Math.cos(m.angle), y: Math.sin(m.angle) }; //make a vector for the player's direction of length 1; used in dot product
for (let i = 0, len = mob.length; i < len; ++i) {
- if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0) {
+ if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) {
const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors
const dist = Vector.magnitude(Vector.sub(where, mob[i].position))
if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target
@@ -7066,7 +7204,11 @@ const b = {
fire() {},
chooseFireMethod() {
this.lensDamage = 1
- if (tech.isLaserLens) this.do = this.lens
+ if (tech.isLaserLens) {
+ this.do = this.lens
+ } else {
+ this.do = this.stuckOn
+ }
if (tech.isPulseLaser) {
this.fire = () => {
const drain = 0.01 * tech.isLaserDiode * (tech.isCapacitor ? 10 : 1)
diff --git a/js/index.js b/js/index.js
index 76e4124..381303b 100644
--- a/js/index.js
+++ b/js/index.js
@@ -263,9 +263,8 @@ ${simulation.isCheating ? "
lore disabled": ""}
text += `
${build.nameLink(b.guns[b.inventory[i]].name)} -
${b.guns[b.inventory[i]].ammo} ${b.guns[b.inventory[i]].description}
`
}
let el = document.getElementById("pause-grid-left")
- el.style.display = "grid"
+ el.style.display = tech.isNoDraftPause ? "none" : "grid" //disabled for eternalism because eternalism lets the player play while this menu is up but the menu doesn't update
el.innerHTML = text
-
//right side
text = "";
if (tech.isPauseSwitchField && !simulation.isChoosing) {
@@ -304,7 +303,7 @@ ${simulation.isCheating ? "
lore disabled": ""}
}
}
el = document.getElementById("pause-grid-right")
- el.style.display = "grid"
+ el.style.display = tech.isNoDraftPause ? "none" : "grid" //disabled for eternalism because eternalism lets the player play while this menu is up but the menu doesn't update
el.innerHTML = text
document.getElementById("tech").style.display = "none"
@@ -933,7 +932,7 @@ window.addEventListener("keydown", function(event) {
if (m.alive && localSettings.loreCount > 0) {
if (simulation.difficultyMode > 4) {
simulation.makeTextLog("testing mode disabled for this difficulty");
- break
+ // break
}
if (simulation.testing) {
simulation.testing = false;
diff --git a/js/level.js b/js/level.js
index 93aa7ec..2d38281 100644
--- a/js/level.js
+++ b/js/level.js
@@ -15,40 +15,31 @@ const level = {
levels: [],
start() {
if (level.levelsCleared === 0) { //this code only runs on the first level
+ // simulation.enableConstructMode() //used to build maps in testing mode
// simulation.isHorizontalFlipped = true
// m.addHealth(Infinity)
- // m.setField("time dilation")
- // b.giveGuns("laser")
+ // m.setField("molecular assembler")
+ // b.giveGuns("spores")
+ // tech.giveTech("fleas")
+ // tech.giveTech("flagella")
// b.guns[0].ammo = 10000
- // // b.giveGuns("mine")
- // tech.giveTech("lens")
- // for (let i = 0; i < 2; ++i) tech.giveTech("diffraction grating")
- // for (let i = 0; i < 9; ++i) tech.giveTech("propagator")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("mycelium manufacturing")
+ // for (let i = 0; i < 9; ++i) tech.giveTech("WIMPs")
// for (let i = 0; i < 100; ++i) tech.giveTech("nail-bot")
- // for (let i = 0; i < 9; ++i) tech.giveTech("emergence")
- // tech.giveTech("laser-bot")
- // tech.giveTech("slow light")
- // tech.giveTech("iridescence")
- // m.maxHealth = 100
- // m.health = m.maxHealth
- // for (let i = 0; i < 10; i++) tech.giveTech("tungsten carbide")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("necrophage")
+ // for (let i = 0; i < 1; i++) tech.giveTech("cryodesiccation")
// for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "research");
- // for (let i = 0; i < 15; i++) tech.giveTech()
- // for (let i = 10; i < tech.tech.length; i++) { tech.tech[i].isBanished = true }
+ // m.maxHealth = m.health = 100
// powerUps.research.changeRerolls(100000)
- // tech.tech[297].frequency = 100
// m.immuneCycle = Infinity //you can't take damage
- // simulation.enableConstructMode() //used to build maps in testing mode
- // level.temple();
- // spawn.cellBossCulture(1900, -500)
// powerUps.research.changeRerolls(100)
- // spawn.starter(1900, -500, 40)
- // spawn.starter(1900, -500, 20)
- // spawn.starter(1900, -500, 100)
- // for (let i = 0; i < 20; ++i) spawn.exploder(1900, -500)
- // spawn.timeSkipBoss(1900, -500)
- // level.difficultyIncrease(50) //30 is near max on hard //60 is near max on why
+ // tech.tech[297].frequency = 100
+ // spawn.starter(1900, -500, 200)
+ // for (let i = 0; i < 10; ++i) spawn.hopBullet(1900, -500)
+ // spawn.hopMomBoss(1900, -500)
+ // spawn.grenadier(1900, -1450, 10)
+ // level.difficultyIncrease(8 * 4) //30 is near max on hard //60 is near max on why
// level.testing(); //not in rotation, used for testing
// for (let i = 0; i < 7; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research");
// for (let i = 0; i < 4; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech");
@@ -2215,11 +2206,124 @@ const level = {
// spawn.randomLevelBoss(x + 950, y + -2200);
// },
+ // (x = offset.x, y = offset.y) => { //hopBoss1
+ // const button = level.button(x + 935, y + 0)
+ // button.isUp = true
+ // // spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges
+ // // spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges
+ // doCustomTopLayer.push(
+ // () => {
+ // button.draw();
+ // if (button.isUp) {
+ // button.query();
+ // if (!button.isUp) {
+ // // doCustomTopLayer.push(() => {
+ // // ctx.fillStyle = "rgba(150,255,220,0.15)"
+ // // ctx.fillRect(x + 250, y + -2725, 625, 725)
+ // // })
+ // const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress
+ // addMapToLevelInProgress = (who) => { //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
+ // who.collisionFilter.category = cat.map;
+ // who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
+ // Matter.Body.setStatic(who, true); //make static
+ // Composite.add(engine.world, who); //add to world
+ // }
+ // //map elements go here
+ // // spawn.mapRect(x + -50, y + -1875, 875, 200);
+ // // spawn.mapRect(x + 650, y + -2700, 125, 625);
+ // // spawn.mapRect(x + 1200, y + -2250, 250, 25);
+
+ // spawn.mapRect(x + -25, y + -1875, 1250, 200);
+ // // spawn.mapRect(x + 1075, y + -2700, 100, 650);
+ // spawn.mapRect(x + 1325, y + -1875, 475, 200);
+ // // spawn.mapRect(x + 1900, y + -1600, 125, 25);
+ // // spawn.mapRect(x + 900, y + -1875, 325, 25);
+ // // spawn.mapRect(x + 1375, y + -1875, 350, 25);
+
+ // // spawn.mapRect(x + 675, y + -2725, 50, 650);
+ // spawn.mapRect(x + 1900, y + -1675, 125, 25);
+ // spawn.mapRect(x + 1700, y + -1400, 325, 25);
+ // spawn.mapRect(x + -50, y + -1400, 325, 25);
+
+
+ // spawn.mapRect(x + -25, y + -700, 500, 25);
+ // spawn.mapRect(x + 675, y + -700, 600, 25);
+ // spawn.mapRect(x + 1475, y + -700, 500, 25);
+
+ // spawn.mapRect(x + 475, y + -1025, 200, 25);
+ // spawn.mapRect(x + 1275, y + -1025, 200, 25);
+ // spawn.mapRect(x + 475, y + -300, 200, 25);
+ // spawn.mapRect(x + 1275, y + -300, 200, 25);
+
+
+ // for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i])
+ // simulation.draw.setPaths() //update map graphics
+ // //mobs go here
+ // powerUps.directSpawn(x + 50, y - 1525, "ammo");
+ // powerUps.directSpawn(x + 1950, y - 1525, "ammo");
+ // spawn.hopMomBoss(x + 550, y + -2325)
+ // for (let i = 0; i < 20; ++i) spawn.hopBullet(x + 50 + 1900 * Math.random(), y + -2325)
+ // // spawn.hopper(x + 1500, y + -775);
+ // // spawn.hopper(x + 525, y + -775);
+ // }
+ // }
+ // }
+ // )
+ // },
+ (x = offset.x, y = offset.y) => { //hopBoss2
+ const button = level.button(x + 935, y + 0)
+ button.isUp = true
+ // spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges
+ // spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges
+ doCustomTopLayer.push(
+ () => {
+ button.draw();
+ if (button.isUp) {
+ button.query();
+ if (!button.isUp) {
+ const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress
+ addMapToLevelInProgress = (who) => { //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
+ who.collisionFilter.category = cat.map;
+ who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
+ Matter.Body.setStatic(who, true); //make static
+ Composite.add(engine.world, who); //add to world
+ }
+ //map elements go here
+ spawn.mapRect(x + 150, y + -1400, 750, 50);
+ spawn.mapRect(x + 1100, y + -1400, 750, 50);
+ spawn.mapRect(x + 1825, y + -1050, 200, 50);
+ spawn.mapRect(x + -25, y + -1050, 200, 50);
+ spawn.mapRect(x + 1825, y + -325, 200, 50);
+ spawn.mapRect(x + -25, y + -325, 200, 50);
+ spawn.mapRect(x + 275, y + -700, 525, 50);
+ spawn.mapRect(x + 1200, y + -700, 525, 50);
+ spawn.mapRect(x + -25, y + -1400, 125, 1125); //side walls
+ spawn.mapRect(x + 1900, y + -1400, 150, 1125);
+ spawn.mapRect(x + 1900, y + -2700, 125, 1000);
+ spawn.mapRect(x + -50, y + -2725, 150, 1025);
+ spawn.mapRect(x + -25, y + -1750, 450, 50);
+ spawn.mapRect(x + 1575, y + -1750, 450, 50);
+ spawn.mapRect(x + 525, y + -1750, 950, 50);
+ for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i])
+ simulation.draw.setPaths() //update map graphics
+ //mobs go here
+ powerUps.directSpawn(x + 50, y - 1525, "ammo");
+ powerUps.directSpawn(x + 1950, y - 1525, "ammo");
+ spawn.hopMomBoss(x + 800, y + -2200)
+ for (let i = 0; i < 10; ++i) spawn.hopBullet(x + 150 + 750 * Math.random(), y + -1600)
+ for (let i = 0; i < 10; ++i) spawn.hopBullet(x + 1100 + 750 * Math.random(), y + -1600)
+ spawn.hopper(x + 1500, y + -775);
+ spawn.hopper(x + 525, y + -775);
+ }
+ }
+ }
+ )
+ },
(x = offset.x, y = offset.y) => {
// const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) {
// toggle.isAddedElements = false
- const button = level.button(x + 950, y + 0)
+ const button = level.button(x + 935, y + 0)
button.isUp = true
@@ -2310,7 +2414,7 @@ const level = {
(x = offset.x, y = offset.y) => {
// const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) {
// toggle.isAddedElements = false
- const button = level.button(x + 950, y + 0)
+ const button = level.button(x + 935, y + 0)
button.isUp = true
//left ledges
spawn.mapVertex(x + 5, y + -1868, "0 0 0 -250 125 -250");
@@ -2412,7 +2516,7 @@ const level = {
empty = emptyOptions[Math.floor(Math.random() * emptyOptions.length)];
loot = lootOptions[Math.floor(Math.random() * lootOptions.length)];
upDown = upDownOptions[Math.floor(Math.random() * upDownOptions.length)];
- // upDown = upDownOptions[1] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION ***************************************
+ // upDown = upDownOptions[0] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION ***************************************
//3x2: 4 short rooms (3000x1500), 1 double tall room (3000x3000)
//rooms
let rooms = ["exit", "loot", "enter", "empty"]
@@ -2661,7 +2765,7 @@ const level = {
document.body.style.backgroundColor = "#ddd";
spawn.mapRect(-950, 0, 8200, 800); //ground
spawn.mapRect(-950, -1200, 800, 1400); //left wall
- spawn.mapRect(-950, -1800, 8200, 800); //roof
+ // spawn.mapRect(-950, -1800, 8200, 800); //roof
spawn.mapRect(-250, -400, 1000, 600); // shelf
spawn.mapRect(-250, -1200, 1000, 550); // shelf roof
// for (let i = 0; i < 10; ++i) powerUps.spawn(550, -800, "ammo", false);
@@ -2968,10 +3072,10 @@ const level = {
};
level.setPosToSpawn(0, -250); //normal spawn
- spawn.mapRect(5500, -330 + 20, 100, 20); //spawn this because the real exit is in the wrong spot
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
- level.exit.x = 550000;
- level.exit.y = -330;
+ spawn.mapRect(5500, -330 + 20, 100, 20); //spawn this because the real exit is in the wrong spot
+ level.exit.x = 0;
+ level.exit.y = -8000;
level.defaultZoom = 2500
simulation.zoomTransition(level.defaultZoom)
@@ -7892,6 +7996,7 @@ const level = {
spawn.bodyRect(-2100, 2050, 290, 30) //Portal platform
let b = body[body.length - 1];
+ b.isNotHoldable = true
cons[cons.length] = Constraint.create({
pointA: {
x: -1820,
diff --git a/js/player.js b/js/player.js
index 0840f95..171162a 100644
--- a/js/player.js
+++ b/js/player.js
@@ -1658,6 +1658,9 @@ const m = {
// description: "attract power ups from far away
deflecting doesn't drain energy
thrown blocks have",
// description: "gain energy when blocking
no recoil when blocking",
effect: () => {
+ m.fieldMeterColor = "#48f" //"#0c5"
+ m.eyeFillColor = m.fieldMeterColor
+
m.fieldShieldingScale = 0;
m.fieldBlockCD = 3;
m.grabPowerUpRange2 = 10000000
@@ -1724,9 +1727,8 @@ const m = {
ctx.strokeStyle = "#f0f";
ctx.stroke();
} else if (isFree) {
- //when blocking draw this graphic
- ctx.fillStyle = "rgba(110,170,200," + (0.2 + 0.4 * Math.random()) + ")";
- ctx.lineWidth = 2;
+ ctx.lineWidth = 2; //when blocking draw this graphic
+ ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})`
ctx.strokeStyle = "#000";
const len = mob[i].vertices.length - 1;
const mag = mob[i].radius
@@ -1739,11 +1741,11 @@ const m = {
ctx.fill();
ctx.stroke();
} else {
- //when blocking draw this graphic
- const eye = 15;
+
+ const eye = 15; //when blocking draw this graphic
const len = mob[i].vertices.length - 1;
- ctx.fillStyle = "rgba(110,170,200," + (0.2 + 0.4 * Math.random()) + ")";
ctx.lineWidth = 1;
+ ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})`
ctx.strokeStyle = "#000";
ctx.beginPath();
ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle));
@@ -1800,11 +1802,11 @@ const m = {
m.fieldAngle = m.angle
//draw field attached to player
if (m.holdingTarget) {
- ctx.fillStyle = "rgba(110,170,200," + (0.06 + 0.03 * Math.random()) + ")";
- ctx.strokeStyle = "rgba(110, 200, 235, " + (0.35 + 0.05 * Math.random()) + ")"
+ ctx.fillStyle = `rgba(110,150,220, ${0.06 + 0.03 * Math.random()})`
+ ctx.strokeStyle = `rgba(110,150,220, ${0.35 + 0.05 * Math.random()})`
} else {
- ctx.fillStyle = "rgba(110,170,200," + (0.27 + 0.2 * Math.random() - 0.1 * wave) + ")";
- ctx.strokeStyle = "rgba(110, 200, 235, " + (0.4 + 0.5 * Math.random()) + ")"
+ ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})`
+ ctx.strokeStyle = `rgba(110,150,220, ${0.4 + 0.5 * Math.random()})`
}
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false);
@@ -1828,8 +1830,8 @@ const m = {
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)
if (!input.field) { //&& tech.isFieldFree
//draw field free of player
- ctx.fillStyle = "rgba(110,170,200," + (0.27 + 0.2 * Math.random() - 0.1 * wave) + ")";
- ctx.strokeStyle = "rgba(110, 200, 235, " + (0.4 + 0.5 * Math.random()) + ")"
+ ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})`
+ ctx.strokeStyle = `rgba(110,180,255, ${0.4 + 0.5 * Math.random()})`
ctx.beginPath();
ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false);
ctx.lineWidth = 2.5 - 1.5 * wave;
@@ -1842,7 +1844,8 @@ const m = {
m.perfectPush(true);
}
}
- m.drawFieldMeter()
+ // m.drawFieldMeter()
+ m.drawFieldMeter("rgba(0,0,0,0.2)")
if (tech.isPerfectBrake) { //cap mob speed around player
const range = 200 + 140 * wave + 150 * m.energy
for (let i = 0; i < mob.length; i++) {
@@ -2018,17 +2021,19 @@ const m = {
description: "excess energy used to build drones
use energy to deflect mobs
generate 12 energy per second",
//double your default energy regeneration
effect: () => {
- // m.fieldMeterColor = "#0c5"
- // m.eyeFillColor = m.fieldMeterColor
+ m.fieldMeterColor = "#ff0"
+ m.eyeFillColor = m.fieldMeterColor
m.hold = function() {
if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 300 && (m.cycle % 2)) {
- // if (tech.isBotField) {
- // b.randomBot(this.position, false)
- // bullet[bullet.length - 1].endCycle = simulation.cycle + 840 //14 seconds
- // m.energy -= 0.35
- // } else
if (tech.isSporeField) {
- if (tech.isSporeWorm) {
+ if (tech.isSporeFlea) {
+ const drain = 0.16 + (Math.max(bullet.length, 130) - 130) * 0.02
+ if (m.energy > drain) {
+ m.energy -= drain
+ const speed = m.crouch ? 20 + 8 * Math.random() : 10 + 3 * Math.random()
+ b.flea({ x: m.pos.x + 35 * Math.cos(m.angle), y: m.pos.y + 35 * Math.sin(m.angle) }, { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) })
+ }
+ } else if (tech.isSporeWorm) {
const drain = 0.16 + (Math.max(bullet.length, 130) - 130) * 0.02
if (m.energy > drain) {
m.energy -= drain
@@ -2260,7 +2265,7 @@ const m = {
const damageRadius = circleRadiusScale * this.circleRadius
const dischargeRange = 150 + 1600 * tech.plasmaDischarge + 1.3 * damageRadius
for (let i = 0, len = mob.length; i < len; i++) {
- if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet)) {
+ if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet) && !mob[i].isInvulnerable) {
const sub = Vector.magnitude(Vector.sub(this.position, mob[i].position))
if (sub < damageRadius + mob[i].radius) {
// if (!this.isAttached && !mob[i].isMobBullet) this.isPopping = true
@@ -2530,11 +2535,43 @@ const m = {
// description: "use energy to stop time
while time is stopped you can move and fire
and collisions do 50% less harm",
description: "use energy to stop time
+25% movement, jumping, and fire rate
generate 18 energy per second",
set() {
+ // m.fieldMeterColor = "#0fc"
+ // m.fieldMeterColor = "#ff0"
+ m.fieldMeterColor = "#3fe"
+ m.eyeFillColor = m.fieldMeterColor
+
m.fieldFireRate = 0.75
b.setFireCD();
m.fieldFx = 1.2
m.fieldJump = 1.09
m.setMovement();
+
+ const timeStop = () => {
+ m.immuneCycle = m.cycle + 10; //immune to harm while time is stopped, this also disables regen
+ //draw field everywhere
+ ctx.globalCompositeOperation = "saturation"
+ ctx.fillStyle = "#ccc";
+ ctx.fillRect(-100000, -100000, 200000, 200000)
+ ctx.globalCompositeOperation = "source-over"
+ //stop time
+ m.isBodiesAsleep = true;
+
+ function sleep(who) {
+ for (let i = 0, len = who.length; i < len; ++i) {
+ if (!who[i].isSleeping) {
+ who[i].storeVelocity = who[i].velocity
+ who[i].storeAngularVelocity = who[i].angularVelocity
+ }
+ Matter.Sleeping.set(who[i], true)
+ }
+ }
+ sleep(mob);
+ sleep(body);
+ sleep(bullet);
+
+ simulation.cycle--; //pause all functions that depend on game cycle increasing
+ }
+
if (tech.isRewindField) {
this.rewindCount = 0
m.grabPowerUpRange2 = 300000
@@ -2562,6 +2599,7 @@ const m = {
m.drawHold(m.holdingTarget);
m.holding();
m.throwBlock();
+ m.wakeCheck();
} else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed
m.grabPowerUp();
if (this.rewindCount === 0) m.lookForPickUp();
@@ -2612,12 +2650,18 @@ const m = {
}
}
}
+ m.wakeCheck();
} else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
m.pickUp();
this.rewindCount = 0;
+ m.wakeCheck();
+ } else if (tech.isTimeStop && player.speed < 1 && m.onGround && !input.fire) {
+ timeStop();
+ this.rewindCount = 0;
} 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)
this.rewindCount = 0;
+ m.wakeCheck();
}
if (m.energy < m.maxEnergy) m.regenEnergy(); //extra energy regen
m.drawFieldMeter() // this calls m.regenEnergy(); also
@@ -2637,37 +2681,30 @@ const m = {
m.lookForPickUp();
if (m.energy > m.drain) {
m.energy -= m.drain;
- if (m.energy < m.drain) {
+ if (m.energy < m.drain) { //out of energy
m.fieldCDcycle = m.cycle + 120;
m.energy = 0;
m.wakeCheck();
}
- m.immuneCycle = m.cycle + 10; //immune to harm while time is stopped, this also disables regen
- //draw field everywhere
- ctx.globalCompositeOperation = "saturation"
- ctx.fillStyle = "#ccc";
- ctx.fillRect(-100000, -100000, 200000, 200000)
- ctx.globalCompositeOperation = "source-over"
- //stop time
- m.isBodiesAsleep = true;
-
- function sleep(who) {
- for (let i = 0, len = who.length; i < len; ++i) {
- if (!who[i].isSleeping) {
- who[i].storeVelocity = who[i].velocity
- who[i].storeAngularVelocity = who[i].angularVelocity
- }
- Matter.Sleeping.set(who[i], true)
- }
- }
- sleep(mob);
- sleep(body);
- sleep(bullet);
-
- simulation.cycle--; //pause all functions that depend on game cycle increasing
+ timeStop();
} else { //holding, but field button is released
m.wakeCheck();
}
+ } else if (tech.isTimeStop && player.speed < 1 && m.onGround && m.fireCDcycle < m.cycle && !input.fire) {
+ timeStop();
+ //makes things move at 1/5 time rate, but has an annoying flicker for mob graphics, and other minor bugs
+ // if (!(m.cycle % 4)) {
+ // // requestAnimationFrame(() => {
+ // m.wakeCheck();
+ // // simulation.timePlayerSkip(1)
+ // // }); //wrapping in animation frame prevents errors, probably
+ // ctx.globalCompositeOperation = "saturation"
+ // ctx.fillStyle = "#ccc";
+ // ctx.fillRect(-100000, -100000, 200000, 200000)
+ // ctx.globalCompositeOperation = "source-over"
+ // } else {
+ // timeStop();
+ // }
} else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released
m.wakeCheck();
m.pickUp();
@@ -2682,6 +2719,11 @@ const m = {
}
},
effect() {
+ if (tech.isTimeStop) {
+ m.fieldHarmReduction = 0.66; //33% reduction
+ } else {
+ m.fieldHarmReduction = 1;
+ }
this.set();
}
},
@@ -2750,7 +2792,6 @@ const m = {
} 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)
}
-
//not shooting (or using field) enable cloak
if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle
if (m.fireCDcycle + 30 < m.cycle && !input.fire) { //automatically cloak if not firing
@@ -2992,6 +3033,9 @@ const m = {
//field radius decreases out of line of sight
description: "use energy to guide blocks
unlock tech from other fields
generate 6 energy per second",
effect: () => {
+ m.fieldMeterColor = "#333"
+ m.eyeFillColor = m.fieldMeterColor
+
m.fieldPhase = 0;
m.fieldPosition = {
x: simulation.mouseInGame.x,
@@ -3179,7 +3223,7 @@ const m = {
m.fieldOn = false
m.fieldRadius = 0
}
- m.drawFieldMeter()
+ m.drawFieldMeter("rgba(0,0,0,0.2)")
}
}
},
@@ -3189,6 +3233,9 @@ const m = {
description: "use energy to tunnel through a wormhole
+4% chance to duplicate spawned power ups
generate 6 energy per second", //
bullets may also traverse wormholes
drain: 0,
effect: function() {
+ m.fieldMeterColor = "#bbf" //"#0c5"
+ m.eyeFillColor = m.fieldMeterColor
+
m.duplicateChance = 0.04
m.fieldRange = 0
powerUps.setDupChance(); //needed after adjusting duplication chance
diff --git a/js/powerup.js b/js/powerup.js
index 17fd7e8..374177b 100644
--- a/js/powerup.js
+++ b/js/powerup.js
@@ -303,7 +303,7 @@ const powerUps = {
},
endDraft(type, isCanceled = false) { //type should be a gun, tech, or field
if (isCanceled) {
- if (tech.isCancelTech && Math.random() < 0.96) {
+ if (tech.isCancelTech && Math.random() < 0.94) {
// powerUps.research.use('tech')
powerUps[type].effect();
return
@@ -396,9 +396,6 @@ const powerUps = {
}
}, delay);
}
- // for (let i = 0, len = tech.tech.length; i < len; i++) {
- // if (tech.tech[i].name === "bot fabrication") tech.tech[i].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)`
- // }
}
if (tech.isDeathAvoid && document.getElementById("tech-anthropic")) {
document.getElementById("tech-anthropic").innerHTML = `-${powerUps.research.count}`
diff --git a/js/simulation.js b/js/simulation.js
index c7987d5..dfc0cad 100644
--- a/js/simulation.js
+++ b/js/simulation.js
@@ -120,6 +120,44 @@ const simulation = {
}
simulation.isTimeSkipping = false;
},
+ // timeMobSkip() {
+ // simulation.gravity();
+ // Engine.update(engine, simulation.delta);
+ // simulation.wipe();
+ // simulation.textLog();
+ // if (m.onGround) {
+ // m.groundControl()
+ // } else {
+ // m.airControl()
+ // }
+ // m.move();
+ // m.look();
+ // simulation.camera();
+ // level.custom();
+ // powerUps.do();
+ // mobs.draw();
+ // simulation.draw.cons();
+ // simulation.draw.body();
+ // if (!m.isBodiesAsleep) {
+ // simulation.checks();
+ // // mobs.loop();
+ // }
+ // mobs.healthBar();
+ // m.draw();
+ // m.hold();
+ // // v.draw(); //working on visibility work in progress
+ // level.customTopLayer();
+ // simulation.draw.drawMapPath();
+ // b.fire();
+ // b.bulletRemove();
+ // b.bulletDraw();
+ // if (!m.isBodiesAsleep) b.bulletDo();
+ // simulation.drawCircle();
+ // // simulation.clip();
+ // ctx.restore();
+ // simulation.drawCursor();
+ // // simulation.pixelGraphics();
+ // },
mouse: {
x: canvas.width / 2,
y: canvas.height / 2
@@ -857,6 +895,7 @@ const simulation = {
let droneCount = 0
let sporeCount = 0
let wormCount = 0
+ let fleaCount = 0
let deliveryCount = 0
for (let i = 0; i < bullet.length; ++i) {
if (bullet[i].isDrone) {
@@ -866,6 +905,8 @@ const simulation = {
sporeCount++
} else if (bullet[i].wormSize) {
wormCount++
+ } else if (bullet[i].isFlea) {
+ fleaCount++
}
}
@@ -920,6 +961,21 @@ const simulation = {
}
}
requestAnimationFrame(respawnWorms);
+
+ //respawn fleas in animation frame
+ let respawnFleas = () => {
+ if (fleaCount > 0) {
+ requestAnimationFrame(respawnFleas);
+ if (!simulation.paused && !simulation.isChoosing) {
+ fleaCount--
+ const where = { x: level.enter.x + 50, y: level.enter.y - 60 }
+ const speed = 6 + 3 * Math.random()
+ const angle = 2 * Math.PI * Math.random()
+ b.flea({ x: where.x + 100 * (Math.random() - 0.5), y: where.y + 120 * (Math.random() - 0.5) }, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) })
+ }
+ }
+ }
+ requestAnimationFrame(respawnFleas);
}
if (tech.isQuantumEraser) {
diff --git a/js/spawn.js b/js/spawn.js
index c7d4b33..93b17ae 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -1,7 +1,7 @@
//main object for spawning things in a level
const spawn = {
nonCollideBossList: ["cellBossCulture", "bomberBoss", "powerUpBoss", "growBossCulture"],
- // other bosses: suckerBoss, laserBoss, tetherBoss, bounceBoss, sprayBoss, mineBoss //these need a particular level to work so they are not included in the random pool
+ // other bosses: suckerBoss, laserBoss, tetherBoss, bounceBoss, sprayBoss, mineBoss, hopMomBoss //these need a particular level to work so they are not included in the random pool
randomBossList: [
"orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss",
"powerUpBoss", "powerUpBossBaby", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss",
@@ -51,13 +51,13 @@ const spawn = {
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].isDropPowerUp && mob[i].alive) { //&& !mob[i].isBoss
if (mob[i].isFinalBoss) {
- mob[i].health = 0.66
+ tech.quantumEraserCount = 0;
+ return
} else {
tech.isQuantumEraserDuplication = true
mob[i].death()
tech.isQuantumEraserDuplication = false
}
-
//graphics
const color = 'rgba(255,255,255, 0.8)'
simulation.drawList.push({
@@ -81,7 +81,6 @@ const spawn = {
color: color, //"rgb(0,0,0)",
time: 120
});
-
tech.quantumEraserCount--
simulation.makeTextLog(`tech.quantumEraserCount = ${tech.quantumEraserCount}`)
if (tech.quantumEraserCount < 1) break
@@ -1580,99 +1579,166 @@ const spawn = {
}
};
},
- hopBoss(x, y, radius = 90) {
+ hopBullet(x, y, radius = 10 + Math.ceil(Math.random() * 8)) {
mobs.spawn(x, y, 5, radius, "rgb(0,200,180)");
let me = mob[mob.length - 1];
- me.isBoss = true;
+ me.stroke = "transparent";
+ me.leaveBody = false;
+ me.isDropPowerUp = false;
+ // me.isBadTarget = true;
+ me.isMobBullet = true;
+ me.showHealthBar = false;
+ me.timeLeft = 1500 + Math.floor(600 * Math.random());
- me.g = 0.005; //required if using this.gravity
+ me.isRandomMove = Math.random() < 0.3 //most chase player, some don't
+ me.accelMag = 0.01; //jump height
+ me.g = 0.0015; //required if using this.gravity
me.frictionAir = 0.01;
me.friction = 1
me.frictionStatic = 1
me.restitution = 0;
- me.accelMag = 0.07;
- me.delay = 120 * simulation.CDScale;
- me.randomHopFrequency = 200
- me.randomHopCD = simulation.cycle + me.randomHopFrequency;
- // me.memory = 420;
- me.isInAir = false
- Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger
+ me.delay = 130 + 60 * simulation.CDScale;
+ // Matter.Body.rotate(me, Math.random() * Math.PI);
+ me.collisionFilter.category = cat.mobBullet;
+ me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet;
+ me.onHit = function() {
+ this.explode(this.mass * 2);
+ };
+ me.do = function() {
+ this.gravity();
+ this.checkStatus();
+ if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) {
+ this.cd = simulation.cycle + this.delay;
+ if (this.isRandomMove || Math.random() < 0.2) {
+ this.force.x += (0.01 + 0.03 * Math.random()) * this.mass * (Math.random() < 0.5 ? 1 : -1); //random move
+ } else {
+ this.force.x += (0.01 + 0.03 * Math.random()) * this.mass * (player.position.x > this.position.x ? 1 : -1); //chase player
+ }
+ this.force.y -= (0.04 + 0.04 * Math.random()) * this.mass
+ }
+ this.timeLimit();
+ };
+ },
+ hopMomBoss(x, y, radius = 120) {
+ mobs.spawn(x, y, 5, radius, "rgb(0,200,180)");
+ let me = mob[mob.length - 1];
+ me.isBoss = true;
+ me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
+ me.accelMag = 0.05; //jump height
+ me.g = 0.003; //required if using this.gravity
+ me.frictionAir = 0.01;
+ me.friction = 1
+ me.frictionStatic = 1
+ me.restitution = 0;
+ me.delay = 100 + 40 * simulation.CDScale;
+ Matter.Body.rotate(me, Math.random() * Math.PI);
spawn.shield(me, x, y, 1);
- spawn.spawnOrbitals(me, radius + 60, 1)
me.onDeath = function() {
powerUps.spawnBossPowerUp(this.position.x, this.position.y)
+ // for (let i = 0, len = 3 + 0.1 * simulation.difficulty; i < len; ++i) spawn.hopBullet(this.position.x + 100 * (Math.random() - 0.5), this.position.y + 100 * (Math.random() - 0.5))
};
- me.lastSpeed = me.speed
- me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.do = function() {
- // this.armor();
this.gravity();
this.seePlayerCheck();
this.checkStatus();
- if (this.seePlayer.recall) {
- const deltaSpeed = this.lastSpeed - this.speed
- this.lastSpeed = this.speed
- if (deltaSpeed > 13 && this.speed < 5) { //if the player slows down greatly in one cycle
- //damage and push player away, push away blocks
- const range = 800 //Math.min(800, 50 * deltaSpeed)
- for (let i = body.length - 1; i > -1; i--) {
- if (!body[i].isNotHoldable) {
- sub = Vector.sub(body[i].position, this.position);
- dist = Vector.magnitude(sub);
- if (dist < range) {
- knock = Vector.mult(Vector.normalise(sub), Math.min(20, 50 * body[i].mass / dist));
- body[i].force.x += knock.x;
- body[i].force.y += knock.y;
- }
- }
- }
+ if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) {
+ this.cd = simulation.cycle + this.delay;
+ //spawn hopBullets after each jump
+ for (let i = 0, len = 1 + 0.05 * simulation.difficulty; i < len; ++i) spawn.hopBullet(this.position.x + 100 * (Math.random() - 0.5), this.position.y + 100 * (Math.random() - 0.5))
- simulation.drawList.push({ //draw radius
- x: this.position.x,
- y: this.position.y,
- radius: range,
- color: "rgba(0,200,180,0.6)",
- time: 4
- });
- }
-
- if (this.isInAir) {
- if (this.velocity.y > -0.01 && Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { //not moving up, and has hit the map or a body
- this.isInAir = false //landing
- this.cd = simulation.cycle + this.delay
-
- }
- } else { //on ground
- if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { //jump
- this.isInAir = true
- const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass;
- const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
- this.force.x += forceMag * Math.cos(angle);
- this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity
- }
- }
-
- // if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) {
- // this.cd = simulation.cycle + this.delay;
- // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass;
- // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
- // this.force.x += forceMag * Math.cos(angle);
- // this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity
- // }
- } else {
- //randomly hob if not aware of player
- if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) {
- this.randomHopCD = simulation.cycle + this.randomHopFrequency;
- //slowly change randomHopFrequency after each hop
- this.randomHopFrequency = Math.max(100, this.randomHopFrequency + 200 * (0.5 - Math.random()));
- const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.5 + Math.random() * 0.2);
- const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI;
- this.force.x += forceMag * Math.cos(angle);
- this.force.y += forceMag * Math.sin(angle) - (0.1 + 0.08 * Math.random()) * this.mass; //antigravity
- }
+ this.force.x += (0.02 + 0.06 * Math.random()) * this.mass * (player.position.x > this.position.x ? 1 : -1);
+ this.force.y -= (0.08 + 0.08 * Math.random()) * this.mass
}
};
},
+ // hopBoss(x, y, radius = 90) {
+ // mobs.spawn(x, y, 5, radius, "rgb(0,200,180)");
+ // let me = mob[mob.length - 1];
+ // me.isBoss = true;
+ // me.g = 0.005; //required if using this.gravity
+ // me.frictionAir = 0.01;
+ // me.friction = 1
+ // me.frictionStatic = 1
+ // me.restitution = 0;
+ // me.accelMag = 0.07;
+ // me.delay = 120 * simulation.CDScale;
+ // me.randomHopFrequency = 200
+ // me.randomHopCD = simulation.cycle + me.randomHopFrequency;
+ // // me.memory = 420;
+ // me.isInAir = false
+ // Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger
+ // spawn.shield(me, x, y, 1);
+ // spawn.spawnOrbitals(me, radius + 60, 1)
+ // me.onDeath = function() {
+ // powerUps.spawnBossPowerUp(this.position.x, this.position.y)
+ // };
+ // me.lastSpeed = me.speed
+ // me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
+ // me.do = function() {
+ // // this.armor();
+ // this.gravity();
+ // this.seePlayerCheck();
+ // this.checkStatus();
+ // if (this.seePlayer.recall) {
+ // const deltaSpeed = this.lastSpeed - this.speed
+ // this.lastSpeed = this.speed
+ // if (deltaSpeed > 13 && this.speed < 5) { //if the player slows down greatly in one cycle
+ // //damage and push player away, push away blocks
+ // const range = 800 //Math.min(800, 50 * deltaSpeed)
+ // for (let i = body.length - 1; i > -1; i--) {
+ // if (!body[i].isNotHoldable) {
+ // sub = Vector.sub(body[i].position, this.position);
+ // dist = Vector.magnitude(sub);
+ // if (dist < range) {
+ // knock = Vector.mult(Vector.normalise(sub), Math.min(20, 50 * body[i].mass / dist));
+ // body[i].force.x += knock.x;
+ // body[i].force.y += knock.y;
+ // }
+ // }
+ // }
+ // simulation.drawList.push({ //draw radius
+ // x: this.position.x,
+ // y: this.position.y,
+ // radius: range,
+ // color: "rgba(0,200,180,0.6)",
+ // time: 4
+ // });
+ // }
+ // if (this.isInAir) {
+ // if (this.velocity.y > -0.01 && Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { //not moving up, and has hit the map or a body
+ // this.isInAir = false //landing
+ // this.cd = simulation.cycle + this.delay
+ // }
+ // } else { //on ground
+ // if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { //jump
+ // this.isInAir = true
+ // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass;
+ // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
+ // this.force.x += forceMag * Math.cos(angle);
+ // this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity
+ // }
+ // }
+ // // if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) {
+ // // this.cd = simulation.cycle + this.delay;
+ // // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass;
+ // // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
+ // // this.force.x += forceMag * Math.cos(angle);
+ // // this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity
+ // // }
+ // } else {
+ // //randomly hob if not aware of player
+ // if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) {
+ // this.randomHopCD = simulation.cycle + this.randomHopFrequency;
+ // //slowly change randomHopFrequency after each hop
+ // this.randomHopFrequency = Math.max(100, this.randomHopFrequency + 200 * (0.5 - Math.random()));
+ // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.5 + Math.random() * 0.2);
+ // const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI;
+ // this.force.x += forceMag * Math.cos(angle);
+ // this.force.y += forceMag * Math.sin(angle) - (0.1 + 0.08 * Math.random()) * this.mass; //antigravity
+ // }
+ // }
+ // };
+ // },
spinner(x, y, radius = 30 + Math.ceil(Math.random() * 35)) {
mobs.spawn(x, y, 5, radius, "#000000");
let me = mob[mob.length - 1];
@@ -5118,7 +5184,7 @@ const spawn = {
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.onHit = function() {
- this.explode(this.mass * 20);
+ this.explode(this.mass);
};
Matter.Body.setDensity(me, 0.00004); //normal is 0.001
@@ -5135,7 +5201,7 @@ const spawn = {
//damage player if in range
if (Vector.magnitude(Vector.sub(player.position, this.position)) < pulseRadius && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage
- m.damage(0.02 * simulation.dmgScale);
+ m.damage(0.015 * simulation.dmgScale);
}
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
diff --git a/js/tech.js b/js/tech.js
index 210e444..ed55bf9 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -256,7 +256,7 @@ const tech = {
return dmg * tech.slowFire * tech.aimDamage
},
duplicationChance() {
- return Math.max(0, (tech.isPowerUpsVanish ? 0.12 : 0) + (tech.isStimulatedEmission ? 0.15 : 0) + tech.cancelCount * 0.045 + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0) + tech.isQuantumEraserDuplication * (1 - 0.01 * (simulation.difficultyMode ** 2)))
+ return Math.max(0, (tech.isPowerUpsVanish ? 0.12 : 0) + (tech.isStimulatedEmission ? 0.15 : 0) + tech.cancelCount * 0.045 + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0) + tech.isQuantumEraserDuplication * (1 - 0.016 * (simulation.difficultyMode ** 2)))
},
isScaleMobsWithDuplication: false,
maxDuplicationEvent() {
@@ -2977,7 +2977,7 @@ const tech = {
effect() {
tech.isBrainstorm = true
tech.isBrainstormActive = false
- tech.brainStormDelay = 145 - simulation.difficultyMode * 10
+ tech.brainStormDelay = 150 - simulation.difficultyMode * 7
},
remove() {
tech.isBrainstorm = false
@@ -3319,7 +3319,7 @@ const tech = {
{
name: "options exchange",
link: `options exchange`,
- description: `clicking × for a field, tech, or gun has a 96%
chance to randomize choices and not cancel`,
+ description: `clicking × for a field, tech, or gun has a 94%
chance to randomize choices and not cancel`,
maxCount: 1,
count: 0,
frequency: 1,
@@ -5205,7 +5205,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm
+ return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm || tech.isSporeFlea
},
requires: "spores",
effect() {
@@ -5225,7 +5225,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm
+ return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm || tech.isSporeFlea
},
requires: "spores",
effect() {
@@ -5263,7 +5263,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isEnergyHealth || tech.isSporeWorm
+ return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isEnergyHealth || tech.isSporeWorm || tech.isSporeFlea
},
requires: "spores, not mass-energy",
effect() {
@@ -5273,26 +5273,55 @@ const tech = {
tech.isMutualism = false
}
},
- // {
- // name: "worm-shot",
- // link: `worm-shot`,
- // description: "shotgun hatches 3-4 mob seeking worms
worms benefit from spore technology", //
worms seek out nearby mobs
- // isGunTech: true,
- // maxCount: 1,
- // count: 0,
- // frequency: 2,
- // frequencyDefault: 2,
- // allowed() {
- // return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles
- // },
- // requires: "shotgun, not incendiary, nail-shot, rivets, foam-shot, ice-shot, needles",
- // effect() {
- // tech.isWormShot = true;
- // },
- // remove() {
- // tech.isWormShot = false;
+ {
+ name: "fleas",
+ description: "sporangium hatch fleas
spore tech applies to fleas",
+ isGunTech: true,
+ maxCount: 1,
+ count: 0,
+ frequency: 3,
+ frequencyDefault: 3,
+ allowed() {
+ return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isSporeWorm
+ },
+ requires: "spores, not worms",
+ effect() {
+ tech.isSporeFlea = true
+ },
+ remove() {
+ tech.isSporeFlea = false
+
+ }
+ },
+
+ // ammoBonus: 8,
+ // effect() {
+ // tech.isRailGun = true;
+ // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
+ // if (b.guns[i].name === "harpoon") {
+ // b.guns[i].chooseFireMethod()
+ // b.guns[i].ammoPack = this.ammoBonus;
+ // b.guns[i].ammo = b.guns[i].ammo * this.ammoBonus;
+ // simulation.updateGunHUD();
+ // break
+ // }
// }
// },
+ // remove() {
+ // if (tech.isRailGun) {
+ // tech.isRailGun = false;
+ // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
+ // if (b.guns[i].name === "harpoon") {
+ // b.guns[i].chooseFireMethod()
+ // b.guns[i].ammoPack = 0.6;
+ // b.guns[i].ammo = Math.ceil(b.guns[i].ammo / this.ammoBonus);
+ // simulation.updateGunHUD();
+ // break
+ // }
+ // }
+ // }
+ // }
+
{
name: "nematodes",
description: "shotgun and sporangium hatch worms
spore tech applies to worms",
@@ -5302,9 +5331,9 @@ const tech = {
frequency: 3,
frequencyDefault: 3,
allowed() {
- return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)
+ return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeFlea
},
- requires: "spores",
+ requires: "spores, not fleas",
effect() {
tech.isSporeWorm = true
},
@@ -5333,7 +5362,7 @@ const tech = {
},
{
name: "path integration",
- description: "drones, spores, and worms
travel with you through levels",
+ description: "drones, spores, fleas, and worms
travel with you through levels",
isGunTech: true,
maxCount: 1,
count: 0,
@@ -5342,7 +5371,7 @@ const tech = {
allowed() {
return (tech.isSporeFollow && (tech.haveGunCheck("spores") || (tech.haveGunCheck("shotgun") && tech.isSporeWorm))) || tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isMissileField || tech.isIceField))
},
- requires: "spores, worms, diplochory, drones",
+ requires: "spores, worms, flagella, drones",
effect() {
tech.isDronesTravel = true
},
@@ -5353,7 +5382,7 @@ const tech = {
{
name: "anti-shear topology",
link: `anti-shear topology`,
- description: "+30% projectile duration
drone, spore, worm, missile, foam, wave, neutron, ice",
+ description: "+30% projectile duration
drone spore worm flea missile foam wave neutron ice",
isGunTech: true,
maxCount: 3,
count: 0,
@@ -5362,7 +5391,7 @@ const tech = {
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || tech.haveGunCheck("spores") || tech.haveGunCheck("drones") || tech.haveGunCheck("missiles") || tech.haveGunCheck("foam") || tech.haveGunCheck("matter wave") || tech.isNeutronBomb || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 || tech.isSporeWorm || tech.isFoamBotUpgrade || tech.isFoamBall
},
- requires: "drones, spores, missiles, foam, matter wave, neutron bomb, ice IX",
+ requires: "drones, spores, missiles, foam, matter wave, neutron bomb, ice IX, flea",
effect() {
tech.isBulletsLastLonger += 0.3
},
@@ -5662,14 +5691,14 @@ const tech = {
},
{
name: "necrophage",
- description: "if foam or worms kill their target
grow 3 copies",
+ description: "if foam, fleas, or worms kill their target
grow 3 copies",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.haveGunCheck("foam") || tech.isFoamBall || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isSporeWorm
+ return tech.haveGunCheck("foam") || tech.isFoamBall || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isSporeWorm || tech.isSporeFlea
},
requires: "foam, worms",
effect() {
@@ -5818,7 +5847,7 @@ const tech = {
},
{
name: "railgun",
- description: `+50% harpoon density, but they don't retract
+800% harpoon ammo per ${powerUps.orb.ammo(1)}`,
+ description: `+50% harpoon density, but they don't retract
+900% harpoon ammo per ${powerUps.orb.ammo(1)}`,
isGunTech: true,
maxCount: 1,
count: 0,
@@ -5828,7 +5857,7 @@ const tech = {
return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isGrapple
},
requires: "harpoon, not UHMWPE, induction furnace, grappling hook",
- ammoBonus: 8,
+ ammoBonus: 9,
effect() {
tech.isRailGun = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
@@ -6109,7 +6138,7 @@ const tech = {
},
{
name: "laser diode",
- description: "+30% laser energy efficiency
affects laser-gun, laser-bot, laser-mines, pulse",
+ description: "+33% laser energy efficiency",
isGunTech: true,
maxCount: 1,
count: 0,
@@ -6120,7 +6149,7 @@ const tech = {
},
requires: "laser, not free-electron",
effect() {
- tech.isLaserDiode = 0.70; //100%-37%
+ tech.isLaserDiode = 0.66; //100%-37%
tech.laserColor = "rgb(0, 11, 255)"
tech.laserColorAlpha = "rgba(0, 11, 255,0.5)"
},
@@ -6177,7 +6206,7 @@ const tech = {
{
name: "iridescence",
// description: "if a laser hits a mob at a low angle of illumination
+66% laser damage",
- description: "if mobs are struck near their center
+88% laser damage",
+ description: "if laser beams hit mobs near their center
+88% laser damage",
isGunTech: true,
maxCount: 3,
count: 0,
@@ -6196,7 +6225,7 @@ const tech = {
},
{
name: "lens",
- description: "if directed through a revolving +π / 4 circular arc
+150% laser gun damage",
+ description: "+150% laser gun damage if it passes
through a revolving +90° arc circular lens", //π / 2
isGunTech: true,
maxCount: 3,
count: 0,
@@ -6255,7 +6284,7 @@ const tech = {
},
{
name: "diffraction grating",
- description: `+1 laser gun beam`,
+ description: `+1 diverging laser gun beam`,
isGunTech: true,
maxCount: 9,
count: 0,
@@ -6384,7 +6413,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.isPulseLaser && !tech.beamSplitter
+ return tech.haveGunCheck("laser") && tech.isPulseLaser && !tech.beamSplitter
},
requires: "laser gun, pulse, not diffraction grating",
effect() {
@@ -7090,7 +7119,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.isExtruder
+ return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && tech.isExtruder
},
requires: "extruder",
effect() {
@@ -7146,8 +7175,8 @@ const tech = {
isFieldTech: true,
maxCount: 1,
count: 0,
- frequency: 2,
- frequencyDefault: 2,
+ frequency: 1,
+ frequencyDefault: 1,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isEnergyHealth && !tech.isTimeSkip
},
@@ -7162,6 +7191,27 @@ const tech = {
if (this.count) m.fieldUpgrades[m.fieldMode].set()
}
},
+ {
+ name: "frame-dragging", //"non-inertial frame",
+ description: "when not moving time dilation stops time
+33% defense",
+ isFieldTech: true,
+ maxCount: 1,
+ count: 0,
+ frequency: 1,
+ frequencyDefault: 1,
+ allowed() {
+ return m.fieldUpgrades[m.fieldMode].name === "time dilation"
+ },
+ requires: "time dilation",
+ effect() {
+ tech.isTimeStop = true;
+ m.fieldHarmReduction = 0.66; //33% reduction
+ },
+ remove() {
+ tech.isTimeStop = false;
+ if (m.fieldUpgrades[m.fieldMode].name === "time dilation") m.fieldHarmReduction = 1;
+ }
+ },
{
name: "Lorentz transformation",
description: `use ${powerUps.orb.research(3)}
+50% movement, jumping, and fire rate`,
@@ -7235,7 +7285,7 @@ const tech = {
},
{
name: "quantum eraser",
- descriptionFunction() { return `for each mob left alive after you exit a level
kill a mob as they spawn at ${100-simulation.difficultyMode**2}% duplication` },
+ descriptionFunction() { return `for each mob left alive after you exit a level
kill a mob as they spawn at +${100-1.6*simulation.difficultyMode**2}% duplication` },
description: `for each mob left alive after you exit a level
kill a mob as they spawn at 100% duplication`,
isFieldTech: true,
@@ -10403,6 +10453,7 @@ const tech = {
isFastTime: null,
isAnthropicTech: null,
isSporeWorm: null,
+ isSporeFlea: null,
isFoamShot: null,
isIceShot: null,
isBlockRestitution: null,
diff --git a/todo.txt b/todo.txt
index a607401..183e888 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,32 +1,46 @@
******************************************************** NEXT PATCH **************************************************
-tech: iridescence - laser does 100% damage to mobs hit near their center
-tech: lens - laser does extra damage if you fire through a lens that revolves around you
- tech: arc length - increase the size of the lens
+tech: fleas - replace spores with little hoppers
-virtual particles costs 4->6 research for 11% duplication
-quantum eraser has less duplication chance at higher difficulty modes
-community map temple updates
+frame-dragging - time dilation field stops time when you aren't moving or firing, +33% defense
+ the odds of finding this tech is low because I find it kinda annoying, but maybe you will like it
+
+molecular assembler field energy meter is yellow
+ wormhole is lavender
+ perfect diamagnetism is blue
+ time dilation is green blue
+ pilot wave is black
+
+new room in labs: hopBossMom
+harpoon now auto targets by default, but disabled when crouched
+pulse + neocognitron auto targeting also disabled when crouched
bug fixes
*********************************************************** TODO *****************************************************
-give laser gun _____ if you fire in an angle range
- draw angle range as a slowly rotation circle arc around player
- effect:
- bonus damage
- extra beams
- extra reflections
+fleas
+ add very short (2 cycle) delay after each landing -> hop
+ zero velocity while on short delay? try it?
+ flea tech:
+ add a delay to flea jumping also +dmg
+ reduce lifespan after hitting mob, like drones
+ but add in a long cooldown after a hit
+ mid flight attraction towards nearby mobs?
+ if close to mob and line of sight: set isAttached flag = true
+ draw a line to mob, attraction
+ reset isAttached to false after: 1-2s or distance gets too big
+ tech for shotgun?
-laser tech: critical hits do _______ damage?
- name? polarized light, iridescence
+anti-shear topology apply to grenades too? and other less useful bullets
+ then open up tech requirements
-hopBossMom
- spawns lots of small hopBullets
- drops eggs, that hatch into hopBullets
- like sporangium
- normally runs away from player, but goes closer to drop eggs
+menagerie: sporangium release a variety of things
+ spores, worms, hoppers, drones, iceIX?, foam?, missiles?
+ benefit: they release more stuff than normal
+
+swim through slime
+ hold up to float while in slime?
level element: exploding barrels
@@ -34,22 +48,12 @@ improve mob invincible graphic
opacity oscillates from 100% to 0%?
make different from stun
-junk tech give strange CSS to a keyword
- @keyframes bounce-munch {
- 0% {vertical-align: -40px;}
- 100% {vertical-align: 40px;}
- }
-
make plasma ball power up and block pick up still work when you have no no energy
make a unique CD var for plasma ball?
wormhole tech: entropic gravity - gain defense for each research
requires wormhole or negative mass field or pilot wave
-shrapnel: a new bullet type: a very small bullet with no targeting that has a high gravity arc
- maybe 1 bounce or no bounce before it ends on map or mob collision
- often spawns in groups
-
testChamber2
mechanics
companion cube
@@ -184,6 +188,7 @@ tech expansion: should also make other fields do things
perfect diamagnetism moves forward when you hold down the shield
it's great, but maybe annoying?
maybe only with crouch?
+ perfect diamagnetism just replace Messier effect
time dilation drains 1/2 as much energy when paused
grow plasma torch as you hold it down
negative mass effects much more space