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

BIN
.DS_Store vendored

Binary file not shown.

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

View File

@@ -794,17 +794,7 @@ window.addEventListener("keydown", function(event) {
simulation.testing = true; simulation.testing = true;
simulation.loop = simulation.testingLoop simulation.loop = simulation.testingLoop
if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'inline' if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'inline'
if (!simulation.isCheating) { if (simulation.testing) tech.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;
}
}
}
simulation.makeTextLog( simulation.makeTextLog(
`<table class="pause-table"> `<table class="pause-table">
<tr> <tr>

View File

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

View File

@@ -1145,20 +1145,20 @@ const m = {
m.fieldCDcycle = m.cycle + 15; m.fieldCDcycle = m.cycle + 15;
m.isHolding = false; m.isHolding = false;
//bullet-like collisions //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; 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 //check every second to see if player is away from thrown body, and make solid
const solid = function(that) { const solid = function(that) {
const dx = that.position.x - player.position.x; const dx = that.position.x - player.position.x;
const dy = that.position.y - player.position.y; const dy = that.position.y - player.position.y;
if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { 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 that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now
} else { } 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) const charge = Math.min(m.throwCharge / 5, 1)
//***** scale throw speed with the first number, 80 ***** //***** 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.moveTo(this.vertices[1].x, this.vertices[1].y)
ctx.lineTo(this.fireTarget.x, this.fireTarget.y) ctx.lineTo(this.fireTarget.x, this.fireTarget.y)
ctx.lineWidth = 20; ctx.lineWidth = 20;
ctx.strokeStyle = "rgba(120,0,255,0.2)"; ctx.strokeStyle = "rgba(120,0,255,0.3)";
ctx.stroke(); ctx.stroke();
ctx.lineWidth = 4; ctx.lineWidth = 5;
ctx.strokeStyle = "rgba(120,0,255,1)"; ctx.strokeStyle = "rgba(120,0,255,1)";
ctx.stroke(); ctx.stroke();
} else { //delay before firing } else { //delay before firing
@@ -1631,7 +1631,7 @@ const spawn = {
//draw explosion outline //draw explosion outline
ctx.beginPath(); ctx.beginPath();
ctx.arc(this.fireTarget.x, this.fireTarget.y, this.pulseRadius, 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay 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(); ctx.fill();
//draw path from mob to explosion //draw path from mob to explosion
ctx.beginPath(); ctx.beginPath();
@@ -1639,7 +1639,7 @@ const spawn = {
ctx.lineTo(this.fireTarget.x, this.fireTarget.y) ctx.lineTo(this.fireTarget.x, this.fireTarget.y)
ctx.setLineDash([40 * Math.random(), 200 * Math.random()]); ctx.setLineDash([40 * Math.random(), 200 * Math.random()]);
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.strokeStyle = "rgba(120,0,255,0.2)"; ctx.strokeStyle = "rgba(120,0,255,0.3)";
ctx.stroke(); ctx.stroke();
ctx.setLineDash([0, 0]); ctx.setLineDash([0, 0]);
} }
@@ -1741,9 +1741,9 @@ const spawn = {
ctx.moveTo(this.vertices[1].x, this.vertices[1].y) ctx.moveTo(this.vertices[1].x, this.vertices[1].y)
ctx.lineTo(this.fireTarget.x, this.fireTarget.y) ctx.lineTo(this.fireTarget.x, this.fireTarget.y)
ctx.lineWidth = 20; ctx.lineWidth = 20;
ctx.strokeStyle = "rgba(255,0,100,0.2)"; ctx.strokeStyle = "rgba(255,0,100,0.3)";
ctx.stroke(); ctx.stroke();
ctx.lineWidth = 4; ctx.lineWidth = 5;
ctx.strokeStyle = "rgba(255,0,100,1)"; ctx.strokeStyle = "rgba(255,0,100,1)";
ctx.stroke(); ctx.stroke();
} else { //delay before firing } else { //delay before firing
@@ -1754,7 +1754,7 @@ const spawn = {
//draw explosion outline //draw explosion outline
ctx.beginPath(); ctx.beginPath();
ctx.arc(this.fireTarget.x, this.fireTarget.y, this.pulseRadius, 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay 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(); ctx.fill();
//draw path from mob to explosion //draw path from mob to explosion
ctx.beginPath(); ctx.beginPath();
@@ -1762,7 +1762,7 @@ const spawn = {
ctx.lineTo(this.fireTarget.x, this.fireTarget.y) ctx.lineTo(this.fireTarget.x, this.fireTarget.y)
ctx.setLineDash([40 * Math.random(), 200 * Math.random()]); ctx.setLineDash([40 * Math.random(), 200 * Math.random()]);
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.strokeStyle = "rgba(255,0,100,0.2)"; ctx.strokeStyle = "rgba(255,0,100,0.3)";
ctx.stroke(); ctx.stroke();
ctx.setLineDash([0, 0]); 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) { haveGunCheck(name) {
if ( if (
!build.isExperimentSelection && !build.isExperimentSelection &&
@@ -745,7 +756,7 @@
}, },
{ {
name: "nitroglycerin", 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, maxCount: 1,
count: 0, count: 0,
frequency: 2, frequency: 2,
@@ -778,6 +789,24 @@
tech.isExplosionHarm = false; 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", 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>", // 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, frequency: 4,
frequencyDefault: 4, frequencyDefault: 4,
allowed() { 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() { effect() {
tech.isBlockHarm = true tech.isBlockHarm = true
}, },
@@ -1687,23 +1716,6 @@
tech.isHarmFreeze = false; 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", name: "superfluidity",
description: "<strong class='color-s'>freeze</strong> effects are applied to a small area", description: "<strong class='color-s'>freeze</strong> effects are applied to a small area",
@@ -1721,6 +1733,40 @@
tech.isAoESlow = false 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", name: "ablative drones",
description: "rebuild your broken parts as <strong>drones</strong><br>chance to occur after receiving <strong class='color-harm'>harm</strong>", 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, frequency: 4,
frequencyDefault: 4, frequencyDefault: 4,
allowed() { allowed() {
return tech.isDamageAfterKill return tech.isDamageAfterKill && !tech.isEnergyHealth
}, },
requires: "dormancy", requires: "dormancy, not mass-energy",
effect() { effect() {
tech.isHarmReduceAfterKill = true; tech.isHarmReduceAfterKill = true;
}, },
@@ -2553,6 +2599,7 @@
if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech
tech.setupAllTech(); // remove all tech tech.setupAllTech(); // remove all tech
if (simulation.isCheating) tech.setCheating();
lore.techCount = 0; lore.techCount = 0;
// tech.addLoreTechToPool(); // 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 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", 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, isGunTech: true,
maxCount: 9, maxCount: 9,
count: 0, count: 0,
@@ -4302,7 +4349,7 @@
}, },
{ {
name: "pulse", 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, isGunTech: true,
maxCount: 1, maxCount: 1,
count: 0, 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", 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, isGunTech: true,
maxCount: 1, maxCount: 1,
count: 0, count: 0,
@@ -4366,6 +4395,28 @@
//************************************************** field //************************************************** field
//************************************************** tech //************************************************** 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", name: "bremsstrahlung",
description: "<strong>blocking</strong> does <strong class='color-d'>damage</strong> to mobs", description: "<strong>blocking</strong> does <strong class='color-d'>damage</strong> to mobs",
@@ -4402,28 +4453,6 @@
tech.blockingIce = 0; 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", name: "flux pinning",
description: "blocking with your <strong>field</strong><br><strong>stuns</strong> mobs for <strong>+2</strong> second", description: "blocking with your <strong>field</strong><br><strong>stuns</strong> mobs for <strong>+2</strong> second",
@@ -4442,24 +4471,6 @@
tech.isStunField = 0; 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", 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>", 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; 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", name: "bot manufacturing",
description: "use <strong>nano-scale manufacturing</strong><br>to build <strong>3</strong> random <strong class='color-bot'>bots</strong>", 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() {} 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", 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>", 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, count: 0,
frequency: 2, frequency: 2,
allowed() { 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() { effect() {
tech.isHarmReduce = true tech.isHarmReduce = true
}, },
@@ -6304,7 +6315,7 @@
isSporeFollow: null, isSporeFollow: null,
isNailRadiation: null, isNailRadiation: null,
isEnergyHealth: null, isEnergyHealth: null,
isPulseStun: null, isExplosionStun: null,
restDamage: null, restDamage: null,
isRPG: null, isRPG: null,
missileCount: null, missileCount: null,

View File

@@ -1,5 +1,11 @@
******************************************************** NEXT PATCH ******************************************************** ******************************************************** NEXT PATCH ********************************************************
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
******************************************************** BUGS ******************************************************** ******************************************************** BUGS ********************************************************
@@ -18,9 +24,9 @@ fix door.isOpen actually meaning isClosed?
wasn't able to understand bug after extensive testing wasn't able to understand bug after extensive testing
had tech: complex spin statistics had tech: complex spin statistics
(always) make it so that when you are immune to harm you can either jump on mobs or you pass through them make it so that when you are immune to harm you can either jump on mobs or you pass through them
(always) is there a way to check if the player is stuck inside the map or block is there a way to check if the player is stuck inside the map or block
trigger a short term non-collide if that occurs trigger a short term non-collide if that occurs
(intermittent, but almost every time) bug - capping the fps causes random slow downs, that can be fixed with pause (intermittent, but almost every time) bug - capping the fps causes random slow downs, that can be fixed with pause
@@ -33,8 +39,16 @@ fix door.isOpen actually meaning isClosed?
******************************************************** TODO ******************************************************** ******************************************************** TODO ********************************************************
edit foam gun text? avoid taking collision damage by teleporting to a random power up
add a foam charge meter would be nice removes the power up
make a tech that improves all charge guns
for: pulse, foam, rail gun
effect:
faster charge rate?
fire speed already does that...
harm reduction while charging
less ammo/energy used while charging?
apply the new gun.do functions to other guns apply the new gun.do functions to other guns
rail gun rail gun