radioisotope generator

tech - radioisotope generator - drones have the effect of neutron bomb, but you get less ammo
bug fix - reduced tolerances now properly gives extra ammo
water shielding now protects you from all radioactivity, but only gives 75% protection
  (drones, neutron bomb, radioactive explosions, and even slime)

final boss has more durability in it's final phase
final boss no longers gets knocked around as much from explosions

level.warehouse predraw lighting for performance
bug fix on sewers where slime was doing too much harm
This commit is contained in:
landgreen
2021-06-27 12:40:26 -07:00
parent 0263ef7d57
commit f427f181a3
8 changed files with 654 additions and 267 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -326,11 +326,12 @@ const b = {
//player damage //player damage
if (Vector.magnitude(Vector.sub(where, player.position)) < radius) { if (Vector.magnitude(Vector.sub(where, player.position)) < radius) {
const drain = (tech.isExplosionHarm ? 0.5 : 0.25) * (tech.isImmuneExplosion ? Math.min(1, Math.max(1 - m.energy * 0.7, 0)) : 1) const DRAIN = (tech.isExplosionHarm ? 0.5 : 0.25) * (tech.isRadioactiveResistance ? 0.25 : 1)
m.energy -= drain // * (tech.isImmuneExplosion ? Math.min(1, Math.max(1 - m.energy * 0.7, 0)) : 1)
m.energy -= DRAIN
if (m.energy < 0) { if (m.energy < 0) {
m.energy = 0 m.energy = 0
m.damage(0.03); m.damage(0.03 * (tech.isRadioactiveResistance ? 0.25 : 1));
} }
} }
@@ -922,11 +923,10 @@ const b = {
bullet[me].restitution = 0; bullet[me].restitution = 0;
bullet[me].minDmgSpeed = 0; bullet[me].minDmgSpeed = 0;
bullet[me].damageRadius = 100; bullet[me].damageRadius = 100;
bullet[me].maxDamageRadius = 450 + 130 * tech.isNeutronSlow + 130 * tech.isNeutronImmune //+ 150 * Math.random() bullet[me].maxDamageRadius = 450 + 130 * tech.isNeutronSlow //+ 150 * Math.random()
bullet[me].radiusDecay = (0.81 + 0.15 * tech.isNeutronSlow + 0.15 * tech.isNeutronImmune) / tech.isBulletsLastLonger bullet[me].radiusDecay = (0.81 + 0.15 * tech.isNeutronSlow) / tech.isBulletsLastLonger
bullet[me].stuckTo = null; bullet[me].stuckTo = null;
bullet[me].stuckToRelativePosition = null; bullet[me].stuckToRelativePosition = null;
bullet[me].vacuumSlow = 0.97;
if (tech.isRPG) { if (tech.isRPG) {
const SCALE = 2 const SCALE = 2
@@ -1030,27 +1030,27 @@ const b = {
this.endCycle = 0; this.endCycle = 0;
} else { } else {
//aoe damage to player //aoe damage to player
if (!tech.isNeutronImmune && Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) { if (Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) {
const DRAIN = 0.0023 const DRAIN = tech.isRadioactiveResistance ? 0.0025 * 0.25 : 0.0025
if (m.energy > DRAIN) { if (m.energy > DRAIN) {
m.energy -= DRAIN m.energy -= DRAIN
} else { } else {
m.energy = 0; m.energy = 0;
m.damage(0.00015) m.damage(tech.isRadioactiveResistance ? 0.00016 * 0.25 : 0.00016) //0.00015
} }
} }
//aoe damage to mobs //aoe damage to mobs
for (let i = 0, len = mob.length; i < len; i++) { for (let i = 0, len = mob.length; i < len; i++) {
if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.damageRadius) { if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.damageRadius) {
let dmg = b.dmgScale * 0.082 let dmg = b.dmgScale * 0.09
if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way
if (mob[i].shield) dmg *= 4 //x5 to make up for the /5 that shields normally take if (mob[i].shield) dmg *= 4 //x5 to make up for the /5 that shields normally take
mob[i].damage(dmg); mob[i].damage(dmg);
mob[i].locatePlayer(); mob[i].locatePlayer();
if (tech.isNeutronSlow) { if (tech.isNeutronSlow) {
Matter.Body.setVelocity(mob[i], { Matter.Body.setVelocity(mob[i], {
x: mob[i].velocity.x * this.vacuumSlow, x: mob[i].velocity.x * 0.97,
y: mob[i].velocity.y * this.vacuumSlow y: mob[i].velocity.y * 0.97
}); });
} }
} }
@@ -1062,22 +1062,21 @@ const b = {
ctx.fill(); ctx.fill();
ctx.globalCompositeOperation = "source-over" ctx.globalCompositeOperation = "source-over"
if (tech.isNeutronSlow) { if (tech.isNeutronSlow) {
const that = this
function slow(who, radius = that.explodeRad * 3.2) { let slow = (who, radius = this.explodeRad * 3.2) => {
for (i = 0, len = who.length; i < len; i++) { for (i = 0, len = who.length; i < len; i++) {
const sub = Vector.sub(that.position, who[i].position); const sub = Vector.sub(this.position, who[i].position);
const dist = Vector.magnitude(sub); const dist = Vector.magnitude(sub);
if (dist < radius) { if (dist < radius) {
Matter.Body.setVelocity(who[i], { Matter.Body.setVelocity(who[i], {
x: who[i].velocity.x * that.vacuumSlow, x: who[i].velocity.x * 0.975,
y: who[i].velocity.y * that.vacuumSlow y: who[i].velocity.y * 0.975
}); });
} }
} }
} }
slow(body, this.damageRadius) slow(body, this.damageRadius)
if (!tech.isNeutronImmune) slow([player], this.damageRadius) slow([player], this.damageRadius)
} }
} }
} }
@@ -2210,6 +2209,242 @@ const b = {
y: speed * Math.sin(dir) y: speed * Math.sin(dir)
}); });
}, },
droneRadioactive(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 = tech.isFastDrones ? 0.002 : 0.0012
const dir = m.angle + 0.4 * (Math.random() - 0.5);
const RADIUS = (4 + 1 * Math.random())
bullet[me] = Bodies.polygon(where.x, where.y, 8, RADIUS, {
angle: dir,
inertia: Infinity,
friction: 0.05,
frictionAir: 0,
restitution: 1,
dmg: 0.24, //damage done in addition to the damage from momentum
lookFrequency: 120 + Math.floor(23 * Math.random()),
endCycle: simulation.cycle + Math.floor((900 + 400 * Math.random()) * tech.isBulletsLastLonger) + 140 + RADIUS * 5,
classType: "bullet",
collisionFilter: {
category: cat.bullet,
mask: cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield //self collide
},
minDmgSpeed: 0,
lockedOn: null,
isFollowMouse: true,
deathCycles: 110 + RADIUS * 5,
isImproved: false,
radioRadius: 0,
maxRadioRadius: 400 + Math.floor(75 * Math.random()) + 80 * tech.isNeutronSlow,
beforeDmg(who) {
const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20) //move away from target after hitting
Matter.Body.setVelocity(this, {
x: unit.x,
y: unit.y
});
this.lockedOn = null
if (this.endCycle > simulation.cycle + this.deathCycles) {
this.endCycle -= 60
if (simulation.cycle + this.deathCycles > this.endCycle) this.endCycle = simulation.cycle + this.deathCycles
}
},
onEnd() {
if (tech.isDroneRespawn) {
const who = b.guns[b.activeGun]
if (who.name === "drones" && who.ammo > 0 && mob.length) {
b.droneRadioactive({ x: this.position.x, y: this.position.y }, 0)
if (Math.random() < 0.33) {
b.guns[b.activeGun].ammo--;
simulation.updateGunHUD();
}
}
}
},
do() {
//radioactive zone
this.radioRadius = this.radioRadius * 0.993 + 0.007 * this.maxRadioRadius //smooth radius towards max
//aoe damage to player
if (Vector.magnitude(Vector.sub(player.position, this.position)) < this.radioRadius) {
const DRAIN = tech.isRadioactiveResistance ? 0.0023 * 0.25 : 0.0023
if (m.energy > DRAIN) {
m.energy -= DRAIN
} else {
m.energy = 0;
m.damage(tech.isRadioactiveResistance ? 0.00015 * 0.25 : 0.00015) //0.00015
}
}
//aoe damage to mobs
for (let i = 0, len = mob.length; i < len; i++) {
if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.radioRadius) {
let dmg = b.dmgScale * 0.035 //neutron bombs dmg = 0.09
if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way
if (mob[i].shield) dmg *= 4 //x5 to make up for the /5 that shields normally take
mob[i].damage(dmg);
mob[i].locatePlayer();
if (tech.isNeutronSlow) {
Matter.Body.setVelocity(mob[i], {
x: mob[i].velocity.x * 0.97,
y: mob[i].velocity.y * 0.97
});
}
}
}
//draw
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.radioRadius, 0, 2 * Math.PI);
ctx.globalCompositeOperation = "lighter"
ctx.fillStyle = `rgba(25,139,170,${0.1+0.05*Math.random()})`;
ctx.fill();
ctx.globalCompositeOperation = "source-over"
if (tech.isNeutronSlow) {
const slow = (who, radius = this.radioRadius * 3.2) => {
for (i = 0, len = who.length; i < len; i++) {
const sub = Vector.sub(this.position, who[i].position);
const dist = Vector.magnitude(sub);
if (dist < radius) {
Matter.Body.setVelocity(who[i], {
x: who[i].velocity.x * 0.975,
y: who[i].velocity.y * 0.975
});
}
}
}
slow(body, this.radioRadius)
slow([player], this.radioRadius)
}
//normal drone actions
if (simulation.cycle + this.deathCycles > this.endCycle) { //fall shrink and die
this.force.y += this.mass * 0.0012;
this.restitution = 0.2;
const scale = 0.995;
Matter.Body.scale(this, scale, scale);
this.maxRadioRadius = 0
this.radioRadius = this.radioRadius * 0.98 //let radioactivity decrease
} else {
this.force.y += this.mass * 0.0002; //gravity
if (!(simulation.cycle % this.lookFrequency)) {
//find mob targets
this.lockedOn = null;
let closeDist = Infinity;
for (let i = 0, len = mob.length; i < len; ++i) {
if (
!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
) {
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]
}
}
}
//power ups
if (!this.isImproved && !simulation.isChoosing && !tech.isExtraMaxEnergy) {
if (this.lockedOn) {
//grab, but don't lock onto nearby power up
for (let i = 0, len = powerUp.length; i < len; ++i) {
if (
(powerUp[i].name !== "heal" || m.health < 0.9 * m.maxHealth || tech.isDroneGrab) &&
(powerUp[i].name !== "field" || !tech.isDeterminism) &&
Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000
) {
//draw pickup for a single cycle
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y);
ctx.strokeStyle = "#000"
ctx.lineWidth = 4
ctx.stroke();
//pick up nearby power ups
powerUps.onPickUp(powerUp[i]);
powerUp[i].effect();
Matter.World.remove(engine.world, powerUp[i]);
powerUp.splice(i, 1);
if (tech.isDroneGrab) {
this.isImproved = true;
const SCALE = 2.25
Matter.Body.scale(this, SCALE, SCALE);
this.lookFrequency = 30 + Math.floor(11 * Math.random());
this.endCycle += 1000 * tech.isBulletsLastLonger
this.maxRadioRadius *= 1.25
}
break;
}
}
} else {
//look for power ups to lock onto
let closeDist = Infinity;
for (let i = 0, len = powerUp.length; i < len; ++i) {
if (
(powerUp[i].name !== "heal" || m.health < 0.9 * m.maxHealth || tech.isDroneGrab) &&
(powerUp[i].name !== "field" || !tech.isDeterminism)
) {
if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) {
//draw pickup for a single cycle
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y);
ctx.strokeStyle = "#000"
ctx.lineWidth = 4
ctx.stroke();
//pick up nearby power ups
powerUps.onPickUp(powerUp[i]);
powerUp[i].effect();
Matter.World.remove(engine.world, powerUp[i]);
powerUp.splice(i, 1);
if (tech.isDroneGrab) {
this.isImproved = true;
const SCALE = 2.25
Matter.Body.scale(this, SCALE, SCALE);
this.lookFrequency = 30 + Math.floor(11 * Math.random());
this.endCycle += 1000 * tech.isBulletsLastLonger
this.maxRadioRadius *= 1.25
}
break;
}
//look for power ups to lock onto
if (
Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 &&
Matter.Query.ray(body, this.position, powerUp[i].position).length === 0
) {
const TARGET_VECTOR = Vector.sub(this.position, powerUp[i].position)
const DIST = Vector.magnitude(TARGET_VECTOR);
if (DIST < closeDist) {
closeDist = DIST;
this.lockedOn = powerUp[i]
}
}
}
}
}
}
}
if (this.lockedOn) { //accelerate towards mobs
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST)
} else { //accelerate towards mouse
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, simulation.mouseInGame)), -this.mass * THRUST)
}
// speed cap instead of friction to give more agility
if (this.speed > 6) {
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.97,
y: this.velocity.y * 0.97
});
}
}
}
})
World.add(engine.world, bullet[me]); //add bullet to world
Matter.Body.setVelocity(bullet[me], {
x: speed * Math.cos(dir),
y: speed * Math.sin(dir)
});
},
foam(position, velocity, radius) { foam(position, velocity, radius) {
// radius *= Math.sqrt(tech.bulletSize) // radius *= Math.sqrt(tech.bulletSize)
const me = bullet.length; const me = bullet.length;
@@ -3323,7 +3558,7 @@ const b = {
if (tech.isNailRadiation) { if (tech.isNailRadiation) {
mobs.statusDoT(who, tech.isFastRadiation ? 12 : 3, tech.isSlowRadiation ? 240 : (tech.isFastRadiation ? 30 : 120)) // one tick every 30 cycles mobs.statusDoT(who, tech.isFastRadiation ? 12 : 3, tech.isSlowRadiation ? 240 : (tech.isFastRadiation ? 30 : 120)) // one tick every 30 cycles
} else { } else {
let dmg = b.dmgScale * 5 let dmg = b.dmgScale * 5.5
if (tech.isCrit && who.isStunned) dmg *= 4 if (tech.isCrit && who.isStunned) dmg *= 4
who.damage(dmg, tech.isNeedleShieldPierce); who.damage(dmg, tech.isNeedleShieldPierce);
simulation.drawList.push({ //add dmg to draw queue simulation.drawList.push({ //add dmg to draw queue
@@ -3356,18 +3591,18 @@ const b = {
x: m.Vx / 2 + SPEED * Math.cos(angle), x: m.Vx / 2 + SPEED * Math.cos(angle),
y: m.Vy / 2 + SPEED * Math.sin(angle) y: m.Vy / 2 + SPEED * Math.sin(angle)
}); });
Matter.Body.setDensity(bullet[me], 0.00001); // Matter.Body.setDensity(bullet[me], 0.00001);
World.add(engine.world, bullet[me]); //add bullet to world World.add(engine.world, bullet[me]); //add bullet to world
} }
if (m.crouch) { if (m.crouch) {
m.fireCDcycle = m.cycle + 50 * b.fireCD; // cool down m.fireCDcycle = m.cycle + 45 * b.fireCD; // cool down
makeNeedle() makeNeedle()
for (let i = 1; i < 4; i++) { //4 total needles for (let i = 1; i < 4; i++) { //4 total needles
setTimeout(() => { if (!simulation.paused) makeNeedle() }, 60 * i); setTimeout(() => { if (!simulation.paused) makeNeedle() }, 60 * i);
} }
} else { } else {
m.fireCDcycle = m.cycle + 30 * b.fireCD; // cool down m.fireCDcycle = m.cycle + 25 * b.fireCD; // cool down
makeNeedle() makeNeedle()
for (let i = 1; i < 3; i++) { //3 total needles for (let i = 1; i < 3; i++) { //3 total needles
setTimeout(() => { if (!simulation.paused) makeNeedle() }, 60 * i); setTimeout(() => { if (!simulation.paused) makeNeedle() }, 60 * i);
@@ -4145,12 +4380,22 @@ const b = {
have: false, have: false,
do() {}, do() {},
fire() { fire() {
if (m.crouch) { if (tech.isDroneRadioactive) {
b.drone({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 45) if (m.crouch) {
m.fireCDcycle = m.cycle + Math.floor(13 * b.fireCD); // cool down b.droneRadioactive({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 45)
m.fireCDcycle = m.cycle + Math.floor(7 * 13 * b.fireCD); // cool down
} else {
b.droneRadioactive({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 10)
m.fireCDcycle = m.cycle + Math.floor(7 * 6 * b.fireCD); // cool down
}
} else { } else {
b.drone() if (m.crouch) {
m.fireCDcycle = m.cycle + Math.floor(6 * b.fireCD); // cool down b.drone({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 45)
m.fireCDcycle = m.cycle + Math.floor(13 * b.fireCD); // cool down
} else {
b.drone()
m.fireCDcycle = m.cycle + Math.floor(6 * b.fireCD); // cool down
}
} }
} }
}, },

View File

@@ -16,12 +16,15 @@ const level = {
// simulation.difficulty = 20 // simulation.difficulty = 20
// simulation.isHorizontalFlipped = true // simulation.isHorizontalFlipped = true
// level.difficultyIncrease(99) // level.difficultyIncrease(99)
// m.setField("standing wave harmonics") // m.setField("nano-scale manufacturing")
// tech.giveTech("spherical harmonics")
// for (let i = 0; i < 9; i++) tech.giveTech("spherical harmonics")
// b.giveGuns("grenades") // b.giveGuns("grenades")
// tech.giveTech("neutron bomb")
// b.giveGuns("drones")
// tech.giveTech("radioactive drones")
// tech.isRadioactiveResistance = true
// for (let i = 0; i < 9; i++) tech.giveTech("spherical harmonics")
// tech.isExplodeRadio = true // tech.isExplodeRadio = true
// tech.giveTech("chain reaction") // tech.giveTech("vacuum permittivity")
// tech.giveTech("quenching") // tech.giveTech("quenching")
// tech.giveTech("decoherence") // tech.giveTech("decoherence")
// tech.giveTech("supertemporal") // tech.giveTech("supertemporal")
@@ -96,7 +99,7 @@ const level = {
powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "tech", false); powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "tech", false);
} }
if (tech.isHealLowHealth) { if (tech.isHealLowHealth) {
const len = Math.ceil((m.maxHealth - m.health) / 0.33) const len = Math.ceil((m.maxHealth - m.health) / 0.25)
for (let i = 0; i < len; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "heal", false); for (let i = 0; i < len; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "heal", false);
} }
if (tech.isMACHO) spawn.MACHO() if (tech.isMACHO) spawn.MACHO()
@@ -965,23 +968,12 @@ const level = {
ctx.fillRect(this.min.x, this.min.y + offset, this.width, this.height - offset) ctx.fillRect(this.min.x, this.min.y + offset, this.width, this.height - offset)
if (this.height > 0 && Matter.Query.region([player], this).length) { if (this.height > 0 && Matter.Query.region([player], this).length) {
const drain = 0.003 + m.fieldRegen const DRAIN = 0.003 * (tech.isRadioactiveResistance ? 0.25 : 1) + m.fieldRegen
if (m.energy > drain) { console.log(DRAIN)
m.energy -= drain if (m.energy > DRAIN) {
m.energy -= DRAIN
} else { } else {
if (damage < 0.02) { m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1))
m.damage(damage)
} else if (m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + tech.collisionImmuneCycles;
m.damage(damage)
simulation.drawList.push({ //add dmg to draw queue
x: player.position.x,
y: player.position.y,
radius: damage * 1500,
color: simulation.mobDmgColor,
time: 20
});
}
} }
//float //float
if (player.velocity.y > 5) player.force.y -= 0.95 * player.mass * simulation.g if (player.velocity.y > 5) player.force.y -= 0.95 * player.mass * simulation.g
@@ -1926,7 +1918,7 @@ const level = {
const doorWidth2 = 15 + Math.floor(100 * Math.random() * Math.random()) const doorWidth2 = 15 + Math.floor(100 * Math.random() * Math.random())
spawn.bodyRect(offset.x + 2050 - doorWidth2 / 2, offset.y - 175, doorWidth2, 165); //block door spawn.bodyRect(offset.x + 2050 - doorWidth2 / 2, offset.y - 175, doorWidth2, 165); //block door
} else { } else {
spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 2800); //right wall spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 2900); //right wall
} }
} }
@@ -2086,7 +2078,7 @@ const level = {
}, },
testing() { testing() {
// const button = level.button(200, -700) // const button = level.button(200, -700)
const toggle = level.toggle(200, -700) // const toggle = level.toggle(200, -700)
level.custom = () => { level.custom = () => {
// button.draw(); // button.draw();
ctx.fillStyle = "rgba(0,255,255,0.1)"; ctx.fillStyle = "rgba(0,255,255,0.1)";
@@ -2096,7 +2088,7 @@ const level = {
level.enter.draw(); level.enter.draw();
}; };
level.customTopLayer = () => { level.customTopLayer = () => {
toggle.query(); // toggle.query();
}; };
level.setPosToSpawn(0, -750); //normal spawn level.setPosToSpawn(0, -750); //normal spawn
@@ -2127,11 +2119,9 @@ const level = {
function blockDoor(x, y, blockSize = 58) { function blockDoor(x, y, blockSize = 58) {
spawn.mapRect(x, y - 290, 40, 60); // door lip spawn.mapRect(x, y - 290, 40, 60); // door lip
spawn.mapRect(x, y, 40, 50); // door lip spawn.mapRect(x, y, 40, 50); // door lip
for (let i = 0; i < 4; ++i) { for (let i = 0; i < 4; ++i) spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize);
spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize);
}
} }
blockDoor(710, -710); // blockDoor(710, -710);
// for (let i = 0; i < 30; i++) powerUps.directSpawn(710, -710, "tech"); // for (let i = 0; i < 30; i++) powerUps.directSpawn(710, -710, "tech");
spawn.mapRect(2500, -1200, 200, 750); //right wall spawn.mapRect(2500, -1200, 200, 750); //right wall
@@ -2151,10 +2141,10 @@ const level = {
// spawn.shooterBoss(1900, -500) // spawn.shooterBoss(1900, -500)
// spawn.historyBoss(1200, -500) // spawn.historyBoss(1200, -500)
// spawn.laserTargetingBoss(1600, -400) // spawn.laserTargetingBoss(1600, -400)
// spawn.striker(1600, -500) spawn.hopper(1600, -500)
// spawn.laserTargetingBoss(1700, -120) // spawn.laserTargetingBoss(1700, -120)
// spawn.bomberBoss(1400, -500) // spawn.bomberBoss(1400, -500)
// spawn.ghoster(1800, -120) spawn.hopBoss(1800, -120)
// spawn.streamBoss(1600, -500) // spawn.streamBoss(1600, -500)
// spawn.orbitalBoss(1600, -500) // spawn.orbitalBoss(1600, -500)
// spawn.cellBossCulture(1600, -500) // spawn.cellBossCulture(1600, -500)
@@ -2962,8 +2952,6 @@ const level = {
button1.query(); button1.query();
button1.draw(); button1.draw();
hazard.query();
hazard.level(button1.isUp)
rotor.rotate(); rotor.rotate();
ctx.fillStyle = "hsl(175, 15%, 76%)" ctx.fillStyle = "hsl(175, 15%, 76%)"
@@ -4017,43 +4005,54 @@ const level = {
level.enter.draw(); level.enter.draw();
}; };
// simulation.draw.mapPath = new Path2D();
// for (let i = 0, len = map.length; i < len; ++i) {
// let vertices = map[i].vertices;
// simulation.draw.mapPath.moveTo(vertices[0].x, vertices[0].y);
// for (let j = 1; j < vertices.length; j += 1) {
// simulation.draw.mapPath.lineTo(vertices[j].x, vertices[j].y);
// }
// simulation.draw.mapPath.lineTo(vertices[0].x, vertices[0].y);
// }
const lightingPath = new Path2D() //pre-draw the complex lighting path to save processing
lightingPath.moveTo(-1800, -500)
lightingPath.lineTo(-910, -500) //3rd floor light
lightingPath.lineTo(-1300, 0)
lightingPath.lineTo(-500, 0)
lightingPath.lineTo(-890, -500)
lightingPath.lineTo(-175, -500)
lightingPath.lineTo(-175, -250)
lightingPath.lineTo(175, -250)
lightingPath.lineTo(175, 0)
lightingPath.lineTo(-910, 100) //2nd floor light left
lightingPath.lineTo(-1300, 600)
lightingPath.lineTo(-500, 600)
lightingPath.lineTo(-890, 100)
lightingPath.lineTo(190, 100) //2nd floor light right
lightingPath.lineTo(-200, 600)
lightingPath.lineTo(600, 600)
lightingPath.lineTo(210, 100)
lightingPath.lineTo(1100, 100)
lightingPath.lineTo(1100, 1400)
lightingPath.lineTo(600, 1400) //1st floor light right
lightingPath.lineTo(10, 700)
lightingPath.lineTo(-10, 700)
lightingPath.lineTo(-600, 1400)
lightingPath.lineTo(-1950, 1400) //1st floor light left
lightingPath.lineTo(-2290, 950)
lightingPath.lineTo(-2310, 950)
lightingPath.lineTo(-2650, 1400)
lightingPath.lineTo(-3025, 1400)
lightingPath.lineTo(-3025, 150)
lightingPath.lineTo(-2590, 150)
lightingPath.lineTo(-2600, -150)
lightingPath.lineTo(-1800, -150)
lightingPath.lineTo(-1800, -500) //top left end/start of path
level.customTopLayer = () => { level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights
ctx.beginPath() ctx.fill(lightingPath);
ctx.moveTo(-1800, -500)
ctx.lineTo(-910, -500) //3rd floor light
ctx.lineTo(-1300, 0)
ctx.lineTo(-500, 0)
ctx.lineTo(-890, -500)
ctx.lineTo(-175, -500)
ctx.lineTo(-175, -250)
ctx.lineTo(175, -250)
ctx.lineTo(175, 0)
ctx.lineTo(-910, 100) //2nd floor light left
ctx.lineTo(-1300, 600)
ctx.lineTo(-500, 600)
ctx.lineTo(-890, 100)
ctx.lineTo(190, 100) //2nd floor light right
ctx.lineTo(-200, 600)
ctx.lineTo(600, 600)
ctx.lineTo(210, 100)
ctx.lineTo(1100, 100)
ctx.lineTo(1100, 1400)
ctx.lineTo(600, 1400) //1st floor light right
ctx.lineTo(10, 700)
ctx.lineTo(-10, 700)
ctx.lineTo(-600, 1400)
ctx.lineTo(-1950, 1400) //1st floor light left
ctx.lineTo(-2290, 950)
ctx.lineTo(-2310, 950)
ctx.lineTo(-2650, 1400)
ctx.lineTo(-3025, 1400)
ctx.lineTo(-3025, 150)
ctx.lineTo(-2590, 150)
ctx.lineTo(-2600, -150)
ctx.lineTo(-1800, -150)
ctx.lineTo(-1800, -500) //top left end/start of path
ctx.fill()
}; };
level.setPosToSpawn(25, -55); //normal spawn level.setPosToSpawn(25, -55); //normal spawn

View File

@@ -495,7 +495,7 @@ const m = {
if (tech.isSlowFPS) dmg *= 0.8 if (tech.isSlowFPS) dmg *= 0.8
// if (tech.isPiezo) dmg *= 0.85 // if (tech.isPiezo) dmg *= 0.85
if (tech.isHarmReduce && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.4 if (tech.isHarmReduce && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.4
if (tech.isBotArmor) dmg *= 0.94 ** b.totalBots() if (tech.isBotArmor) dmg *= 0.93 ** b.totalBots()
if (tech.isHarmArmor && m.lastHarmCycle + 600 > m.cycle) dmg *= 0.33; if (tech.isHarmArmor && m.lastHarmCycle + 600 > m.cycle) dmg *= 0.33;
if (tech.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.34 if (tech.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.34
if (tech.energyRegen === 0) dmg *= 0.34 if (tech.energyRegen === 0) dmg *= 0.34
@@ -1682,6 +1682,9 @@ const m = {
} else if (tech.isIceField) { } else if (tech.isIceField) {
m.energy -= 0.04; m.energy -= 0.04;
b.iceIX(1) b.iceIX(1)
} else if (tech.isDroneRadioactive) {
m.energy -= 1;
b.droneRadioactive({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 25)
} else { } else {
m.energy -= 0.45 * tech.droneEnergyReduction; m.energy -= 0.45 * tech.droneEnergyReduction;
b.drone() b.drone()

View File

@@ -736,7 +736,11 @@ const powerUps = {
//bonus power ups for clearing runs in the last game //bonus power ups for clearing runs in the last game
if (level.levelsCleared === 0 && !simulation.isCheating && localSettings.levelsClearedLastGame > 1) { if (level.levelsCleared === 0 && !simulation.isCheating && localSettings.levelsClearedLastGame > 1) {
for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech", false); //spawn a tech for levels cleared in last game for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) {
powerUps.spawn(m.pos.x, m.pos.y, "tech", false); //spawn a tech for levels cleared in last game
simulation.makeTextLog(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`);
simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") //simulation superposition }`);
}
localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
} }

View File

@@ -170,13 +170,13 @@ const spawn = {
Matter.Body.setVelocity(this, { x: 0, y: 0 }); Matter.Body.setVelocity(this, { x: 0, y: 0 });
//aoe damage to player //aoe damage to player
if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(player.position, this.position)) < this.radius && !tech.isNeutronImmune) { if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(player.position, this.position)) < this.radius) {
const DRAIN = 0.07 const DRAIN = tech.isRadioactiveResistance ? 0.07 * 0.25 : 0.07
if (m.energy > DRAIN) { if (m.energy > DRAIN) {
m.energy -= DRAIN m.energy -= DRAIN
} else { } else {
m.energy = 0; m.energy = 0;
m.damage(0.007 * simulation.dmgScale) m.damage((tech.isRadioactiveResistance ? 0.007 * 0.25 : 0.007) * simulation.dmgScale)
simulation.drawList.push({ //add dmg to draw queue simulation.drawList.push({ //add dmg to draw queue
x: this.position.x, x: this.position.x,
y: this.position.y, y: this.position.y,
@@ -233,7 +233,7 @@ const spawn = {
y: me.position.y y: me.position.y
}, },
bodyB: me, bodyB: me,
stiffness: 0.001, stiffness: 1,
damping: 1 damping: 1
}); });
World.add(engine.world, me.constraint); World.add(engine.world, me.constraint);
@@ -244,7 +244,7 @@ const spawn = {
me.memory = Infinity; me.memory = Infinity;
me.hasRunDeathScript = false me.hasRunDeathScript = false
me.locatePlayer(); me.locatePlayer();
const density = 0.25 const density = 0.23
Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger
// spawn.shield(me, x, y, 1); // spawn.shield(me, x, y, 1);
me.onDeath = function() { me.onDeath = function() {
@@ -416,7 +416,7 @@ const spawn = {
} }
} else if (this.mode !== 3) { //all three modes at once } else if (this.mode !== 3) { //all three modes at once
this.cycle = 0; this.cycle = 0;
Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger Matter.Body.setDensity(me, 10 * density); //extra dense //normal is 0.001 //makes effective life much larger
if (this.mode === 2) { if (this.mode === 2) {
Matter.Body.scale(this, 5, 5); Matter.Body.scale(this, 5, 5);
} else { } else {
@@ -1017,6 +1017,8 @@ const spawn = {
me.accelMag = 0.04; me.accelMag = 0.04;
me.g = 0.0017; //required if using 'gravity' me.g = 0.0017; //required if using 'gravity'
me.frictionAir = 0.01; me.frictionAir = 0.01;
me.friction = 1
me.frictionStatic = 1
me.restitution = 0; me.restitution = 0;
me.delay = 120 * simulation.CDScale; me.delay = 120 * simulation.CDScale;
me.randomHopFrequency = 200 + Math.floor(Math.random() * 150); me.randomHopFrequency = 200 + Math.floor(Math.random() * 150);
@@ -1032,7 +1034,7 @@ const spawn = {
const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; 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); 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.x += forceMag * Math.cos(angle);
this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.07 + 0.02) * this.mass; //antigravity this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.07 + 0.06) * this.mass; //antigravity
} }
} else { } else {
//randomly hob if not aware of player //randomly hob if not aware of player
@@ -1048,6 +1050,96 @@ const spawn = {
} }
}; };
}, },
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 '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.do = function() {
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)) { spinner(x, y, radius = 30 + Math.ceil(Math.random() * 35)) {
mobs.spawn(x, y, 5, radius, "#000000"); mobs.spawn(x, y, 5, radius, "#000000");
let me = mob[mob.length - 1]; let me = mob[mob.length - 1];
@@ -2063,7 +2155,7 @@ const spawn = {
y: me.position.y y: me.position.y
}, },
bodyB: me, bodyB: me,
stiffness: 0.001, stiffness: 1,
damping: 1 damping: 1
}); });
World.add(engine.world, me.constraint); World.add(engine.world, me.constraint);
@@ -2575,8 +2667,8 @@ const spawn = {
y: me.position.y y: me.position.y
}, },
bodyB: me, bodyB: me,
stiffness: 0.0002, stiffness: 0.00004,
damping: 1 damping: 0.1
}); });
World.add(engine.world, me.constraint); World.add(engine.world, me.constraint);
}, 2000); //add in a delay in case the level gets flipped left right }, 2000); //add in a delay in case the level gets flipped left right
@@ -2912,7 +3004,7 @@ const spawn = {
y: me.position.y y: me.position.y
}, },
bodyB: me, bodyB: me,
stiffness: 0.001, stiffness: 0.0001,
damping: 1 damping: 1
}); });
World.add(engine.world, me.constraint); World.add(engine.world, me.constraint);

View File

@@ -177,7 +177,7 @@
if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.22 if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.22
if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 1.9 if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 1.9
if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.43, player.speed * 0.015) if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.43, player.speed * 0.015)
if (tech.isBotDamage) dmg *= 1 + 0.05 * b.totalBots() if (tech.isBotDamage) dmg *= 1 + 0.06 * b.totalBots()
return dmg * tech.slowFire * tech.aimDamage return dmg * tech.slowFire * tech.aimDamage
}, },
duplicationChance() { duplicationChance() {
@@ -716,7 +716,7 @@
}, },
requires: "an explosive damage source, not ammonium nitrate or nitroglycerin", requires: "an explosive damage source, not ammonium nitrate or nitroglycerin",
effect: () => { effect: () => {
tech.isExplodeRadio = true; tech.isExplodeRadio = true; //iridium-192
}, },
remove() { remove() {
tech.isExplodeRadio = false; tech.isExplodeRadio = false;
@@ -823,9 +823,9 @@
frequency: 2, frequency: 2,
frequencyDefault: 2, frequencyDefault: 2,
allowed() { allowed() {
return tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isMissileField || tech.isExplodeMob || tech.isPulseLaser || tech.isBlockExplosion return !tech.isExplodeRadio && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isMissileField || tech.isExplodeMob || tech.isPulseLaser || tech.isBlockExplosion)
}, },
requires: "an explosive damage source", requires: "an explosive damage source, not iridium-192",
effect: () => { effect: () => {
tech.isImmuneExplosion = true; tech.isImmuneExplosion = true;
}, },
@@ -841,9 +841,9 @@
frequency: 2, frequency: 2,
frequencyDefault: 2, frequencyDefault: 2,
allowed() { 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 return ((m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isDroneRadioactive || tech.isSporeField || tech.isMissileField || tech.isIceField)) || (tech.haveGunCheck("drones") && !tech.isDroneRadioactive) || tech.haveGunCheck("super balls") || tech.haveGunCheck("shotgun")) && !tech.isNailShot
}, },
requires: "drones, super balls, shotgun", requires: "super balls, shotgun, drones, not radioactive drones",
effect() { effect() {
tech.isIncendiary = true tech.isIncendiary = true
}, },
@@ -1376,7 +1376,7 @@
}, },
{ {
name: "perimeter defense", name: "perimeter defense",
description: "reduce <strong class='color-harm'>harm</strong> by <strong>6%</strong><br>for each of your permanent <strong class='color-bot'>bots</strong>", description: "reduce <strong class='color-harm'>harm</strong> by <strong>7%</strong><br>for each of your permanent <strong class='color-bot'>bots</strong>",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
frequency: 2, frequency: 2,
@@ -1394,7 +1394,7 @@
}, },
{ {
name: "network effect", name: "network effect",
description: "increase <strong class='color-d'>damage</strong> by <strong>5%</strong><br>for each of your permanent <strong class='color-bot'>bots</strong>", description: "increase <strong class='color-d'>damage</strong> by <strong>6%</strong><br>for each of your permanent <strong class='color-bot'>bots</strong>",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
frequency: 2, frequency: 2,
@@ -2509,7 +2509,7 @@
}, },
{ {
name: "negentropy", name: "negentropy",
description: `at the start of each <strong>level</strong><br>spawn a <strong class='color-h'>heal</strong> for every <strong>33</strong> missing health`, description: `at the start of each <strong>level</strong><br>spawn a <strong class='color-h'>heal</strong> for every <strong>25</strong> missing health`,
maxCount: 1, maxCount: 1,
count: 0, count: 0,
frequency: 1, frequency: 1,
@@ -4039,7 +4039,7 @@
}, },
{ {
name: "neutron bomb", name: "neutron bomb",
description: "<strong>grenades</strong> are irradiated with <strong class='color-p'>Cf-252</strong><br>does <strong class='color-d'>damage</strong>, <strong class='color-harm'>harm</strong>, and drains <strong class='color-f'>energy</strong>", description: "<strong>grenades</strong> are <strong class='color-p'>irradiated</strong> with <strong class='color-p'>Cf-252</strong><br>does <strong class='color-d'>damage</strong>, <strong class='color-harm'>harm</strong>, and drains <strong class='color-f'>energy</strong>",
isGunTech: true, isGunTech: true,
maxCount: 1, maxCount: 1,
count: 0, count: 0,
@@ -4060,35 +4060,35 @@
}, },
{ {
name: "water shielding", name: "water shielding",
description: "increase <strong>neutron bomb's</strong> range by <strong>20%</strong><br>you are <strong>immune</strong> to its harmful effects", description: "<strong class='color-p'>radioactive</strong> effects on you are reduced by 75%<br><em>neutron bomb, drones, explosions, slime</em>",
isGunTech: true, isGunTech: true,
maxCount: 1, maxCount: 1,
count: 0, count: 0,
frequency: 2, frequency: 2,
frequencyDefault: 2, frequencyDefault: 2,
allowed() { allowed() {
return tech.isNeutronBomb return tech.isNeutronBomb || tech.isDroneRadioactive
}, },
requires: "neutron bomb", requires: "neutron bomb or radioactive drones",
effect() { effect() {
tech.isNeutronImmune = true tech.isRadioactiveResistance = true
}, },
remove() { remove() {
tech.isNeutronImmune = false tech.isRadioactiveResistance = false
} }
}, },
{ {
name: "vacuum permittivity", name: "vacuum permittivity",
description: "increase <strong>neutron bomb's</strong> range by <strong>20%</strong><br>objects in range of the bomb are <strong>slowed</strong>", description: "increase <strong class='color-p'>radioactive</strong> range by <strong>20%</strong><br>objects in range of the bomb are <strong>slowed</strong>",
isGunTech: true, isGunTech: true,
maxCount: 1, maxCount: 1,
count: 0, count: 0,
frequency: 2, frequency: 2,
frequencyDefault: 2, frequencyDefault: 2,
allowed() { allowed() {
return tech.isNeutronBomb return tech.isNeutronBomb || tech.isDroneRadioactive
}, },
requires: "neutron bomb", requires: "neutron bomb or radioactive drones",
effect() { effect() {
tech.isNeutronSlow = true tech.isNeutronSlow = true
}, },
@@ -4270,6 +4270,39 @@
tech.isMutualism = false tech.isMutualism = false
} }
}, },
{
name: "radioisotope generator",
description: "<strong class='color-p'>irradiate</strong> <strong>drones</strong>, reduce <strong class='color-g'>ammo</strong> by <strong>75%</strong><br>does <strong class='color-d'>damage</strong>, <strong class='color-harm'>harm</strong>, and drains <strong class='color-f'>energy</strong>",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("drones") && tech.droneCycleReduction === 1 && !tech.isIncendiary
},
requires: "drone gun, not reduced tolerances or incendiary",
effect() {
tech.isDroneRadioactive = true
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "drones") {
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.25
b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25)
}
}
},
remove() {
if (tech.isDroneRadioactive) {
tech.isDroneRadioactive = false
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "drones") {
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack
b.guns[i].ammo = b.guns[i].ammo * 4
}
}
}
}
},
{ {
name: "brushless motor", name: "brushless motor",
description: "<strong>drones</strong> accelerate <strong>50%</strong> faster", description: "<strong>drones</strong> accelerate <strong>50%</strong> faster",
@@ -4308,33 +4341,6 @@
tech.isDroneGrab = false tech.isDroneGrab = false
} }
}, },
{
name: "reduced tolerances",
description: "increase <strong>drone</strong> <strong class='color-g'>ammo</strong>/<strong class='color-f'>efficiency</strong> by <strong>66%</strong><br>reduce the average <strong>drone</strong> lifetime by <strong>40%</strong>",
isGunTech: true,
maxCount: 3,
count: 0,
frequency: 2,
frequencyDefault: 2,
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.6, 1 + this.count)
tech.droneEnergyReduction = Math.pow(0.333, 1 + 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: "drone repair", name: "drone repair",
description: "broken <strong>drones</strong> <strong>repair</strong> if the drone <strong class='color-g'>gun</strong> is active<br><strong>repairing</strong> has a <strong>33%</strong> chance to use <strong>1</strong> <strong class='color-g'>ammo</strong>", description: "broken <strong>drones</strong> <strong>repair</strong> if the drone <strong class='color-g'>gun</strong> is active<br><strong>repairing</strong> has a <strong>33%</strong> chance to use <strong>1</strong> <strong class='color-g'>ammo</strong>",
@@ -4354,6 +4360,36 @@
tech.isDroneRespawn = false tech.isDroneRespawn = false
} }
}, },
{
name: "reduced tolerances",
description: "increase <strong>drone</strong> <strong class='color-g'>ammo</strong>/<strong class='color-f'>efficiency</strong> by <strong>66%</strong><br>reduce the average <strong>drone</strong> lifetime by <strong>40%</strong>",
isGunTech: true,
maxCount: 3,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return !tech.isDroneRadioactive && (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.6, 1 + this.count)
tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count)
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "drones") {
const scale = Math.pow(3, this.count + 1)
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * scale
}
}
},
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", name: "necrophoresis",
description: "<strong>foam</strong> bubbles grow and split into 3 <strong>copies</strong><br> when the mob they are stuck to <strong>dies</strong>", description: "<strong>foam</strong> bubbles grow and split into 3 <strong>copies</strong><br> when the mob they are stuck to <strong>dies</strong>",
@@ -7209,5 +7245,6 @@
isBlockExplosion: null, isBlockExplosion: null,
superBallDelay: null, superBallDelay: null,
isBlockExplode: null, isBlockExplode: null,
isOverHeal: null isOverHeal: null,
isDroneRadioactive: null
} }

