diff --git a/img/Higgs mechanism.webp b/img/Higgs mechanism.webp
index 4d7c71f..26ede45 100644
Binary files a/img/Higgs mechanism.webp and b/img/Higgs mechanism.webp differ
diff --git a/img/dead reckoning.webp b/img/dead reckoning.webp
index aca40a0..05e9abf 100644
Binary files a/img/dead reckoning.webp and b/img/dead reckoning.webp differ
diff --git a/img/quantum eraser.webp b/img/metamaterial absorber.webp
similarity index 100%
rename from img/quantum eraser.webp
rename to img/metamaterial absorber.webp
diff --git a/img/negative entropy.webp b/img/negative entropy.webp
deleted file mode 100644
index a64846b..0000000
Binary files a/img/negative entropy.webp and /dev/null differ
diff --git a/img/self-assembly.webp b/img/self-assembly.webp
new file mode 100644
index 0000000..3cb0fe2
Binary files /dev/null and b/img/self-assembly.webp differ
diff --git a/img/sound-bot upgrade.webp b/img/sound-bot upgrade.webp
new file mode 100644
index 0000000..119cbfa
Binary files /dev/null and b/img/sound-bot upgrade.webp differ
diff --git a/img/sound-bot.webp b/img/sound-bot.webp
new file mode 100644
index 0000000..85b39b8
Binary files /dev/null and b/img/sound-bot.webp differ
diff --git a/img/von Neumann probe.webp b/img/von Neumann probe.webp
new file mode 100644
index 0000000..7addbb3
Binary files /dev/null and b/img/von Neumann probe.webp differ
diff --git a/js/bullet.js b/js/bullet.js
index cdf687d..ef2a272 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -3648,6 +3648,31 @@ const b = {
bullet[bullet.length - 1].isMutualismActive = true
}
},
+ delayDrones(where, droneCount = 1) {
+ let respawnDrones = () => {
+ if (droneCount > 0) {
+ requestAnimationFrame(respawnDrones);
+ if (!simulation.paused && !simulation.isChoosing && m.alive) {
+ droneCount--
+ if (tech.isDroneRadioactive) {
+ b.droneRadioactive({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0)
+ } else {
+ b.drone({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0)
+ if (tech.isDroneGrab && deliveryCount > 0) {
+ const who = bullet[bullet.length - 1]
+ who.isImproved = true;
+ const SCALE = 2.25
+ Matter.Body.scale(who, SCALE, SCALE);
+ who.lookFrequency = 30 + Math.floor(11 * Math.random());
+ who.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
+ deliveryCount--
+ }
+ }
+ }
+ }
+ }
+ requestAnimationFrame(respawnDrones);
+ },
drone(where = {
x: m.pos.x + 30 * Math.cos(m.angle) + 20 * (Math.random() - 0.5),
y: m.pos.y + 30 * Math.sin(m.angle) + 20 * (Math.random() - 0.5)
@@ -3693,10 +3718,7 @@ const b = {
} else {
//move away from target after hitting
const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20)
- Matter.Body.setVelocity(this, {
- x: unit.x,
- y: unit.y
- });
+ Matter.Body.setVelocity(this, { x: unit.x, y: unit.y });
this.lockedOn = null
if (this.endCycle > simulation.cycle + this.deathCycles) {
this.endCycle -= 60
@@ -3705,68 +3727,164 @@ const b = {
}
},
onEnd() {
- if (tech.isDroneRespawn && b.inventory.length && b.activeGun === 7 && b.guns[b.activeGun].ammo > 0 && mob.length) {
- b.drone({
- x: this.position.x,
- y: this.position.y
- }, 0)
- if (Math.random() < 0.2) {
- b.guns[b.activeGun].ammo--;
- simulation.updateGunHUD();
+ if (tech.isDroneRespawn) {
+ //are there any nearby bodies nearby that aren't blocked by map?
+ const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable && Vector.magnitude(Vector.sub(this.position, a.position)) < 70 + 30 * a.mass)
+ if (canSee.length) {
+ //find the closest body to the drone from the canSee array
+ const found = canSee.reduce((a, b) => {
+ const distA = Vector.magnitude(Vector.sub(this.position, a.position))
+ const distB = Vector.magnitude(Vector.sub(this.position, b.position))
+ return distA < distB ? a : b
+ })
+ if (found && m.energy > 0.05) {
+ m.energy -= 0.05
+ //remove the body and spawn a new drone
+ Composite.remove(engine.world, found)
+ body.splice(body.indexOf(found), 1)
+ b.delayDrones(found.position, 0.7 * Math.sqrt(found.mass))
+ //draw a line from the drone to the body on the canvas
+ ctx.beginPath();
+ ctx.moveTo(this.position.x, this.position.y);
+ ctx.lineTo(found.position.x, found.position.y);
+ ctx.strokeStyle = "#000";
+ ctx.lineWidth = 2;
+ ctx.stroke();
+
+ //animate the block fading away
+ simulation.ephemera.push({
+ name: "droneRespawn",
+ count: 60, //cycles before it self removes
+ do() {
+ this.count--
+ if (this.count < 0) simulation.removeEphemera(this.name)
+ ctx.beginPath();
+ let vertices = found.vertices;
+ ctx.moveTo(vertices[0].x, vertices[0].y);
+ for (let j = 1; j < vertices.length; j++) {
+ ctx.lineTo(vertices[j].x, vertices[j].y);
+ }
+ ctx.lineTo(vertices[0].x, vertices[0].y);
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = `rgba(0,0,0,${this.count / 60})`
+ ctx.stroke();
+ },
+ })
+ }
}
}
},
- do() {
- 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);
+ doRespawning() { //fall shrink and die
+ const scale = 0.995;
+ Matter.Body.scale(this, scale, scale);
+ if (this.bodyTarget) {
+ this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.bodyTarget.position)), -this.mass * THRUST)
} else {
- this.force.y += this.mass * 0.0002;
- 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 &&
- !mob[i].isInvulnerable
- ) {
- 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]
- }
+ this.force.y += this.mass * 0.0012;
+ }
+ },
+ doDieing() { //fall shrink and die
+ this.force.y += this.mass * 0.0012;
+ const scale = 0.995;
+ Matter.Body.scale(this, scale, scale);
+ },
+ do() {
+ if (simulation.cycle + this.deathCycles > this.endCycle) {
+ this.restitution = 0.2;
+ if (tech.isDroneRespawn) {
+ this.do = this.doRespawning
+ //make a list of all elements of array body that a ray can be drawn to from the drone
+ const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable)
+ if (canSee.length) {
+ //find the closest body to the drone from the canSee array
+ const found = canSee.reduce((a, b) => {
+ const distA = Vector.magnitude(Vector.sub(this.position, a.position))
+ const distB = Vector.magnitude(Vector.sub(this.position, b.position))
+ return distA < distB ? a : b
+ })
+ if (found) this.bodyTarget = found
+ }
+ } else {
+ this.do = this.doDieing
+ }
+ }
+
+ this.force.y += this.mass * 0.0002;
+ 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 &&
+ !mob[i].isInvulnerable
+ ) {
+ 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]
}
}
- //blink towards mobs
- if (tech.isDroneTeleport && this.lockedOn) {
- const sub = Vector.sub(this.lockedOn.position, this.position);
- const distMag = Vector.magnitude(sub);
- const unit = Vector.normalise(sub)
- Matter.Body.setVelocity(this, Vector.mult(unit, Math.max(20, this.speed * 1.5)));
- ctx.beginPath();
- ctx.moveTo(this.position.x, this.position.y);
- Matter.Body.translate(this, Vector.mult(unit, Math.min(350, distMag - this.lockedOn.radius + 10)));
- ctx.lineTo(this.position.x, this.position.y);
- ctx.lineWidth = RADIUS * 2;
- ctx.strokeStyle = "rgba(0,0,0,0.5)";
- ctx.stroke();
- }
- //power ups
- if (!this.isImproved && !simulation.isChoosing) {
- if (this.lockedOn) {
- for (let i = 0, len = powerUp.length; i < len; ++i) { //grab, but don't lock onto nearby power up
- if (
- Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 &&
- (powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
- (powerUp[i].name !== "field" || !tech.isSuperDeterminism)
- // &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
- ) {
+ }
+ //blink towards mobs
+ if (tech.isDroneTeleport && this.lockedOn) {
+ const sub = Vector.sub(this.lockedOn.position, this.position);
+ const distMag = Vector.magnitude(sub);
+ const unit = Vector.normalise(sub)
+ Matter.Body.setVelocity(this, Vector.mult(unit, Math.max(20, this.speed * 1.5)));
+ ctx.beginPath();
+ ctx.moveTo(this.position.x, this.position.y);
+ Matter.Body.translate(this, Vector.mult(unit, Math.min(350, distMag - this.lockedOn.radius + 10)));
+ ctx.lineTo(this.position.x, this.position.y);
+ ctx.lineWidth = RADIUS * 2;
+ ctx.strokeStyle = "rgba(0,0,0,0.5)";
+ ctx.stroke();
+ }
+ //power ups
+ if (!this.isImproved && !simulation.isChoosing) {
+ if (this.lockedOn) {
+ for (let i = 0, len = powerUp.length; i < len; ++i) { //grab, but don't lock onto nearby power up
+ if (
+ Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 &&
+ (powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
+ (powerUp[i].name !== "field" || !tech.isSuperDeterminism)
+ // &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
+ ) {
+ //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.Composite.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 += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
+ }
+ 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.93 * m.maxHealth || tech.isDroneGrab) &&
+ (powerUp[i].name !== "field" || !tech.isSuperDeterminism)
+ // &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
+ ) {
+ 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);
@@ -3785,71 +3903,38 @@ const b = {
Matter.Body.scale(this, SCALE, SCALE);
this.lookFrequency = 30 + Math.floor(11 * Math.random());
this.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
+ // this.frictionAir = 0
}
break;
}
- }
- } else {
- //look for power ups to lock onto
- let closeDist = Infinity;
- for (let i = 0, len = powerUp.length; i < len; ++i) {
+ //look for power ups to lock onto
if (
- (powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
- (powerUp[i].name !== "field" || !tech.isSuperDeterminism)
- // &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
+ Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 &&
+ Matter.Query.ray(body, this.position, powerUp[i].position).length === 0
) {
- 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.Composite.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 += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
- // this.frictionAir = 0
- }
- 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]
- }
+ 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
- });
- }
+ }
+ 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
+ });
}
}
})
@@ -3891,14 +3976,50 @@ const b = {
maxRadioRadius: 270 + Math.floor(90 * Math.random()),
beforeDmg() { },
onEnd() {
- if (tech.isDroneRespawn && b.inventory.length && b.activeGun === 7 && b.guns[b.activeGun].ammo > 0 && mob.length) {
- b.droneRadioactive({
- x: this.position.x,
- y: this.position.y
- }, 0)
- if (Math.random() < 0.2) {
- b.guns[b.activeGun].ammo--;
- simulation.updateGunHUD();
+ if (tech.isDroneRespawn) {
+ //are there any nearby bodies nearby that aren't blocked by map?
+ const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable && Vector.magnitude(Vector.sub(this.position, a.position)) < 70 + 30 * a.mass)
+ if (canSee.length) {
+ //find the closest body to the drone from the canSee array
+ const found = canSee.reduce((a, b) => {
+ const distA = Vector.magnitude(Vector.sub(this.position, a.position))
+ const distB = Vector.magnitude(Vector.sub(this.position, b.position))
+ return distA < distB ? a : b
+ })
+ if (found && m.energy > 0.05) {
+ m.energy -= 0.1
+ //remove the body and spawn a new drone
+ Composite.remove(engine.world, found)
+ body.splice(body.indexOf(found), 1)
+ b.delayDrones(found.position, 0.35 * Math.sqrt(found.mass))
+ //draw a line from the drone to the body on the canvas
+ ctx.beginPath();
+ ctx.moveTo(this.position.x, this.position.y);
+ ctx.lineTo(found.position.x, found.position.y);
+ ctx.strokeStyle = "#000";
+ ctx.lineWidth = 2;
+ ctx.stroke();
+
+ //animate the block fading away
+ simulation.ephemera.push({
+ name: "droneRespawn",
+ count: 60, //cycles before it self removes
+ do() {
+ this.count--
+ if (this.count < 0) simulation.removeEphemera(this.name)
+ ctx.beginPath();
+ let vertices = found.vertices;
+ ctx.moveTo(vertices[0].x, vertices[0].y);
+ for (let j = 1; j < vertices.length; j++) {
+ ctx.lineTo(vertices[j].x, vertices[j].y);
+ }
+ ctx.lineTo(vertices[0].x, vertices[0].y);
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = `rgba(0,0,0,${this.count / 60})`
+ ctx.stroke();
+ },
+ })
+ }
}
}
},
@@ -4074,7 +4195,8 @@ const b = {
bullet[me] = Bodies.polygon(where.x, where.y, 12, radius, b.fireAttributes(dir, false));
Composite.add(engine.world, bullet[me]); //add bullet to world
Matter.Body.setVelocity(bullet[me], velocity);
- Matter.Body.setDensity(bullet[me], 0.0007 + 0.00055 * tech.isSuperHarm);
+ bullet[me].calcDensity = () => 0.0007 + 0.00055 * tech.isSuperHarm + 0.0004 * tech.isBulletTeleport
+ Matter.Body.setDensity(bullet[me], bullet[me].calcDensity());
bullet[me].endCycle = simulation.cycle + Math.floor(270 + 90 * Math.random());
bullet[me].minDmgSpeed = 0;
bullet[me].restitution = 1;
@@ -4086,18 +4208,7 @@ const b = {
this.force.y += this.mass * 0.001;
if (Matter.Query.collides(this, [player]).length) {
this.endCycle = 0
- // let dmg = 0.015 * this.mass * tech.isSuperHarm
- // m.damage(dmg);
- const drain = m.energy * 0.25
- m.energy -= drain
-
- // simulation.drawList.push({ //add dmg to draw queue
- // x: this.position.x,
- // y: this.position.y,
- // radius: radius * 3,
- // color: "hsla(194, 100%, 43%,0.2)",
- // time: 7
- // });
+ m.energy -= m.energy * 0.25
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
@@ -4113,6 +4224,18 @@ const b = {
if (this.cycle > 2) this.do = this.collidePlayerDo
this.force.y += this.mass * 0.001;
};
+ } else if (tech.isBulletTeleport) {
+ bullet[me].portFrequency = 25 + Math.floor(10 * Math.random())
+ bullet[me].nextPortCycle = simulation.cycle + bullet[me].portFrequency
+ bullet[me].do = function () {
+ this.force.y += this.mass * 0.001;
+ if (this.nextPortCycle < simulation.cycle) { //teleport around if you have tech.isBulletTeleport
+ this.nextPortCycle = simulation.cycle + this.portFrequency
+ const range = 33 * Math.sqrt(radius) * Math.random()
+ Matter.Body.setPosition(this, Vector.add(this.position, Vector.rotate({ x: range, y: 0 }, 2 * Math.PI * Math.random())))
+ Matter.Body.setVelocity(this, Vector.rotate(this.velocity, 2 * (Math.random() * Math.random() - 0.25)))
+ }
+ };
} else {
bullet[me].do = function () {
this.force.y += this.mass * 0.001;
@@ -4133,7 +4256,7 @@ const b = {
this.endCycle = 0
} else if (tech.isSuperBounce) {
const cycle = () => {
- Matter.Body.setDensity(this, (0.0008 + 0.0009 * tech.isSuperHarm) * 1.33); //50% more density and damage
+ Matter.Body.setDensity(bullet[me], bullet[me].calcDensity() * 1.33);//33% more density and damage
this.endCycle = simulation.cycle + Math.floor(300 + 90 * Math.random()); //reset to full duration of time
Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(this.velocity), 60)); //reset to high velocity
@@ -4848,7 +4971,7 @@ const b = {
// **************************************************************************************************
// **************************************************************************************************
totalBots() {
- return tech.dynamoBotCount + tech.foamBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount
+ return tech.dynamoBotCount + tech.foamBotCount + tech.soundBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount
},
hasBotUpgrade() {
return tech.isNailBotUpgrade + tech.isFoamBotUpgrade + tech.isBoomBotUpgrade + tech.isLaserBotUpgrade + tech.isOrbitBotUpgrade + tech.isDynamoBotUpgrade
@@ -4898,6 +5021,10 @@ const b = {
tech.foamBotCount--
return
}
+ if (tech.soundBotCount > 1) {
+ tech.soundBotCount--
+ return
+ }
if (tech.boomBotCount > 1) {
tech.boomBotCount--
return
@@ -4924,6 +5051,7 @@ const b = {
tech.laserBotCount = 0
tech.nailBotCount = 0
tech.foamBotCount = 0
+ tech.soundBotCount = 0
tech.boomBotCount = 0
tech.orbitBotCount = 0
tech.missileBotCount = 0
@@ -4945,6 +5073,10 @@ const b = {
x: player.position.x + 50 * (Math.random() - 0.5),
y: player.position.y + 50 * (Math.random() - 0.5)
}, false)
+ for (let i = 0; i < tech.soundBotCount; i++) b.soundBot({
+ x: player.position.x + 50 * (Math.random() - 0.5),
+ y: player.position.y + 50 * (Math.random() - 0.5)
+ }, false)
for (let i = 0; i < tech.boomBotCount; i++) b.boomBot({
x: player.position.x + 50 * (Math.random() - 0.5),
y: player.position.y + 50 * (Math.random() - 0.5)
@@ -4968,13 +5100,16 @@ const b = {
}
},
randomBot(where = player.position, isKeep = true, isLaser = true) {
- if (Math.random() < 0.5) { //chance to match scrap bot to your upgrade
+ if (Math.random() < 0.5) { //chance to match bot to your upgrade
if (tech.isNailBotUpgrade) { //check for upgrades first
b.nailBot(where, isKeep)
if (isKeep) tech.nailBotCount++;
} else if (tech.isFoamBotUpgrade) {
b.foamBot(where, isKeep)
if (isKeep) tech.foamBotCount++;
+ } else if (tech.isSoundBotUpgrade) {
+ b.soundBot(where, isKeep)
+ if (isKeep) tech.soundBotCount++;
} else if (tech.isBoomBotUpgrade) {
b.boomBot(where, isKeep)
if (isKeep) tech.boomBotCount++;
@@ -4987,7 +5122,10 @@ const b = {
} else if (tech.isDynamoBotUpgrade) {
b.dynamoBot(where, isKeep)
if (isKeep) tech.dynamoBotCount++;
- } else if (Math.random() < 0.166 && isLaser) { //random
+ } else if (Math.random() < 0.143) { //random
+ b.soundBot(where, isKeep)
+ if (isKeep) tech.soundBotCount++;
+ } else if (Math.random() < 0.166 && isLaser) {
b.laserBot(where, isKeep)
if (isKeep) tech.laserBotCount++;
} else if (Math.random() < 0.2) {
@@ -5006,8 +5144,11 @@ const b = {
b.boomBot(where, isKeep)
if (isKeep) tech.boomBotCount++;
}
- } else { //else don't match scrap bot to upgrade
- if (Math.random() < 0.166 && isLaser) { //random
+ } else { //else don't match bot to upgrade
+ if (Math.random() < 0.143) { //random
+ b.soundBot(where, isKeep)
+ if (isKeep) tech.soundBotCount++;
+ } else if (Math.random() < 0.166 && isLaser) { //random
b.laserBot(where, isKeep)
if (isKeep) tech.laserBotCount++;
} else if (Math.random() < 0.2) {
@@ -5084,7 +5225,7 @@ const b = {
if (Vector.magnitude(Vector.sub(this.position, player.position)) < 250 && m.immuneCycle < m.cycle) { //give energy
Matter.Body.setAngularVelocity(this, this.spin)
if (this.isUpgraded) {
- m.energy += 0.115
+ m.energy += 0.12
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
@@ -5093,7 +5234,7 @@ const b = {
time: simulation.drawTime
});
} else {
- m.energy += 0.035
+ m.energy += 0.04
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
@@ -5148,10 +5289,7 @@ const b = {
Composite.add(engine.world, bullet[me]); //add bullet to world
b.setDynamoBotDelay()
},
- nailBot(position = {
- x: player.position.x + 50 * (Math.random() - 0.5),
- y: player.position.y + 50 * (Math.random() - 0.5)
- }, isConsole = true) {
+ nailBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) {
if (isConsole) simulation.makeTextLog(`b.nailBot()`);
const me = bullet.length;
const dir = m.angle;
@@ -5168,7 +5306,7 @@ const b = {
minDmgSpeed: 2,
// lookFrequency: 56 + Math.floor(17 * Math.random()) - isUpgraded * 20,
lastLookCycle: simulation.cycle + 60 * Math.random(),
- delay: Math.floor((tech.isNailBotUpgrade ? 20 : 100) * b.fireCDscale),
+ delay: Math.floor((tech.isNailBotUpgrade ? 18 : 85) * b.fireCDscale),
acceleration: 0.005 * (1 + 0.5 * Math.random()),
range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots(),
endCycle: Infinity,
@@ -5199,13 +5337,13 @@ const b = {
) {
const unit = Vector.normalise(Vector.sub(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60)), this.position))
if (this.isUpgraded) {
- const SPEED = 55
+ const SPEED = 60
b.nail(this.position, Vector.mult(unit, SPEED))
- this.force = Vector.mult(unit, -0.018 * this.mass)
+ this.force = Vector.mult(unit, -0.02 * this.mass)
} else {
- const SPEED = 40
+ const SPEED = 45
b.nail(this.position, Vector.mult(unit, SPEED))
- this.force = Vector.mult(unit, -0.01 * this.mass)
+ this.force = Vector.mult(unit, -0.012 * this.mass)
}
break;
}
@@ -5354,7 +5492,7 @@ const b = {
if (this.cd < simulation.cycle && !m.isCloak && !(simulation.cycle % this.lookFrequency)) {
for (let i = 0, len = mob.length; i < len; i++) {
const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position));
- if (dist2 < 1700000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) {
+ if (dist2 < 1600000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) {
this.fireTarget = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) //set target to where the mob will be in 1 second
this.fire()
break;
@@ -5414,6 +5552,172 @@ const b = {
})
Composite.add(engine.world, bullet[me]); //add bullet to world
},
+ soundBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) {
+ if (isConsole) simulation.makeTextLog(`b.soundBot()`);
+ const me = bullet.length;
+ const dir = m.angle;
+ bullet[me] = Bodies.rectangle(position.x, position.y, 12, 30, {
+ isUpgraded: tech.isSoundBotUpgrade,
+ botType: "sound",
+ angle: dir,
+ friction: 0,
+ frictionStatic: 0,
+ frictionAir: 0.05,
+ restitution: 0.6 * (1 + 0.5 * Math.random()),
+ dmg: 0, // 0.14 //damage done in addition to the damage from momentum
+ minDmgSpeed: 2,
+ lookFrequency: 17 + Math.floor(7 * Math.random()) - 10 * tech.isSoundBotUpgrade,
+ cd: 0,
+ fireCount: 0,
+ fireLimit: 5 + 3 * tech.isSoundBotUpgrade,
+ delay: Math.floor((90 + (tech.isSoundBotUpgrade ? 0 : 90)) * b.fireCDscale),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade,
+ acceleration: 0.005 * (1 + 0.5 * Math.random()),
+ range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots(), //how far from the player the bot will move
+ endCycle: Infinity,
+ classType: "bullet",
+ collisionFilter: {
+ category: cat.bullet,
+ mask: b.totalBots() < 50 ? cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield //if over 50 bots, they no longer collide with each other
+ },
+ beforeDmg() { },
+ onEnd() { },
+ fireTarget: { x: 0, y: 0 },
+ waves: [],
+ phononWaveCD: 0,
+ addWave(where, angle) {
+ const halfArc = 0.2 * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) + 0.05 * tech.isSoundBotUpgrade //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions
+ this.waves.push({
+ position: where,
+ angle: angle - halfArc, //used in drawing ctx.arc
+ unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision
+ unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision
+ arc: halfArc * 2,
+ radius: 25,
+ resonanceCount: 0,
+ dmg: (tech.isUpgraded ? 3 : 1.5) * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.5 : 1),
+ })
+ },
+ fire() {
+ if (!(simulation.cycle % (6 - 2 * tech.isSoundBotUpgrade))) {
+ this.fireCount++
+ if (this.fireCount > this.fireLimit) {
+ this.fireCount = 0
+ this.cd = simulation.cycle + this.delay;
+ }
+ this.addWave({ x: this.position.x, y: this.position.y }, Math.atan2(this.fireTarget.y - this.position.y, this.fireTarget.x - this.position.x) + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5)) //add wave to waves array
+ //face target
+ Matter.Body.setAngle(this, Vector.angle(this.position, this.fireTarget));
+ }
+ },
+ do() {
+ if (this.fireCount === 0) { //passive mode: look for targets and following player
+ const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos))
+ if (distanceToPlayer > this.range) { //if far away move towards player
+ this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.acceleration)
+ } else { //close to player
+ Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity
+ }
+ if (this.cd < simulation.cycle && !m.isCloak && !(simulation.cycle % this.lookFrequency)) {
+ for (let i = 0, len = mob.length; i < len; i++) {
+ const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position));
+ if (dist2 < 1300000 && !mob[i].isBadTarget && (Matter.Query.ray(map, this.position, mob[i].position).length === 0 || dist2 < 300000) && !mob[i].isInvulnerable) {
+ this.fireTarget = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) //set target to where the mob will be in 1 second
+ this.fire()
+ break;
+ }
+ }
+ }
+ } else { //fire mode: quickly fire at targets and doesn't follow player
+ this.fire()
+ }
+ if (!m.isBodiesAsleep) { //update current waves
+ ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000";
+ ctx.lineWidth = 2 * tech.wavePacketDamage
+ ctx.beginPath();
+ const end = 1200 * tech.isBulletsLastLonger
+ //this does less damage than the player phonon waves 2.3 -> 2
+ for (let i = this.waves.length - 1; i > -1; i--) {
+ const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius))
+ const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius))
+ //draw wave
+ ctx.moveTo(v1.x, v1.y)
+ ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc);
+ //using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector
+ let hits = Matter.Query.ray(mob, v1, v2, 50)
+ for (let j = 0; j < hits.length; j++) {
+ const who = hits[j].body
+ if (!who.isShielded) {
+ who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
+ who.force.y += 0.01 * (Math.random() - 0.5) * who.mass
+ Matter.Body.setVelocity(who, { x: who.velocity.x * 0.95, y: who.velocity.y * 0.95 });
+ let vertices = who.vertices;
+ const vibe = 50 + who.radius * 0.15
+ ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
+ for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5));
+ ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
+ who.locatePlayer();
+ who.damage(this.waves[i].dmg / Math.pow(who.radius, 0.33));
+
+
+ if (tech.isPhononWave && this.phononWaveCD < m.cycle) {
+ this.phononWaveCD = m.cycle + 10 * (1 + this.waves[i].resonanceCount)
+ let closestMob, dist
+ let range = end - 30 * this.waves[i].resonanceCount
+ for (let i = 0, len = mob.length; i < len; i++) {
+ if (who !== mob[i] && !mob[i].isBadTarget && !mob[i].isInvulnerable) {
+ dist = Vector.magnitude(Vector.sub(who.position, mob[i].position));
+ if (dist < range) {
+ closestMob = mob[i]
+ range = dist
+ }
+ }
+ }
+ if (closestMob) { //add wave to waves array
+ this.addWave(who.position, Math.atan2(closestMob.position.y - who.position.y, closestMob.position.x - who.position.x) + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5))
+ } else {
+ this.addWave(who.position, Math.random() * Math.PI)
+ }
+ this.waves[this.waves.length - 1].resonanceCount = this.waves[i].resonanceCount + 1
+ break
+ }
+ }
+ }
+
+ hits = Matter.Query.ray(body, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth])
+ for (let j = 0, len = Math.min(30, hits.length); j < len; j++) {
+ const who = hits[j].body
+ //make them shake around
+ who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
+ who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity
+ let vertices = who.vertices;
+ const vibe = 25
+ ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
+ for (let j = 1; j < vertices.length; j++) {
+ ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5));
+ }
+ ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
+
+ if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) {
+ if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) {
+ // Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1));
+ who.torque += who.inertia * 0.001 * (Math.random() - 0.5)
+ }
+ }
+
+ this.waves[i].radius += tech.waveBeamSpeed * 2
+ if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) {
+ this.waves.splice(i, 1) //end
+ }
+ }
+ ctx.stroke();
+ }
+
+
+
+ }
+ })
+ Composite.add(engine.world, bullet[me]); //add bullet to world
+ },
laserBot(position = {
x: player.position.x + 50 * (Math.random() - 0.5),
y: player.position.y + 50 * (Math.random() - 0.5)
@@ -5823,15 +6127,7 @@ const b = {
//calculate laser collision
let best;
let range = tech.isPlasmaRange * (120 + 300 * Math.sqrt(Math.random()))
- const path = [{
- x: this.position.x,
- y: this.position.y
- },
- {
- x: this.position.x + range * unit.x,
- y: this.position.y + range * unit.y
- }
- ];
+ const path = [{ x: this.position.x, y: this.position.y }, { x: this.position.x + range * unit.x, y: this.position.y + range * unit.y }];
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
@@ -5890,7 +6186,7 @@ const b = {
y: best.y
};
if (best.who.alive) {
- const dmg = 0.75 * m.dmgScale; //********** SCALE DAMAGE HERE *********************
+ const dmg = 0.9 * m.dmgScale; //********** SCALE DAMAGE HERE *********************
best.who.damage(dmg);
best.who.locatePlayer();
//push mobs away
@@ -6747,7 +7043,6 @@ const b = {
have: false,
wavePacketCycle: 0,
delay: 40,
- propagationRate: 20,
phononWaveCD: 0,
waves: [], //used in longitudinal mode
chooseFireMethod() { //set in simulation.startGame
@@ -6771,7 +7066,7 @@ const b = {
ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000";
ctx.lineWidth = 2 * tech.wavePacketDamage
ctx.beginPath();
- const end = 700 * Math.sqrt(tech.isBulletsLastLonger) * Math.pow(0.93, tech.waveReflections) // / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1060
+ const end = 700 * Math.sqrt(tech.isBulletsLastLonger)
const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.43 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer
for (let i = this.waves.length - 1; i > -1; i--) {
@@ -6815,8 +7110,6 @@ const b = {
this.waves.push({
position: mob[j].position,
radius: 25,
- reflection: 1,
- expanding: true,
resonanceCount: this.waves[i].resonanceCount + 1,
})
}
@@ -6848,16 +7141,10 @@ const b = {
}
}
}
- this.waves[i].radius += 0.9 * tech.waveBeamSpeed * this.waves[i].expanding //expand / move
+ this.waves[i].radius += 0.9 * tech.waveBeamSpeed //expand / move
// if (this.waves[i].radius > end) this.waves.splice(i, 1) //end
if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) { //* Math.pow(0.9, this.waves[i].resonanceCount)
- this.waves[i].expanding = -1
- this.waves[i].reflection--
- if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
- } else if (this.waves[i].radius < 25) {
- this.waves[i].expanding = 1
- this.waves[i].reflection--
- if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
+ this.waves.splice(i, 1) //end
}
}
ctx.stroke();
@@ -6866,13 +7153,8 @@ const b = {
fire360Longitudinal() {
m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down
this.waves.push({
- position: {
- x: m.pos.x,
- y: m.pos.y,
- },
+ position: { x: m.pos.x, y: m.pos.y, },
radius: 25,
- reflection: tech.waveReflections,
- expanding: true,
resonanceCount: 0 //used with tech.isPhononWave
})
},
@@ -6881,8 +7163,7 @@ const b = {
ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000";
ctx.lineWidth = 2 * tech.wavePacketDamage
ctx.beginPath();
- // const end = 1100 * tech.isBulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1767
- const end = 1100 * tech.isBulletsLastLonger * Math.pow(0.93, tech.waveReflections) //should equal about 1767
+ const end = 1100 * tech.isBulletsLastLonger
const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.4 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer
for (let i = this.waves.length - 1; i > -1; i--) {
const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius))
@@ -6897,10 +7178,7 @@ const b = {
if (!who.isShielded) {
who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
who.force.y += 0.01 * (Math.random() - 0.5) * who.mass
- Matter.Body.setVelocity(who, { //friction
- x: who.velocity.x * 0.95,
- y: who.velocity.y * 0.95
- });
+ Matter.Body.setVelocity(who, { x: who.velocity.x * 0.95, y: who.velocity.y * 0.95 });
let vertices = who.vertices;
const vibe = 50 + who.radius * 0.15
ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
@@ -6909,7 +7187,6 @@ const b = {
who.locatePlayer();
who.damage(damage / Math.sqrt(who.radius));
- // if (tech.isPhononWave && (!who.alive || this.waves.length < 30 + 30 * Math.random()) && m.fireCDcycle < m.cycle) { //
if (tech.isPhononWave && this.phononWaveCD < m.cycle) {
this.phononWaveCD = m.cycle + 8 * (1 + this.waves[i].resonanceCount)
const halfArc = 0.27 //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions
@@ -6933,18 +7210,10 @@ const b = {
this.waves.push({
position: who.position,
angle: angle - halfArc, //used in drawing ctx.arc
- unit1: {
- x: Math.cos(angle - halfArc),
- y: Math.sin(angle - halfArc)
- }, //used for collision
- unit2: {
- x: Math.cos(angle + halfArc),
- y: Math.sin(angle + halfArc)
- }, //used for collision
+ unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision
+ unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision
arc: halfArc * 2,
radius: 25,
- reflection: 1,
- expanding: 1,
resonanceCount: this.waves[i].resonanceCount + 1
})
}
@@ -6952,13 +7221,11 @@ const b = {
}
hits = Matter.Query.ray(body, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth])
- // for (let j = 0; j < hits.length; j++) {
for (let j = 0, len = Math.min(30, hits.length); j < len; j++) {
const who = hits[j].body
//make them shake around
who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity
-
let vertices = who.vertices;
const vibe = 25
ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
@@ -6973,17 +7240,10 @@ const b = {
who.torque += who.inertia * 0.001 * (Math.random() - 0.5)
}
}
- // ctx.stroke(); //draw vibes
- this.waves[i].radius += tech.waveBeamSpeed * 1.8 * this.waves[i].expanding //expand / move
+ this.waves[i].radius += tech.waveBeamSpeed * 1.8 //expand / move
if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) {
- this.waves[i].expanding = -1
- this.waves[i].reflection--
- if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
- } else if (this.waves[i].radius < 25) {
- this.waves[i].expanding = 1
- this.waves[i].reflection--
- if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
+ this.waves.splice(i, 1) //end
}
}
ctx.stroke();
@@ -6992,29 +7252,14 @@ const b = {
fireLongitudinal() {
m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down
const halfArc = (m.crouch ? 0.0785 : 0.275) * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions
- // if (tech.isBulletTeleport && Math.random() < 0.04) {
- // const scale = 400 * Math.random()
- // this.waves[i].position = Vector.add(this.waves[i].position, { x: scale * (Math.random() - 0.5), y: scale * (Math.random() - 0.5) })
- // }
const angle = m.angle + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5)
this.waves.push({
- position: {
- x: m.pos.x + 25 * Math.cos(m.angle),
- y: m.pos.y + 25 * Math.sin(m.angle),
- },
+ position: { x: m.pos.x + 25 * Math.cos(m.angle), y: m.pos.y + 25 * Math.sin(m.angle), },
angle: angle - halfArc, //used in drawing ctx.arc
- unit1: {
- x: Math.cos(angle - halfArc),
- y: Math.sin(angle - halfArc)
- }, //used for collision
- unit2: {
- x: Math.cos(angle + halfArc),
- y: Math.sin(angle + halfArc)
- }, //used for collision
+ unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision
+ unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision
arc: halfArc * 2,
radius: 25,
- reflection: tech.waveReflections,
- expanding: 1,
resonanceCount: 0
})
},
diff --git a/js/index.js b/js/index.js
index f8467de..05b0913 100644
--- a/js/index.js
+++ b/js/index.js
@@ -373,6 +373,7 @@ const build = {
if (tech.boomBotCount) botText += `
boom-bots: ${tech.boomBotCount}`
if (tech.laserBotCount) botText += `
laser-bots: ${tech.laserBotCount}`
if (tech.foamBotCount) botText += `
foam-bots: ${tech.foamBotCount}`
+ if (tech.soundBotCount) botText += `
sound-bots: ${tech.soundBotCount}`
if (tech.dynamoBotCount) botText += `
dynamo-bots: ${tech.dynamoBotCount}`
if (tech.plasmaBotCount) botText += `
plasma-bots: ${tech.plasmaBotCount}`
if (tech.missileBotCount) botText += `
missile-bots: ${tech.missileBotCount}`
diff --git a/js/level.js b/js/level.js
index e08f8ae..85cc774 100644
--- a/js/level.js
+++ b/js/level.js
@@ -10,7 +10,7 @@ const level = {
// playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"],
//see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , gauntlet, final) added later
playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"],
- communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever"],
+ communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "dojo"],
trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"],
levels: [],
start() {
@@ -18,7 +18,7 @@ const level = {
// simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
// simulation.isHorizontalFlipped = true
// tech.giveTech("performance")
- // level.difficultyIncrease(8 * 4) //30 is near max on hard //60 is near max on why
+ // level.difficultyIncrease(2 * 4) //30 is near max on hard //60 is near max on why
// spawn.setSpawnList();
// spawn.setSpawnList();
// m.maxHealth = m.health = 100
@@ -31,26 +31,30 @@ const level = {
// m.energy = 0
// simulation.molecularMode = 2
// m.damage(0.1);
- // b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
- // b.giveGuns("shotgun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
+ // b.giveGuns("super balls") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
+ // b.giveGuns("drones") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.guns[3].ammo = 100000000
- // tech.giveTech("iridescence")
- // tech.giveTech("cherenkov radiation")
- // tech.giveTech("irradiated nails")
- // for (let i = 0; i < 6; ++i) tech.giveTech("Lorentz transformation")
- // for (let i = 0; i < 1; ++i) tech.giveTech("rivet gun")
+ // tech.giveTech("von Neumann probe")
+ // tech.giveTech("path integration")
+ // tech.giveTech("cordyceps")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("sympathetic resonance")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("foam-bot")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot upgrade")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot upgrade")
// requestAnimationFrame(() => { for (let i = 0; i < 30; i++) tech.giveTech("laser-bot") });
// for (let i = 0; i < 1; i++) tech.giveTech("laser-bot upgrade")
- // for (let i = 0; i < 1; ++i) tech.giveTech("ternary")
- // for (let i = 0; i < 3; ++i) tech.giveTech("mechatronics")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("uncertainty principle")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("mechanical resonance")
// for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling");
- // level.subway();
+ // level.testing();
// for (let i = 0; i < 2; ++i) spawn.starter(1900, -500, 50)
// spawn.sneaker(1900, -500, 25)
// spawn.sniper(2000, -450)
- // spawn.zombie(1000 + 1000 * Math.random(), -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color)
+ // spawn.zombie(-3000, -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color)
// for (let i = 0; i < 20; ++i) spawn.starter(1000 + 1000 * Math.random(), -500 + 300 * Math.random())
// tech.addJunkTechToPool(2)
// tech.tech[322].frequency = 100
@@ -81,8 +85,6 @@ const level = {
// for (let i = 0; i < 13; i++) level.nextLevel(); //jump to final boss
// lore.unlockTesting();
// tech.giveTech("tinker"); //show junk tech in experiment mode
- // simulation.isCheating = false
-
// m.storeTech()
// powerUps.spawn(m.pos.x, m.pos.y, "entanglement", false);
} else {
@@ -103,7 +105,6 @@ const level = {
simulation.draw.setPaths();
b.respawnBots();
m.resetHistory();
- spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
if (tech.isForeverDrones) {
if (tech.isDroneRadioactive) {
@@ -160,9 +161,9 @@ const level = {
}
if (tech.isHealLowHealth) {
if (tech.isEnergyHealth) {
- var len = 3 * (1 - m.energy / m.maxEnergy) //as a percent
+ var len = 4 * (1 - m.energy / m.maxEnergy) //as a percent
} else {
- var len = 3 * (1 - m.health / m.maxHealth) //as a percent
+ var len = 4 * (1 - m.health / m.maxHealth) //as a percent
}
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);
}
@@ -1279,7 +1280,10 @@ const level = {
y: this.position.y + speed
}
Matter.Body.setPosition(this, position)
- if (isSetPaths) simulation.draw.setPaths()
+ if (isSetPaths) {
+ simulation.draw.setPaths()
+ simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
+ }
}
}
} else {
@@ -1289,7 +1293,10 @@ const level = {
y: this.position.y - speed
}
Matter.Body.setPosition(this, position)
- if (isSetPaths) simulation.draw.setPaths()
+ if (isSetPaths) {
+ simulation.draw.setPaths()
+ simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
+ }
}
}
}
@@ -1991,6 +1998,7 @@ const level = {
// m.addHealth(Infinity)
spawn.starter(1900, -500, 200) //big boy
+ // spawn.starter(1900, -500, 100) //big boy
// for (let i = 0; i < 10; ++i) spawn.launcher(1900, -500)
// spawn.suckerBoss(1900, -500)
// spawn.launcherBoss(3200, -500)
@@ -2584,10 +2592,6 @@ const level = {
// simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
// level.difficultyIncrease(10 * 4);
// m.maxHealth = m.health = 100
-
- level.isProcedural = true //used in generating text for the level builder
- simulation.draw.drawMapPath = simulation.draw.drawMapSight
-
// color.map = "#333" //custom map color
document.body.style.backgroundColor = "#e3e3e3"//"#e3e3e3"//color.map//"#333"//"#000"
level.defaultZoom = 1400
@@ -2654,8 +2658,10 @@ const level = {
gateButton.query();
if (!gateButton.isUp) {
if (stationNumber > 0) {
+ if (!isExitOpen && gatesOpenRight < stationNumber) level.newLevelOrPhase() //run some new level tech effects
gatesOpenRight = stationNumber
} else if (stationNumber < 0) {
+ if (!isExitOpen && gatesOpenLeft > stationNumber) level.newLevelOrPhase() //run some new level tech effects
gatesOpenLeft = stationNumber
} else { //starting station both doors open
gatesOpenLeft = stationNumber
@@ -3384,6 +3390,8 @@ const level = {
Composite.add(engine.world, map[i]); //add to world
}
simulation.draw.setPaths()
+ simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
+
//shift trains left/right, as you move left or right a train will jump over and become the train needed at the next station
let repositionTrain
@@ -3437,6 +3445,9 @@ const level = {
for (let i = 0; i < train.length; i++) train[i].draw()
stationCustomTopLayer()
};
+ level.isProcedural = true //only used in generating text for the level builder
+ simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
+ simulation.draw.drawMapPath = simulation.draw.drawMapSight
},
reservoir() {
level.exit.x = 1700;
@@ -25591,6 +25602,456 @@ const level = {
};
powerUps.addResearchToLevel(); //needs to run after mobs are spawned
},
+ dojo() { // By
+ simulation.makeTextLog(`underpass by weird_pusheen`);
+
+ const vanishes = [];
+ const smoofes = [];
+ const leftRotor = level.rotor(-550, 900, 950, 25);
+ leftRotor.frictionAir = 0.01;
+ var leftSchwoof = level.boost(-20, -60, -2000);
+ var rightSchwoof = level.button(2550, -50);
+ var rightSchwoofState = false;
+ var rightSchwoofLive = true;
+ spawn.mapRect(2513, -39, 200, 100);
+ var pathPoints = [
+ [0, 0], // Index 0 is owned by M and is set to M's position during play
+ // this means that occasionally the boss will bonk M on the way to somewhere else, which gives it a chance to hurt M and gives the player a chance to hurt it
+ [250, -750], /* Left bases */
+ [250, -2500],
+ [350, -1500], // Left doorway
+ [1150, -1500], // Home base
+ [1150, -2750], // Upper base
+ [1950, -1500], // Right doorway
+ [2050, -750], /* Right bases */
+ [2050, -2500],
+ [-150, -250], // Left porthole
+ ];
+ function isntIn(point, array) {
+ for (var x = 0; x < array.length; x++) {
+ if (point[0] == array[x][0] && point[1] == array[x][1]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ function isObstructed(v1, v2) {
+ var ret = Matter.Query.ray(map,
+ {
+ x: v1[0],
+ y: v1[1],
+ },
+ {
+ x: v2[0],
+ y: v2[1]
+ }).length != 0;
+ return ret; // Kinda-ish stolen from mob.js
+ }
+ function pythag(p1, p2) {
+ var dx = p1[0] - p2[0];
+ var dy = p1[1] - p2[1];
+ return Math.sqrt(dx * dx + dy * dy);
+ }
+ var path = undefined; // This is a stupid way to go about pathfinding code. I might even clean it up!
+ function pathFind(goalPoint, startPoint, curPath = []) {
+ var myPoint = startPoint;
+ if (curPath.length) {
+ myPoint = curPath[curPath.length - 1];
+ }
+ if (path && (curPath.length >= path.length)) { // If we've already found a shorter or equal path, no reason to continue and waste CPU time
+ return; // Minimizes for HOP COUNT, not PATH LENGTH - path length was buggy
+ }
+ if (!isObstructed(myPoint, goalPoint)) { // If the line to the goal point ain't blocked by a map object, we've arrived!
+ path = [...curPath];
+ path.push(goalPoint);
+ return;
+ }
+ pathPoints.forEach(testPoint => {
+ if (isntIn(testPoint, curPath)) { // If it's reusing points, there's clearly something wrong
+ if (!isObstructed(myPoint, testPoint)) { // If the line to the test point ain't blocked by a map object
+ var thing = [...curPath];
+ thing.push(testPoint);
+ pathFind(goalPoint, startPoint, thing); // Branch to a valid test point
+ }
+ }
+ });
+ }
+ level.setPosToSpawn(1200, 500);
+ level.exit.x = 51500;
+ level.exit.y = -1875;
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
+ level.defaultZoom = 1500;
+ simulation.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#d8dadf";
+
+ spawn.mapRect(-500, 0, 3300, 300); // Floor
+ spawn.mapRect(-100, -3000, 2500, 100); // Ceiling
+ spawn.mapRect(-200, -3000, 100, 2600); // Left wall
+ spawn.mapRect(2400, -3000, 100, 3000); // Right wall
+
+ spawn.mapRect(500, -1000, 100, 500); /* obstruction blocks */
+ smoofes.push(map[map.length - 1]);
+ spawn.mapRect(500, -2500, 100, 500);
+ smoofes.push(map[map.length - 1]);
+ spawn.mapRect(1700, -1000, 100, 500);
+ smoofes.push(map[map.length - 1]);
+ spawn.mapRect(1700, -2500, 100, 500);
+ smoofes.push(map[map.length - 1]);
+
+ spawn.mapRect(-1000, 550, 200, 50); // Left chonky stepppp low
+ spawn.mapRect(-800, 300, 200, 50); // Left chonky stepppp high
+ spawn.mapVertex(-1000, 1200, "0 0 100 0 700 500 700 700 0 700"); // Left chonky
+ spawn.mapRect(3100, 550, 200, 50); // Right chonky stepppp low
+ spawn.mapRect(2900, 300, 200, 50); // Right chonky stepppp high
+ spawn.mapVertex(3300, 1200, "0 0 -100 0 -700 500 -700 700 0 700"); // Right chonky
+ const leftElevator = level.elevator(-1400 - 300, 1450, 300, 100, 500);
+ const rightElevator = level.elevator(-1400 + 5100, 1450, 300, 100, 500);
+
+ spawn.mapRect(-150, -1700, 200, 50);
+ spawn.mapRect(400, -2050, 200, 50);
+ spawn.mapRect(1600, -1000, 200, 50);
+
+ spawn.randomMob(1200, 700);
+ spawn.randomMob(600, 1000);
+ spawn.randomMob(1800, 1000);
+ spawn.randomMob(3200, 400);
+ spawn.randomMob(3000, 200);
+ spawn.randomMob(-900, 400);
+ spawn.randomMob(-700, 200);
+ spawn.randomMob(1200, 1000);
+ for (var i = 0; i < 4; i++) {
+ spawn.randomSmallMob(Math.random() * 600 - 600, Math.random() * 3000 - 400);
+ }
+ spawn.grenadier(-300, -1000);
+ spawn.grenadier(2600, -1000);
+
+ spawn.mapRect(-1400, 1450, 5100, 100); // The True Floor
+
+ const slime = level.hazard(-1250, 1400, 4800, 50);
+ slime.maxHeight = 600;
+ simulation.draw.body = function () {
+ ctx.beginPath();
+ for (let i = 0, len = body.length; i < len; ++i) {
+ if (!body[i].hidden) {
+ let vertices = body[i].vertices;
+ ctx.moveTo(vertices[0].x, vertices[0].y);
+ for (let j = 1; j < vertices.length; j++) {
+ ctx.lineTo(vertices[j].x, vertices[j].y);
+ }
+ ctx.lineTo(vertices[0].x, vertices[0].y);
+ }
+ }
+ ctx.lineWidth = 2;
+ ctx.fillStyle = color.block;
+ ctx.fill();
+ ctx.strokeStyle = color.blockS;
+ ctx.stroke();
+ } // Override the old draw code to allow intelligent hiding of blocks - preferably this becomes official code because it's just a single added if statement and makes a lot of things cleaner and more intelligent
+
+ const vanish = function (x, y, width, height) { // normal vanishes don't work well on my map for some reason, so I rewrote
+ x += width / 2;
+ y += height / 2;
+ const getVertices = function (bX, bY, bW, bH) { return [{ x: bX, y: bY, index: 0, isInternal: false }, { x: bX + bW, y: bY, index: 1, isInternal: false }, { x: bX + bW, y: bY + bH, index: 4, isInternal: false }, { x: bX, y: bY + bH, index: 3, isInternal: false }] };
+ const cMask = cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
+ const vertices = getVertices(x, y, width, height);
+ const block = body[body.length] = Bodies.fromVertices(x, y, vertices, {
+ collisionFilter: {
+ category: cat.map,
+ mask: cMask
+ },
+ isNoSetCollision: true,
+ inertia: Infinity, //prevents rotation
+ isNotHoldable: true,
+ isNonStick: true, //this keep sporangium from sticking
+ isTouched: false,
+ cWidth: width,
+ hiddenCycle: 0,
+ isStatic: true,
+ query() {
+ if (this.cWidth <= 0) {
+ if (this.cWidth > -100) {
+ this.cWidth = -100;
+ Matter.Body.setVertices(this, vertices);
+ }
+ this.isTouched = false;
+ this.collisionFilter.mask = undefined;
+ this.hidden = true;
+ this.hiddenCycle++;
+ if (this.hiddenCycle > 100) {
+ if (Matter.Query.collides(this, [player]).length) {
+ this.hiddenCycle = 50;
+ }
+ else {
+ this.hiddenCycle = 0;
+ this.cWidth = width;
+ this.collisionFilter.mask = cMask;
+ this.hidden = false;
+ }
+ }
+ }
+ else if (this.isTouched) {
+ Matter.Body.setVertices(this, getVertices(x, y, this.cWidth, height * (this.cWidth / width)));
+ this.cWidth -= 3;
+ }
+ else if (Matter.Query.collides(this, [player]).length) { // Elseif short circuit avoids expensive collision detection
+ this.isTouched = true;
+ }
+ }
+ });
+ return block;
+ };
+
+ vanishes.push(vanish(800, 800, 800, 50));
+ vanishes.push(vanish(400, 1100, 400, 50));
+ vanishes.push(vanish(1600, 1100, 400, 50));
+ spawn.bodyRect(1700, 812, 300, 25, 1, {
+ collisionFilter: {
+ category: cat.body,
+ mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.map
+ },
+ isNoSetCollision: true,
+ isNotHoldable: true,
+ isNonStick: true, //this keep sporangium from sticking
+ restitution: 1,
+ friction: 0,
+ frictionAir: 0,
+ frictionStatic: 0,
+ query() {
+ Matter.Body.setAngularVelocity(this, 0);
+ Matter.Body.applyForce(this, this.position, {
+ x: 0,
+ y: -(this.position.y - 812) * 0.002
+ });
+ }
+ });
+ const zigzag = body[body.length - 1];
+ Matter.Body.applyForce(zigzag, zigzag.position, {
+ x: 0.1,
+ y: 0
+ });
+ var buttonWasDown = false;
+ level.customTopLayer = () => {
+
+ }
+ level.custom = () => {
+ rightSchwoof.isUp = false;
+ level.exit.drawAndCheck();
+ leftSchwoof.query();
+ level.enter.draw();
+ pathPoints[0][0] = m.pos.x;
+ pathPoints[0][1] = m.pos.y;
+ leftElevator.move();
+ rightElevator.move();
+ slime.query();
+ zigzag.query();
+ slime.levelRise(0.2);
+ for (var i = 0; i < vanishes.length; i++) {
+ vanishes[i].query();
+ }
+ if (!rightSchwoofState) {
+ var math = m.pos.y < leftRotor.position.y;
+ Matter.Body.setAngularVelocity(leftRotor, (math ? 1 : -1) * Math.PI / 45);
+ }
+ if (rightSchwoofLive) {
+ rightSchwoof.query();
+ rightSchwoof.draw();
+ if (rightSchwoofState) {
+ ctx.fillStyle = "lightgreen";
+ }
+ else {
+ ctx.fillStyle = "red";
+ }
+ ctx.beginPath();
+ ctx.arc(2615, -220, 40, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ if (rightSchwoof.isUp) {
+ buttonWasDown = true;
+ }
+ else if (buttonWasDown) {
+ buttonWasDown = false;
+ rightSchwoofState = !rightSchwoofState;
+ }
+ if (Matter.Query.collides(player, smoofes).length) {
+ Matter.Body.applyForce(player, player.position, {
+ x: 0,
+ y: -0.015
+ });
+ }
+ };
+
+ mobs.spawn(500, -500, 10, 100, "yellow"); /* TacticalBoss
+ Modes:
+ Spawn:
+ Pathfinds to a point above M and starts dropping mobs. Learns which mobs to drop to cause the most damage, of course.
+ Occasionally strikes at M.
+ Hide:
+ Pathfinds to the point furthest from M
+ Strike:
+ Pathfind really, really fast to M
+ Recharge:
+ Stop moving for a bit to "recharge" (this is so the player has a chance to hit it)
+
+ It must always Hide or Recharge after Spawning or Striking. Which one it does is based on some factor I'll figure out.
+ Pathfinding is a hypersimplified algorithm with hard-coded "points" that it can travel between. M is one of these.
+ */
+ var boss = mob[mob.length - 1];
+ boss.isBoss = true;
+ boss.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
+ boss.onDeath = function () {
+ powerUps.spawnBossPowerUp(this.position.x, this.position.y);
+ level.exit.x = 2560;
+ level.exit.y = -90;
+ rightSchwoofLive = false;
+ };
+ var spawnables = {};
+ ["hopper", "stabber", "springer", "striker", "sneaker", "grower"].forEach((m) => { /* Used to be spawn.fullPickList, but some of those mobs don't do collision-only damage and would thus never be properly selected for */
+ if (spawn[m]) {
+ spawnables[m] = {
+ fun: spawn[m],
+ name: m,
+ weight: 1
+ }
+ }
+ });
+ boss.stabCycle = 0;
+ boss.spawnCycle = 0;
+ function spawny() {
+ var totalWeight = 0;
+ Object.keys(spawnables).forEach(key => {
+ totalWeight += spawnables[key].weight;
+ });
+ var cursorWeight = 0;
+ var choice = Math.random();
+ var mC = undefined;
+ Object.values(spawnables).forEach((thing) => {
+ var lower = cursorWeight / totalWeight;
+ cursorWeight += thing.weight;
+ var upper = cursorWeight / totalWeight;
+ if ((choice > lower && choice <= upper) || !mC) {
+ mC = thing;
+ }
+ });
+ mC.fun(boss.position.x, boss.position.y);
+ var sp = mob[mob.length - 1];
+ sp.typeName = mC.name;
+ sp.onHit = () => {
+ spawnables[sp.typeName].weight += 1;
+ };
+ var oldFun = sp.onDeath;
+ sp.onDeath = () => { /* Mobs that die are worth less */
+ oldFun.call(sp);
+ spawnables[sp.typeName].weight -= 0.3; /* But not too much less */
+ };
+ }
+ boss.spawnDelay = 40;
+ boss.mode = "hide";
+ boss.modeSwitch = -1; // Randomize mode immediately
+ boss.damageReduction = 0.1;
+ var oldOnHit = boss.onHit;
+ boss.onHit = () => {
+ boss.modeSwitch = -1; // After striking the player, always switch modes
+ oldOnHit.call(boss);
+ };
+ boss.do = () => {
+ path = undefined;
+ var pfGoal = [0, 0];
+ boss.modeSwitch--;
+ if (boss.modeSwitch < 0) {
+ if (!boss.isShielded) {
+ spawn.shield(boss, boss.position.x, boss.position.y, 0.75); // Every time the mode switches, have a 75% chance to gain a new shield
+ }
+ if (boss.mode == "hide" || boss.mode == "recharge") {
+ if (Math.random() > 0.5) {
+ boss.mode = "spawn";
+ }
+ else {
+ boss.mode = "strike";
+ }
+ boss.modeSwitch = 600;
+ }
+ else {
+ if (boss.mode == "strike") {
+ boss.mode = "hide"; // Always hides after striking
+ }
+ else {
+ if (Math.random() > 0.5) {
+ boss.mode = "hide";
+ }
+ else {
+ boss.mode = "recharge"; // same when it goes into recharge mode
+ spawn.shield(boss, boss.position.x, boss.position.y, 1);
+ }
+ }
+ boss.modeSwitch = 200;
+ }
+ }
+ if (boss.mode == "hide") { /* Find the furthest point from M and get to it */
+ var longest = 0;
+ pathPoints.forEach(item => {
+ if (item[0] == 1150) {
+ return;
+ }
+ var iL = pythag(item, [m.pos.x, m.pos.y]);
+ if (iL > longest) {
+ longest = iL;
+ pfGoal = item;
+ }
+ });
+ }
+ else if (boss.mode == "strike") {
+ pfGoal = pathPoints[0]; // Target M
+ }
+ else if (boss.mode == "spawn") {
+ pfGoal = pathPoints[4]; // Go to Home Base to spawn
+ }
+ if (boss.mode != "recharge") {
+ if (m.pos.x > 2350 || m.pos.x < -150 || m.pos.y > 50) {
+ boss.mode = "hide";
+ }
+ pathFind(pfGoal, [boss.position.x, boss.position.y]);
+ if (!path) {
+ return; // If it couldn't pathfind, just drift
+ }
+ var goalX = path[0][0];
+ var goalY = path[0][1];
+
+ var dX = goalX - boss.position.x;
+ var dY = goalY - boss.position.y;
+ var hyp = Math.sqrt(dX * dX + dY * dY);
+ Matter.Body.applyForce(boss, {
+ x: goalX,
+ y: goalY
+ }, {
+ x: dX / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1),
+ y: dY / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1)// - 0.005
+ });
+ }
+ if (boss.mode == "spawn") {
+ boss.stabCycle++;
+ if (boss.stabCycle > 25) {
+ if (Math.abs(dX) < 200 && dY > 0) {
+ Matter.Body.applyForce(boss, {
+ x: player.position.x,
+ y: player.position.y
+ }, {
+ x: 0,
+ y: 5
+ });
+ }
+ boss.stabCycle = 0;
+ }
+ boss.spawnCycle++;
+ if (boss.spawnCycle > boss.spawnDelay) {
+ spawny();
+ boss.spawnDelay += 4;
+ boss.spawnCycle = 0;
+ }
+ }
+ };
+ boss.showHealthBar = true;
+ powerUps.addResearchToLevel() //needs to run after mobs are spawned
+ },
// ********************************************************************************************************
// ********************************************************************************************************
// ***************************************** training levels **********************************************
diff --git a/js/mob.js b/js/mob.js
index 090b17a..76ba90a 100644
--- a/js/mob.js
+++ b/js/mob.js
@@ -189,7 +189,6 @@ const mobs = {
effect() {
if ((simulation.cycle - this.startCycle) % 30 === 0) {
let dmg = m.dmgScale * tech.radioactiveDamage * this.dmg
- console.log(dmg)
who.damage(dmg);
if (who.damageReduction) {
simulation.drawList.push({ //add dmg to draw queue
@@ -409,10 +408,7 @@ const mobs = {
isLookingAtPlayer(threshold) {
const diff = Vector.normalise(Vector.sub(player.position, this.position));
//make a vector for the mob's direction of length 1
- const dir = {
- x: Math.cos(this.angle),
- y: Math.sin(this.angle)
- };
+ const dir = { x: Math.cos(this.angle), y: Math.sin(this.angle) };
//the dot product of diff and dir will return how much over lap between the vectors
const dot = Vector.dot(dir, diff);
// console.log(Math.cos(dot)*180/Math.PI)
diff --git a/js/player.js b/js/player.js
index 21540ea..14d49d3 100644
--- a/js/player.js
+++ b/js/player.js
@@ -551,7 +551,7 @@ const m = {
if (m.fieldMode === 0 || m.fieldMode === 3) dmg *= 0.973 ** m.coupling
if (tech.isLowHealthDefense) dmg *= 1 - Math.max(0, 1 - m.health) * 0.8
if (tech.isHarmReduceNoKill && m.lastKillCycle + 300 < m.cycle) dmg *= 0.33
- if (tech.squirrelFx !== 1) dmg *= Math.pow(0.7, (tech.squirrelFx - 1) / 0.4) //cause more damage
+ if (tech.squirrelFx !== 1) dmg *= 0.78//Math.pow(0.78, (tech.squirrelFx - 1) / 0.4)
if (tech.isAddBlockMass && m.isHolding) dmg *= 0.15
if (tech.isSpeedHarm && player.speed > 0.1) dmg *= 1 - Math.min(player.speed * 0.0165, 0.66)
if (tech.isHarmReduce && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.25
@@ -845,6 +845,7 @@ const m = {
draw() { },
isAltSkin: false,
resetSkin() {
+ simulation.isAutoZoom = true;
m.yOffWhen.jump = 70
m.yOffWhen.stand = 49
m.yOffWhen.crouch = 22
@@ -1352,6 +1353,7 @@ const m = {
},
dilate() {
m.isAltSkin = true
+ simulation.isAutoZoom = false;
m.draw = function () {
const amplitude = 8 + 4 * Math.sin(m.cycle * 0.0075)
ctx.fillStyle = m.fillColor;
@@ -1380,6 +1382,13 @@ const m = {
ctx.restore();
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
powerUps.boost.draw()
+
+ //zoom camera in and out
+
+ // console.log(simulation.zoomScale)
+ simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075))
+
+
}
},
dilate2() {
@@ -1415,6 +1424,7 @@ const m = {
ctx.restore();
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
powerUps.boost.draw()
+ simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075))
}
m.drawLeg = function (stroke) {
// if (simulation.mouseInGame.x > m.pos.x) {
@@ -1903,7 +1913,7 @@ const m = {
}
},
setMaxEnergy(isMessage = true) {
- m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2 * tech.isGroundState + 3 * tech.isRelay * tech.isFlipFlopOn * tech.isRelayEnergy + 1.5 * (m.fieldMode === 1) + (m.fieldMode === 0 || m.fieldMode === 1) * 0.05 * m.coupling + 0.4 * tech.isStandingWaveExpand
+ m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2.66 * tech.isGroundState + 3 * tech.isRelay * tech.isFlipFlopOn * tech.isRelayEnergy + 1.5 * (m.fieldMode === 1) + (m.fieldMode === 0 || m.fieldMode === 1) * 0.05 * m.coupling + 0.4 * tech.isStandingWaveExpand
if (isMessage) simulation.makeTextLog(`m.maxEnergy = ${(m.maxEnergy.toFixed(2))}`)
},
fieldMeterColor: "#0cf",
diff --git a/js/powerup.js b/js/powerup.js
index faa2d90..87b8a1e 100644
--- a/js/powerup.js
+++ b/js/powerup.js
@@ -452,7 +452,7 @@ const powerUps = {
b.randomBot()
if (tech.renormalization) {
for (let i = 0; i < cost; i++) {
- if (Math.random() < 0.44) {
+ if (Math.random() < 0.46) {
m.fieldCDcycle = m.cycle + 20;
powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research");
}
@@ -465,7 +465,7 @@ const powerUps = {
if (tech.isDeathAvoid && document.getElementById("tech-anthropic")) {
document.getElementById("tech-anthropic").innerHTML = `-${powerUps.research.count}`
}
- if (tech.renormalization && Math.random() < 0.44 && amount < 0) {
+ if (tech.renormalization && Math.random() < 0.46 && amount < 0) {
for (let i = 0, len = -amount; i < len; i++) powerUps.spawn(m.pos.x, m.pos.y, "research");
}
if (tech.isRerollHaste) {
diff --git a/js/simulation.js b/js/simulation.js
index c694597..a89422e 100644
--- a/js/simulation.js
+++ b/js/simulation.js
@@ -766,6 +766,7 @@ const simulation = {
tech.laserBotCount = 0;
tech.orbitBotCount = 0;
tech.foamBotCount = 0;
+ tech.soundBotCount = 0;
tech.boomBotCount = 0;
tech.plasmaBotCount = 0;
tech.missileBotCount = 0;
@@ -1014,6 +1015,7 @@ const simulation = {
},
clearNow: false,
clearMap() {
+ level.isProcedural = false;
ctx.setTransform(1, 0, 0, 1, 0, 0);
if (m.alive) {
if (tech.isLongitudinal) b.guns[3].waves = []; //empty array of wave bullets
@@ -1103,7 +1105,6 @@ const simulation = {
}
requestAnimationFrame(respawnHeal);
}
-
if (tech.isDronesTravel && m.alive) {
//count drones
let droneCount = 0
@@ -1125,39 +1126,7 @@ const simulation = {
}
//respawn drones in animation frame
- let respawnDrones = () => {
- if (droneCount > 0) {
- requestAnimationFrame(respawnDrones);
- if (!simulation.paused && !simulation.isChoosing && m.alive) {
- const where = {
- x: level.enter.x + 50,
- y: level.enter.y - 60
- }
- droneCount--
- if (tech.isDroneRadioactive) {
- b.droneRadioactive({
- x: where.x + 100 * (Math.random() - 0.5),
- y: where.y + 100 * (Math.random() - 0.5)
- }, 0)
- } else {
- b.drone({
- x: where.x + 100 * (Math.random() - 0.5),
- y: where.y + 120 * (Math.random() - 0.5)
- }, 0)
- if (tech.isDroneGrab && deliveryCount > 0) {
- const who = bullet[bullet.length - 1]
- who.isImproved = true;
- const SCALE = 2.25
- Matter.Body.scale(who, SCALE, SCALE);
- who.lookFrequency = 30 + Math.floor(11 * Math.random());
- who.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
- deliveryCount--
- }
- }
- }
- }
- }
- requestAnimationFrame(respawnDrones);
+ requestAnimationFrame(() => { b.delayDrones({ x: level.enter.x + 50, y: level.enter.y - 60 }, droneCount) });
//respawn spores in animation frame
let respawnSpores = () => {
@@ -1221,11 +1190,17 @@ const simulation = {
}
requestAnimationFrame(respawnFleas);
}
-
if (tech.isQuantumEraser) {
+ let count = 0
for (let i = 0, len = mob.length; i < len; i++) {
- if (mob[i].isDropPowerUp && mob[i].alive) tech.quantumEraserCount++
+ if (mob[i].isDropPowerUp && mob[i].alive) count++
}
+ count *= 0.17 //to fake the chance, this makes it not random, and maybe less confusing
+ let cycle = () => { //run after waiting a cycle for the map to be cleared
+ const types = ["heal", "ammo", "heal", "ammo", "research", "coupling", "boost", "tech", "gun", "field"]
+ for (let i = 0; i < count; i++) powerUps.spawnDelay(types[Math.floor(Math.random() * types.length)], 1)
+ }
+ requestAnimationFrame(cycle);
}
function removeAll(array) {
@@ -1305,7 +1280,7 @@ const simulation = {
},
sight: { //credit to Cornbread for adding this algorithm to n-gon
// square: 0,
- intersectMap: [], //this is precalculated in simulation.draw.setPaths() when the map changes
+ intersectMap: [], //this is precalculated in simulation.draw.lineOfSightPrecalculation()
getIntersection(v1, v1End, domain) {
const intersections = simulation.sight.getIntersections(v1, v1End, domain);
var best = { x: v1End.x, y: v1End.y, dist: (v1End.x - v1.x) ** 2 + (v1End.y - v1.y) ** 2 }
@@ -1596,9 +1571,8 @@ const simulation = {
}
simulation.draw.mapPath.lineTo(vertices[0].x, vertices[0].y);
}
-
-
- //store data for line of sight precalculation
+ },
+ lineOfSightPrecalculation() {
simulation.sight.intersectMap = [];
for (var i = 0; i < map.length; i++) {
const obj = map[i];
diff --git a/js/spawn.js b/js/spawn.js
index 5601b1c..742454d 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -17,7 +17,6 @@ const spawn = {
} else {
spawn[options[Math.floor(Math.random() * options.length)]](x, y)
}
- spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
},
pickList: ["starter", "starter"],
fullPickList: [
@@ -49,93 +48,6 @@ const spawn = {
spawnChance(chance) {
return Math.random() < chance + 0.07 * simulation.difficulty && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1)
},
- quantumEraserCheck() { //remove mobs from tech: quantum eraser
- if (tech.isQuantumEraser && tech.quantumEraserCount > 0) {
-
- //start at a random location in array
- const randomMiddle = Math.floor(mob.length * Math.random())
- let i = randomMiddle
- for (let j = 0; j < mob.length; j++) {
- i++
- if (i > mob.length - 1) i = 0
- if (mob[i].isDropPowerUp && mob[i].alive) { //&& !mob[i].isBoss
- if (mob[i].isFinalBoss) {
- tech.quantumEraserCount = 0;
- return
- } else {
- tech.isQuantumEraserDuplication = true
- mob[i].death()
- tech.isQuantumEraserDuplication = false
- }
- //graphics
- const color = 'rgba(255,255,255, 0.8)'
- simulation.drawList.push({
- x: mob[i].position.x,
- y: mob[i].position.y,
- radius: mob[i].radius * 2,
- color: color, //"rgba(0,0,0,0.6)",
- time: 60
- });
- simulation.drawList.push({
- x: mob[i].position.x,
- y: mob[i].position.y,
- radius: mob[i].radius * 1,
- color: color, //"rgba(0,0,0,0.85)",
- time: 90
- });
- simulation.drawList.push({
- x: mob[i].position.x,
- y: mob[i].position.y,
- radius: mob[i].radius * 0.5,
- color: color, //"rgb(0,0,0)",
- time: 120
- });
- tech.quantumEraserCount--
- simulation.makeTextLog(`tech.quantumEraserCount = ${tech.quantumEraserCount}`)
- if (tech.quantumEraserCount < 1) break
- }
- }
-
- // for (let i = 0, len = mob.length; i < len; i++) {
- // if (mob[i].isDropPowerUp && mob[i].alive) { //&& !mob[i].isBoss
- // if (mob[i].isFinalBoss) {
- // tech.quantumEraserCount = 0;
- // return
- // } else {
- // tech.isQuantumEraserDuplication = true
- // mob[i].death()
- // tech.isQuantumEraserDuplication = false
- // }
- // //graphics
- // const color = 'rgba(255,255,255, 0.8)'
- // simulation.drawList.push({
- // x: mob[i].position.x,
- // y: mob[i].position.y,
- // radius: mob[i].radius * 2,
- // color: color, //"rgba(0,0,0,0.6)",
- // time: 60
- // });
- // simulation.drawList.push({
- // x: mob[i].position.x,
- // y: mob[i].position.y,
- // radius: mob[i].radius * 1,
- // color: color, //"rgba(0,0,0,0.85)",
- // time: 90
- // });
- // simulation.drawList.push({
- // x: mob[i].position.x,
- // y: mob[i].position.y,
- // radius: mob[i].radius * 0.5,
- // color: color, //"rgb(0,0,0)",
- // time: 120
- // });
- // tech.quantumEraserCount--
- // simulation.makeTextLog(`tech.quantumEraserCount = ${tech.quantumEraserCount}`)
- // if (tech.quantumEraserCount < 1) break
- // }
- // }
- }
- },
randomMob(x, y, chance = 1) {
if (spawn.spawnChance(chance) || chance === Infinity) {
const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
@@ -145,7 +57,6 @@ const spawn = {
const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
spawn[pick](x, y);
}
- spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
},
randomSmallMob(x, y,
num = Math.max(Math.min(Math.round(Math.random() * simulation.difficulty * 0.2), 4), 0),
@@ -163,7 +74,6 @@ const spawn = {
spawn[pick](x + Math.round((Math.random() - 0.5) * 20) + i * size * 2.5, y + Math.round((Math.random() - 0.5) * 20), size);
}
}
- spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
},
randomGroup(x, y, chance = 1) {
if (spawn.spawnChance(chance) && simulation.difficulty > 2 || chance === Infinity) {
@@ -198,7 +108,6 @@ const spawn = {
}
}
}
- spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
}
},
secondaryBossChance(x, y) {
@@ -588,7 +497,7 @@ const spawn = {
ctx.beginPath();
if (this.fadeCycle < 120) { //damage scales up over 2 seconds to give player time to move as it fades in
const scale = this.fadeCycle / 120
- const dmg = this.fadeCycle < 60 ? 0 : 0.13 * simulation.dmgScale * scale
+ const dmg = this.fadeCycle < 60 ? 0 : 0.1 * simulation.dmgScale * scale
me.lasers(me.vertices[0], me.angle + Math.PI / 6, dmg);
me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6, dmg);
me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6, dmg);
@@ -1548,7 +1457,7 @@ const spawn = {
me.isDropPowerUp = false;
me.showHealthBar = false;
me.stroke = "#83a"
- me.accelMag = 0.001
+ me.accelMag = 0.003
me.frictionAir = 0.005
me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob
me.seeAtDistance2 = 1000000 //1000 vision range
@@ -1583,6 +1492,7 @@ const spawn = {
if (
!mob[i].isZombie &&
!mob[i].isUnblockable &&
+ !mob[i].isMobBullet &&
Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
Matter.Query.ray(body, this.position, mob[i].position).length === 0
// !mob[i].isBadTarget &&
@@ -1605,7 +1515,7 @@ const spawn = {
}
}
me.zombieHealthBar = function () {
- this.health -= 0.0004 //decay
+ this.health -= 0.0003 //decay
if ((this.health < 0.01 || isNaN(this.health)) && this.alive) this.death();
const h = this.radius * 0.3;
const w = this.radius * 2;
@@ -1623,15 +1533,16 @@ const spawn = {
this.force = Vector.mult(Vector.normalise(Vector.sub(this.target.position, this.position)), this.accelMag * this.mass)
} else { //wonder around
this.torque += 0.0000003 * this.inertia;
- const mag = 0.00015 * this.mass
+ const mag = 0.0003 * this.mass
this.force.x += mag * Math.cos(this.angle)
this.force.y += mag * Math.sin(this.angle)
}
- if (this.speed > 6) { // speed cap instead of friction to give more agility
- Matter.Body.setVelocity(this, {
- x: this.velocity.x * 0.93,
- y: this.velocity.y * 0.93
- });
+ if (this.speed > 15) { // speed cap instead of friction to give more agility
+ Matter.Body.setVelocity(this, { x: this.velocity.x * 0.96, y: this.velocity.y * 0.96 });
+ } else if (this.speed < 10) {
+ Matter.Body.setVelocity(this, { x: this.velocity.x * 0.98, y: this.velocity.y * 0.98 });
+ } else if (this.speed < 8) {
+ Matter.Body.setVelocity(this, { x: this.velocity.x * 0.99, y: this.velocity.y * 0.99 });
}
const hit = (who) => {
if (!who.isZombie && who.damageReduction) {
@@ -1642,9 +1553,14 @@ const spawn = {
this.force.y -= force.y;
this.target = null //look for a new target
- const dmg = 1.3 * m.dmgScale
- who.damage(dmg);
+ // who.damage(dmg);
+ //by pass normal damage method
+ const dmg = 1.5 * who.damageReduction / Math.sqrt(who.mass)
+ who.health -= dmg
+ who.onDamage(dmg); //custom damage effects
who.locatePlayer();
+ if ((who.health < 0.01 || isNaN(who.health)) && who.alive) who.death();
+
simulation.drawList.push({
x: this.position.x,
y: this.position.y,
diff --git a/js/tech.js b/js/tech.js
index 141d7e4..a148f69 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -252,7 +252,7 @@ const tech = {
if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.007
if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2
if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.66, player.speed * 0.0165)
- if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.6
+ if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.83
if (tech.isAxion && tech.isHarmMACHO) dmg *= 2 - m.defense()
if (tech.isHarmDamage && m.lastHarmCycle + 480 > m.cycle) dmg *= 3;
if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit * (2 - m.defense()) // if (!simulation.paused) m.lastHit = 0
@@ -260,7 +260,7 @@ const tech = {
return dmg
},
duplicationChance() {
- return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.15 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0) + tech.isQuantumEraserDuplication * (1 - 0.016 * (simulation.difficultyMode ** 2))))
+ return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.17 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0)))
},
isScaleMobsWithDuplication: false,
maxDuplicationEvent() {
@@ -337,8 +337,8 @@ const tech = {
},
{
name: "nitinol",
- description: "+33% movement and jumping
+30% defense",
- maxCount: 3,
+ description: "+33% movement and jumping
+22% defense",
+ maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
@@ -454,8 +454,8 @@ const tech = {
},
maxCount: 1,
count: 0,
- frequency: 3,
- frequencyDefault: 3,
+ frequency: 4,
+ frequencyDefault: 4,
allowed() {
return tech.isEnergyHealth
},
@@ -854,7 +854,7 @@ const tech = {
},
{
name: "non-renewables",
- description: `+67% damage
${powerUps.orb.ammo()} can't spawn`,
+ description: `+78% damage
${powerUps.orb.ammo()} can't spawn`,
maxCount: 1,
count: 0,
frequency: 1,
@@ -863,7 +863,7 @@ const tech = {
return !tech.isAmmoFromHealth && !tech.isBoostReplaceAmmo
},
requires: "not catabolism, quasiparticles",
- damage: 1.67,
+ damage: 1.78,
effect() {
tech.damage *= this.damage
tech.isEnergyNoAmmo = true;
@@ -1139,7 +1139,7 @@ const tech = {
},
{
name: "heuristics",
- description: "+25% fire rate
spawn a gun",
+ description: "+22% fire rate
spawn a gun",
maxCount: 9,
count: 0,
frequency: 1,
@@ -1149,7 +1149,7 @@ const tech = {
},
requires: "",
effect() {
- tech.fireRate *= 0.75
+ tech.fireRate *= 0.78
b.setFireCD();
powerUps.spawn(m.pos.x, m.pos.y, "gun");
},
@@ -1161,7 +1161,7 @@ const tech = {
{
name: "anti-shear topology",
link: `anti-shear topology`,
- description: "+30% projectile duration", //
drone spore worm flea missile foam wave neutron ice",
+ description: "your bullets last +30% longer", //
drone spore worm flea missile foam wave neutron ice",
maxCount: 3,
count: 0,
frequency: 1,
@@ -1195,7 +1195,7 @@ const tech = {
},
{
name: "shear stress",
- description: "after mobs die
they release a nail that targets nearby mobs",
+ description: "after mobs die
they fire a nail at nearby mobs",
maxCount: 9,
count: 0,
frequency: 1,
@@ -1493,6 +1493,62 @@ const tech = {
tech.isFoamBotUpgrade = false
}
},
+ {
+ name: "sound-bot",
+ link: `sound-bot`,
+ description: "a bot emits expanding arcs
aimed towards nearby mobs",
+ maxCount: 9,
+ count: 0,
+ frequency: 1,
+ frequencyDefault: 1,
+ isBot: true,
+ isBotTech: true,
+ allowed() { return true },
+ requires: "",
+ effect() {
+ tech.soundBotCount++;
+ b.soundBot();
+ },
+ remove() {
+ if (this.count) {
+ tech.soundBotCount -= this.count;
+ b.clearPermanentBots();
+ b.respawnBots();
+ }
+ }
+ },
+ {
+ name: "sound-bot upgrade",
+ link: `sound-bot upgrade`,
+ description: "convert your bots to sound-bots
+200% wave fire rate and +100% damage",
+ maxCount: 1,
+ count: 0,
+ frequency: 3,
+ frequencyDefault: 3,
+ isBotTech: true,
+ allowed() {
+ return tech.soundBotCount > 1 && !b.hasBotUpgrade()
+ },
+ requires: "2 or more sound bots and no other bot upgrade",
+ effect() {
+ tech.isSoundBotUpgrade = true
+ b.convertBotsTo("sound-bot")
+ for (let i = 0; i < bullet.length; i++) {
+ if (bullet[i].botType === 'sound') bullet[i].isUpgraded = true
+ }
+ tech.setBotTechFrequency()
+ tech.setTechFrequency("sound-bot", 5)
+ },
+ remove() {
+ if (this.count) {
+ for (let i = 0; i < bullet.length; i++) {
+ if (bullet[i].botType === 'sound') bullet[i].isUpgraded = false
+ }
+ tech.setBotTechFrequency(1)
+ }
+ tech.isSoundBotUpgrade = false
+ }
+ },
{
name: "boom-bot",
link: `boom-bot`,
@@ -1679,7 +1735,7 @@ const tech = {
{
name: "dynamo-bot",
link: `dynamo-bot`,
- description: "a bot damages mobs while it traces your path
when it's near generate +7 energy per second",
+ description: "a bot damages mobs while it traces your path
when it's near generate +8 energy per second",
maxCount: 9,
count: 0,
frequency: 1,
@@ -1705,7 +1761,7 @@ const tech = {
{
name: "dynamo-bot upgrade",
link: `dynamo-bot upgrade`,
- description: "convert your bots to dynamo-bots
when it's near generate +23 energy per second",
+ description: "convert your bots to dynamo-bots
when it's near generate +24 energy per second",
maxCount: 1,
count: 0,
frequency: 3,
@@ -1831,6 +1887,8 @@ const tech = {
tech.orbitBotCount *= 2
for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot();
tech.dynamoBotCount *= 2
+ for (let i = 0; i < tech.soundBotCount; i++) b.soundBot();
+ tech.soundBotCount *= 2
for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot();
tech.plasmaBotCount *= 2
for (let i = 0; i < tech.missileBotCount; i++) b.missileBot();
@@ -1846,6 +1904,7 @@ const tech = {
tech.nailBotCount = Math.round(tech.nailBotCount / 2)
tech.laserBotCount = Math.round(tech.laserBotCount / 2)
tech.foamBotCount = Math.round(tech.foamBotCount / 2)
+ tech.soundBotCount = Math.round(tech.soundBotCount / 2)
tech.boomBotCount = Math.round(tech.boomBotCount / 2)
tech.orbitBotCount = Math.round(tech.orbitBotCount / 2)
tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2)
@@ -2503,7 +2562,9 @@ const tech = {
},
{
name: "refrigerant",
- description: `after losing at least 5% health
freeze all mobs for 7 seconds`,
+ descriptionFunction() {
+ return `after losing at least 5% ${tech.isEnergyHealth ? "energy" : "health"}
freeze all mobs for 7 seconds`
+ },
maxCount: 1,
count: 0,
frequency: 1,
@@ -2559,7 +2620,7 @@ const tech = {
},
{
name: "ground state",
- description: "+200 maximum energy
–33% passive energy generation",
+ description: "+266 maximum energy
–33% passive energy generation",
// description: "reduce defense by 66%
you no longer passively regenerate energy",
maxCount: 1,
count: 0,
@@ -2731,7 +2792,7 @@ const tech = {
},
{
name: "parasitism",
- description: "if a mob has died in the last 5 seconds
+60% damage, inhibit energy generation",
+ description: "if a mob has died in the last 5 seconds
+83% damage, inhibit energy generation",
maxCount: 1,
count: 0,
frequency: 1,
@@ -2952,7 +3013,7 @@ const tech = {
},
{
name: "induction brake",
- description: `after using ${powerUps.orb.heal()} slow nearby mobs for 15 seconds
spawn ${powerUps.orb.heal(3)}`,
+ description: `after using ${powerUps.orb.heal()} slow nearby mobs for 15 seconds
spawn ${powerUps.orb.heal(4)}`,
maxCount: 1,
count: 0,
frequency: 1,
@@ -2963,7 +3024,7 @@ const tech = {
requires: "not eddy current brake",
effect() {
tech.isHealBrake = true;
- for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal");
+ for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal");
},
remove() {
tech.isHealBrake = false;
@@ -2972,7 +3033,7 @@ const tech = {
{
name: "adiabatic healing",
descriptionFunction() {
- return `${powerUps.orb.heal()} have +100% effect
+5% JUNK to tech pool`
+ return `${powerUps.orb.heal()} have +100% effect
+4% JUNK to tech pool`
},
maxCount: 3,
count: 0,
@@ -2993,7 +3054,7 @@ const tech = {
Matter.Body.scale(powerUp[i], scale, scale); //grow
}
}
- this.refundAmount += tech.addJunkTechToPool(0.05)
+ this.refundAmount += tech.addJunkTechToPool(0.04)
},
refundAmount: 0,
remove() {
@@ -3055,11 +3116,10 @@ const tech = {
powerUps.setPowerUpMode();
},
},
-
{
- name: "negative entropy",
+ name: "self-assembly",
descriptionFunction() {
- return `at the start of each level
for every 33% missing ${tech.isEnergyHealth ? "energy" : "health"} spawn ${powerUps.orb.heal()}`
+ return `at the start of each level
for every 25% missing ${tech.isEnergyHealth ? "energy" : "health"} spawn ${powerUps.orb.heal()}`
},
maxCount: 1,
count: 0,
@@ -3303,7 +3363,7 @@ const tech = {
},
{
name: "renormalization",
- description: `44% chance to spawn ${powerUps.orb.research(1)}
after consuming ${powerUps.orb.research(1)}`,
+ description: `46% chance to spawn ${powerUps.orb.research(1)} after consuming ${powerUps.orb.research(1)}
+3% JUNK to tech pool`,
maxCount: 1,
count: 0,
frequency: 2,
@@ -3314,9 +3374,16 @@ const tech = {
requires: "at least 4 research, not superdeterminism",
effect() {
tech.renormalization = true;
+ this.refundAmount += tech.addJunkTechToPool(0.03)
+
},
+ refundAmount: 0,
remove() {
tech.renormalization = false;
+ if (this.count > 0 && this.refundAmount > 0) {
+ tech.removeJunkTechFromPool(this.refundAmount)
+ this.refundAmount = 0
+ }
}
},
{
@@ -3601,7 +3668,7 @@ const tech = {
},
{
name: "dark patterns",
- description: "+17% damage
+17% JUNK to tech pool",
+ description: "+22% damage
+22% JUNK to tech pool",
maxCount: 9,
count: 0,
frequency: 1,
@@ -3610,10 +3677,10 @@ const tech = {
return true
},
requires: "",
- damage: 1.17,
+ damage: 1.22,
effect() {
tech.damage *= this.damage
- this.refundAmount += tech.addJunkTechToPool(0.17)
+ this.refundAmount += tech.addJunkTechToPool(0.22)
},
refundAmount: 0,
remove() {
@@ -3665,7 +3732,7 @@ const tech = {
},
{
name: "eternalism",
- description: "+30% damage
time can't be paused (time can be dilated)",
+ description: "+24% damage
time can't be paused (time can be dilated)",
maxCount: 1,
count: 0,
frequency: 1,
@@ -3674,7 +3741,7 @@ const tech = {
return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause
},
requires: "not unified field theory, paradigm shift, invariant",
- damage: 1.3,
+ damage: 1.24,
effect() {
tech.damage *= this.damage
tech.isNoDraftPause = true
@@ -3969,7 +4036,7 @@ const tech = {
},
{
name: "stimulated emission",
- description: "+15% chance to duplicate spawned power ups,
but after a collision eject 1 tech",
+ description: "+17% chance to duplicate spawned power ups,
but after a collision eject 1 tech",
maxCount: 1,
count: 0,
frequency: 1,
@@ -4938,9 +5005,9 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary
+ return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary && !tech.isBulletTeleport
},
- requires: "super balls not incendiary ammunition",
+ requires: "super balls not incendiary ammunition, uncertainty principle",
effect() {
tech.isSuperHarm = true
},
@@ -5068,7 +5135,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.haveGunCheck("wave")
+ return tech.haveGunCheck("wave") || tech.isSoundBotUpgrade
},
requires: "wave",
effect() {
@@ -5089,7 +5156,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.haveGunCheck("wave")
+ return tech.haveGunCheck("wave") || tech.isSoundBotUpgrade
},
requires: "wave",
effect() {
@@ -5110,9 +5177,9 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.haveGunCheck("wave")
+ return tech.haveGunCheck("wave") && !tech.isLongitudinal
},
- requires: "wave",
+ requires: "wave, not phonon",
effect() {
tech.waveReflections += 2
},
@@ -5155,9 +5222,9 @@ const tech = {
frequency: 3,
frequencyDefault: 3,
allowed() {
- return tech.haveGunCheck("wave") && !tech.isPhaseVelocity
+ return tech.haveGunCheck("wave") && !tech.isPhaseVelocity && tech.waveReflections === 1
},
- requires: "wave, not phase velocity",
+ requires: "wave, not phase velocity, bound state",
ammoScale: 6,
effect() {
tech.isLongitudinal = true;
@@ -5215,7 +5282,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.isLongitudinal && tech.haveGunCheck("wave")
+ return (tech.isLongitudinal && tech.haveGunCheck("wave")) || tech.isSoundBotUpgrade
},
requires: "wave, phonon",
effect() {
@@ -5234,7 +5301,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return tech.isLongitudinal && tech.haveGunCheck("wave")
+ return (tech.isLongitudinal && tech.haveGunCheck("wave")) || tech.isSoundBotUpgrade
},
requires: "wave, phonon",
effect() {
@@ -6231,9 +6298,8 @@ const tech = {
}
},
{
- name: "drone repair",
- link: `drone repair`,
- description: "after a drone expires it redeploys
for a 20% chance to use 1 drone ammo",
+ name: "von Neumann probe", //"drone repair",
+ description: "after a drone expires
it will harvest a nearby block to replicate itself",
// description: "broken drones repair if the drone gun is active
repairing has a 25% chance to use 1 drone",
isGunTech: true,
maxCount: 1,
@@ -6467,16 +6533,16 @@ const tech = {
},
{
name: "uncertainty principle",
- description: "foam and wave positions are erratic
+53% foam and wave damage",
+ description: "foam, wave, and super ball positions are erratic
+53% foam, wave, and super ball damage",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
- return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine)) || (tech.haveGunCheck("wave") && !tech.is360Longitudinal)
+ return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine)) || (tech.haveGunCheck("wave") && !tech.is360Longitudinal) || (tech.haveGunCheck("super balls") && !tech.isSuperHarm) || tech.isSoundBotUpgrade
},
- requires: "foam, wave, not isotropic, electrostatic induction",
+ requires: "foam, wave, super balls, not isotropic, electrostatic induction, Zectron",
effect() {
tech.isBulletTeleport = true
},
@@ -7715,6 +7781,14 @@ const tech = {
}
simulation.makeTextLog(`tech.isFoamBotUpgrade = true`)
})
+ notUpgradedBots.push(() => {
+ tech.giveTech("sound-bot upgrade")
+ for (let i = 0; i < num; i++) {
+ b.soundBot()
+ tech.soundBotCount++;
+ }
+ simulation.makeTextLog(`tech.isSoundBotUpgrade = true`)
+ })
notUpgradedBots.push(() => {
tech.giveTech("boom-bot upgrade")
for (let i = 0; i < num; i++) {
@@ -8192,9 +8266,9 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return (m.fieldMode === 6 || m.fieldMode === 7) && !tech.isQuantumEraser
+ return (m.fieldMode === 6 || m.fieldMode === 7)
},
- requires: "cloaking, time dilation, not quantum eraser",
+ requires: "cloaking, time dilation",
effect() {
tech.cloakDuplication = 0.45
powerUps.setPowerUpMode(); //needed after adjusting duplication chance
@@ -8206,9 +8280,9 @@ const tech = {
}
},
{
- name: "quantum eraser",
+ name: "metamaterial absorber", //quantum eraser
descriptionFunction() {
- return `for each mob left alive after you exit a level
kill a mob as they spawn at +${(100 - 1.1 * simulation.difficultyMode ** 2).toFixed(0)}% duplication`
+ return `for each mob left alive after you exit a level
there is a 17% chance to spawn a random power up`
},
// descriptionFunction() {
// return `for each mob left alive after you exit a level
`
@@ -8223,13 +8297,9 @@ const tech = {
},
requires: "cloaking",
effect() {
- tech.quantumEraserCount = 0
- tech.isQuantumEraserDuplication = 0
tech.isQuantumEraser = true
},
remove() {
- tech.quantumEraserCount = 0
- tech.isQuantumEraserDuplication = 0
tech.isQuantumEraser = false
}
},
@@ -10672,6 +10742,7 @@ const tech = {
tech.laserBotCount = 0;
tech.orbitBotCount = 0;
tech.foamBotCount = 0;
+ tech.soundBotCount = 0;
tech.boomBotCount = 0;
tech.plasmaBotCount = 0;
tech.missileBotCount = 0;
@@ -10688,6 +10759,10 @@ const tech = {
b.foamBot();
tech.foamBotCount++;
},
+ () => {
+ b.soundBot();
+ tech.soundBotCount++;
+ },
() => {
b.boomBot();
tech.boomBotCount++;
@@ -11288,6 +11363,7 @@ const tech = {
dynamoBotCount: null,
nailBotCount: null,
foamBotCount: null,
+ soundBotCount: null,
boomBotCount: null,
plasmaBotCount: null,
missileBotCount: null,
@@ -11358,6 +11434,7 @@ const tech = {
isRerollBots: null,
isNailBotUpgrade: null,
isFoamBotUpgrade: null,
+ isSoundBotUpgrade: null,
isLaserBotUpgrade: null,
isBoomBotUpgrade: null,
isOrbitBotUpgrade: null,
@@ -11568,11 +11645,8 @@ const tech = {
isIceKill: null,
isCritKill: null,
isQuantumEraser: null,
- isQuantumEraserDuplication: null,
- quantumEraserCount: null,
isPhononBlock: null,
isPhononWave: null,
- // isMicroTransactions: null,
isLaserLens: null,
laserCrit: null,
isSporeColony: null,
diff --git a/todo.txt b/todo.txt
index 0c693f0..67fd3ac 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,33 +1,77 @@
******************************************************** NEXT PATCH **************************************************
-subway
- start in an empty station
- station exits are blocked by a gate that opens when you press a button
- level exit spawns after you clear 4 stations
- added 2 new stations (7 possible stations)
+new community level: dojo by weird_pusheen
-laser-bots lasers wiggle as it aims at the target
- this is a nerf to iridescence and it looks cool
+sound-bot: makes phonon waves
+
+ updated tech
+drone repair -> von Neumann probe - if a drones ends near a block it will use that block to repair
+quantum eraser -> metamaterial absorber: for each mob left alive after you exit a level there is a 17% chance to spawn a power up
+uncertainty principle: extended to super balls
+aperture: camera zooms in and out along with the skin animation
+
+ balanced tech:
+cordyceps: zombies are much smarter, faster, do more damage, and last longer
+negative entropy -> self-assembly: 1 heal per 33->25% missing health on each new level
+renormalization: 44 -> 46% and +3% JUNK tech to pool
+parasitism: 60 -> 83% damage
+non-renewables: 67 -> 78% damage
+ground state: 200 -> 266 max energy
+dark patterns: 17 -> 22% damage and JUNK
+eternalism: 30 -> 24% damage
+stimulated emission 15 -> 17% duplication
+nitinol 30 -> 22% defense
bug fixes
*********************************************************** TODO *****************************************************
-only calculate setPaths on subway level?
- does this mess with flatland, no
+make a mob similar to slasher
+ because it's just a very well made mob.
+
+sound-bot: makes phonon waves
+ works with
+ other bot tech - done?
+ phonon - no
+ frequency - no
+ bound state - no
+ phase velocity - no
+ isotropic - no
+ maybe, but reduce fire rate?
+
+ uncertainty principle - done
+ propagation - done
+ amplitude - done
+ sympathetic resonance - done
+ mechanical resonance - done
+
+remove reflection from phonon?
+ but return it with isotropic?
+
+super-bot: fires super balls
+
+tech - only allow 1,2 turrets at time. spawning a new mine removes the oldest mine
+ turrets never run out of ammo
+ or turrets automatically use one of your mine ammos when they run out?
+ good with multi gun builds
+ conflict with booby trap?
+
+tech: after a needle hits a mobs
+ the needle splits into 3 needles?
+ reset your fire CD?
+ 2x damage for each consecutive mob hit?
+
+tech: mass production - add a few selection options to all tech, gun, fields to do something:
+ this would work similar to the tech that adds a bot themed tech to every tech choice
+ options
+ spawn: ammo, heals, research, coupling?
+ random amounts?
+ make each option a full tech with images?
+
+
improve flatland performance?
-get rid of the word permanent in bot tech
- ersatz bots, perimeter defense, network effect
-
-maybe reduce the fps on the line of sight graphics to make it look more like a sensor?
-make a bot the follows the player the pov for line of sight levels, not the player
- also need to make the vision a slice of a circle, not a full circle
-
-aperture also increases and decreases vision range
- I think messing with vision range causes problems with the start of level vision code
-
mob non-combat behaviors, like Rain World
gathering
blocks
@@ -47,31 +91,6 @@ consider increasing the base player horizontal movement
maybe only increase ground movement, air control seems fine
would this unbalance any maps?
-level: subway - a map that uses the train level element and line of sight graphics
- stations
- station theme ideas:
- portals
- teleport to far away rooms
- map elements that alternate between positions
- buttons and doors
- maybe add mob mobs to each station? this makes it faster to clear the level
- boss
- spawn at the exit station? or at a random station? or at the station before the exit?
- spawn on the station after enough mobs have been killed?
- type of boss?
- might need to make a new boss designed for this map: los and stations
- small, quick, sneaky
- do random bosses work?
- I think
- looks good with line of sight
- background lighting for each room drawn in level.custom
- no outdoors, no fall off the edge
- slime
- no small bumps
- starting in a small room with a hole to the right with a short drop, like highrise or aerie
- floating hexagons, like in reservoir, labs
- ramping walls to jump over, like satellite
-
tech stubs should be a tech unlocked by skins
nitinol, tungsten?
maybe give another benefit?
@@ -82,18 +101,7 @@ make a lemming that walks until it hits a wall and then turns around robotically
body or mob?
can't be killed?
-
-Also another thing I made that could fit in-game: https://kgurchiek.github.io/universal-n-gon-loader/
-by default it just plays a random version of n-gon downloaded from past github commits
-maybe the "snapshots" could work like this rather than downloading 8 versions of the game?
-also you can play any version with https://kgurchiek.github.io/universal-n-gon-loader/?commitIndex=NUM
-where setting "NUM" to 0 is the very first commit
-here's the code if you want to check it out: https://github.com/kgurchiek/universal-n-gon-loader/blob/main/script.js
-
-missile bot and plasma bot don't get converted by bot upgrade tech?
- is this more confusing because it contradicts text?
-
-use ephemera to replace things
+use ephemera to replace some bad code
JUNK?
request animation stuff
simulation checks
@@ -111,8 +119,6 @@ mobs attack mines
mines periodically set all mobs to have player location to be the mine
is this going to work with all mob vision types?
-rework quantum eraser
-
tech circular polarization - wave gun bullets move in a circle
tech: choose next map by name after exiting current map
@@ -124,8 +130,6 @@ Tech: relativity
Simulation speed scales with movement speed. When still, time moves at 0.4 speed, at full walking speed it’s 1. (So if you’re falling or something and you move faster the simulation will be faster than usual)
Also a damage and/or defense boost to make it worth using
-Tech: Turbine - Energy generation is proportional to your speed up to +X% energy generation at 40 speed
-
wormhole tech - teleport away mobs with mass below 3 when they get too near the player
short CD, small energy cost, only mobs below a mass
@@ -133,75 +137,12 @@ extend brainstorming animation timers to fps cap?
will it be smoother or choppier?
anything else needs to hit limited fps on a high fps monitor?
-level element - mover, transport
- test effect of changing m.Vx on things like: shooting bullets?
- extend the recentered friction zero to other things
- like blocks the player stands on?
- maybe zero on the horizontal velocity of whatever the player is standing on
-
-extend uncertainty to superballs
- maybe make aiming them more random?
-
perfect diamagnatism could bounce on mobs, or even map elements?
could work like a rocket jump?
-Tech: Von Neuman probes - Drones will consume blocks to replicate themselves
- it's a little too similar to the drone repair tech, but I kinda like it better. drones that eat blocks and spit out more drones is cool
-
-tech: parry - immune to harm for 0.25-0.5 seconds after pressing field button
- needs a 5 second CD?
-
-tech: if a needle hits 2 mobs reset your fire CD
- maybe to 2x damage for each consecutive mob hit?
- maybe after a needle hits a mob the needle splits into 3 needles
-
-tech for lens - you can only fire through the lens and some buff? damage or energy?
- this was in todo.txt on GitHub. I think it should be 'laser never drains energy, but you can only fire through lens and +90° lens arc, +100% damage (also you can not gain compound lens with this upgrade)
-
-new boss level like reactor with a very very big boss
- mechanics around a very big boss?
- maybe the boss moves into rooms so you have to do platforming to clear the room before the boss enters the room
- boss can destroy blocks and smaller map elements
-
-tech - after standing wave runs out of energy from blocking, gain a buff
- buff: defense, damage?
- aoe damage like railgun
- push mobs away
-
-level: lock
- should there be something in the top part of the map?
- add alt versions of left and right sides
- make flipped L/R version (after everything else is done)
-
-tech: add an selection option to all tech, gun, fields to do something
- set all mobs to 30% health, and stun all mobs
- 50% chance to convert all power ups into research
- heal to full
-
-tech: if you die inside MACHO, heal to full and delete the MACHO for the rest of the Level
- MACHO gives less defense
-
-tech: after bosses die
they spawn a research
-
-tech: +8% damage each time you kill a boss
-
-tech: sticky grenades
- needs another effect to be good enough
- stick to mobs?
-
-make a mob similar to slasher
- because it's just a very well made mob.
-
-tech - only allow 1,2 turrets at time?
- turrets never run out of ammo
- or turrets automatically use one of your mine ammos when they run out?
- good with multi gun builds
- conflict with booby trap?
tech: Bose Einstein condensate - freezes enemies in pilot wave, and drains some energy?
-super-bot
-
make super balls with Zectron deflectable with field
but is there a simple way to do this?
@@ -234,28 +175,13 @@ tech: sporangium that grow little trees
the trees have an area of effect damage for about 6-10 seconds
maybe something similar to radioactive drones, but maybe a few smaller shapes
-new bot type that makes phonon waves
- name: phono-bot?
- each bot has to generate it themselves, can't run code in gun.do
- synergy with 2 resonance tech
- not isotropic? I think no
- synergy with bound? phase velocity, amplitude, propagation
-
harpoon tech that makes auto aim work much better
-tech - super balls gain 20 seconds of time and are reset to original launch speed after hitting a mob
-
-railgun
- magnetic pinch: harpoon does damage to nearby mobs
- draw charge graphic on harpoon
- use same code as the damage when fire effect
-
hookBoss fires a hook that pulls player towards it
hook does a bit of damage
player targeted unless cloaking
also add effect to finalBoss
-
finalBoss
add synergies between modes:
new modes:
@@ -1219,7 +1145,7 @@ possible names for tech
hypergraph
SQUID (for superconducting quantum interference device) is a very sensitive magnetometer used to measure extremely subtle magnetic fields, based on superconducting loops containing Josephson junctions.
nuclear pasta - hard matter in neutron star
- nonlocal
+ nonlocal: maybe use for pilot wave
fine-tuned universe
nonperturbative
D-branes
@@ -1241,6 +1167,9 @@ possible names for tech
https://en.wikipedia.org/wiki/Cosmic_censorship_hypothesis - black holes can't leak
Alcubierre warp drive (FTL with negative mass)
Spherules - A spherule is a small sphere or spherical body. It can also refer to a thick-walled spherical structure that contains endospores and occurs in the parasitic form of fungi
+ negative entropy
+ memetics
+ magnetorquers - produce spin by pushing on earth's magnetic field
******************************************************** CARS IMAGES ********************************************************
@@ -1264,8 +1193,7 @@ if pause is pressed while selecting power ups, display pause menu on top of sele
laser
supercritical fission
***past style themes***
- field emitter - bipedal white robot spherical gun turret on bird legs
- damaged dirty white robot spherical gun turret on bird legs in the style of Solarpunk
+ base prompt for player on 5.2: clean white robot spherical turret on bird legs test chamber
standing wave - a 3-D cyan transparent nested concentric aligned centered sphere with rings
by Philippe Starck
perfect diamagnetism - physics magnetic field chalk diagram
@@ -1276,13 +1204,13 @@ if pause is pressed while selecting power ups, display pause menu on top of sele
metamaterial cloaking - Scientific photography by Miki Asai, by Bruce Munro
molecular assembler - by Laurie Greasley 16-bit Isometric
wormhole - by Tim White
- pilot wave -
+ pilot wave - none
nail gun - Screenprint
shotgun - blueprint by Dan McPharlin
grenades, missiles, explosions - vibrant fireball explosion sonic shockwave ring art by Victo Ngai --ar 3:2 --v 5 --s 750
spores - turquoise black spores on a white background full color scientific anatomy by Ernst Haeckel
- drones - tilt-shift photography
+ drones - insect quadcopter tilt-shift photography
super balls - By Akari Toriyama
wave - sound wave oscilloscope by Paul Catherall, concentric circles by Paul Catherall
Barbara Takenaga's painting depicting a clean sound wave on aoscilloscope device --ar 3:2 --v 5
@@ -1306,7 +1234,6 @@ if pause is pressed while selecting power ups, display pause menu on top of sele
invulnerable - by Nick Veasey (photos that look like x-rays)
alternate reality - Fractal art
tech choice - mandala tile Mosaic
- tech that spawns heal power ups - green Quilling
time, CPT, pause - by Lee Bontecou
boost, coupling power ups tech - cyan electron orbiting a black nucleus electric field as bas-relief //(by Kazumasa Nagai)
radioactive - volumetric atomic nucleus diagram by Paul Catherall