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