235
todo.txt
View File

@@ -1,20 +1,22 @@
******************************************************** NEXT PATCH ******************************************************** ******************************************************** NEXT PATCH ********************************************************
new level labs is done (it's kinda randomized, so expect it to feel different each run) tech - radioisotope generator - drones have the effect of neutron bomb, but you get less ammo
(I know there are tons of bugs, but I figure we can find them together :) bug fix - reduced tolerances now properly gives extra ammo
water shielding now protects you from all radioactivity, but only gives 75% protection
(drones, neutron bomb, radioactive explosions, and even slime)
foam gun now gets about 20% less ammo final boss has more durability in it's final phase
new junk tech: "emergency broadcasting" - plays some fun sounds and gives you health final boss no longers gets knocked around as much from explosions
updated matter.js to a newer build matter-js 0.17.1 by @liabru level.warehouse predraw lighting for performance
(was matter-js 0.14.2 by @liabru 2018-06-11) bug fix on sewers where slime was doing too much harm
matter.js patch notes suggest a possible 30% physics performance increase
decomp.min.js was removed because I don't think it does anything anymore
I did get one error message about it being missing, but there were other bugs too
******************************************************** BUGS ******************************************************** ******************************************************** BUGS ********************************************************
Final boss acts super weird when hit by explosions slime does extra damage on flipped levels?
using time dilatation breaks constraints
on map n-gon, toggles
player can become crouched while not touching the ground if they exit the ground while crouched player can become crouched while not touching the ground if they exit the ground while crouched
@@ -38,9 +40,18 @@ is there a way to check if the player is stuck inside the map or block
******************************************************** LEVELS ******************************************************** ******************************************************** LEVELS ********************************************************
make a power up you can carry around like the heal power up when at full health
drop it on a map element to turn it on?
use it in combat?
labs - procedural generation labs - procedural generation
bugs bugs
mob spawns shouldn't be based on probability? mob spawns shouldn't be based on probability?
style
graphics look too bright?
add shadows and lighting and graphic details?
what about performance?
with the mobs staggered spawning it should be fine
feel feel
disrupt the flat ground disrupt the flat ground
less platforming / easier platforming less platforming / easier platforming
@@ -49,7 +60,13 @@ labs - procedural generation
is it laggy? is it laggy?
in loot room, spawn mobs after power up is grabbed in loot room, spawn mobs after power up is grabbed
more background graphics, better colors more background graphics, better colors
science theme, with a cool technology showcased loot room:
make it more interesting to get the power up
slow player and reduce damage in region
increase the size of the region
don't have space for much
make graphics more unique
push player away, so that normal pick up methods don't work, but add a button to disable region
room ideas - room ideas -
portal room portal room
endlessly falling blocks down a slide, that the player has to climb up endlessly falling blocks down a slide, that the player has to climb up
@@ -57,18 +74,95 @@ labs - procedural generation
slime room slime room
sound room, with buttons to control sound sound room, with buttons to control sound
color room with r,g,b buttons to control color color room with r,g,b buttons to control color
test fire room, button fires blocks at a wall
mob buff zone: Map element: "Orbital Pickup Zone": Mobs that enter a specific area of the map gain +1 orbital per second, or a shield mob buff zone: Map element: "Orbital Pickup Zone": Mobs that enter a specific area of the map gain +1 orbital per second, or a shield
could put in the loot room could put in the loot room
buttons can now on/off boosts
repeat map in vertical when you fall teleport to above the mab, as if the map repeats
camera looks strange when you teleport player with a high velocity
map element - player rotates a rotor that makes a platform go up or down
level element: a zone with wind, anti-gravity, extra gravity
control with button
map: observatory
button controls rotation of telescope
laser beam shoots out of telescope
button opens the dome
map: prison
doors linked to buttons
mobs inside the doors?
boss levels - small levels just a boss, and maybe a few mobs
boss level for timeSkipBoss because of game instability for boss on normal levels
this might not fix issues
******************************************************** MOBS ********************************************************
mob mechanics
use the force at a location effect, like the plasma field
mob - after taking damage
release seekers
teleports
hop boss:
AoE damage when landing
pull in player? and blocks?
extra gravity on falling?
immune to damage while falling?
mob: molecule shapes - 2 separate mobs joined by a bond
use constraints: just spawn 2x or 3x groupings
low friction so they can spin around
spin when attacking player?
increase constraint length when attacking
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
write find in spawn undo exploder, but commented out
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
level Boss: fractal Sierpiński triangle
https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle
spawns a 1/2 size version of the boss, this version can also spawn a smaller version, but it is capped at some size level
they spawn once at the start of the level
if a version dies, one can be replaced every ten seconds by the largest version
give mobs more animal-like behaviors like rain world
mobs play, look for food, explore
mobs some times aren't aggressive
when low on life or after taking a large hit
mobs can fight each other
this might be hard to code
isolated mobs try to group up
mob: wall mounted guns / lasers
not part of randomized mob pool, customized to each level
level boss: fires a line intersection in a random direction every few seconds.
the last two intersections have a destructive laser between them.
******************************************************** TODO ******************************************************** ******************************************************** TODO ********************************************************
tech foam teleports around, like a quantum wave function collapse
should ammo apply to all guns, or just one of your guns?
if one gun only, it would make multi-gun builds weaker
nail bots should benefit from nail gun tech
tech: picking up heal power ups at max health does harm, but increases max health tech: picking up heal power ups at max health does harm, but increases max health
scales with heal value scales with heal value
does catabolism give too much ammo?
synergy with shotgun harm immunity
let standing wave harmonics get tech decorrelation let standing wave harmonics get tech decorrelation
tech: cloaking field - decrease/increase cooldown on sneak attack? tech: cloaking field - decrease/increase cooldown on sneak attack?
@@ -88,24 +182,12 @@ tech plasma : plasma length increases then decreases as you hold down the field
tech: at the start of a new level remove 5 research and spawn a second boss tech: at the start of a new level remove 5 research and spawn a second boss
what about the single axis graphic? (find the code in standing wave harmonic)
maybe just save it for a mob
maybe use it on lore
buttons can now on/off boosts
energy conservation 6% damage recovered as energy energy conservation 6% damage recovered as energy
add a negative effect: add a negative effect:
junk tech junk tech
Weak Anthropic Principle: you get a second chance at life, but .... Weak Anthropic Principle: you get a second chance at life, but ....
mob: molecule shapes - 2 separate mobs joined by a bond
use constraints: just spawn 2x or 3x groupings
low friction so they can spin around
spin when attacking player?
increase constraint length when attacking
tech: use the ability for power ups to have custom code tech: use the ability for power ups to have custom code
(note: this code is half way done, it just needs to be completed) (note: this code is half way done, it just needs to be completed)
attracted to player attracted to player
@@ -138,30 +220,11 @@ apply the new gun.do functions to other guns
works similar to foam works similar to foam
performance issues? performance issues?
tech plasma field - plasma field becomes an aoe damage field with the same radius
200% more energy drain, 100% more damage
draw a square (or two) that rapidly spins
look into improving mouse lag with pointer lock? look into improving mouse lag with pointer lock?
https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API
https://www.vsynctester.com/game.html https://www.vsynctester.com/game.html
https://news.ycombinator.com/item?id=26530272 https://news.ycombinator.com/item?id=26530272
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
write find in spawn undo exploder, but commented out
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
mob - after taking damage
attack outwardly
grows
teleports
mobile requirements: mobile requirements:
detect mobile, flip to landscape detect mobile, flip to landscape
detect no keyboard, no mouse detect no keyboard, no mouse
@@ -170,20 +233,12 @@ mobile requirements:
tap screen regions to move (WASD) tap screen regions to move (WASD)
reduce font size reduce font size
lore: a tutorial / lore intro
needs to be optional so it doesn't slow experienced players
put something on the intro map
maybe a button triggers something
add back in gamepad support add back in gamepad support
but does anyone care? but does anyone care?
https://github.com/landgreen/landgreen.github.io/search?q=gamepadconnected https://github.com/landgreen/landgreen.github.io/search?q=gamepadconnected
rename intro level to something lore related rename intro level to something lore related
rename ?
health -> integrity, unity
heal -> also integrity, unity
RPG default or tech: grenades detonate on your cursor / where your cursor was when they were fired RPG default or tech: grenades detonate on your cursor / where your cursor was when they were fired
@@ -219,18 +274,6 @@ tech pilot wave: antigravity - blocks have no gravity for a few seconds after ex
wormhole - make it clear when the wormhole can and can't teleport to a location before the player clicks wormhole - make it clear when the wormhole can and can't teleport to a location before the player clicks
flavor - your bullets destroy blocks
this isn't really a bonus, so maybe just add this as flavor to another tech field/gun
a chance for destroyed blocks to drop stuff
power ups
spores
using a reroll gives 3 options for tech, and 3 options for guns/fields/tech
or 6 options for tech (rewrite tech selection to work with 1-6 options)
the second stack of 3 tech could have repeats, so you don't have to write new tech code
adjust css to make 2 columns of 3
can't use with cardinality
new power up - increase damage and fire speed, for 15 seconds new power up - increase damage and fire speed, for 15 seconds
named boost? named boost?
enabled by a tech enabled by a tech
@@ -242,9 +285,6 @@ tech- do 50% more damage in close, but 50% less at a distance
code it like techisFarAwayDmg code it like techisFarAwayDmg
have these tech disable each other have these tech disable each other
repeat map in vertical and horizontal space
or at least vertical space
camera looks strange when you teleport player with a high velocity
new status effect: weakness, mobs do 75% les damage new status effect: weakness, mobs do 75% les damage
graphic indication? graphic indication?
@@ -267,45 +307,8 @@ look for tech that could update description text with count and tech is informat
can only use variables that change in effect() and remove() can only use variables that change in effect() and remove()
this.description = `<strong>8%</strong> chance to <strong>duplicate</strong> spawned <strong>power ups</strong><br><em>chance to duplicate = ${techduplicateChance}</em>` this.description = `<strong>8%</strong> chance to <strong>duplicate</strong> spawned <strong>power ups</strong><br><em>chance to duplicate = ${techduplicateChance}</em>`
map element - player rotates a rotor that makes a platform go up or down
use mac automator to speed up your n-gon -> git sync use mac automator to speed up your n-gon -> git sync
level Boss: fractal Sierpiński triangle
https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle
spawns a 1/2 size version of the boss, this version can also spawn a smaller version, but it is capped at some size level
they spawn once at the start of the level
if a version dies, one can be replaced every ten seconds by the largest version
level element: a zone with wind, anti-gravity, extra gravity
control with button
give mobs more animal-like behaviors like rain world
mobs play, look for food, explore
mobs some times aren't aggressive
when low on life or after taking a large hit
mobs can fight each other
this might be hard to code
isolated mobs try to group up
mob: wall mounted guns / lasers
not part of randomized mob pool, customized to each level
level boss: fires a line intersection in a random direction every few seconds.
the last two intersections have a destructive laser between them.
map: observatory
button controls rotation of telescope
laser beam shoots out of telescope
button opens the dome
map: prison
doors linked to buttons
mobs inside the doors?
graphic idea: bezier curve that moves smoothly from mob to mob
loops around player
movement fluidity movement fluidity
let legs jump on mobs, but player will still take damage let legs jump on mobs, but player will still take damage
like: ori and the blind forest, celeste like: ori and the blind forest, celeste
@@ -328,14 +331,6 @@ have a mob apply a positive status effect on other mobs,
damage bonus, but how? damage bonus, but how?
possible balance issues possible balance issues
boss levels - small levels just a boss, and maybe a few mobs
boss level for timeSkipBoss because of game instability for boss on normal levels
this might not fix issues
an effect when canceling a power up
ammo? heals?
50% chance for a tech 25% heal, 25% ammo
css transition for pause menu css transition for pause menu
animate new level spawn by having the map aspects randomly fly into place animate new level spawn by having the map aspects randomly fly into place
@@ -344,6 +339,7 @@ n-gon outreach ideas
blips - errant signal on youtube blips - errant signal on youtube
reddit - r/IndieGaming reddit - r/IndieGaming
hacker news - show hacker news post hacker news - show hacker news post
twitch - lets play
******************************************************** LORE ******************************************************** ******************************************************** LORE ********************************************************
@@ -369,6 +365,17 @@ possible names for tech
phlogiston theory is a superseded scientific theory that postulated the existence of a fire-like element called phlogiston phlogiston theory is a superseded scientific theory that postulated the existence of a fire-like element called phlogiston
Laplace's demon was a notable published articulation of causal determinism on a scientific basis by Pierre-Simon Laplace in 1814.[1] According to determinism, if someone (the demon) knows the precise location and momentum of every atom in the universe, their past and future values for any given time are entailed; they can be calculated from the laws of classical mechanics. Laplace's demon was a notable published articulation of causal determinism on a scientific basis by Pierre-Simon Laplace in 1814.[1] According to determinism, if someone (the demon) knows the precise location and momentum of every atom in the universe, their past and future values for any given time are entailed; they can be calculated from the laws of classical mechanics.
Degenerate matter is a highly dense state of fermionic matter in which the Pauli exclusion principle exerts significant pressure in addition to, or in lieu of thermal pressure. Degenerate matter is a highly dense state of fermionic matter in which the Pauli exclusion principle exerts significant pressure in addition to, or in lieu of thermal pressure.
evolutionary cosmology
eternal inflation
a tutorial / lore intro
needs to be optional so it doesn't slow experienced players
put something on the intro map
maybe a button triggers something
rename ?
health -> integrity, unity
heal -> also integrity, unity
plot script: plot script: