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:
699
js/bullet.js
699
js/bullet.js
@@ -3648,6 +3648,31 @@ const b = {
|
||||
bullet[bullet.length - 1].isMutualismActive = true
|
||||
}
|
||||
},
|
||||
delayDrones(where, droneCount = 1) {
|
||||
let respawnDrones = () => {
|
||||
if (droneCount > 0) {
|
||||
requestAnimationFrame(respawnDrones);
|
||||
if (!simulation.paused && !simulation.isChoosing && m.alive) {
|
||||
droneCount--
|
||||
if (tech.isDroneRadioactive) {
|
||||
b.droneRadioactive({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0)
|
||||
} else {
|
||||
b.drone({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0)
|
||||
if (tech.isDroneGrab && deliveryCount > 0) {
|
||||
const who = bullet[bullet.length - 1]
|
||||
who.isImproved = true;
|
||||
const SCALE = 2.25
|
||||
Matter.Body.scale(who, SCALE, SCALE);
|
||||
who.lookFrequency = 30 + Math.floor(11 * Math.random());
|
||||
who.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
|
||||
deliveryCount--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(respawnDrones);
|
||||
},
|
||||
drone(where = {
|
||||
x: m.pos.x + 30 * Math.cos(m.angle) + 20 * (Math.random() - 0.5),
|
||||
y: m.pos.y + 30 * Math.sin(m.angle) + 20 * (Math.random() - 0.5)
|
||||
@@ -3693,10 +3718,7 @@ const b = {
|
||||
} else {
|
||||
//move away from target after hitting
|
||||
const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20)
|
||||
Matter.Body.setVelocity(this, {
|
||||
x: unit.x,
|
||||
y: unit.y
|
||||
});
|
||||
Matter.Body.setVelocity(this, { x: unit.x, y: unit.y });
|
||||
this.lockedOn = null
|
||||
if (this.endCycle > simulation.cycle + this.deathCycles) {
|
||||
this.endCycle -= 60
|
||||
@@ -3705,68 +3727,164 @@ const b = {
|
||||
}
|
||||
},
|
||||
onEnd() {
|
||||
if (tech.isDroneRespawn && b.inventory.length && b.activeGun === 7 && b.guns[b.activeGun].ammo > 0 && mob.length) {
|
||||
b.drone({
|
||||
x: this.position.x,
|
||||
y: this.position.y
|
||||
}, 0)
|
||||
if (Math.random() < 0.2) {
|
||||
b.guns[b.activeGun].ammo--;
|
||||
simulation.updateGunHUD();
|
||||
if (tech.isDroneRespawn) {
|
||||
//are there any nearby bodies nearby that aren't blocked by map?
|
||||
const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable && Vector.magnitude(Vector.sub(this.position, a.position)) < 70 + 30 * a.mass)
|
||||
if (canSee.length) {
|
||||
//find the closest body to the drone from the canSee array
|
||||
const found = canSee.reduce((a, b) => {
|
||||
const distA = Vector.magnitude(Vector.sub(this.position, a.position))
|
||||
const distB = Vector.magnitude(Vector.sub(this.position, b.position))
|
||||
return distA < distB ? a : b
|
||||
})
|
||||
if (found && m.energy > 0.05) {
|
||||
m.energy -= 0.05
|
||||
//remove the body and spawn a new drone
|
||||
Composite.remove(engine.world, found)
|
||||
body.splice(body.indexOf(found), 1)
|
||||
b.delayDrones(found.position, 0.7 * Math.sqrt(found.mass))
|
||||
//draw a line from the drone to the body on the canvas
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.position.x, this.position.y);
|
||||
ctx.lineTo(found.position.x, found.position.y);
|
||||
ctx.strokeStyle = "#000";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
|
||||
//animate the block fading away
|
||||
simulation.ephemera.push({
|
||||
name: "droneRespawn",
|
||||
count: 60, //cycles before it self removes
|
||||
do() {
|
||||
this.count--
|
||||
if (this.count < 0) simulation.removeEphemera(this.name)
|
||||
ctx.beginPath();
|
||||
let vertices = found.vertices;
|
||||
ctx.moveTo(vertices[0].x, vertices[0].y);
|
||||
for (let j = 1; j < vertices.length; j++) {
|
||||
ctx.lineTo(vertices[j].x, vertices[j].y);
|
||||
}
|
||||
ctx.lineTo(vertices[0].x, vertices[0].y);
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = `rgba(0,0,0,${this.count / 60})`
|
||||
ctx.stroke();
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
do() {
|
||||
if (simulation.cycle + this.deathCycles > this.endCycle) { //fall shrink and die
|
||||
this.force.y += this.mass * 0.0012;
|
||||
this.restitution = 0.2;
|
||||
const scale = 0.995;
|
||||
Matter.Body.scale(this, scale, scale);
|
||||
doRespawning() { //fall shrink and die
|
||||
const scale = 0.995;
|
||||
Matter.Body.scale(this, scale, scale);
|
||||
if (this.bodyTarget) {
|
||||
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.bodyTarget.position)), -this.mass * THRUST)
|
||||
} else {
|
||||
this.force.y += this.mass * 0.0002;
|
||||
if (!(simulation.cycle % this.lookFrequency)) {
|
||||
//find mob targets
|
||||
this.lockedOn = null;
|
||||
let closeDist = Infinity;
|
||||
for (let i = 0, len = mob.length; i < len; ++i) {
|
||||
if (
|
||||
!mob[i].isBadTarget &&
|
||||
Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
|
||||
Matter.Query.ray(body, this.position, mob[i].position).length === 0 &&
|
||||
!mob[i].isInvulnerable
|
||||
) {
|
||||
const TARGET_VECTOR = Vector.sub(this.position, mob[i].position)
|
||||
const DIST = Vector.magnitude(TARGET_VECTOR);
|
||||
if (DIST < closeDist) {
|
||||
closeDist = DIST;
|
||||
this.lockedOn = mob[i]
|
||||
}
|
||||
this.force.y += this.mass * 0.0012;
|
||||
}
|
||||
},
|
||||
doDieing() { //fall shrink and die
|
||||
this.force.y += this.mass * 0.0012;
|
||||
const scale = 0.995;
|
||||
Matter.Body.scale(this, scale, scale);
|
||||
},
|
||||
do() {
|
||||
if (simulation.cycle + this.deathCycles > this.endCycle) {
|
||||
this.restitution = 0.2;
|
||||
if (tech.isDroneRespawn) {
|
||||
this.do = this.doRespawning
|
||||
//make a list of all elements of array body that a ray can be drawn to from the drone
|
||||
const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable)
|
||||
if (canSee.length) {
|
||||
//find the closest body to the drone from the canSee array
|
||||
const found = canSee.reduce((a, b) => {
|
||||
const distA = Vector.magnitude(Vector.sub(this.position, a.position))
|
||||
const distB = Vector.magnitude(Vector.sub(this.position, b.position))
|
||||
return distA < distB ? a : b
|
||||
})
|
||||
if (found) this.bodyTarget = found
|
||||
}
|
||||
} else {
|
||||
this.do = this.doDieing
|
||||
}
|
||||
}
|
||||
|
||||
this.force.y += this.mass * 0.0002;
|
||||
if (!(simulation.cycle % this.lookFrequency)) {
|
||||
//find mob targets
|
||||
this.lockedOn = null;
|
||||
let closeDist = Infinity;
|
||||
for (let i = 0, len = mob.length; i < len; ++i) {
|
||||
if (
|
||||
!mob[i].isBadTarget &&
|
||||
Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
|
||||
Matter.Query.ray(body, this.position, mob[i].position).length === 0 &&
|
||||
!mob[i].isInvulnerable
|
||||
) {
|
||||
const TARGET_VECTOR = Vector.sub(this.position, mob[i].position)
|
||||
const DIST = Vector.magnitude(TARGET_VECTOR);
|
||||
if (DIST < closeDist) {
|
||||
closeDist = DIST;
|
||||
this.lockedOn = mob[i]
|
||||
}
|
||||
}
|
||||
//blink towards mobs
|
||||
if (tech.isDroneTeleport && this.lockedOn) {
|
||||
const sub = Vector.sub(this.lockedOn.position, this.position);
|
||||
const distMag = Vector.magnitude(sub);
|
||||
const unit = Vector.normalise(sub)
|
||||
Matter.Body.setVelocity(this, Vector.mult(unit, Math.max(20, this.speed * 1.5)));
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.position.x, this.position.y);
|
||||
Matter.Body.translate(this, Vector.mult(unit, Math.min(350, distMag - this.lockedOn.radius + 10)));
|
||||
ctx.lineTo(this.position.x, this.position.y);
|
||||
ctx.lineWidth = RADIUS * 2;
|
||||
ctx.strokeStyle = "rgba(0,0,0,0.5)";
|
||||
ctx.stroke();
|
||||
}
|
||||
//power ups
|
||||
if (!this.isImproved && !simulation.isChoosing) {
|
||||
if (this.lockedOn) {
|
||||
for (let i = 0, len = powerUp.length; i < len; ++i) { //grab, but don't lock onto nearby power up
|
||||
if (
|
||||
Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 &&
|
||||
(powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
|
||||
(powerUp[i].name !== "field" || !tech.isSuperDeterminism)
|
||||
// &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
|
||||
) {
|
||||
}
|
||||
//blink towards mobs
|
||||
if (tech.isDroneTeleport && this.lockedOn) {
|
||||
const sub = Vector.sub(this.lockedOn.position, this.position);
|
||||
const distMag = Vector.magnitude(sub);
|
||||
const unit = Vector.normalise(sub)
|
||||
Matter.Body.setVelocity(this, Vector.mult(unit, Math.max(20, this.speed * 1.5)));
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.position.x, this.position.y);
|
||||
Matter.Body.translate(this, Vector.mult(unit, Math.min(350, distMag - this.lockedOn.radius + 10)));
|
||||
ctx.lineTo(this.position.x, this.position.y);
|
||||
ctx.lineWidth = RADIUS * 2;
|
||||
ctx.strokeStyle = "rgba(0,0,0,0.5)";
|
||||
ctx.stroke();
|
||||
}
|
||||
//power ups
|
||||
if (!this.isImproved && !simulation.isChoosing) {
|
||||
if (this.lockedOn) {
|
||||
for (let i = 0, len = powerUp.length; i < len; ++i) { //grab, but don't lock onto nearby power up
|
||||
if (
|
||||
Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 &&
|
||||
(powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
|
||||
(powerUp[i].name !== "field" || !tech.isSuperDeterminism)
|
||||
// &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
|
||||
) {
|
||||
//draw pickup for a single cycle
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.position.x, this.position.y);
|
||||
ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y);
|
||||
ctx.strokeStyle = "#000"
|
||||
ctx.lineWidth = 4
|
||||
ctx.stroke();
|
||||
//pick up nearby power ups
|
||||
powerUps.onPickUp(powerUp[i]);
|
||||
powerUp[i].effect();
|
||||
Matter.Composite.remove(engine.world, powerUp[i]);
|
||||
powerUp.splice(i, 1);
|
||||
if (tech.isDroneGrab) {
|
||||
this.isImproved = true;
|
||||
const SCALE = 2.25
|
||||
Matter.Body.scale(this, SCALE, SCALE);
|
||||
this.lookFrequency = 30 + Math.floor(11 * Math.random());
|
||||
this.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//look for power ups to lock onto
|
||||
let closeDist = Infinity;
|
||||
for (let i = 0, len = powerUp.length; i < len; ++i) {
|
||||
if (
|
||||
(powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
|
||||
(powerUp[i].name !== "field" || !tech.isSuperDeterminism)
|
||||
// &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
|
||||
) {
|
||||
if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) {
|
||||
//draw pickup for a single cycle
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.position.x, this.position.y);
|
||||
@@ -3785,71 +3903,38 @@ const b = {
|
||||
Matter.Body.scale(this, SCALE, SCALE);
|
||||
this.lookFrequency = 30 + Math.floor(11 * Math.random());
|
||||
this.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
|
||||
// this.frictionAir = 0
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//look for power ups to lock onto
|
||||
let closeDist = Infinity;
|
||||
for (let i = 0, len = powerUp.length; i < len; ++i) {
|
||||
//look for power ups to lock onto
|
||||
if (
|
||||
(powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
|
||||
(powerUp[i].name !== "field" || !tech.isSuperDeterminism)
|
||||
// &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
|
||||
Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 &&
|
||||
Matter.Query.ray(body, this.position, powerUp[i].position).length === 0
|
||||
) {
|
||||
if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) {
|
||||
//draw pickup for a single cycle
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.position.x, this.position.y);
|
||||
ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y);
|
||||
ctx.strokeStyle = "#000"
|
||||
ctx.lineWidth = 4
|
||||
ctx.stroke();
|
||||
//pick up nearby power ups
|
||||
powerUps.onPickUp(powerUp[i]);
|
||||
powerUp[i].effect();
|
||||
Matter.Composite.remove(engine.world, powerUp[i]);
|
||||
powerUp.splice(i, 1);
|
||||
if (tech.isDroneGrab) {
|
||||
this.isImproved = true;
|
||||
const SCALE = 2.25
|
||||
Matter.Body.scale(this, SCALE, SCALE);
|
||||
this.lookFrequency = 30 + Math.floor(11 * Math.random());
|
||||
this.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
|
||||
// this.frictionAir = 0
|
||||
}
|
||||
break;
|
||||
}
|
||||
//look for power ups to lock onto
|
||||
if (
|
||||
Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 &&
|
||||
Matter.Query.ray(body, this.position, powerUp[i].position).length === 0
|
||||
) {
|
||||
const TARGET_VECTOR = Vector.sub(this.position, powerUp[i].position)
|
||||
const DIST = Vector.magnitude(TARGET_VECTOR);
|
||||
if (DIST < closeDist) {
|
||||
closeDist = DIST;
|
||||
this.lockedOn = powerUp[i]
|
||||
}
|
||||
const TARGET_VECTOR = Vector.sub(this.position, powerUp[i].position)
|
||||
const DIST = Vector.magnitude(TARGET_VECTOR);
|
||||
if (DIST < closeDist) {
|
||||
closeDist = DIST;
|
||||
this.lockedOn = powerUp[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.lockedOn) { //accelerate towards mobs
|
||||
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST)
|
||||
} else { //accelerate towards mouse
|
||||
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, simulation.mouseInGame)), -this.mass * THRUST)
|
||||
}
|
||||
// speed cap instead of friction to give more agility
|
||||
if (this.speed > 6) {
|
||||
Matter.Body.setVelocity(this, {
|
||||
x: this.velocity.x * 0.97,
|
||||
y: this.velocity.y * 0.97
|
||||
});
|
||||
}
|
||||
}
|
||||
if (this.lockedOn) { //accelerate towards mobs
|
||||
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST)
|
||||
} else { //accelerate towards mouse
|
||||
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, simulation.mouseInGame)), -this.mass * THRUST)
|
||||
}
|
||||
// speed cap instead of friction to give more agility
|
||||
if (this.speed > 6) {
|
||||
Matter.Body.setVelocity(this, {
|
||||
x: this.velocity.x * 0.97,
|
||||
y: this.velocity.y * 0.97
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -3891,14 +3976,50 @@ const b = {
|
||||
maxRadioRadius: 270 + Math.floor(90 * Math.random()),
|
||||
beforeDmg() { },
|
||||
onEnd() {
|
||||
if (tech.isDroneRespawn && b.inventory.length && b.activeGun === 7 && b.guns[b.activeGun].ammo > 0 && mob.length) {
|
||||
b.droneRadioactive({
|
||||
x: this.position.x,
|
||||
y: this.position.y
|
||||
}, 0)
|
||||
if (Math.random() < 0.2) {
|
||||
b.guns[b.activeGun].ammo--;
|
||||
simulation.updateGunHUD();
|
||||
if (tech.isDroneRespawn) {
|
||||
//are there any nearby bodies nearby that aren't blocked by map?
|
||||
const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable && Vector.magnitude(Vector.sub(this.position, a.position)) < 70 + 30 * a.mass)
|
||||
if (canSee.length) {
|
||||
//find the closest body to the drone from the canSee array
|
||||
const found = canSee.reduce((a, b) => {
|
||||
const distA = Vector.magnitude(Vector.sub(this.position, a.position))
|
||||
const distB = Vector.magnitude(Vector.sub(this.position, b.position))
|
||||
return distA < distB ? a : b
|
||||
})
|
||||
if (found && m.energy > 0.05) {
|
||||
m.energy -= 0.1
|
||||
//remove the body and spawn a new drone
|
||||
Composite.remove(engine.world, found)
|
||||
body.splice(body.indexOf(found), 1)
|
||||
b.delayDrones(found.position, 0.35 * Math.sqrt(found.mass))
|
||||
//draw a line from the drone to the body on the canvas
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.position.x, this.position.y);
|
||||
ctx.lineTo(found.position.x, found.position.y);
|
||||
ctx.strokeStyle = "#000";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
|
||||
//animate the block fading away
|
||||
simulation.ephemera.push({
|
||||
name: "droneRespawn",
|
||||
count: 60, //cycles before it self removes
|
||||
do() {
|
||||
this.count--
|
||||
if (this.count < 0) simulation.removeEphemera(this.name)
|
||||
ctx.beginPath();
|
||||
let vertices = found.vertices;
|
||||
ctx.moveTo(vertices[0].x, vertices[0].y);
|
||||
for (let j = 1; j < vertices.length; j++) {
|
||||
ctx.lineTo(vertices[j].x, vertices[j].y);
|
||||
}
|
||||
ctx.lineTo(vertices[0].x, vertices[0].y);
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = `rgba(0,0,0,${this.count / 60})`
|
||||
ctx.stroke();
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4074,7 +4195,8 @@ const b = {
|
||||
bullet[me] = Bodies.polygon(where.x, where.y, 12, radius, b.fireAttributes(dir, false));
|
||||
Composite.add(engine.world, bullet[me]); //add bullet to world
|
||||
Matter.Body.setVelocity(bullet[me], velocity);
|
||||
Matter.Body.setDensity(bullet[me], 0.0007 + 0.00055 * tech.isSuperHarm);
|
||||
bullet[me].calcDensity = () => 0.0007 + 0.00055 * tech.isSuperHarm + 0.0004 * tech.isBulletTeleport
|
||||
Matter.Body.setDensity(bullet[me], bullet[me].calcDensity());
|
||||
bullet[me].endCycle = simulation.cycle + Math.floor(270 + 90 * Math.random());
|
||||
bullet[me].minDmgSpeed = 0;
|
||||
bullet[me].restitution = 1;
|
||||
@@ -4086,18 +4208,7 @@ const b = {
|
||||
this.force.y += this.mass * 0.001;
|
||||
if (Matter.Query.collides(this, [player]).length) {
|
||||
this.endCycle = 0
|
||||
// let dmg = 0.015 * this.mass * tech.isSuperHarm
|
||||
// m.damage(dmg);
|
||||
const drain = m.energy * 0.25
|
||||
m.energy -= drain
|
||||
|
||||
// simulation.drawList.push({ //add dmg to draw queue
|
||||
// x: this.position.x,
|
||||
// y: this.position.y,
|
||||
// radius: radius * 3,
|
||||
// color: "hsla(194, 100%, 43%,0.2)",
|
||||
// time: 7
|
||||
// });
|
||||
m.energy -= m.energy * 0.25
|
||||
simulation.drawList.push({ //add dmg to draw queue
|
||||
x: this.position.x,
|
||||
y: this.position.y,
|
||||
@@ -4113,6 +4224,18 @@ const b = {
|
||||
if (this.cycle > 2) this.do = this.collidePlayerDo
|
||||
this.force.y += this.mass * 0.001;
|
||||
};
|
||||
} else if (tech.isBulletTeleport) {
|
||||
bullet[me].portFrequency = 25 + Math.floor(10 * Math.random())
|
||||
bullet[me].nextPortCycle = simulation.cycle + bullet[me].portFrequency
|
||||
bullet[me].do = function () {
|
||||
this.force.y += this.mass * 0.001;
|
||||
if (this.nextPortCycle < simulation.cycle) { //teleport around if you have tech.isBulletTeleport
|
||||
this.nextPortCycle = simulation.cycle + this.portFrequency
|
||||
const range = 33 * Math.sqrt(radius) * Math.random()
|
||||
Matter.Body.setPosition(this, Vector.add(this.position, Vector.rotate({ x: range, y: 0 }, 2 * Math.PI * Math.random())))
|
||||
Matter.Body.setVelocity(this, Vector.rotate(this.velocity, 2 * (Math.random() * Math.random() - 0.25)))
|
||||
}
|
||||
};
|
||||
} else {
|
||||
bullet[me].do = function () {
|
||||
this.force.y += this.mass * 0.001;
|
||||
@@ -4133,7 +4256,7 @@ const b = {
|
||||
this.endCycle = 0
|
||||
} else if (tech.isSuperBounce) {
|
||||
const cycle = () => {
|
||||
Matter.Body.setDensity(this, (0.0008 + 0.0009 * tech.isSuperHarm) * 1.33); //50% more density and damage
|
||||
Matter.Body.setDensity(bullet[me], bullet[me].calcDensity() * 1.33);//33% more density and damage
|
||||
this.endCycle = simulation.cycle + Math.floor(300 + 90 * Math.random()); //reset to full duration of time
|
||||
Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(this.velocity), 60)); //reset to high velocity
|
||||
|
||||
@@ -4848,7 +4971,7 @@ const b = {
|
||||
// **************************************************************************************************
|
||||
// **************************************************************************************************
|
||||
totalBots() {
|
||||
return tech.dynamoBotCount + tech.foamBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount
|
||||
return tech.dynamoBotCount + tech.foamBotCount + tech.soundBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount
|
||||
},
|
||||
hasBotUpgrade() {
|
||||
return tech.isNailBotUpgrade + tech.isFoamBotUpgrade + tech.isBoomBotUpgrade + tech.isLaserBotUpgrade + tech.isOrbitBotUpgrade + tech.isDynamoBotUpgrade
|
||||
@@ -4898,6 +5021,10 @@ const b = {
|
||||
tech.foamBotCount--
|
||||
return
|
||||
}
|
||||
if (tech.soundBotCount > 1) {
|
||||
tech.soundBotCount--
|
||||
return
|
||||
}
|
||||
if (tech.boomBotCount > 1) {
|
||||
tech.boomBotCount--
|
||||
return
|
||||
@@ -4924,6 +5051,7 @@ const b = {
|
||||
tech.laserBotCount = 0
|
||||
tech.nailBotCount = 0
|
||||
tech.foamBotCount = 0
|
||||
tech.soundBotCount = 0
|
||||
tech.boomBotCount = 0
|
||||
tech.orbitBotCount = 0
|
||||
tech.missileBotCount = 0
|
||||
@@ -4945,6 +5073,10 @@ const b = {
|
||||
x: player.position.x + 50 * (Math.random() - 0.5),
|
||||
y: player.position.y + 50 * (Math.random() - 0.5)
|
||||
}, false)
|
||||
for (let i = 0; i < tech.soundBotCount; i++) b.soundBot({
|
||||
x: player.position.x + 50 * (Math.random() - 0.5),
|
||||
y: player.position.y + 50 * (Math.random() - 0.5)
|
||||
}, false)
|
||||
for (let i = 0; i < tech.boomBotCount; i++) b.boomBot({
|
||||
x: player.position.x + 50 * (Math.random() - 0.5),
|
||||
y: player.position.y + 50 * (Math.random() - 0.5)
|
||||
@@ -4968,13 +5100,16 @@ const b = {
|
||||
}
|
||||
},
|
||||
randomBot(where = player.position, isKeep = true, isLaser = true) {
|
||||
if (Math.random() < 0.5) { //chance to match scrap bot to your upgrade
|
||||
if (Math.random() < 0.5) { //chance to match bot to your upgrade
|
||||
if (tech.isNailBotUpgrade) { //check for upgrades first
|
||||
b.nailBot(where, isKeep)
|
||||
if (isKeep) tech.nailBotCount++;
|
||||
} else if (tech.isFoamBotUpgrade) {
|
||||
b.foamBot(where, isKeep)
|
||||
if (isKeep) tech.foamBotCount++;
|
||||
} else if (tech.isSoundBotUpgrade) {
|
||||
b.soundBot(where, isKeep)
|
||||
if (isKeep) tech.soundBotCount++;
|
||||
} else if (tech.isBoomBotUpgrade) {
|
||||
b.boomBot(where, isKeep)
|
||||
if (isKeep) tech.boomBotCount++;
|
||||
@@ -4987,7 +5122,10 @@ const b = {
|
||||
} else if (tech.isDynamoBotUpgrade) {
|
||||
b.dynamoBot(where, isKeep)
|
||||
if (isKeep) tech.dynamoBotCount++;
|
||||
} else if (Math.random() < 0.166 && isLaser) { //random
|
||||
} else if (Math.random() < 0.143) { //random
|
||||
b.soundBot(where, isKeep)
|
||||
if (isKeep) tech.soundBotCount++;
|
||||
} else if (Math.random() < 0.166 && isLaser) {
|
||||
b.laserBot(where, isKeep)
|
||||
if (isKeep) tech.laserBotCount++;
|
||||
} else if (Math.random() < 0.2) {
|
||||
@@ -5006,8 +5144,11 @@ const b = {
|
||||
b.boomBot(where, isKeep)
|
||||
if (isKeep) tech.boomBotCount++;
|
||||
}
|
||||
} else { //else don't match scrap bot to upgrade
|
||||
if (Math.random() < 0.166 && isLaser) { //random
|
||||
} else { //else don't match bot to upgrade
|
||||
if (Math.random() < 0.143) { //random
|
||||
b.soundBot(where, isKeep)
|
||||
if (isKeep) tech.soundBotCount++;
|
||||
} else if (Math.random() < 0.166 && isLaser) { //random
|
||||
b.laserBot(where, isKeep)
|
||||
if (isKeep) tech.laserBotCount++;
|
||||
} else if (Math.random() < 0.2) {
|
||||
@@ -5084,7 +5225,7 @@ const b = {
|
||||
if (Vector.magnitude(Vector.sub(this.position, player.position)) < 250 && m.immuneCycle < m.cycle) { //give energy
|
||||
Matter.Body.setAngularVelocity(this, this.spin)
|
||||
if (this.isUpgraded) {
|
||||
m.energy += 0.115
|
||||
m.energy += 0.12
|
||||
simulation.drawList.push({ //add dmg to draw queue
|
||||
x: this.position.x,
|
||||
y: this.position.y,
|
||||
@@ -5093,7 +5234,7 @@ const b = {
|
||||
time: simulation.drawTime
|
||||
});
|
||||
} else {
|
||||
m.energy += 0.035
|
||||
m.energy += 0.04
|
||||
simulation.drawList.push({ //add dmg to draw queue
|
||||
x: this.position.x,
|
||||
y: this.position.y,
|
||||
@@ -5148,10 +5289,7 @@ const b = {
|
||||
Composite.add(engine.world, bullet[me]); //add bullet to world
|
||||
b.setDynamoBotDelay()
|
||||
},
|
||||
nailBot(position = {
|
||||
x: player.position.x + 50 * (Math.random() - 0.5),
|
||||
y: player.position.y + 50 * (Math.random() - 0.5)
|
||||
}, isConsole = true) {
|
||||
nailBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) {
|
||||
if (isConsole) simulation.makeTextLog(`<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
|
||||
})
|
||||
},
|
||||
|
||||
@@ -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}`
|
||||
|
||||
509
js/level.js
509
js/level.js
@@ -10,7 +10,7 @@ const level = {
|
||||
// playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"],
|
||||
//see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , gauntlet, final) added later
|
||||
playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"],
|
||||
communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever"],
|
||||
communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "dojo"],
|
||||
trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"],
|
||||
levels: [],
|
||||
start() {
|
||||
@@ -18,7 +18,7 @@ const level = {
|
||||
// simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
|
||||
// simulation.isHorizontalFlipped = true
|
||||
// tech.giveTech("performance")
|
||||
// level.difficultyIncrease(8 * 4) //30 is near max on hard //60 is near max on why
|
||||
// level.difficultyIncrease(2 * 4) //30 is near max on hard //60 is near max on why
|
||||
// spawn.setSpawnList();
|
||||
// spawn.setSpawnList();
|
||||
// m.maxHealth = m.health = 100
|
||||
@@ -31,26 +31,30 @@ const level = {
|
||||
// m.energy = 0
|
||||
// simulation.molecularMode = 2
|
||||
// m.damage(0.1);
|
||||
// b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
|
||||
// b.giveGuns("shotgun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
|
||||
// b.giveGuns("super balls") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
|
||||
// b.giveGuns("drones") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
|
||||
// b.guns[3].ammo = 100000000
|
||||
// tech.giveTech("iridescence")
|
||||
// tech.giveTech("cherenkov radiation")
|
||||
// tech.giveTech("irradiated nails")
|
||||
// for (let i = 0; i < 6; ++i) tech.giveTech("Lorentz transformation")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("rivet gun")
|
||||
// tech.giveTech("von Neumann probe")
|
||||
// tech.giveTech("path integration")
|
||||
// tech.giveTech("cordyceps")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("sympathetic resonance")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("foam-bot")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot upgrade")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot upgrade")
|
||||
// requestAnimationFrame(() => { for (let i = 0; i < 30; i++) tech.giveTech("laser-bot") });
|
||||
// for (let i = 0; i < 1; i++) tech.giveTech("laser-bot upgrade")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("ternary")
|
||||
// for (let i = 0; i < 3; ++i) tech.giveTech("mechatronics")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("uncertainty principle")
|
||||
// for (let i = 0; i < 1; ++i) tech.giveTech("mechanical resonance")
|
||||
// for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech");
|
||||
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research");
|
||||
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling");
|
||||
// level.subway();
|
||||
// level.testing();
|
||||
// for (let i = 0; i < 2; ++i) spawn.starter(1900, -500, 50)
|
||||
// spawn.sneaker(1900, -500, 25)
|
||||
// spawn.sniper(2000, -450)
|
||||
// spawn.zombie(1000 + 1000 * Math.random(), -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color)
|
||||
// spawn.zombie(-3000, -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color)
|
||||
// for (let i = 0; i < 20; ++i) spawn.starter(1000 + 1000 * Math.random(), -500 + 300 * Math.random())
|
||||
// tech.addJunkTechToPool(2)
|
||||
// tech.tech[322].frequency = 100
|
||||
@@ -81,8 +85,6 @@ const level = {
|
||||
// for (let i = 0; i < 13; i++) level.nextLevel(); //jump to final boss
|
||||
// lore.unlockTesting();
|
||||
// tech.giveTech("tinker"); //show junk tech in experiment mode
|
||||
// simulation.isCheating = false
|
||||
|
||||
// m.storeTech()
|
||||
// powerUps.spawn(m.pos.x, m.pos.y, "entanglement", false);
|
||||
} else {
|
||||
@@ -103,7 +105,6 @@ const level = {
|
||||
simulation.draw.setPaths();
|
||||
b.respawnBots();
|
||||
m.resetHistory();
|
||||
spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
|
||||
|
||||
if (tech.isForeverDrones) {
|
||||
if (tech.isDroneRadioactive) {
|
||||
@@ -160,9 +161,9 @@ const level = {
|
||||
}
|
||||
if (tech.isHealLowHealth) {
|
||||
if (tech.isEnergyHealth) {
|
||||
var len = 3 * (1 - m.energy / m.maxEnergy) //as a percent
|
||||
var len = 4 * (1 - m.energy / m.maxEnergy) //as a percent
|
||||
} else {
|
||||
var len = 3 * (1 - m.health / m.maxHealth) //as a percent
|
||||
var len = 4 * (1 - m.health / m.maxHealth) //as a percent
|
||||
}
|
||||
for (let i = 0; i < len; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "heal", false);
|
||||
}
|
||||
@@ -1279,7 +1280,10 @@ const level = {
|
||||
y: this.position.y + speed
|
||||
}
|
||||
Matter.Body.setPosition(this, position)
|
||||
if (isSetPaths) simulation.draw.setPaths()
|
||||
if (isSetPaths) {
|
||||
simulation.draw.setPaths()
|
||||
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1289,7 +1293,10 @@ const level = {
|
||||
y: this.position.y - speed
|
||||
}
|
||||
Matter.Body.setPosition(this, position)
|
||||
if (isSetPaths) simulation.draw.setPaths()
|
||||
if (isSetPaths) {
|
||||
simulation.draw.setPaths()
|
||||
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1991,6 +1998,7 @@ const level = {
|
||||
// m.addHealth(Infinity)
|
||||
|
||||
spawn.starter(1900, -500, 200) //big boy
|
||||
// spawn.starter(1900, -500, 100) //big boy
|
||||
// for (let i = 0; i < 10; ++i) spawn.launcher(1900, -500)
|
||||
// spawn.suckerBoss(1900, -500)
|
||||
// spawn.launcherBoss(3200, -500)
|
||||
@@ -2584,10 +2592,6 @@ const level = {
|
||||
// simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
|
||||
// level.difficultyIncrease(10 * 4);
|
||||
// m.maxHealth = m.health = 100
|
||||
|
||||
level.isProcedural = true //used in generating text for the level builder
|
||||
simulation.draw.drawMapPath = simulation.draw.drawMapSight
|
||||
|
||||
// color.map = "#333" //custom map color
|
||||
document.body.style.backgroundColor = "#e3e3e3"//"#e3e3e3"//color.map//"#333"//"#000"
|
||||
level.defaultZoom = 1400
|
||||
@@ -2654,8 +2658,10 @@ const level = {
|
||||
gateButton.query();
|
||||
if (!gateButton.isUp) {
|
||||
if (stationNumber > 0) {
|
||||
if (!isExitOpen && gatesOpenRight < stationNumber) level.newLevelOrPhase() //run some new level tech effects
|
||||
gatesOpenRight = stationNumber
|
||||
} else if (stationNumber < 0) {
|
||||
if (!isExitOpen && gatesOpenLeft > stationNumber) level.newLevelOrPhase() //run some new level tech effects
|
||||
gatesOpenLeft = stationNumber
|
||||
} else { //starting station both doors open
|
||||
gatesOpenLeft = stationNumber
|
||||
@@ -3384,6 +3390,8 @@ const level = {
|
||||
Composite.add(engine.world, map[i]); //add to world
|
||||
}
|
||||
simulation.draw.setPaths()
|
||||
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
|
||||
|
||||
|
||||
//shift trains left/right, as you move left or right a train will jump over and become the train needed at the next station
|
||||
let repositionTrain
|
||||
@@ -3437,6 +3445,9 @@ const level = {
|
||||
for (let i = 0; i < train.length; i++) train[i].draw()
|
||||
stationCustomTopLayer()
|
||||
};
|
||||
level.isProcedural = true //only used in generating text for the level builder
|
||||
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
|
||||
simulation.draw.drawMapPath = simulation.draw.drawMapSight
|
||||
},
|
||||
reservoir() {
|
||||
level.exit.x = 1700;
|
||||
@@ -25591,6 +25602,456 @@ const level = {
|
||||
};
|
||||
powerUps.addResearchToLevel(); //needs to run after mobs are spawned
|
||||
},
|
||||
dojo() { // By
|
||||
simulation.makeTextLog(`<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 **********************************************
|
||||
|
||||
@@ -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)
|
||||
|
||||
14
js/player.js
14
js/player.js
@@ -551,7 +551,7 @@ const m = {
|
||||
if (m.fieldMode === 0 || m.fieldMode === 3) dmg *= 0.973 ** m.coupling
|
||||
if (tech.isLowHealthDefense) dmg *= 1 - Math.max(0, 1 - m.health) * 0.8
|
||||
if (tech.isHarmReduceNoKill && m.lastKillCycle + 300 < m.cycle) dmg *= 0.33
|
||||
if (tech.squirrelFx !== 1) dmg *= Math.pow(0.7, (tech.squirrelFx - 1) / 0.4) //cause more damage
|
||||
if (tech.squirrelFx !== 1) dmg *= 0.78//Math.pow(0.78, (tech.squirrelFx - 1) / 0.4)
|
||||
if (tech.isAddBlockMass && m.isHolding) dmg *= 0.15
|
||||
if (tech.isSpeedHarm && player.speed > 0.1) dmg *= 1 - Math.min(player.speed * 0.0165, 0.66)
|
||||
if (tech.isHarmReduce && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.25
|
||||
@@ -845,6 +845,7 @@ const m = {
|
||||
draw() { },
|
||||
isAltSkin: false,
|
||||
resetSkin() {
|
||||
simulation.isAutoZoom = true;
|
||||
m.yOffWhen.jump = 70
|
||||
m.yOffWhen.stand = 49
|
||||
m.yOffWhen.crouch = 22
|
||||
@@ -1352,6 +1353,7 @@ const m = {
|
||||
},
|
||||
dilate() {
|
||||
m.isAltSkin = true
|
||||
simulation.isAutoZoom = false;
|
||||
m.draw = function () {
|
||||
const amplitude = 8 + 4 * Math.sin(m.cycle * 0.0075)
|
||||
ctx.fillStyle = m.fillColor;
|
||||
@@ -1380,6 +1382,13 @@ const m = {
|
||||
ctx.restore();
|
||||
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
|
||||
powerUps.boost.draw()
|
||||
|
||||
//zoom camera in and out
|
||||
|
||||
// console.log(simulation.zoomScale)
|
||||
simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075))
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
dilate2() {
|
||||
@@ -1415,6 +1424,7 @@ const m = {
|
||||
ctx.restore();
|
||||
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
|
||||
powerUps.boost.draw()
|
||||
simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075))
|
||||
}
|
||||
m.drawLeg = function (stroke) {
|
||||
// if (simulation.mouseInGame.x > m.pos.x) {
|
||||
@@ -1903,7 +1913,7 @@ const m = {
|
||||
}
|
||||
},
|
||||
setMaxEnergy(isMessage = true) {
|
||||
m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2 * tech.isGroundState + 3 * tech.isRelay * tech.isFlipFlopOn * tech.isRelayEnergy + 1.5 * (m.fieldMode === 1) + (m.fieldMode === 0 || m.fieldMode === 1) * 0.05 * m.coupling + 0.4 * tech.isStandingWaveExpand
|
||||
m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2.66 * tech.isGroundState + 3 * tech.isRelay * tech.isFlipFlopOn * tech.isRelayEnergy + 1.5 * (m.fieldMode === 1) + (m.fieldMode === 0 || m.fieldMode === 1) * 0.05 * m.coupling + 0.4 * tech.isStandingWaveExpand
|
||||
if (isMessage) simulation.makeTextLog(`<span class='color-var'>m</span>.<span class='color-f'>maxEnergy</span> <span class='color-symbol'>=</span> ${(m.maxEnergy.toFixed(2))}`)
|
||||
},
|
||||
fieldMeterColor: "#0cf",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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];
|
||||
|
||||
120
js/spawn.js
120
js/spawn.js
@@ -17,7 +17,6 @@ const spawn = {
|
||||
} else {
|
||||
spawn[options[Math.floor(Math.random() * options.length)]](x, y)
|
||||
}
|
||||
spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
|
||||
},
|
||||
pickList: ["starter", "starter"],
|
||||
fullPickList: [
|
||||
@@ -49,93 +48,6 @@ const spawn = {
|
||||
spawnChance(chance) {
|
||||
return Math.random() < chance + 0.07 * simulation.difficulty && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1)
|
||||
},
|
||||
quantumEraserCheck() { //remove mobs from tech: quantum eraser
|
||||
if (tech.isQuantumEraser && tech.quantumEraserCount > 0) {
|
||||
|
||||
//start at a random location in array
|
||||
const randomMiddle = Math.floor(mob.length * Math.random())
|
||||
let i = randomMiddle
|
||||
for (let j = 0; j < mob.length; j++) {
|
||||
i++
|
||||
if (i > mob.length - 1) i = 0
|
||||
if (mob[i].isDropPowerUp && mob[i].alive) { //&& !mob[i].isBoss
|
||||
if (mob[i].isFinalBoss) {
|
||||
tech.quantumEraserCount = 0;
|
||||
return
|
||||
} else {
|
||||
tech.isQuantumEraserDuplication = true
|
||||
mob[i].death()
|
||||
tech.isQuantumEraserDuplication = false
|
||||
}
|
||||
//graphics
|
||||
const color = 'rgba(255,255,255, 0.8)'
|
||||
simulation.drawList.push({
|
||||
x: mob[i].position.x,
|
||||
y: mob[i].position.y,
|
||||
radius: mob[i].radius * 2,
|
||||
color: color, //"rgba(0,0,0,0.6)",
|
||||
time: 60
|
||||
});
|
||||
simulation.drawList.push({
|
||||
x: mob[i].position.x,
|
||||
y: mob[i].position.y,
|
||||
radius: mob[i].radius * 1,
|
||||
color: color, //"rgba(0,0,0,0.85)",
|
||||
time: 90
|
||||
});
|
||||
simulation.drawList.push({
|
||||
x: mob[i].position.x,
|
||||
y: mob[i].position.y,
|
||||
radius: mob[i].radius * 0.5,
|
||||
color: color, //"rgb(0,0,0)",
|
||||
time: 120
|
||||
});
|
||||
tech.quantumEraserCount--
|
||||
simulation.makeTextLog(`<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,
|
||||
|
||||
190
js/tech.js
190
js/tech.js
@@ -252,7 +252,7 @@ const tech = {
|
||||
if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.007
|
||||
if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2
|
||||
if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.66, player.speed * 0.0165)
|
||||
if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.6
|
||||
if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.83
|
||||
if (tech.isAxion && tech.isHarmMACHO) dmg *= 2 - m.defense()
|
||||
if (tech.isHarmDamage && m.lastHarmCycle + 480 > m.cycle) dmg *= 3;
|
||||
if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit * (2 - m.defense()) // if (!simulation.paused) m.lastHit = 0
|
||||
@@ -260,7 +260,7 @@ const tech = {
|
||||
return dmg
|
||||
},
|
||||
duplicationChance() {
|
||||
return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.15 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0) + tech.isQuantumEraserDuplication * (1 - 0.016 * (simulation.difficultyMode ** 2))))
|
||||
return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.17 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0)))
|
||||
},
|
||||
isScaleMobsWithDuplication: false,
|
||||
maxDuplicationEvent() {
|
||||
@@ -337,8 +337,8 @@ const tech = {
|
||||
},
|
||||
{
|
||||
name: "nitinol",
|
||||
description: "<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,
|
||||
|
||||
Reference in New Issue
Block a user