sound-bot

new community level: dojo by weird_pusheen

sound-bot: makes phonon waves

  updated tech
drone repair -> von Neumann probe - if a drones ends near a block it will use that block to repair
quantum eraser -> metamaterial absorber: for each mob left alive after you exit a level there is a 17% chance to spawn a power up
uncertainty principle: extended to super balls
aperture: camera zooms in and out along with the skin animation

  balanced tech:
cordyceps: zombies are much smarter, faster, do more damage, and last longer
negative entropy -> self-assembly: 1 heal per 33->25% missing health on each new level
renormalization: 44 -> 46% and +3% JUNK tech to pool
parasitism: 60 -> 83% damage
non-renewables: 67 -> 78% damage
ground state: 200 -> 266 max energy
dark patterns: 17 -> 22% damage and JUNK
eternalism: 30 -> 24% damage
stimulated emission 15 -> 17% duplication
nitinol 30 -> 22% defense

bug fixes
This commit is contained in:
landgreen
2023-07-04 08:14:43 -07:00
parent 4415942b94
commit 6cd2502fb5
18 changed files with 1208 additions and 604 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

BIN
img/self-assembly.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
img/sound-bot upgrade.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
img/sound-bot.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
img/von Neumann probe.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -3648,6 +3648,31 @@ const b = {
bullet[bullet.length - 1].isMutualismActive = true
}
},
delayDrones(where, droneCount = 1) {
let respawnDrones = () => {
if (droneCount > 0) {
requestAnimationFrame(respawnDrones);
if (!simulation.paused && !simulation.isChoosing && m.alive) {
droneCount--
if (tech.isDroneRadioactive) {
b.droneRadioactive({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0)
} else {
b.drone({ x: where.x + 50 * (Math.random() - 0.5), y: where.y + 50 * (Math.random() - 0.5) }, 0)
if (tech.isDroneGrab && deliveryCount > 0) {
const who = bullet[bullet.length - 1]
who.isImproved = true;
const SCALE = 2.25
Matter.Body.scale(who, SCALE, SCALE);
who.lookFrequency = 30 + Math.floor(11 * Math.random());
who.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
deliveryCount--
}
}
}
}
}
requestAnimationFrame(respawnDrones);
},
drone(where = {
x: m.pos.x + 30 * Math.cos(m.angle) + 20 * (Math.random() - 0.5),
y: m.pos.y + 30 * Math.sin(m.angle) + 20 * (Math.random() - 0.5)
@@ -3693,10 +3718,7 @@ const b = {
} else {
//move away from target after hitting
const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20)
Matter.Body.setVelocity(this, {
x: unit.x,
y: unit.y
});
Matter.Body.setVelocity(this, { x: unit.x, y: unit.y });
this.lockedOn = null
if (this.endCycle > simulation.cycle + this.deathCycles) {
this.endCycle -= 60
@@ -3705,68 +3727,164 @@ const b = {
}
},
onEnd() {
if (tech.isDroneRespawn && b.inventory.length && b.activeGun === 7 && b.guns[b.activeGun].ammo > 0 && mob.length) {
b.drone({
x: this.position.x,
y: this.position.y
}, 0)
if (Math.random() < 0.2) {
b.guns[b.activeGun].ammo--;
simulation.updateGunHUD();
if (tech.isDroneRespawn) {
//are there any nearby bodies nearby that aren't blocked by map?
const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable && Vector.magnitude(Vector.sub(this.position, a.position)) < 70 + 30 * a.mass)
if (canSee.length) {
//find the closest body to the drone from the canSee array
const found = canSee.reduce((a, b) => {
const distA = Vector.magnitude(Vector.sub(this.position, a.position))
const distB = Vector.magnitude(Vector.sub(this.position, b.position))
return distA < distB ? a : b
})
if (found && m.energy > 0.05) {
m.energy -= 0.05
//remove the body and spawn a new drone
Composite.remove(engine.world, found)
body.splice(body.indexOf(found), 1)
b.delayDrones(found.position, 0.7 * Math.sqrt(found.mass))
//draw a line from the drone to the body on the canvas
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(found.position.x, found.position.y);
ctx.strokeStyle = "#000";
ctx.lineWidth = 2;
ctx.stroke();
//animate the block fading away
simulation.ephemera.push({
name: "droneRespawn",
count: 60, //cycles before it self removes
do() {
this.count--
if (this.count < 0) simulation.removeEphemera(this.name)
ctx.beginPath();
let vertices = found.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 2;
ctx.strokeStyle = `rgba(0,0,0,${this.count / 60})`
ctx.stroke();
},
})
}
}
}
},
do() {
if (simulation.cycle + this.deathCycles > this.endCycle) { //fall shrink and die
this.force.y += this.mass * 0.0012;
this.restitution = 0.2;
const scale = 0.995;
Matter.Body.scale(this, scale, scale);
doRespawning() { //fall shrink and die
const scale = 0.995;
Matter.Body.scale(this, scale, scale);
if (this.bodyTarget) {
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.bodyTarget.position)), -this.mass * THRUST)
} else {
this.force.y += this.mass * 0.0002;
if (!(simulation.cycle % this.lookFrequency)) {
//find mob targets
this.lockedOn = null;
let closeDist = Infinity;
for (let i = 0, len = mob.length; i < len; ++i) {
if (
!mob[i].isBadTarget &&
Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
Matter.Query.ray(body, this.position, mob[i].position).length === 0 &&
!mob[i].isInvulnerable
) {
const TARGET_VECTOR = Vector.sub(this.position, mob[i].position)
const DIST = Vector.magnitude(TARGET_VECTOR);
if (DIST < closeDist) {
closeDist = DIST;
this.lockedOn = mob[i]
}
this.force.y += this.mass * 0.0012;
}
},
doDieing() { //fall shrink and die
this.force.y += this.mass * 0.0012;
const scale = 0.995;
Matter.Body.scale(this, scale, scale);
},
do() {
if (simulation.cycle + this.deathCycles > this.endCycle) {
this.restitution = 0.2;
if (tech.isDroneRespawn) {
this.do = this.doRespawning
//make a list of all elements of array body that a ray can be drawn to from the drone
const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable)
if (canSee.length) {
//find the closest body to the drone from the canSee array
const found = canSee.reduce((a, b) => {
const distA = Vector.magnitude(Vector.sub(this.position, a.position))
const distB = Vector.magnitude(Vector.sub(this.position, b.position))
return distA < distB ? a : b
})
if (found) this.bodyTarget = found
}
} else {
this.do = this.doDieing
}
}
this.force.y += this.mass * 0.0002;
if (!(simulation.cycle % this.lookFrequency)) {
//find mob targets
this.lockedOn = null;
let closeDist = Infinity;
for (let i = 0, len = mob.length; i < len; ++i) {
if (
!mob[i].isBadTarget &&
Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
Matter.Query.ray(body, this.position, mob[i].position).length === 0 &&
!mob[i].isInvulnerable
) {
const TARGET_VECTOR = Vector.sub(this.position, mob[i].position)
const DIST = Vector.magnitude(TARGET_VECTOR);
if (DIST < closeDist) {
closeDist = DIST;
this.lockedOn = mob[i]
}
}
//blink towards mobs
if (tech.isDroneTeleport && this.lockedOn) {
const sub = Vector.sub(this.lockedOn.position, this.position);
const distMag = Vector.magnitude(sub);
const unit = Vector.normalise(sub)
Matter.Body.setVelocity(this, Vector.mult(unit, Math.max(20, this.speed * 1.5)));
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
Matter.Body.translate(this, Vector.mult(unit, Math.min(350, distMag - this.lockedOn.radius + 10)));
ctx.lineTo(this.position.x, this.position.y);
ctx.lineWidth = RADIUS * 2;
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.stroke();
}
//power ups
if (!this.isImproved && !simulation.isChoosing) {
if (this.lockedOn) {
for (let i = 0, len = powerUp.length; i < len; ++i) { //grab, but don't lock onto nearby power up
if (
Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 &&
(powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
(powerUp[i].name !== "field" || !tech.isSuperDeterminism)
// &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
) {
}
//blink towards mobs
if (tech.isDroneTeleport && this.lockedOn) {
const sub = Vector.sub(this.lockedOn.position, this.position);
const distMag = Vector.magnitude(sub);
const unit = Vector.normalise(sub)
Matter.Body.setVelocity(this, Vector.mult(unit, Math.max(20, this.speed * 1.5)));
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
Matter.Body.translate(this, Vector.mult(unit, Math.min(350, distMag - this.lockedOn.radius + 10)));
ctx.lineTo(this.position.x, this.position.y);
ctx.lineWidth = RADIUS * 2;
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.stroke();
}
//power ups
if (!this.isImproved && !simulation.isChoosing) {
if (this.lockedOn) {
for (let i = 0, len = powerUp.length; i < len; ++i) { //grab, but don't lock onto nearby power up
if (
Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 &&
(powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
(powerUp[i].name !== "field" || !tech.isSuperDeterminism)
// &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
) {
//draw pickup for a single cycle
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y);
ctx.strokeStyle = "#000"
ctx.lineWidth = 4
ctx.stroke();
//pick up nearby power ups
powerUps.onPickUp(powerUp[i]);
powerUp[i].effect();
Matter.Composite.remove(engine.world, powerUp[i]);
powerUp.splice(i, 1);
if (tech.isDroneGrab) {
this.isImproved = true;
const SCALE = 2.25
Matter.Body.scale(this, SCALE, SCALE);
this.lookFrequency = 30 + Math.floor(11 * Math.random());
this.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
}
break;
}
}
} else {
//look for power ups to lock onto
let closeDist = Infinity;
for (let i = 0, len = powerUp.length; i < len; ++i) {
if (
(powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
(powerUp[i].name !== "field" || !tech.isSuperDeterminism)
// &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
) {
if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) {
//draw pickup for a single cycle
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
@@ -3785,71 +3903,38 @@ const b = {
Matter.Body.scale(this, SCALE, SCALE);
this.lookFrequency = 30 + Math.floor(11 * Math.random());
this.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
// this.frictionAir = 0
}
break;
}
}
} else {
//look for power ups to lock onto
let closeDist = Infinity;
for (let i = 0, len = powerUp.length; i < len; ++i) {
//look for power ups to lock onto
if (
(powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) &&
(powerUp[i].name !== "field" || !tech.isSuperDeterminism)
// &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab)
Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 &&
Matter.Query.ray(body, this.position, powerUp[i].position).length === 0
) {
if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) {
//draw pickup for a single cycle
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(powerUp[i].position.x, powerUp[i].position.y);
ctx.strokeStyle = "#000"
ctx.lineWidth = 4
ctx.stroke();
//pick up nearby power ups
powerUps.onPickUp(powerUp[i]);
powerUp[i].effect();
Matter.Composite.remove(engine.world, powerUp[i]);
powerUp.splice(i, 1);
if (tech.isDroneGrab) {
this.isImproved = true;
const SCALE = 2.25
Matter.Body.scale(this, SCALE, SCALE);
this.lookFrequency = 30 + Math.floor(11 * Math.random());
this.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
// this.frictionAir = 0
}
break;
}
//look for power ups to lock onto
if (
Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 &&
Matter.Query.ray(body, this.position, powerUp[i].position).length === 0
) {
const TARGET_VECTOR = Vector.sub(this.position, powerUp[i].position)
const DIST = Vector.magnitude(TARGET_VECTOR);
if (DIST < closeDist) {
closeDist = DIST;
this.lockedOn = powerUp[i]
}
const TARGET_VECTOR = Vector.sub(this.position, powerUp[i].position)
const DIST = Vector.magnitude(TARGET_VECTOR);
if (DIST < closeDist) {
closeDist = DIST;
this.lockedOn = powerUp[i]
}
}
}
}
}
}
if (this.lockedOn) { //accelerate towards mobs
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST)
} else { //accelerate towards mouse
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, simulation.mouseInGame)), -this.mass * THRUST)
}
// speed cap instead of friction to give more agility
if (this.speed > 6) {
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.97,
y: this.velocity.y * 0.97
});
}
}
if (this.lockedOn) { //accelerate towards mobs
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST)
} else { //accelerate towards mouse
this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, simulation.mouseInGame)), -this.mass * THRUST)
}
// speed cap instead of friction to give more agility
if (this.speed > 6) {
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.97,
y: this.velocity.y * 0.97
});
}
}
})
@@ -3891,14 +3976,50 @@ const b = {
maxRadioRadius: 270 + Math.floor(90 * Math.random()),
beforeDmg() { },
onEnd() {
if (tech.isDroneRespawn && b.inventory.length && b.activeGun === 7 && b.guns[b.activeGun].ammo > 0 && mob.length) {
b.droneRadioactive({
x: this.position.x,
y: this.position.y
}, 0)
if (Math.random() < 0.2) {
b.guns[b.activeGun].ammo--;
simulation.updateGunHUD();
if (tech.isDroneRespawn) {
//are there any nearby bodies nearby that aren't blocked by map?
const canSee = body.filter(a => Matter.Query.ray(map, this.position, a.position).length === 0 && !a.isNotHoldable && Vector.magnitude(Vector.sub(this.position, a.position)) < 70 + 30 * a.mass)
if (canSee.length) {
//find the closest body to the drone from the canSee array
const found = canSee.reduce((a, b) => {
const distA = Vector.magnitude(Vector.sub(this.position, a.position))
const distB = Vector.magnitude(Vector.sub(this.position, b.position))
return distA < distB ? a : b
})
if (found && m.energy > 0.05) {
m.energy -= 0.1
//remove the body and spawn a new drone
Composite.remove(engine.world, found)
body.splice(body.indexOf(found), 1)
b.delayDrones(found.position, 0.35 * Math.sqrt(found.mass))
//draw a line from the drone to the body on the canvas
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(found.position.x, found.position.y);
ctx.strokeStyle = "#000";
ctx.lineWidth = 2;
ctx.stroke();
//animate the block fading away
simulation.ephemera.push({
name: "droneRespawn",
count: 60, //cycles before it self removes
do() {
this.count--
if (this.count < 0) simulation.removeEphemera(this.name)
ctx.beginPath();
let vertices = found.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 2;
ctx.strokeStyle = `rgba(0,0,0,${this.count / 60})`
ctx.stroke();
},
})
}
}
}
},
@@ -4074,7 +4195,8 @@ const b = {
bullet[me] = Bodies.polygon(where.x, where.y, 12, radius, b.fireAttributes(dir, false));
Composite.add(engine.world, bullet[me]); //add bullet to world
Matter.Body.setVelocity(bullet[me], velocity);
Matter.Body.setDensity(bullet[me], 0.0007 + 0.00055 * tech.isSuperHarm);
bullet[me].calcDensity = () => 0.0007 + 0.00055 * tech.isSuperHarm + 0.0004 * tech.isBulletTeleport
Matter.Body.setDensity(bullet[me], bullet[me].calcDensity());
bullet[me].endCycle = simulation.cycle + Math.floor(270 + 90 * Math.random());
bullet[me].minDmgSpeed = 0;
bullet[me].restitution = 1;
@@ -4086,18 +4208,7 @@ const b = {
this.force.y += this.mass * 0.001;
if (Matter.Query.collides(this, [player]).length) {
this.endCycle = 0
// let dmg = 0.015 * this.mass * tech.isSuperHarm
// m.damage(dmg);
const drain = m.energy * 0.25
m.energy -= drain
// simulation.drawList.push({ //add dmg to draw queue
// x: this.position.x,
// y: this.position.y,
// radius: radius * 3,
// color: "hsla(194, 100%, 43%,0.2)",
// time: 7
// });
m.energy -= m.energy * 0.25
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
@@ -4113,6 +4224,18 @@ const b = {
if (this.cycle > 2) this.do = this.collidePlayerDo
this.force.y += this.mass * 0.001;
};
} else if (tech.isBulletTeleport) {
bullet[me].portFrequency = 25 + Math.floor(10 * Math.random())
bullet[me].nextPortCycle = simulation.cycle + bullet[me].portFrequency
bullet[me].do = function () {
this.force.y += this.mass * 0.001;
if (this.nextPortCycle < simulation.cycle) { //teleport around if you have tech.isBulletTeleport
this.nextPortCycle = simulation.cycle + this.portFrequency
const range = 33 * Math.sqrt(radius) * Math.random()
Matter.Body.setPosition(this, Vector.add(this.position, Vector.rotate({ x: range, y: 0 }, 2 * Math.PI * Math.random())))
Matter.Body.setVelocity(this, Vector.rotate(this.velocity, 2 * (Math.random() * Math.random() - 0.25)))
}
};
} else {
bullet[me].do = function () {
this.force.y += this.mass * 0.001;
@@ -4133,7 +4256,7 @@ const b = {
this.endCycle = 0
} else if (tech.isSuperBounce) {
const cycle = () => {
Matter.Body.setDensity(this, (0.0008 + 0.0009 * tech.isSuperHarm) * 1.33); //50% more density and damage
Matter.Body.setDensity(bullet[me], bullet[me].calcDensity() * 1.33);//33% more density and damage
this.endCycle = simulation.cycle + Math.floor(300 + 90 * Math.random()); //reset to full duration of time
Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(this.velocity), 60)); //reset to high velocity
@@ -4848,7 +4971,7 @@ const b = {
// **************************************************************************************************
// **************************************************************************************************
totalBots() {
return tech.dynamoBotCount + tech.foamBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount
return tech.dynamoBotCount + tech.foamBotCount + tech.soundBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount
},
hasBotUpgrade() {
return tech.isNailBotUpgrade + tech.isFoamBotUpgrade + tech.isBoomBotUpgrade + tech.isLaserBotUpgrade + tech.isOrbitBotUpgrade + tech.isDynamoBotUpgrade
@@ -4898,6 +5021,10 @@ const b = {
tech.foamBotCount--
return
}
if (tech.soundBotCount > 1) {
tech.soundBotCount--
return
}
if (tech.boomBotCount > 1) {
tech.boomBotCount--
return
@@ -4924,6 +5051,7 @@ const b = {
tech.laserBotCount = 0
tech.nailBotCount = 0
tech.foamBotCount = 0
tech.soundBotCount = 0
tech.boomBotCount = 0
tech.orbitBotCount = 0
tech.missileBotCount = 0
@@ -4945,6 +5073,10 @@ const b = {
x: player.position.x + 50 * (Math.random() - 0.5),
y: player.position.y + 50 * (Math.random() - 0.5)
}, false)
for (let i = 0; i < tech.soundBotCount; i++) b.soundBot({
x: player.position.x + 50 * (Math.random() - 0.5),
y: player.position.y + 50 * (Math.random() - 0.5)
}, false)
for (let i = 0; i < tech.boomBotCount; i++) b.boomBot({
x: player.position.x + 50 * (Math.random() - 0.5),
y: player.position.y + 50 * (Math.random() - 0.5)
@@ -4968,13 +5100,16 @@ const b = {
}
},
randomBot(where = player.position, isKeep = true, isLaser = true) {
if (Math.random() < 0.5) { //chance to match scrap bot to your upgrade
if (Math.random() < 0.5) { //chance to match bot to your upgrade
if (tech.isNailBotUpgrade) { //check for upgrades first
b.nailBot(where, isKeep)
if (isKeep) tech.nailBotCount++;
} else if (tech.isFoamBotUpgrade) {
b.foamBot(where, isKeep)
if (isKeep) tech.foamBotCount++;
} else if (tech.isSoundBotUpgrade) {
b.soundBot(where, isKeep)
if (isKeep) tech.soundBotCount++;
} else if (tech.isBoomBotUpgrade) {
b.boomBot(where, isKeep)
if (isKeep) tech.boomBotCount++;
@@ -4987,7 +5122,10 @@ const b = {
} else if (tech.isDynamoBotUpgrade) {
b.dynamoBot(where, isKeep)
if (isKeep) tech.dynamoBotCount++;
} else if (Math.random() < 0.166 && isLaser) { //random
} else if (Math.random() < 0.143) { //random
b.soundBot(where, isKeep)
if (isKeep) tech.soundBotCount++;
} else if (Math.random() < 0.166 && isLaser) {
b.laserBot(where, isKeep)
if (isKeep) tech.laserBotCount++;
} else if (Math.random() < 0.2) {
@@ -5006,8 +5144,11 @@ const b = {
b.boomBot(where, isKeep)
if (isKeep) tech.boomBotCount++;
}
} else { //else don't match scrap bot to upgrade
if (Math.random() < 0.166 && isLaser) { //random
} else { //else don't match bot to upgrade
if (Math.random() < 0.143) { //random
b.soundBot(where, isKeep)
if (isKeep) tech.soundBotCount++;
} else if (Math.random() < 0.166 && isLaser) { //random
b.laserBot(where, isKeep)
if (isKeep) tech.laserBotCount++;
} else if (Math.random() < 0.2) {
@@ -5084,7 +5225,7 @@ const b = {
if (Vector.magnitude(Vector.sub(this.position, player.position)) < 250 && m.immuneCycle < m.cycle) { //give energy
Matter.Body.setAngularVelocity(this, this.spin)
if (this.isUpgraded) {
m.energy += 0.115
m.energy += 0.12
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
@@ -5093,7 +5234,7 @@ const b = {
time: simulation.drawTime
});
} else {
m.energy += 0.035
m.energy += 0.04
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
@@ -5148,10 +5289,7 @@ const b = {
Composite.add(engine.world, bullet[me]); //add bullet to world
b.setDynamoBotDelay()
},
nailBot(position = {
x: player.position.x + 50 * (Math.random() - 0.5),
y: player.position.y + 50 * (Math.random() - 0.5)
}, isConsole = true) {
nailBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) {
if (isConsole) simulation.makeTextLog(`<span class='color-var'>b</span>.nailBot()`);
const me = bullet.length;
const dir = m.angle;
@@ -5168,7 +5306,7 @@ const b = {
minDmgSpeed: 2,
// lookFrequency: 56 + Math.floor(17 * Math.random()) - isUpgraded * 20,
lastLookCycle: simulation.cycle + 60 * Math.random(),
delay: Math.floor((tech.isNailBotUpgrade ? 20 : 100) * b.fireCDscale),
delay: Math.floor((tech.isNailBotUpgrade ? 18 : 85) * b.fireCDscale),
acceleration: 0.005 * (1 + 0.5 * Math.random()),
range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots(),
endCycle: Infinity,
@@ -5199,13 +5337,13 @@ const b = {
) {
const unit = Vector.normalise(Vector.sub(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60)), this.position))
if (this.isUpgraded) {
const SPEED = 55
const SPEED = 60
b.nail(this.position, Vector.mult(unit, SPEED))
this.force = Vector.mult(unit, -0.018 * this.mass)
this.force = Vector.mult(unit, -0.02 * this.mass)
} else {
const SPEED = 40
const SPEED = 45
b.nail(this.position, Vector.mult(unit, SPEED))
this.force = Vector.mult(unit, -0.01 * this.mass)
this.force = Vector.mult(unit, -0.012 * this.mass)
}
break;
}
@@ -5354,7 +5492,7 @@ const b = {
if (this.cd < simulation.cycle && !m.isCloak && !(simulation.cycle % this.lookFrequency)) {
for (let i = 0, len = mob.length; i < len; i++) {
const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position));
if (dist2 < 1700000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) {
if (dist2 < 1600000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) {
this.fireTarget = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) //set target to where the mob will be in 1 second
this.fire()
break;
@@ -5414,6 +5552,172 @@ const b = {
})
Composite.add(engine.world, bullet[me]); //add bullet to world
},
soundBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) {
if (isConsole) simulation.makeTextLog(`<span class='color-var'>b</span>.soundBot()`);
const me = bullet.length;
const dir = m.angle;
bullet[me] = Bodies.rectangle(position.x, position.y, 12, 30, {
isUpgraded: tech.isSoundBotUpgrade,
botType: "sound",
angle: dir,
friction: 0,
frictionStatic: 0,
frictionAir: 0.05,
restitution: 0.6 * (1 + 0.5 * Math.random()),
dmg: 0, // 0.14 //damage done in addition to the damage from momentum
minDmgSpeed: 2,
lookFrequency: 17 + Math.floor(7 * Math.random()) - 10 * tech.isSoundBotUpgrade,
cd: 0,
fireCount: 0,
fireLimit: 5 + 3 * tech.isSoundBotUpgrade,
delay: Math.floor((90 + (tech.isSoundBotUpgrade ? 0 : 90)) * b.fireCDscale),// + 30 - 20 * tech.isFoamBotUpgrade,//20 + Math.floor(85 * b.fireCDscale) - 20 * tech.isFoamBotUpgrade,
acceleration: 0.005 * (1 + 0.5 * Math.random()),
range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots(), //how far from the player the bot will move
endCycle: Infinity,
classType: "bullet",
collisionFilter: {
category: cat.bullet,
mask: b.totalBots() < 50 ? cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield //if over 50 bots, they no longer collide with each other
},
beforeDmg() { },
onEnd() { },
fireTarget: { x: 0, y: 0 },
waves: [],
phononWaveCD: 0,
addWave(where, angle) {
const halfArc = 0.2 * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) + 0.05 * tech.isSoundBotUpgrade //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions
this.waves.push({
position: where,
angle: angle - halfArc, //used in drawing ctx.arc
unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision
unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision
arc: halfArc * 2,
radius: 25,
resonanceCount: 0,
dmg: (tech.isUpgraded ? 3 : 1.5) * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.5 : 1),
})
},
fire() {
if (!(simulation.cycle % (6 - 2 * tech.isSoundBotUpgrade))) {
this.fireCount++
if (this.fireCount > this.fireLimit) {
this.fireCount = 0
this.cd = simulation.cycle + this.delay;
}
this.addWave({ x: this.position.x, y: this.position.y }, Math.atan2(this.fireTarget.y - this.position.y, this.fireTarget.x - this.position.x) + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5)) //add wave to waves array
//face target
Matter.Body.setAngle(this, Vector.angle(this.position, this.fireTarget));
}
},
do() {
if (this.fireCount === 0) { //passive mode: look for targets and following player
const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, m.pos))
if (distanceToPlayer > this.range) { //if far away move towards player
this.force = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.mass * this.acceleration)
} else { //close to player
Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity
}
if (this.cd < simulation.cycle && !m.isCloak && !(simulation.cycle % this.lookFrequency)) {
for (let i = 0, len = mob.length; i < len; i++) {
const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position));
if (dist2 < 1300000 && !mob[i].isBadTarget && (Matter.Query.ray(map, this.position, mob[i].position).length === 0 || dist2 < 300000) && !mob[i].isInvulnerable) {
this.fireTarget = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) //set target to where the mob will be in 1 second
this.fire()
break;
}
}
}
} else { //fire mode: quickly fire at targets and doesn't follow player
this.fire()
}
if (!m.isBodiesAsleep) { //update current waves
ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000";
ctx.lineWidth = 2 * tech.wavePacketDamage
ctx.beginPath();
const end = 1200 * tech.isBulletsLastLonger
//this does less damage than the player phonon waves 2.3 -> 2
for (let i = this.waves.length - 1; i > -1; i--) {
const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius))
const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius))
//draw wave
ctx.moveTo(v1.x, v1.y)
ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc);
//using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector
let hits = Matter.Query.ray(mob, v1, v2, 50)
for (let j = 0; j < hits.length; j++) {
const who = hits[j].body
if (!who.isShielded) {
who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
who.force.y += 0.01 * (Math.random() - 0.5) * who.mass
Matter.Body.setVelocity(who, { x: who.velocity.x * 0.95, y: who.velocity.y * 0.95 });
let vertices = who.vertices;
const vibe = 50 + who.radius * 0.15
ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5));
ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
who.locatePlayer();
who.damage(this.waves[i].dmg / Math.pow(who.radius, 0.33));
if (tech.isPhononWave && this.phononWaveCD < m.cycle) {
this.phononWaveCD = m.cycle + 10 * (1 + this.waves[i].resonanceCount)
let closestMob, dist
let range = end - 30 * this.waves[i].resonanceCount
for (let i = 0, len = mob.length; i < len; i++) {
if (who !== mob[i] && !mob[i].isBadTarget && !mob[i].isInvulnerable) {
dist = Vector.magnitude(Vector.sub(who.position, mob[i].position));
if (dist < range) {
closestMob = mob[i]
range = dist
}
}
}
if (closestMob) { //add wave to waves array
this.addWave(who.position, Math.atan2(closestMob.position.y - who.position.y, closestMob.position.x - who.position.x) + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5))
} else {
this.addWave(who.position, Math.random() * Math.PI)
}
this.waves[this.waves.length - 1].resonanceCount = this.waves[i].resonanceCount + 1
break
}
}
}
hits = Matter.Query.ray(body, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth])
for (let j = 0, len = Math.min(30, hits.length); j < len; j++) {
const who = hits[j].body
//make them shake around
who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity
let vertices = who.vertices;
const vibe = 25
ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
for (let j = 1; j < vertices.length; j++) {
ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5));
}
ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) {
if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) {
// Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1));
who.torque += who.inertia * 0.001 * (Math.random() - 0.5)
}
}
this.waves[i].radius += tech.waveBeamSpeed * 2
if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) {
this.waves.splice(i, 1) //end
}
}
ctx.stroke();
}
}
})
Composite.add(engine.world, bullet[me]); //add bullet to world
},
laserBot(position = {
x: player.position.x + 50 * (Math.random() - 0.5),
y: player.position.y + 50 * (Math.random() - 0.5)
@@ -5823,15 +6127,7 @@ const b = {
//calculate laser collision
let best;
let range = tech.isPlasmaRange * (120 + 300 * Math.sqrt(Math.random()))
const path = [{
x: this.position.x,
y: this.position.y
},
{
x: this.position.x + range * unit.x,
y: this.position.y + range * unit.y
}
];
const path = [{ x: this.position.x, y: this.position.y }, { x: this.position.x + range * unit.x, y: this.position.y + range * unit.y }];
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
@@ -5890,7 +6186,7 @@ const b = {
y: best.y
};
if (best.who.alive) {
const dmg = 0.75 * m.dmgScale; //********** SCALE DAMAGE HERE *********************
const dmg = 0.9 * m.dmgScale; //********** SCALE DAMAGE HERE *********************
best.who.damage(dmg);
best.who.locatePlayer();
//push mobs away
@@ -6747,7 +7043,6 @@ const b = {
have: false,
wavePacketCycle: 0,
delay: 40,
propagationRate: 20,
phononWaveCD: 0,
waves: [], //used in longitudinal mode
chooseFireMethod() { //set in simulation.startGame
@@ -6771,7 +7066,7 @@ const b = {
ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000";
ctx.lineWidth = 2 * tech.wavePacketDamage
ctx.beginPath();
const end = 700 * Math.sqrt(tech.isBulletsLastLonger) * Math.pow(0.93, tech.waveReflections) // / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1060
const end = 700 * Math.sqrt(tech.isBulletsLastLonger)
const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.43 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer
for (let i = this.waves.length - 1; i > -1; i--) {
@@ -6815,8 +7110,6 @@ const b = {
this.waves.push({
position: mob[j].position,
radius: 25,
reflection: 1,
expanding: true,
resonanceCount: this.waves[i].resonanceCount + 1,
})
}
@@ -6848,16 +7141,10 @@ const b = {
}
}
}
this.waves[i].radius += 0.9 * tech.waveBeamSpeed * this.waves[i].expanding //expand / move
this.waves[i].radius += 0.9 * tech.waveBeamSpeed //expand / move
// if (this.waves[i].radius > end) this.waves.splice(i, 1) //end
if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) { //* Math.pow(0.9, this.waves[i].resonanceCount)
this.waves[i].expanding = -1
this.waves[i].reflection--
if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
} else if (this.waves[i].radius < 25) {
this.waves[i].expanding = 1
this.waves[i].reflection--
if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
this.waves.splice(i, 1) //end
}
}
ctx.stroke();
@@ -6866,13 +7153,8 @@ const b = {
fire360Longitudinal() {
m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down
this.waves.push({
position: {
x: m.pos.x,
y: m.pos.y,
},
position: { x: m.pos.x, y: m.pos.y, },
radius: 25,
reflection: tech.waveReflections,
expanding: true,
resonanceCount: 0 //used with tech.isPhononWave
})
},
@@ -6881,8 +7163,7 @@ const b = {
ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000";
ctx.lineWidth = 2 * tech.wavePacketDamage
ctx.beginPath();
// const end = 1100 * tech.isBulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1767
const end = 1100 * tech.isBulletsLastLonger * Math.pow(0.93, tech.waveReflections) //should equal about 1767
const end = 1100 * tech.isBulletsLastLonger
const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.4 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer
for (let i = this.waves.length - 1; i > -1; i--) {
const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius))
@@ -6897,10 +7178,7 @@ const b = {
if (!who.isShielded) {
who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
who.force.y += 0.01 * (Math.random() - 0.5) * who.mass
Matter.Body.setVelocity(who, { //friction
x: who.velocity.x * 0.95,
y: who.velocity.y * 0.95
});
Matter.Body.setVelocity(who, { x: who.velocity.x * 0.95, y: who.velocity.y * 0.95 });
let vertices = who.vertices;
const vibe = 50 + who.radius * 0.15
ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
@@ -6909,7 +7187,6 @@ const b = {
who.locatePlayer();
who.damage(damage / Math.sqrt(who.radius));
// if (tech.isPhononWave && (!who.alive || this.waves.length < 30 + 30 * Math.random()) && m.fireCDcycle < m.cycle) { //
if (tech.isPhononWave && this.phononWaveCD < m.cycle) {
this.phononWaveCD = m.cycle + 8 * (1 + this.waves[i].resonanceCount)
const halfArc = 0.27 //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions
@@ -6933,18 +7210,10 @@ const b = {
this.waves.push({
position: who.position,
angle: angle - halfArc, //used in drawing ctx.arc
unit1: {
x: Math.cos(angle - halfArc),
y: Math.sin(angle - halfArc)
}, //used for collision
unit2: {
x: Math.cos(angle + halfArc),
y: Math.sin(angle + halfArc)
}, //used for collision
unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision
unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision
arc: halfArc * 2,
radius: 25,
reflection: 1,
expanding: 1,
resonanceCount: this.waves[i].resonanceCount + 1
})
}
@@ -6952,13 +7221,11 @@ const b = {
}
hits = Matter.Query.ray(body, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth])
// for (let j = 0; j < hits.length; j++) {
for (let j = 0, len = Math.min(30, hits.length); j < len; j++) {
const who = hits[j].body
//make them shake around
who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity
let vertices = who.vertices;
const vibe = 25
ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
@@ -6973,17 +7240,10 @@ const b = {
who.torque += who.inertia * 0.001 * (Math.random() - 0.5)
}
}
// ctx.stroke(); //draw vibes
this.waves[i].radius += tech.waveBeamSpeed * 1.8 * this.waves[i].expanding //expand / move
this.waves[i].radius += tech.waveBeamSpeed * 1.8 //expand / move
if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) {
this.waves[i].expanding = -1
this.waves[i].reflection--
if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
} else if (this.waves[i].radius < 25) {
this.waves[i].expanding = 1
this.waves[i].reflection--
if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
this.waves.splice(i, 1) //end
}
}
ctx.stroke();
@@ -6992,29 +7252,14 @@ const b = {
fireLongitudinal() {
m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down
const halfArc = (m.crouch ? 0.0785 : 0.275) * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions
// if (tech.isBulletTeleport && Math.random() < 0.04) {
// const scale = 400 * Math.random()
// this.waves[i].position = Vector.add(this.waves[i].position, { x: scale * (Math.random() - 0.5), y: scale * (Math.random() - 0.5) })
// }
const angle = m.angle + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5)
this.waves.push({
position: {
x: m.pos.x + 25 * Math.cos(m.angle),
y: m.pos.y + 25 * Math.sin(m.angle),
},
position: { x: m.pos.x + 25 * Math.cos(m.angle), y: m.pos.y + 25 * Math.sin(m.angle), },
angle: angle - halfArc, //used in drawing ctx.arc
unit1: {
x: Math.cos(angle - halfArc),
y: Math.sin(angle - halfArc)
}, //used for collision
unit2: {
x: Math.cos(angle + halfArc),
y: Math.sin(angle + halfArc)
}, //used for collision
unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision
unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision
arc: halfArc * 2,
radius: 25,
reflection: tech.waveReflections,
expanding: 1,
resonanceCount: 0
})
},

View File

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

View File

@@ -10,7 +10,7 @@ const level = {
// playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"],
//see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , gauntlet, final) added later
playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"],
communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever"],
communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "dojo"],
trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"],
levels: [],
start() {
@@ -18,7 +18,7 @@ const level = {
// simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
// simulation.isHorizontalFlipped = true
// tech.giveTech("performance")
// level.difficultyIncrease(8 * 4) //30 is near max on hard //60 is near max on why
// level.difficultyIncrease(2 * 4) //30 is near max on hard //60 is near max on why
// spawn.setSpawnList();
// spawn.setSpawnList();
// m.maxHealth = m.health = 100
@@ -31,26 +31,30 @@ const level = {
// m.energy = 0
// simulation.molecularMode = 2
// m.damage(0.1);
// b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("shotgun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("super balls") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("drones") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.guns[3].ammo = 100000000
// tech.giveTech("iridescence")
// tech.giveTech("cherenkov radiation")
// tech.giveTech("irradiated nails")
// for (let i = 0; i < 6; ++i) tech.giveTech("Lorentz transformation")
// for (let i = 0; i < 1; ++i) tech.giveTech("rivet gun")
// tech.giveTech("von Neumann probe")
// tech.giveTech("path integration")
// tech.giveTech("cordyceps")
// for (let i = 0; i < 1; ++i) tech.giveTech("sympathetic resonance")
// for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot")
// for (let i = 0; i < 1; ++i) tech.giveTech("foam-bot")
// for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot")
// for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot upgrade")
// for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot upgrade")
// requestAnimationFrame(() => { for (let i = 0; i < 30; i++) tech.giveTech("laser-bot") });
// for (let i = 0; i < 1; i++) tech.giveTech("laser-bot upgrade")
// for (let i = 0; i < 1; ++i) tech.giveTech("ternary")
// for (let i = 0; i < 3; ++i) tech.giveTech("mechatronics")
// for (let i = 0; i < 1; ++i) tech.giveTech("uncertainty principle")
// for (let i = 0; i < 1; ++i) tech.giveTech("mechanical resonance")
// for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling");
// level.subway();
// level.testing();
// for (let i = 0; i < 2; ++i) spawn.starter(1900, -500, 50)
// spawn.sneaker(1900, -500, 25)
// spawn.sniper(2000, -450)
// spawn.zombie(1000 + 1000 * Math.random(), -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color)
// spawn.zombie(-3000, -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color)
// for (let i = 0; i < 20; ++i) spawn.starter(1000 + 1000 * Math.random(), -500 + 300 * Math.random())
// tech.addJunkTechToPool(2)
// tech.tech[322].frequency = 100
@@ -81,8 +85,6 @@ const level = {
// for (let i = 0; i < 13; i++) level.nextLevel(); //jump to final boss
// lore.unlockTesting();
// tech.giveTech("tinker"); //show junk tech in experiment mode
// simulation.isCheating = false
// m.storeTech()
// powerUps.spawn(m.pos.x, m.pos.y, "entanglement", false);
} else {
@@ -103,7 +105,6 @@ const level = {
simulation.draw.setPaths();
b.respawnBots();
m.resetHistory();
spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
if (tech.isForeverDrones) {
if (tech.isDroneRadioactive) {
@@ -160,9 +161,9 @@ const level = {
}
if (tech.isHealLowHealth) {
if (tech.isEnergyHealth) {
var len = 3 * (1 - m.energy / m.maxEnergy) //as a percent
var len = 4 * (1 - m.energy / m.maxEnergy) //as a percent
} else {
var len = 3 * (1 - m.health / m.maxHealth) //as a percent
var len = 4 * (1 - m.health / m.maxHealth) //as a percent
}
for (let i = 0; i < len; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "heal", false);
}
@@ -1279,7 +1280,10 @@ const level = {
y: this.position.y + speed
}
Matter.Body.setPosition(this, position)
if (isSetPaths) simulation.draw.setPaths()
if (isSetPaths) {
simulation.draw.setPaths()
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
}
}
}
} else {
@@ -1289,7 +1293,10 @@ const level = {
y: this.position.y - speed
}
Matter.Body.setPosition(this, position)
if (isSetPaths) simulation.draw.setPaths()
if (isSetPaths) {
simulation.draw.setPaths()
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
}
}
}
}
@@ -1991,6 +1998,7 @@ const level = {
// m.addHealth(Infinity)
spawn.starter(1900, -500, 200) //big boy
// spawn.starter(1900, -500, 100) //big boy
// for (let i = 0; i < 10; ++i) spawn.launcher(1900, -500)
// spawn.suckerBoss(1900, -500)
// spawn.launcherBoss(3200, -500)
@@ -2584,10 +2592,6 @@ const level = {
// simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
// level.difficultyIncrease(10 * 4);
// m.maxHealth = m.health = 100
level.isProcedural = true //used in generating text for the level builder
simulation.draw.drawMapPath = simulation.draw.drawMapSight
// color.map = "#333" //custom map color
document.body.style.backgroundColor = "#e3e3e3"//"#e3e3e3"//color.map//"#333"//"#000"
level.defaultZoom = 1400
@@ -2654,8 +2658,10 @@ const level = {
gateButton.query();
if (!gateButton.isUp) {
if (stationNumber > 0) {
if (!isExitOpen && gatesOpenRight < stationNumber) level.newLevelOrPhase() //run some new level tech effects
gatesOpenRight = stationNumber
} else if (stationNumber < 0) {
if (!isExitOpen && gatesOpenLeft > stationNumber) level.newLevelOrPhase() //run some new level tech effects
gatesOpenLeft = stationNumber
} else { //starting station both doors open
gatesOpenLeft = stationNumber
@@ -3384,6 +3390,8 @@ const level = {
Composite.add(engine.world, map[i]); //add to world
}
simulation.draw.setPaths()
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
//shift trains left/right, as you move left or right a train will jump over and become the train needed at the next station
let repositionTrain
@@ -3437,6 +3445,9 @@ const level = {
for (let i = 0; i < train.length; i++) train[i].draw()
stationCustomTopLayer()
};
level.isProcedural = true //only used in generating text for the level builder
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
simulation.draw.drawMapPath = simulation.draw.drawMapSight
},
reservoir() {
level.exit.x = 1700;
@@ -25591,6 +25602,456 @@ const level = {
};
powerUps.addResearchToLevel(); //needs to run after mobs are spawned
},
dojo() { // By
simulation.makeTextLog(`<strong>underpass</strong> by <span class='color-var'>weird_pusheen</span>`);
const vanishes = [];
const smoofes = [];
const leftRotor = level.rotor(-550, 900, 950, 25);
leftRotor.frictionAir = 0.01;
var leftSchwoof = level.boost(-20, -60, -2000);
var rightSchwoof = level.button(2550, -50);
var rightSchwoofState = false;
var rightSchwoofLive = true;
spawn.mapRect(2513, -39, 200, 100);
var pathPoints = [
[0, 0], // Index 0 is owned by M and is set to M's position during play
// this means that occasionally the boss will bonk M on the way to somewhere else, which gives it a chance to hurt M and gives the player a chance to hurt it
[250, -750], /* Left bases */
[250, -2500],
[350, -1500], // Left doorway
[1150, -1500], // Home base
[1150, -2750], // Upper base
[1950, -1500], // Right doorway
[2050, -750], /* Right bases */
[2050, -2500],
[-150, -250], // Left porthole
];
function isntIn(point, array) {
for (var x = 0; x < array.length; x++) {
if (point[0] == array[x][0] && point[1] == array[x][1]) {
return false;
}
}
return true;
}
function isObstructed(v1, v2) {
var ret = Matter.Query.ray(map,
{
x: v1[0],
y: v1[1],
},
{
x: v2[0],
y: v2[1]
}).length != 0;
return ret; // Kinda-ish stolen from mob.js
}
function pythag(p1, p2) {
var dx = p1[0] - p2[0];
var dy = p1[1] - p2[1];
return Math.sqrt(dx * dx + dy * dy);
}
var path = undefined; // This is a stupid way to go about pathfinding code. I might even clean it up!
function pathFind(goalPoint, startPoint, curPath = []) {
var myPoint = startPoint;
if (curPath.length) {
myPoint = curPath[curPath.length - 1];
}
if (path && (curPath.length >= path.length)) { // If we've already found a shorter or equal path, no reason to continue and waste CPU time
return; // Minimizes for HOP COUNT, not PATH LENGTH - path length was buggy
}
if (!isObstructed(myPoint, goalPoint)) { // If the line to the goal point ain't blocked by a map object, we've arrived!
path = [...curPath];
path.push(goalPoint);
return;
}
pathPoints.forEach(testPoint => {
if (isntIn(testPoint, curPath)) { // If it's reusing points, there's clearly something wrong
if (!isObstructed(myPoint, testPoint)) { // If the line to the test point ain't blocked by a map object
var thing = [...curPath];
thing.push(testPoint);
pathFind(goalPoint, startPoint, thing); // Branch to a valid test point
}
}
});
}
level.setPosToSpawn(1200, 500);
level.exit.x = 51500;
level.exit.y = -1875;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.defaultZoom = 1500;
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d8dadf";
spawn.mapRect(-500, 0, 3300, 300); // Floor
spawn.mapRect(-100, -3000, 2500, 100); // Ceiling
spawn.mapRect(-200, -3000, 100, 2600); // Left wall
spawn.mapRect(2400, -3000, 100, 3000); // Right wall
spawn.mapRect(500, -1000, 100, 500); /* obstruction blocks */
smoofes.push(map[map.length - 1]);
spawn.mapRect(500, -2500, 100, 500);
smoofes.push(map[map.length - 1]);
spawn.mapRect(1700, -1000, 100, 500);
smoofes.push(map[map.length - 1]);
spawn.mapRect(1700, -2500, 100, 500);
smoofes.push(map[map.length - 1]);
spawn.mapRect(-1000, 550, 200, 50); // Left chonky stepppp low
spawn.mapRect(-800, 300, 200, 50); // Left chonky stepppp high
spawn.mapVertex(-1000, 1200, "0 0 100 0 700 500 700 700 0 700"); // Left chonky
spawn.mapRect(3100, 550, 200, 50); // Right chonky stepppp low
spawn.mapRect(2900, 300, 200, 50); // Right chonky stepppp high
spawn.mapVertex(3300, 1200, "0 0 -100 0 -700 500 -700 700 0 700"); // Right chonky
const leftElevator = level.elevator(-1400 - 300, 1450, 300, 100, 500);
const rightElevator = level.elevator(-1400 + 5100, 1450, 300, 100, 500);
spawn.mapRect(-150, -1700, 200, 50);
spawn.mapRect(400, -2050, 200, 50);
spawn.mapRect(1600, -1000, 200, 50);
spawn.randomMob(1200, 700);
spawn.randomMob(600, 1000);
spawn.randomMob(1800, 1000);
spawn.randomMob(3200, 400);
spawn.randomMob(3000, 200);
spawn.randomMob(-900, 400);
spawn.randomMob(-700, 200);
spawn.randomMob(1200, 1000);
for (var i = 0; i < 4; i++) {
spawn.randomSmallMob(Math.random() * 600 - 600, Math.random() * 3000 - 400);
}
spawn.grenadier(-300, -1000);
spawn.grenadier(2600, -1000);
spawn.mapRect(-1400, 1450, 5100, 100); // The True Floor
const slime = level.hazard(-1250, 1400, 4800, 50);
slime.maxHeight = 600;
simulation.draw.body = function () {
ctx.beginPath();
for (let i = 0, len = body.length; i < len; ++i) {
if (!body[i].hidden) {
let vertices = body[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
}
}
ctx.lineWidth = 2;
ctx.fillStyle = color.block;
ctx.fill();
ctx.strokeStyle = color.blockS;
ctx.stroke();
} // Override the old draw code to allow intelligent hiding of blocks - preferably this becomes official code because it's just a single added if statement and makes a lot of things cleaner and more intelligent
const vanish = function (x, y, width, height) { // normal vanishes don't work well on my map for some reason, so I rewrote
x += width / 2;
y += height / 2;
const getVertices = function (bX, bY, bW, bH) { return [{ x: bX, y: bY, index: 0, isInternal: false }, { x: bX + bW, y: bY, index: 1, isInternal: false }, { x: bX + bW, y: bY + bH, index: 4, isInternal: false }, { x: bX, y: bY + bH, index: 3, isInternal: false }] };
const cMask = cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
const vertices = getVertices(x, y, width, height);
const block = body[body.length] = Bodies.fromVertices(x, y, vertices, {
collisionFilter: {
category: cat.map,
mask: cMask
},
isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
isNonStick: true, //this keep sporangium from sticking
isTouched: false,
cWidth: width,
hiddenCycle: 0,
isStatic: true,
query() {
if (this.cWidth <= 0) {
if (this.cWidth > -100) {
this.cWidth = -100;
Matter.Body.setVertices(this, vertices);
}
this.isTouched = false;
this.collisionFilter.mask = undefined;
this.hidden = true;
this.hiddenCycle++;
if (this.hiddenCycle > 100) {
if (Matter.Query.collides(this, [player]).length) {
this.hiddenCycle = 50;
}
else {
this.hiddenCycle = 0;
this.cWidth = width;
this.collisionFilter.mask = cMask;
this.hidden = false;
}
}
}
else if (this.isTouched) {
Matter.Body.setVertices(this, getVertices(x, y, this.cWidth, height * (this.cWidth / width)));
this.cWidth -= 3;
}
else if (Matter.Query.collides(this, [player]).length) { // Elseif short circuit avoids expensive collision detection
this.isTouched = true;
}
}
});
return block;
};
vanishes.push(vanish(800, 800, 800, 50));
vanishes.push(vanish(400, 1100, 400, 50));
vanishes.push(vanish(1600, 1100, 400, 50));
spawn.bodyRect(1700, 812, 300, 25, 1, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.map
},
isNoSetCollision: true,
isNotHoldable: true,
isNonStick: true, //this keep sporangium from sticking
restitution: 1,
friction: 0,
frictionAir: 0,
frictionStatic: 0,
query() {
Matter.Body.setAngularVelocity(this, 0);
Matter.Body.applyForce(this, this.position, {
x: 0,
y: -(this.position.y - 812) * 0.002
});
}
});
const zigzag = body[body.length - 1];
Matter.Body.applyForce(zigzag, zigzag.position, {
x: 0.1,
y: 0
});
var buttonWasDown = false;
level.customTopLayer = () => {
}
level.custom = () => {
rightSchwoof.isUp = false;
level.exit.drawAndCheck();
leftSchwoof.query();
level.enter.draw();
pathPoints[0][0] = m.pos.x;
pathPoints[0][1] = m.pos.y;
leftElevator.move();
rightElevator.move();
slime.query();
zigzag.query();
slime.levelRise(0.2);
for (var i = 0; i < vanishes.length; i++) {
vanishes[i].query();
}
if (!rightSchwoofState) {
var math = m.pos.y < leftRotor.position.y;
Matter.Body.setAngularVelocity(leftRotor, (math ? 1 : -1) * Math.PI / 45);
}
if (rightSchwoofLive) {
rightSchwoof.query();
rightSchwoof.draw();
if (rightSchwoofState) {
ctx.fillStyle = "lightgreen";
}
else {
ctx.fillStyle = "red";
}
ctx.beginPath();
ctx.arc(2615, -220, 40, 0, Math.PI * 2);
ctx.fill();
}
if (rightSchwoof.isUp) {
buttonWasDown = true;
}
else if (buttonWasDown) {
buttonWasDown = false;
rightSchwoofState = !rightSchwoofState;
}
if (Matter.Query.collides(player, smoofes).length) {
Matter.Body.applyForce(player, player.position, {
x: 0,
y: -0.015
});
}
};
mobs.spawn(500, -500, 10, 100, "yellow"); /* TacticalBoss
Modes:
Spawn:
Pathfinds to a point above M and starts dropping mobs. Learns which mobs to drop to cause the most damage, of course.
Occasionally strikes at M.
Hide:
Pathfinds to the point furthest from M
Strike:
Pathfind really, really fast to M
Recharge:
Stop moving for a bit to "recharge" (this is so the player has a chance to hit it)
It must always Hide or Recharge after Spawning or Striking. Which one it does is based on some factor I'll figure out.
Pathfinding is a hypersimplified algorithm with hard-coded "points" that it can travel between. M is one of these.
*/
var boss = mob[mob.length - 1];
boss.isBoss = true;
boss.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
boss.onDeath = function () {
powerUps.spawnBossPowerUp(this.position.x, this.position.y);
level.exit.x = 2560;
level.exit.y = -90;
rightSchwoofLive = false;
};
var spawnables = {};
["hopper", "stabber", "springer", "striker", "sneaker", "grower"].forEach((m) => { /* Used to be spawn.fullPickList, but some of those mobs don't do collision-only damage and would thus never be properly selected for */
if (spawn[m]) {
spawnables[m] = {
fun: spawn[m],
name: m,
weight: 1
}
}
});
boss.stabCycle = 0;
boss.spawnCycle = 0;
function spawny() {
var totalWeight = 0;
Object.keys(spawnables).forEach(key => {
totalWeight += spawnables[key].weight;
});
var cursorWeight = 0;
var choice = Math.random();
var mC = undefined;
Object.values(spawnables).forEach((thing) => {
var lower = cursorWeight / totalWeight;
cursorWeight += thing.weight;
var upper = cursorWeight / totalWeight;
if ((choice > lower && choice <= upper) || !mC) {
mC = thing;
}
});
mC.fun(boss.position.x, boss.position.y);
var sp = mob[mob.length - 1];
sp.typeName = mC.name;
sp.onHit = () => {
spawnables[sp.typeName].weight += 1;
};
var oldFun = sp.onDeath;
sp.onDeath = () => { /* Mobs that die are worth less */
oldFun.call(sp);
spawnables[sp.typeName].weight -= 0.3; /* But not too much less */
};
}
boss.spawnDelay = 40;
boss.mode = "hide";
boss.modeSwitch = -1; // Randomize mode immediately
boss.damageReduction = 0.1;
var oldOnHit = boss.onHit;
boss.onHit = () => {
boss.modeSwitch = -1; // After striking the player, always switch modes
oldOnHit.call(boss);
};
boss.do = () => {
path = undefined;
var pfGoal = [0, 0];
boss.modeSwitch--;
if (boss.modeSwitch < 0) {
if (!boss.isShielded) {
spawn.shield(boss, boss.position.x, boss.position.y, 0.75); // Every time the mode switches, have a 75% chance to gain a new shield
}
if (boss.mode == "hide" || boss.mode == "recharge") {
if (Math.random() > 0.5) {
boss.mode = "spawn";
}
else {
boss.mode = "strike";
}
boss.modeSwitch = 600;
}
else {
if (boss.mode == "strike") {
boss.mode = "hide"; // Always hides after striking
}
else {
if (Math.random() > 0.5) {
boss.mode = "hide";
}
else {
boss.mode = "recharge"; // same when it goes into recharge mode
spawn.shield(boss, boss.position.x, boss.position.y, 1);
}
}
boss.modeSwitch = 200;
}
}
if (boss.mode == "hide") { /* Find the furthest point from M and get to it */
var longest = 0;
pathPoints.forEach(item => {
if (item[0] == 1150) {
return;
}
var iL = pythag(item, [m.pos.x, m.pos.y]);
if (iL > longest) {
longest = iL;
pfGoal = item;
}
});
}
else if (boss.mode == "strike") {
pfGoal = pathPoints[0]; // Target M
}
else if (boss.mode == "spawn") {
pfGoal = pathPoints[4]; // Go to Home Base to spawn
}
if (boss.mode != "recharge") {
if (m.pos.x > 2350 || m.pos.x < -150 || m.pos.y > 50) {
boss.mode = "hide";
}
pathFind(pfGoal, [boss.position.x, boss.position.y]);
if (!path) {
return; // If it couldn't pathfind, just drift
}
var goalX = path[0][0];
var goalY = path[0][1];
var dX = goalX - boss.position.x;
var dY = goalY - boss.position.y;
var hyp = Math.sqrt(dX * dX + dY * dY);
Matter.Body.applyForce(boss, {
x: goalX,
y: goalY
}, {
x: dX / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1),
y: dY / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1)// - 0.005
});
}
if (boss.mode == "spawn") {
boss.stabCycle++;
if (boss.stabCycle > 25) {
if (Math.abs(dX) < 200 && dY > 0) {
Matter.Body.applyForce(boss, {
x: player.position.x,
y: player.position.y
}, {
x: 0,
y: 5
});
}
boss.stabCycle = 0;
}
boss.spawnCycle++;
if (boss.spawnCycle > boss.spawnDelay) {
spawny();
boss.spawnDelay += 4;
boss.spawnCycle = 0;
}
}
};
boss.showHealthBar = true;
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
// ********************************************************************************************************
// ********************************************************************************************************
// ***************************************** training levels **********************************************

View File

@@ -189,7 +189,6 @@ const mobs = {
effect() {
if ((simulation.cycle - this.startCycle) % 30 === 0) {
let dmg = m.dmgScale * tech.radioactiveDamage * this.dmg
console.log(dmg)
who.damage(dmg);
if (who.damageReduction) {
simulation.drawList.push({ //add dmg to draw queue
@@ -409,10 +408,7 @@ const mobs = {
isLookingAtPlayer(threshold) {
const diff = Vector.normalise(Vector.sub(player.position, this.position));
//make a vector for the mob's direction of length 1
const dir = {
x: Math.cos(this.angle),
y: Math.sin(this.angle)
};
const dir = { x: Math.cos(this.angle), y: Math.sin(this.angle) };
//the dot product of diff and dir will return how much over lap between the vectors
const dot = Vector.dot(dir, diff);
// console.log(Math.cos(dot)*180/Math.PI)

View File

@@ -551,7 +551,7 @@ const m = {
if (m.fieldMode === 0 || m.fieldMode === 3) dmg *= 0.973 ** m.coupling
if (tech.isLowHealthDefense) dmg *= 1 - Math.max(0, 1 - m.health) * 0.8
if (tech.isHarmReduceNoKill && m.lastKillCycle + 300 < m.cycle) dmg *= 0.33
if (tech.squirrelFx !== 1) dmg *= Math.pow(0.7, (tech.squirrelFx - 1) / 0.4) //cause more damage
if (tech.squirrelFx !== 1) dmg *= 0.78//Math.pow(0.78, (tech.squirrelFx - 1) / 0.4)
if (tech.isAddBlockMass && m.isHolding) dmg *= 0.15
if (tech.isSpeedHarm && player.speed > 0.1) dmg *= 1 - Math.min(player.speed * 0.0165, 0.66)
if (tech.isHarmReduce && input.field && m.fieldCDcycle < m.cycle) dmg *= 0.25
@@ -845,6 +845,7 @@ const m = {
draw() { },
isAltSkin: false,
resetSkin() {
simulation.isAutoZoom = true;
m.yOffWhen.jump = 70
m.yOffWhen.stand = 49
m.yOffWhen.crouch = 22
@@ -1352,6 +1353,7 @@ const m = {
},
dilate() {
m.isAltSkin = true
simulation.isAutoZoom = false;
m.draw = function () {
const amplitude = 8 + 4 * Math.sin(m.cycle * 0.0075)
ctx.fillStyle = m.fillColor;
@@ -1380,6 +1382,13 @@ const m = {
ctx.restore();
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
powerUps.boost.draw()
//zoom camera in and out
// console.log(simulation.zoomScale)
simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075))
}
},
dilate2() {
@@ -1415,6 +1424,7 @@ const m = {
ctx.restore();
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
powerUps.boost.draw()
simulation.setZoom(1800 + 400 * Math.sin(m.cycle * 0.0075))
}
m.drawLeg = function (stroke) {
// if (simulation.mouseInGame.x > m.pos.x) {
@@ -1903,7 +1913,7 @@ const m = {
}
},
setMaxEnergy(isMessage = true) {
m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2 * tech.isGroundState + 3 * tech.isRelay * tech.isFlipFlopOn * tech.isRelayEnergy + 1.5 * (m.fieldMode === 1) + (m.fieldMode === 0 || m.fieldMode === 1) * 0.05 * m.coupling + 0.4 * tech.isStandingWaveExpand
m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2.66 * tech.isGroundState + 3 * tech.isRelay * tech.isFlipFlopOn * tech.isRelayEnergy + 1.5 * (m.fieldMode === 1) + (m.fieldMode === 0 || m.fieldMode === 1) * 0.05 * m.coupling + 0.4 * tech.isStandingWaveExpand
if (isMessage) simulation.makeTextLog(`<span class='color-var'>m</span>.<span class='color-f'>maxEnergy</span> <span class='color-symbol'>=</span> ${(m.maxEnergy.toFixed(2))}`)
},
fieldMeterColor: "#0cf",

View File

@@ -452,7 +452,7 @@ const powerUps = {
b.randomBot()
if (tech.renormalization) {
for (let i = 0; i < cost; i++) {
if (Math.random() < 0.44) {
if (Math.random() < 0.46) {
m.fieldCDcycle = m.cycle + 20;
powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research");
}
@@ -465,7 +465,7 @@ const powerUps = {
if (tech.isDeathAvoid && document.getElementById("tech-anthropic")) {
document.getElementById("tech-anthropic").innerHTML = `-${powerUps.research.count}`
}
if (tech.renormalization && Math.random() < 0.44 && amount < 0) {
if (tech.renormalization && Math.random() < 0.46 && amount < 0) {
for (let i = 0, len = -amount; i < len; i++) powerUps.spawn(m.pos.x, m.pos.y, "research");
}
if (tech.isRerollHaste) {

View File

@@ -766,6 +766,7 @@ const simulation = {
tech.laserBotCount = 0;
tech.orbitBotCount = 0;
tech.foamBotCount = 0;
tech.soundBotCount = 0;
tech.boomBotCount = 0;
tech.plasmaBotCount = 0;
tech.missileBotCount = 0;
@@ -1014,6 +1015,7 @@ const simulation = {
},
clearNow: false,
clearMap() {
level.isProcedural = false;
ctx.setTransform(1, 0, 0, 1, 0, 0);
if (m.alive) {
if (tech.isLongitudinal) b.guns[3].waves = []; //empty array of wave bullets
@@ -1103,7 +1105,6 @@ const simulation = {
}
requestAnimationFrame(respawnHeal);
}
if (tech.isDronesTravel && m.alive) {
//count drones
let droneCount = 0
@@ -1125,39 +1126,7 @@ const simulation = {
}
//respawn drones in animation frame
let respawnDrones = () => {
if (droneCount > 0) {
requestAnimationFrame(respawnDrones);
if (!simulation.paused && !simulation.isChoosing && m.alive) {
const where = {
x: level.enter.x + 50,
y: level.enter.y - 60
}
droneCount--
if (tech.isDroneRadioactive) {
b.droneRadioactive({
x: where.x + 100 * (Math.random() - 0.5),
y: where.y + 100 * (Math.random() - 0.5)
}, 0)
} else {
b.drone({
x: where.x + 100 * (Math.random() - 0.5),
y: where.y + 120 * (Math.random() - 0.5)
}, 0)
if (tech.isDroneGrab && deliveryCount > 0) {
const who = bullet[bullet.length - 1]
who.isImproved = true;
const SCALE = 2.25
Matter.Body.scale(who, SCALE, SCALE);
who.lookFrequency = 30 + Math.floor(11 * Math.random());
who.endCycle += 3000 * tech.droneCycleReduction * tech.isBulletsLastLonger
deliveryCount--
}
}
}
}
}
requestAnimationFrame(respawnDrones);
requestAnimationFrame(() => { b.delayDrones({ x: level.enter.x + 50, y: level.enter.y - 60 }, droneCount) });
//respawn spores in animation frame
let respawnSpores = () => {
@@ -1221,11 +1190,17 @@ const simulation = {
}
requestAnimationFrame(respawnFleas);
}
if (tech.isQuantumEraser) {
let count = 0
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].isDropPowerUp && mob[i].alive) tech.quantumEraserCount++
if (mob[i].isDropPowerUp && mob[i].alive) count++
}
count *= 0.17 //to fake the chance, this makes it not random, and maybe less confusing
let cycle = () => { //run after waiting a cycle for the map to be cleared
const types = ["heal", "ammo", "heal", "ammo", "research", "coupling", "boost", "tech", "gun", "field"]
for (let i = 0; i < count; i++) powerUps.spawnDelay(types[Math.floor(Math.random() * types.length)], 1)
}
requestAnimationFrame(cycle);
}
function removeAll(array) {
@@ -1305,7 +1280,7 @@ const simulation = {
},
sight: { //credit to Cornbread for adding this algorithm to n-gon
// square: 0,
intersectMap: [], //this is precalculated in simulation.draw.setPaths() when the map changes
intersectMap: [], //this is precalculated in simulation.draw.lineOfSightPrecalculation()
getIntersection(v1, v1End, domain) {
const intersections = simulation.sight.getIntersections(v1, v1End, domain);
var best = { x: v1End.x, y: v1End.y, dist: (v1End.x - v1.x) ** 2 + (v1End.y - v1.y) ** 2 }
@@ -1596,9 +1571,8 @@ const simulation = {
}
simulation.draw.mapPath.lineTo(vertices[0].x, vertices[0].y);
}
//store data for line of sight precalculation
},
lineOfSightPrecalculation() {
simulation.sight.intersectMap = [];
for (var i = 0; i < map.length; i++) {
const obj = map[i];

View File

@@ -17,7 +17,6 @@ const spawn = {
} else {
spawn[options[Math.floor(Math.random() * options.length)]](x, y)
}
spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
},
pickList: ["starter", "starter"],
fullPickList: [
@@ -49,93 +48,6 @@ const spawn = {
spawnChance(chance) {
return Math.random() < chance + 0.07 * simulation.difficulty && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1)
},
quantumEraserCheck() { //remove mobs from tech: quantum eraser
if (tech.isQuantumEraser && tech.quantumEraserCount > 0) {
//start at a random location in array
const randomMiddle = Math.floor(mob.length * Math.random())
let i = randomMiddle
for (let j = 0; j < mob.length; j++) {
i++
if (i > mob.length - 1) i = 0
if (mob[i].isDropPowerUp && mob[i].alive) { //&& !mob[i].isBoss
if (mob[i].isFinalBoss) {
tech.quantumEraserCount = 0;
return
} else {
tech.isQuantumEraserDuplication = true
mob[i].death()
tech.isQuantumEraserDuplication = false
}
//graphics
const color = 'rgba(255,255,255, 0.8)'
simulation.drawList.push({
x: mob[i].position.x,
y: mob[i].position.y,
radius: mob[i].radius * 2,
color: color, //"rgba(0,0,0,0.6)",
time: 60
});
simulation.drawList.push({
x: mob[i].position.x,
y: mob[i].position.y,
radius: mob[i].radius * 1,
color: color, //"rgba(0,0,0,0.85)",
time: 90
});
simulation.drawList.push({
x: mob[i].position.x,
y: mob[i].position.y,
radius: mob[i].radius * 0.5,
color: color, //"rgb(0,0,0)",
time: 120
});
tech.quantumEraserCount--
simulation.makeTextLog(`<span class='color-var'>tech</span>.quantumEraserCount <span class='color-symbol'>=</span> ${tech.quantumEraserCount}`)
if (tech.quantumEraserCount < 1) break
}
}
// for (let i = 0, len = mob.length; i < len; i++) {
// if (mob[i].isDropPowerUp && mob[i].alive) { //&& !mob[i].isBoss
// if (mob[i].isFinalBoss) {
// tech.quantumEraserCount = 0;
// return
// } else {
// tech.isQuantumEraserDuplication = true
// mob[i].death()
// tech.isQuantumEraserDuplication = false
// }
// //graphics
// const color = 'rgba(255,255,255, 0.8)'
// simulation.drawList.push({
// x: mob[i].position.x,
// y: mob[i].position.y,
// radius: mob[i].radius * 2,
// color: color, //"rgba(0,0,0,0.6)",
// time: 60
// });
// simulation.drawList.push({
// x: mob[i].position.x,
// y: mob[i].position.y,
// radius: mob[i].radius * 1,
// color: color, //"rgba(0,0,0,0.85)",
// time: 90
// });
// simulation.drawList.push({
// x: mob[i].position.x,
// y: mob[i].position.y,
// radius: mob[i].radius * 0.5,
// color: color, //"rgb(0,0,0)",
// time: 120
// });
// tech.quantumEraserCount--
// simulation.makeTextLog(`<span class='color-var'>tech</span>.quantumEraserCount <span class='color-symbol'>=</span> ${tech.quantumEraserCount}`)
// if (tech.quantumEraserCount < 1) break
// }
// }
}
},
randomMob(x, y, chance = 1) {
if (spawn.spawnChance(chance) || chance === Infinity) {
const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
@@ -145,7 +57,6 @@ const spawn = {
const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
spawn[pick](x, y);
}
spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
},
randomSmallMob(x, y,
num = Math.max(Math.min(Math.round(Math.random() * simulation.difficulty * 0.2), 4), 0),
@@ -163,7 +74,6 @@ const spawn = {
spawn[pick](x + Math.round((Math.random() - 0.5) * 20) + i * size * 2.5, y + Math.round((Math.random() - 0.5) * 20), size);
}
}
spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
},
randomGroup(x, y, chance = 1) {
if (spawn.spawnChance(chance) && simulation.difficulty > 2 || chance === Infinity) {
@@ -198,7 +108,6 @@ const spawn = {
}
}
}
spawn.quantumEraserCheck(); //remove mobs from tech: quantum eraser
}
},
secondaryBossChance(x, y) {
@@ -588,7 +497,7 @@ const spawn = {
ctx.beginPath();
if (this.fadeCycle < 120) { //damage scales up over 2 seconds to give player time to move as it fades in
const scale = this.fadeCycle / 120
const dmg = this.fadeCycle < 60 ? 0 : 0.13 * simulation.dmgScale * scale
const dmg = this.fadeCycle < 60 ? 0 : 0.1 * simulation.dmgScale * scale
me.lasers(me.vertices[0], me.angle + Math.PI / 6, dmg);
me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6, dmg);
me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6, dmg);
@@ -1548,7 +1457,7 @@ const spawn = {
me.isDropPowerUp = false;
me.showHealthBar = false;
me.stroke = "#83a"
me.accelMag = 0.001
me.accelMag = 0.003
me.frictionAir = 0.005
me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob
me.seeAtDistance2 = 1000000 //1000 vision range
@@ -1583,6 +1492,7 @@ const spawn = {
if (
!mob[i].isZombie &&
!mob[i].isUnblockable &&
!mob[i].isMobBullet &&
Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
Matter.Query.ray(body, this.position, mob[i].position).length === 0
// !mob[i].isBadTarget &&
@@ -1605,7 +1515,7 @@ const spawn = {
}
}
me.zombieHealthBar = function () {
this.health -= 0.0004 //decay
this.health -= 0.0003 //decay
if ((this.health < 0.01 || isNaN(this.health)) && this.alive) this.death();
const h = this.radius * 0.3;
const w = this.radius * 2;
@@ -1623,15 +1533,16 @@ const spawn = {
this.force = Vector.mult(Vector.normalise(Vector.sub(this.target.position, this.position)), this.accelMag * this.mass)
} else { //wonder around
this.torque += 0.0000003 * this.inertia;
const mag = 0.00015 * this.mass
const mag = 0.0003 * this.mass
this.force.x += mag * Math.cos(this.angle)
this.force.y += mag * Math.sin(this.angle)
}
if (this.speed > 6) { // speed cap instead of friction to give more agility
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.93,
y: this.velocity.y * 0.93
});
if (this.speed > 15) { // speed cap instead of friction to give more agility
Matter.Body.setVelocity(this, { x: this.velocity.x * 0.96, y: this.velocity.y * 0.96 });
} else if (this.speed < 10) {
Matter.Body.setVelocity(this, { x: this.velocity.x * 0.98, y: this.velocity.y * 0.98 });
} else if (this.speed < 8) {
Matter.Body.setVelocity(this, { x: this.velocity.x * 0.99, y: this.velocity.y * 0.99 });
}
const hit = (who) => {
if (!who.isZombie && who.damageReduction) {
@@ -1642,9 +1553,14 @@ const spawn = {
this.force.y -= force.y;
this.target = null //look for a new target
const dmg = 1.3 * m.dmgScale
who.damage(dmg);
// who.damage(dmg);
//by pass normal damage method
const dmg = 1.5 * who.damageReduction / Math.sqrt(who.mass)
who.health -= dmg
who.onDamage(dmg); //custom damage effects
who.locatePlayer();
if ((who.health < 0.01 || isNaN(who.health)) && who.alive) who.death();
simulation.drawList.push({
x: this.position.x,
y: this.position.y,

View File

@@ -252,7 +252,7 @@ const tech = {
if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.007
if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2
if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.66, player.speed * 0.0165)
if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.6
if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.83
if (tech.isAxion && tech.isHarmMACHO) dmg *= 2 - m.defense()
if (tech.isHarmDamage && m.lastHarmCycle + 480 > m.cycle) dmg *= 3;
if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit * (2 - m.defense()) // if (!simulation.paused) m.lastHit = 0
@@ -260,7 +260,7 @@ const tech = {
return dmg
},
duplicationChance() {
return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.15 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0) + tech.isQuantumEraserDuplication * (1 - 0.016 * (simulation.difficultyMode ** 2))))
return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.17 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0)))
},
isScaleMobsWithDuplication: false,
maxDuplicationEvent() {
@@ -337,8 +337,8 @@ const tech = {
},
{
name: "nitinol",
description: "<strong>+33%</strong> <strong>movement</strong> and <strong>jumping</strong><br><strong>+30%</strong> <strong class='color-defense'>defense</strong>",
maxCount: 3,
description: "<strong>+33%</strong> <strong>movement</strong> and <strong>jumping</strong><br><strong>+22%</strong> <strong class='color-defense'>defense</strong>",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
@@ -454,8 +454,8 @@ const tech = {
},
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
frequency: 4,
frequencyDefault: 4,
allowed() {
return tech.isEnergyHealth
},
@@ -854,7 +854,7 @@ const tech = {
},
{
name: "non-renewables",
description: `<strong>+67%</strong> <strong class='color-d'>damage</strong><br>${powerUps.orb.ammo()} can't <strong>spawn</strong>`,
description: `<strong>+78%</strong> <strong class='color-d'>damage</strong><br>${powerUps.orb.ammo()} can't <strong>spawn</strong>`,
maxCount: 1,
count: 0,
frequency: 1,
@@ -863,7 +863,7 @@ const tech = {
return !tech.isAmmoFromHealth && !tech.isBoostReplaceAmmo
},
requires: "not catabolism, quasiparticles",
damage: 1.67,
damage: 1.78,
effect() {
tech.damage *= this.damage
tech.isEnergyNoAmmo = true;
@@ -1139,7 +1139,7 @@ const tech = {
},
{
name: "heuristics",
description: "<strong>+25%</strong> <strong><em>fire rate</em></strong><br>spawn a <strong class='color-g'>gun</strong>",
description: "<strong>+22%</strong> <strong><em>fire rate</em></strong><br>spawn a <strong class='color-g'>gun</strong>",
maxCount: 9,
count: 0,
frequency: 1,
@@ -1149,7 +1149,7 @@ const tech = {
},
requires: "",
effect() {
tech.fireRate *= 0.75
tech.fireRate *= 0.78
b.setFireCD();
powerUps.spawn(m.pos.x, m.pos.y, "gun");
},
@@ -1161,7 +1161,7 @@ const tech = {
{
name: "anti-shear topology",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Topology' class="link">anti-shear topology</a>`,
description: "<strong>+30%</strong> projectile <strong>duration</strong>", //<br><em style = 'font-size: 83%'>drone spore worm flea missile foam wave neutron ice</em>",
description: "your bullets last <strong>+30%</strong> <strong>longer</strong>", //<br><em style = 'font-size: 83%'>drone spore worm flea missile foam wave neutron ice</em>",
maxCount: 3,
count: 0,
frequency: 1,
@@ -1195,7 +1195,7 @@ const tech = {
},
{
name: "shear stress",
description: "after mobs <strong>die</strong><br>they release a <strong>nail</strong> that targets nearby mobs",
description: "after mobs <strong>die</strong><br>they fire a <strong>nail</strong> at nearby mobs",
maxCount: 9,
count: 0,
frequency: 1,
@@ -1493,6 +1493,62 @@ const tech = {
tech.isFoamBotUpgrade = false
}
},
{
name: "sound-bot",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">sound-bot</a>`,
description: "a <strong class='color-bot'>bot</strong> emits <strong>expanding arcs</strong><br>aimed towards nearby mobs",
maxCount: 9,
count: 0,
frequency: 1,
frequencyDefault: 1,
isBot: true,
isBotTech: true,
allowed() { return true },
requires: "",
effect() {
tech.soundBotCount++;
b.soundBot();
},
remove() {
if (this.count) {
tech.soundBotCount -= this.count;
b.clearPermanentBots();
b.respawnBots();
}
}
},
{
name: "sound-bot upgrade",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">sound-bot upgrade</a>`,
description: "<strong>convert</strong> your bots to <strong>sound-bots</strong><br><strong>+200%</strong> wave <strong>fire rate</strong> and <strong>+100%</strong> <strong class='color-d'>damage</strong>",
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
isBotTech: true,
allowed() {
return tech.soundBotCount > 1 && !b.hasBotUpgrade()
},
requires: "2 or more sound bots and no other bot upgrade",
effect() {
tech.isSoundBotUpgrade = true
b.convertBotsTo("sound-bot")
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType === 'sound') bullet[i].isUpgraded = true
}
tech.setBotTechFrequency()
tech.setTechFrequency("sound-bot", 5)
},
remove() {
if (this.count) {
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType === 'sound') bullet[i].isUpgraded = false
}
tech.setBotTechFrequency(1)
}
tech.isSoundBotUpgrade = false
}
},
{
name: "boom-bot",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">boom-bot</a>`,
@@ -1679,7 +1735,7 @@ const tech = {
{
name: "dynamo-bot",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">dynamo-bot</a>`,
description: "a <strong class='color-bot'>bot</strong> <strong class='color-d'>damages</strong> mobs while it <strong>traces</strong> your path<br>when it's near generate <strong>+7</strong> <strong class='color-f'>energy</strong> per second",
description: "a <strong class='color-bot'>bot</strong> <strong class='color-d'>damages</strong> mobs while it <strong>traces</strong> your path<br>when it's near generate <strong>+8</strong> <strong class='color-f'>energy</strong> per second",
maxCount: 9,
count: 0,
frequency: 1,
@@ -1705,7 +1761,7 @@ const tech = {
{
name: "dynamo-bot upgrade",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Robot' class="link">dynamo-bot upgrade</a>`,
description: "<strong>convert</strong> your bots to <strong>dynamo-bots</strong><br>when it's near generate <strong>+23</strong> <strong class='color-f'>energy</strong> per second",
description: "<strong>convert</strong> your bots to <strong>dynamo-bots</strong><br>when it's near generate <strong>+24</strong> <strong class='color-f'>energy</strong> per second",
maxCount: 1,
count: 0,
frequency: 3,
@@ -1831,6 +1887,8 @@ const tech = {
tech.orbitBotCount *= 2
for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot();
tech.dynamoBotCount *= 2
for (let i = 0; i < tech.soundBotCount; i++) b.soundBot();
tech.soundBotCount *= 2
for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot();
tech.plasmaBotCount *= 2
for (let i = 0; i < tech.missileBotCount; i++) b.missileBot();
@@ -1846,6 +1904,7 @@ const tech = {
tech.nailBotCount = Math.round(tech.nailBotCount / 2)
tech.laserBotCount = Math.round(tech.laserBotCount / 2)
tech.foamBotCount = Math.round(tech.foamBotCount / 2)
tech.soundBotCount = Math.round(tech.soundBotCount / 2)
tech.boomBotCount = Math.round(tech.boomBotCount / 2)
tech.orbitBotCount = Math.round(tech.orbitBotCount / 2)
tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2)
@@ -2503,7 +2562,9 @@ const tech = {
},
{
name: "refrigerant",
description: `after losing at least <strong>5%</strong> <strong class='color-h'>health</strong><br><strong class='color-s'>freeze</strong> all mobs for <strong>7</strong> seconds`,
descriptionFunction() {
return `after losing at least <strong>5%</strong> ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>" : "<strong class='color-h'>health</strong>"}<br><strong class='color-s'>freeze</strong> all mobs for <strong>7</strong> seconds`
},
maxCount: 1,
count: 0,
frequency: 1,
@@ -2559,7 +2620,7 @@ const tech = {
},
{
name: "ground state",
description: "<strong>+200</strong> maximum <strong class='color-f'>energy</strong><br><strong>33%</strong> passive <strong class='color-f'>energy</strong> generation",
description: "<strong>+266</strong> maximum <strong class='color-f'>energy</strong><br><strong>33%</strong> passive <strong class='color-f'>energy</strong> generation",
// description: "reduce <strong class='color-defense'>defense</strong> by <strong>66%</strong><br>you <strong>no longer</strong> passively regenerate <strong class='color-f'>energy</strong>",
maxCount: 1,
count: 0,
@@ -2731,7 +2792,7 @@ const tech = {
},
{
name: "parasitism",
description: "if a mob has <strong>died</strong> in the last <strong>5 seconds</strong><br><strong>+60%</strong> <strong class='color-d'>damage</strong>, inhibit <strong class='color-f'>energy</strong> generation",
description: "if a mob has <strong>died</strong> in the last <strong>5 seconds</strong><br><strong>+83%</strong> <strong class='color-d'>damage</strong>, inhibit <strong class='color-f'>energy</strong> generation",
maxCount: 1,
count: 0,
frequency: 1,
@@ -2952,7 +3013,7 @@ const tech = {
},
{
name: "induction brake",
description: `after using ${powerUps.orb.heal()} <strong class='color-s'>slow</strong> nearby mobs for <strong>15</strong> seconds<br>spawn ${powerUps.orb.heal(3)}`,
description: `after using ${powerUps.orb.heal()} <strong class='color-s'>slow</strong> nearby mobs for <strong>15</strong> seconds<br>spawn ${powerUps.orb.heal(4)}`,
maxCount: 1,
count: 0,
frequency: 1,
@@ -2963,7 +3024,7 @@ const tech = {
requires: "not eddy current brake",
effect() {
tech.isHealBrake = true;
for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal");
for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal");
},
remove() {
tech.isHealBrake = false;
@@ -2972,7 +3033,7 @@ const tech = {
{
name: "adiabatic healing",
descriptionFunction() {
return `${powerUps.orb.heal()} have <strong>+100%</strong> effect<br><strong>+5%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> pool`
return `${powerUps.orb.heal()} have <strong>+100%</strong> effect<br><strong>+4%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> pool`
},
maxCount: 3,
count: 0,
@@ -2993,7 +3054,7 @@ const tech = {
Matter.Body.scale(powerUp[i], scale, scale); //grow
}
}
this.refundAmount += tech.addJunkTechToPool(0.05)
this.refundAmount += tech.addJunkTechToPool(0.04)
},
refundAmount: 0,
remove() {
@@ -3055,11 +3116,10 @@ const tech = {
powerUps.setPowerUpMode();
},
},
{
name: "negative entropy",
name: "self-assembly",
descriptionFunction() {
return `at the start of each <strong>level</strong><br>for every <strong>33%</strong> missing ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>" : "<strong class='color-h'>health</strong>"} spawn ${powerUps.orb.heal()}`
return `at the start of each <strong>level</strong><br>for every <strong>25%</strong> missing ${tech.isEnergyHealth ? "<strong class='color-f'>energy</strong>" : "<strong class='color-h'>health</strong>"} spawn ${powerUps.orb.heal()}`
},
maxCount: 1,
count: 0,
@@ -3303,7 +3363,7 @@ const tech = {
},
{
name: "renormalization",
description: `<strong>44%</strong> chance to spawn ${powerUps.orb.research(1)}<br>after consuming ${powerUps.orb.research(1)}`,
description: `<strong>46%</strong> chance to spawn ${powerUps.orb.research(1)} after consuming ${powerUps.orb.research(1)}<br><strong>+3%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> pool`,
maxCount: 1,
count: 0,
frequency: 2,
@@ -3314,9 +3374,16 @@ const tech = {
requires: "at least 4 research, not superdeterminism",
effect() {
tech.renormalization = true;
this.refundAmount += tech.addJunkTechToPool(0.03)
},
refundAmount: 0,
remove() {
tech.renormalization = false;
if (this.count > 0 && this.refundAmount > 0) {
tech.removeJunkTechFromPool(this.refundAmount)
this.refundAmount = 0
}
}
},
{
@@ -3601,7 +3668,7 @@ const tech = {
},
{
name: "dark patterns",
description: "<strong>+17%</strong> <strong class='color-d'>damage</strong><br><strong>+17%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> pool",
description: "<strong>+22%</strong> <strong class='color-d'>damage</strong><br><strong>+22%</strong> <strong class='color-junk'>JUNK</strong> to <strong class='color-m'>tech</strong> pool",
maxCount: 9,
count: 0,
frequency: 1,
@@ -3610,10 +3677,10 @@ const tech = {
return true
},
requires: "",
damage: 1.17,
damage: 1.22,
effect() {
tech.damage *= this.damage
this.refundAmount += tech.addJunkTechToPool(0.17)
this.refundAmount += tech.addJunkTechToPool(0.22)
},
refundAmount: 0,
remove() {
@@ -3665,7 +3732,7 @@ const tech = {
},
{
name: "eternalism",
description: "<strong>+30%</strong> <strong class='color-d'>damage</strong><br><strong>time</strong> can't be <strong>paused</strong> <em>(time can be dilated)</em>",
description: "<strong>+24%</strong> <strong class='color-d'>damage</strong><br><strong>time</strong> can't be <strong>paused</strong> <em>(time can be dilated)</em>",
maxCount: 1,
count: 0,
frequency: 1,
@@ -3674,7 +3741,7 @@ const tech = {
return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause
},
requires: "not unified field theory, paradigm shift, invariant",
damage: 1.3,
damage: 1.24,
effect() {
tech.damage *= this.damage
tech.isNoDraftPause = true
@@ -3969,7 +4036,7 @@ const tech = {
},
{
name: "stimulated emission",
description: "<strong>+15%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong>,<br>but after a <strong>collision</strong> eject <strong>1</strong> <strong class='color-m'>tech</strong>",
description: "<strong>+17%</strong> chance to <strong class='color-dup'>duplicate</strong> spawned <strong>power ups</strong>,<br>but after a <strong>collision</strong> eject <strong>1</strong> <strong class='color-m'>tech</strong>",
maxCount: 1,
count: 0,
frequency: 1,
@@ -4938,9 +5005,9 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary
return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary && !tech.isBulletTeleport
},
requires: "super balls not incendiary ammunition",
requires: "super balls not incendiary ammunition, uncertainty principle",
effect() {
tech.isSuperHarm = true
},
@@ -5068,7 +5135,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("wave")
return tech.haveGunCheck("wave") || tech.isSoundBotUpgrade
},
requires: "wave",
effect() {
@@ -5089,7 +5156,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("wave")
return tech.haveGunCheck("wave") || tech.isSoundBotUpgrade
},
requires: "wave",
effect() {
@@ -5110,9 +5177,9 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("wave")
return tech.haveGunCheck("wave") && !tech.isLongitudinal
},
requires: "wave",
requires: "wave, not phonon",
effect() {
tech.waveReflections += 2
},
@@ -5155,9 +5222,9 @@ const tech = {
frequency: 3,
frequencyDefault: 3,
allowed() {
return tech.haveGunCheck("wave") && !tech.isPhaseVelocity
return tech.haveGunCheck("wave") && !tech.isPhaseVelocity && tech.waveReflections === 1
},
requires: "wave, not phase velocity",
requires: "wave, not phase velocity, bound state",
ammoScale: 6,
effect() {
tech.isLongitudinal = true;
@@ -5215,7 +5282,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isLongitudinal && tech.haveGunCheck("wave")
return (tech.isLongitudinal && tech.haveGunCheck("wave")) || tech.isSoundBotUpgrade
},
requires: "wave, phonon",
effect() {
@@ -5234,7 +5301,7 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isLongitudinal && tech.haveGunCheck("wave")
return (tech.isLongitudinal && tech.haveGunCheck("wave")) || tech.isSoundBotUpgrade
},
requires: "wave, phonon",
effect() {
@@ -6231,9 +6298,8 @@ const tech = {
}
},
{
name: "drone repair",
link: `<a target="_blank" href='https://en.wikipedia.org/wiki/Unmanned_aerial_vehicle' class="link">drone repair</a>`,
description: "after a <strong>drone</strong> expires it <strong>redeploys</strong><br>for a <strong>20%</strong> chance to use <strong>1</strong> <strong>drone</strong> <strong class='color-ammo'>ammo</strong>",
name: "von Neumann probe", //"drone repair",
description: "after a <strong>drone</strong> expires<br>it will <strong>harvest</strong> a nearby <strong class='color-block'>block</strong> to <strong>replicate</strong> itself",
// description: "broken <strong>drones</strong> <strong>repair</strong> if the drone <strong class='color-g'>gun</strong> is active<br><strong>repairing</strong> has a <strong>25%</strong> chance to use <strong>1</strong> <strong>drone</strong>",
isGunTech: true,
maxCount: 1,
@@ -6467,16 +6533,16 @@ const tech = {
},
{
name: "uncertainty principle",
description: "<strong>foam</strong> and <strong>wave</strong> positions are erratic<br><strong>+53%</strong> <strong>foam</strong> and <strong>wave</strong> <strong class='color-d'>damage</strong>",
description: "<strong>foam</strong>, <strong>wave</strong>, and <strong>super ball</strong> positions are erratic<br><strong>+53%</strong> <strong>foam</strong>, <strong>wave</strong>, and <strong>super ball</strong> <strong class='color-d'>damage</strong>",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine)) || (tech.haveGunCheck("wave") && !tech.is360Longitudinal)
return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine)) || (tech.haveGunCheck("wave") && !tech.is360Longitudinal) || (tech.haveGunCheck("super balls") && !tech.isSuperHarm) || tech.isSoundBotUpgrade
},
requires: "foam, wave, not isotropic, electrostatic induction",
requires: "foam, wave, super balls, not isotropic, electrostatic induction, Zectron",
effect() {
tech.isBulletTeleport = true
},
@@ -7715,6 +7781,14 @@ const tech = {
}
simulation.makeTextLog(`tech.isFoamBotUpgrade = true`)
})
notUpgradedBots.push(() => {
tech.giveTech("sound-bot upgrade")
for (let i = 0; i < num; i++) {
b.soundBot()
tech.soundBotCount++;
}
simulation.makeTextLog(`tech.isSoundBotUpgrade = true`)
})
notUpgradedBots.push(() => {
tech.giveTech("boom-bot upgrade")
for (let i = 0; i < num; i++) {
@@ -8192,9 +8266,9 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
return (m.fieldMode === 6 || m.fieldMode === 7) && !tech.isQuantumEraser
return (m.fieldMode === 6 || m.fieldMode === 7)
},
requires: "cloaking, time dilation, not quantum eraser",
requires: "cloaking, time dilation",
effect() {
tech.cloakDuplication = 0.45
powerUps.setPowerUpMode(); //needed after adjusting duplication chance
@@ -8206,9 +8280,9 @@ const tech = {
}
},
{
name: "quantum eraser",
name: "metamaterial absorber", //quantum eraser
descriptionFunction() {
return `<span style = 'font-size:90%;'>for each mob left <strong>alive</strong> after you exit a <strong>level</strong><br><strong>kill</strong> a mob as they spawn at <strong>+${(100 - 1.1 * simulation.difficultyMode ** 2).toFixed(0)}%</strong> <strong class='color-dup'>duplication</strong></span>`
return `for each mob left <strong>alive</strong> after you exit a <strong>level</strong><br>there is a <strong>17%</strong> chance to spawn a random <strong>power up</strong>`
},
// descriptionFunction() {
// return `for each mob left <strong>alive</strong> after you exit a <strong>level</strong><br>`
@@ -8223,13 +8297,9 @@ const tech = {
},
requires: "cloaking",
effect() {
tech.quantumEraserCount = 0
tech.isQuantumEraserDuplication = 0
tech.isQuantumEraser = true
},
remove() {
tech.quantumEraserCount = 0
tech.isQuantumEraserDuplication = 0
tech.isQuantumEraser = false
}
},
@@ -10672,6 +10742,7 @@ const tech = {
tech.laserBotCount = 0;
tech.orbitBotCount = 0;
tech.foamBotCount = 0;
tech.soundBotCount = 0;
tech.boomBotCount = 0;
tech.plasmaBotCount = 0;
tech.missileBotCount = 0;
@@ -10688,6 +10759,10 @@ const tech = {
b.foamBot();
tech.foamBotCount++;
},
() => {
b.soundBot();
tech.soundBotCount++;
},
() => {
b.boomBot();
tech.boomBotCount++;
@@ -11288,6 +11363,7 @@ const tech = {
dynamoBotCount: null,
nailBotCount: null,
foamBotCount: null,
soundBotCount: null,
boomBotCount: null,
plasmaBotCount: null,
missileBotCount: null,
@@ -11358,6 +11434,7 @@ const tech = {
isRerollBots: null,
isNailBotUpgrade: null,
isFoamBotUpgrade: null,
isSoundBotUpgrade: null,
isLaserBotUpgrade: null,
isBoomBotUpgrade: null,
isOrbitBotUpgrade: null,
@@ -11568,11 +11645,8 @@ const tech = {
isIceKill: null,
isCritKill: null,
isQuantumEraser: null,
isQuantumEraserDuplication: null,
quantumEraserCount: null,
isPhononBlock: null,
isPhononWave: null,
// isMicroTransactions: null,
isLaserLens: null,
laserCrit: null,
isSporeColony: null,

215
todo.txt
View File

@@ -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 its 1. (So if youre 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