sound-bot

new community level: dojo by weird_pusheen

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
This commit is contained in:
landgreen
2023-07-04 08:14:43 -07:00
parent 4415942b94
commit 6cd2502fb5
18 changed files with 1208 additions and 604 deletions

View File

@@ -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(`<span class='color-var'>b</span>.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(`<span class='color-var'>b</span>.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
})
},

View File

@@ -373,6 +373,7 @@ const build = {
if (tech.boomBotCount) botText += `<br>boom-bots: ${tech.boomBotCount}`
if (tech.laserBotCount) botText += `<br>laser-bots: ${tech.laserBotCount}`
if (tech.foamBotCount) botText += `<br>foam-bots: ${tech.foamBotCount}`
if (tech.soundBotCount) botText += `<br>sound-bots: ${tech.soundBotCount}`
if (tech.dynamoBotCount) botText += `<br>dynamo-bots: ${tech.dynamoBotCount}`
if (tech.plasmaBotCount) botText += `<br>plasma-bots: ${tech.plasmaBotCount}`
if (tech.missileBotCount) botText += `<br>missile-bots: ${tech.missileBotCount}`

View File

@@ -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(`<strong>underpass</strong> by <span class='color-var'>weird_pusheen</span>`);
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 **********************************************

View File

@@ -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)

View File

@@ -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(`<span class='color-var'>m</span>.<span class='color-f'>maxEnergy</span> <span class='color-symbol'>=</span> ${(m.maxEnergy.toFixed(2))}`)
},
fieldMeterColor: "#0cf",

View File

@@ -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) {

View File

@@ -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];

View File

