pulse charge

pulse laser now charges up with energy before you fire, but it fires 3 overlapping explosions
  please give feedback on balance (too strong, too weak?)

tech shockwave: now applies to all explosions

foam gun now gets 20% less ammo
This commit is contained in:
landgreen
2021-04-27 05:10:36 -07:00
parent 07a78743be
commit e619a2d57b
8 changed files with 351 additions and 308 deletions

View File

@@ -51,8 +51,6 @@ const b = {
if (m.fireCDcycle < m.cycle && player.speed < 0.5 && m.onGround && Math.abs(m.yOff - m.yOffGoal) < 1) {
if (b.guns[b.activeGun].ammo > 0) {
b.fireWithAmmo()
} else {
b.outOfAmmo()
}
if (m.holdingTarget) m.drop();
}
@@ -297,17 +295,17 @@ const b = {
}
},
explosionRange() {
return tech.explosiveRadius * (tech.isExplosionHarm ? 1.8 : 1) * (tech.isSmallExplosion ? 0.8 : 1) * (tech.isExplodeRadio ? 1.25 : 1)
return tech.explosiveRadius * (tech.isExplosionHarm ? 1.8 : 1) * (tech.isSmallExplosion ? 0.66 : 1) * (tech.isExplodeRadio ? 1.25 : 1)
},
explosion(where, radius, color = "rgba(255,25,0,0.6)") { // typically explode is used for some bullets with .onEnd
radius *= tech.explosiveRadius
let dist, sub, knock;
let dmg = radius * 0.013;
let dmg = radius * 0.013 * (tech.isExplosionStun ? 0.6 : 1);
if (tech.isExplosionHarm) radius *= 1.8 // 1/sqrt(2) radius -> area
if (tech.isSmallExplosion) {
color = "rgba(255,0,30,0.7)"
radius *= 0.8
dmg *= 1.6
radius *= 0.66
dmg *= 1.66
}
if (tech.isExplodeRadio) { //radiation explosion
@@ -430,6 +428,7 @@ const b = {
knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) * 0.01);
mob[i].force.x += knock.x;
mob[i].force.y += knock.y;
if (tech.isExplosionStun) mobs.statusStun(mob[i], 120)
radius *= 0.95 //reduced range for each additional explosion target
damageScale *= 0.87 //reduced damage for each additional explosion target
} else if (!mob[i].seePlayer.recall && dist < alertRange) {
@@ -437,15 +436,16 @@ const b = {
knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) * 0.006);
mob[i].force.x += knock.x;
mob[i].force.y += knock.y;
if (tech.isExplosionStun) mobs.statusStun(mob[i], 60)
}
}
}
}
},
pulse(energy, angle = m.angle) {
pulse(charge, angle = m.angle) {
let best;
let explosionRadius = 1250 * energy
let range = 3000
let explosionRadius = 6 * charge
let range = 5000
const path = [{
x: m.pos.x + 20 * Math.cos(angle),
y: m.pos.y + 20 * Math.sin(angle)
@@ -529,26 +529,23 @@ const b = {
};
}
}
if (best.who) b.explosion(path[1], explosionRadius)
if (tech.isPulseStun) {
const range = 100 + 2000 * energy
for (let i = 0, len = mob.length; i < len; ++i) {
if (mob[i].alive && !mob[i].isShielded) {
dist = Vector.magnitude(Vector.sub(path[1], mob[i].position)) - mob[i].radius;
if (dist < range) mobs.statusStun(mob[i], 30 + Math.floor(energy * 60))
}
}
if (best.who) {
b.explosion(path[1], explosionRadius)
const off = explosionRadius
b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius)
b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius)
}
//draw laser beam
ctx.beginPath();
ctx.moveTo(path[0].x, path[0].y);
ctx.lineTo(path[1].x, path[1].y);
ctx.strokeStyle = "rgba(255,0,0,0.13)"
ctx.lineWidth = 60 * energy / 0.2
ctx.stroke();
ctx.strokeStyle = "rgba(255,0,0,0.2)"
ctx.lineWidth = 18
if (charge > 50) {
ctx.strokeStyle = "rgba(255,0,0,0.10)"
ctx.lineWidth = 70
ctx.stroke();
}
ctx.strokeStyle = "rgba(255,0,0,0.25)"
ctx.lineWidth = 20
ctx.stroke();
ctx.strokeStyle = "#f00";
ctx.lineWidth = 4
@@ -557,7 +554,7 @@ const b = {
//draw little dots along the laser path
const sub = Vector.sub(path[1], path[0])
const mag = Vector.magnitude(sub)
for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) {
for (let i = 0, len = Math.floor(mag * 0.0005 * charge); i < len; i++) {
const dist = Math.random()
simulation.drawList.push({
x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5),
@@ -568,121 +565,121 @@ const b = {
});
}
},
photon(where, angle = m.angle) {
let best;
const path = [{
x: m.pos.x + 20 * Math.cos(angle),
y: m.pos.y + 20 * Math.sin(angle)
},
{
x: m.pos.x + range * Math.cos(angle),
y: m.pos.y + range * Math.sin(angle)
}
];
const vertexCollision = function(v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
}
}
}
};
//check for collisions
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
if (tech.isPulseAim) { //find mobs in line of sight
let dist = 2200
for (let i = 0, len = mob.length; i < len; i++) {
const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position))
if (explosionRadius < newDist &&
newDist < dist &&
Matter.Query.ray(map, path[0], mob[i].position).length === 0 &&
Matter.Query.ray(body, path[0], mob[i].position).length === 0) {
dist = newDist
best.who = mob[i]
path[path.length - 1] = mob[i].position
}
}
}
if (!best.who) {
vertexCollision(path[0], path[1], mob);
vertexCollision(path[0], path[1], map);
vertexCollision(path[0], path[1], body);
if (best.dist2 != Infinity) { //if hitting something
path[path.length - 1] = {
x: best.x,
y: best.y
};
}
}
if (best.who) b.explosion(path[1], explosionRadius)
// photon(where, angle = m.angle) {
// let best;
// const path = [{
// x: m.pos.x + 20 * Math.cos(angle),
// y: m.pos.y + 20 * Math.sin(angle)
// },
// {
// x: m.pos.x + range * Math.cos(angle),
// y: m.pos.y + range * Math.sin(angle)
// }
// ];
// const vertexCollision = function(v1, v1End, domain) {
// for (let i = 0; i < domain.length; ++i) {
// let vertices = domain[i].vertices;
// const len = vertices.length - 1;
// for (let j = 0; j < len; j++) {
// results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
// if (results.onLine1 && results.onLine2) {
// const dx = v1.x - results.x;
// const dy = v1.y - results.y;
// const dist2 = dx * dx + dy * dy;
// if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
// best = {
// x: results.x,
// y: results.y,
// dist2: dist2,
// who: domain[i],
// v1: vertices[j],
// v2: vertices[j + 1]
// };
// }
// }
// }
// results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
// if (results.onLine1 && results.onLine2) {
// const dx = v1.x - results.x;
// const dy = v1.y - results.y;
// const dist2 = dx * dx + dy * dy;
// if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
// best = {
// x: results.x,
// y: results.y,
// dist2: dist2,
// who: domain[i],
// v1: vertices[0],
// v2: vertices[len]
// };
// }
// }
// }
// };
// //check for collisions
// best = {
// x: null,
// y: null,
// dist2: Infinity,
// who: null,
// v1: null,
// v2: null
// };
// if (tech.isPulseAim) { //find mobs in line of sight
// let dist = 2200
// for (let i = 0, len = mob.length; i < len; i++) {
// const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position))
// if (explosionRadius < newDist &&
// newDist < dist &&
// Matter.Query.ray(map, path[0], mob[i].position).length === 0 &&
// Matter.Query.ray(body, path[0], mob[i].position).length === 0) {
// dist = newDist
// best.who = mob[i]
// path[path.length - 1] = mob[i].position
// }
// }
// }
// if (!best.who) {
// vertexCollision(path[0], path[1], mob);
// vertexCollision(path[0], path[1], map);
// vertexCollision(path[0], path[1], body);
// if (best.dist2 != Infinity) { //if hitting something
// path[path.length - 1] = {
// x: best.x,
// y: best.y
// };
// }
// }
// if (best.who) b.explosion(path[1], explosionRadius)
//draw laser beam
ctx.beginPath();
ctx.moveTo(path[0].x, path[0].y);
ctx.lineTo(path[1].x, path[1].y);
ctx.strokeStyle = "rgba(255,0,0,0.13)"
ctx.lineWidth = 60 * energy / 0.2
ctx.stroke();
ctx.strokeStyle = "rgba(255,0,0,0.2)"
ctx.lineWidth = 18
ctx.stroke();
ctx.strokeStyle = "#f00";
ctx.lineWidth = 4
ctx.stroke();
// //draw laser beam
// ctx.beginPath();
// ctx.moveTo(path[0].x, path[0].y);
// ctx.lineTo(path[1].x, path[1].y);
// ctx.strokeStyle = "rgba(255,0,0,0.13)"
// ctx.lineWidth = 60 * energy / 0.2
// ctx.stroke();
// ctx.strokeStyle = "rgba(255,0,0,0.2)"
// ctx.lineWidth = 18
// ctx.stroke();
// ctx.strokeStyle = "#f00";
// ctx.lineWidth = 4
// ctx.stroke();
//draw little dots along the laser path
const sub = Vector.sub(path[1], path[0])
const mag = Vector.magnitude(sub)
for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) {
const dist = Math.random()
simulation.drawList.push({
x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5),
y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5),
radius: 1 + 4 * Math.random(),
color: "rgba(255,0,0,0.5)",
time: Math.floor(2 + 33 * Math.random() * Math.random())
});
}
},
// //draw little dots along the laser path
// const sub = Vector.sub(path[1], path[0])
// const mag = Vector.magnitude(sub)
// for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) {
// const dist = Math.random()
// simulation.drawList.push({
// x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5),
// y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5),
// radius: 1 + 4 * Math.random(),
// color: "rgba(255,0,0,0.5)",
// time: Math.floor(2 + 33 * Math.random() * Math.random())
// });
// }
// },
grenade() {
},
@@ -2226,7 +2223,7 @@ const b = {
if (this.target.isShielded) {
this.target.damage(b.dmgScale * this.damage, true); //shield damage bypass
//shrink if mob is shielded
const SCALE = 1 - 0.014 / tech.isBulletsLastLonger
const SCALE = 1 - 0.01 / tech.isBulletsLastLonger
Matter.Body.scale(this, SCALE, SCALE);
this.radius *= SCALE;
} else {
@@ -2873,7 +2870,7 @@ const b = {
let closeDist = this.range;
for (let i = 0, len = mob.length; i < len; ++i) {
const DIST = Vector.magnitude(Vector.sub(this.position, mob[i].position)) - mob[i].radius;
if (DIST < closeDist && mob[i].isDropPowerUp &&
if (DIST < closeDist &&
Matter.Query.ray(map, this.position, mob[i].position).length === 0 &&
Matter.Query.ray(body, this.position, mob[i].position).length === 0) {
closeDist = DIST;
@@ -4049,16 +4046,18 @@ const b = {
name: "foam",
description: "spray bubbly foam that <strong>sticks</strong> to mobs<br><strong class='color-s'>slows</strong> mobs and does <strong class='color-d'>damage</strong> over time",
ammo: 0,
ammoPack: 30,
ammoPack: 27,
have: false,
charge: 0,
isDischarge: false,
do() {
if (this.charge > 0) {
//draw charge level
ctx.fillStyle = "rgba(0,50,50,0.2)";
ctx.fillStyle = "rgba(0,50,50,0.3)";
ctx.beginPath();
ctx.arc(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 10 * Math.sqrt(this.charge), 0, 2 * Math.PI);
const radius = 10 * Math.sqrt(this.charge)
const mag = 11 + radius
ctx.arc(m.pos.x + mag * Math.cos(m.angle), m.pos.y + mag * Math.sin(m.angle), radius, 0, 2 * Math.PI);
ctx.fill();
if (this.isDischarge) {
@@ -4094,15 +4093,15 @@ const b = {
x: position.x,
y: position.y,
radius: 5,
color: "rgba(0,0,0,0.1)",
color: "rgba(0,50,50,0.3)",
time: 15 * tech.foamFutureFire
});
setTimeout(() => {
if (!simulation.paused) {
b.foam(position, Vector.rotate(velocity, spread), radius)
bullet[bullet.length - 1].damage = (1 + 1.27 * tech.foamFutureFire) * (tech.isFastFoam ? 0.048 : 0.012) //double damage
bullet[bullet.length - 1].damage = (1 + 0.9 * tech.foamFutureFire) * (tech.isFastFoam ? 0.048 : 0.012) //double damage
}
}, 250 * tech.foamFutureFire);
}, 300 * tech.foamFutureFire);
} else {
b.foam(position, Vector.rotate(velocity, spread), radius)
}
@@ -4467,14 +4466,43 @@ const b = {
ammo: 0,
ammoPack: Infinity,
have: false,
nextFireCycle: 0, //use to remember how longs its been since last fire, used to reset count
charge: 0,
do() {},
fire() {
},
fire() {},
chooseFireMethod() {
this.do = () => {};
if (tech.isPulseLaser) {
this.fire = this.firePulse
this.fire = () => {
const drain = 0.01 * tech.isLaserDiode / b.fireCD
if (m.energy > drain) {
m.energy -= m.fieldRegen
if (this.charge < 50 * m.maxEnergy) {
m.energy -= drain
this.charge += 1 / b.fireCD
}
}
}
this.do = () => {
if (this.charge > 0) {
//draw charge level
ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`;
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 4.2 * Math.sqrt(this.charge), 0, 2 * Math.PI);
ctx.fill();
if (!input.fire) {
m.fireCDcycle = m.cycle + 10; // cool down
if (tech.beamSplitter) {
const divergence = m.crouch ? 0.2 : 0.5
const angle = m.angle - tech.beamSplitter * divergence / 2
for (let i = 0; i < 1 + tech.beamSplitter; i++) b.pulse(this.charge, angle + i * divergence)
} else {
b.pulse(1.75 * this.charge, m.angle)
}
this.charge = 0;
}
}
};
} else if (tech.beamSplitter) {
this.fire = this.fireSplit
} else if (tech.historyLaser) {
@@ -4627,27 +4655,27 @@ const b = {
ctx.stroke();
}
},
firePulse() {
m.fireCDcycle = m.cycle + Math.floor((tech.isPulseAim ? 25 : 50) * b.fireCD); // cool down
let energy = 0.3 * Math.min(m.energy, 1.5)
m.energy -= energy * tech.isLaserDiode
if (tech.beamSplitter) {
// energy *= Math.pow(0.9, tech.beamSplitter)
// b.pulse(energy, m.angle)
// for (let i = 1; i < 1 + tech.beamSplitter; i++) {
// b.pulse(energy, m.angle - i * 0.27)
// b.pulse(energy, m.angle + i * 0.27)
// }
const divergence = m.crouch ? 0.2 : 0.5
const angle = m.angle - tech.beamSplitter * divergence / 2
for (let i = 0; i < 1 + tech.beamSplitter; i++) {
b.pulse(energy, angle + i * divergence)
}
// firePulse() {
// m.fireCDcycle = m.cycle + Math.floor((tech.isPulseAim ? 25 : 50) * b.fireCD); // cool down
// let energy = 0.3 * Math.min(m.energy, 1.5)
// m.energy -= energy * tech.isLaserDiode
// if (tech.beamSplitter) {
// // energy *= Math.pow(0.9, tech.beamSplitter)
// // b.pulse(energy, m.angle)
// // for (let i = 1; i < 1 + tech.beamSplitter; i++) {
// // b.pulse(energy, m.angle - i * 0.27)
// // b.pulse(energy, m.angle + i * 0.27)
// // }
// const divergence = m.crouch ? 0.2 : 0.5
// const angle = m.angle - tech.beamSplitter * divergence / 2
// for (let i = 0; i < 1 + tech.beamSplitter; i++) {
// b.pulse(energy, angle + i * divergence)
// }
} else {
b.pulse(energy, m.angle)
}
},
// } else {
// b.pulse(energy, m.angle)
// }
// },
},
],
gunRewind: { //this gun is added with a tech

View File

@@ -794,17 +794,7 @@ window.addEventListener("keydown", function(event) {
simulation.testing = true;
simulation.loop = simulation.testingLoop
if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'inline'
if (!simulation.isCheating) {
simulation.isCheating = true;
level.levelAnnounce();
lore.techCount = 0;
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isLore) {
tech.tech[i].frequency = 0;
tech.tech[i].count = 0;
}
}
}
if (simulation.testing) tech.setCheating();
simulation.makeTextLog(
`<table class="pause-table">
<tr>

View File

@@ -12,14 +12,15 @@ const level = {
start() {
if (level.levelsCleared === 0) { //this code only runs on the first level
// simulation.enableConstructMode() //used to build maps in testing mode
// level.difficultyIncrease(50)
// level.difficultyIncrease(30)
// simulation.zoomScale = 1000;
// simulation.setZoom();
// m.setField("nano-scale manufacturing")
// b.giveGuns("foam")
// b.giveGuns("laser")
// tech.isExplodeRadio = true
// for (let i = 0; i < 9; i++) tech.giveTech("auto-loading heuristics")
// tech.giveTech("superfluidity")
// tech.giveTech("pulse")
// tech.giveTech("ice crystal nucleation")
// tech.giveTech("needle gun")
// tech.giveTech("cardinality")
@@ -1109,9 +1110,8 @@ const level = {
spawn.mapRect(6700, -1800, 800, 2600); //right wall
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
// simulation.difficulty = 30
spawn.starter(1900, -500, 100) //big boy
spawn.grower(1900, -500)
spawn.starter(1900, -500, 200) //big boy
// spawn.grower(1900, -500)
// spawn.pulsarBoss(1900, -500)
// spawn.shooterBoss(1900, -500)
// spawn.launcherBoss(1200, -500)

View File

@@ -1145,20 +1145,20 @@ const m = {
m.fieldCDcycle = m.cycle + 15;
m.isHolding = false;
//bullet-like collisions
m.holdingTarget.collisionFilter.category = cat.bullet; //cat.body;
// m.holdingTarget.collisionFilter.category = cat.bullet; //cat.body;
m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield;
//check every second to see if player is away from thrown body, and make solid
const solid = function(that) {
const dx = that.position.x - player.position.x;
const dy = that.position.y - player.position.y;
if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) {
that.collisionFilter.category = cat.body; //make solid
// that.collisionFilter.category = cat.body; //make solid
that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now
} else {
setTimeout(solid, 50, that);
setTimeout(solid, 40, that);
}
};
setTimeout(solid, 500, m.holdingTarget);
setTimeout(solid, 200, m.holdingTarget);
const charge = Math.min(m.throwCharge / 5, 1)
//***** scale throw speed with the first number, 80 *****

View File

@@ -1621,9 +1621,9 @@ const spawn = {
ctx.moveTo(this.vertices[1].x, this.vertices[1].y)
ctx.lineTo(this.fireTarget.x, this.fireTarget.y)
ctx.lineWidth = 20;
ctx.strokeStyle = "rgba(120,0,255,0.2)";
ctx.strokeStyle = "rgba(120,0,255,0.3)";
ctx.stroke();
ctx.lineWidth = 4;
ctx.lineWidth = 5;
ctx.strokeStyle = "rgba(120,0,255,1)";
ctx.stroke();
} else { //delay before firing
@@ -1631,7 +1631,7 @@ const spawn = {
//draw explosion outline
ctx.beginPath();
ctx.arc(this.fireTarget.x, this.fireTarget.y, this.pulseRadius, 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay
ctx.fillStyle = "rgba(120,0,255,0.05)";
ctx.fillStyle = "rgba(120,0,255,0.07)";
ctx.fill();
//draw path from mob to explosion
ctx.beginPath();
@@ -1639,7 +1639,7 @@ const spawn = {
ctx.lineTo(this.fireTarget.x, this.fireTarget.y)
ctx.setLineDash([40 * Math.random(), 200 * Math.random()]);
ctx.lineWidth = 2;
ctx.strokeStyle = "rgba(120,0,255,0.2)";
ctx.strokeStyle = "rgba(120,0,255,0.3)";
ctx.stroke();
ctx.setLineDash([0, 0]);
}
@@ -1741,9 +1741,9 @@ const spawn = {
ctx.moveTo(this.vertices[1].x, this.vertices[1].y)
ctx.lineTo(this.fireTarget.x, this.fireTarget.y)
ctx.lineWidth = 20;
ctx.strokeStyle = "rgba(255,0,100,0.2)";
ctx.strokeStyle = "rgba(255,0,100,0.3)";
ctx.stroke();
ctx.lineWidth = 4;
ctx.lineWidth = 5;
ctx.strokeStyle = "rgba(255,0,100,1)";
ctx.stroke();
} else { //delay before firing
@@ -1754,7 +1754,7 @@ const spawn = {
//draw explosion outline
ctx.beginPath();
ctx.arc(this.fireTarget.x, this.fireTarget.y, this.pulseRadius, 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay
ctx.fillStyle = "rgba(255,0,100,0.05)";
ctx.fillStyle = "rgba(255,0,100,0.07)";
ctx.fill();
//draw path from mob to explosion
ctx.beginPath();
@@ -1762,7 +1762,7 @@ const spawn = {
ctx.lineTo(this.fireTarget.x, this.fireTarget.y)
ctx.setLineDash([40 * Math.random(), 200 * Math.random()]);
ctx.lineWidth = 2;
ctx.strokeStyle = "rgba(255,0,100,0.2)";
ctx.strokeStyle = "rgba(255,0,100,0.3)";
ctx.stroke();
ctx.setLineDash([0, 0]);
}

View File

@@ -107,6 +107,17 @@
}
}
},
setCheating() {
simulation.isCheating = true;
level.levelAnnounce();
lore.techCount = 0;
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isLore) {
tech.tech[i].frequency = 0;
tech.tech[i].count = 0;
}
}
},
haveGunCheck(name) {
if (
!build.isExperimentSelection &&
@@ -745,7 +756,7 @@
},
{
name: "nitroglycerin",
description: "increase <strong class='color-e'>explosive</strong> <strong class='color-d'>damage</strong> by <strong>60%</strong><br>decrease <strong class='color-e'>explosive</strong> <strong>radius</strong> by <strong>20%</strong>",
description: "increase <strong class='color-e'>explosive</strong> <strong class='color-d'>damage</strong> by <strong>66%</strong><br>decrease <strong class='color-e'>explosive</strong> <strong>radius</strong> by <strong>33%</strong>",
maxCount: 1,
count: 0,
frequency: 2,
@@ -778,6 +789,24 @@
tech.isExplosionHarm = false;
}
},
{
name: "shock wave",
description: "<strong class='color-e'>explosions</strong> <strong>stun</strong> mobs for <strong>1-2</strong> seconds<br>decrease <strong class='color-e'>explosive</strong> <strong class='color-d'>damage</strong> by <strong>40%</strong>",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
allowed() {
return !tech.isExplodeRadio && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1)
},
requires: "an explosive damage source, not iridium-192",
effect() {
tech.isExplosionStun = true;
},
remove() {
tech.isExplosionStun = false;
}
},
{
name: "electric reactive armor",
// description: "<strong class='color-e'>explosions</strong> do no <strong class='color-harm'>harm</strong><br> while your <strong class='color-f'>energy</strong> is above <strong>98%</strong>",
@@ -1443,9 +1472,9 @@
frequency: 4,
frequencyDefault: 4,
allowed() {
return tech.throwChargeRate > 1 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole"
return tech.throwChargeRate > 1 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isEnergyHealth
},
requires: "mass driver, a field that can hold things",
requires: "mass driver, a field that can hold things, not mass-energy",
effect() {
tech.isBlockHarm = true
},
@@ -1687,23 +1716,6 @@
tech.isHarmFreeze = false;
}
},
{
name: "osmoprotectant",
description: `collisions with <strong>stunned</strong> or <strong class='color-s'>frozen</strong> mobs<br>cause you <strong>no</strong> <strong class='color-harm'>harm</strong>`,
maxCount: 1,
count: 0,
frequency: 2,
allowed() {
return tech.isStunField || tech.isPulseStun || tech.oneSuperBall || tech.isHarmFreeze || tech.isIceField || tech.relayIce || tech.isIceCrystals || tech.isSporeFreeze || tech.isAoESlow || tech.isFreezeMobs || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isWormholeDamage || tech.blockingIce > 1
},
requires: "a freezing or stunning effect",
effect() {
tech.isFreezeHarmImmune = true;
},
remove() {
tech.isFreezeHarmImmune = false;
}
},
{
name: "superfluidity",
description: "<strong class='color-s'>freeze</strong> effects are applied to a small area",
@@ -1721,6 +1733,40 @@
tech.isAoESlow = false
}
},
{
name: "osmoprotectant",
description: `collisions with <strong>stunned</strong> or <strong class='color-s'>frozen</strong> mobs<br>cause you <strong>no</strong> <strong class='color-harm'>harm</strong>`,
maxCount: 1,
count: 0,
frequency: 2,
allowed() {
return tech.isStunField || tech.isExplosionStun || tech.oneSuperBall || tech.isHarmFreeze || tech.isIceField || tech.relayIce || tech.isIceCrystals || tech.isSporeFreeze || tech.isAoESlow || tech.isFreezeMobs || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isWormholeDamage || tech.blockingIce > 1
},
requires: "a freezing or stunning effect",
effect() {
tech.isFreezeHarmImmune = true;
},
remove() {
tech.isFreezeHarmImmune = false;
}
},
{
name: "fracture analysis",
description: "bullet impacts do <strong>400%</strong> <strong class='color-d'>damage</strong><br>to <strong>stunned</strong> mobs",
maxCount: 1,
count: 0,
frequency: 2,
allowed() {
return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isPerpetualStun || tech.isExplosionStun
},
requires: "a stun effect",
effect() {
tech.isCrit = true;
},
remove() {
tech.isCrit = false;
}
},
{
name: "ablative drones",
description: "rebuild your broken parts as <strong>drones</strong><br>chance to occur after receiving <strong class='color-harm'>harm</strong>",
@@ -2124,9 +2170,9 @@
frequency: 4,
frequencyDefault: 4,
allowed() {
return tech.isDamageAfterKill
return tech.isDamageAfterKill && !tech.isEnergyHealth
},
requires: "dormancy",
requires: "dormancy, not mass-energy",
effect() {
tech.isHarmReduceAfterKill = true;
},
@@ -2553,6 +2599,7 @@
if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech
tech.setupAllTech(); // remove all tech
if (simulation.isCheating) tech.setCheating();
lore.techCount = 0;
// tech.addLoreTechToPool();
for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups
@@ -4006,7 +4053,7 @@
},
{
name: "quantum foam",
description: "<strong>foam</strong> gun fires <strong>0.25</strong> seconds into the <strong>future</strong><br>increase <strong>foam</strong> gun <strong class='color-d'>damage</strong> by <strong>127%</strong>",
description: "<strong>foam</strong> gun fires <strong>0.30</strong> seconds into the <strong>future</strong><br>increase <strong>foam</strong> gun <strong class='color-d'>damage</strong> by <strong>90%</strong>",
isGunTech: true,
maxCount: 9,
count: 0,
@@ -4302,7 +4349,7 @@
},
{
name: "pulse",
description: "use <strong>25%</strong> of your <strong class='color-f'>energy</strong> in a pulsed <strong class='color-laser'>laser</strong><br>that instantly initiates a fusion <strong class='color-e'>explosion</strong>",
description: "charge your <strong class='color-f'>energy</strong> and release it as a<br><strong class='color-laser'>laser</strong> pulse that initiates an <strong class='color-e'>explosion</strong> cluster",
isGunTech: true,
maxCount: 1,
count: 0,
@@ -4326,27 +4373,9 @@
}
}
},
{
name: "shock wave",
description: "mobs caught in <strong class='color-laser'>pulse</strong>'s explosion are <strong>stunned</strong><br>for up to <strong>2 seconds</strong>",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
allowed() {
return tech.isPulseLaser
},
requires: "pulse",
effect() {
tech.isPulseStun = true;
},
remove() {
tech.isPulseStun = false;
}
},
{
name: "neocognitron",
description: "<strong class='color-laser'>pulse</strong> automatically <strong>aims</strong> at a nearby mob<br><strong>50%</strong> decreased <strong><em>delay</em></strong> after firing",
description: "<strong class='color-laser'>pulse</strong> automatically <strong>aims</strong> at a nearby mob",
isGunTech: true,
maxCount: 1,
count: 0,
@@ -4366,6 +4395,28 @@
//************************************************** field
//************************************************** tech
//**************************************************
{
name: "frequency resonance",
description: "<strong>standing wave harmonics</strong> shield is retuned<br>increase <strong>size</strong> and <strong>blocking</strong> efficiency by <strong>50%</strong>",
isFieldTech: true,
maxCount: 9,
count: 0,
frequency: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "standing wave harmonics"
},
requires: "standing wave harmonics",
effect() {
tech.frequencyResonance = this.count + 1 // +1 because count updates later
m.fieldRange = 175 + 175 * 0.25 * tech.frequencyResonance
m.fieldShieldingScale = Math.pow(0.5, tech.frequencyResonance)
},
remove() {
m.fieldRange = 175;
m.fieldShieldingScale = 1;
tech.frequencyResonance = 0
}
},
{
name: "bremsstrahlung",
description: "<strong>blocking</strong> does <strong class='color-d'>damage</strong> to mobs",
@@ -4402,28 +4453,6 @@
tech.blockingIce = 0;
}
},
{
name: "frequency resonance",
description: "<strong>standing wave harmonics</strong> shield is retuned<br>increase <strong>size</strong> and <strong>blocking</strong> efficiency by <strong>50%</strong>",
isFieldTech: true,
maxCount: 9,
count: 0,
frequency: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "standing wave harmonics"
},
requires: "standing wave harmonics",
effect() {
tech.frequencyResonance = this.count + 1 // +1 because count updates later
m.fieldRange = 175 + 175 * 0.25 * tech.frequencyResonance
m.fieldShieldingScale = Math.pow(0.5, tech.frequencyResonance)
},
remove() {
m.fieldRange = 175;
m.fieldShieldingScale = 1;
tech.frequencyResonance = 0
}
},
{
name: "flux pinning",
description: "blocking with your <strong>field</strong><br><strong>stuns</strong> mobs for <strong>+2</strong> second",
@@ -4442,24 +4471,6 @@
tech.isStunField = 0;
}
},
{
name: "fracture analysis",
description: "bullet impacts do <strong>400%</strong> <strong class='color-d'>damage</strong><br>to <strong>stunned</strong> mobs",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
allowed() {
return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isPerpetualStun
},
requires: "a stun effect",
effect() {
tech.isCrit = true;
},
remove() {
tech.isCrit = false;
}
},
{
name: "eddy current brake",
description: "project a field that limits the <strong>top speed</strong> of mobs<br>field <strong>radius</strong> scales with stored <strong class='color-f'>energy</strong>",
@@ -4478,25 +4489,6 @@
tech.isPerfectBrake = false;
}
},
{
name: "pair production",
description: "picking up a <strong>power up</strong> gives you <strong>250</strong> <strong class='color-f'>energy</strong>",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || m.fieldUpgrades[m.fieldMode].name === "pilot wave"
},
requires: "nano-scale manufacturing",
effect: () => {
tech.isMassEnergy = true // used in m.grabPowerUp
m.energy += 3
},
remove() {
tech.isMassEnergy = false;
}
},
{
name: "bot manufacturing",
description: "use <strong>nano-scale manufacturing</strong><br>to build <strong>3</strong> random <strong class='color-bot'>bots</strong>",
@@ -4608,6 +4600,25 @@
},
remove() {}
},
{
name: "pair production",
description: "picking up a <strong>power up</strong> gives you <strong>250</strong> <strong class='color-f'>energy</strong>",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || m.fieldUpgrades[m.fieldMode].name === "pilot wave"
},
requires: "nano-scale manufacturing",
effect: () => {
tech.isMassEnergy = true // used in m.grabPowerUp
m.energy += 3
},
remove() {
tech.isMassEnergy = false;
}
},
{
name: "mycelium manufacturing",
description: "<strong>nano-scale manufacturing</strong> is repurposed<br>excess <strong class='color-f'>energy</strong> used to grow <strong class='color-p' style='letter-spacing: 2px;'>spores</strong>",
@@ -4688,9 +4699,9 @@
count: 0,
frequency: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "negative mass field"
return m.fieldUpgrades[m.fieldMode].name === "negative mass field" && !tech.isEnergyHealth
},
requires: "negative mass field",
requires: "negative mass field, not mass-energy",
effect() {
tech.isHarmReduce = true
},
@@ -6304,7 +6315,7 @@
isSporeFollow: null,
isNailRadiation: null,
isEnergyHealth: null,
isPulseStun: null,
isExplosionStun: null,
restDamage: null,
isRPG: null,
missileCount: null,