diff --git a/js/bullet.js b/js/bullet.js index ebe2c71..33df1a5 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -274,6 +274,307 @@ const b = { } } }, + grenade() { + + }, + setGrenadeMode() { + grenadeDefault = function(where = { x: mech.pos.x + 30 * Math.cos(mech.angle), y: mech.pos.y + 30 * Math.sin(mech.angle) }, angle = mech.angle) { + const me = bullet.length; + bullet[me] = Bodies.circle(where.x, where.y, 15, b.fireAttributes(angle, false)); + Matter.Body.setDensity(bullet[me], 0.0005); + bullet[me].explodeRad = 275; + bullet[me].onEnd = function() { + b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end + if (mod.grenadeFragments) b.targetedNail(this.position, mod.grenadeFragments) + } + bullet[me].minDmgSpeed = 1; + bullet[me].beforeDmg = function() { + this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion + }; + speed = mech.crouch ? 43 : 32 + Matter.Body.setVelocity(bullet[me], { + x: mech.Vx / 2 + speed * Math.cos(angle), + y: mech.Vy / 2 + speed * Math.sin(angle) + }); + bullet[me].endCycle = game.cycle + Math.floor(mech.crouch ? 120 : 80); + bullet[me].restitution = 0.4; + bullet[me].do = function() { + this.force.y += this.mass * 0.0025; //extra gravity for harder arcs + }; + World.add(engine.world, bullet[me]); //add bullet to world + } + grenadeRPG = function(where = { x: mech.pos.x + 30 * Math.cos(mech.angle), y: mech.pos.y + 30 * Math.sin(mech.angle) }, angle = mech.angle) { + const me = bullet.length; + bullet[me] = Bodies.circle(where.x, where.y, 15, b.fireAttributes(angle, false)); + Matter.Body.setDensity(bullet[me], 0.0005); + bullet[me].explodeRad = 275; + bullet[me].onEnd = function() { + b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end + if (mod.grenadeFragments) b.targetedNail(this.position, mod.grenadeFragments) + } + bullet[me].minDmgSpeed = 1; + bullet[me].beforeDmg = function() { + this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion + }; + speed = mech.crouch ? 43 : 32 + Matter.Body.setVelocity(bullet[me], { + x: mech.Vx / 2 + speed * Math.cos(angle), + y: mech.Vy / 2 + speed * Math.sin(angle) + }); + World.add(engine.world, bullet[me]); //add bullet to world + + bullet[me].endCycle = game.cycle + 70; + bullet[me].frictionAir = 0.07; + const MAG = 0.015 + bullet[me].thrust = { + x: bullet[me].mass * MAG * Math.cos(angle), + y: bullet[me].mass * MAG * Math.sin(angle) + } + bullet[me].do = function() { + this.force.x += this.thrust.x; + this.force.y += this.thrust.y; + if (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { + this.endCycle = 0; //explode if touching map or blocks + } + }; + } + grenadeVacuum = function(where = { x: mech.pos.x + 30 * Math.cos(mech.angle), y: mech.pos.y + 30 * Math.sin(mech.angle) }, angle = mech.angle) { + const me = bullet.length; + bullet[me] = Bodies.circle(where.x, where.y, 20, b.fireAttributes(angle, false)); + Matter.Body.setDensity(bullet[me], 0.0003); + bullet[me].explodeRad = 325 + Math.floor(Math.random() * 50);; + bullet[me].onEnd = function() { + b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end + if (mod.grenadeFragments) b.targetedNail(this.position, mod.grenadeFragments) + } + bullet[me].beforeDmg = function() {}; + bullet[me].restitution = 0.4; + bullet[me].do = function() { + this.force.y += this.mass * 0.0025; //extra gravity for harder arcs + + const suckCycles = 40 + if (game.cycle > this.endCycle - suckCycles) { //suck + const that = this + + function suck(who, radius = that.explodeRad * 3.2) { + for (i = 0, len = who.length; i < len; i++) { + const sub = Vector.sub(that.position, who[i].position); + const dist = Vector.magnitude(sub); + if (dist < radius && dist > 150) { + knock = Vector.mult(Vector.normalise(sub), mag * who[i].mass / Math.sqrt(dist)); + who[i].force.x += knock.x; + who[i].force.y += knock.y; + } + } + } + let mag = 0.1 + if (game.cycle > this.endCycle - 5) { + mag = -0.22 + suck(mob, this.explodeRad * 3) + suck(body, this.explodeRad * 2) + suck(powerUp, this.explodeRad * 1.5) + suck(bullet, this.explodeRad * 1.5) + suck([player], this.explodeRad * 1.3) + } else { + mag = 0.11 + suck(mob, this.explodeRad * 3) + suck(body, this.explodeRad * 2) + suck(powerUp, this.explodeRad * 1.5) + suck(bullet, this.explodeRad * 1.5) + suck([player], this.explodeRad * 1.3) + } + //keep bomb in place + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + //draw suck + const radius = 2.75 * this.explodeRad * (this.endCycle - game.cycle) / suckCycles + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); + ctx.fill(); + } + }; + speed = 35 + bullet[me].endCycle = game.cycle + 70; + if (mech.crouch) { + speed += 9 + bullet[me].endCycle += 20; + } + Matter.Body.setVelocity(bullet[me], { + x: mech.Vx / 2 + speed * Math.cos(angle), + y: mech.Vy / 2 + speed * Math.sin(angle) + }); + World.add(engine.world, bullet[me]); //add bullet to world + } + + grenadeNeutron = function(where = { x: mech.pos.x + 30 * Math.cos(mech.angle), y: mech.pos.y + 30 * Math.sin(mech.angle) }, angle = mech.angle) { + const me = bullet.length; + bullet[me] = Bodies.polygon(where.x, where.y, 10, 4, b.fireAttributes(angle, false)); + b.fireProps(mech.crouch ? 45 : 25, mech.crouch ? 35 : 20, angle, me); //cd , speed + Matter.Body.setDensity(bullet[me], 0.000001); + bullet[me].endCycle = Infinity; + bullet[me].frictionAir = 0; + bullet[me].friction = 1; + bullet[me].frictionStatic = 1; + bullet[me].restitution = 0; + bullet[me].minDmgSpeed = 0; + bullet[me].damageRadius = 100; + bullet[me].maxDamageRadius = 450 + 130 * mod.isNeutronSlow + 130 * mod.isNeutronImmune //+ 150 * Math.random() + bullet[me].radiusDecay = (0.81 + 0.15 * mod.isNeutronSlow + 0.15 * mod.isNeutronImmune) / mod.isBulletsLastLonger + bullet[me].stuckTo = null; + bullet[me].stuckToRelativePosition = null; + bullet[me].vacuumSlow = 0.97; + if (mod.isRewindGrenade && input.down) { + Matter.Body.setVelocity(bullet[me], { + x: 0, + y: 0 + }); + bullet[me].maxDamageRadius *= 1.3 + mech.rewind(200, false) + } + bullet[me].beforeDmg = function() {}; + bullet[me].stuck = function() {}; + bullet[me].do = function() { + function onCollide(that) { + that.collisionFilter.mask = 0; //non collide with everything + Matter.Body.setVelocity(that, { + x: 0, + y: 0 + }); + that.do = that.radiationMode; + } + + const mobCollisions = Matter.Query.collides(this, mob) + if (mobCollisions.length) { + onCollide(this) + this.stuckTo = mobCollisions[0].bodyA + mobs.statusDoT(this.stuckTo, 0.5, 360) //apply radiation damage status effect on direct hits + + if (this.stuckTo.isVerticesChange) { + this.stuckToRelativePosition = { + x: 0, + y: 0 + } + } else { + //find the relative position for when the mob is at angle zero by undoing the mobs rotation + this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) + } + this.stuck = function() { + if (this.stuckTo && this.stuckTo.alive) { + const rotate = Vector.rotate(this.stuckToRelativePosition, this.stuckTo.angle) //add in the mob's new angle to the relative position vector + Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.stuckTo.velocity), this.stuckTo.position)) + Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck + } else { + this.collisionFilter.mask = cat.map | cat.body | cat.player | cat.mob; //non collide with everything but map + this.stuck = function() { + this.force.y += this.mass * 0.001; + } + } + } + } else { + const bodyCollisions = Matter.Query.collides(this, body) + if (bodyCollisions.length) { + if (!bodyCollisions[0].bodyA.isNotHoldable) { + onCollide(this) + this.stuckTo = bodyCollisions[0].bodyA + //find the relative position for when the mob is at angle zero by undoing the mobs rotation + this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) + } else { + this.do = this.radiationMode; + } + this.stuck = function() { + if (this.stuckTo) { + const rotate = Vector.rotate(this.stuckToRelativePosition, this.stuckTo.angle) //add in the mob's new angle to the relative position vector + Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.stuckTo.velocity), this.stuckTo.position)) + // Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck + } else { + this.force.y += this.mass * 0.001; + } + } + } else { + if (Matter.Query.collides(this, map).length) { + onCollide(this) + } else { //if colliding with nothing just fall + this.force.y += this.mass * 0.001; + } + } + } + } + bullet[me].radiationMode = function() { //the do code after the bullet is stuck on something, projects a damaging radiation field + this.stuck(); //runs different code based on what the bullet is stuck to + if (!mech.isBodiesAsleep) { + this.damageRadius = this.damageRadius * 0.85 + 0.15 * this.maxDamageRadius //smooth radius towards max + this.maxDamageRadius -= this.radiusDecay + if (this.damageRadius < 15) { + this.endCycle = 0; + } else { + //aoe damage to player + if (!mod.isNeutronImmune && Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) { + const DRAIN = 0.0023 + if (mech.energy > DRAIN) { + mech.energy -= DRAIN + } else { + mech.energy = 0; + mech.damage(0.00015) + } + } + //aoe damage to mobs + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.damageRadius) { + let dmg = b.dmgScale * 0.082 + if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way + if (mob[i].shield) dmg *= 4 //x5 to make up for the /5 that shields normally take + mob[i].damage(dmg); + mob[i].locatePlayer(); + if (mod.isNeutronSlow) { + Matter.Body.setVelocity(mob[i], { + x: mob[i].velocity.x * this.vacuumSlow, + y: mob[i].velocity.y * this.vacuumSlow + }); + } + } + } + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.damageRadius, 0, 2 * Math.PI); + ctx.globalCompositeOperation = "lighter" + ctx.fillStyle = `rgba(25,139,170,${0.2+0.06*Math.random()})`; + ctx.fill(); + ctx.globalCompositeOperation = "source-over" + if (mod.isNeutronSlow) { + const that = this + + function slow(who, radius = that.explodeRad * 3.2) { + for (i = 0, len = who.length; i < len; i++) { + const sub = Vector.sub(that.position, who[i].position); + const dist = Vector.magnitude(sub); + if (dist < radius) { + Matter.Body.setVelocity(who[i], { + x: who[i].velocity.x * that.vacuumSlow, + y: who[i].velocity.y * that.vacuumSlow + }); + } + } + } + slow(body, this.damageRadius) + slow([player], this.damageRadius) + } + } + } + } + } + + if (mod.isNeutronBomb) { + b.grenade = grenadeNeutron + } else if (mod.isVacuumBomb) { + b.grenade = grenadeVacuum + } else if (mod.isRPG) { + b.grenade = grenadeRPG + } else { + b.grenade = grenadeDefault + } + }, missile(where, angle, speed, size = 1, spawn = 0) { const me = bullet.length; bullet[me] = Bodies.rectangle(where.x, where.y, 30 * size, 4 * size, { @@ -1237,16 +1538,16 @@ const b = { } } }, - randomBot(where = mech.pos, isKeep = true) { - if (Math.random() < 0.2) { - b.orbitBot(); - if (isKeep) mod.orbitBotCount++; - } else if (Math.random() < 0.25) { - b.nailBot(where) - if (isKeep) mod.nailBotCount++; - } else if (Math.random() < 0.33) { + randomBot(where = mech.pos, isKeep = true, isAll = true) { + if (Math.random() < 0.2 && isAll) { b.laserBot(where) if (isKeep) mod.laserBotCount++; + } else if (Math.random() < 0.25 && isAll) { + b.orbitBot(); + if (isKeep) mod.orbitBotCount++; + } else if (Math.random() < 0.33) { + b.nailBot(where) + if (isKeep) mod.nailBotCount++; } else if (Math.random() < 0.5) { b.foamBot(where) if (isKeep) mod.foamBotCount++; @@ -2334,32 +2635,6 @@ const b = { const wiggle = Vector.mult(transverse, wiggleMag * Math.cos(this.cycle * 0.35) * ((i % 2) ? -1 : 1)) Matter.Body.setPosition(this, Vector.add(this.position, wiggle)) } - // if (mod.isWaveReflect) { //single reflection - // const sub = Vector.sub(this.position, mech.pos) - // if (Vector.magnitude(sub) > 630) { - // // Matter.Body.setPosition(this, Vector.add(this.position, Vector.mult(Vector.normalise(sub), -2 * POCKET_RANGE))) //teleport to opposite side - // if (!this.isJustReflected) { - // Matter.Body.setVelocity(this, Vector.mult(this.velocity, -1)); //reflect - // this.isJustReflected = true; - // } - // } - // } - - // if (mod.isWaveReflect) { - // Matter.Body.setPosition(this, Vector.add(this.position, player.velocity)) //bullets move with player - - // Matter.Body.setPosition(this, Vector.add(this.position, Vector.mult(Vector.normalise(sub), -2 * POCKET_RANGE))) //teleport to opposite side - - // const sub = Vector.sub(this.position, mech.pos) - // if (Vector.magnitude(sub) > 630) { - // if (!this.isJustReflected) { - // Matter.Body.setVelocity(this, Vector.mult(this.velocity, -1)); //reflect - // this.isJustReflected = true; - // } - // } else { - // this.isJustReflected = false - // } - // } } }); World.add(engine.world, bullet[me]); //add bullet to world @@ -2431,48 +2706,6 @@ 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: "grenades", description: "lob a single bouncy projectile
explodes on contact or after one second", @@ -2480,46 +2713,8 @@ const b = { ammoPack: 5, have: false, fire() { - - }, - fireNormal() { - const me = bullet.length; - const dir = mech.angle; // + Math.random() * 0.05; - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 15, b.fireAttributes(dir, false)); - Matter.Body.setDensity(bullet[me], 0.0005); - bullet[me].explodeRad = 275; - bullet[me].onEnd = function() { - b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end - if (mod.grenadeFragments) b.targetedNail(this.position, mod.grenadeFragments) - } - bullet[me].minDmgSpeed = 1; - bullet[me].beforeDmg = function() { - this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion - }; - if (mod.isRPG) { - b.fireProps(35, mech.crouch ? 60 : -15, dir, me); //cd , speed - bullet[me].endCycle = game.cycle + 70; - bullet[me].frictionAir = 0.07; - const MAG = 0.015 - bullet[me].thrust = { - x: bullet[me].mass * MAG * Math.cos(dir), - y: bullet[me].mass * MAG * Math.sin(dir) - } - bullet[me].do = function() { - this.force.x += this.thrust.x; - this.force.y += this.thrust.y; - if (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { - this.endCycle = 0; //explode if touching map or blocks - } - }; - } else { - b.fireProps(mech.crouch ? 40 : 30, mech.crouch ? 43 : 32, dir, me); //cd , speed - bullet[me].endCycle = game.cycle + Math.floor(mech.crouch ? 120 : 80); - bullet[me].restitution = 0.4; - bullet[me].do = function() { - this.force.y += this.mass * 0.0025; //extra gravity for harder arcs - }; - } + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 40 : 30) * b.fireCD); // cool down + b.grenade() }, fireNeutron() { const me = bullet.length; @@ -2539,6 +2734,14 @@ const b = { bullet[me].stuckTo = null; bullet[me].stuckToRelativePosition = null; bullet[me].vacuumSlow = 0.97; + if (mod.isRewindGrenade && input.down) { + Matter.Body.setVelocity(bullet[me], { + x: 0, + y: 0 + }); + bullet[me].maxDamageRadius *= 1.3 + mech.rewind(200, false) + } bullet[me].beforeDmg = function() {}; bullet[me].stuck = function() {}; bullet[me].do = function() { @@ -2669,69 +2872,6 @@ const b = { } } }, - fireVacuum() { - const me = bullet.length; - const dir = mech.angle; // + Math.random() * 0.05; - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 20, b.fireAttributes(dir, false)); - Matter.Body.setDensity(bullet[me], 0.0003); - bullet[me].explodeRad = 350 + Math.floor(Math.random() * 50);; - bullet[me].onEnd = function() { - b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end - if (mod.grenadeFragments) b.targetedNail(this.position, mod.grenadeFragments) - } - bullet[me].beforeDmg = function() {}; - const cd = mech.crouch ? 90 : 75 - b.fireProps(cd, mech.crouch ? 46 : 35, dir, me); //cd , speed - bullet[me].endCycle = game.cycle + cd; - bullet[me].restitution = 0.4; - bullet[me].do = function() { - this.force.y += this.mass * 0.0025; //extra gravity for harder arcs - - const suckCycles = 40 - if (game.cycle > this.endCycle - suckCycles) { //suck - const that = this - - function suck(who, radius = that.explodeRad * 3.2) { - for (i = 0, len = who.length; i < len; i++) { - const sub = Vector.sub(that.position, who[i].position); - const dist = Vector.magnitude(sub); - if (dist < radius && dist > 150) { - knock = Vector.mult(Vector.normalise(sub), mag * who[i].mass / Math.sqrt(dist)); - who[i].force.x += knock.x; - who[i].force.y += knock.y; - } - } - } - let mag = 0.1 - if (game.cycle > this.endCycle - 5) { - mag = -0.22 - suck(mob, this.explodeRad * 3) - suck(body, this.explodeRad * 2) - suck(powerUp, this.explodeRad * 1.5) - suck(bullet, this.explodeRad * 1.5) - suck([player], this.explodeRad * 1.3) - } else { - mag = 0.11 - suck(mob, this.explodeRad * 3) - suck(body, this.explodeRad * 2) - suck(powerUp, this.explodeRad * 1.5) - suck(bullet, this.explodeRad * 1.5) - suck([player], this.explodeRad * 1.3) - } - //keep bomb in place - Matter.Body.setVelocity(this, { - x: 0, - y: 0 - }); - //draw suck - const radius = 2.75 * this.explodeRad * (this.endCycle - game.cycle) / suckCycles - ctx.fillStyle = "rgba(0,0,0,0.1)"; - ctx.beginPath(); - ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); - ctx.fill(); - } - }; - } }, { name: "mine", @@ -2959,7 +3099,7 @@ const b = { if (mod.isRailAreaDamage) { mob[i].force.x += 2 * FORCE.x; mob[i].force.y += 2 * FORCE.y; - const damage = b.dmgScale * 0.1 * Math.sqrt(DEPTH) + const damage = b.dmgScale * 0.13 * Math.sqrt(DEPTH) mob[i].damage(damage); mob[i].locatePlayer(); game.drawList.push({ //add dmg to draw queue diff --git a/js/level.js b/js/level.js index 48cf96a..e963f02 100644 --- a/js/level.js +++ b/js/level.js @@ -17,11 +17,11 @@ const level = { // game.zoomScale = 1000; // game.setZoom(); // mech.setField("wormhole") - // b.giveGuns("shotgun") + // b.giveGuns("grenades") // mod.isIncendiary = true // mod.is3Missiles = true - // mod.giveMod("shotgun slug") - // mod.giveMod("diffuse beam") + // mod.giveMod("CPT reversal") + // mod.giveMod("causality bombs") level.intro(); //starting level // level.testing(); //not in rotation diff --git a/js/mods.js b/js/mods.js index d5a8957..d81d4e9 100644 --- a/js/mods.js +++ b/js/mods.js @@ -108,21 +108,6 @@ const mod = { return mod.foamBotCount + mod.nailBotCount + mod.laserBotCount + mod.boomBotCount + mod.plasmaBotCount + mod.orbitBotCount }, mods: [{ - name: "integrated armament", - description: "increase damage by 25%
your inventory can only hold 1 gun", - maxCount: 1, - count: 0, - allowed() { - return b.inventory.length < 2 - }, - requires: "no more than 1 gun", - effect() { - mod.isOneGun = true; - }, - remove() { - mod.isOneGun = false; - } - }, { name: "capacitor", description: "increase damage by 1%
for every 7 stored energy", maxCount: 1, @@ -176,7 +161,7 @@ const mod = { maxCount: 1, count: 0, allowed() { - return mod.isEnergyLoss && mech.maxEnergy === 1 && !mod.isMissileField && !mod.isSporeField && !mod.isTimeAvoidDeath + return mod.isEnergyLoss && mech.maxEnergy === 1 && !mod.isMissileField && !mod.isSporeField && !mod.isRewindAvoidDeath }, requires: "heat engine, not max energy increase, CPT, missile or spore nano-scale", effect() { @@ -210,9 +195,9 @@ const mod = { maxCount: 6, count: 0, allowed() { - return true + return mech.Fx === 0.016 }, - requires: "", + requires: "base movement speed", effect: () => { mod.restDamage += 0.25 }, @@ -252,6 +237,22 @@ const mod = { mod.isAcidDmg = false; } }, + { + name: "integrated armament", + description: "increase damage by 25%
your inventory can only hold 1 gun", + maxCount: 1, + count: 0, + allowed() { + return b.inventory.length < 2 + }, + requires: "no more than 1 gun", + effect() { + mod.isOneGun = true; + }, + remove() { + mod.isOneGun = false; + } + }, { name: "negative feedback", description: "increase damage by 6%
for every 10 missing base health", @@ -371,78 +372,6 @@ const mod = { mod.throwChargeRate = 1 } }, - { - name: "reaction inhibitor", - description: "mobs spawn with 12% less health", - maxCount: 3, - count: 0, - allowed() { - return mod.nailsDeathMob || mod.sporesOnDeath || mod.isExplodeMob - }, - requires: "zoospore vector or impact shear or thermal runaway", - effect: () => { - mod.mobSpawnWithHealth *= 0.88 - - //set all mobs at full health to 0.85 - for (let i = 0; i < mob.length; i++) { - if (mob.health > mod.mobSpawnWithHealth) mob.health = mod.mobSpawnWithHealth - } - }, - remove() { - mod.mobSpawnWithHealth = 1; - } - }, - { - name: "zoospore vector", - description: "mobs produce spores when they die
9% chance", - maxCount: 9, - count: 0, - allowed() { - return !mod.nailsDeathMob && !mod.isExplodeMob - }, - requires: "not impact shear or thermal runaway", - effect() { - mod.sporesOnDeath += 0.09; - for (let i = 0; i < 8; i++) { - b.spore(mech.pos) - } - }, - remove() { - mod.sporesOnDeath = 0; - } - }, - { - name: "impact shear", - description: "mobs release a nail when they die
nails target nearby mobs", - maxCount: 9, - count: 0, - allowed() { - return !mod.sporesOnDeath && !mod.isExplodeMob - }, - requires: "not zoospore vector or thermal runaway", - effect: () => { - mod.nailsDeathMob++ - }, - remove() { - mod.nailsDeathMob = 0; - } - }, - { - name: "thermal runaway", - description: "mobs explode when they die
be careful", - maxCount: 1, - count: 0, - allowed() { - return (mod.haveGunCheck("missiles") || mod.isIncendiary || (mod.haveGunCheck("grenades") && !mod.isNeutronBomb) || mod.haveGunCheck("vacuum bomb") || mod.isPulseLaser || mod.isMissileField || mod.boomBotCount > 1 || mod.isFlechetteExplode) && !mod.sporesOnDeath && !mod.nailsDeathMob - }, - requires: "an explosive damage source, not zoospore vector or impact shear", - effect: () => { - mod.isExplodeMob = true; - }, - remove() { - mod.isExplodeMob = false; - } - }, { name: "ammonium nitrate", description: "increase explosive damage by 20%
increase explosive radius by 20%", @@ -508,15 +437,87 @@ const mod = { mod.isImmuneExplosion = false; } }, + { + name: "thermal runaway", + description: "mobs explode when they die
be careful", + maxCount: 1, + count: 0, + allowed() { + return (mod.haveGunCheck("missiles") || mod.isIncendiary || (mod.haveGunCheck("grenades") && !mod.isNeutronBomb) || mod.haveGunCheck("vacuum bomb") || mod.isPulseLaser || mod.isMissileField || mod.boomBotCount > 1 || mod.isFlechetteExplode) && !mod.sporesOnDeath && !mod.nailsDeathMob && !mod.isBotSpawner + }, + requires: "an explosive damage source, no other mob death mods", + effect: () => { + mod.isExplodeMob = true; + }, + remove() { + mod.isExplodeMob = false; + } + }, + { + name: "reaction inhibitor", + description: "mobs spawn with 12% less health", + maxCount: 3, + count: 0, + allowed() { + return mod.nailsDeathMob || mod.sporesOnDeath || mod.isExplodeMob || mod.isBotSpawner + }, + requires: "any mob death mod", + effect: () => { + mod.mobSpawnWithHealth *= 0.88 + + //set all mobs at full health to 0.85 + for (let i = 0; i < mob.length; i++) { + if (mob.health > mod.mobSpawnWithHealth) mob.health = mod.mobSpawnWithHealth + } + }, + remove() { + mod.mobSpawnWithHealth = 1; + } + }, + { + name: "zoospore vector", + description: "mobs produce spores when they die
9% chance", + maxCount: 9, + count: 0, + allowed() { + return !mod.nailsDeathMob && !mod.isExplodeMob && !mod.isBotSpawner + }, + requires: "no other mob death mods", + effect() { + mod.sporesOnDeath += 0.09; + for (let i = 0; i < 8; i++) { + b.spore(mech.pos) + } + }, + remove() { + mod.sporesOnDeath = 0; + } + }, + { + name: "impact shear", + description: "mobs release a nail when they die
nails target nearby mobs", + maxCount: 9, + count: 0, + allowed() { + return !mod.sporesOnDeath && !mod.isExplodeMob && !mod.isBotSpawner + }, + requires: "no other mob death mods", + effect: () => { + mod.nailsDeathMob++ + }, + remove() { + mod.nailsDeathMob = 0; + } + }, { name: "scrap bots", description: "20% chance to build a bot after killing a mob
the bot lasts for about 20 seconds", maxCount: 3, count: 0, allowed() { - return mod.totalBots() > 0 + return mod.totalBots() > 0 && !mod.sporesOnDeath && !mod.nailsDeathMob && !mod.isExplodeMob }, - requires: "a bot", + requires: "a bot and no other mob death mods", effect() { mod.isBotSpawner += 0.20; }, @@ -524,23 +525,6 @@ const mod = { mod.isBotSpawner = 0; } }, - { - name: "bot fabrication", - description: "anytime you collect 5 rerolls
use them to build a random bot", - maxCount: 1, - count: 0, - allowed() { - return powerUps.reroll.rerolls > 5 || build.isCustomSelection - }, - requires: "at least 6 rerolls", - effect() { - mod.isRerollBots = true; - powerUps.reroll.changeRerolls(0) - }, - remove() { - mod.isRerollBots = false; - } - }, { name: "nail-bot", description: "a bot fires nails at targets in line of sight", @@ -664,9 +648,9 @@ const mod = { maxCount: 9, count: 0, allowed() { - return true + return mech.maxEnergy > 0.5 }, - requires: "", + requires: "maximum energy above 50%", effect() { mod.laserBotCount++; b.laserBot(); @@ -746,6 +730,23 @@ const mod = { } } }, + { + name: "bot fabrication", + description: "anytime you collect 5 rerolls
use them to build a random bot", + maxCount: 1, + count: 0, + allowed() { + return powerUps.reroll.rerolls > 5 || build.isCustomSelection + }, + requires: "at least 6 rerolls", + effect() { + mod.isRerollBots = true; + powerUps.reroll.changeRerolls(0) + }, + remove() { + mod.isRerollBots = false; + } + }, { name: "perimeter defense", description: "reduce harm by 3%
for each of your permanent bots", @@ -1039,18 +1040,50 @@ const mod = { }, { name: "CPT reversal", - description: "rewind 1.5 - 5 seconds to avoid harm
drains 66 - 220 energy", + description: "rewind 1.5 - 5 seconds to avoid harm
drains 66 - 220 energy", maxCount: 1, count: 0, - allowed() { - return mech.maxEnergy > 0.99 && (mech.fieldUpgrades[mech.fieldMode].name !== "nano-scale manufacturing" || mech.maxEnergy > 1) && mech.fieldUpgrades[mech.fieldMode].name !== "standing wave harmonics" && !mod.isEnergyHealth && !mod.isEnergyLoss && !mod.isPiezo + allowed() { //&& (mech.fieldUpgrades[mech.fieldMode].name !== "nano-scale manufacturing" || mech.maxEnergy > 1) + return mech.maxEnergy > 0.99 && mech.fieldUpgrades[mech.fieldMode].name !== "standing wave harmonics" && !mod.isEnergyHealth && !mod.isEnergyLoss && !mod.isPiezo }, requires: "not nano-scale, mass-energy, standing wave, acute stress, piezoelectricity", effect() { - mod.isTimeAvoidDeath = true; + mod.isRewindAvoidDeath = true; }, remove() { - mod.isTimeAvoidDeath = false; + mod.isRewindAvoidDeath = false; + } + }, + { + name: "causality bots", + description: "when you rewind, build some bots
that protect you for about 7 seconds", + maxCount: 3, + count: 0, + allowed() { + return mod.isRewindAvoidDeath || mod.isRewindEnergy + }, + requires: "CPT", + effect() { + mod.isRewindBot++; + }, + remove() { + mod.isRewindBot = 0; + } + }, + { + name: "causality bombs", + description: "before you rewind drop some grenades", + maxCount: 1, + count: 0, + allowed() { + return mod.isRewindAvoidDeath + }, + requires: "CPT", + effect() { + mod.isRewindGrenade = true; + }, + remove() { + mod.isRewindGrenade = false; } }, { @@ -1059,7 +1092,7 @@ const mod = { maxCount: 1, count: 0, allowed() { - return !mod.isEnergyHealth && !mod.isTimeAvoidDeath + return !mod.isEnergyHealth && !mod.isRewindAvoidDeath }, requires: "not mass-energy equivalence, CPT reversal", effect() { @@ -1094,7 +1127,7 @@ const mod = { maxCount: 1, count: 0, allowed() { - return !mod.isEnergyLoss && !mod.isPiezo && !mod.isTimeAvoidDeath && !mod.isSpeedHarm && mech.fieldUpgrades[mech.fieldMode].name !== "negative mass field" + return !mod.isEnergyLoss && !mod.isPiezo && !mod.isRewindAvoidDeath && !mod.isSpeedHarm && mech.fieldUpgrades[mech.fieldMode].name !== "negative mass field" }, requires: "not piezoelectricity, acute stress response, 1st law, negative mass field", effect: () => { @@ -1163,7 +1196,7 @@ const mod = { }, { name: "energy conservation", - description: "7% of damage done recovered as energy", + description: "6% of damage done recovered as energy", maxCount: 9, count: 0, allowed() { @@ -1171,7 +1204,7 @@ const mod = { }, requires: "some increased damage", effect() { - mod.energySiphon += 0.07; + mod.energySiphon += 0.06; }, remove() { mod.energySiphon = 0; @@ -2355,12 +2388,14 @@ const mod = { allowed() { return mod.haveGunCheck("grenades") && !mod.isVacuumBomb && !mod.isNeutronBomb }, - requires: "grenades, not vacuum bomb", + requires: "grenades, not vacuum bomb, neutron", effect() { mod.isRPG = true; + b.setGrenadeMode() }, remove() { mod.isRPG = false; + b.setGrenadeMode() } }, { @@ -2374,17 +2409,11 @@ const mod = { requires: "grenades, not rocket-propelled", effect() { mod.isVacuumBomb = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "grenades") b.guns[i].fire = b.guns[i].fireVacuum - } + b.setGrenadeMode() }, remove() { mod.isVacuumBomb = false; - if (!mod.isNeutronBomb) { - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "grenades") b.guns[i].fire = (mod.isNeutronBomb) ? b.guns[i].fireNeutron : b.guns[i].fireNormal - } - } + b.setGrenadeMode() } }, { @@ -2398,15 +2427,11 @@ const mod = { requires: "grenades, not rocket-propelled or fragmentation", effect() { mod.isNeutronBomb = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "grenades") b.guns[i].fire = b.guns[i].fireNeutron - } + b.setGrenadeMode() }, remove() { mod.isNeutronBomb = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "grenades") b.guns[i].fire = (mod.isVacuumBomb) ? b.guns[i].fireVacuum : b.guns[i].fireNormal - } + b.setGrenadeMode() } }, { @@ -3671,5 +3696,7 @@ const mod = { isBotDamage: null, isBanish: null, isMaxEnergyMod: null, - isLowEnergyDamage: null + isLowEnergyDamage: null, + isRewindBot: null, + isRewindGrenade: null } \ No newline at end of file diff --git a/js/player.js b/js/player.js index 2a5cc64..7e7408a 100644 --- a/js/player.js +++ b/js/player.js @@ -485,65 +485,104 @@ const mech = { if (mod.energyRegen === 0) dmg *= 0.4 //0.22 + 0.78 * mech.energy //77% damage reduction at zero energy if (mod.isTurret && mech.crouch) dmg *= 0.5; if (mod.isEntanglement && b.inventory[0] === b.activeGun) { - for (let i = 0, len = b.inventory.length; i < len; i++) { - dmg *= 0.85 // 1 - 0.15 - } + for (let i = 0, len = b.inventory.length; i < len; i++) dmg *= 0.85 // 1 - 0.15 } return dmg }, - damage(dmg) { - if (mod.isTimeAvoidDeath && mech.energy > 0.66) { - const steps = Math.floor(Math.min(299, 137 * mech.energy)) //go back 2 seconds at 100% energy - let history = mech.history[(mech.cycle - steps) % 300] - Matter.Body.setPosition(player, history.position); - Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); - // move bots to follow player - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType) { - Matter.Body.setPosition(bullet[i], Vector.add(player.position, { - x: 250 * (Math.random() - 0.5), - y: 250 * (Math.random() - 0.5) - })); - Matter.Body.setVelocity(bullet[i], { - x: 0, - y: 0 - }); + rewind(steps, isDrain = true) { + let history = mech.history[(mech.cycle - steps) % 300] + Matter.Body.setPosition(player, history.position); + Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); + // move bots to follow player + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + if (isDrain) { + mech.energy = Math.max(mech.energy - steps / 136, 0.01) + } + mech.immuneCycle = mech.cycle + 30; //player is immune to collision damage for 30 cycles + + let isDrawPlayer = true + const shortPause = function() { + if (mech.defaultFPSCycle < mech.cycle) { //back to default values + game.fpsCap = game.fpsCapDefault + game.fpsInterval = 1000 / game.fpsCap; + document.getElementById("dmg").style.transition = "opacity 1s"; + document.getElementById("dmg").style.opacity = "0"; + } else { + requestAnimationFrame(shortPause); + if (isDrawPlayer) { + isDrawPlayer = false + ctx.save(); + ctx.translate(canvas.width2, canvas.height2); //center + ctx.scale(game.zoom / game.edgeZoomOutSmooth, game.zoom / game.edgeZoomOutSmooth); //zoom in once centered + ctx.translate(-canvas.width2 + mech.transX, -canvas.height2 + mech.transY); //translate + for (let i = 1; i < steps; i++) { + history = mech.history[(mech.cycle - i) % 300] + mech.pos.x = history.position.x + mech.pos.y = history.position.y + mech.draw(); + } + ctx.restore(); + mech.resetHistory() } } + }; - mech.energy = Math.max(mech.energy - steps / 136, 0.01) - mech.immuneCycle = mech.cycle + 30; //player is immune to collision damage for 30 cycles - - let isDrawPlayer = true - const shortPause = function() { - if (mech.defaultFPSCycle < mech.cycle) { //back to default values - game.fpsCap = game.fpsCapDefault - game.fpsInterval = 1000 / game.fpsCap; - } else { - requestAnimationFrame(shortPause); - if (isDrawPlayer) { - isDrawPlayer = false - - ctx.save(); - ctx.translate(canvas.width2, canvas.height2); //center - ctx.scale(game.zoom / game.edgeZoomOutSmooth, game.zoom / game.edgeZoomOutSmooth); //zoom in once centered - ctx.translate(-canvas.width2 + mech.transX, -canvas.height2 + mech.transY); //translate - for (let i = 1; i < steps; i++) { - history = mech.history[(mech.cycle - i) % 300] - mech.pos.x = history.position.x - mech.pos.y = history.position.y - mech.draw(); - } - ctx.restore(); - mech.resetHistory() + if (mech.defaultFPSCycle < mech.cycle) requestAnimationFrame(shortPause); + game.fpsCap = (isDrain ? 3 : 5) //1 is longest pause, 4 is standard + game.fpsInterval = 1000 / game.fpsCap; + mech.defaultFPSCycle = mech.cycle + if (mod.isRewindBot) { + const len = (isDrain ? steps * 0.042 : 2) * mod.isRewindBot + for (let i = 0; i < len; i++) { + where = mech.history[(mech.cycle - i * 40) % 300].position //spread out spawn locations along past history + b.randomBot({ + x: where.x + 100 * (Math.random() - 0.5), + y: where.y + 100 * (Math.random() - 0.5) + }, false, false) + bullet[bullet.length - 1].endCycle = game.cycle + 360 + Math.floor(180 * Math.random()) //6-9 seconds + } + } + }, + damage(dmg) { + if (mod.isRewindAvoidDeath && mech.energy > 0.66) { + const steps = Math.floor(Math.min(299, 137 * mech.energy)) //go back 2 seconds at 100% energy + if (mod.isRewindGrenade) { + for (let i = 1, len = Math.floor(3 + steps / 60); i < len; i++) { + b.grenade(Vector.add(mech.pos, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }), -i * Math.PI / len) //fire different angles for each grenade + const who = bullet[bullet.length - 1] + if (mod.isVacuumBomb) { + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.5, + y: who.velocity.y * 0.5 + }); + } else if (mod.isRPG) { + who.endCycle = (who.endCycle - game.cycle) * 0.2 + game.cycle + } else if (mod.isNeutronBomb) { + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.3, + y: who.velocity.y * 0.3 + }); + } else { + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.5, + y: who.velocity.y * 0.5 + }); + who.endCycle = (who.endCycle - game.cycle) * 0.5 + game.cycle } } - }; - - if (mech.defaultFPSCycle < mech.cycle) requestAnimationFrame(shortPause); - game.fpsCap = 3 //1 is shortest pause, 4 is standard - game.fpsInterval = 1000 / game.fpsCap; - mech.defaultFPSCycle = mech.cycle + } + mech.rewind(steps) return } mech.lastHarmCycle = mech.cycle @@ -1765,7 +1804,7 @@ const mech = { mech.grabPowerUp(); mech.lookForPickUp(180); - const DRAIN = 0.0011 + const DRAIN = 0.0013 if (mech.energy > DRAIN) { mech.energy -= DRAIN; if (mech.energy < DRAIN) { diff --git a/style.css b/style.css index ca62443..f3e2516 100644 --- a/style.css +++ b/style.css @@ -517,13 +517,20 @@ em { .color-harm { /* color: */ /* text-shadow: #FC0 1px 0 10px; */ - background-color: hsla(325, 100%, 85%, 0.15); + background-color: hsla(51, 100%, 71%, 0.187); padding: 2px; border-radius: 4px; letter-spacing: 1px; font-weight: 100; } +/* .color-rewind { + background-image: linear-gradient(to left, #fff, #bbb); + border-radius: 5px; + padding: 2px; + letter-spacing: 1px; +} */ + .color-r { color: #f7b; letter-spacing: 1px; diff --git a/todo.txt b/todo.txt index 7ee8521..ba43a92 100644 --- a/todo.txt +++ b/todo.txt @@ -1,21 +1,19 @@ -*********** NEXT PATCH *********** +******************************************************** NEXT PATCH ******************************************************** -added more requirements to various mods +mod: causality bots - before you rewind, build bots that protect you for for 7 seconds -CPT reversal is more flexible with energy - 1.5-5 seconds of rewind drains 66% - 220% energy +mod grenade: causality bombs - before you rewind, drop some grenades -mod: exothermic process - renamed acute stress response -mod: heat engine - reduce max energy by 50 increase damage by 40% -mod: Gibbs free energy - gain 5% damage for every 10 energy below 100 -************** BUGS ************** +******************************************************** BUGS ******************************************************** -(fixed) red square mobs no longer die on contact - might be side effects (we put the player invincibility after mob on damage code) +mod and mob are too similar (always) make it so that when you are immune to harm you can either jump on mobs or you pass through them +(always) is there a way to check if the player is stuck inside the map or block + trigger a short term non-collide if that occurs + (4+ reports before potential fix) bug - crouch and worm hole? -> crouch locked in players have extra gravity might be from the short jump code @@ -31,20 +29,39 @@ mod: Gibbs free energy - gain 5% damage for every 10 energy below 100 (repeatable almost every time) bug - mines spawn extra mines when fired at thin map wall while jumping -************** TODO ************** +******************************************************** TODO ******************************************************** -mod that gives a bonus for low energy - damage again or something different - requires heat engine +combine fragmentation grenades that that mod that makes railguns fragment into nails into one single mod, and let it apply to shotgun slug -CPT like mods - delayed rewind: after taking damage, play for 2 seconds, then rewind to just before you took damage - and return lost health - this might need code to run in a field to work - time dilation field? - gain a scrap bot after rewinding - use ammo to rewind instead of energy - explode the area where you were hit before rewinding +mod: power up magnetism - power ups drift towards player + where would this code go? + +super balls start at 3, not 4 + have to balance damage + +RPG might need a buff + +retrocausality bomb should fire 3 grenades at once that spread out a small bit before they explode? + this means you'd have to make grenades a method. + +make different move methods + mod: crouch charge jump + mod: double jump + +mod: when mobs are at full health you do 40% to them + +mechanic: failed technology - add mods to the mod pool with a dumb effect + don't show up in custom? + negative effect (one time effects are better to avoid code clutter) + remove all your energy + eject all your rerolls (not bad with dup) + teleport to the start of the level + remove your bots (requires you to have some bots) + your bots are changed to random bots + +mod - move super fast, go intangible, drain energy very fast + this is like a dodge roll + mod for standing wave?, cloaking? spawn a few power ups on the final boss level @@ -279,7 +296,7 @@ n-gon outreach ideas javascript:(function(){var script=document.createElement('script');script.onload=function(){var stats=new Stats();document.body.appendChild(stats.dom);requestAnimationFrame(function loop(){stats.update();requestAnimationFrame(loop)});};script.src='//mrdoob.github.io/stats.js/build/stats.min.js';document.head.appendChild(script);})() -************** LORE ************** +******************************************************** LORE ******************************************************** lore - a robot (the player) gains self awareness each mod/gun/field is a new tech