@@ -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(`<span class='color-var'>tech</span>.quantumEraserCount <span class='color-symbol'>=</span> ${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(`<span class='color-var'>tech</span>.quantumEraserCount <span class='color-symbol'>=</span> ${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,

View File

@@ -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: "<strong>+33%</strong> <strong>movement</strong> and <strong>jumping</strong><br><strong>+30%</strong> <strong class='color-defense'>defense</strong>",
maxCount: 3,
description: "<strong>+33%</strong> <strong>movement</strong> and <strong>jumping</strong><br><strong>+22%</strong> <strong class='color-defense'>defense</strong>",
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: `<strong>+67%</strong> <strong class='color-d'>damage</strong><br>${powerUps.orb.ammo()} can't <strong>spawn</strong>`,
description: `<strong>+78%</strong> <strong class='color-d'>damage</strong><br>${powerUps.orb.ammo()} can't <strong>spawn</strong>`,
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: "<strong>+25%</strong> <strong><em>fire rate</em></strong><br>spawn a <strong class='color-g'>gun</strong>",
description: "<strong>+22%</strong> <strong><em>fire rate</em></strong><br>spawn a <strong class='color-g'>gun</strong>",
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: `<a target="_blank" href='https://en.wikipedia.org/wiki/Topology' class="link">anti-shear topology</a>`,
description: "<strong>+30%</strong> projectile <strong>duration</strong>", //<br><em style = 'font-size: 83%'>drone spore worm flea missile foam wave neutron ice</em>",
description: "your bullets last <strong>+30%</strong> <strong>longer</strong>", //<br><em style = 'font-size: 83%'>drone spore worm flea missile foam wave neutron ice</em>",
maxCount: 3,
count: 0,
frequency: 1,
@@ -1195,7 +1195,7 @@ const tech = {
},
{
name: "shear stress",
description: "after mobs <strong>die</strong><br>they release a <strong>nail</strong> that targets nearby mobs",
description: "after mobs <strong>die</strong><br>they fire a <strong>nail</strong> at nearby mobs",
maxCount: 9,
count: 0,
frequency: 1,
@@ -1493,6 +1493,62 @@ const tech = {
tech.isFoamBotUpgrade = false
}
},
{
name: "sound-bot",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">sound-bot</a>`,
description: "a <strong class='color-bot'>bot</strong> emits <strong>expanding arcs</strong><br>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: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">sound-bot upgrade</a>`,
description: "<strong>convert</strong> your bots to <strong>sound-bots</strong><br><strong>+200%</strong> wave <strong>fire rate</strong> and <strong>+100%</strong> <strong class='color-d'>damage</strong>",
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: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">boom-bot</a>`,
@@ -1679,7 +1735,7 @@ const tech = {
{
name: "dynamo-bot",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">dynamo-bot</a>`,
description: "a <strong class='color-bot'>bot</strong> <strong class='color-d'>damages</strong> mobs while it <strong>traces</strong> your path<br>when it's near generate <strong>+7</strong> <strong class='color-f'>energy</strong> per second",
description: "a <strong class='color-bot'>bot</strong> <strong class='color-d'>damages</strong> mobs while it <strong>traces</strong> your path<br>when it's near generate <strong>+8</strong> <strong class='color-f'>energy</strong> per second",
maxCount: 9,
count: 0,
frequency: 1,
@@ -1705,7 +1761,7 @@ const tech = {
{
name: "dynamo-bot upgrade",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">dynamo-bot upgrade</a>`,
description: "<strong>convert</strong> your bots to <strong>dynamo-bots</strong><br>when it's near generate <strong>+23</strong> <strong class='color-f'>energy</strong> per second",
description: "<strong>convert</strong> your bots to <strong>dynamo-bots</strong><br>when it's near generate <strong>+24</strong> <strong class='color-f'>energy</strong> 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 <strong>5%</strong> <strong class='color-h'>health</strong><br><strong class='color-s'>freeze</strong> all mobs for <strong>7</strong> seconds`,
descriptionFunction() {
return `after losing at least <strong>5%</strong> ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>" : "<strong class='color-h'>health</strong>"}<br><strong class='color-s'>freeze</strong> all mobs for <strong>7</strong> seconds`
},
maxCount: 1,
count: 0,
frequency: 1,
@@ -2559,7 +2620,7 @@ const tech = {
},
{
name: "ground state",
description: "<strong>+200</strong> maximum <strong class='color-f'>energy</strong><br><strong>33%</strong> passive <strong class='color-f'>energy</strong> generation",
description: "<strong>+266</strong> maximum <strong class='color-f'>energy</strong><br><strong>33%</strong> passive <strong class='color-f'>energy</strong> generation",
// description: "reduce <strong class='color-defense'>defense</strong> by <strong>66%</strong><br>you <strong>no longer</strong> passively regenerate <strong class='color-f'>energy</strong>",
maxCount: 1,
count: 0,
@@ -2731,7 +2792,7 @@ const tech = {
},
{
name: "parasitism",
description: "if a mob has <strong>died</strong> in the last <strong>5 seconds</strong><br><strong>+60%</strong> <strong class='color-d'>damage</strong>, inhibit <strong class='color-f'>energy</strong> generation",
description: "if a mob has <strong>died</strong> in the last <strong>5 seconds</strong><br><strong>+83%</strong> <strong class='color-d'>damage</strong>, inhibit <strong class='color-f'>energy</strong> generation",
maxCount: 1,
count: 0,
frequency: 1,
@@ -2952,7 +3013,7 @@ const tech = {
},
{
name: "induction brake",
description: `after using ${powerUps.orb.heal()} <strong class='color-s'>slow</strong> nearby mobs for <strong>15</strong> seconds<br>spawn ${powerUps.orb.heal(3)}`,
description: `after using ${powerUps.orb.heal()} <strong class='color-s'>slow</strong> nearby mobs for <strong>15</strong> seconds<br>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 <strong>+100%</strong> effect<br><strong>+5%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> pool`
return `${powerUps.orb.heal()} have <strong>+100%</strong> effect<br><strong>+4%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> 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 <strong>level</strong><br>for every <strong>33%</strong> missing ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>" : "<strong class='color-h'>health</strong>"} spawn ${powerUps.orb.heal()}`
return `at the start of each <strong>level</strong><br>for every <strong>25%</strong> missing ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>" : "<strong class='color-h'>health</strong>"} spawn ${powerUps.orb.heal()}`
},
maxCount: 1,
count: 0,
@@ -3303,7 +3363,7 @@ const tech = {
},
{
name: "renormalization",
description: `<strong>44%</strong> chance to spawn ${powerUps.orb.research(1)}<br>after consuming ${powerUps.orb.research(1)}`,
description: `<strong>46%</strong> chance to spawn ${powerUps.orb.research(1)} after consuming ${powerUps.orb.research(1)}<br><strong>+3%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> 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: "<strong>+17%</strong> <strong class='color-d'>damage</strong><br><strong>+17%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> pool",
description: "<strong>+22%</strong> <strong class='color-d'>damage</strong><br><strong>+22%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> 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: "<strong>+30%</strong> <strong class='color-d'>damage</strong><br><strong>time</strong> can't be <strong>paused</strong> <em>(time can be dilated)</em>",
description: "<strong>+24%</strong> <strong class='color-d'>damage</strong><br><strong>time</strong> can't be <strong>paused</strong> <em>(time can be dilated)</em>",
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: "<strong>+15%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong>,<br>but after a <strong>collision</strong> eject <strong>1</strong> <strong class='color-m'>tech</strong>",
description: "<strong>+17%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong>,<br>but after a <strong>collision</strong> eject <strong>1</strong> <strong class='color-m'>tech</strong>",
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: `<a target="_blank" href='https://en.wikipedia.org/wiki/Unmanned_aerial_vehicle' class="link">drone repair</a>`,
description: "after a <strong>drone</strong> expires it <strong>redeploys</strong><br>for a <strong>20%</strong> chance to use <strong>1</strong> <strong>drone</strong> <strong class='color-ammo'>ammo</strong>",
name: "von Neumann probe", //"drone repair",
description: "after a <strong>drone</strong> expires<br>it will <strong>harvest</strong> a nearby <strong class='color-block'>block</strong> to <strong>replicate</strong> itself",
// description: "broken <strong>drones</strong> <strong>repair</strong> if the drone <strong class='color-g'>gun</strong> is active<br><strong>repairing</strong> has a <strong>25%</strong> chance to use <strong>1</strong> <strong>drone</strong>",
isGunTech: true,
maxCount: 1,
@@ -6467,16 +6533,16 @@ const tech = {
},
{
name: "uncertainty principle",
description: "<strong>foam</strong> and <strong>wave</strong> positions are erratic<br><strong>+53%</strong> <strong>foam</strong> and <strong>wave</strong> <strong class='color-d'>damage</strong>",
description: "<strong>foam</strong>, <strong>wave</strong>, and <strong>super ball</strong> positions are erratic<br><strong>+53%</strong> <strong>foam</strong>, <strong>wave</strong>, and <strong>super ball</strong> <strong class='color-d'>damage</strong>",
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 `<span style = 'font-size:90%;'>for each mob left <strong>alive</strong> after you exit a <strong>level</strong><br><strong>kill</strong> a mob as they spawn at <strong>+${(100 - 1.1 * simulation.difficultyMode ** 2).toFixed(0)}%</strong> <strong class='color-dup'>duplication</strong></span>`
return `for each mob left <strong>alive</strong> after you exit a <strong>level</strong><br>there is a <strong>17%</strong> chance to spawn a random <strong>power up</strong>`
},
// descriptionFunction() {
// return `for each mob left <strong>alive</strong> after you exit a <strong>level</strong><br>`
@@ -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,