diff --git a/js/bullet.js b/js/bullet.js index 852aa64..b5ebfff 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -928,17 +928,22 @@ const b = { deathCycles: 110 + RADIUS * 5, isImproved: false, beforeDmg(who) { - //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 - }); + if (mod.isIncendiary) { + b.explosion(this.position, 120 + (Math.random() - 0.5) * 60); //makes bullet do explosive damage at end + this.endCycle = 0 + } else { + //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 - if (this.endCycle > game.cycle + this.deathCycles) { - this.endCycle -= 60 - if (game.cycle + this.deathCycles > this.endCycle) this.endCycle = game.cycle + this.deathCycles + this.lockedOn = null + if (this.endCycle > game.cycle + this.deathCycles) { + this.endCycle -= 60 + if (game.cycle + this.deathCycles > this.endCycle) this.endCycle = game.cycle + this.deathCycles + } } }, onEnd() {}, @@ -1377,6 +1382,7 @@ const b = { dmg: 0, // 0.14 //damage done in addition to the damage from momentum minDmgSpeed: 2, lookFrequency: 40 + Math.floor(7 * Math.random()), + drainThreshold: mod.isEnergyHealth ? 0.5 : 0.15, acceleration: 0.0015 * (1 + 0.3 * Math.random()), range: 700 * (1 + 0.1 * Math.random()) + 250 * mod.isLaserBotUpgrade, followRange: 150 + Math.floor(30 * Math.random()), @@ -1430,47 +1436,9 @@ const b = { } } //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 - // 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)) - - // //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, acceleration: 0.009, endCycle: Infinity, + drainThreshold: mod.isEnergyHealth ? 0.5 : 0.15, classType: "bullet", collisionFilter: { category: cat.bullet, @@ -1608,9 +1577,8 @@ const b = { const sub = Vector.sub(this.lockedOn.position, this.position) const DIST = Vector.magnitude(sub); const unit = Vector.normalise(sub) - const DRAIN = 0.0012 - if (DIST < mod.isPlasmaRange * 450 && mech.energy > DRAIN) { - mech.energy -= DRAIN; + if (DIST < mod.isPlasmaRange * 450 && mech.energy > this.drainThreshold) { + mech.energy -= 0.0012; if (mech.energy < 0) { mech.fieldCDcycle = mech.cycle + 120; mech.energy = 0; @@ -1904,27 +1872,58 @@ const b = { mech.fireCDcycle = mech.cycle + Math.floor(CD * b.fireCD); // cool down 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 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 (mod.isIncendiary) { + const me = bullet.length; + bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), 25, 2, { + density: 0.0001, //frictionAir: 0.01, //restitution: 0, + angle: angle, + friction: 0.5, + frictionAir: 0, + dmg: 0, //damage done in addition to the damage from momentum + endCycle: Math.floor(mech.crouch ? 28 : 13) + Math.random() * 5 + game.cycle, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + 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) { - mech.fireCDcycle = mech.cycle + 60; // cool down - } else { - mech.energy -= mech.fieldRegen + 0.009 + if (mech.energy < 0.01) { + mech.fireCDcycle = mech.cycle + 60; // cool down + } else { + mech.energy -= mech.fieldRegen + 0.009 + } } } + } }, { @@ -1958,7 +1957,57 @@ const b = { } 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++) { const dir = mech.angle + (Math.random() - 0.5) * spread * 0.2 const pos = { @@ -2349,48 +2398,48 @@ const b = { } } }, - { - name: "flak", - description: "fire a cluster of short range projectiles
explodes on contact or after half a second", - ammo: 0, - ammoPack: 4, - defaultAmmoPack: 4, //use to revert ammoPack after mod changes drop rate - have: false, - fire() { - mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 10) * b.fireCD); // cool down - b.muzzleFlash(30); - const SPEED = mech.crouch ? 29 : 25 - const END = Math.floor(mech.crouch ? 30 : 18); - const side1 = 17 - const side2 = 4 - const totalBullets = 6 - const angleStep = (mech.crouch ? 0.06 : 0.25) / 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), side1, side2, 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 + game.cycle + END - bullet[me].restitution = 0; - bullet[me].friction = 1; - bullet[me].explodeRad = (mech.crouch ? 95 : 75) + (Math.random() - 0.5) * 50; - bullet[me].onEnd = function() { - b.explosion(this.position, this.explodeRad); //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; - } - } - } - }, + // { + // name: "flak", + // description: "fire a cluster of short range projectiles
explodes on contact or after half a second", + // ammo: 0, + // ammoPack: 4, + // defaultAmmoPack: 4, //use to revert ammoPack after mod changes drop rate + // have: false, + // fire() { + // mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 10) * b.fireCD); // cool down + // b.muzzleFlash(30); + // const SPEED = mech.crouch ? 29 : 25 + // const END = Math.floor(mech.crouch ? 30 : 18); + // const side1 = 17 + // const side2 = 4 + // const totalBullets = 6 + // const angleStep = (mech.crouch ? 0.06 : 0.25) / 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), side1, side2, 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 + game.cycle + END + // bullet[me].restitution = 0; + // bullet[me].friction = 1; + // bullet[me].explodeRad = (mech.crouch ? 95 : 75) + (Math.random() - 0.5) * 50; + // bullet[me].onEnd = function() { + // b.explosion(this.position, this.explodeRad); //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; + // } + // } + // } + // }, { name: "grenades", description: "lob a single bouncy projectile
explodes on contact or after one second", diff --git a/js/engine.js b/js/engine.js index 9ebe0a3..5856b25 100644 --- a/js/engine.js +++ b/js/engine.js @@ -24,8 +24,29 @@ function playerOnGroundCheck(event) { //runs on collisions events function enter() { 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; for (let i = 0, j = pairs.length; i != j; ++i) { let pair = pairs[i]; @@ -42,76 +63,26 @@ function playerOnGroundCheck(event) { function playerOffGroundCheck(event) { //runs on collisions events - function enter() { - if (mech.onGround && mech.numTouching === 0) mech.enterAir(); - } const pairs = event.pairs; for (let i = 0, j = pairs.length; i != j; ++i) { - if (pairs[i].bodyA === jumpSensor) { - enter(); - } else if (pairs[i].bodyB === jumpSensor) { - enter(); + if (pairs[i].bodyA === jumpSensor || pairs[i].bodyB === jumpSensor) { + if (mech.onGround && mech.numTouching === 0) { + mech.onGround = false; + 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) { const pairs = event.pairs; 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 for (let k = 0; k < mob.length; k++) { if (mob[k].alive && mech.alive) { diff --git a/js/index.js b/js/index.js index cee0c81..e6af7b3 100644 --- a/js/index.js +++ b/js/index.js @@ -987,8 +987,8 @@ document.getElementById("updates").addEventListener("toggle", function() { loadJSON('https://api.github.com/repos/landgreen/n-gon/commits', function(data) { // console.log(data) - for (let i = 0, len = 4; i < len; i++) { - text += data[i].commit.author.date.substr(0, 10) + ": "; //+ "
" + for (let i = 0, len = 3; i < len; i++) { + text += "" + data[i].commit.author.date.substr(0, 10) + " - "; //+ "
" text += data[i].commit.message if (i < len - 1) text += "
" } diff --git a/js/level.js b/js/level.js index 048bc65..69c359d 100644 --- a/js/level.js +++ b/js/level.js @@ -12,20 +12,19 @@ const level = { levels: [], start() { 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.zoomScale = 1000; // game.setZoom(); // mech.setField("wormhole") - // b.giveGuns("mine") - // mod.giveMod("sentry") + // b.giveGuns("drones") + // mod.giveMod("incendiary ammunition") - level.intro(); //starting level + // level.intro(); //starting level + level.testing(); //not in rotation // level.finalBoss() //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.sewers(); // level.satellite(); @@ -141,16 +140,16 @@ const level = { spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump // spawn.boost(1500, 0, 900); - // spawn.starter(1600, -500) + spawn.starter(1600, -500, 200) // spawn.bomberBoss(2900, -500) // spawn.launcherBoss(1200, -500) // spawn.laserTargetingBoss(1600, -400) // spawn.spawner(1600, -500) // spawn.sniper(1700, -120, 50) // spawn.bomberBoss(1400, -500) - spawn.sucker(1800, -120) + // spawn.sucker(1800, -120) // spawn.cellBossCulture(1600, -500) - // spawn.powerUpBoss(1600, -500) + // spawn.spiderBoss(1600, -500) // spawn.sniper(1200, -500) // spawn.shield(mob[mob.length - 1], 1800, -120, 1); diff --git a/js/mods.js b/js/mods.js index 72a9955..be9030a 100644 --- a/js/mods.js +++ b/js/mods.js @@ -197,7 +197,7 @@ const mod = { }, { name: "fluoroantimonic acid", - description: "increase damage by 40%
when your base health is above 100%", + description: "increase damage by 40%
when your health is above 100%", maxCount: 1, count: 0, allowed() { @@ -408,7 +408,7 @@ const mod = { maxCount: 9, count: 0, 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", effect: () => { @@ -424,7 +424,7 @@ const mod = { maxCount: 1, count: 0, 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", effect: () => { @@ -440,7 +440,7 @@ const mod = { maxCount: 1, count: 0, 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", effect: () => { @@ -457,7 +457,7 @@ const mod = { maxCount: 1, count: 0, 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", effect: () => { @@ -469,7 +469,7 @@ const mod = { }, { name: "scrap bots", - description: "20% chance to build a bot after killing a mob
the bot last for about 20 seconds", + description: "20% chance to build a bot after killing a mob
the bot lasts for about 20 seconds", maxCount: 3, count: 0, allowed() { @@ -1574,9 +1574,25 @@ const mod = { //************************************************** gun //************************************************** mods //************************************************** + { + name: "incendiary ammunition", + description: "your bullets explode after a short time
(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", - description: "your bullets last 33% longer", + description: "your bullets last 33% longer
drones, spores, super balls, foam, wave, ice IX, neutron", maxCount: 3, count: 0, allowed() { @@ -1612,9 +1628,9 @@ const mod = { maxCount: 1, count: 0, 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() { mod.isIceCrystals = true; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun @@ -1645,9 +1661,9 @@ const mod = { maxCount: 1, count: 0, allowed() { - return mod.haveGunCheck("nail gun") + return mod.haveGunCheck("nail gun") && !mod.isIncendiary }, - requires: "nail gun", + requires: "nail gun, not incendiary", effect() { mod.isNailCrit = true }, @@ -1731,7 +1747,7 @@ const mod = { maxCount: 1, count: 0, allowed() { - return mod.haveGunCheck("shotgun") + return mod.haveGunCheck("shotgun") && !mod.isIncendiary }, requires: "shotgun", effect() { @@ -1765,7 +1781,7 @@ const mod = { allowed() { return mod.haveGunCheck("super balls") && !mod.oneSuperBall }, - requires: "super balls", + requires: "super balls, but not the mod super ball", effect() { mod.superBallNumber += 2 }, @@ -1781,7 +1797,7 @@ const mod = { allowed() { return mod.haveGunCheck("super balls") && mod.superBallNumber === 4 }, - requires: "super balls", + requires: "super balls, but not super duper", effect() { mod.oneSuperBall = true; }, @@ -1999,26 +2015,6 @@ const mod = { mod.is3Missiles = false; } }, - { - name: "optimized shell packing", - description: "flak ammo drops contain 2x 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", description: "grenades are loaded with 5 nails
on detonation nails are ejected towards mobs", @@ -2143,7 +2139,7 @@ const mod = { maxCount: 1, count: 0, 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", effect() { @@ -2159,7 +2155,7 @@ const mod = { maxCount: 1, count: 0, 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", effect() { @@ -3259,5 +3255,6 @@ const mod = { timeEnergyRegen: null, isRadioactive: null, isRailEnergyGain: null, - isMineSentry: null + isMineSentry: null, + isIncendiary: null } \ No newline at end of file diff --git a/js/player.js b/js/player.js index 7f602b8..760ef77 100644 --- a/js/player.js +++ b/js/player.js @@ -186,39 +186,6 @@ const mech = { 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 groundControl() { //check for crouch or jump @@ -516,7 +483,6 @@ const mech = { ctx.clearRect(0, 0, canvas.width, canvas.height); } }, 3000); - return; } else { //death mech.health = 0; @@ -2410,6 +2376,7 @@ const mech = { mech.hole.isReady = false; mech.fieldRange = 0 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) Matter.Body.setVelocity(player, { x: velocity.x, diff --git a/js/powerup.js b/js/powerup.js index cd757a3..092edf5 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -1,607 +1,609 @@ let powerUp = []; const powerUps = { - totalPowerUps: 0, //used for mods that count power ups at the end of a level - choose(type, index) { - if (type === "gun") { - b.giveGuns(index) - // game.replaceTextLog = true; - // game.makeTextLog(`${game.SVGleftMouse} ${b.guns[index].name}

${b.guns[index].description}`, 500); - // game.replaceTextLog = false; - } else if (type === "field") { - mech.setField(index) - // game.replaceTextLog = true; - // game.makeTextLog(`${game.SVGrightMouse} ${mech.fieldUpgrades[mech.fieldMode].name}

${mech.fieldUpgrades[mech.fieldMode].description}`, 600); - // game.replaceTextLog = false; - } else if (type === "mod") { - mod.giveMod(index) - // game.replaceTextLog = true; - // game.makeTextLog(`
  ${mod.mods[index].name}

${mod.mods[index].description}`, 500); - // game.replaceTextLog = false; - } - powerUps.endDraft(); - }, - showDraft() { - // document.getElementById("choose-grid").style.gridTemplateColumns = "repeat(2, minmax(370px, 1fr))" - document.getElementById("choose-grid").style.display = "grid" - document.getElementById("choose-background").style.display = "inline" - - document.body.style.cursor = "auto"; - if (mod.isExtraChoice) { - document.body.style.overflowY = "scroll"; - document.body.style.overflowX = "hidden"; - } - game.paused = true; - game.isChoosing = true; //stops p from un pausing on key down - }, - endDraft() { - if (mod.manyWorlds && powerUps.reroll.rerolls < 1) { - powerUps.spawn(mech.pos.x, mech.pos.y, "reroll"); - } - document.getElementById("choose-grid").style.display = "none" - document.getElementById("choose-background").style.display = "none" - document.body.style.cursor = "none"; - document.body.style.overflow = "hidden" - game.paused = false; - game.isChoosing = false; //stops p from un pausing on key down - mech.immuneCycle = mech.cycle + 60; //player is immune to collision damage for 30 cycles - requestAnimationFrame(cycle); - }, - reroll: { - rerolls: 0, - name: "reroll", - color: "#f7b", - size() { - return 20; + totalPowerUps: 0, //used for mods that count power ups at the end of a level + choose(type, index) { + if (type === "gun") { + b.giveGuns(index) + // game.replaceTextLog = true; + // game.makeTextLog(`${game.SVGleftMouse} ${b.guns[index].name}

${b.guns[index].description}`, 500); + // game.replaceTextLog = false; + } else if (type === "field") { + mech.setField(index) + // game.replaceTextLog = true; + // game.makeTextLog(`${game.SVGrightMouse} ${mech.fieldUpgrades[mech.fieldMode].name}

${mech.fieldUpgrades[mech.fieldMode].description}`, 600); + // game.replaceTextLog = false; + } else if (type === "mod") { + mod.giveMod(index) + // game.replaceTextLog = true; + // game.makeTextLog(`
  ${mod.mods[index].name}

${mod.mods[index].description}`, 500); + // game.replaceTextLog = false; + } + powerUps.endDraft(); }, - effect() { - powerUps.reroll.changeRerolls(1) - game.makeTextLog(`
  rerolls: ${powerUps.reroll.rerolls}`, 300) - }, - changeRerolls(amount) { - powerUps.reroll.rerolls += amount - if (powerUps.reroll.rerolls < 0) powerUps.reroll.rerolls = 0 + showDraft() { + // document.getElementById("choose-grid").style.gridTemplateColumns = "repeat(2, minmax(370px, 1fr))" + document.getElementById("choose-grid").style.display = "grid" + document.getElementById("choose-background").style.display = "inline" - if (mod.isRerollBots) { - const limit = 4 - for (; powerUps.reroll.rerolls > limit - 1; powerUps.reroll.rerolls -= limit) { - b.randomBot() - if (mod.renormalization) { - for (let i = 0; i < limit; i++) { - if (Math.random() < 0.37) { - mech.fieldCDcycle = mech.cycle + 30; + document.body.style.cursor = "auto"; + if (mod.isExtraChoice) { + document.body.style.overflowY = "scroll"; + document.body.style.overflowX = "hidden"; + } + game.paused = true; + game.isChoosing = true; //stops p from un pausing on key down + }, + endDraft() { + if (mod.manyWorlds && powerUps.reroll.rerolls < 1) { + powerUps.spawn(mech.pos.x, mech.pos.y, "reroll"); + } + document.getElementById("choose-grid").style.display = "none" + document.getElementById("choose-background").style.display = "none" + document.body.style.cursor = "none"; + document.body.style.overflow = "hidden" + game.paused = false; + game.isChoosing = false; //stops p from un pausing on key down + mech.immuneCycle = mech.cycle + 60; //player is immune to collision damage for 30 cycles + requestAnimationFrame(cycle); + }, + reroll: { + rerolls: 0, + name: "reroll", + color: "#f7b", + size() { + return 20; + }, + effect() { + powerUps.reroll.changeRerolls(1) + game.makeTextLog(`
  rerolls: ${powerUps.reroll.rerolls}`, 300) + }, + changeRerolls(amount) { + powerUps.reroll.rerolls += amount + if (powerUps.reroll.rerolls < 0) powerUps.reroll.rerolls = 0 + + if (mod.isRerollBots) { + const limit = 4 + for (; powerUps.reroll.rerolls > limit - 1; powerUps.reroll.rerolls -= limit) { + b.randomBot() + if (mod.renormalization) { + for (let i = 0; i < limit; i++) { + if (Math.random() < 0.37) { + mech.fieldCDcycle = mech.cycle + 30; + powerUps.spawn(mech.pos.x, mech.pos.y, "reroll"); + } + } + } + } + } + if (mod.isDeathAvoid && document.getElementById("mod-anthropic")) { + document.getElementById("mod-anthropic").innerHTML = `-${powerUps.reroll.rerolls}` + } + if (mod.renormalization && Math.random() < 0.37 && amount < 0) { powerUps.spawn(mech.pos.x, mech.pos.y, "reroll"); - } } - } - } - } - if (mod.isDeathAvoid && document.getElementById("mod-anthropic")) { - document.getElementById("mod-anthropic").innerHTML = `-${powerUps.reroll.rerolls}` - } - if (mod.renormalization && Math.random() < 0.37 && amount < 0) { - powerUps.spawn(mech.pos.x, mech.pos.y, "reroll"); - } - if (mod.isRerollHaste) { - if (powerUps.reroll.rerolls === 0) { - mod.rerollHaste = 0.66; - b.setFireCD(); - } else { - mod.rerollHaste = 1; - b.setFireCD(); - } - } - }, - diceText() { - const diceLimit = 5 - const r = powerUps.reroll.rerolls - const fullDice = Math.min(Math.floor(r / 6), diceLimit) - const lastDice = r % 6 - let out = '' - if (Math.floor(r / 6) > diceLimit) out += "+" - for (let i = 0; i < fullDice; i++) { - out += '⚅' - } - if (lastDice === 1) { - out += '⚀' - } else if (lastDice === 2) { - out += '⚁' - } else if (lastDice === 3) { - out += '⚂' - } else if (lastDice === 4) { - out += '⚃' - } else if (lastDice === 5) { - out += '⚄' - } - return out - }, - 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(); - }, - }, - heal: { - name: "heal", - color: "#0eb", - size() { - return 40 * (game.healScale ** 0.25) * Math.sqrt(mod.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5); //(game.healScale ** 0.25) gives a smaller radius as heal scale goes down - }, - effect() { - if (!mod.isEnergyHealth && mech.alive) { - const heal = mod.largerHeals * (this.size / 40 / Math.sqrt(mod.largerHeals) / (game.healScale ** 0.25)) ** 2 //heal scale is undone here because heal scale is properly affected on mech.addHealth() - if (heal > 0) { - game.makeTextLog("
  heal " + (Math.min(mech.maxHealth - mech.health, heal) * game.healScale * 100).toFixed(0) + "%", 300) - mech.addHealth(heal); - } - } - if (mod.healGiveMaxEnergy) { - mod.healMaxEnergyBonus += 0.04 - mech.setMaxEnergy(); - } - }, - spawn() { //used to spawn a heal with a specific size / heal amount, not normally used - - } - }, - - ammo: { - name: "ammo", - color: "#467", - size() { - return 17; - }, - effect() { - //give ammo to all guns in inventory - if (mod.isAmmoForGun) { - const target = b.guns[b.activeGun] - target.ammo += Math.ceil(Math.random() * target.ammoPack) + Math.ceil(Math.random() * target.ammoPack) - } else { - for (let i = 0, len = b.inventory.length; i < len; i++) { - const target = b.guns[b.inventory[i]] - if (target.ammo !== Infinity) { - target.ammo += Math.ceil(Math.random() * target.ammoPack) - } - } - } - game.updateGunHUD(); - - - // //only get ammo for guns player has - // let target; - // if (b.inventory.length > 0) { - // if (mod.isAmmoForGun) { - // target = b.guns[b.activeGun]; - // } else { - // //find a gun in your inventory - // target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]]; - // //try 3 more times to give ammo to a gun with ammo, not Infinity - // if (target.ammo === Infinity) { - // target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]] - // if (target.ammo === Infinity) { - // target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]] - // if (target.ammo === Infinity) target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]] - // } - // } - // } - // //give ammo - // if (target.ammo === Infinity) { - // if (mech.energy < mech.maxEnergy) mech.energy = mech.maxEnergy; - // if (!game.lastLogTime) game.makeTextLog("+energy", 300); - // } else { - // let ammo = Math.ceil((target.ammoPack * (0.8 + 0.25 * Math.random()))); - // target.ammo += ammo; - // game.updateGunHUD(); - // game.makeTextLog("
  +" + ammo + " ammo for " + target.name + "", 300); - // } - // } else { - // // target = b.guns[Math.floor(Math.random() * b.guns.length)]; //if you don't have any guns just add ammo to a random gun you don't have yet - // if (mech.energy < mech.maxEnergy) mech.energy = mech.maxEnergy; - // if (!game.lastLogTime) game.makeTextLog("+energy", 300); - // } - } - }, - field: { - name: "field", - color: "#0cf", - size() { - return 45; - }, - choiceLog: [], //records all previous choice options - effect() { - function pick(who, skip1 = -1, skip2 = -1, skip3 = -1, skip4 = -1) { - let options = []; - for (let i = 1; i < who.length; i++) { - if (i !== mech.fieldMode && i !== skip1 && i !== skip2 && i !== skip3 && i !== skip4) options.push(i); - } - //remove repeats from last selection - const totalChoices = mod.isDeterminism ? 1 : 3 + mod.isExtraChoice * 2 - if (powerUps.field.choiceLog.length > totalChoices || powerUps.field.choiceLog.length === totalChoices) { //make sure this isn't the first time getting a power up and there are previous choices to remove - for (let i = 0; i < totalChoices; i++) { //repeat for each choice from the last selection - if (options.length > totalChoices) { - for (let j = 0, len = options.length; j < len; j++) { - if (powerUps.field.choiceLog[powerUps.field.choiceLog.length - 1 - i] === options[j]) { - options.splice(j, 1) //remove previous choice from option pool - break + if (mod.isRerollHaste) { + if (powerUps.reroll.rerolls === 0) { + mod.rerollHaste = 0.66; + b.setFireCD(); + } else { + mod.rerollHaste = 1; + b.setFireCD(); } - } } - } - } - if (options.length > 0) { - return options[Math.floor(Math.random() * options.length)] - } - } - - let choice1 = pick(mech.fieldUpgrades) - let choice2 = -1 - let choice3 = -1 - if (choice1 > -1) { - let text = "" - if (!mod.isDeterminism) text += `
` - text += `

choose a field

` - text += `
  ${mech.fieldUpgrades[choice1].name}
${mech.fieldUpgrades[choice1].description}
` - if (!mod.isDeterminism) { - choice2 = pick(mech.fieldUpgrades, choice1) - if (choice2 > -1) text += `
  ${mech.fieldUpgrades[choice2].name}
${mech.fieldUpgrades[choice2].description}
` - choice3 = pick(mech.fieldUpgrades, choice1, choice2) - if (choice3 > -1) text += `
  ${mech.fieldUpgrades[choice3].name}
${mech.fieldUpgrades[choice3].description}
` - } - if (mod.isExtraChoice) { - let choice4 = pick(mech.fieldUpgrades, choice1, choice2, choice3) - if (choice4 > -1) text += `
  ${mech.fieldUpgrades[choice4].name}
${mech.fieldUpgrades[choice4].description}
` - let choice5 = pick(mech.fieldUpgrades, choice1, choice2, choice3, choice4) - if (choice5 > -1) text += `
  ${mech.fieldUpgrades[choice5].name}
${mech.fieldUpgrades[choice5].description}
` - powerUps.field.choiceLog.push(choice4) - powerUps.field.choiceLog.push(choice5) - } - powerUps.field.choiceLog.push(choice1) - powerUps.field.choiceLog.push(choice2) - powerUps.field.choiceLog.push(choice3) - - if (powerUps.reroll.rerolls) text += `
  reroll ${powerUps.reroll.diceText()}
` - - // text += `
${game.SVGrightMouse} activate the shield with the right mouse
fields shield you from damage
and let you pick up and throw blocks
` - document.getElementById("choose-grid").innerHTML = text - powerUps.showDraft(); - } else { - powerUps.giveRandomAmmo() - } - } - }, - mod: { - name: "mod", - color: "hsl(246,100%,77%)", //"#a8f", - size() { - return 42; + }, + diceText() { + const diceLimit = 5 + const r = powerUps.reroll.rerolls + const fullDice = Math.min(Math.floor(r / 6), diceLimit) + const lastDice = r % 6 + let out = '' + if (Math.floor(r / 6) > diceLimit) out += "+" + for (let i = 0; i < fullDice; i++) { + out += '⚅' + } + if (lastDice === 1) { + out += '⚀' + } else if (lastDice === 2) { + out += '⚁' + } else if (lastDice === 3) { + out += '⚂' + } else if (lastDice === 4) { + out += '⚃' + } else if (lastDice === 5) { + out += '⚄' + } + return out + }, + 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(); + }, }, - choiceLog: [], //records all previous choice options - effect() { - if (mech.alive) { - function pick(skip1 = -1, skip2 = -1, skip3 = -1, skip4 = -1) { - let options = []; - for (let i = 0; i < mod.mods.length; i++) { - if (mod.mods[i].count < mod.mods[i].maxCount && i !== skip1 && i !== skip2 && i !== skip3 && i !== skip4 && mod.mods[i].allowed()) { - options.push(i); - } - } - //remove repeats from last selection - const totalChoices = mod.isDeterminism ? 1 : 3 + mod.isExtraChoice * 2 - if (powerUps.mod.choiceLog.length > totalChoices || powerUps.mod.choiceLog.length === totalChoices) { //make sure this isn't the first time getting a power up and there are previous choices to remove - for (let i = 0; i < totalChoices; i++) { //repeat for each choice from the last selection - if (options.length > totalChoices) { - for (let j = 0, len = options.length; j < len; j++) { - if (powerUps.mod.choiceLog[powerUps.mod.choiceLog.length - 1 - i] === options[j]) { - options.splice(j, 1) //remove previous choice from option pool - break - } + heal: { + name: "heal", + color: "#0eb", + size() { + return 40 * (game.healScale ** 0.25) * Math.sqrt(mod.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5); //(game.healScale ** 0.25) gives a smaller radius as heal scale goes down + }, + effect() { + if (!mod.isEnergyHealth && mech.alive) { + const heal = mod.largerHeals * (this.size / 40 / Math.sqrt(mod.largerHeals) / (game.healScale ** 0.25)) ** 2 //heal scale is undone here because heal scale is properly affected on mech.addHealth() + if (heal > 0) { + game.makeTextLog("
  heal " + (Math.min(mech.maxHealth - mech.health, heal) * game.healScale * 100).toFixed(0) + "%", 300) + mech.addHealth(heal); } - } } - } - - if (options.length > 0) { - const choose = options[Math.floor(Math.random() * options.length)] - text += `
  ${mod.mods[choose].name}
${mod.mods[choose].description}
` - return choose - } + if (mod.healGiveMaxEnergy) { + mod.healMaxEnergyBonus += 0.04 + mech.setMaxEnergy(); + } + }, + spawn() { //used to spawn a heal with a specific size / heal amount, not normally used } - let text = "" - if (!mod.isDeterminism) text += `
` - text += `

choose a mod

` - let choice1 = pick() - let choice2 = -1 - let choice3 = -1 - if (choice1 > -1) { - if (!mod.isDeterminism) { - choice2 = pick(choice1) - // if (choice2 > -1) text += `
  ${mod.mods[choice2].name}
${mod.mods[choice2].description}
` - choice3 = pick(choice1, choice2) - // if (choice3 > -1) text += `
  ${mod.mods[choice3].name}
${mod.mods[choice3].description}
` - } - if (mod.isExtraChoice) { - let choice4 = pick(choice1, choice2, choice3) - // if (choice4 > -1) text += `
  ${mod.mods[choice4].name}
${mod.mods[choice4].description}
` - let choice5 = pick(choice1, choice2, choice3, choice4) - // if (choice5 > -1) text += `
  ${mod.mods[choice5].name}
${mod.mods[choice5].description}
` - powerUps.mod.choiceLog.push(choice4) - powerUps.mod.choiceLog.push(choice5) - } - powerUps.mod.choiceLog.push(choice1) - powerUps.mod.choiceLog.push(choice2) - powerUps.mod.choiceLog.push(choice3) - if (powerUps.reroll.rerolls) text += `
  reroll ${powerUps.reroll.diceText()}
` - - document.getElementById("choose-grid").innerHTML = text - powerUps.showDraft(); - } else { - powerUps.giveRandomAmmo() - } - } - } - }, - gun: { - name: "gun", - color: "#26a", - size() { - return 35; }, - choiceLog: [], //records all previous choice options - effect() { - function pick(who, skip1 = -1, skip2 = -1, skip3 = -1, skip4 = -1) { - let options = []; - for (let i = 0; i < who.length; i++) { - if (!who[i].have && i !== skip1 && i !== skip2 && i !== skip3 && i !== skip4) { - options.push(i); - } - } - //remove repeats from last selection - const totalChoices = mod.isDeterminism ? 1 : 3 + mod.isExtraChoice * 2 - if (powerUps.gun.choiceLog.length > totalChoices || powerUps.gun.choiceLog.length === totalChoices) { //make sure this isn't the first time getting a power up and there are previous choices to remove - for (let i = 0; i < totalChoices; i++) { //repeat for each choice from the last selection - if (options.length > totalChoices) { - for (let j = 0, len = options.length; j < len; j++) { - if (powerUps.gun.choiceLog[powerUps.gun.choiceLog.length - 1 - i] === options[j]) { - options.splice(j, 1) //remove previous choice from option pool - break + ammo: { + name: "ammo", + color: "#467", + size() { + return 17; + }, + effect() { + //give ammo to all guns in inventory + if (mod.isAmmoForGun) { + const target = b.guns[b.activeGun] + target.ammo += Math.ceil(Math.random() * target.ammoPack) + Math.ceil(Math.random() * target.ammoPack) + } else { + for (let i = 0, len = b.inventory.length; i < len; i++) { + const target = b.guns[b.inventory[i]] + if (target.ammo !== Infinity) { + target.ammo += Math.ceil(Math.random() * target.ammoPack) + } } - } } - } - } - if (options.length > 0) { - return options[Math.floor(Math.random() * options.length)] - } - } + game.updateGunHUD(); - let choice1 = pick(b.guns) - let choice2 = -1 - let choice3 = -1 - if (choice1 > -1) { - let text = "" - if (!mod.isDeterminism) text += `
` - text += `

choose a gun

` - text += `
  ${b.guns[choice1].name}
${b.guns[choice1].description}
` - if (!mod.isDeterminism) { - choice2 = pick(b.guns, choice1) - if (choice2 > -1) text += `
  ${b.guns[choice2].name}
${b.guns[choice2].description}
` - choice3 = pick(b.guns, choice1, choice2) - if (choice3 > -1) text += `
  ${b.guns[choice3].name}
${b.guns[choice3].description}
` + + // //only get ammo for guns player has + // let target; + // if (b.inventory.length > 0) { + // if (mod.isAmmoForGun) { + // target = b.guns[b.activeGun]; + // } else { + // //find a gun in your inventory + // target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]]; + // //try 3 more times to give ammo to a gun with ammo, not Infinity + // if (target.ammo === Infinity) { + // target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]] + // if (target.ammo === Infinity) { + // target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]] + // if (target.ammo === Infinity) target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]] + // } + // } + // } + // //give ammo + // if (target.ammo === Infinity) { + // if (mech.energy < mech.maxEnergy) mech.energy = mech.maxEnergy; + // if (!game.lastLogTime) game.makeTextLog("+energy", 300); + // } else { + // let ammo = Math.ceil((target.ammoPack * (0.8 + 0.25 * Math.random()))); + // target.ammo += ammo; + // game.updateGunHUD(); + // game.makeTextLog("
  +" + ammo + " ammo for " + target.name + "", 300); + // } + // } else { + // // target = b.guns[Math.floor(Math.random() * b.guns.length)]; //if you don't have any guns just add ammo to a random gun you don't have yet + // if (mech.energy < mech.maxEnergy) mech.energy = mech.maxEnergy; + // if (!game.lastLogTime) game.makeTextLog("+energy", 300); + // } } - if (mod.isExtraChoice) { - let choice4 = pick(b.guns, choice1, choice2, choice3) - if (choice4 > -1) text += `
  ${b.guns[choice4].name}
${b.guns[choice4].description}
` - let choice5 = pick(b.guns, choice1, choice2, choice3, choice4) - if (choice5 > -1) text += `
+ }, + field: { + name: "field", + color: "#0cf", + size() { + return 45; + }, + choiceLog: [], //records all previous choice options + effect() { + function pick(who, skip1 = -1, skip2 = -1, skip3 = -1, skip4 = -1) { + let options = []; + for (let i = 1; i < who.length; i++) { + if (i !== mech.fieldMode && i !== skip1 && i !== skip2 && i !== skip3 && i !== skip4) options.push(i); + } + //remove repeats from last selection + const totalChoices = mod.isDeterminism ? 1 : 3 + mod.isExtraChoice * 2 + if (powerUps.field.choiceLog.length > totalChoices || powerUps.field.choiceLog.length === totalChoices) { //make sure this isn't the first time getting a power up and there are previous choices to remove + for (let i = 0; i < totalChoices; i++) { //repeat for each choice from the last selection + if (options.length > totalChoices) { + for (let j = 0, len = options.length; j < len; j++) { + if (powerUps.field.choiceLog[powerUps.field.choiceLog.length - 1 - i] === options[j]) { + options.splice(j, 1) //remove previous choice from option pool + break + } + } + } + } + } + if (options.length > 0) { + return options[Math.floor(Math.random() * options.length)] + } + } + + let choice1 = pick(mech.fieldUpgrades) + let choice2 = -1 + let choice3 = -1 + if (choice1 > -1) { + let text = "" + if (!mod.isDeterminism) text += `
` + text += `

choose a field

` + text += `
  ${mech.fieldUpgrades[choice1].name}
${mech.fieldUpgrades[choice1].description}
` + if (!mod.isDeterminism) { + choice2 = pick(mech.fieldUpgrades, choice1) + if (choice2 > -1) text += `
  ${mech.fieldUpgrades[choice2].name}
${mech.fieldUpgrades[choice2].description}
` + choice3 = pick(mech.fieldUpgrades, choice1, choice2) + if (choice3 > -1) text += `
  ${mech.fieldUpgrades[choice3].name}
${mech.fieldUpgrades[choice3].description}
` + } + if (mod.isExtraChoice) { + let choice4 = pick(mech.fieldUpgrades, choice1, choice2, choice3) + if (choice4 > -1) text += `
  ${mech.fieldUpgrades[choice4].name}
${mech.fieldUpgrades[choice4].description}
` + let choice5 = pick(mech.fieldUpgrades, choice1, choice2, choice3, choice4) + if (choice5 > -1) text += `
  ${mech.fieldUpgrades[choice5].name}
${mech.fieldUpgrades[choice5].description}
` + powerUps.field.choiceLog.push(choice4) + powerUps.field.choiceLog.push(choice5) + } + powerUps.field.choiceLog.push(choice1) + powerUps.field.choiceLog.push(choice2) + powerUps.field.choiceLog.push(choice3) + + if (powerUps.reroll.rerolls) text += `
  reroll ${powerUps.reroll.diceText()}
` + + // text += `
${game.SVGrightMouse} activate the shield with the right mouse
fields shield you from damage
and let you pick up and throw blocks
` + document.getElementById("choose-grid").innerHTML = text + powerUps.showDraft(); + } else { + powerUps.giveRandomAmmo() + } + } + }, + mod: { + name: "mod", + color: "hsl(246,100%,77%)", //"#a8f", + size() { + return 42; + }, + choiceLog: [], //records all previous choice options + effect() { + if (mech.alive) { + function pick(skip1 = -1, skip2 = -1, skip3 = -1, skip4 = -1) { + let options = []; + for (let i = 0; i < mod.mods.length; i++) { + if (mod.mods[i].count < mod.mods[i].maxCount && i !== skip1 && i !== skip2 && i !== skip3 && i !== skip4 && mod.mods[i].allowed()) { + options.push(i); + } + } + //remove repeats from last selection + const totalChoices = mod.isDeterminism ? 1 : 3 + mod.isExtraChoice * 2 + if (powerUps.mod.choiceLog.length > totalChoices || powerUps.mod.choiceLog.length === totalChoices) { //make sure this isn't the first time getting a power up and there are previous choices to remove + for (let i = 0; i < totalChoices; i++) { //repeat for each choice from the last selection + if (options.length > totalChoices) { + for (let j = 0, len = options.length; j < len; j++) { + if (powerUps.mod.choiceLog[powerUps.mod.choiceLog.length - 1 - i] === options[j]) { + options.splice(j, 1) //remove previous choice from option pool + break + } + } + } + } + } + + if (options.length > 0) { + const choose = options[Math.floor(Math.random() * options.length)] + text += `
  ${mod.mods[choose].name}
${mod.mods[choose].description}
` + return choose + } + + } + let text = "" + if (!mod.isDeterminism) text += `
` + text += `

choose a mod

` + let choice1 = pick() + let choice2 = -1 + let choice3 = -1 + if (choice1 > -1) { + if (!mod.isDeterminism) { + choice2 = pick(choice1) + // if (choice2 > -1) text += `
  ${mod.mods[choice2].name}
${mod.mods[choice2].description}
` + choice3 = pick(choice1, choice2) + // if (choice3 > -1) text += `
  ${mod.mods[choice3].name}
${mod.mods[choice3].description}
` + } + if (mod.isExtraChoice) { + let choice4 = pick(choice1, choice2, choice3) + // if (choice4 > -1) text += `
  ${mod.mods[choice4].name}
${mod.mods[choice4].description}
` + let choice5 = pick(choice1, choice2, choice3, choice4) + // if (choice5 > -1) text += `
  ${mod.mods[choice5].name}
${mod.mods[choice5].description}
` + powerUps.mod.choiceLog.push(choice4) + powerUps.mod.choiceLog.push(choice5) + } + powerUps.mod.choiceLog.push(choice1) + powerUps.mod.choiceLog.push(choice2) + powerUps.mod.choiceLog.push(choice3) + if (powerUps.reroll.rerolls) text += `
  reroll ${powerUps.reroll.diceText()}
` + + document.getElementById("choose-grid").innerHTML = text + powerUps.showDraft(); + } else { + powerUps.giveRandomAmmo() + } + } + } + }, + gun: { + name: "gun", + color: "#26a", + size() { + return 35; + }, + choiceLog: [], //records all previous choice options + effect() { + function pick(who, skip1 = -1, skip2 = -1, skip3 = -1, skip4 = -1) { + let options = []; + for (let i = 0; i < who.length; i++) { + if (!who[i].have && i !== skip1 && i !== skip2 && i !== skip3 && i !== skip4) { + options.push(i); + } + } + + //remove repeats from last selection + const totalChoices = mod.isDeterminism ? 1 : 3 + mod.isExtraChoice * 2 + if (powerUps.gun.choiceLog.length > totalChoices || powerUps.gun.choiceLog.length === totalChoices) { //make sure this isn't the first time getting a power up and there are previous choices to remove + for (let i = 0; i < totalChoices; i++) { //repeat for each choice from the last selection + if (options.length > totalChoices) { + for (let j = 0, len = options.length; j < len; j++) { + if (powerUps.gun.choiceLog[powerUps.gun.choiceLog.length - 1 - i] === options[j]) { + options.splice(j, 1) //remove previous choice from option pool + break + } + } + } + } + } + if (options.length > 0) { + return options[Math.floor(Math.random() * options.length)] + } + } + + let choice1 = pick(b.guns) + let choice2 = -1 + let choice3 = -1 + if (choice1 > -1) { + let text = "" + if (!mod.isDeterminism) text += `
` + text += `

choose a gun

` + text += `
  ${b.guns[choice1].name}
${b.guns[choice1].description}
` + if (!mod.isDeterminism) { + choice2 = pick(b.guns, choice1) + if (choice2 > -1) text += `
  ${b.guns[choice2].name}
${b.guns[choice2].description}
` + choice3 = pick(b.guns, choice1, choice2) + if (choice3 > -1) text += `
  ${b.guns[choice3].name}
${b.guns[choice3].description}
` + } + if (mod.isExtraChoice) { + let choice4 = pick(b.guns, choice1, choice2, choice3) + if (choice4 > -1) text += `
  ${b.guns[choice4].name}
${b.guns[choice4].description}
` + let choice5 = pick(b.guns, choice1, choice2, choice3, choice4) + if (choice5 > -1) text += `
  ${b.guns[choice5].name}
${b.guns[choice5].description}
` - powerUps.gun.choiceLog.push(choice4) - powerUps.gun.choiceLog.push(choice5) + powerUps.gun.choiceLog.push(choice4) + powerUps.gun.choiceLog.push(choice5) + } + powerUps.gun.choiceLog.push(choice1) + powerUps.gun.choiceLog.push(choice2) + powerUps.gun.choiceLog.push(choice3) + if (powerUps.reroll.rerolls) text += `
  reroll ${powerUps.reroll.diceText()}
` + + // console.log(powerUps.gun.choiceLog) + // console.log(choice1, choice2, choice3) + if (mod.isOneGun) text += `
replaces your current gun
` + document.getElementById("choose-grid").innerHTML = text + powerUps.showDraft(); + } else { + powerUps.giveRandomAmmo() + } } - powerUps.gun.choiceLog.push(choice1) - powerUps.gun.choiceLog.push(choice2) - powerUps.gun.choiceLog.push(choice3) - if (powerUps.reroll.rerolls) text += `
  reroll ${powerUps.reroll.diceText()}
` + }, + onPickUp(where) { + if (mod.isMassEnergy && mech.energy < mech.maxEnergy * 2.5) mech.energy = mech.maxEnergy * 2.5; + if (mod.isMineDrop) b.mine({ + x: where.x, + y: where.y + }, { + x: 0, + y: 0 + }, 0, mod.isMineAmmoBack) + }, + giveRandomAmmo() { + const ammoTarget = Math.floor(Math.random() * (b.guns.length)); + const ammo = Math.ceil(b.guns[ammoTarget].ammoPack * 6); + if (ammo === Infinity) { + b.guns[ammoTarget].ammo += ammo; + game.makeTextLog("+energy", 300); + } else { + b.guns[ammoTarget].ammo += ammo; + game.updateGunHUD(); + game.makeTextLog("+" + ammo + " ammo for " + b.guns[ammoTarget].name + "", 300); + } + }, + spawnRandomPowerUp(x, y) { //mostly used after mob dies, doesn't always return a power up + if ((Math.random() * Math.random() - 0.3 > Math.sqrt(mech.health) && !mod.isEnergyHealth) || Math.random() < 0.04) { //spawn heal chance is higher at low health + powerUps.spawn(x, y, "heal"); + return; + } + if (Math.random() < 0.15 && b.inventory.length > 0) { + powerUps.spawn(x, y, "ammo"); + return; + } + if (Math.random() < 0.0015 * (3 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun up to 3 + powerUps.spawn(x, y, "gun"); + return; + } + if (Math.random() < 0.0027 * (25 - mod.totalCount)) { //a new mod has a low chance for each not acquired mod up to 15 + powerUps.spawn(x, y, "mod"); + return; + } + if (Math.random() < 0.006) { + powerUps.spawn(x, y, "field"); + return; + } + // if (Math.random() < 0.01) { + // powerUps.spawn(x, y, "reroll"); + // return; + // } + }, + randomPowerUpCounter: 0, + spawnBossPowerUp(x, y) { //boss spawns field and gun mod upgrades + level.bossKilled = true; - // console.log(powerUps.gun.choiceLog) - // console.log(choice1, choice2, choice3) - - document.getElementById("choose-grid").innerHTML = text - powerUps.showDraft(); - } else { - powerUps.giveRandomAmmo() - } - } - }, - onPickUp(where) { - if (mod.isMassEnergy && mech.energy < mech.maxEnergy * 2.5) mech.energy = mech.maxEnergy * 2.5; - if (mod.isMineDrop) b.mine({ - x: where.x, - y: where.y - }, { - x: 0, - y: 0 - }, 0, mod.isMineAmmoBack) - }, - giveRandomAmmo() { - const ammoTarget = Math.floor(Math.random() * (b.guns.length)); - const ammo = Math.ceil(b.guns[ammoTarget].ammoPack * 6); - if (ammo === Infinity) { - b.guns[ammoTarget].ammo += ammo; - game.makeTextLog("+energy", 300); - } else { - b.guns[ammoTarget].ammo += ammo; - game.updateGunHUD(); - game.makeTextLog("+" + ammo + " ammo for " + b.guns[ammoTarget].name + "", 300); - } - }, - spawnRandomPowerUp(x, y) { //mostly used after mob dies, doesn't always return a power up - if ((Math.random() * Math.random() - 0.3 > Math.sqrt(mech.health) && !mod.isEnergyHealth) || Math.random() < 0.04) { //spawn heal chance is higher at low health - powerUps.spawn(x, y, "heal"); - return; - } - if (Math.random() < 0.15 && b.inventory.length > 0) { - powerUps.spawn(x, y, "ammo"); - return; - } - if (Math.random() < 0.0015 * (3 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun up to 3 - powerUps.spawn(x, y, "gun"); - return; - } - if (Math.random() < 0.0027 * (25 - mod.totalCount)) { //a new mod has a low chance for each not acquired mod up to 15 - powerUps.spawn(x, y, "mod"); - return; - } - if (Math.random() < 0.006) { - powerUps.spawn(x, y, "field"); - return; - } - // if (Math.random() < 0.01) { - // powerUps.spawn(x, y, "reroll"); - // return; - // } - }, - randomPowerUpCounter: 0, - spawnBossPowerUp(x, y) { //boss spawns field and gun mod upgrades - level.bossKilled = true; - - powerUps.randomPowerUpCounter++; - powerUpChance(Math.max(level.levelsCleared, 10) * 0.1) - powerUps.randomPowerUpCounter += 0.6; - powerUpChance(Math.max(level.levelsCleared, 6) * 0.1) - - function powerUpChance(chanceToFail) { - if (Math.random() * chanceToFail < powerUps.randomPowerUpCounter) { - powerUps.randomPowerUpCounter = 0; if (mech.fieldMode === 0) { - powerUps.spawn(x, y, "field") - } else if (Math.random() < 0.95) { - powerUps.spawn(x, y, "mod") + powerUps.spawn(x, y, "field") } else { - powerUps.spawn(x, y, "gun") + powerUps.randomPowerUpCounter++; + powerUpChance(Math.max(level.levelsCleared, 10) * 0.1) } - } else { - if (mech.health < 0.65 && !mod.isEnergyHealth) { - powerUps.spawn(x, y, "heal"); - powerUps.spawn(x, y, "heal"); - } else { - powerUps.spawn(x, y, "ammo"); - powerUps.spawn(x, y, "ammo"); - } - } - } - }, - chooseRandomPowerUp(x, y) { //100% chance to drop a random power up //used in spawn.debris - if (Math.random() < 0.5) { - powerUps.spawn(x, y, "heal", false); - } else { - powerUps.spawn(x, y, "ammo", false); - } - }, - addRerollToLevel() { //add a random power up to a location that has a mob, mostly used to give each level one randomly placed reroll - if (mob.length && Math.random() < 0.8) { // 80% chance - const index = Math.floor(Math.random() * mob.length) - powerUps.spawn(mob[index].position.x, mob[index].position.y, "reroll"); - } - }, - spawnStartingPowerUps(x, y) { //used for map specific power ups, mostly to give player a starting gun - if (level.levelsCleared < 4) { //runs 4 times on all difficulty levels - if (level.levelsCleared > 1) powerUps.spawn(x, y, "mod") + powerUps.randomPowerUpCounter += 0.6; + powerUpChance(Math.max(level.levelsCleared, 6) * 0.1) - //bonus power ups for clearing runs in the last game - if (level.levelsCleared === 0 && !game.isCheating) { - for (let i = 0; i < localSettings.levelsClearedLastGame / 4 - 1; i++) { - powerUps.spawn(mech.pos.x, mech.pos.y, "mod", false); //spawn a mod for levels cleared in last game + function powerUpChance(chanceToFail) { + if (Math.random() * chanceToFail < powerUps.randomPowerUpCounter) { + powerUps.randomPowerUpCounter = 0; + if (Math.random() < 0.95) { + powerUps.spawn(x, y, "mod") + } else { + powerUps.spawn(x, y, "gun") + } + } else { + if (mech.health < 0.65 && !mod.isEnergyHealth) { + powerUps.spawn(x, y, "heal"); + powerUps.spawn(x, y, "heal"); + } else { + powerUps.spawn(x, y, "ammo"); + powerUps.spawn(x, y, "ammo"); + } + } } - localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage - } - if (b.inventory.length === 0) { - powerUps.spawn(x, y, "gun", false); //first gun - } else if (mod.totalCount === 0) { //first mod - powerUps.spawn(x, y, "mod", false); - } else if (b.inventory.length < 2) { //second gun or extra ammo + }, + chooseRandomPowerUp(x, y) { //100% chance to drop a random power up //used in spawn.debris if (Math.random() < 0.5) { - powerUps.spawn(x, y, "gun", false); + powerUps.spawn(x, y, "heal", false); } else { - powerUps.spawn(x, y, "ammo", false); - powerUps.spawn(x, y, "ammo", false); - powerUps.spawn(x, y, "ammo", false); - powerUps.spawn(x, y, "ammo", false); + powerUps.spawn(x, y, "ammo", false); } - } else { - powerUps.spawnRandomPowerUp(x, y); - powerUps.spawnRandomPowerUp(x, y); - powerUps.spawnRandomPowerUp(x, y); - powerUps.spawnRandomPowerUp(x, y); - } - } else { - powerUps.spawnRandomPowerUp(x, y); - powerUps.spawnRandomPowerUp(x, y); - powerUps.spawnRandomPowerUp(x, y); - } - }, - ejectMod() { - //find which mods you have - const have = [] - for (let i = 0; i < mod.mods.length; i++) { - if (mod.mods[i].count > 0) have.push(i) - } - if (have.length) { - const choose = have[Math.floor(Math.random() * have.length)] - game.makeTextLog(`
  ${mod.mods[choose].name} was ejected`, 600) //message about what mod was lost - for (let i = 0; i < mod.mods[choose].count; i++) { - powerUps.directSpawn(mech.pos.x, mech.pos.y, "mod"); - powerUp[powerUp.length - 1].isBonus = true - } - // remove a random mod from the list of mods you have - mod.mods[choose].remove(); - mod.mods[choose].count = 0; - mod.mods[choose].isLost = true; - game.updateModHUD(); - mech.fieldCDcycle = mech.cycle + 30; //disable field so you can't pick up the ejected mod - } - }, - directSpawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { - let index = powerUp.length; - target = powerUps[target]; - powerUp[index] = Matter.Bodies.polygon(x, y, 0, size, { - density: 0.001, - frictionAir: 0.03, - restitution: 0.85, - inertia: Infinity, //prevents rotation - collisionFilter: { - group: 0, - category: cat.powerUp, - mask: cat.map | cat.powerUp - }, - color: target.color, - effect: target.effect, - name: target.name, - size: size - }); - if (mode) { - powerUp[index].mode = mode - } - if (moving) { - Matter.Body.setVelocity(powerUp[index], { - x: (Math.random() - 0.5) * 15, - y: Math.random() * -9 - 3 - }); - } - World.add(engine.world, powerUp[index]); //add to world - }, - spawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { - if ( - (!mod.isSuperDeterminism || (target === 'mod' || target === 'heal' || target === 'ammo')) && - !(mod.isEnergyNoAmmo && target === 'ammo') && - (!game.isNoPowerUps || (target === 'reroll' || target === 'heal' || target === 'ammo')) - ) { - powerUps.directSpawn(x, y, target, moving, mode, size) - if (mod.duplicateChance && Math.random() < mod.duplicateChance) { - powerUps.directSpawn(x, y, target, moving, mode) - powerUp[powerUp.length - 1].isBonus = true - } - } - }, + }, + addRerollToLevel() { //add a random power up to a location that has a mob, mostly used to give each level one randomly placed reroll + if (mob.length && Math.random() < 0.8) { // 80% chance + const index = Math.floor(Math.random() * mob.length) + powerUps.spawn(mob[index].position.x, mob[index].position.y, "reroll"); + } + }, + spawnStartingPowerUps(x, y) { //used for map specific power ups, mostly to give player a starting gun + if (level.levelsCleared < 4) { //runs 4 times on all difficulty levels + if (level.levelsCleared > 1) powerUps.spawn(x, y, "mod") + + //bonus power ups for clearing runs in the last game + if (level.levelsCleared === 0 && !game.isCheating) { + for (let i = 0; i < localSettings.levelsClearedLastGame / 4 - 1; i++) { + powerUps.spawn(mech.pos.x, mech.pos.y, "mod", false); //spawn a mod for levels cleared in last game + } + localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + if (b.inventory.length === 0) { + powerUps.spawn(x, y, "gun", false); //first gun + } else if (mod.totalCount === 0) { //first mod + powerUps.spawn(x, y, "mod", false); + } else if (b.inventory.length < 2) { //second gun or extra ammo + if (Math.random() < 0.5) { + powerUps.spawn(x, y, "gun", false); + } else { + powerUps.spawn(x, y, "ammo", false); + powerUps.spawn(x, y, "ammo", false); + powerUps.spawn(x, y, "ammo", false); + powerUps.spawn(x, y, "ammo", false); + } + } else { + powerUps.spawnRandomPowerUp(x, y); + powerUps.spawnRandomPowerUp(x, y); + powerUps.spawnRandomPowerUp(x, y); + powerUps.spawnRandomPowerUp(x, y); + } + } else { + powerUps.spawnRandomPowerUp(x, y); + powerUps.spawnRandomPowerUp(x, y); + powerUps.spawnRandomPowerUp(x, y); + } + }, + ejectMod() { + //find which mods you have + const have = [] + for (let i = 0; i < mod.mods.length; i++) { + if (mod.mods[i].count > 0) have.push(i) + } + if (have.length) { + const choose = have[Math.floor(Math.random() * have.length)] + game.makeTextLog(`
  ${mod.mods[choose].name} was ejected`, 600) //message about what mod was lost + for (let i = 0; i < mod.mods[choose].count; i++) { + powerUps.directSpawn(mech.pos.x, mech.pos.y, "mod"); + powerUp[powerUp.length - 1].isBonus = true + } + // remove a random mod from the list of mods you have + mod.mods[choose].remove(); + mod.mods[choose].count = 0; + mod.mods[choose].isLost = true; + game.updateModHUD(); + mech.fieldCDcycle = mech.cycle + 30; //disable field so you can't pick up the ejected mod + } + }, + directSpawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { + let index = powerUp.length; + target = powerUps[target]; + powerUp[index] = Matter.Bodies.polygon(x, y, 0, size, { + density: 0.001, + frictionAir: 0.03, + restitution: 0.85, + inertia: Infinity, //prevents rotation + collisionFilter: { + group: 0, + category: cat.powerUp, + mask: cat.map | cat.powerUp + }, + color: target.color, + effect: target.effect, + name: target.name, + size: size + }); + if (mode) { + powerUp[index].mode = mode + } + if (moving) { + Matter.Body.setVelocity(powerUp[index], { + x: (Math.random() - 0.5) * 15, + y: Math.random() * -9 - 3 + }); + } + World.add(engine.world, powerUp[index]); //add to world + }, + spawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { + if ( + (!mod.isSuperDeterminism || (target === 'mod' || target === 'heal' || target === 'ammo')) && + !(mod.isEnergyNoAmmo && target === 'ammo') && + (!game.isNoPowerUps || (target === 'reroll' || target === 'heal' || target === 'ammo')) + ) { + powerUps.directSpawn(x, y, target, moving, mode, size) + if (mod.duplicateChance && Math.random() < mod.duplicateChance) { + powerUps.directSpawn(x, y, target, moving, mode) + powerUp[powerUp.length - 1].isBonus = true + } + } + }, }; \ No newline at end of file diff --git a/js/spawn.js b/js/spawn.js index b8cd6ea..a824987 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -159,7 +159,7 @@ const spawn = { } this.mode = 3 this.fill = "#000"; - this.eventHorizon = 700 + this.eventHorizon = 750 this.spawnInterval = 600 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 ? @@ -200,7 +200,7 @@ const spawn = { me.eventHorizonCycleRate = 4 * Math.PI / me.endCycle me.modeSuck = function() { //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 ctx.beginPath(); ctx.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI); @@ -763,7 +763,7 @@ const spawn = { this.checkStatus(); if (this.seePlayer.recall) { //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 const forceMag = this.accelMag * this.mass; @@ -860,7 +860,7 @@ const spawn = { this.force.y += forceMag * dy / mag; //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 //draw darkness @@ -910,7 +910,6 @@ const spawn = { } }, 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 mobs.spawn(x, y, 6, radius, "#b386e8"); let me = mob[mob.length - 1]; @@ -919,10 +918,10 @@ const spawn = { me.friction = 0; me.frictionAir = 0.0065; 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); - const springStiffness = isDaddyLongLegs ? 0.0001 : 0.000065; - const springDampening = isDaddyLongLegs ? 0 : 0.0006; + const springStiffness = 0.00014; + const springDampening = 0.0005; me.springTarget = { x: me.position.x, @@ -935,8 +934,8 @@ const spawn = { stiffness: springStiffness, damping: springDampening }); + World.add(engine.world, cons[cons.length - 1]); cons[len].length = 100 + 1.5 * radius; - me.cons = cons[len]; me.springTarget2 = { @@ -948,11 +947,12 @@ const spawn = { pointA: me.springTarget2, bodyB: me, stiffness: springStiffness, - damping: springDampening + damping: springDampening, + length: 0 }); + World.add(engine.world, cons[cons.length - 1]); cons[len2].length = 100 + 1.5 * radius; 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() { this.removeCons(); @@ -974,15 +974,12 @@ const spawn = { for (let i = 0; i < nodes; ++i) { 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 } - //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 - if (!isDaddyLongLegs) spawn.constrain2AdjacentMobs(nodes + 2, attachmentStiffness, true); //loop mobs together + const attachmentStiffness = 0.05 + spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together + for (let i = 0; i < nodes; ++i) { //attach to center mob consBB[consBB.length] = Constraint.create({ bodyA: me, @@ -992,6 +989,9 @@ const spawn = { }); 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) { mobs.spawn(x, y, 6, radius, '#000'); diff --git a/todo.txt b/todo.txt index 48bba23..27b237b 100644 --- a/todo.txt +++ b/todo.txt @@ -1,32 +1,56 @@ *********** 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 - fixed, but only for one portal... oh well +you get a warning before you overwrite your current gun due to integrated armament -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 ************** -bug - crouch and worm hole? -> crouch locked in -bug - getting locked into crouch on community levels, but showing the player as still standing +(4+ reports before potential fix) bug - crouch and worm hole? -> crouch locked in + 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 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 apply stun status 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 excess energy is converted to bullets 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 cloaking field - maybe just don't spawn bullets when cloaked -mod - sentry fires other bullet types - laser or missile - -flak could be a mod for shotgun? +Mod: "Solar Power": Energy regeneration is doubled while standing still mod - self destruct - drones explode when they die 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 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 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 repeat map in vertical and horizontal space