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
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 57 KiB |
BIN
img/self-assembly.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
img/sound-bot upgrade.webp
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
img/sound-bot.webp
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
img/von Neumann probe.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
215
todo.txt
@@ -1,33 +1,77 @@
|
||||
******************************************************** NEXT PATCH **************************************************
|
||||
|
||||
subway
|
||||
start in an empty station
|
||||
station exits are blocked by a gate that opens when you press a button
|
||||
level exit spawns after you clear 4 stations
|
||||
added 2 new stations (7 possible stations)
|
||||
new community level: dojo by weird_pusheen
|
||||
|
||||
laser-bots lasers wiggle as it aims at the target
|
||||
this is a nerf to iridescence and it looks cool
|
||||
sound-bot: makes phonon waves
|
||||
|
||||
updated tech
|
||||
drone repair -> von Neumann probe - if a drones ends near a block it will use that block to repair
|
||||
quantum eraser -> metamaterial absorber: for each mob left alive after you exit a level there is a 17% chance to spawn a power up
|
||||
uncertainty principle: extended to super balls
|
||||
aperture: camera zooms in and out along with the skin animation
|
||||
|
||||
balanced tech:
|
||||
cordyceps: zombies are much smarter, faster, do more damage, and last longer
|
||||
negative entropy -> self-assembly: 1 heal per 33->25% missing health on each new level
|
||||
renormalization: 44 -> 46% and +3% JUNK tech to pool
|
||||
parasitism: 60 -> 83% damage
|
||||
non-renewables: 67 -> 78% damage
|
||||
ground state: 200 -> 266 max energy
|
||||
dark patterns: 17 -> 22% damage and JUNK
|
||||
eternalism: 30 -> 24% damage
|
||||
stimulated emission 15 -> 17% duplication
|
||||
nitinol 30 -> 22% defense
|
||||
|
||||
bug fixes
|
||||
|
||||
*********************************************************** TODO *****************************************************
|
||||
|
||||
only calculate setPaths on subway level?
|
||||
does this mess with flatland, no
|
||||
make a mob similar to slasher
|
||||
because it's just a very well made mob.
|
||||
|
||||
sound-bot: makes phonon waves
|
||||
works with
|
||||
other bot tech - done?
|
||||
phonon - no
|
||||
frequency - no
|
||||
bound state - no
|
||||
phase velocity - no
|
||||
isotropic - no
|
||||
maybe, but reduce fire rate?
|
||||
|
||||
uncertainty principle - done
|
||||
propagation - done
|
||||
amplitude - done
|
||||
sympathetic resonance - done
|
||||
mechanical resonance - done
|
||||
|
||||
remove reflection from phonon?
|
||||
but return it with isotropic?
|
||||
|
||||
super-bot: fires super balls
|
||||
|
||||
tech - only allow 1,2 turrets at time. spawning a new mine removes the oldest mine
|
||||
turrets never run out of ammo
|
||||
or turrets automatically use one of your mine ammos when they run out?
|
||||
good with multi gun builds
|
||||
conflict with booby trap?
|
||||
|
||||
tech: after a needle hits a mobs
|
||||
the needle splits into 3 needles?
|
||||
reset your fire CD?
|
||||
2x damage for each consecutive mob hit?
|
||||
|
||||
tech: mass production - add a few selection options to all tech, gun, fields to do something:
|
||||
this would work similar to the tech that adds a bot themed tech to every tech choice
|
||||
options
|
||||
spawn: ammo, heals, research, coupling?
|
||||
random amounts?
|
||||
make each option a full tech with images?
|
||||
|
||||
|
||||
|
||||
improve flatland performance?
|
||||
|
||||
get rid of the word permanent in bot tech
|
||||
ersatz bots, perimeter defense, network effect
|
||||
|
||||
maybe reduce the fps on the line of sight graphics to make it look more like a sensor?
|
||||
make a bot the follows the player the pov for line of sight levels, not the player
|
||||
also need to make the vision a slice of a circle, not a full circle
|
||||
|
||||
aperture also increases and decreases vision range
|
||||
I think messing with vision range causes problems with the start of level vision code
|
||||
|
||||
mob non-combat behaviors, like Rain World
|
||||
gathering
|
||||
blocks
|
||||
@@ -47,31 +91,6 @@ consider increasing the base player horizontal movement
|
||||
maybe only increase ground movement, air control seems fine
|
||||
would this unbalance any maps?
|
||||
|
||||
level: subway - a map that uses the train level element and line of sight graphics
|
||||
stations
|
||||
station theme ideas:
|
||||
portals
|
||||
teleport to far away rooms
|
||||
map elements that alternate between positions
|
||||
buttons and doors
|
||||
maybe add mob mobs to each station? this makes it faster to clear the level
|
||||
boss
|
||||
spawn at the exit station? or at a random station? or at the station before the exit?
|
||||
spawn on the station after enough mobs have been killed?
|
||||
type of boss?
|
||||
might need to make a new boss designed for this map: los and stations
|
||||
small, quick, sneaky
|
||||
do random bosses work?
|
||||
I think
|
||||
looks good with line of sight
|
||||
background lighting for each room drawn in level.custom
|
||||
no outdoors, no fall off the edge
|
||||
slime
|
||||
no small bumps
|
||||
starting in a small room with a hole to the right with a short drop, like highrise or aerie
|
||||
floating hexagons, like in reservoir, labs
|
||||
ramping walls to jump over, like satellite
|
||||
|
||||
tech stubs should be a tech unlocked by skins
|
||||
nitinol, tungsten?
|
||||
maybe give another benefit?
|
||||
@@ -82,18 +101,7 @@ make a lemming that walks until it hits a wall and then turns around robotically
|
||||
body or mob?
|
||||
can't be killed?
|
||||
|
||||
|
||||
Also another thing I made that could fit in-game: https://kgurchiek.github.io/universal-n-gon-loader/
|
||||
by default it just plays a random version of n-gon downloaded from past github commits
|
||||
maybe the "snapshots" could work like this rather than downloading 8 versions of the game?
|
||||
also you can play any version with https://kgurchiek.github.io/universal-n-gon-loader/?commitIndex=NUM
|
||||
where setting "NUM" to 0 is the very first commit
|
||||
here's the code if you want to check it out: https://github.com/kgurchiek/universal-n-gon-loader/blob/main/script.js
|
||||
|
||||
missile bot and plasma bot don't get converted by bot upgrade tech?
|
||||
is this more confusing because it contradicts text?
|
||||
|
||||
use ephemera to replace things
|
||||
use ephemera to replace some bad code
|
||||
JUNK?
|
||||
request animation stuff
|
||||
simulation checks
|
||||
@@ -111,8 +119,6 @@ mobs attack mines
|
||||
mines periodically set all mobs to have player location to be the mine
|
||||
is this going to work with all mob vision types?
|
||||
|
||||
rework quantum eraser
|
||||
|
||||
tech circular polarization - wave gun bullets move in a circle
|
||||
|
||||
tech: choose next map by name after exiting current map
|
||||
@@ -124,8 +130,6 @@ Tech: relativity
|
||||
Simulation speed scales with movement speed. When still, time moves at 0.4 speed, at full walking speed it’s 1. (So if you’re falling or something and you move faster the simulation will be faster than usual)
|
||||
Also a damage and/or defense boost to make it worth using
|
||||
|
||||
Tech: Turbine - Energy generation is proportional to your speed up to +X% energy generation at 40 speed
|
||||
|
||||
wormhole tech - teleport away mobs with mass below 3 when they get too near the player
|
||||
short CD, small energy cost, only mobs below a mass
|
||||
|
||||
@@ -133,75 +137,12 @@ extend brainstorming animation timers to fps cap?
|
||||
will it be smoother or choppier?
|
||||
anything else needs to hit limited fps on a high fps monitor?
|
||||
|
||||
level element - mover, transport
|
||||
test effect of changing m.Vx on things like: shooting bullets?
|
||||
extend the recentered friction zero to other things
|
||||
like blocks the player stands on?
|
||||
maybe zero on the horizontal velocity of whatever the player is standing on
|
||||
|
||||
extend uncertainty to superballs
|
||||
maybe make aiming them more random?
|
||||
|
||||
perfect diamagnatism could bounce on mobs, or even map elements?
|
||||
could work like a rocket jump?
|
||||
|
||||
Tech: Von Neuman probes - Drones will consume blocks to replicate themselves
|
||||
it's a little too similar to the drone repair tech, but I kinda like it better. drones that eat blocks and spit out more drones is cool
|
||||
|
||||
tech: parry - immune to harm for 0.25-0.5 seconds after pressing field button
|
||||
needs a 5 second CD?
|
||||
|
||||
tech: if a needle hits 2 mobs reset your fire CD
|
||||
maybe to 2x damage for each consecutive mob hit?
|
||||
maybe after a needle hits a mob the needle splits into 3 needles
|
||||
|
||||
tech for lens - you can only fire through the lens and some buff? damage or energy?
|
||||
this was in todo.txt on GitHub. I think it should be 'laser never drains energy, but you can only fire through lens and +90° lens arc, +100% damage (also you can not gain compound lens with this upgrade)
|
||||
|
||||
new boss level like reactor with a very very big boss
|
||||
mechanics around a very big boss?
|
||||
maybe the boss moves into rooms so you have to do platforming to clear the room before the boss enters the room
|
||||
boss can destroy blocks and smaller map elements
|
||||
|
||||
tech - after standing wave runs out of energy from blocking, gain a buff
|
||||
buff: defense, damage?
|
||||
aoe damage like railgun
|
||||
push mobs away
|
||||
|
||||
level: lock
|
||||
should there be something in the top part of the map?
|
||||
add alt versions of left and right sides
|
||||
make flipped L/R version (after everything else is done)
|
||||
|
||||
tech: add an selection option to all tech, gun, fields to do something
|
||||
set all mobs to 30% health, and stun all mobs
|
||||
50% chance to convert all power ups into research
|
||||
heal to full
|
||||
|
||||
tech: if you die inside MACHO, heal to full and delete the MACHO for the rest of the Level
|
||||
MACHO gives less defense
|
||||
|
||||
tech: after bosses die<br>they spawn a research
|
||||
|
||||
tech: +8% damage each time you kill a boss
|
||||
|
||||
tech: sticky grenades
|
||||
needs another effect to be good enough
|
||||
stick to mobs?
|
||||
|
||||
make a mob similar to slasher
|
||||
because it's just a very well made mob.
|
||||
|
||||
tech - only allow 1,2 turrets at time?
|
||||
turrets never run out of ammo
|
||||
or turrets automatically use one of your mine ammos when they run out?
|
||||
good with multi gun builds
|
||||
conflict with booby trap?
|
||||
|
||||
tech: Bose Einstein condensate - freezes enemies in pilot wave, and drains some energy?
|
||||
|
||||
super-bot
|
||||
|
||||
make super balls with Zectron deflectable with field
|
||||
but is there a simple way to do this?
|
||||
|
||||
@@ -234,28 +175,13 @@ tech: sporangium that grow little trees
|
||||
the trees have an area of effect damage for about 6-10 seconds
|
||||
maybe something similar to radioactive drones, but maybe a few smaller shapes
|
||||
|
||||
new bot type that makes phonon waves
|
||||
name: phono-bot?
|
||||
each bot has to generate it themselves, can't run code in gun.do
|
||||
synergy with 2 resonance tech
|
||||
not isotropic? I think no
|
||||
synergy with bound? phase velocity, amplitude, propagation
|
||||
|
||||
harpoon tech that makes auto aim work much better
|
||||
|
||||
tech - super balls gain 20 seconds of time and are reset to original launch speed after hitting a mob
|
||||
|
||||
railgun
|
||||
magnetic pinch: harpoon does damage to nearby mobs
|
||||
draw charge graphic on harpoon
|
||||
use same code as the damage when fire effect
|
||||
|
||||
hookBoss fires a hook that pulls player towards it
|
||||
hook does a bit of damage
|
||||
player targeted unless cloaking
|
||||
also add effect to finalBoss
|
||||
|
||||
|
||||
finalBoss
|
||||
add synergies between modes:
|
||||
new modes:
|
||||
@@ -1219,7 +1145,7 @@ possible names for tech
|
||||
hypergraph
|
||||
SQUID (for superconducting quantum interference device) is a very sensitive magnetometer used to measure extremely subtle magnetic fields, based on superconducting loops containing Josephson junctions.
|
||||
nuclear pasta - hard matter in neutron star
|
||||
nonlocal
|
||||
nonlocal: maybe use for pilot wave
|
||||
fine-tuned universe
|
||||
nonperturbative
|
||||
D-branes
|
||||
@@ -1241,6 +1167,9 @@ possible names for tech
|
||||
https://en.wikipedia.org/wiki/Cosmic_censorship_hypothesis - black holes can't leak
|
||||
Alcubierre warp drive (FTL with negative mass)
|
||||
Spherules - A spherule is a small sphere or spherical body. It can also refer to a thick-walled spherical structure that contains endospores and occurs in the parasitic form of fungi
|
||||
negative entropy
|
||||
memetics
|
||||
magnetorquers - produce spin by pushing on earth's magnetic field
|
||||
|
||||
******************************************************** CARS IMAGES ********************************************************
|
||||
|
||||
@@ -1264,8 +1193,7 @@ if pause is pressed while selecting power ups, display pause menu on top of sele
|
||||
laser
|
||||
supercritical fission
|
||||
***past style themes***
|
||||
field emitter - bipedal white robot spherical gun turret on bird legs
|
||||
damaged dirty white robot spherical gun turret on bird legs in the style of Solarpunk
|
||||
base prompt for player on 5.2: clean white robot spherical turret on bird legs test chamber
|
||||
standing wave - a 3-D cyan transparent nested concentric aligned centered sphere with rings
|
||||
by Philippe Starck
|
||||
perfect diamagnetism - physics magnetic field chalk diagram
|
||||
@@ -1276,13 +1204,13 @@ if pause is pressed while selecting power ups, display pause menu on top of sele
|
||||
metamaterial cloaking - Scientific photography by Miki Asai, by Bruce Munro
|
||||
molecular assembler - by Laurie Greasley 16-bit Isometric
|
||||
wormhole - by Tim White
|
||||
pilot wave -
|
||||
pilot wave - none
|
||||
|
||||
nail gun - Screenprint
|
||||
shotgun - blueprint by Dan McPharlin
|
||||
grenades, missiles, explosions - vibrant fireball explosion sonic shockwave ring art by Victo Ngai --ar 3:2 --v 5 --s 750
|
||||
spores - turquoise black spores on a white background full color scientific anatomy by Ernst Haeckel
|
||||
drones - tilt-shift photography
|
||||
drones - insect quadcopter tilt-shift photography
|
||||
super balls - By Akari Toriyama
|
||||
wave - sound wave oscilloscope by Paul Catherall, concentric circles by Paul Catherall
|
||||
Barbara Takenaga's painting depicting a clean sound wave on aoscilloscope device --ar 3:2 --v 5
|
||||
@@ -1306,7 +1234,6 @@ if pause is pressed while selecting power ups, display pause menu on top of sele
|
||||
invulnerable - by Nick Veasey (photos that look like x-rays)
|
||||
alternate reality - Fractal art
|
||||
tech choice - mandala tile Mosaic
|
||||
tech that spawns heal power ups - green Quilling
|
||||
time, CPT, pause - by Lee Bontecou
|
||||
boost, coupling power ups tech - cyan electron orbiting a black nucleus electric field as bas-relief //(by Kazumasa Nagai)
|
||||
radioactive - volumetric atomic nucleus diagram by Paul Catherall
|
||||
|
||||