diff --git a/js/bullet.js b/js/bullet.js
index 9061de8..d66004e 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -1079,7 +1079,7 @@ const b = {
plasmaBot(position = mech.pos) {
const me = bullet.length;
const dir = mech.angle;
- const RADIUS = 20
+ const RADIUS = 21
bullet[me] = Bodies.polygon(position.x, position.y, 5, RADIUS, {
angle: dir,
friction: 0,
@@ -1115,7 +1115,7 @@ const b = {
let closeDist = mod.isPlasmaRange * 1000;
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].dropPowerUp &&
+ 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;
diff --git a/js/game.js b/js/game.js
index 61ff2f8..c6e513a 100644
--- a/js/game.js
+++ b/js/game.js
@@ -233,7 +233,10 @@ const game = {
if (mod.mods[i].count > 0) {
if (text) text += "
" //add a new line, but not on the first line
text += mod.mods[i].name
- if (mod.mods[i].nameInfo) text += mod.mods[i].nameInfo
+ if (mod.mods[i].nameInfo) {
+ text += mod.mods[i].nameInfo
+ mod.mods[i].addNameInfo();
+ }
if (mod.mods[i].count > 1) text += ` (${mod.mods[i].count}x)`
}
}
@@ -243,7 +246,7 @@ const game = {
replaceTextLog: true,
//
- SVGleftMouse: '',
+ // SVGleftMouse: '',
SVGrightMouse: '',
makeTextLog(text, time = 180) {
if (game.replaceTextLog) {
@@ -400,6 +403,8 @@ const game = {
// game.noCameraScroll()
} else if (keys[85]) { // next level with U
level.nextLevel();
+ } else if (keys[88] && keys[90]) {
+ mech.death();
}
}
},
@@ -775,12 +780,6 @@ const game = {
if (mod.isHealthRecovery) mech.addHealth(0.01)
}
- if (mod.isHealLowHealth) {
- if (mech.health < mech.maxHealth * 0.25) {
- mech.addHealth(0.01 * mech.maxHealth)
- }
- }
-
if (!(game.cycle % 420)) { //once every 7 seconds
fallCheck = function (who, save = false) {
let i = who.length;
@@ -1086,6 +1085,8 @@ const game = {
enableConstructMode() {
game.isConstructionMode = true;
game.isAutoZoom = false;
+ game.zoomScale = 2200;
+ game.setZoom();
document.body.addEventListener("mouseup", (e) => {
if (game.testing && game.constructMouseDownPosition) {
diff --git a/js/index.js b/js/index.js
index b41bc25..e100986 100644
--- a/js/index.js
+++ b/js/index.js
@@ -248,7 +248,6 @@ const build = {
document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-custom").value
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
});
- mod.resetModText();
},
reset() {
build.isCustomSelection = true;
diff --git a/js/level.js b/js/level.js
index 031c00a..d3b88f3 100644
--- a/js/level.js
+++ b/js/level.js
@@ -13,18 +13,16 @@ const level = {
// game.enableConstructMode() //used to build maps in testing mode
// level.difficultyIncrease(9)
// mech.isStealth = true;
- // mod.giveMod("plasma-bot");
- // mod.giveMod("nail-bot");
- // mod.giveMod("laser-bot");
- // mod.giveMod("boom-bot");
// mod.giveMod("supply chain");
- // b.giveGuns("pulse")
+ b.giveGuns("ice IX")
// mech.setField("plasma torch")
level.intro(); //starting level
+ // level.bossRoom1()
// level.testing();
- // level.stronghold()
+ // level.template()
// level.bosses();
+ // level.stronghold()
// level.satellite();
// level.skyscrapers();
// level.aerie();
@@ -32,6 +30,7 @@ const level = {
// level.warehouse();
// level.highrise();
// level.office();
+
} else {
spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
// spawn.pickList = ["focuser", "focuser"]
@@ -61,245 +60,86 @@ const level = {
if (mod.isArmorFromPowerUps) {
// for (let i = 0; i < powerUps.totalPowerUps; i++) {}
- mech.maxHealth += 0.04 * powerUps.totalPowerUps
- if (powerUps.totalPowerUps) game.makeTextLog(" max health increased by " + (0.04 * powerUps.totalPowerUps * 100).toFixed(0) + "%", 300)
+ mech.maxHealth += 0.05 * powerUps.totalPowerUps
+ if (powerUps.totalPowerUps) game.makeTextLog(" max health increased by " + (0.05 * powerUps.totalPowerUps * 100).toFixed(0) + "%", 300)
}
},
- isBuildRun: false,
- difficultyIncrease(num = 1) {
- // if (level.isBuildRun) num++
- for (let i = 0; i < num; i++) {
- game.difficulty++
- game.dmgScale += 0.21; //damage done by mobs increases each level
- b.dmgScale *= 0.91; //damage done by player decreases each level
- game.accelScale *= 1.027 //mob acceleration increases each level
- game.lookFreqScale *= 0.974 //mob cycles between looks decreases each level
- game.CDScale *= 0.964 //mob CD time decreases each level
- }
- game.healScale = 1 / (1 + game.difficulty * 0.09) //a higher denominator makes for lower heals // mech.health += heal * game.healScale;
- },
- difficultyDecrease(num = 1) { //used in easy mode for game.reset()
- for (let i = 0; i < num; i++) {
- game.difficulty--
- game.dmgScale -= 0.21; //damage done by mobs increases each level
- if (game.dmgScale < 0.1) game.dmgScale = 0.1;
- b.dmgScale /= 0.91; //damage done by player decreases each level
- game.accelScale /= 1.027 //mob acceleration increases each level
- game.lookFreqScale /= 0.974 //mob cycles between looks decreases each level
- game.CDScale /= 0.964 //mob CD time decreases each level
- }
- if (game.difficulty < 1) game.difficulty = 0;
- game.healScale = 1 / (1 + game.difficulty * 0.09)
- },
- difficultyText(mode = document.getElementById("difficulty-select").value) {
- if (mode === "0") {
- return "easy"
- } else if (mode === "1") {
- return "normal"
- } else if (mode === "2") {
- return "hard"
- } else if (mode === "4") {
- return "why"
- }
- },
- levelAnnounce() {
- if (level.levelsCleared === 0) {
- document.title = "n-gon: intro (" + level.difficultyText() + ")";
- } else {
- document.title = "n-gon: L" + (level.levelsCleared) + " " + level.levels[level.onLevel] + " (" + level.difficultyText() + ")";
- }
- },
- custom() {}, //each level runs it's own custom code (level exits, ...)
- nextLevel() {
- level.levelsCleared++;
- level.onLevel++; //cycles map to next level
- if (level.onLevel > level.levels.length - 1) level.onLevel = 0;
-
- level.difficultyIncrease(game.difficultyMode) //increase difficulty based on modes
- if (game.isEasyMode && level.levelsCleared % 2) level.difficultyDecrease(1);
- game.clearNow = true; //triggers in game.clearMap to remove all physics bodies and setup for new map
- },
- playerExitCheck() {
- if (
- player.position.x > level.exit.x &&
- player.position.x < level.exit.x + 100 &&
- player.position.y > level.exit.y - 150 &&
- player.position.y < level.exit.y - 40 &&
- player.velocity.y < 0.1
- ) {
- level.nextLevel()
- }
- },
- setPosToSpawn(xPos, yPos) {
- mech.spawnPos.x = mech.pos.x = xPos;
- mech.spawnPos.y = mech.pos.y = yPos;
- level.enter.x = mech.spawnPos.x - 50;
- level.enter.y = mech.spawnPos.y + 20;
- mech.transX = mech.transSmoothX = canvas.width2 - mech.pos.x;
- mech.transY = mech.transSmoothY = canvas.height2 - mech.pos.y;
- mech.Vx = mech.spawnVel.x;
- mech.Vy = mech.spawnVel.y;
- player.force.x = 0;
- player.force.y = 0;
- Matter.Body.setPosition(player, mech.spawnPos);
- Matter.Body.setVelocity(player, mech.spawnVel);
- },
- enter: {
- x: 0,
- y: 0,
- draw() {
- ctx.beginPath();
- ctx.moveTo(level.enter.x, level.enter.y + 30);
- ctx.lineTo(level.enter.x, level.enter.y - 80);
- ctx.bezierCurveTo(level.enter.x, level.enter.y - 170, level.enter.x + 100, level.enter.y - 170, level.enter.x + 100, level.enter.y - 80);
- ctx.lineTo(level.enter.x + 100, level.enter.y + 30);
- ctx.lineTo(level.enter.x, level.enter.y + 30);
- ctx.fillStyle = "#ccc";
- ctx.fill();
- }
- },
- exit: {
- x: 0,
- y: 0,
- draw() {
- ctx.beginPath();
- ctx.moveTo(level.exit.x, level.exit.y + 30);
- ctx.lineTo(level.exit.x, level.exit.y - 80);
- ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80);
- ctx.lineTo(level.exit.x + 100, level.exit.y + 30);
- ctx.lineTo(level.exit.x, level.exit.y + 30);
- ctx.fillStyle = "#0ff";
- ctx.fill();
- }
- },
- fillBG: [],
- drawFillBGs() {
- for (let i = 0, len = level.fillBG.length; i < len; ++i) {
- const f = level.fillBG[i];
- ctx.fillStyle = f.color;
- ctx.fillRect(f.x, f.y, f.width, f.height);
- }
- },
- fill: [],
- drawFills() {
- for (let i = 0, len = level.fill.length; i < len; ++i) {
- const f = level.fill[i];
- ctx.fillStyle = f.color;
- ctx.fillRect(f.x, f.y, f.width, f.height);
- }
- },
- queryList: [], //queries do actions on many objects in regions
- checkQuery() {
- let bounds, action, info;
-
- function isInZone(targetArray) {
- let results = Matter.Query.region(targetArray, bounds);
- for (let i = 0, len = results.length; i < len; ++i) {
- level.queryActions[action](results[i], info);
- }
- }
- for (let i = 0, len = level.queryList.length; i < len; ++i) {
- bounds = level.queryList[i].bounds;
- action = level.queryList[i].action;
- info = level.queryList[i].info;
- for (let j = 0, l = level.queryList[i].groups.length; j < l; ++j) {
- isInZone(level.queryList[i].groups[j]);
- }
- }
- },
- //oddly query regions can't get smaller than 50 width?
- addQueryRegion(x, y, width, height, action, groups = [
- [player], body, mob, powerUp, bullet
- ], info) {
- level.queryList[level.queryList.length] = {
- bounds: {
- min: {
- x: x,
- y: y
- },
- max: {
- x: x + width,
- y: y + height
- }
- },
- action: action,
- groups: groups,
- info: info
+ //******************************************************************************************************************
+ //******************************************************************************************************************
+ //******************************************************************************************************************
+ //******************************************************************************************************************
+ bossRoom1() {
+ level.custom = () => {
+ level.playerExitCheck();
};
- },
- queryActions: {
- bounce(target, info) {
- //jerky fling upwards
- Matter.Body.setVelocity(target, {
- x: info.Vx + (Math.random() - 0.5) * 6,
- y: info.Vy
- });
- target.torque = (Math.random() - 0.5) * 2 * target.mass;
- },
- boost(target, yVelocity) {
- // if (target.velocity.y < 0) {
- // mech.undoCrouch();
- // mech.enterAir();
- mech.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts
- mech.hardLandCD = 0 // disable hard landing
- if (target.velocity.y > 30) {
- Matter.Body.setVelocity(target, {
- x: target.velocity.x + (Math.random() - 0.5) * 2,
- y: -23 //gentle bounce if coming down super fast
- });
- } else {
- Matter.Body.setVelocity(target, {
- x: target.velocity.x + (Math.random() - 0.5) * 2,
- y: yVelocity
- });
- }
+ level.setPosToSpawn(0, -50); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
+ level.exit.x = 1500;
+ level.exit.y = -1875;
+ level.defaultZoom = 1800
+ game.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#dcdcde";
+ // powerUps.spawnStartingPowerUps(1475, -1175);
+ // spawn.debris(750, -2200, 3700, 16); //16 debris per level
+ // level.fill.push({ //foreground
+ // x: 2500,
+ // y: -1100,
+ // width: 450,
+ // height: 250,
+ // color: "rgba(0,0,0,0.1)"
+ // });
+ // level.fillBG.push({ //background
+ // x: 1300,
+ // y: -1800,
+ // width: 750,
+ // height: 1800,
+ // color: "#d4d4d7"
+ // });
- },
- force(target, info) {
- if (target.velocity.y < 0) {
- //gently force up if already on the way up
- target.force.x += info.Vx * target.mass;
- target.force.y += info.Vy * target.mass;
- } else {
- target.force.y -= 0.0007 * target.mass; //gently fall in on the way down
- }
- },
- antiGrav(target) {
- target.force.y -= 0.0011 * target.mass;
- },
- death(target) {
- target.death();
- }
+ spawn.mapRect(-100, 0, 1000, 100);
+ // spawn.bodyRect(1540, -1110, 300, 25, 0.9);
+ // spawn.boost(4150, 0, 1300);
+ // spawn.randomSmallMob(1300, -70);
+ // spawn.randomMob(2650, -975, 0.8);
+ // spawn.randomBoss(1700, -900, 0.4);
+ // if (game.difficulty > 3) spawn.randomLevelBoss(2200, -1300);
},
- addToWorld() {
- //needs to be run to put bodies into the world
- for (let i = 0; i < body.length; i++) {
- //body[i].collisionFilter.group = 0;
- if (body[i] !== mech.holdingTarget) {
- body[i].collisionFilter.category = cat.body;
- body[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
- }
- body[i].classType = "body";
- World.add(engine.world, body[i]); //add to world
- }
- for (let i = 0; i < map.length; i++) {
- //map[i].collisionFilter.group = 0;
- map[i].collisionFilter.category = cat.map;
- map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
- Matter.Body.setStatic(map[i], true); //make static
- World.add(engine.world, map[i]); //add to world
- }
- for (let i = 0; i < cons.length; i++) {
- World.add(engine.world, cons[i]);
- }
- for (let i = 0; i < consBB.length; i++) {
- World.add(engine.world, consBB[i]);
- }
+ template() {
+ level.custom = () => {
+ level.playerExitCheck();
+ };
+ level.setPosToSpawn(0, -50); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
+ level.exit.x = 1500;
+ level.exit.y = -1875;
+ level.defaultZoom = 1800
+ game.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#dcdcde";
+ // powerUps.spawnStartingPowerUps(1475, -1175);
+ // spawn.debris(750, -2200, 3700, 16); //16 debris per level
+ // level.fill.push({ //foreground
+ // x: 2500,
+ // y: -1100,
+ // width: 450,
+ // height: 250,
+ // color: "rgba(0,0,0,0.1)"
+ // });
+ // level.fillBG.push({ //background
+ // x: 1300,
+ // y: -1800,
+ // width: 750,
+ // height: 1800,
+ // color: "#d4d4d7"
+ // });
+
+ spawn.mapRect(-100, 0, 1000, 100);
+ // spawn.bodyRect(1540, -1110, 300, 25, 0.9);
+ // spawn.boost(4150, 0, 1300);
+ // spawn.randomSmallMob(1300, -70);
+ // spawn.randomMob(2650, -975, 0.8);
+ // spawn.randomBoss(1700, -900, 0.4);
+ // if (game.difficulty > 3) spawn.randomLevelBoss(2200, -1300);
},
- //******************************************************************************************************************
- //******************************************************************************************************************
- //******************************************************************************************************************
- //******************************************************************************************************************
testing() {
level.custom = () => {
level.playerExitCheck();
@@ -358,14 +198,14 @@ const level = {
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
spawn.boost(1500, 0, 900);
- // spawn.bomberBoss(2900, -500)
+ spawn.bomberBoss(2900, -500)
// spawn.launcherBoss(1200, -500)
// spawn.laserTargetingBoss(1600, -400)
// spawn.spawner(1600, -500)
// spawn.sniper(1700, -120, 50)
- spawn.sniper(1400, -120)
- spawn.sniper(1800, -120)
- spawn.sniper(2200, -120)
+ // spawn.sniper(1400, -120)
+ // spawn.sniper(1800, -120)
+ // spawn.sniper(2200, -120)
// spawn.cellBossCulture(1600, -500)
// spawn.shooter(1600, -500)
// spawn.striker(1600, -500)
@@ -2056,4 +1896,239 @@ const level = {
if (game.difficulty > 3) spawn.randomLevelBoss(1850, -1400, 1);
},
+ //******************************************************************************************************************
+ //******************************************************************************************************************
+ //******************************************************************************************************************
+ //******************************************************************************************************************
+ isBuildRun: false,
+ difficultyIncrease(num = 1) {
+ // if (level.isBuildRun) num++
+ for (let i = 0; i < num; i++) {
+ game.difficulty++
+ game.dmgScale += 0.21; //damage done by mobs increases each level
+ b.dmgScale *= 0.91; //damage done by player decreases each level
+ game.accelScale *= 1.027 //mob acceleration increases each level
+ game.lookFreqScale *= 0.974 //mob cycles between looks decreases each level
+ game.CDScale *= 0.964 //mob CD time decreases each level
+ }
+ game.healScale = 1 / (1 + game.difficulty * 0.09) //a higher denominator makes for lower heals // mech.health += heal * game.healScale;
+ },
+ difficultyDecrease(num = 1) { //used in easy mode for game.reset()
+ for (let i = 0; i < num; i++) {
+ game.difficulty--
+ game.dmgScale -= 0.21; //damage done by mobs increases each level
+ if (game.dmgScale < 0.1) game.dmgScale = 0.1;
+ b.dmgScale /= 0.91; //damage done by player decreases each level
+ game.accelScale /= 1.027 //mob acceleration increases each level
+ game.lookFreqScale /= 0.974 //mob cycles between looks decreases each level
+ game.CDScale /= 0.964 //mob CD time decreases each level
+ }
+ if (game.difficulty < 1) game.difficulty = 0;
+ game.healScale = 1 / (1 + game.difficulty * 0.09)
+ },
+ difficultyText(mode = document.getElementById("difficulty-select").value) {
+ if (mode === "0") {
+ return "easy"
+ } else if (mode === "1") {
+ return "normal"
+ } else if (mode === "2") {
+ return "hard"
+ } else if (mode === "4") {
+ return "why"
+ }
+ },
+ levelAnnounce() {
+ if (level.levelsCleared === 0) {
+ document.title = "n-gon: intro (" + level.difficultyText() + ")";
+ } else {
+ document.title = "n-gon: L" + (level.levelsCleared) + " " + level.levels[level.onLevel] + " (" + level.difficultyText() + ")";
+ }
+ },
+ custom() {}, //each level runs it's own custom code (level exits, ...)
+ nextLevel() {
+ level.levelsCleared++;
+ level.onLevel++; //cycles map to next level
+ if (level.onLevel > level.levels.length - 1) level.onLevel = 0;
+
+ level.difficultyIncrease(game.difficultyMode) //increase difficulty based on modes
+ if (game.isEasyMode && level.levelsCleared % 2) level.difficultyDecrease(1);
+ game.clearNow = true; //triggers in game.clearMap to remove all physics bodies and setup for new map
+ if (mod.isHealLowHealth && mech.health < mech.maxHealth * 0.5) {
+ mech.health = mech.maxHealth * 0.5
+ mod.onHealthChange();
+ mech.displayHealth();
+ }
+ },
+ playerExitCheck() {
+ if (
+ player.position.x > level.exit.x &&
+ player.position.x < level.exit.x + 100 &&
+ player.position.y > level.exit.y - 150 &&
+ player.position.y < level.exit.y - 40 &&
+ player.velocity.y < 0.1
+ ) {
+ level.nextLevel()
+ }
+ },
+ setPosToSpawn(xPos, yPos) {
+ mech.spawnPos.x = mech.pos.x = xPos;
+ mech.spawnPos.y = mech.pos.y = yPos;
+ level.enter.x = mech.spawnPos.x - 50;
+ level.enter.y = mech.spawnPos.y + 20;
+ mech.transX = mech.transSmoothX = canvas.width2 - mech.pos.x;
+ mech.transY = mech.transSmoothY = canvas.height2 - mech.pos.y;
+ mech.Vx = mech.spawnVel.x;
+ mech.Vy = mech.spawnVel.y;
+ player.force.x = 0;
+ player.force.y = 0;
+ Matter.Body.setPosition(player, mech.spawnPos);
+ Matter.Body.setVelocity(player, mech.spawnVel);
+ },
+ enter: {
+ x: 0,
+ y: 0,
+ draw() {
+ ctx.beginPath();
+ ctx.moveTo(level.enter.x, level.enter.y + 30);
+ ctx.lineTo(level.enter.x, level.enter.y - 80);
+ ctx.bezierCurveTo(level.enter.x, level.enter.y - 170, level.enter.x + 100, level.enter.y - 170, level.enter.x + 100, level.enter.y - 80);
+ ctx.lineTo(level.enter.x + 100, level.enter.y + 30);
+ ctx.lineTo(level.enter.x, level.enter.y + 30);
+ ctx.fillStyle = "#ccc";
+ ctx.fill();
+ }
+ },
+ exit: {
+ x: 0,
+ y: 0,
+ draw() {
+ ctx.beginPath();
+ ctx.moveTo(level.exit.x, level.exit.y + 30);
+ ctx.lineTo(level.exit.x, level.exit.y - 80);
+ ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80);
+ ctx.lineTo(level.exit.x + 100, level.exit.y + 30);
+ ctx.lineTo(level.exit.x, level.exit.y + 30);
+ ctx.fillStyle = "#0ff";
+ ctx.fill();
+ }
+ },
+ fillBG: [],
+ drawFillBGs() {
+ for (let i = 0, len = level.fillBG.length; i < len; ++i) {
+ const f = level.fillBG[i];
+ ctx.fillStyle = f.color;
+ ctx.fillRect(f.x, f.y, f.width, f.height);
+ }
+ },
+ fill: [],
+ drawFills() {
+ for (let i = 0, len = level.fill.length; i < len; ++i) {
+ const f = level.fill[i];
+ ctx.fillStyle = f.color;
+ ctx.fillRect(f.x, f.y, f.width, f.height);
+ }
+ },
+ queryList: [], //queries do actions on many objects in regions
+ checkQuery() {
+ let bounds, action, info;
+
+ function isInZone(targetArray) {
+ let results = Matter.Query.region(targetArray, bounds);
+ for (let i = 0, len = results.length; i < len; ++i) {
+ level.queryActions[action](results[i], info);
+ }
+ }
+ for (let i = 0, len = level.queryList.length; i < len; ++i) {
+ bounds = level.queryList[i].bounds;
+ action = level.queryList[i].action;
+ info = level.queryList[i].info;
+ for (let j = 0, l = level.queryList[i].groups.length; j < l; ++j) {
+ isInZone(level.queryList[i].groups[j]);
+ }
+ }
+ },
+ //oddly query regions can't get smaller than 50 width?
+ addQueryRegion(x, y, width, height, action, groups = [
+ [player], body, mob, powerUp, bullet
+ ], info) {
+ level.queryList[level.queryList.length] = {
+ bounds: {
+ min: {
+ x: x,
+ y: y
+ },
+ max: {
+ x: x + width,
+ y: y + height
+ }
+ },
+ action: action,
+ groups: groups,
+ info: info
+ };
+ },
+ queryActions: {
+ bounce(target, info) {
+ //jerky fling upwards
+ Matter.Body.setVelocity(target, {
+ x: info.Vx + (Math.random() - 0.5) * 6,
+ y: info.Vy
+ });
+ target.torque = (Math.random() - 0.5) * 2 * target.mass;
+ },
+ boost(target, yVelocity) {
+ mech.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts
+ mech.hardLandCD = 0 // disable hard landing
+ if (target.velocity.y > 30) {
+ Matter.Body.setVelocity(target, {
+ x: target.velocity.x + (Math.random() - 0.5) * 2,
+ y: -23 //gentle bounce if coming down super fast
+ });
+ } else {
+ Matter.Body.setVelocity(target, {
+ x: target.velocity.x + (Math.random() - 0.5) * 2,
+ y: yVelocity
+ });
+ }
+
+ },
+ force(target, info) {
+ if (target.velocity.y < 0) { //gently force up if already on the way up
+ target.force.x += info.Vx * target.mass;
+ target.force.y += info.Vy * target.mass;
+ } else {
+ target.force.y -= 0.0007 * target.mass; //gently fall in on the way down
+ }
+ },
+ antiGrav(target) {
+ target.force.y -= 0.0011 * target.mass;
+ },
+ death(target) {
+ target.death();
+ }
+ },
+ addToWorld() { //needs to be run to put bodies into the world
+ for (let i = 0; i < body.length; i++) {
+ //body[i].collisionFilter.group = 0;
+ if (body[i] !== mech.holdingTarget) {
+ body[i].collisionFilter.category = cat.body;
+ body[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
+ }
+ body[i].classType = "body";
+ World.add(engine.world, body[i]); //add to world
+ }
+ for (let i = 0; i < map.length; i++) {
+ //map[i].collisionFilter.group = 0;
+ map[i].collisionFilter.category = cat.map;
+ map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
+ Matter.Body.setStatic(map[i], true); //make static
+ World.add(engine.world, map[i]); //add to world
+ }
+ for (let i = 0; i < cons.length; i++) {
+ World.add(engine.world, cons[i]);
+ }
+ for (let i = 0; i < consBB.length; i++) {
+ World.add(engine.world, consBB[i]);
+ }
+ },
};
\ No newline at end of file
diff --git a/js/mob.js b/js/mob.js
index 3dd2233..b50a6ba 100644
--- a/js/mob.js
+++ b/js/mob.js
@@ -44,46 +44,63 @@ const mobs = {
}
},
statusSlow(who, cycles = 60) {
- if (!who.shield && !who.isShielded && !mech.isBodiesAsleep) {
- if (who.isBoss) {
- cycles = Math.floor(cycles * 0.25)
+ applySlow(who)
+ //look for mobs near the target
+ if (mod.isAoESlow) {
+ const range = (220 + 150 * Math.random()) ** 2
+ for (let i = 0, len = mob.length; i < len; i++) {
+ if (Vector.magnitudeSquared(Vector.sub(who.position, mob[i].position)) < range) applySlow(mob[i])
}
- //remove other "slow" effects on this mob
- let i = who.status.length
- while (i--) {
- if (who.status[i].type === "slow") who.status.splice(i, 1);
+ game.drawList.push({
+ x: who.position.x,
+ y: who.position.y,
+ radius: Math.sqrt(range),
+ color: "rgba(0,100,255,0.05)",
+ time: 3
+ });
+ }
+
+
+ function applySlow(target) {
+ if (!target.shield && !target.isShielded && !mech.isBodiesAsleep) {
+ if (target.isBoss) cycles = Math.floor(cycles * 0.25)
+
+ let i = target.status.length
+ while (i--) {
+ if (target.status[i].type === "slow") target.status.splice(i, 1); //remove other "slow" effects on this mob
+ }
+ target.status.push({
+ effect() {
+ Matter.Body.setVelocity(target, {
+ x: 0,
+ y: 0
+ });
+ Matter.Body.setAngularVelocity(target, 0);
+ ctx.beginPath();
+ ctx.moveTo(target.vertices[0].x, target.vertices[0].y);
+ for (let j = 1, len = target.vertices.length; j < len; ++j) {
+ ctx.lineTo(target.vertices[j].x, target.vertices[j].y);
+ }
+ ctx.lineTo(target.vertices[0].x, target.vertices[0].y);
+ ctx.strokeStyle = "rgba(0,100,255,0.8)";
+ ctx.lineWidth = 15;
+ ctx.stroke();
+ ctx.fillStyle = target.fill
+ ctx.fill();
+ },
+ type: "slow",
+ endCycle: game.cycle + cycles,
+ })
}
- who.status.push({
- effect() {
- Matter.Body.setVelocity(who, {
- x: 0,
- y: 0
- });
- Matter.Body.setAngularVelocity(who, 0);
- ctx.beginPath();
- ctx.moveTo(who.vertices[0].x, who.vertices[0].y);
- for (let j = 1, len = who.vertices.length; j < len; ++j) {
- ctx.lineTo(who.vertices[j].x, who.vertices[j].y);
- }
- ctx.lineTo(who.vertices[0].x, who.vertices[0].y);
- ctx.strokeStyle = "rgba(0,100,255,0.8)";
- ctx.lineWidth = 15;
- ctx.stroke();
- ctx.fillStyle = who.fill
- ctx.fill();
- },
- type: "slow",
- endCycle: game.cycle + cycles,
- })
}
},
statusStun(who, cycles = 180) {
if (!who.shield && !who.isShielded && !mech.isBodiesAsleep) {
Matter.Body.setVelocity(who, {
- x: who.velocity.x * 0.5,
- y: who.velocity.y * 0.5
+ x: who.velocity.x * 0.8,
+ y: who.velocity.y * 0.8
});
- Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.5);
+ Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.8);
//remove other "stun" effects on this mob
let i = who.status.length
while (i--) {
@@ -97,7 +114,7 @@ const mobs = {
x: who.position.x + 100 * (Math.random() - 0.5),
y: who.position.y + 100 * (Math.random() - 0.5)
}
- if (who.velocity.y < 2) who.force.y += who.mass * 0.0005 //extra gravity
+ if (who.velocity.y < 2) who.force.y += who.mass * 0.0004 //extra gravity
ctx.beginPath();
ctx.moveTo(who.vertices[0].x, who.vertices[0].y);
for (let j = 1, len = who.vertices.length; j < len; ++j) {
@@ -831,7 +848,7 @@ const mobs = {
Matter.Query.ray(map, this.position, this.mechPosRange()).length === 0 && //see player
Matter.Query.ray(body, this.position, this.mechPosRange()).length === 0
) {
- spawn.bullet(this.position.x, this.position.y + this.radius * 0.5, 10 + Math.ceil(this.radius / 15), 5);
+ spawn.bomb(this.position.x, this.position.y + this.radius * 0.5, 10 + Math.ceil(this.radius / 15), 5);
//add spin and speed
Matter.Body.setAngularVelocity(mob[mob.length - 1], (Math.random() - 0.5) * 0.5);
Matter.Body.setVelocity(mob[mob.length - 1], {
@@ -1000,7 +1017,7 @@ const mobs = {
}
}
if (Math.random() < mod.isBotSpawner) {
- if (Math.random() < 0.2) { //very low chance of plasma bot
+ if (Math.random() < 0.1) { //very low chance of plasma bot
b.plasmaBot(this.position)
} else if (Math.random() < 0.25) {
b.nailBot(this.position)
diff --git a/js/mods.js b/js/mods.js
index 215f20a..051a5d2 100644
--- a/js/mods.js
+++ b/js/mods.js
@@ -80,36 +80,24 @@ const mod = {
return dmg * mod.slowFire
},
onHealthChange() { //used with acid mod
- if (mod.isAcidDmg && mech.health > 0.8) {
- mod.acidDmg = 0.5
- if (!build.isCustomSelection) {
- setTimeout(function () {
- if (document.getElementById("mod-acid")) document.getElementById("mod-acid").innerHTML = " (on)"
- }, 10);
- }
- } else {
- mod.acidDmg = 0
- if (!build.isCustomSelection) {
- setTimeout(function () {
- if (document.getElementById("mod-acid")) document.getElementById("mod-acid").innerHTML = " (off)"
- }, 10);
+ if (mod.isAcidDmg) {
+ if (mech.health > 0.8) {
+ mod.acidDmg = 0.5
+ if (!build.isCustomSelection) {
+ setTimeout(function () {
+ if (document.getElementById("mod-acid")) document.getElementById("mod-acid").innerHTML = " (on)"
+ }, 10);
+ }
+ } else {
+ mod.acidDmg = 0
+ if (!build.isCustomSelection) {
+ setTimeout(function () {
+ if (document.getElementById("mod-acid")) document.getElementById("mod-acid").innerHTML = " (off)"
+ }, 10);
+ }
}
}
- // if (mod.isLowHealthDmg) {
- // if (!build.isCustomSelection) {
- // setTimeout(function () {
- // if (document.getElementById("mod-low-health-damage")) document.getElementById("mod-low-health-damage").innerHTML = " +" + (((3 / (2 + Math.min(mech.health, 1))) - 1) * 100).toFixed(0) + "%"
- // }, 10);
- // }
- // }
},
- resetModText() {
- setTimeout(function () {
- if (document.getElementById("mod-acid")) document.getElementById("mod-acid").innerHTML = "";
- if (document.getElementById("mod-low-health-damage")) document.getElementById("mod-low-health-damage").innerHTML = "";
- }, 10);
- },
-
mods: [{
name: "capacitor",
// nameInfo: "",
@@ -178,7 +166,6 @@ const mod = {
},
{
name: "negative feedback",
- // nameInfo: "",
description: "do extra damage at low health
up to 50% increase when near death",
maxCount: 1,
count: 0,
@@ -587,7 +574,7 @@ const mod = {
},
{
name: "Pauli exclusion",
- description: `immune to harm for 1 second
activates after being harmed from a collision`,
+ description: `after being harmed from a collision
immune to harm for 1 second`,
maxCount: 9,
count: 0,
allowed() {
@@ -621,6 +608,11 @@ const mod = {
{
name: "entanglement",
nameInfo: "",
+ addNameInfo() {
+ setTimeout(function () {
+ game.boldActiveGunHUD();
+ }, 1000);
+ },
description: "16% less harm for each gun in your inventory
while your first gun is equipped",
maxCount: 1,
count: 0,
@@ -769,7 +761,7 @@ const mod = {
},
{
name: "negentropy",
- description: "when below 25% of maximum health
heal 1% of maximum health per second",
+ description: "at the start of each level
heal up to 50% of maximum health",
maxCount: 9,
count: 0,
allowed() {
@@ -785,7 +777,7 @@ const mod = {
},
{
name: "crystallized armor",
- description: "increase maximum health by 4% for each
unused power up at the end of a level",
+ description: "increase maximum health by 5% for each
unused power up at the end of a level",
maxCount: 1,
count: 0,
allowed() {
@@ -815,23 +807,6 @@ const mod = {
mod.recursiveHealing = 1;
}
},
- {
- name: "pair production",
- description: "power ups overfill your energy
temporarily gain twice your max energy",
- maxCount: 1,
- count: 0,
- allowed() {
- return true
- },
- requires: "",
- effect: () => {
- mod.isMassEnergy = true // used in mech.grabPowerUp
- mech.energy = mech.maxEnergy * 2
- },
- remove() {
- mod.isMassEnergy = false;
- }
- },
{
name: "bubble fusion",
description: "after destroying a mob's shield
spawn 3 heals, ammo, or rerolls",
@@ -886,7 +861,7 @@ const mod = {
},
{
name: "supply chain",
- description: "double your current ammo for all gun",
+ description: "double your current ammo for all guns",
maxCount: 9,
count: 0,
isNonRefundable: true,
@@ -936,7 +911,7 @@ const mod = {
},
{
name: "cardinality",
- description: "2 extra choices when selecting power ups",
+ description: "2 extra choices for mods, guns, and fields",
maxCount: 1,
count: 0,
allowed() {
@@ -994,7 +969,7 @@ const mod = {
},
{
name: "many-worlds",
- description: "after choosing a gun, field, or mod
spawn a reroll, if you have none",
+ description: "after choosing a mod, gun, or field
spawn a reroll, if you have none",
maxCount: 1,
count: 0,
allowed() {
@@ -1011,7 +986,12 @@ const mod = {
{
name: "anthropic principle",
nameInfo: "",
- description: "heal to 50% health instead of dying
consumes 1 reroll",
+ addNameInfo() {
+ setTimeout(function () {
+ powerUps.reroll.changeRerolls(0)
+ }, 1000);
+ },
+ description: "heal to 60% health instead of dying
consumes 1 reroll",
maxCount: 1,
count: 0,
allowed() {
@@ -1030,7 +1010,7 @@ const mod = {
},
{
name: "quantum immortality",
- description: "after dying, continue in an alternate reality
spawn 3 rerolls",
+ description: "after dying, continue in an alternate reality
spawn 5 rerolls",
maxCount: 1,
count: 0,
allowed() {
@@ -1039,9 +1019,9 @@ const mod = {
requires: "",
effect() {
mod.isImmortal = true;
- powerUps.spawn(mech.pos.x, mech.pos.y, "reroll", false);
- powerUps.spawn(mech.pos.x, mech.pos.y, "reroll", false);
- powerUps.spawn(mech.pos.x, mech.pos.y, "reroll", false);
+ for (let i = 0; i < 5; i++) {
+ powerUps.spawn(mech.pos.x, mech.pos.y, "reroll", false);
+ }
},
remove() {
mod.isImmortal = false;
@@ -1143,6 +1123,9 @@ const mod = {
{
name: "fluoroantimonic acid",
nameInfo: "",
+ addNameInfo() {
+ mod.onHealthChange();
+ },
description: "each bullet does instant acid damage
active when you are above 80% base health",
maxCount: 1,
count: 0,
@@ -1153,7 +1136,6 @@ const mod = {
requires: "health above 80%",
effect() {
mod.isAcidDmg = true;
- mod.onHealthChange();
},
remove() {
mod.acidDmg = 0;
@@ -1697,6 +1679,22 @@ const mod = {
mod.isFastDrones = false
}
},
+ {
+ name: "superfluidity",
+ description: "freeze effects apply to mobs near it's target",
+ maxCount: 1,
+ count: 0,
+ allowed() {
+ return mod.haveGunCheck("ice IX") || mod.isIceCrystals || mod.isSporeFreeze || (mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" && mod.isIceField)
+ },
+ requires: "a freeze effect",
+ effect() {
+ mod.isAoESlow = true
+ },
+ remove() {
+ mod.isAoESlow = false
+ }
+ },
{
name: "heavy water",
description: "ice IX is synthesized with an extra neutron
does radioactive damage over 3 seconds",
@@ -1991,6 +1989,23 @@ const mod = {
mech.fieldShieldingScale = 1;
}
},
+ {
+ name: "pair production",
+ description: "power ups overfill your energy
temporarily gain 3x your maximum energy",
+ maxCount: 1,
+ count: 0,
+ allowed() {
+ return mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing"
+ },
+ requires: "nano-scale manufacturing",
+ effect: () => {
+ mod.isMassEnergy = true // used in mech.grabPowerUp
+ mech.energy = mech.maxEnergy * 3
+ },
+ remove() {
+ mod.isMassEnergy = false;
+ }
+ },
{
name: "mycelium manufacturing",
description: "nano-scale manufacturing is repurposed
excess energy used to grow spores",
@@ -2276,5 +2291,6 @@ const mod = {
isPulseAim: null,
isSporeFreeze: null,
isShotgunRecoil: null,
- isHealLowHealth: null
+ isHealLowHealth: null,
+ isAoESlow: null
}
\ No newline at end of file
diff --git a/js/player.js b/js/player.js
index 41e2da2..f8f2e87 100644
--- a/js/player.js
+++ b/js/player.js
@@ -476,7 +476,7 @@ const mech = {
if (mod.isDeathAvoid && powerUps.reroll.rerolls) { //&& Math.random() < 0.5
powerUps.reroll.changeRerolls(-1)
- mech.energy = mech.maxEnergy * 0.5
+ mech.energy = mech.maxEnergy * 0.6
// if (mech.energy < 0.05) mech.energy = 0.05
mech.immuneCycle = mech.cycle + 120 //disable this.immuneCycle bonus seconds
game.makeTextLog(" death avoided
1 reroll consumed", 420)
@@ -505,7 +505,7 @@ const mech = {
if (mech.health < 0 || isNaN(mech.health)) {
if (mod.isDeathAvoid && powerUps.reroll.rerolls > 0) { //&& Math.random() < 0.5
powerUps.reroll.changeRerolls(-1)
- mech.health = mech.maxHealth * 0.5
+ mech.health = mech.maxHealth * 0.6
// if (mech.health < 0.05) mech.health = 0.05
mech.immuneCycle = mech.cycle + 120 //disable this.immuneCycle bonus seconds
game.makeTextLog(" death avoided
1 reroll consumed", 420)
@@ -942,7 +942,7 @@ const mech = {
y: powerUp[i].velocity.y * 0.11
});
if (dist2 < 5000 && !game.isChoosing) { //use power up if it is close enough
- if (mod.isMassEnergy) mech.energy = mech.maxEnergy * 2;
+ if (mod.isMassEnergy) mech.energy = mech.maxEnergy * 3;
Matter.Body.setVelocity(player, { //player knock back, after grabbing power up
x: player.velocity.x + ((powerUp[i].velocity.x * powerUp[i].mass) / player.mass) * 0.3,
y: player.velocity.y + ((powerUp[i].velocity.y * powerUp[i].mass) / player.mass) * 0.3
@@ -1950,7 +1950,7 @@ const mech = {
y: powerUp[i].velocity.y * 0.11
});
if (dist2 < 5000 && !game.isChoosing) { //use power up if it is close enough
- if (mod.isMassEnergy) mech.energy = mech.maxEnergy * 2;
+ if (mod.isMassEnergy) mech.energy = mech.maxEnergy * 3;
powerUp[i].effect();
Matter.World.remove(engine.world, powerUp[i]);
powerUp.splice(i, 1);
diff --git a/js/powerup.js b/js/powerup.js
index 72559a2..491d1df 100644
--- a/js/powerup.js
+++ b/js/powerup.js
@@ -79,7 +79,7 @@ const powerUps = {
}
return out
},
- use(type) {
+ use(type) { //runs when you actually reroll a list of selections, type can be field, gun, or mod
powerUps.reroll.changeRerolls(-1)
powerUps[type].effect();
},
@@ -218,7 +218,6 @@ const powerUps = {
choiceLog: [], //records all previous choice options
effect() {
-
function pick(skip1 = -1, skip2 = -1, skip3 = -1, skip4 = -1) {
let options = [];
for (let i = 0; i < mod.mods.length; i++) {
diff --git a/js/spawn.js b/js/spawn.js
index 1bbe00f..10aee9d 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -1466,20 +1466,20 @@ const spawn = {
// }
// };
// },
- bomberBoss(x, y, radius = 85 + Math.ceil(Math.random() * 20)) {
+ bomberBoss(x, y, radius = 80 + Math.floor(Math.random() * 15)) {
//boss that drops bombs from above and holds a set distance from player
mobs.spawn(x, y, 3, radius, "transparent");
let me = mob[mob.length - 1];
me.isBoss = true;
- Matter.Body.setDensity(me, 0.0015 + 0.0004 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger
+ Matter.Body.setDensity(me, 0.0014 + 0.0003 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger
me.stroke = "rgba(255,0,200)"; //used for drawGhost
me.seeAtDistance2 = 1500000;
- me.fireFreq = Math.ceil(30 + 2000 / radius);
+ me.fireFreq = Math.ceil(60 + 3000 / radius);
me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search
me.hoverElevation = 460 + (Math.random() - 0.5) * 200; //squared
me.hoverXOff = (Math.random() - 0.5) * 100;
- me.accelMag = Math.floor(10 * (Math.random() + 5)) * 0.00001 * game.accelScale;
+ me.accelMag = Math.floor(10 * (Math.random() + 4.5)) * 0.00001 * game.accelScale;
me.g = 0.0002; //required if using 'gravity' // gravity called in hoverOverPlayer
me.frictionStatic = 0;
me.friction = 0;
@@ -1578,7 +1578,7 @@ const spawn = {
this.explode(this.mass * 10);
};
Matter.Body.setDensity(me, 0.0001); //normal is 0.001
- me.timeLeft = 240;
+ me.timeLeft = 200;
me.g = 0.001; //required if using 'gravity'
me.frictionAir = 0;
me.restitution = 0.8;
@@ -1592,6 +1592,58 @@ const spawn = {
this.timeLimit();
};
},
+ bomb(x, y, radius = 6, sides = 5) {
+ mobs.spawn(x, y, sides, radius, "rgb(255,0,0)");
+ let me = mob[mob.length - 1];
+ me.stroke = "transparent";
+ me.onHit = function () {
+ this.explode(this.mass * 10);
+ };
+ me.onDeath = function () {
+ if (game.difficulty > 10) {
+ spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5);
+ spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5);
+ spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5);
+ const mag = 8
+ const v1 = Vector.rotate({
+ x: 1,
+ y: 1
+ }, 2 * Math.PI * Math.random())
+ const v2 = Vector.rotate({
+ x: 1,
+ y: 1
+ }, 2 * Math.PI * Math.random())
+ const v3 = Vector.normalise(Vector.add(v1, v2)) //last vector is opposite the sum of the other two to look a bit like momentum is conserved
+
+ Matter.Body.setVelocity(mob[mob.length - 1], {
+ x: mag * v1.x,
+ y: mag * v1.y
+ });
+ Matter.Body.setVelocity(mob[mob.length - 2], {
+ x: mag * v2.x,
+ y: mag * v2.y
+ });
+ Matter.Body.setVelocity(mob[mob.length - 3], {
+ x: -mag * v3.x,
+ y: -mag * v3.y
+ });
+ }
+ }
+ Matter.Body.setDensity(me, 0.0001); //normal is 0.001
+ me.timeLeft = 95 + Math.floor(Math.random() * 15);
+ me.g = 0.001; //required if using 'gravity'
+ me.frictionAir = 0;
+ me.restitution = 1;
+ me.leaveBody = false;
+ me.dropPowerUp = false;
+ me.showHealthBar = false;
+ me.collisionFilter.category = cat.mobBullet;
+ me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet;
+ me.do = function () {
+ this.gravity();
+ this.timeLimit();
+ };
+ },
sniper(x, y, radius = 35 + Math.ceil(Math.random() * 30)) {
mobs.spawn(x, y, 3, radius, "transparent"); //"rgb(25,0,50)")
diff --git a/todo.txt b/todo.txt
index c78e17c..3150be3 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,13 +1,29 @@
+negentropy - heals player to 50% max health at the end of a level
+crystallized armor - give 5% (was 4%) max health for a power up
+mod - superfluidity: AoE freeze effect
+bomberBoss's bombs split into 3 bombs on later levels
+mod - pair production only works for nano-scale manufacturing, but gives 3x (up from 2x) max energy
+press Z+X in testing mode to trigger player death
************** TODO - n-gon **************
+plasma field gets longer as you hold it longer
+
+mod - AoE radiation might work when the effect ends
+ or maybe just anytime another mob is near
+
+wave beam: mod - longitudinal waves (bullets oscillate fast /slow not up/down)
+ trigger on crouch?
+
+medium caliber gun in between minigun and rail gun
+ mod: electricity damages mobs that get near the bullet
+ get ammo back if it hits mobs
+ ammo returns to you if it misses
+
mob that flashes the player (makes the graphics not update for a couple seconds)
-held blocks aren't moving to the next level
- only issue is setting held block to not collide
-
-mod do 50% more damage in close, but 50% less at a distance
+mod - do 50% more damage in close, but 50% less at a distance
code it like mod.isFarAwayDmg
have these mods disable each other