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
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)
m.energy -= drain
const DRAIN = (tech.isExplosionHarm ? 0.5 : 0.25) * (tech.isRadioactiveResistance ? 0.25 : 1)
// * (tech.isImmuneExplosion ? Math.min(1, Math.max(1 - m.energy * 0.7, 0)) : 1)
m.energy -= DRAIN
if (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].minDmgSpeed = 0;
bullet[me].damageRadius = 100;
bullet[me].maxDamageRadius = 450 + 130 * tech.isNeutronSlow + 130 * tech.isNeutronImmune //+ 150 * Math.random()
bullet[me].radiusDecay = (0.81 + 0.15 * tech.isNeutronSlow + 0.15 * tech.isNeutronImmune) / tech.isBulletsLastLonger
bullet[me].maxDamageRadius = 450 + 130 * tech.isNeutronSlow //+ 150 * Math.random()
bullet[me].radiusDecay = (0.81 + 0.15 * tech.isNeutronSlow) / tech.isBulletsLastLonger
bullet[me].stuckTo = null;
bullet[me].stuckToRelativePosition = null;
bullet[me].vacuumSlow = 0.97;
if (tech.isRPG) {
const SCALE = 2
@@ -1030,27 +1030,27 @@ const b = {
this.endCycle = 0;
} else {
//aoe damage to player
if (!tech.isNeutronImmune && Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) {
const DRAIN = 0.0023
if (Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) {
const DRAIN = tech.isRadioactiveResistance ? 0.0025 * 0.25 : 0.0025
if (m.energy > DRAIN) {
m.energy -= DRAIN
} else {
m.energy = 0;
m.damage(0.00015)
m.damage(tech.isRadioactiveResistance ? 0.00016 * 0.25 : 0.00016) //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.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 (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 * this.vacuumSlow,
y: mob[i].velocity.y * this.vacuumSlow
x: mob[i].velocity.x * 0.97,
y: mob[i].velocity.y * 0.97
});
}
}
@@ -1062,22 +1062,21 @@ const b = {
ctx.fill();
ctx.globalCompositeOperation = "source-over"
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++) {
const sub = Vector.sub(that.position, who[i].position);
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 * that.vacuumSlow,
y: who[i].velocity.y * that.vacuumSlow
x: who[i].velocity.x * 0.975,
y: who[i].velocity.y * 0.975
});
}
}
}
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)
});
},
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) {
// radius *= Math.sqrt(tech.bulletSize)
const me = bullet.length;
@@ -3323,7 +3558,7 @@ const b = {
if (tech.isNailRadiation) {
mobs.statusDoT(who, tech.isFastRadiation ? 12 : 3, tech.isSlowRadiation ? 240 : (tech.isFastRadiation ? 30 : 120)) // one tick every 30 cycles
} else {
let dmg = b.dmgScale * 5
let dmg = b.dmgScale * 5.5
if (tech.isCrit && who.isStunned) dmg *= 4
who.damage(dmg, tech.isNeedleShieldPierce);
simulation.drawList.push({ //add dmg to draw queue
@@ -3356,18 +3591,18 @@ const b = {
x: m.Vx / 2 + SPEED * Math.cos(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
}
if (m.crouch) {
m.fireCDcycle = m.cycle + 50 * b.fireCD; // cool down
m.fireCDcycle = m.cycle + 45 * b.fireCD; // cool down
makeNeedle()
for (let i = 1; i < 4; i++) { //4 total needles
setTimeout(() => { if (!simulation.paused) makeNeedle() }, 60 * i);
}
} else {
m.fireCDcycle = m.cycle + 30 * b.fireCD; // cool down
m.fireCDcycle = m.cycle + 25 * b.fireCD; // cool down
makeNeedle()
for (let i = 1; i < 3; i++) { //3 total needles
setTimeout(() => { if (!simulation.paused) makeNeedle() }, 60 * i);
@@ -4145,12 +4380,22 @@ const b = {
have: false,
do() {},
fire() {
if (m.crouch) {
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
if (tech.isDroneRadioactive) {
if (m.crouch) {
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 {
b.drone()
m.fireCDcycle = m.cycle + Math.floor(6 * b.fireCD); // cool down
if (m.crouch) {
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.isHorizontalFlipped = true
// level.difficultyIncrease(99)
// m.setField("standing wave harmonics")
// tech.giveTech("spherical harmonics")
// for (let i = 0; i < 9; i++) tech.giveTech("spherical harmonics")
// m.setField("nano-scale manufacturing")
// 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.giveTech("chain reaction")
// tech.giveTech("vacuum permittivity")
// tech.giveTech("quenching")
// tech.giveTech("decoherence")
// 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);
}
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);
}
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)
if (this.height > 0 && Matter.Query.region([player], this).length) {
const drain = 0.003 + m.fieldRegen
if (m.energy > drain) {
m.energy -= drain
const DRAIN = 0.003 * (tech.isRadioactiveResistance ? 0.25 : 1) + m.fieldRegen
console.log(DRAIN)
if (m.energy > DRAIN) {
m.energy -= DRAIN
} else {
if (damage < 0.02) {
m.damage(damage)
} else if (m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + tech.collisionImmuneCycles;
m.damage(damage)
simulation.drawList.push({ //add dmg to draw queue
x: player.position.x,
y: player.position.y,
radius: damage * 1500,
color: simulation.mobDmgColor,
time: 20
});
}
m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1))
}
//float
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())
spawn.bodyRect(offset.x + 2050 - doorWidth2 / 2, offset.y - 175, doorWidth2, 165); //block door
} 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() {
// const button = level.button(200, -700)
const toggle = level.toggle(200, -700)
// const toggle = level.toggle(200, -700)
level.custom = () => {
// button.draw();
ctx.fillStyle = "rgba(0,255,255,0.1)";
@@ -2096,7 +2088,7 @@ const level = {
level.enter.draw();
};
level.customTopLayer = () => {
toggle.query();
// toggle.query();
};
level.setPosToSpawn(0, -750); //normal spawn
@@ -2127,11 +2119,9 @@ const level = {
function blockDoor(x, y, blockSize = 58) {
spawn.mapRect(x, y - 290, 40, 60); // door lip
spawn.mapRect(x, y, 40, 50); // door lip
for (let i = 0; i < 4; ++i) {
spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize);
}
for (let i = 0; i < 4; ++i) 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");
spawn.mapRect(2500, -1200, 200, 750); //right wall
@@ -2151,10 +2141,10 @@ const level = {
// spawn.shooterBoss(1900, -500)
// spawn.historyBoss(1200, -500)
// spawn.laserTargetingBoss(1600, -400)
// spawn.striker(1600, -500)
spawn.hopper(1600, -500)
// spawn.laserTargetingBoss(1700, -120)
// spawn.bomberBoss(1400, -500)
// spawn.ghoster(1800, -120)
spawn.hopBoss(1800, -120)
// spawn.streamBoss(1600, -500)
// spawn.orbitalBoss(1600, -500)
// spawn.cellBossCulture(1600, -500)
@@ -2962,8 +2952,6 @@ const level = {
button1.query();
button1.draw();
hazard.query();
hazard.level(button1.isUp)
rotor.rotate();
ctx.fillStyle = "hsl(175, 15%, 76%)"
@@ -4017,43 +4005,54 @@ const level = {
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 = () => {
ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights
ctx.beginPath()
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()
ctx.fill(lightingPath);
};
level.setPosToSpawn(25, -55); //normal spawn

View File

@@ -495,7 +495,7 @@ const m = {
if (tech.isSlowFPS) dmg *= 0.8
// if (tech.isPiezo) dmg *= 0.85
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.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.34
if (tech.energyRegen === 0) dmg *= 0.34
@@ -1682,6 +1682,9 @@ const m = {
} else if (tech.isIceField) {
m.energy -= 0.04;
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 {
m.energy -= 0.45 * tech.droneEnergyReduction;
b.drone()

View File

@@ -736,7 +736,11 @@ const powerUps = {
//bonus power ups for clearing runs in the last game
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
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 });
//aoe damage to player
if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(player.position, this.position)) < this.radius && !tech.isNeutronImmune) {
const DRAIN = 0.07
if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(player.position, this.position)) < this.radius) {
const DRAIN = tech.isRadioactiveResistance ? 0.07 * 0.25 : 0.07
if (m.energy > DRAIN) {
m.energy -= DRAIN
} else {
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
x: this.position.x,
y: this.position.y,
@@ -233,7 +233,7 @@ const spawn = {
y: me.position.y
},
bodyB: me,
stiffness: 0.001,
stiffness: 1,
damping: 1
});
World.add(engine.world, me.constraint);
@@ -244,7 +244,7 @@ const spawn = {
me.memory = Infinity;
me.hasRunDeathScript = false
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
// spawn.shield(me, x, y, 1);
me.onDeath = function() {
@@ -416,7 +416,7 @@ const spawn = {
}
} else if (this.mode !== 3) { //all three modes at once
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) {
Matter.Body.scale(this, 5, 5);
} else {
@@ -1017,6 +1017,8 @@ const spawn = {
me.accelMag = 0.04;
me.g = 0.0017; //required if using 'gravity'
me.frictionAir = 0.01;
me.friction = 1
me.frictionStatic = 1
me.restitution = 0;
me.delay = 120 * simulation.CDScale;
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 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.07 + 0.02) * this.mass; //antigravity
this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.07 + 0.06) * this.mass; //antigravity
}
} else {
//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)) {
mobs.spawn(x, y, 5, radius, "#000000");
let me = mob[mob.length - 1];
@@ -2063,7 +2155,7 @@ const spawn = {
y: me.position.y
},
bodyB: me,
stiffness: 0.001,
stiffness: 1,
damping: 1
});
World.add(engine.world, me.constraint);
@@ -2575,8 +2667,8 @@ const spawn = {
y: me.position.y
},
bodyB: me,
stiffness: 0.0002,
damping: 1
stiffness: 0.00004,
damping: 0.1
});
World.add(engine.world, me.constraint);
}, 2000); //add in a delay in case the level gets flipped left right
@@ -2912,7 +3004,7 @@ const spawn = {
y: me.position.y
},
bodyB: me,
stiffness: 0.001,
stiffness: 0.0001,
damping: 1
});
World.add(engine.world, me.constraint);

View File

@@ -177,7 +177,7 @@
if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.22
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.isBotDamage) dmg *= 1 + 0.05 * b.totalBots()
if (tech.isBotDamage) dmg *= 1 + 0.06 * b.totalBots()
return dmg * tech.slowFire * tech.aimDamage
},
duplicationChance() {
@@ -716,7 +716,7 @@
},
requires: "an explosive damage source, not ammonium nitrate or nitroglycerin",
effect: () => {
tech.isExplodeRadio = true;
tech.isExplodeRadio = true; //iridium-192
},
remove() {
tech.isExplodeRadio = false;
@@ -823,9 +823,9 @@
frequency: 2,
frequencyDefault: 2,
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: () => {
tech.isImmuneExplosion = true;
},
@@ -841,9 +841,9 @@
frequency: 2,
frequencyDefault: 2,
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() {
tech.isIncendiary = true
},
@@ -1376,7 +1376,7 @@
},
{
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,
count: 0,
frequency: 2,
@@ -1394,7 +1394,7 @@
},
{
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,
count: 0,
frequency: 2,
@@ -2509,7 +2509,7 @@
},
{
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,
count: 0,
frequency: 1,
@@ -4039,7 +4039,7 @@
},
{
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,
maxCount: 1,
count: 0,
@@ -4060,35 +4060,35 @@
},
{
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,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isNeutronBomb
return tech.isNeutronBomb || tech.isDroneRadioactive
},
requires: "neutron bomb",
requires: "neutron bomb or radioactive drones",
effect() {
tech.isNeutronImmune = true
tech.isRadioactiveResistance = true
},
remove() {
tech.isNeutronImmune = false
tech.isRadioactiveResistance = false
}
},
{
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,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isNeutronBomb
return tech.isNeutronBomb || tech.isDroneRadioactive
},
requires: "neutron bomb",
requires: "neutron bomb or radioactive drones",
effect() {
tech.isNeutronSlow = true
},
@@ -4270,6 +4270,39 @@
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",
description: "<strong>drones</strong> accelerate <strong>50%</strong> faster",
@@ -4308,33 +4341,6 @@
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",
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
}
},
{
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",
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,
superBallDelay: null,
isBlockExplode: null,
isOverHeal: null
isOverHeal: null,
isDroneRadioactive: null
}

235
todo.txt
View File

@@ -1,20 +1,22 @@
******************************************************** NEXT PATCH ********************************************************
new level labs is done (it's kinda randomized, so expect it to feel different each run)
(I know there are tons of bugs, but I figure we can find them together :)
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)
foam gun now gets about 20% less ammo
new junk tech: "emergency broadcasting" - plays some fun sounds and gives you health
final boss has more durability in it's final phase
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
(was matter-js 0.14.2 by @liabru 2018-06-11)
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
level.warehouse predraw lighting for performance
bug fix on sewers where slime was doing too much harm
******************************************************** 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
@@ -38,9 +40,18 @@ is there a way to check if the player is stuck inside the map or block
******************************************************** 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
bugs
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
disrupt the flat ground
less platforming / easier platforming
@@ -49,7 +60,13 @@ labs - procedural generation
is it laggy?
in loot room, spawn mobs after power up is grabbed
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 -
portal room
endlessly falling blocks down a slide, that the player has to climb up
@@ -57,18 +74,95 @@ labs - procedural generation
slime room
sound room, with buttons to control sound
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
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 ********************************************************
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
scales with heal value
does catabolism give too much ammo?
synergy with shotgun harm immunity
let standing wave harmonics get tech decorrelation
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
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
add a negative effect:
junk tech
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
(note: this code is half way done, it just needs to be completed)
attracted to player
@@ -138,30 +220,11 @@ apply the new gun.do functions to other guns
works similar to foam
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?
https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API
https://www.vsynctester.com/game.html
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:
detect mobile, flip to landscape
detect no keyboard, no mouse
@@ -170,20 +233,12 @@ mobile requirements:
tap screen regions to move (WASD)
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
but does anyone care?
https://github.com/landgreen/landgreen.github.io/search?q=gamepadconnected
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
@@ -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
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
named boost?
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
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
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()
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
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
let legs jump on mobs, but player will still take damage
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?
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
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
reddit - r/IndieGaming
hacker news - show hacker news post
twitch - lets play
******************************************************** 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
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.
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: