incendiary ammunition

bug - spider boss move at full speed again (watch out)
bots that uses energy stop when you hit 50% energy if you have mass-energy mod

you get a warning before you overwrite your current gun due to integrated armament

gun - flak is removed
mod: High-explosive incendiary, or incendiary rounds
  shotgun: several large explosions
  nail gun: rapid fire explosions
  drones: drones explode on impact
  super balls: explode on contact
This commit is contained in:
landgreen
2020-11-14 05:37:27 -08:00
parent 72e61eab78
commit 6e90ed2376
9 changed files with 876 additions and 869 deletions

View File

@@ -928,17 +928,22 @@ const b = {
deathCycles: 110 + RADIUS * 5, deathCycles: 110 + RADIUS * 5,
isImproved: false, isImproved: false,
beforeDmg(who) { beforeDmg(who) {
//move away from target after hitting if (mod.isIncendiary) {
const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20) b.explosion(this.position, 120 + (Math.random() - 0.5) * 60); //makes bullet do explosive damage at end
Matter.Body.setVelocity(this, { this.endCycle = 0
x: unit.x, } else {
y: unit.y //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
});
this.lockedOn = null this.lockedOn = null
if (this.endCycle > game.cycle + this.deathCycles) { if (this.endCycle > game.cycle + this.deathCycles) {
this.endCycle -= 60 this.endCycle -= 60
if (game.cycle + this.deathCycles > this.endCycle) this.endCycle = game.cycle + this.deathCycles if (game.cycle + this.deathCycles > this.endCycle) this.endCycle = game.cycle + this.deathCycles
}
} }
}, },
onEnd() {}, onEnd() {},
@@ -1377,6 +1382,7 @@ const b = {
dmg: 0, // 0.14 //damage done in addition to the damage from momentum dmg: 0, // 0.14 //damage done in addition to the damage from momentum
minDmgSpeed: 2, minDmgSpeed: 2,
lookFrequency: 40 + Math.floor(7 * Math.random()), lookFrequency: 40 + Math.floor(7 * Math.random()),
drainThreshold: mod.isEnergyHealth ? 0.5 : 0.15,
acceleration: 0.0015 * (1 + 0.3 * Math.random()), acceleration: 0.0015 * (1 + 0.3 * Math.random()),
range: 700 * (1 + 0.1 * Math.random()) + 250 * mod.isLaserBotUpgrade, range: 700 * (1 + 0.1 * Math.random()) + 250 * mod.isLaserBotUpgrade,
followRange: 150 + Math.floor(30 * Math.random()), followRange: 150 + Math.floor(30 * Math.random()),
@@ -1430,47 +1436,9 @@ const b = {
} }
} }
//hit target with laser //hit target with laser
if (this.lockedOn && this.lockedOn.alive && mech.energy > 0.15) { if (this.lockedOn && this.lockedOn.alive && mech.energy > this.drainThreshold) {
mech.energy -= 0.0012 * mod.isLaserDiode mech.energy -= 0.0012 * mod.isLaserDiode
// const sub = Vector.sub(this.lockedOn.position, this.vertices[0])
// const angle = Math.atan2(sub.y, sub.x);
b.laser(this.vertices[0], this.lockedOn.position, b.dmgScale * (0.06 + 0.1 * this.isUpgraded)) b.laser(this.vertices[0], this.lockedOn.position, b.dmgScale * (0.06 + 0.1 * this.isUpgraded))
// //make sure you can still see vertex
// const DIST = Vector.magnitude(Vector.sub(this.vertices[0], this.lockedOn.position));
// if (DIST - this.lockedOn.radius < this.range + 150 &&
// Matter.Query.ray(map, this.vertices[0], this.lockedOn.position).length === 0 &&
// Matter.Query.ray(body, this.vertices[0], this.lockedOn.position).length === 0) {
// //move towards the target
// this.force = Vector.add(this.force, Vector.mult(Vector.normalise(Vector.sub(this.lockedOn.position, this.position)), 0.0013))
// //find the closest vertex
// let bestVertexDistance = Infinity
// let bestVertex = null
// for (let i = 0; i < this.lockedOn.vertices.length; i++) {
// const dist = Vector.magnitude(Vector.sub(this.vertices[0], this.lockedOn.vertices[i]));
// if (dist < bestVertexDistance) {
// bestVertex = i
// bestVertexDistance = dist
// }
// }
// const dmg = b.dmgScale * (0.06 + 0.08 * this.isUpgraded);
// this.lockedOn.damage(dmg);
// this.lockedOn.locatePlayer();
// ctx.beginPath(); //draw laser
// ctx.moveTo(this.vertices[0].x, this.vertices[0].y);
// ctx.lineTo(this.lockedOn.vertices[bestVertex].x, this.lockedOn.vertices[bestVertex].y);
// ctx.strokeStyle = "#f00";
// ctx.lineWidth = "2"
// ctx.lineDashOffset = 300 * Math.random()
// ctx.setLineDash([50 + 100 * Math.random(), 100 * Math.random()]);
// ctx.stroke();
// ctx.setLineDash([0, 0]);
// ctx.beginPath();
// ctx.arc(this.lockedOn.vertices[bestVertex].x, this.lockedOn.vertices[bestVertex].y, Math.sqrt(dmg) * 100, 0, 2 * Math.PI);
// ctx.fillStyle = "#f00";
// ctx.fill();
// }
} }
} }
}) })
@@ -1571,6 +1539,7 @@ const b = {
cd: 0, cd: 0,
acceleration: 0.009, acceleration: 0.009,
endCycle: Infinity, endCycle: Infinity,
drainThreshold: mod.isEnergyHealth ? 0.5 : 0.15,
classType: "bullet", classType: "bullet",
collisionFilter: { collisionFilter: {
category: cat.bullet, category: cat.bullet,
@@ -1608,9 +1577,8 @@ const b = {
const sub = Vector.sub(this.lockedOn.position, this.position) const sub = Vector.sub(this.lockedOn.position, this.position)
const DIST = Vector.magnitude(sub); const DIST = Vector.magnitude(sub);
const unit = Vector.normalise(sub) const unit = Vector.normalise(sub)
const DRAIN = 0.0012 if (DIST < mod.isPlasmaRange * 450 && mech.energy > this.drainThreshold) {
if (DIST < mod.isPlasmaRange * 450 && mech.energy > DRAIN) { mech.energy -= 0.0012;
mech.energy -= DRAIN;
if (mech.energy < 0) { if (mech.energy < 0) {
mech.fieldCDcycle = mech.cycle + 120; mech.fieldCDcycle = mech.cycle + 120;
mech.energy = 0; mech.energy = 0;
@@ -1904,27 +1872,58 @@ const b = {
mech.fireCDcycle = mech.cycle + Math.floor(CD * b.fireCD); // cool down mech.fireCDcycle = mech.cycle + Math.floor(CD * b.fireCD); // cool down
const speed = 30 + 6 * Math.random() + 9 * mod.nailInstantFireRate const speed = 30 + 6 * Math.random() + 9 * mod.nailInstantFireRate
const angle = mech.angle + (Math.random() - 0.5) * (Math.random() - 0.5) * (mech.crouch ? 1.35 : 3.2) / CD const angle = mech.angle + (Math.random() - 0.5) * (Math.random() - 0.5) * (mech.crouch ? 1.35 : 3.2) / CD
const dmg = 0.9 if (mod.isIncendiary) {
b.nail({ const me = bullet.length;
x: mech.pos.x + 30 * Math.cos(mech.angle), bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), 25, 2, {
y: mech.pos.y + 30 * Math.sin(mech.angle) density: 0.0001, //frictionAir: 0.01, //restitution: 0,
}, { angle: angle,
x: mech.Vx / 2 + speed * Math.cos(angle), friction: 0.5,
y: mech.Vy / 2 + speed * Math.sin(angle) frictionAir: 0,
}, dmg) //position, velocity, damage dmg: 0, //damage done in addition to the damage from momentum
if (mod.isIceCrystals) { endCycle: Math.floor(mech.crouch ? 28 : 13) + Math.random() * 5 + game.cycle,
bullet[bullet.length - 1].beforeDmg = function(who) { classType: "bullet",
mobs.statusSlow(who, 30) collisionFilter: {
if (mod.isNailPoison) mobs.statusDoT(who, dmg * 0.22, 120) // one tick every 30 cycles category: cat.bullet,
if (mod.isNailCrit && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.99) this.dmg *= 5 //crit if hit near center mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield
}; },
minDmgSpeed: 10,
beforeDmg() {
this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion
},
onEnd() {
b.explosion(this.position, 72 + (Math.random() - 0.5) * 30); //makes bullet do explosive damage at end
},
do() {}
});
Matter.Body.setVelocity(bullet[me], {
x: speed * Math.cos(angle),
y: speed * Math.sin(angle)
});
World.add(engine.world, bullet[me]); //add bullet to world
} else {
const dmg = 0.9
b.nail({
x: mech.pos.x + 30 * Math.cos(mech.angle),
y: mech.pos.y + 30 * Math.sin(mech.angle)
}, {
x: mech.Vx / 2 + speed * Math.cos(angle),
y: mech.Vy / 2 + speed * Math.sin(angle)
}, dmg) //position, velocity, damage
if (mod.isIceCrystals) {
bullet[bullet.length - 1].beforeDmg = function(who) {
mobs.statusSlow(who, 30)
if (mod.isNailPoison) mobs.statusDoT(who, dmg * 0.22, 120) // one tick every 30 cycles
if (mod.isNailCrit && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.99) this.dmg *= 5 //crit if hit near center
};
if (mech.energy < 0.01) { if (mech.energy < 0.01) {
mech.fireCDcycle = mech.cycle + 60; // cool down mech.fireCDcycle = mech.cycle + 60; // cool down
} else { } else {
mech.energy -= mech.fieldRegen + 0.009 mech.energy -= mech.fieldRegen + 0.009
}
} }
} }
} }
}, },
{ {
@@ -1958,7 +1957,57 @@ const b = {
} }
b.muzzleFlash(35); b.muzzleFlash(35);
if (mod.isNailShot) { if (mod.isIncendiary) {
const SPEED = mech.crouch ? 35 : 25
const END = Math.floor(mech.crouch ? 9 : 6);
const totalBullets = 8
const angleStep = (mech.crouch ? 0.1 : 0.33) / totalBullets
let dir = mech.angle - angleStep * totalBullets / 2;
for (let i = 0; i < totalBullets; i++) { //5 -> 7
dir += angleStep
const me = bullet.length;
bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), 17, 4, b.fireAttributes(dir));
const end = END + Math.random() * 3
bullet[me].endCycle = 2 * end + game.cycle
const speed = SPEED * end / END
const dirOff = dir + 0.15 * (Math.random() - 0.5)
Matter.Body.setVelocity(bullet[me], {
x: speed * Math.cos(dirOff),
y: speed * Math.sin(dirOff)
});
bullet[me].onEnd = function() {
b.explosion(this.position, 60 + (Math.random() - 0.5) * 40); //makes bullet do explosive damage at end
}
bullet[me].beforeDmg = function() {
this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion
};
bullet[me].do = function() {}
World.add(engine.world, bullet[me]); //add bullet to world
}
// for (let i = 0; i < totalBullets; i++) { //5 -> 7
// dir += angleStep
// const me = bullet.length;
// bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), 17, 4, b.fireAttributes(dir));
// World.add(engine.world, bullet[me]); //add bullet to world
// Matter.Body.setVelocity(bullet[me], {
// x: (SPEED + 15 * Math.random() - 2 * i) * Math.cos(dir),
// y: (SPEED + 15 * Math.random() - 2 * i) * Math.sin(dir)
// });
// bullet[me].endCycle = 2 * i + END
// bullet[me].restitution = 0;
// bullet[me].friction = 1;
// bullet[me].onEnd = function() {
// b.explosion(this.position, (mech.crouch ? 95 : 75) + (Math.random() - 0.5) * 50); //makes bullet do explosive damage at end
// }
// bullet[me].beforeDmg = function() {
// this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion
// };
// bullet[me].do = function() {
// // this.force.y += this.mass * 0.0004;
// }
// }
} else if (mod.isNailShot) {
for (let i = 0; i < 14; i++) { for (let i = 0; i < 14; i++) {
const dir = mech.angle + (Math.random() - 0.5) * spread * 0.2 const dir = mech.angle + (Math.random() - 0.5) * spread * 0.2
const pos = { const pos = {
@@ -2349,48 +2398,48 @@ const b = {
} }
} }
}, },
{ // {
name: "flak", // name: "flak",
description: "fire a <strong>cluster</strong> of short range <strong>projectiles</strong><br><strong class='color-e'>explodes</strong> on <strong>contact</strong> or after half a second", // description: "fire a <strong>cluster</strong> of short range <strong>projectiles</strong><br><strong class='color-e'>explodes</strong> on <strong>contact</strong> or after half a second",
ammo: 0, // ammo: 0,
ammoPack: 4, // ammoPack: 4,
defaultAmmoPack: 4, //use to revert ammoPack after mod changes drop rate // defaultAmmoPack: 4, //use to revert ammoPack after mod changes drop rate
have: false, // have: false,
fire() { // fire() {
mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 10) * b.fireCD); // cool down // mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 10) * b.fireCD); // cool down
b.muzzleFlash(30); // b.muzzleFlash(30);
const SPEED = mech.crouch ? 29 : 25 // const SPEED = mech.crouch ? 29 : 25
const END = Math.floor(mech.crouch ? 30 : 18); // const END = Math.floor(mech.crouch ? 30 : 18);
const side1 = 17 // const side1 = 17
const side2 = 4 // const side2 = 4
const totalBullets = 6 // const totalBullets = 6
const angleStep = (mech.crouch ? 0.06 : 0.25) / totalBullets // const angleStep = (mech.crouch ? 0.06 : 0.25) / totalBullets
let dir = mech.angle - angleStep * totalBullets / 2; // let dir = mech.angle - angleStep * totalBullets / 2;
for (let i = 0; i < totalBullets; i++) { //5 -> 7 // for (let i = 0; i < totalBullets; i++) { //5 -> 7
dir += angleStep // dir += angleStep
const me = bullet.length; // const me = bullet.length;
bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), side1, side2, b.fireAttributes(dir)); // bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), side1, side2, b.fireAttributes(dir));
World.add(engine.world, bullet[me]); //add bullet to world // World.add(engine.world, bullet[me]); //add bullet to world
Matter.Body.setVelocity(bullet[me], { // Matter.Body.setVelocity(bullet[me], {
x: (SPEED + 15 * Math.random() - 2 * i) * Math.cos(dir), // x: (SPEED + 15 * Math.random() - 2 * i) * Math.cos(dir),
y: (SPEED + 15 * Math.random() - 2 * i) * Math.sin(dir) // y: (SPEED + 15 * Math.random() - 2 * i) * Math.sin(dir)
}); // });
bullet[me].endCycle = 2 * i + game.cycle + END // bullet[me].endCycle = 2 * i + game.cycle + END
bullet[me].restitution = 0; // bullet[me].restitution = 0;
bullet[me].friction = 1; // bullet[me].friction = 1;
bullet[me].explodeRad = (mech.crouch ? 95 : 75) + (Math.random() - 0.5) * 50; // bullet[me].explodeRad = (mech.crouch ? 95 : 75) + (Math.random() - 0.5) * 50;
bullet[me].onEnd = function() { // bullet[me].onEnd = function() {
b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end // b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end
} // }
bullet[me].beforeDmg = function() { // bullet[me].beforeDmg = function() {
this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion // this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion
}; // };
bullet[me].do = function() { // bullet[me].do = function() {
// this.force.y += this.mass * 0.0004; // // this.force.y += this.mass * 0.0004;
} // }
} // }
} // }
}, // },
{ {
name: "grenades", name: "grenades",
description: "lob a single <strong>bouncy</strong> projectile<br><strong class='color-e'>explodes</strong> on <strong>contact</strong> or after one second", description: "lob a single <strong>bouncy</strong> projectile<br><strong class='color-e'>explodes</strong> on <strong>contact</strong> or after one second",

View File

@@ -24,8 +24,29 @@ function playerOnGroundCheck(event) {
//runs on collisions events //runs on collisions events
function enter() { function enter() {
mech.numTouching++; mech.numTouching++;
if (!mech.onGround) mech.enterLand(); if (!mech.onGround) {
mech.onGround = true;
if (mech.crouch) {
if (mech.checkHeadClear()) {
mech.undoCrouch();
} else {
mech.yOffGoal = mech.yOffWhen.crouch;
}
} else {
//sets a hard land where player stays in a crouch for a bit and can't jump
//crouch is forced in groundControl below
const momentum = player.velocity.y * player.mass //player mass is 5 so this triggers at 26 down velocity, unless the player is holding something
if (momentum > 130) {
mech.doCrouch();
mech.yOff = mech.yOffWhen.jump;
mech.hardLandCD = mech.cycle + Math.min(momentum / 6.5 - 6, 40)
} else {
mech.yOffGoal = mech.yOffWhen.stand;
}
}
}
} }
const pairs = event.pairs; const pairs = event.pairs;
for (let i = 0, j = pairs.length; i != j; ++i) { for (let i = 0, j = pairs.length; i != j; ++i) {
let pair = pairs[i]; let pair = pairs[i];
@@ -42,76 +63,26 @@ function playerOnGroundCheck(event) {
function playerOffGroundCheck(event) { function playerOffGroundCheck(event) {
//runs on collisions events //runs on collisions events
function enter() {
if (mech.onGround && mech.numTouching === 0) mech.enterAir();
}
const pairs = event.pairs; const pairs = event.pairs;
for (let i = 0, j = pairs.length; i != j; ++i) { for (let i = 0, j = pairs.length; i != j; ++i) {
if (pairs[i].bodyA === jumpSensor) { if (pairs[i].bodyA === jumpSensor || pairs[i].bodyB === jumpSensor) {
enter(); if (mech.onGround && mech.numTouching === 0) {
} else if (pairs[i].bodyB === jumpSensor) { mech.onGround = false;
enter(); mech.hardLandCD = 0 // disable hard landing
if (mech.checkHeadClear()) {
if (mech.crouch) {
mech.undoCrouch();
}
mech.yOffGoal = mech.yOffWhen.jump;
}
}
} }
} }
} }
// function playerHeadCheck(event) {
// //runs on collisions events
// if (mech.crouch) {
// mech.isHeadClear = true;
// const pairs = event.pairs;
// for (let i = 0, j = pairs.length; i != j; ++i) {
// if (pairs[i].bodyA === headSensor) {
// mech.isHeadClear = false;
// } else if (pairs[i].bodyB === headSensor) {
// mech.isHeadClear = false;
// }
// }
// }
// }
function collisionChecks(event) { function collisionChecks(event) {
const pairs = event.pairs; const pairs = event.pairs;
for (let i = 0, j = pairs.length; i != j; i++) { for (let i = 0, j = pairs.length; i != j; i++) {
// //map + bullet collisions
// if (pairs[i].bodyA.collisionFilter.category === cat.map && pairs[i].bodyB.collisionFilter.category === cat.bullet) {
// collideBulletStatic(pairs[i].bodyB)
// } else if (pairs[i].bodyB.collisionFilter.category === cat.map && pairs[i].bodyA.collisionFilter.category === cat.bullet) {
// collideBulletStatic(pairs[i].bodyA)
// }
// //triggers when the bullets hits something static
// function collideBulletStatic(obj, speedThreshold = 12, massThreshold = 2) {
// if (obj.onWallHit) obj.onWallHit();
// }
// function collidePlayer(obj) {
// //player dmg from hitting a body
// if (obj.classType === "body" && obj.speed > 10 && mech.immuneCycle < mech.cycle) {
// const velocityThreshold = 30 //keep this lines up with player.enterLand numbers (130/5 = 26)
// if (player.position.y > obj.position.y) { //block is above the player look at total momentum difference
// const velocityDiffMag = Vector.magnitude(Vector.sub(player.velocity, obj.velocity))
// if (velocityDiffMag > velocityThreshold) hit(velocityDiffMag - velocityThreshold)
// } else { //block is below player only look at horizontal momentum difference
// const velocityDiffMagX = Math.abs(obj.velocity.x - player.velocity.x)
// if (velocityDiffMagX > velocityThreshold) hit(velocityDiffMagX - velocityThreshold)
// }
// function hit(dmg) {
// mech.immuneCycle = mech.cycle + mod.collisionImmuneCycles; //player is immune to collision damage for 30 cycles
// dmg = Math.min(Math.max(Math.sqrt(dmg) * obj.mass * 0.01, 0.02), 0.15);
// mech.damage(dmg);
// game.drawList.push({ //add dmg to draw queue
// x: pairs[i].activeContacts[0].vertex.x,
// y: pairs[i].activeContacts[0].vertex.y,
// radius: dmg * 500,
// color: game.mobDmgColor,
// time: game.drawTime
// });
// }
// }
// }
//mob + (player,bullet,body) collisions //mob + (player,bullet,body) collisions
for (let k = 0; k < mob.length; k++) { for (let k = 0; k < mob.length; k++) {
if (mob[k].alive && mech.alive) { if (mob[k].alive && mech.alive) {

View File

@@ -987,8 +987,8 @@ document.getElementById("updates").addEventListener("toggle", function() {
loadJSON('https://api.github.com/repos/landgreen/n-gon/commits', loadJSON('https://api.github.com/repos/landgreen/n-gon/commits',
function(data) { function(data) {
// console.log(data) // console.log(data)
for (let i = 0, len = 4; i < len; i++) { for (let i = 0, len = 3; i < len; i++) {
text += data[i].commit.author.date.substr(0, 10) + ": "; //+ "<br>" text += "<strong>" + data[i].commit.author.date.substr(0, 10) + "</strong> - "; //+ "<br>"
text += data[i].commit.message text += data[i].commit.message
if (i < len - 1) text += "<hr>" if (i < len - 1) text += "<hr>"
} }

View File

@@ -12,20 +12,19 @@ const level = {
levels: [], levels: [],
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
// level.difficultyIncrease(8) level.difficultyIncrease(8)
// game.enableConstructMode() //used to build maps in testing mode // game.enableConstructMode() //used to build maps in testing mode
// game.zoomScale = 1000; // game.zoomScale = 1000;
// game.setZoom(); // game.setZoom();
// mech.setField("wormhole") // mech.setField("wormhole")
// b.giveGuns("mine") // b.giveGuns("drones")
// mod.giveMod("sentry") // mod.giveMod("incendiary ammunition")
level.intro(); //starting level // level.intro(); //starting level
level.testing(); //not in rotation
// level.finalBoss() //final boss level // level.finalBoss() //final boss level
// level.gauntlet(); //before final boss level // level.gauntlet(); //before final boss level
// level.testing(); //not in rotation
// level.template() //not in rotation
// level.testChamber() //less mobs, more puzzle // level.testChamber() //less mobs, more puzzle
// level.sewers(); // level.sewers();
// level.satellite(); // level.satellite();
@@ -141,16 +140,16 @@ const level = {
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
// spawn.boost(1500, 0, 900); // spawn.boost(1500, 0, 900);
// spawn.starter(1600, -500) spawn.starter(1600, -500, 200)
// spawn.bomberBoss(2900, -500) // spawn.bomberBoss(2900, -500)
// spawn.launcherBoss(1200, -500) // spawn.launcherBoss(1200, -500)
// spawn.laserTargetingBoss(1600, -400) // spawn.laserTargetingBoss(1600, -400)
// spawn.spawner(1600, -500) // spawn.spawner(1600, -500)
// spawn.sniper(1700, -120, 50) // spawn.sniper(1700, -120, 50)
// spawn.bomberBoss(1400, -500) // spawn.bomberBoss(1400, -500)
spawn.sucker(1800, -120) // spawn.sucker(1800, -120)
// spawn.cellBossCulture(1600, -500) // spawn.cellBossCulture(1600, -500)
// spawn.powerUpBoss(1600, -500) // spawn.spiderBoss(1600, -500)
// spawn.sniper(1200, -500) // spawn.sniper(1200, -500)
// spawn.shield(mob[mob.length - 1], 1800, -120, 1); // spawn.shield(mob[mob.length - 1], 1800, -120, 1);

View File

@@ -197,7 +197,7 @@ const mod = {
}, },
{ {
name: "fluoroantimonic acid", name: "fluoroantimonic acid",
description: "increase <strong class='color-d'>damage</strong> by <strong>40%</strong><br>when your base <strong>health</strong> is above <strong>100%</strong>", description: "increase <strong class='color-d'>damage</strong> by <strong>40%</strong><br>when your <strong>health</strong> is above <strong>100%</strong>",
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
@@ -408,7 +408,7 @@ const mod = {
maxCount: 9, maxCount: 9,
count: 0, count: 0,
allowed() { allowed() {
return mod.haveGunCheck("missiles") || mod.haveGunCheck("flak") || mod.haveGunCheck("grenades") || mod.haveGunCheck("vacuum bomb") || mod.isPulseLaser || mod.isMissileField || mod.boomBotCount > 1 || mod.isFlechetteExplode return mod.haveGunCheck("missiles") || mod.isIncendiary || mod.haveGunCheck("grenades") || mod.haveGunCheck("vacuum bomb") || mod.isPulseLaser || mod.isMissileField || mod.boomBotCount > 1 || mod.isFlechetteExplode
}, },
requires: "an explosive damage source", requires: "an explosive damage source",
effect: () => { effect: () => {
@@ -424,7 +424,7 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.haveGunCheck("missiles") || mod.haveGunCheck("flak") || mod.haveGunCheck("grenades") || mod.haveGunCheck("vacuum bomb") || mod.isPulseLaser || mod.isMissileField || mod.boomBotCount > 1 || mod.isFlechetteExplode return mod.haveGunCheck("missiles") || mod.isIncendiary || mod.haveGunCheck("grenades") || mod.haveGunCheck("vacuum bomb") || mod.isPulseLaser || mod.isMissileField || mod.boomBotCount > 1 || mod.isFlechetteExplode
}, },
requires: "an explosive damage source", requires: "an explosive damage source",
effect: () => { effect: () => {
@@ -440,7 +440,7 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.haveGunCheck("missiles") || mod.haveGunCheck("flak") || mod.haveGunCheck("grenades") || mod.haveGunCheck("vacuum bomb") || mod.isPulseLaser || mod.isMissileField || mod.isFlechetteExplode return mod.haveGunCheck("missiles") || mod.isIncendiary || mod.haveGunCheck("grenades") || mod.haveGunCheck("vacuum bomb") || mod.isPulseLaser || mod.isMissileField || mod.isFlechetteExplode
}, },
requires: "an explosive damage source", requires: "an explosive damage source",
effect: () => { effect: () => {
@@ -457,7 +457,7 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.haveGunCheck("missiles") || mod.haveGunCheck("flak") || mod.haveGunCheck("grenades") || mod.haveGunCheck("vacuum bomb") || mod.isMissileField || mod.isExplodeMob || mod.isFlechetteExplode || mod.isPulseLaser return mod.haveGunCheck("missiles") || mod.isIncendiary || mod.haveGunCheck("grenades") || mod.haveGunCheck("vacuum bomb") || mod.isMissileField || mod.isExplodeMob || mod.isFlechetteExplode || mod.isPulseLaser
}, },
requires: "an explosive damage source", requires: "an explosive damage source",
effect: () => { effect: () => {
@@ -469,7 +469,7 @@ const mod = {
}, },
{ {
name: "scrap bots", name: "scrap bots",
description: "<strong>20%</strong> chance to build a <strong>bot</strong> after killing a mob<br>the bot last for about <strong>20</strong> seconds", description: "<strong>20%</strong> chance to build a <strong>bot</strong> after killing a mob<br>the bot lasts for about <strong>20</strong> seconds",
maxCount: 3, maxCount: 3,
count: 0, count: 0,
allowed() { allowed() {
@@ -1574,9 +1574,25 @@ const mod = {
//************************************************** gun //************************************************** gun
//************************************************** mods //************************************************** mods
//************************************************** //**************************************************
{
name: "incendiary ammunition",
description: "your <strong>bullets</strong> <strong class='color-e'>explode</strong> after a short time<br>(nail gun, shotgun, super balls, drones)",
maxCount: 1,
count: 0,
allowed() {
return mod.haveGunCheck("drones") || mod.haveGunCheck("super balls") || (mod.haveGunCheck("nail gun") && !mod.isIceCrystals && !mod.isNailCrit) || (mod.haveGunCheck("shotgun") && !mod.isNailShot)
},
requires: "drones, super balls, nail gun, shotgun",
effect() {
mod.isIncendiary = true
},
remove() {
mod.isIncendiary = false;
}
},
{ {
name: "Lorentzian topology", name: "Lorentzian topology",
description: "your <strong>bullets</strong> last <strong>33% longer</strong>", description: "your <strong>bullets</strong> last <strong>33% longer</strong><br><span style = 'font-size: 85%'>drones, spores, super balls, foam, wave, ice IX, neutron</span>",
maxCount: 3, maxCount: 3,
count: 0, count: 0,
allowed() { allowed() {
@@ -1612,9 +1628,9 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.haveGunCheck("nail gun") && !mod.nailInstantFireRate return mod.haveGunCheck("nail gun") && !mod.nailInstantFireRate && !mod.isIncendiary
}, },
requires: "nail gun", requires: "nail gun, not incendiary, not powder-actuated",
effect() { effect() {
mod.isIceCrystals = true; mod.isIceCrystals = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
@@ -1645,9 +1661,9 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.haveGunCheck("nail gun") return mod.haveGunCheck("nail gun") && !mod.isIncendiary
}, },
requires: "nail gun", requires: "nail gun, not incendiary",
effect() { effect() {
mod.isNailCrit = true mod.isNailCrit = true
}, },
@@ -1731,7 +1747,7 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.haveGunCheck("shotgun") return mod.haveGunCheck("shotgun") && !mod.isIncendiary
}, },
requires: "shotgun", requires: "shotgun",
effect() { effect() {
@@ -1765,7 +1781,7 @@ const mod = {
allowed() { allowed() {
return mod.haveGunCheck("super balls") && !mod.oneSuperBall return mod.haveGunCheck("super balls") && !mod.oneSuperBall
}, },
requires: "super balls", requires: "super balls, but not the mod super ball",
effect() { effect() {
mod.superBallNumber += 2 mod.superBallNumber += 2
}, },
@@ -1781,7 +1797,7 @@ const mod = {
allowed() { allowed() {
return mod.haveGunCheck("super balls") && mod.superBallNumber === 4 return mod.haveGunCheck("super balls") && mod.superBallNumber === 4
}, },
requires: "super balls", requires: "super balls, but not super duper",
effect() { effect() {
mod.oneSuperBall = true; mod.oneSuperBall = true;
}, },
@@ -1999,26 +2015,6 @@ const mod = {
mod.is3Missiles = false; mod.is3Missiles = false;
} }
}, },
{
name: "optimized shell packing",
description: "<strong>flak</strong> <strong class='color-g'>ammo</strong> drops contain <strong>2x</strong> more shells",
maxCount: 3,
count: 0,
allowed() {
return mod.haveGunCheck("flak")
},
requires: "flak",
effect() {
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "flak") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * (2 * (1 + this.count));
}
},
remove() {
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "flak") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack;
}
}
},
{ {
name: "fragmentation grenade", name: "fragmentation grenade",
description: "<strong>grenades</strong> are loaded with <strong>5</strong> nails<br>on detonation <strong>nails</strong> are ejected towards mobs", description: "<strong>grenades</strong> are loaded with <strong>5</strong> nails<br>on detonation <strong>nails</strong> are ejected towards mobs",
@@ -2143,7 +2139,7 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.nailBotCount + mod.grenadeFragments + mod.nailsDeathMob / 2 + (mod.haveGunCheck("mine") + mod.isRailNails + mod.isNailShot + mod.haveGunCheck("nail gun")) * 2 > 1 return mod.nailBotCount + mod.grenadeFragments + mod.nailsDeathMob / 2 + (mod.haveGunCheck("mine") + mod.isRailNails + mod.isNailShot + (mod.haveGunCheck("nail gun") && !mod.isIncendiary)) * 2 > 1
}, },
requires: "nails", requires: "nails",
effect() { effect() {
@@ -2159,7 +2155,7 @@ const mod = {
maxCount: 1, maxCount: 1,
count: 0, count: 0,
allowed() { allowed() {
return mod.nailBotCount + mod.grenadeFragments + mod.nailsDeathMob / 2 + (mod.haveGunCheck("mine") + mod.isRailNails + mod.isNailShot + mod.haveGunCheck("nail gun")) * 2 > 1 return mod.nailBotCount + mod.grenadeFragments + mod.nailsDeathMob / 2 + (mod.haveGunCheck("mine") + mod.isRailNails + mod.isNailShot + (mod.haveGunCheck("nail gun") && !mod.isIncendiary)) * 2 > 1
}, },
requires: "nails", requires: "nails",
effect() { effect() {
@@ -3259,5 +3255,6 @@ const mod = {
timeEnergyRegen: null, timeEnergyRegen: null,
isRadioactive: null, isRadioactive: null,
isRailEnergyGain: null, isRailEnergyGain: null,
isMineSentry: null isMineSentry: null,
isIncendiary: null
} }

View File

@@ -186,39 +186,6 @@ const mech = {
return true return true
} }
}, },
enterAir() {
//triggered in engine.js on collision
mech.onGround = false;
mech.hardLandCD = 0 // disable hard landing
if (mech.checkHeadClear()) {
if (mech.crouch) {
mech.undoCrouch();
}
mech.yOffGoal = mech.yOffWhen.jump;
}
},
//triggered in engine.js on collision
enterLand() {
mech.onGround = true;
if (mech.crouch) {
if (mech.checkHeadClear()) {
mech.undoCrouch();
} else {
mech.yOffGoal = mech.yOffWhen.crouch;
}
} else {
//sets a hard land where player stays in a crouch for a bit and can't jump
//crouch is forced in groundControl below
const momentum = player.velocity.y * player.mass //player mass is 5 so this triggers at 26 down velocity, unless the player is holding something
if (momentum > 130) {
mech.doCrouch();
mech.yOff = mech.yOffWhen.jump;
mech.hardLandCD = mech.cycle + Math.min(momentum / 6.5 - 6, 40)
} else {
mech.yOffGoal = mech.yOffWhen.stand;
}
}
},
buttonCD_jump: 0, //cool down for player buttons buttonCD_jump: 0, //cool down for player buttons
groundControl() { groundControl() {
//check for crouch or jump //check for crouch or jump
@@ -516,7 +483,6 @@ const mech = {
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
} }
}, 3000); }, 3000);
return; return;
} else { //death } else { //death
mech.health = 0; mech.health = 0;
@@ -2410,6 +2376,7 @@ const mech = {
mech.hole.isReady = false; mech.hole.isReady = false;
mech.fieldRange = 0 mech.fieldRange = 0
Matter.Body.setPosition(player, game.mouseInGame); Matter.Body.setPosition(player, game.mouseInGame);
mech.buttonCD_jump = 0 //this might fix a bug with jumping
const velocity = Vector.mult(Vector.normalise(sub), 18) const velocity = Vector.mult(Vector.normalise(sub), 18)
Matter.Body.setVelocity(player, { Matter.Body.setVelocity(player, {
x: velocity.x, x: velocity.x,

File diff suppressed because it is too large Load Diff

View File

@@ -159,7 +159,7 @@ const spawn = {
} }
this.mode = 3 this.mode = 3
this.fill = "#000"; this.fill = "#000";
this.eventHorizon = 700 this.eventHorizon = 750
this.spawnInterval = 600 this.spawnInterval = 600
this.rotateVelocity = 0.001 * (player.position.x > this.position.x ? 1 : -1) //rotate so that the player can get away this.rotateVelocity = 0.001 * (player.position.x > this.position.x ? 1 : -1) //rotate so that the player can get away
if (!this.isShielded) spawn.shield(this, x, y, 1); //regen shield here ? if (!this.isShielded) spawn.shield(this, x, y, 1); //regen shield here ?
@@ -200,7 +200,7 @@ const spawn = {
me.eventHorizonCycleRate = 4 * Math.PI / me.endCycle me.eventHorizonCycleRate = 4 * Math.PI / me.endCycle
me.modeSuck = function() { me.modeSuck = function() {
//eventHorizon waves in and out //eventHorizon waves in and out
if (!mech.isBodiesAsleep) eventHorizon = this.eventHorizon * (1 - 0.25 * Math.cos(this.cycle * this.eventHorizonCycleRate)) //0.014 const eventHorizon = this.eventHorizon * (1 - 0.25 * Math.cos(game.cycle * this.eventHorizonCycleRate)) //0.014
//draw darkness //draw darkness
ctx.beginPath(); ctx.beginPath();
ctx.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI); ctx.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI);
@@ -763,7 +763,7 @@ const spawn = {
this.checkStatus(); this.checkStatus();
if (this.seePlayer.recall) { if (this.seePlayer.recall) {
//eventHorizon waves in and out //eventHorizon waves in and out
eventHorizon = this.eventHorizon * (0.93 + 0.17 * Math.sin(game.cycle * 0.011)) const eventHorizon = this.eventHorizon * (0.93 + 0.17 * Math.sin(game.cycle * 0.011))
//accelerate towards the player //accelerate towards the player
const forceMag = this.accelMag * this.mass; const forceMag = this.accelMag * this.mass;
@@ -860,7 +860,7 @@ const spawn = {
this.force.y += forceMag * dy / mag; this.force.y += forceMag * dy / mag;
//eventHorizon waves in and out //eventHorizon waves in and out
eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(game.cycle * 0.008)) const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(game.cycle * 0.008))
// zoom camera in and out with the event horizon // zoom camera in and out with the event horizon
//draw darkness //draw darkness
@@ -910,7 +910,6 @@ const spawn = {
} }
}, },
spiderBoss(x, y, radius = 60 + Math.ceil(Math.random() * 10)) { spiderBoss(x, y, radius = 60 + Math.ceil(Math.random() * 10)) {
const isDaddyLongLegs = Math.random() < 0.25
let targets = [] //track who is in the node boss, for shields let targets = [] //track who is in the node boss, for shields
mobs.spawn(x, y, 6, radius, "#b386e8"); mobs.spawn(x, y, 6, radius, "#b386e8");
let me = mob[mob.length - 1]; let me = mob[mob.length - 1];
@@ -919,10 +918,10 @@ const spawn = {
me.friction = 0; me.friction = 0;
me.frictionAir = 0.0065; me.frictionAir = 0.0065;
me.lookTorque = 0.0000008; //controls spin while looking for player me.lookTorque = 0.0000008; //controls spin while looking for player
me.g = 0.00025; //required if using 'gravity' me.g = 0.0002; //required if using 'gravity'
me.seePlayerFreq = Math.round((30 + 20 * Math.random()) * game.lookFreqScale); me.seePlayerFreq = Math.round((30 + 20 * Math.random()) * game.lookFreqScale);
const springStiffness = isDaddyLongLegs ? 0.0001 : 0.000065; const springStiffness = 0.00014;
const springDampening = isDaddyLongLegs ? 0 : 0.0006; const springDampening = 0.0005;
me.springTarget = { me.springTarget = {
x: me.position.x, x: me.position.x,
@@ -935,8 +934,8 @@ const spawn = {
stiffness: springStiffness, stiffness: springStiffness,
damping: springDampening damping: springDampening
}); });
World.add(engine.world, cons[cons.length - 1]);
cons[len].length = 100 + 1.5 * radius; cons[len].length = 100 + 1.5 * radius;
me.cons = cons[len]; me.cons = cons[len];
me.springTarget2 = { me.springTarget2 = {
@@ -948,11 +947,12 @@ const spawn = {
pointA: me.springTarget2, pointA: me.springTarget2,
bodyB: me, bodyB: me,
stiffness: springStiffness, stiffness: springStiffness,
damping: springDampening damping: springDampening,
length: 0
}); });
World.add(engine.world, cons[cons.length - 1]);
cons[len2].length = 100 + 1.5 * radius; cons[len2].length = 100 + 1.5 * radius;
me.cons2 = cons[len2]; me.cons2 = cons[len2];
if (isDaddyLongLegs) Matter.Body.setDensity(me, 0.017); //extra dense //normal is 0.001 //makes effective life much larger
me.onDeath = function() { me.onDeath = function() {
this.removeCons(); this.removeCons();
@@ -974,15 +974,12 @@ const spawn = {
for (let i = 0; i < nodes; ++i) { for (let i = 0; i < nodes; ++i) {
spawn.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12); spawn.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12);
if (isDaddyLongLegs) Matter.Body.setDensity(mob[mob.length - 1], 0.01); //extra dense //normal is 0.001 //makes effective life much larger
targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields
} }
//spawn shield around all nodes
if (!isDaddyLongLegs) spawn.bossShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25);
spawn.allowShields = true;
const attachmentStiffness = isDaddyLongLegs ? 0.0003 : 0.05 const attachmentStiffness = 0.05
if (!isDaddyLongLegs) spawn.constrain2AdjacentMobs(nodes + 2, attachmentStiffness, true); //loop mobs together spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together
for (let i = 0; i < nodes; ++i) { //attach to center mob for (let i = 0; i < nodes; ++i) { //attach to center mob
consBB[consBB.length] = Constraint.create({ consBB[consBB.length] = Constraint.create({
bodyA: me, bodyA: me,
@@ -992,6 +989,9 @@ const spawn = {
}); });
World.add(engine.world, consBB[consBB.length - 1]); World.add(engine.world, consBB[consBB.length - 1]);
} }
//spawn shield around all nodes
spawn.bossShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25);
spawn.allowShields = true;
}, },
timeSkipBoss(x, y, radius = 55) { timeSkipBoss(x, y, radius = 55) {
mobs.spawn(x, y, 6, radius, '#000'); mobs.spawn(x, y, 6, radius, '#000');

View File

@@ -1,32 +1,56 @@
*********** NEXT PATCH *********** *********** NEXT PATCH ***********
you can view the patch notes for the last few commits from the game menu
this uses the github API, so it might have some delay
overfill mods add energy instead of setting energy to a value bug - spider boss move at full speed again (watch out)
bots that uses energy stop when you hit 50% energy if you have mass-energy mod
bug - blocks in vertical portals can get stuck inside the portals you get a warning before you overwrite your current gun due to integrated armament
fixed, but only for one portal... oh well
mobs that make a scrap bot, don't leave a block body anymore gun - flak is removed
mod: High-explosive incendiary, or incendiary rounds
shotgun: several large explosions
nail gun: rapid fire explosions
drones: drones explode on impact
super balls: explode on contact
************** BUGS ************** ************** BUGS **************
bug - crouch and worm hole? -> crouch locked in (4+ reports before potential fix) bug - crouch and worm hole? -> crouch locked in
bug - getting locked into crouch on community levels, but showing the player as still standing players have extra gravity
might be from the short jump code
added a line to wormhole to reset possible short jump
bug - capping the fps causes random slow downs, that can be fixed with pause (3 reports) bug - getting locked into crouch on community levels, but showing the player as still standing
maybe improper vertices for map bodies
bug - mine spawned one new mine every second (intermittent, but almost every time) bug - capping the fps causes random slow downs, that can be fixed with pause
(once) bug - mine spawned one new mine every second
after sticking to the top right corner of a wall after sticking to the top right corner of a wall
notes: had only gun mine, mod mine reclamation, field plasma, notes: had only gun mine, mod mine reclamation, field plasma,
bug - mines spawn extra mines when fired at thin map wall while jumping
************** MODS ************** (repeatable almost everytime) bug - mines spawn extra mines when fired at thin map wall while jumping
************** TODO **************
rework super balls - start with more damage, ramp slower from mods
+1 ball per shot from mods
small scale size increased
or incendiary and no size increase
mod - make neutron bomb a grenade mod
mod - railgun's push effect is increased, and it does some damage to nearby mobs
maybe only triggers at max energy
mod - neutron bomb needs a method to "catch" mobs in it's field mod - neutron bomb needs a method to "catch" mobs in it's field
apply stun status effect apply stun status effect
too similar to the stun effect? too similar to the stun effect?
mod - a 4th mod selection option that is always a gun or field
mod - nano scale field could be a mod mod - nano scale field could be a mod
excess energy is converted to bullets excess energy is converted to bullets
run this code in the energy overfill code check run this code in the energy overfill code check
@@ -35,10 +59,7 @@ mod - nano scale field could be a mod
pair production - nerf and give it to anyone pair production - nerf and give it to anyone
cloaking field - maybe just don't spawn bullets when cloaked cloaking field - maybe just don't spawn bullets when cloaked
mod - sentry fires other bullet types Mod: "Solar Power": Energy regeneration is doubled while standing still
laser or missile
flak could be a mod for shotgun?
mod - self destruct - drones explode when they die mod - self destruct - drones explode when they die
drones lose extra time on collisions, so they often explode after a collision drones lose extra time on collisions, so they often explode after a collision
@@ -85,14 +106,15 @@ mod - foam is attracted to mobs
name - static cling name - static cling
could also do bremsstrahlung radiation like damage on attachment could also do bremsstrahlung radiation like damage on attachment
************** TODO ************** field - one block orbits you, it can protect you a bit and do collision damage
use field to fire and press field again to pull it back
mod - more blocks
mod - attach a permanent neutron bomb to the block
lowers energy regen, but it can damage mobs
wormholes need to give feedback on where a portal can go wormholes need to give feedback on where a portal can go
or automatically put portals in safe places or automatically put portals in safe places
field - You have a permanent neutron bomb oscillating on you. Energy regeneration and heal powerup effectiveness is halved.
neutron field could be a passive outwards push, replacing the defined, oscillating border of SWH with a less powerful, larger border
find a way to automatically show patch information from github on n-gon main page find a way to automatically show patch information from github on n-gon main page
repeat map in vertical and horizontal space repeat map in vertical and horizontal space