diff --git a/js/bullets.js b/js/bullets.js index f452d18..e2c6880 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -18,7 +18,7 @@ const b = { isModDroneOnDamage: null, modExtraDmg: null, annihilation: null, - isModRecursiveHealing: null, + modRecursiveHealing: null, modSquirrelFx: null, isModCrit: null, isModBayesian: null, @@ -28,6 +28,7 @@ const b = { isModMassEnergy: null, isModFourOptions: null, modGuardianCount: null, + modCollisionImmuneCycles: null, setModDefaults() { b.modCount = 0; b.modFireRate = 1; @@ -43,7 +44,7 @@ const b = { b.modSpores = 0; b.modExtraDmg = 0; b.isModAnnihilation = false; - b.isModRecursiveHealing = false; + b.modRecursiveHealing = 1; b.modSquirrelFx = 1; b.isModCrit = false; b.isModBayesian = 0; @@ -53,26 +54,23 @@ const b = { b.isModEntanglement = false; b.isModMassEnergy = false; b.modGuardianCount = 0; + b.modCollisionImmuneCycles = 30; mech.Fx = 0.015; mech.jumpForce = 0.38; - mech.throwChargeRate = 2; - mech.throwChargeMax = 50; mech.maxHealth = 1; + mech.fieldEnergyMax = 1; for (let i = 0; i < b.mods.length; i++) { b.mods[i].count = 0 } }, mods: [{ name: "depleted uranium rounds", - description: `your bullets are 11% larger
increased mass and physical damage`, + description: `your bullets are +11% larger
increased mass and physical damage`, //0 count: 0, maxCount: 4, - value: 1.11, effect() { - b.modBulletSize *= this.value; - this.value -= 0.01 - this.description = `your bullets are ${Math.floor((this.value-1)*100)}% larger
increased mass and physical damage` + b.modBulletSize += 0.11 } }, { @@ -118,7 +116,7 @@ const b = { }, { name: "auto-loading heuristics", - description: "your delay after firing is 12% shorter", + description: "your delay after firing is +12% shorter", //5 maxCount: 4, count: 0, @@ -138,7 +136,7 @@ const b = { }, { name: "Lorentzian topology", - description: "your bullets last 33% longer", + description: "your bullets last +33% longer", //7 maxCount: 4, count: 0, @@ -148,12 +146,12 @@ const b = { }, { name: "zoospore vector", - description: "enemies can discharge spores on death
spores seek out enemies", + description: "enemies discharge spores on death
+11% chance", //8 maxCount: 4, count: 0, effect() { //good late game maybe? - b.modSpores += 0.15; + b.modSpores += 0.11; } }, { @@ -166,10 +164,31 @@ const b = { b.isModDroneOnDamage = true; } }, + { + name: "guardian", + description: "a bot protects the space around you
uses a short range laser that drains energy", + //10 + maxCount: 4, + count: 0, + effect() { // good with melee builds, content skipping builds + b.modGuardianCount++; + b.guardian(); + } + }, + { + name: "piezoelectric plating", + description: "immune to harm from collisions for +2 seconds
activates after being harmed from a collision", + //11 + maxCount: 1, + count: 0, + effect() { // good with melee builds, content skipping builds + b.modCollisionImmuneCycles += 120; + } + }, { name: "annihilation", description: "after touching enemies, they are annihilated", - //10 + //12 maxCount: 1, count: 0, effect() { //good with mods that heal: superconductive healing, entropy transfer @@ -178,8 +197,8 @@ const b = { }, { name: "high explosives", - description: "the radius of explosions are 20% larger
immune to harm from explosions", - //11 + description: "the radius of explosions are +20% larger
immune to harm from explosions", + //13 maxCount: 4, count: 0, effect: () => { @@ -190,7 +209,7 @@ const b = { { name: "entanglement", description: "using your first gun reduces harm
scales by 7% for each gun in your inventory", - //12 + //14 maxCount: 1, count: 0, effect() { // good with laser-bots @@ -200,7 +219,7 @@ const b = { { name: "energy transfer", description: "gain energy proportional to damage done", - //13 + //15 maxCount: 4, count: 0, effect() { //good with laser, and all fields @@ -210,7 +229,7 @@ const b = { { name: "entropy transfer", description: "heal proportional to damage done", - //14 + //16 maxCount: 4, count: 0, effect() { //good with guns that overkill: one shot, grenade @@ -219,8 +238,8 @@ const b = { }, { name: "overcharge", - description: "charge energy 33% beyond your maximum", - //15 + description: "charge energy +33% beyond your maximum", + //17 maxCount: 4, count: 0, effect() { @@ -229,8 +248,8 @@ const b = { }, { name: "supersaturation", - description: "heal 33% beyond your max health", - //16 + description: "heal +33% beyond your max health", + //18 maxCount: 4, count: 0, effect() { @@ -239,18 +258,18 @@ const b = { }, { name: "recursive healing", - description: "healing power ups are twice as effective", - //17 - maxCount: 1, + description: "healing power ups trigger an extra time.", + //19 + maxCount: 4, count: 0, effect() { // good with ablative synthesis, melee builds - b.isModRecursiveHealing = true + b.modRecursiveHealing += 1 } }, { name: "mass-energy equivalence", description: "convert the mass of power ups into energy
power ups fill your energy and heal for +5%", - //18 + //20 maxCount: 1, count: 0, effect: () => { @@ -260,7 +279,7 @@ const b = { { name: "+1 cardinality", description: "one extra choice when selecting power ups", - //19 + //21 maxCount: 1, count: 0, effect: () => { @@ -270,36 +289,25 @@ const b = { { name: "Bayesian inference", description: "20% chance for double power ups to drop
one fewer choice when selecting power ups", - //20 + //22 maxCount: 1, count: 0, effect: () => { b.isModBayesian = 0.20; } }, - { - name: "Gauss rifle", - description: "launch blocks at much higher speeds
hold onto larger blocks even after getting hit", - //21 - maxCount: 1, - count: 0, - effect() { // good with guns that run out of ammo - mech.throwChargeRate = 4; - mech.throwChargeMax = 150; - mech.holdingMassScale = 0.05; //can hold heavier blocks with lower cost to jumping - } - }, - { - name: "guardian", - description: "a bot protects the space around you
uses a short range laser that drains energy", - //22 - maxCount: 4, - count: 0, - effect() { // good with melee builds, content skipping builds - b.modGuardianCount++; - b.guardian(); - } - }, + // { + // name: "Gauss rifle", + // description: "launch blocks at much higher speeds
hold onto larger blocks even after getting hit", + // //23 + // maxCount: 1, + // count: 0, + // effect() { // good with guns that run out of ammo + // mech.throwChargeRate = 4; + // mech.throwChargeMax = 150; + // mech.holdingMassScale = 0.05; //can hold heavier blocks with lower cost to jumping + // } + // }, { name: "squirrel-cage rotor", description: "jump higher and move faster
reduced harm from falling ", @@ -658,8 +666,8 @@ const b = { }, spore(who) { //used with the mod upgrade in mob.death() const bIndex = bullet.length; - const RADIUS = 3 * b.modBulletSize; - bullet[bIndex] = Bodies.circle(who.position.x, who.position.y, RADIUS, { + const side = 4 * b.modBulletSize; + bullet[bIndex] = Bodies.polygon(who.position.x, who.position.y, 5, side, { // density: 0.0015, //frictionAir: 0.01, inertia: Infinity, restitution: 0.5, @@ -672,7 +680,7 @@ const b = { category: cat.bullet, mask: cat.map | cat.mob | cat.mobBullet | cat.mobShield //no collide with body }, - endCycle: game.cycle + Math.floor((480 + Math.floor(Math.random() * 240)) * b.isModBulletsLastLonger), + endCycle: game.cycle + Math.floor((660 + Math.floor(Math.random() * 240)) * b.isModBulletsLastLonger), minDmgSpeed: 0, onDmg() { this.endCycle = 0; //bullet ends cycle after doing damage @@ -706,7 +714,7 @@ const b = { // this.force.x -= THRUST * this.lockedOn.x // this.force.y -= THRUST * this.lockedOn.y } else { - this.force.y += this.mass * 0.00027; //gravity + this.force.y += this.mass * 0.0001; //gravity } }, }); @@ -821,7 +829,7 @@ const b = { guardian(speed = 1) { const me = bullet.length; const dir = mech.angle; - const RADIUS = (13 + 10 * Math.random()) * b.modBulletSize //(22 + 10 * Math.random()) * b.modBulletSize + const RADIUS = (14 + 6 * Math.random()) * b.modBulletSize bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 3, RADIUS, { angle: dir, friction: 0, @@ -829,7 +837,7 @@ const b = { restitution: 0.5 + 0.5 * Math.random(), dmg: 0, // 0.14 //damage done in addition to the damage from momentum minDmgSpeed: 2, - lookFrequency: 37 + Math.floor(27 * Math.random()), + lookFrequency: 37 + Math.floor(17 * Math.random()), acceleration: 0.0015 + 0.0013 * Math.random(), range: 500 + Math.floor(200 * Math.random()), endCycle: Infinity, @@ -859,9 +867,9 @@ const b = { } if (this.lockedOn && this.lockedOn.alive && mech.fieldMeter > 0.15) { //hit target with laser - mech.fieldMeter -= 0.001 + mech.fieldMeter -= 0.0012 - //make sure you can still see target + //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 && @@ -876,7 +884,7 @@ const b = { bestVertexDistance = dist } } - const dmg = b.dmgScale * 0.03; + const dmg = b.dmgScale * 0.045; this.lockedOn.damage(dmg); this.lockedOn.locatePlayer(); @@ -912,7 +920,7 @@ const b = { y: speed * Math.sin(dir) }); }, - giveGuns(gun = "random", ammoPacks = 2) { + giveGuns(gun = "random", ammoPacks = 6) { if (gun === "random") { //find what guns player doesn't have options = [] @@ -927,8 +935,9 @@ const b = { b.activeGun = 0; b.inventoryGun = 0; for (let i = 0; i < b.guns.length; i++) { - b.guns[i].ammo = b.guns[i].ammoPack * ammoPacks; b.inventory[i] = i; + b.guns[i].have = true; + b.guns[i].ammo = b.guns[i].ammoPack * ammoPacks; } } else { if (!b.guns[gun].have) b.inventory.push(gun); @@ -942,7 +951,7 @@ const b = { name: "minigun", //0 description: "rapidly fire a stream of small bullets", ammo: 0, - ammoPack: 150, + ammoPack: 50, have: false, isStarterGun: true, fire() { @@ -963,7 +972,7 @@ const b = { name: "shotgun", //1 description: "fire a burst of short range bullets
crouch to reduce recoil", ammo: 0, - ammoPack: 10, + ammoPack: 4, have: false, isStarterGun: true, fire() { @@ -999,7 +1008,7 @@ const b = { name: "super balls", //2 description: "fire five balls in a wide arc
balls bounce with no momentum loss", ammo: 0, - ammoPack: 12, + ammoPack: 4, have: false, isStarterGun: true, fire() { @@ -1034,7 +1043,7 @@ const b = { name: "fléchettes", //3 description: "fire a volley of precise high velocity needles", ammo: 0, - ammoPack: 65, + ammoPack: 22, have: false, isStarterGun: true, count: 0, //used to track how many shots are in a volley before a big CD @@ -1071,7 +1080,7 @@ const b = { name: "wave beam", //4 description: "emit a sine wave of oscillating particles
particles propagate through walls", ammo: 0, - ammoPack: 85, + ammoPack: 30, have: false, isStarterGun: true, fire() { @@ -1125,7 +1134,7 @@ const b = { name: "rail gun", //5 description: "electro-magnetically launch a dense rod
hold left mouse to charge, release to fire", //and repel enemies ammo: 0, - ammoPack: 6, + ammoPack: 2, have: false, isStarterGun: false, fire() { @@ -1357,7 +1366,7 @@ const b = { name: "missiles", //6 description: "fire missiles that accelerate towards enemies
explodes when near target", ammo: 0, - ammoPack: 8, + ammoPack: 3, have: false, isStarterGun: false, fireCycle: 0, @@ -1452,7 +1461,7 @@ const b = { name: "flak", //7 description: "fire a cluster of short range projectiles
explodes on contact or after half a second", ammo: 0, - ammoPack: 14, + ammoPack: 5, have: false, isStarterGun: true, fire() { @@ -1493,7 +1502,7 @@ const b = { name: "grenades", //8 description: "lob a single bouncy projectile
explodes on contact or after one second", ammo: 0, - ammoPack: 12, + ammoPack: 4, have: false, isStarterGun: false, fire() { @@ -1521,7 +1530,7 @@ const b = { name: "vacuum bomb", //9 description: "fire a bomb that sucks before exploding
click left mouse again to detonate", ammo: 0, - ammoPack: 5, + ammoPack: 2, have: false, isStarterGun: false, fire() { @@ -1630,7 +1639,7 @@ const b = { name: "ferro frag", //10 description: "fire a grenade that ejects nails
nails are magnetically attracted to enemies", ammo: 0, - ammoPack: 8, + ammoPack: 3, have: false, isStarterGun: false, fire() { @@ -1700,7 +1709,7 @@ const b = { name: "spores", //11 description: "fire orbs that discharge spores
spores seek out enemies", ammo: 0, - ammoPack: 10, + ammoPack: 4, have: false, isStarterGun: false, fire() { @@ -1745,7 +1754,7 @@ const b = { name: "drones", //12 description: "deploy drones that crash into enemies
collisions reduce drone cycles by 1 second", ammo: 0, - ammoPack: 17, + ammoPack: 6, have: false, isStarterGun: true, fire() { @@ -1853,302 +1862,18 @@ const b = { // b.fireProps(mech.crouch ? 40 : 30, mech.crouch ? 50 : 15, dir, me); //cd , speed // } // }, - { - name: "laser", //14 - description: "drain energy to emit a beam of coherent light
when crouched, initiate a fusion explosion", - ammo: 0, - ammoPack: Infinity, - have: false, - isStarterGun: true, - fire() { - if (mech.crouch) { //crouch fire mode - //calculate laser collision - let best; - let range = 3000 - const path = [{ - x: mech.pos.x + 20 * Math.cos(mech.angle), - y: mech.pos.y + 20 * Math.sin(mech.angle) - }, - { - x: mech.pos.x + range * Math.cos(mech.angle), - y: mech.pos.y + range * Math.sin(mech.angle) - } - ]; - const vertexCollision = function (v1, v1End, domain) { - for (let i = 0; i < domain.length; ++i) { - let vertices = domain[i].vertices; - const len = vertices.length - 1; - for (let j = 0; j < len; j++) { - results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); - if (results.onLine1 && results.onLine2) { - const dx = v1.x - results.x; - const dy = v1.y - results.y; - const dist2 = dx * dx + dy * dy; - if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - best = { - x: results.x, - y: results.y, - dist2: dist2, - who: domain[i], - v1: vertices[j], - v2: vertices[j + 1] - }; - } - } - } - results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); - if (results.onLine1 && results.onLine2) { - const dx = v1.x - results.x; - const dy = v1.y - results.y; - const dist2 = dx * dx + dy * dy; - if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - best = { - x: results.x, - y: results.y, - dist2: dist2, - who: domain[i], - v1: vertices[0], - v2: vertices[len] - }; - } - } - } - }; - - //check for collisions - best = { - x: null, - y: null, - dist2: Infinity, - who: null, - v1: null, - v2: null - }; - vertexCollision(path[0], path[1], mob); - vertexCollision(path[0], path[1], map); - vertexCollision(path[0], path[1], body); - if (best.dist2 != Infinity) { //if hitting something - path[path.length - 1] = { - x: best.x, - y: best.y - }; - } - - //use energy to explode - const energy = 0.25 * Math.min(mech.fieldMeter, 1.5) - mech.fieldMeter -= energy - if (best.who) b.explosion(path[1], 900 * energy) - mech.fireCDcycle = mech.cycle + Math.floor(65 * b.modFireRate); // cool down - - //draw laser beam - ctx.beginPath(); - ctx.moveTo(path[0].x, path[0].y); - ctx.lineTo(path[1].x, path[1].y); - ctx.strokeStyle = "rgba(255,0,0,0.13)" - ctx.lineWidth = 60 * energy / 0.2 - ctx.stroke(); - ctx.strokeStyle = "rgba(255,0,0,0.2)" - ctx.lineWidth = 18 - ctx.stroke(); - ctx.strokeStyle = "#f00"; - ctx.lineWidth = 4 - ctx.stroke(); - - //draw little dots along the laser path - const sub = Vector.sub(path[1], path[0]) - const mag = Vector.magnitude(sub) - for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) { - const dist = Math.random() - game.drawList.push({ - x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5), - y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5), - radius: 1 + 4 * Math.random(), - color: "rgba(255,0,0,0.5)", - time: Math.floor(2 + 33 * Math.random() * Math.random()) - }); - } - } else { //normal fire mode - const FIELD_DRAIN = 0.0018 //laser drains energy as well as bullets - const damage = 0.05 - if (mech.fieldMeter < FIELD_DRAIN) { - mech.fireCDcycle = mech.cycle + 100; // cool down if out of energy - } else { - mech.fieldMeter -= mech.fieldRegen + FIELD_DRAIN - let best; - const color = "#f00"; - const range = 3000; - const path = [{ - x: mech.pos.x + 20 * Math.cos(mech.angle), - y: mech.pos.y + 20 * Math.sin(mech.angle) - }, - { - x: mech.pos.x + range * Math.cos(mech.angle), - y: mech.pos.y + range * Math.sin(mech.angle) - } - ]; - const vertexCollision = function (v1, v1End, domain) { - for (let i = 0; i < domain.length; ++i) { - let vertices = domain[i].vertices; - const len = vertices.length - 1; - for (let j = 0; j < len; j++) { - results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); - if (results.onLine1 && results.onLine2) { - const dx = v1.x - results.x; - const dy = v1.y - results.y; - const dist2 = dx * dx + dy * dy; - if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - best = { - x: results.x, - y: results.y, - dist2: dist2, - who: domain[i], - v1: vertices[j], - v2: vertices[j + 1] - }; - } - } - } - results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); - if (results.onLine1 && results.onLine2) { - const dx = v1.x - results.x; - const dy = v1.y - results.y; - const dist2 = dx * dx + dy * dy; - if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - best = { - x: results.x, - y: results.y, - dist2: dist2, - who: domain[i], - v1: vertices[0], - v2: vertices[len] - }; - } - } - } - }; - const checkForCollisions = function () { - best = { - x: null, - y: null, - dist2: Infinity, - who: null, - v1: null, - v2: null - }; - vertexCollision(path[path.length - 2], path[path.length - 1], mob); - vertexCollision(path[path.length - 2], path[path.length - 1], map); - vertexCollision(path[path.length - 2], path[path.length - 1], body); - }; - const laserHitMob = function (dmg) { - if (best.who.alive) { - dmg *= b.dmgScale * damage; - best.who.damage(dmg); - best.who.locatePlayer(); - //draw mob damage circle - ctx.fillStyle = color; - ctx.beginPath(); - ctx.arc(path[path.length - 1].x, path[path.length - 1].y, Math.sqrt(dmg) * 100, 0, 2 * Math.PI); - ctx.fill(); - } - }; - - const reflection = function () { - // https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector - const n = Vector.perp(Vector.normalise(Vector.sub(best.v1, best.v2))); - const d = Vector.sub(path[path.length - 1], path[path.length - 2]); - const nn = Vector.mult(n, 2 * Vector.dot(d, n)); - const r = Vector.normalise(Vector.sub(d, nn)); - path[path.length] = Vector.add(Vector.mult(r, range), path[path.length - 1]); - }; - //beam before reflection - checkForCollisions(); - if (best.dist2 != Infinity) { - //if hitting something - path[path.length - 1] = { - x: best.x, - y: best.y - }; - laserHitMob(1); - - //1st reflection beam - reflection(); - //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body - let who = best.who; - checkForCollisions(); - if (best.dist2 != Infinity) { - //if hitting something - path[path.length - 1] = { - x: best.x, - y: best.y - }; - laserHitMob(0.8); - - //2nd reflection beam - //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body - if (who !== best.who) { - reflection(); - checkForCollisions(); - if (best.dist2 != Infinity) { - //if hitting something - path[path.length - 1] = { - x: best.x, - y: best.y - }; - laserHitMob(0.63); - - - reflection(); - checkForCollisions(); - if (best.dist2 != Infinity) { - //if hitting something - path[path.length - 1] = { - x: best.x, - y: best.y - }; - laserHitMob(0.5); - } - } - } - } - } - ctx.fillStyle = color; - ctx.strokeStyle = color; - ctx.lineWidth = 2; - ctx.lineDashOffset = 300 * Math.random() - // ctx.setLineDash([200 * Math.random(), 250 * Math.random()]); - - ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); - for (let i = 1, len = path.length; i < len; ++i) { - ctx.beginPath(); - ctx.moveTo(path[i - 1].x, path[i - 1].y); - ctx.lineTo(path[i].x, path[i].y); - ctx.stroke(); - ctx.globalAlpha *= 0.65; //reflections are less intense - // ctx.globalAlpha -= 0.1; //reflections are less intense - } - ctx.setLineDash([0, 0]); - ctx.globalAlpha = 1; - } - } - } - }, // { // name: "laser", //14 - // description: "emit a beam of collimated coherent light
drains energy instead of ammunition", + // description: "drain energy to emit a beam of coherent light
when crouched, initiate a fusion explosion", // ammo: 0, // ammoPack: Infinity, // have: false, // isStarterGun: true, // fire() { - // const FIELD_DRAIN = 0.002 //laser drains energy as well as bullets - // const damage = 0.05 - // if (mech.fieldMeter < FIELD_DRAIN) { - // mech.fireCDcycle = mech.cycle + 100; // cool down if out of energy - // } else { - // mech.fieldMeter -= mech.fieldRegen + FIELD_DRAIN + // if (mech.crouch) { //crouch fire mode + // //calculate laser collision // let best; - // const color = "#f00"; - // const range = 3000; + // let range = 3000 // const path = [{ // x: mech.pos.x + 20 * Math.cos(mech.angle), // y: mech.pos.y + 20 * Math.sin(mech.angle) @@ -2198,54 +1923,153 @@ const b = { // } // } // }; - // const checkForCollisions = function () { - // best = { - // x: null, - // y: null, - // dist2: Infinity, - // who: null, - // v1: null, - // v2: null - // }; - // vertexCollision(path[path.length - 2], path[path.length - 1], mob); - // vertexCollision(path[path.length - 2], path[path.length - 1], map); - // vertexCollision(path[path.length - 2], path[path.length - 1], body); - // }; - // const laserHitMob = function (dmg) { - // if (best.who.alive) { - // dmg *= b.dmgScale * damage; - // best.who.damage(dmg); - // best.who.locatePlayer(); - // //draw mob damage circle - // ctx.fillStyle = color; - // ctx.beginPath(); - // ctx.arc(path[path.length - 1].x, path[path.length - 1].y, Math.sqrt(dmg) * 100, 0, 2 * Math.PI); - // ctx.fill(); - // } - // }; - // const reflection = function () { - // // https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector - // const n = Vector.perp(Vector.normalise(Vector.sub(best.v1, best.v2))); - // const d = Vector.sub(path[path.length - 1], path[path.length - 2]); - // const nn = Vector.mult(n, 2 * Vector.dot(d, n)); - // const r = Vector.normalise(Vector.sub(d, nn)); - // path[path.length] = Vector.add(Vector.mult(r, range), path[path.length - 1]); + // //check for collisions + // best = { + // x: null, + // y: null, + // dist2: Infinity, + // who: null, + // v1: null, + // v2: null // }; - // //beam before reflection - // checkForCollisions(); - // if (best.dist2 != Infinity) { - // //if hitting something + // vertexCollision(path[0], path[1], mob); + // vertexCollision(path[0], path[1], map); + // vertexCollision(path[0], path[1], body); + // if (best.dist2 != Infinity) { //if hitting something // path[path.length - 1] = { // x: best.x, // y: best.y // }; - // laserHitMob(1); + // } - // //1st reflection beam - // reflection(); - // //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body - // let who = best.who; + // //use energy to explode + // const energy = 0.25 * Math.min(mech.fieldMeter, 1.5) + // mech.fieldMeter -= energy + // if (best.who) b.explosion(path[1], 900 * energy) + // mech.fireCDcycle = mech.cycle + Math.floor(65 * b.modFireRate); // cool down + + // //draw laser beam + // ctx.beginPath(); + // ctx.moveTo(path[0].x, path[0].y); + // ctx.lineTo(path[1].x, path[1].y); + // ctx.strokeStyle = "rgba(255,0,0,0.13)" + // ctx.lineWidth = 60 * energy / 0.2 + // ctx.stroke(); + // ctx.strokeStyle = "rgba(255,0,0,0.2)" + // ctx.lineWidth = 18 + // ctx.stroke(); + // ctx.strokeStyle = "#f00"; + // ctx.lineWidth = 4 + // ctx.stroke(); + + // //draw little dots along the laser path + // const sub = Vector.sub(path[1], path[0]) + // const mag = Vector.magnitude(sub) + // for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) { + // const dist = Math.random() + // game.drawList.push({ + // x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5), + // y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5), + // radius: 1 + 4 * Math.random(), + // color: "rgba(255,0,0,0.5)", + // time: Math.floor(2 + 33 * Math.random() * Math.random()) + // }); + // } + // } else { //normal fire mode + // const FIELD_DRAIN = 0.0018 //laser drains energy as well as bullets + // const damage = 0.05 + // if (mech.fieldMeter < FIELD_DRAIN) { + // mech.fireCDcycle = mech.cycle + 100; // cool down if out of energy + // } else { + // mech.fieldMeter -= mech.fieldRegen + FIELD_DRAIN + // let best; + // const color = "#f00"; + // const range = 3000; + // const path = [{ + // x: mech.pos.x + 20 * Math.cos(mech.angle), + // y: mech.pos.y + 20 * Math.sin(mech.angle) + // }, + // { + // x: mech.pos.x + range * Math.cos(mech.angle), + // y: mech.pos.y + range * Math.sin(mech.angle) + // } + // ]; + // const vertexCollision = function (v1, v1End, domain) { + // for (let i = 0; i < domain.length; ++i) { + // let vertices = domain[i].vertices; + // const len = vertices.length - 1; + // for (let j = 0; j < len; j++) { + // results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + // if (results.onLine1 && results.onLine2) { + // const dx = v1.x - results.x; + // const dy = v1.y - results.y; + // const dist2 = dx * dx + dy * dy; + // if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + // best = { + // x: results.x, + // y: results.y, + // dist2: dist2, + // who: domain[i], + // v1: vertices[j], + // v2: vertices[j + 1] + // }; + // } + // } + // } + // results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + // if (results.onLine1 && results.onLine2) { + // const dx = v1.x - results.x; + // const dy = v1.y - results.y; + // const dist2 = dx * dx + dy * dy; + // if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + // best = { + // x: results.x, + // y: results.y, + // dist2: dist2, + // who: domain[i], + // v1: vertices[0], + // v2: vertices[len] + // }; + // } + // } + // } + // }; + // const checkForCollisions = function () { + // best = { + // x: null, + // y: null, + // dist2: Infinity, + // who: null, + // v1: null, + // v2: null + // }; + // vertexCollision(path[path.length - 2], path[path.length - 1], mob); + // vertexCollision(path[path.length - 2], path[path.length - 1], map); + // vertexCollision(path[path.length - 2], path[path.length - 1], body); + // }; + // const laserHitMob = function (dmg) { + // if (best.who.alive) { + // dmg *= b.dmgScale * damage; + // best.who.damage(dmg); + // best.who.locatePlayer(); + // //draw mob damage circle + // ctx.fillStyle = color; + // ctx.beginPath(); + // ctx.arc(path[path.length - 1].x, path[path.length - 1].y, Math.sqrt(dmg) * 100, 0, 2 * Math.PI); + // ctx.fill(); + // } + // }; + + // const reflection = function () { + // // https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector + // const n = Vector.perp(Vector.normalise(Vector.sub(best.v1, best.v2))); + // const d = Vector.sub(path[path.length - 1], path[path.length - 2]); + // const nn = Vector.mult(n, 2 * Vector.dot(d, n)); + // const r = Vector.normalise(Vector.sub(d, nn)); + // path[path.length] = Vector.add(Vector.mult(r, range), path[path.length - 1]); + // }; + // //beam before reflection // checkForCollisions(); // if (best.dist2 != Infinity) { // //if hitting something @@ -2253,22 +2077,24 @@ const b = { // x: best.x, // y: best.y // }; - // laserHitMob(0.8); + // laserHitMob(1); - // //2nd reflection beam + // //1st reflection beam + // reflection(); // //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body - // if (who !== best.who) { - // reflection(); - // checkForCollisions(); - // if (best.dist2 != Infinity) { - // //if hitting something - // path[path.length - 1] = { - // x: best.x, - // y: best.y - // }; - // laserHitMob(0.63); - + // let who = best.who; + // checkForCollisions(); + // if (best.dist2 != Infinity) { + // //if hitting something + // path[path.length - 1] = { + // x: best.x, + // y: best.y + // }; + // laserHitMob(0.8); + // //2nd reflection beam + // //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body + // if (who !== best.who) { // reflection(); // checkForCollisions(); // if (best.dist2 != Infinity) { @@ -2277,152 +2103,49 @@ const b = { // x: best.x, // y: best.y // }; - // laserHitMob(0.5); + // laserHitMob(0.63); + + + // reflection(); + // checkForCollisions(); + // if (best.dist2 != Infinity) { + // //if hitting something + // path[path.length - 1] = { + // x: best.x, + // y: best.y + // }; + // laserHitMob(0.5); + // } // } // } // } // } - // } - // ctx.fillStyle = color; - // ctx.strokeStyle = color; - // ctx.lineWidth = 2; - // ctx.lineDashOffset = 300 * Math.random() - // // ctx.setLineDash([200 * Math.random(), 250 * Math.random()]); + // ctx.fillStyle = color; + // ctx.strokeStyle = color; + // ctx.lineWidth = 2; + // ctx.lineDashOffset = 300 * Math.random() + // // ctx.setLineDash([200 * Math.random(), 250 * Math.random()]); - // ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); - // for (let i = 1, len = path.length; i < len; ++i) { - // ctx.beginPath(); - // ctx.moveTo(path[i - 1].x, path[i - 1].y); - // ctx.lineTo(path[i].x, path[i].y); - // ctx.stroke(); - // ctx.globalAlpha *= 0.65; //reflections are less intense - // // ctx.globalAlpha -= 0.1; //reflections are less intense - // } - // ctx.setLineDash([0, 0]); - // ctx.globalAlpha = 1; - // } - // } - // }, - // { - // name: "pulse", //15 - // description: "power a laser that initiates a fusion explosion
each pulse drains 25% of your current energy", - // ammo: 0, - // ammoPack: Infinity, - // have: false, - // isStarterGun: true, - // fire() { - // //calculate laser collision - // let best; - // let range = 3000 - // const path = [{ - // x: mech.pos.x + 20 * Math.cos(mech.angle), - // y: mech.pos.y + 20 * Math.sin(mech.angle) - // }, - // { - // x: mech.pos.x + range * Math.cos(mech.angle), - // y: mech.pos.y + range * Math.sin(mech.angle) - // } - // ]; - // const vertexCollision = function (v1, v1End, domain) { - // for (let i = 0; i < domain.length; ++i) { - // let vertices = domain[i].vertices; - // const len = vertices.length - 1; - // for (let j = 0; j < len; j++) { - // results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); - // if (results.onLine1 && results.onLine2) { - // const dx = v1.x - results.x; - // const dy = v1.y - results.y; - // const dist2 = dx * dx + dy * dy; - // if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - // best = { - // x: results.x, - // y: results.y, - // dist2: dist2, - // who: domain[i], - // v1: vertices[j], - // v2: vertices[j + 1] - // }; - // } - // } - // } - // results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); - // if (results.onLine1 && results.onLine2) { - // const dx = v1.x - results.x; - // const dy = v1.y - results.y; - // const dist2 = dx * dx + dy * dy; - // if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - // best = { - // x: results.x, - // y: results.y, - // dist2: dist2, - // who: domain[i], - // v1: vertices[0], - // v2: vertices[len] - // }; - // } + // ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + // for (let i = 1, len = path.length; i < len; ++i) { + // ctx.beginPath(); + // ctx.moveTo(path[i - 1].x, path[i - 1].y); + // ctx.lineTo(path[i].x, path[i].y); + // ctx.stroke(); + // ctx.globalAlpha *= 0.65; //reflections are less intense + // // ctx.globalAlpha -= 0.1; //reflections are less intense // } + // ctx.setLineDash([0, 0]); + // ctx.globalAlpha = 1; // } - // }; - - // //check for collisions - // best = { - // x: null, - // y: null, - // dist2: Infinity, - // who: null, - // v1: null, - // v2: null - // }; - // vertexCollision(path[0], path[1], mob); - // vertexCollision(path[0], path[1], map); - // vertexCollision(path[0], path[1], body); - // if (best.dist2 != Infinity) { //if hitting something - // path[path.length - 1] = { - // x: best.x, - // y: best.y - // }; - // } - - // //use energy to explode - // const energy = mech.fieldMeter * 0.25 - // mech.fieldMeter -= energy - // if (best.who) b.explosion(path[1], 1300 * energy) - // mech.fireCDcycle = mech.cycle + Math.floor(60 * b.modFireRate); // cool down - - // //draw laser beam - // ctx.beginPath(); - // ctx.moveTo(path[0].x, path[0].y); - // ctx.lineTo(path[1].x, path[1].y); - // ctx.strokeStyle = "rgba(255,0,0,0.13)" - // ctx.lineWidth = 60 * energy / 0.2 - // ctx.stroke(); - // ctx.strokeStyle = "rgba(255,0,0,0.2)" - // ctx.lineWidth = 18 - // ctx.stroke(); - // ctx.strokeStyle = "#f00"; - // ctx.lineWidth = 4 - // ctx.stroke(); - - // //draw little dots along the laser path - // const sub = Vector.sub(path[1], path[0]) - // const mag = Vector.magnitude(sub) - // for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) { - // const dist = Math.random() - // game.drawList.push({ - // x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5), - // y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5), - // radius: 1 + 4 * Math.random(), - // color: "rgba(255,0,0,0.5)", - // time: Math.floor(2 + 33 * Math.random() * Math.random()) - // }); // } // } // }, { - name: "foam", //16 + name: "foam", //13 description: "spray bubbly foam that sticks to enemies
does damage over time and slows movement", ammo: 0, - ammoPack: 100, + ammoPack: 35, have: false, isStarterGun: true, fire() { @@ -2432,7 +2155,7 @@ const b = { const RADIUS = (8 + 16 * Math.random()) * b.modBulletSize bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 25, RADIUS, { angle: dir, - density: 0.00001, // 0.001 is normal density + density: 0.00004, // 0.001 is normal density inertia: Infinity, frictionAir: 0.003, friction: 0.2, @@ -2512,6 +2235,292 @@ const b = { }); } }, + { + name: "laser", //14 + description: "emit a beam of collimated coherent light
drains energy instead of ammunition", + ammo: 0, + ammoPack: Infinity, + have: false, + isStarterGun: true, + fire() { + const FIELD_DRAIN = 0.002 //laser drains energy as well as bullets + const damage = 0.05 + if (mech.fieldMeter < FIELD_DRAIN) { + mech.fireCDcycle = mech.cycle + 100; // cool down if out of energy + } else { + mech.fieldMeter -= mech.fieldRegen + FIELD_DRAIN + let best; + const color = "#f00"; + const range = 3000; + const path = [{ + x: mech.pos.x + 20 * Math.cos(mech.angle), + y: mech.pos.y + 20 * Math.sin(mech.angle) + }, + { + x: mech.pos.x + range * Math.cos(mech.angle), + y: mech.pos.y + range * Math.sin(mech.angle) + } + ]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + const checkForCollisions = function () { + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + vertexCollision(path[path.length - 2], path[path.length - 1], mob); + vertexCollision(path[path.length - 2], path[path.length - 1], map); + vertexCollision(path[path.length - 2], path[path.length - 1], body); + }; + const laserHitMob = function (dmg) { + if (best.who.alive) { + dmg *= b.dmgScale * damage; + best.who.damage(dmg); + best.who.locatePlayer(); + //draw mob damage circle + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(path[path.length - 1].x, path[path.length - 1].y, Math.sqrt(dmg) * 100, 0, 2 * Math.PI); + ctx.fill(); + } + }; + + const reflection = function () { + // https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector + const n = Vector.perp(Vector.normalise(Vector.sub(best.v1, best.v2))); + const d = Vector.sub(path[path.length - 1], path[path.length - 2]); + const nn = Vector.mult(n, 2 * Vector.dot(d, n)); + const r = Vector.normalise(Vector.sub(d, nn)); + path[path.length] = Vector.add(Vector.mult(r, range), path[path.length - 1]); + }; + //beam before reflection + checkForCollisions(); + if (best.dist2 != Infinity) { + //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + laserHitMob(1); + + //1st reflection beam + reflection(); + //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body + let who = best.who; + checkForCollisions(); + if (best.dist2 != Infinity) { + //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + laserHitMob(0.8); + + //2nd reflection beam + //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body + if (who !== best.who) { + reflection(); + checkForCollisions(); + if (best.dist2 != Infinity) { + //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + laserHitMob(0.63); + + + reflection(); + checkForCollisions(); + if (best.dist2 != Infinity) { + //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + laserHitMob(0.5); + } + } + } + } + } + ctx.fillStyle = color; + ctx.strokeStyle = color; + ctx.lineWidth = 2; + ctx.lineDashOffset = 300 * Math.random() + // ctx.setLineDash([200 * Math.random(), 250 * Math.random()]); + + ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + for (let i = 1, len = path.length; i < len; ++i) { + ctx.beginPath(); + ctx.moveTo(path[i - 1].x, path[i - 1].y); + ctx.lineTo(path[i].x, path[i].y); + ctx.stroke(); + ctx.globalAlpha *= 0.65; //reflections are less intense + // ctx.globalAlpha -= 0.1; //reflections are less intense + } + ctx.setLineDash([0, 0]); + ctx.globalAlpha = 1; + } + } + }, + { + name: "pulse", //15 + description: "pump a laser that initiates a fusion explosion
each pulse drains 25% of your current energy", + ammo: 0, + ammoPack: Infinity, + have: false, + isStarterGun: true, + fire() { + //calculate laser collision + let best; + let range = 3000 + const path = [{ + x: mech.pos.x + 20 * Math.cos(mech.angle), + y: mech.pos.y + 20 * Math.sin(mech.angle) + }, + { + x: mech.pos.x + range * Math.cos(mech.angle), + y: mech.pos.y + range * Math.sin(mech.angle) + } + ]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + + //check for collisions + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + vertexCollision(path[0], path[1], mob); + vertexCollision(path[0], path[1], map); + vertexCollision(path[0], path[1], body); + if (best.dist2 != Infinity) { //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + } + + //use energy to explode + const energy = 0.25 * Math.min(mech.fieldMeter, 1.5) + mech.fieldMeter -= energy + if (best.who) b.explosion(path[1], 1200 * energy) + mech.fireCDcycle = mech.cycle + Math.floor(60 * b.modFireRate); // cool down + + //draw laser beam + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + ctx.strokeStyle = "rgba(255,0,0,0.13)" + ctx.lineWidth = 60 * energy / 0.2 + ctx.stroke(); + ctx.strokeStyle = "rgba(255,0,0,0.2)" + ctx.lineWidth = 18 + ctx.stroke(); + ctx.strokeStyle = "#f00"; + ctx.lineWidth = 4 + ctx.stroke(); + + //draw little dots along the laser path + const sub = Vector.sub(path[1], path[0]) + const mag = Vector.magnitude(sub) + for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) { + const dist = Math.random() + game.drawList.push({ + x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5), + y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5), + radius: 1 + 4 * Math.random(), + color: "rgba(255,0,0,0.5)", + time: Math.floor(2 + 33 * Math.random() * Math.random()) + }); + } + } + }, // { // name: "dwarf star", //14 // description: "drop a mine that gravitational pulls in matter", diff --git a/js/engine.js b/js/engine.js index a19c4b3..40cfbe5 100644 --- a/js/engine.js +++ b/js/engine.js @@ -79,7 +79,7 @@ function mobCollisionChecks(event) { for (let i = 0, j = pairs.length; i != j; i++) { //body + player collision - if (game.isBodyDamage && mech.damageImmune < mech.cycle) { + if (game.isBodyDamage) { if (pairs[i].bodyA === playerBody || pairs[i].bodyA === playerHead) { collidePlayer(pairs[i].bodyB) } else if (pairs[i].bodyB === playerBody || pairs[i].bodyB === playerHead) { @@ -91,7 +91,6 @@ function mobCollisionChecks(event) { if (obj.classType === "body" && obj.speed > speedThreshold && obj.mass > massThreshold) { //dmg from hitting a body const v = Vector.magnitude(Vector.sub(player.velocity, obj.velocity)); if (v > speedThreshold) { - mech.damageImmune = mech.cycle + 30; //player is immune to collision damage for 30 cycles let dmg = Math.sqrt((v - speedThreshold + 0.1) * (obj.mass - massThreshold)) * 0.01; dmg = Math.min(Math.max(dmg, 0.02), 0.15); mech.damage(dmg); @@ -105,21 +104,6 @@ function mobCollisionChecks(event) { return; } } - // else if (obj.isStatic && player.speed > speedThreshold * 2) { //falling dmg / hitting map dmg - // mech.damageImmune = mech.cycle + 30; - // console.log(player.speed) - // let dmg = Math.min(Math.max(player.speed * 0.001, 0.02), 0.2); - // 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 - // }); - // return; - // } } //mob + (player,bullet,body) collisions @@ -135,9 +119,9 @@ function mobCollisionChecks(event) { function collideMob(obj) { //player + mob collision - if (obj === playerBody || obj === playerHead) { - if (mech.damageImmune < mech.cycle) { - mech.damageImmune = mech.cycle + 30; //player is immune to collision damage for 30 cycles + if (mech.collisionImmune < mech.cycle) { + if (obj === playerBody || obj === playerHead) { + mech.collisionImmune = mech.cycle + b.modCollisionImmuneCycles; //player is immune to collision damage for 30 cycles mob[k].foundPlayer(); let dmg = Math.min(Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05), 0.3) * game.dmgScale; //player damage is capped at 0.3*dmgScale of 1.0 mech.damage(dmg); @@ -163,23 +147,24 @@ function mobCollisionChecks(event) { }); } + //extra kick between player and mob //this section would be better with forces but they don't work... + let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x); + Matter.Body.setVelocity(player, { + x: player.velocity.x + 8 * Math.cos(angle), + y: player.velocity.y + 8 * Math.sin(angle) + }); + Matter.Body.setVelocity(mob[k], { + x: mob[k].velocity.x - 8 * Math.cos(angle), + y: mob[k].velocity.y - 8 * Math.sin(angle) + }); + return; } - //extra kick between player and mob //this section would be better with forces but they don't work... - let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x); - Matter.Body.setVelocity(player, { - x: player.velocity.x + 8 * Math.cos(angle), - y: player.velocity.y + 8 * Math.sin(angle) - }); - Matter.Body.setVelocity(mob[k], { - x: mob[k].velocity.x - 8 * Math.cos(angle), - y: mob[k].velocity.y - 8 * Math.sin(angle) - }); - return; } //mob + bullet collisions if (obj.classType === "bullet" && obj.speed > obj.minDmgSpeed) { // const dmg = b.dmgScale * (obj.dmg + 0.15 * obj.mass * Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity))); let dmg = b.dmgScale * (obj.dmg + b.modExtraDmg + 0.15 * obj.mass * Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity))) + if (mob[k].shield) dmg *= 0.3 if (b.isModCrit && !mob[k].seePlayer.recall) dmg *= 5 mob[k].foundPlayer(); mob[k].damage(dmg); diff --git a/js/index.js b/js/index.js index 6c9acdc..82515cd 100644 --- a/js/index.js +++ b/js/index.js @@ -2,11 +2,25 @@ /* TODO: ******************************************* ***************************************************** +mod: fields do damage on blocking + name: something about radiation? + +mod: do something at the end of each level + heal to full + should still be effected by the heal reduction at higher difficulty + give ammo to current gun + give goals/quests for each level + how to track goals? + take no damage + don't shoot + selection options for field to stay with current custom mode grey out mods that are bad, like selection based mods not important +custom mode can't use recursive mods + field graphics too dark at high energy not important @@ -19,9 +33,10 @@ game setting for slower computers fewer debris fewer mobs -mod: guardian - recursive - spawns at start of level +mod: gain energy after taking damage + use piezoelectric name (already used) + +mod: ground stomp on enterLand() mod: if you fire when out of ammo you gain 1 ammo pack at the cost of 10% max health @@ -153,6 +168,7 @@ const build = { list: [], choosePowerUp(who, index, type) { //check if matching a current power up + // if (type === "field" || type === "gun") { for (let i = 0; i < build.list.length; i++) { if (build.list[i].index === index && build.list[i].type === type) { //if already click, toggle off build.list.splice(i, 1); @@ -160,7 +176,6 @@ const build = { return } } - //check if trying to get a second field if (type === "field") { for (let i = 0; i < build.list.length; i++) { @@ -170,15 +185,47 @@ const build = { } } } + // } else { //type is mod + + // //count each mod type to check for recursion caps + // let counts = [] + // for (let i = 0; i < build.list.length; i++) { + // if (build.list[i].type === "mod") { + // if (!counts[build.list[i].index]) { + // counts[build.list[i].index] = 1 + // } else { + // counts[build.list[i].index] = counts[build.list[i].index] + 1 + // } + // } + // } + + // for (let i = 0; i < build.list.length; i++) { + // //if above max count, toggle off + // if (build.list[i].index === index && build.list[i].type === type && + // counts[index] >= b.mods[index].maxCount) { + // //remove all versions of mod + + + // who.style.backgroundColor = "#fff" + // return + // } + // } + // } if (build.list.length < 5) { //add to build array who.style.backgroundColor = "#919ba8" //"#868f9a" build.list[build.list.length] = { who: who, index: index, - type: type + type: type, } } + console.log(build.list) + }, + removeMod(index) { + for (let i = build.list.length - 1; i > -1; i--) { + if (build.list[i].type === "mod" && build.list[i].index === index) build.list.splice(i, 1); + } }, startBuildRun() { spawn.setSpawnList(); diff --git a/js/level.js b/js/level.js index cea3deb..c7d0c3a 100644 --- a/js/level.js +++ b/js/level.js @@ -14,9 +14,9 @@ const level = { start() { if (level.levelsCleared === 0) { // game.difficulty = 6; //for testing to simulate possible mobs spawns - // b.giveGuns(0) - // mech.setField(2) - // b.giveMod(20) + // b.giveGuns(15) + // mech.setField(3) + // b.giveMod(10); level.intro(); //starting level // level.testingMap(); @@ -114,7 +114,7 @@ const level = { // spawn.lineBoss(-500, -600, spawn.allowedBossList[Math.floor(Math.random() * spawn.allowedBossList.length)]); // spawn.bodyRect(-135, -50, 50, 50); // spawn.bodyRect(-140, -100, 50, 50); - powerUps.spawn(420, -400, "field", false); + powerUps.spawn(420, -400, "gun", false); // powerUps.spawn(420, -400, "field", false); // powerUps.spawn(420, -400, "field", false); // powerUps.spawn(420, -400, "field", false); diff --git a/js/mobs.js b/js/mobs.js index dfe9319..3d3f034 100644 --- a/js/mobs.js +++ b/js/mobs.js @@ -961,7 +961,7 @@ const mobs = { if (this.dropPowerUp) { powerUps.spawnRandomPowerUp(this.position.x, this.position.y, this.mass, radius); if (Math.random() < b.modSpores) { - for (let i = 0, len = Math.floor(3 + this.mass * Math.random()); i < len; i++) { + for (let i = 0, len = Math.floor(4 + this.mass * Math.random()); i < len; i++) { b.spore(this) //spawn drone } } diff --git a/js/player.js b/js/player.js index 2a8aa13..69ab13b 100644 --- a/js/player.js +++ b/js/player.js @@ -55,7 +55,7 @@ const mech = { cycle: 0, width: 50, radius: 30, - fillColor: "#fff", + fillColor: "#fff", //changed by mod piezoelectric plating (damage immunity) fillColorDark: "#ccc", height: 42, yOffWhen: { @@ -207,7 +207,6 @@ const mech = { mech.hardLandCD = mech.cycle + Math.min(momentum / 6 - 6, 40) if (game.isBodyDamage && player.velocity.y > 26 && momentum > 165 * b.modSquirrelFx) { //falling damage - mech.damageImmune = mech.cycle + 30; //player is immune to collision damage for 30 cycles let dmg = Math.sqrt(momentum - 165) * 0.01 dmg = Math.min(Math.max(dmg, 0.02), 0.20); mech.damage(dmg); @@ -440,6 +439,7 @@ const mech = { mech.displayHealth(); }, defaultFPSCycle: 0, //tracks when to return to normal fps + collisionImmune: 0, //used in engine damage(dmg) { if (b.isModEntanglement && b.inventory[0] === b.activeGun) { for (let i = 0, len = b.inventory.length; i < len; i++) { @@ -513,7 +513,6 @@ const mech = { // }; // requestAnimationFrame(normalFPS); }, - damageImmune: 0, hitMob(i, dmg) { //prevents damage happening too quick }, @@ -588,11 +587,13 @@ const mech = { mech.knee.y = (l / d) * (mech.foot.y - mech.hip.y) + (h / d) * (mech.foot.x - mech.hip.x) + mech.hip.y; }, draw() { + // mech.fillColor = (mech.collisionImmune < mech.cycle) ? "#fff" : "rgba(255,255,255,0.1)" //"#cff" ctx.fillStyle = mech.fillColor; mech.walk_cycle += mech.flipLegs * mech.Vx; //draw body ctx.save(); + ctx.globalAlpha = (mech.collisionImmune < mech.cycle) ? 1 : 0.7 ctx.translate(mech.pos.x, mech.pos.y); mech.calcLeg(Math.PI, -3); mech.drawLeg("#4a4a4a"); @@ -654,8 +655,10 @@ const mech = { mech.fieldCDcycle = 0; mech.isStealth = false; player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield - mech.holdingMassScale = 0.5; mech.fieldShieldingScale = 1; //scale energy loss after collision with mob + mech.holdingMassScale = 0.5; + mech.throwChargeRate = 2; + mech.throwChargeMax = 50; mech.grabRange = 175; mech.fieldArc = 0.2; //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) mech.calculateFieldThreshold(); //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) @@ -1311,9 +1314,12 @@ const mech = { }, { name: "negative mass field", - description: "use energy to nullify   gravity
can fire bullets while active", + description: "use energy to nullify   gravity
launch larger blocks at much higher speeds", effect: () => { mech.fieldFire = true; + mech.throwChargeRate = 4; + mech.throwChargeMax = 150; + mech.holdingMassScale = 0.05; //can hold heavier blocks with lower cost to jumping mech.hold = function () { if (mech.isHolding) { @@ -1321,11 +1327,11 @@ const mech = { mech.holding(); mech.throwBlock(); } else if ((keys[32] || game.mouseDownRight) && mech.fieldCDcycle < mech.cycle) { //push away - const DRAIN = 0.0004 + const DRAIN = 0.00025 if (mech.fieldMeter > DRAIN) { mech.grabPowerUp(); - mech.lookForPickUp(170); - mech.pushMobs360(200); + mech.lookForPickUp(150); + mech.pushMobs360(150); //look for nearby objects to make zero-g function zeroG(who, mag = 1.06) { for (let i = 0, len = who.length; i < len; ++i) { @@ -1339,22 +1345,17 @@ const mech = { // zeroG(bullet); //works fine, but not that noticeable and maybe not worth the possible performance hit // zeroG(mob); //mobs are too irregular to make this work? - Matter.Body.setVelocity(player, { - x: player.velocity.x, - y: player.velocity.y * 0.97 - }); - if (keys[83] || keys[40]) { //down - player.force.y -= 0.8 * player.mass * mech.gravity; + player.force.y -= 0.5 * player.mass * mech.gravity; mech.grabRange = mech.grabRange * 0.97 + 400 * 0.03; - zeroG(powerUp, 0.85); - zeroG(body, 0.85); + zeroG(powerUp, 0.7); + zeroG(body, 0.7); } else if (keys[87] || keys[38]) { //up mech.fieldMeter -= 5 * DRAIN; - mech.grabRange = mech.grabRange * 0.97 + 750 * 0.03; - player.force.y -= 1.2 * player.mass * mech.gravity; - zeroG(powerUp, 1.13); - zeroG(body, 1.13); + mech.grabRange = mech.grabRange * 0.97 + 850 * 0.03; + player.force.y -= 1.45 * player.mass * mech.gravity; + zeroG(powerUp, 1.38); + zeroG(body, 1.38); } else { mech.fieldMeter -= DRAIN; mech.grabRange = mech.grabRange * 0.97 + 650 * 0.03; @@ -1366,8 +1367,13 @@ const mech = { //add extra friction for horizontal motion if (keys[65] || keys[68] || keys[37] || keys[39]) { Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.85, - y: player.velocity.y + x: player.velocity.x * 0.95, + y: player.velocity.y * 0.97 + }); + } else { //slow rise and fall + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y * 0.97 }); } @@ -1443,7 +1449,7 @@ const mech = { mech.hold = function () { if (mech.fieldMeter > mech.fieldEnergyMax - 0.02 && mech.fieldCDcycle < mech.cycle) { mech.fieldCDcycle = mech.cycle + 17; // set cool down to prevent +energy from making huge numbers of drones - mech.fieldMeter -= 0.32; + mech.fieldMeter -= 0.35; b.drone(1) } if (mech.isHolding) { diff --git a/js/powerups.js b/js/powerups.js index 7c7bc24..09cfeac 100644 --- a/js/powerups.js +++ b/js/powerups.js @@ -22,6 +22,14 @@ const powerUps = { game.isChoosing = false; //stops p from un pausing on key down requestAnimationFrame(cycle); }, + cancel() { + document.body.style.cursor = "none"; + document.getElementById("choose-grid").style.display = "none" + document.getElementById("choose-background").style.display = "none" + game.paused = false; + game.isChoosing = false; //stops p from un pausing on key down + requestAnimationFrame(cycle); + }, showDraft() { document.getElementById("choose-grid").style.display = "grid" document.getElementById("choose-background").style.display = "inline" @@ -36,11 +44,10 @@ const powerUps = { return 40 * Math.sqrt(0.1 + Math.random() * 0.5); }, effect() { - let heal = ((this.size / 40) ** 2) - heal = Math.min(mech.maxHealth - mech.health, heal) - if (b.isModRecursiveHealing) heal *= 2 + let heal = 0 + for (let i = 0; i < b.modRecursiveHealing; i++) heal += ((this.size / 40) ** 2) + if (heal > 0) game.makeTextLog("
  heal " + (Math.min(mech.maxHealth - mech.health, heal) * game.healScale * 100).toFixed(0) + "%", 300) mech.addHealth(heal); - if (heal > 0) game.makeTextLog("
  heal " + (heal * game.healScale * 100).toFixed(0) + "%", 300) } }, ammo: { @@ -72,8 +79,7 @@ const powerUps = { mech.fieldMeter = mech.fieldEnergyMax; if (!game.lastLogTime) game.makeTextLog("+energy", 300); } else { - //ammo given scales as mobs take more hits to kill - let ammo = Math.ceil((target.ammoPack * (0.4 + 0.05 * Math.random()))); + let ammo = Math.ceil((target.ammoPack * (1 + 0.05 * Math.random()))); if (level.isBuildRun) ammo = Math.floor(ammo * 1.1) //extra ammo on build run because no ammo from getting a new gun target.ammo += ammo; game.updateGunHUD(); @@ -100,7 +106,7 @@ const powerUps = { let choice2 = doNotHave(mech.fieldUpgrades, choice1) let choice3 = -1 if (choice1 > -1) { - let text = `

choose a field

` + let text = `

choose a field

` text += `
  ${mech.fieldUpgrades[choice1].name}
${mech.fieldUpgrades[choice1].description}
` if (choice2 > -1) text += `
  ${mech.fieldUpgrades[choice2].name}
${mech.fieldUpgrades[choice2].description}
` if (!b.isModBayesian) { @@ -138,7 +144,7 @@ const powerUps = { let choice2 = doNotHave(b.mods, choice1) let choice3 = -1 if (choice1 > -1) { - let text = "

choose a mod

" + let text = "

choose a mod

" text += `
  ${b.mods[choice1].name}
${b.mods[choice1].description}
` if (choice2 > -1) text += `
  ${b.mods[choice2].name}
${b.mods[choice2].description}
` if (!b.isModBayesian) { @@ -175,7 +181,7 @@ const powerUps = { let choice2 = doNotHave(b.guns, choice1) let choice3 = -1 if (choice1 > -1) { - let text = "

choose a gun

" + let text = "

choose a gun

" text += `
  ${b.guns[choice1].name}
${b.guns[choice1].description}
` if (choice2 > -1) text += `
  ${b.guns[choice2].name}
${b.guns[choice2].description}
` if (!b.isModBayesian) { @@ -195,7 +201,7 @@ const powerUps = { }, giveRandomAmmo() { const ammoTarget = Math.floor(Math.random() * (b.guns.length)); - const ammo = Math.ceil(b.guns[ammoTarget].ammoPack * 2); + const ammo = Math.ceil(b.guns[ammoTarget].ammoPack * 6); b.guns[ammoTarget].ammo += ammo; game.updateGunHUD(); game.makeTextLog("+" + ammo + " ammo for " + b.guns[ammoTarget].name + "", 300); @@ -206,17 +212,17 @@ const powerUps = { if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "heal"); return; } - if (Math.random() < 0.2 && b.inventory.length > 0) { + if (Math.random() < 0.15 && b.inventory.length > 0) { powerUps.spawn(x, y, "ammo"); if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "ammo"); return; } - if (Math.random() < 0.004 * (4 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun up to 4 + if (Math.random() < 0.0035 * (3 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun up to 4 powerUps.spawn(x, y, "gun"); if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "gun"); return; } - if (Math.random() < 0.0035 * (9 - b.modCount)) { //a new mod has a low chance for each not acquired mod up to 7 + if (Math.random() < 0.0031 * (10 - b.modCount)) { //a new mod has a low chance for each not acquired mod up to 7 powerUps.spawn(x, y, "mod"); if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "mod"); return; @@ -231,28 +237,28 @@ const powerUps = { if (mech.fieldMode === 0) { powerUps.spawn(x, y, "field") if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "field") - } else if (Math.random() < 0.55) { + } else if (Math.random() < 0.6) { powerUps.spawn(x, y, "mod") if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "mod") - } else if (Math.random() < 0.2) { + } else if (Math.random() < 0.1) { powerUps.spawn(x, y, "gun") if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "gun") } else if (Math.random() < 0.1) { powerUps.spawn(x, y, "field"); if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "field"); } else if (mech.health < 0.65) { + powerUps.spawn(x, y, "heal"); powerUps.spawn(x, y, "heal"); powerUps.spawn(x, y, "heal"); if (Math.random() < b.isModBayesian) { powerUps.spawn(x, y, "heal"); - powerUps.spawn(x, y, "heal"); } } else { + powerUps.spawn(x, y, "ammo"); powerUps.spawn(x, y, "ammo"); powerUps.spawn(x, y, "ammo"); if (Math.random() < b.isModBayesian) { powerUps.spawn(x, y, "ammo"); - powerUps.spawn(x, y, "ammo"); } } }, diff --git a/js/spawn.js b/js/spawn.js index b054f49..161fd16 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -233,7 +233,7 @@ const spawn = { me.accelMag = 0.001 * game.accelScale;; me.g = me.accelMag * 0.6; //required if using 'gravity' me.memory = 50; - if (Math.random() < Math.min((game.difficulty - 1) * 0.07, 0.5)) spawn.shield(me, x, y); + if (Math.random() < Math.min((game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); me.do = function () { this.gravity(); this.seePlayerCheck(); @@ -293,7 +293,7 @@ const spawn = { me.onDeath = function () { this.removeCons(); }; - if (Math.random() < Math.min((game.difficulty - 1) * 0.07, 0.5)) spawn.shield(me, x, y); + if (Math.random() < Math.min((game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); me.do = function () { this.gravity(); this.searchSpring(); @@ -600,7 +600,7 @@ const spawn = { me.accelMag = 0.0005 * game.accelScale; me.frictionStatic = 0; me.friction = 0; - if (Math.random() < Math.min(0.2 + (game.difficulty - 1) * 0.07, 0.5)) spawn.shield(me, x, y); + if (Math.random() < Math.min(0.2 + (game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); me.do = function () { this.seePlayerByLookingAt(); this.attraction(); @@ -900,7 +900,7 @@ const spawn = { x: 0, y: 0 }; - if (Math.random() < Math.min(0.15 + (game.difficulty - 1) * 0.07, 0.5)) spawn.shield(me, x, y); + if (Math.random() < Math.min(0.15 + (game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); me.do = function () { this.seePlayerByLookingAt(); this.fire(); @@ -970,7 +970,7 @@ const spawn = { }); } }; - if (Math.random() < Math.min((game.difficulty - 1) * 0.05, 0.4)) spawn.shield(me, x, y); + if (Math.random() < Math.min((game.difficulty - 1) * 0.04, 0.2)) spawn.shield(me, x, y); me.do = function () { this.gravity(); this.seePlayerCheck(); @@ -1019,7 +1019,7 @@ const spawn = { me.laserRange = 500; Matter.Body.setDensity(me, 0.001 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger spawn.shield(me, x, y); - if (Math.random() < Math.min((game.difficulty - 1) * 0.07, 0.5)) spawn.shield(me, x, y); + if (Math.random() < Math.min((game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; @@ -1060,7 +1060,7 @@ const spawn = { me.memory = 20; Matter.Body.setDensity(me, 0.001 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger spawn.shield(me, x, y); - if (Math.random() < Math.min((game.difficulty - 1) * 0.07, 0.5)) spawn.shield(me, x, y); + if (Math.random() < Math.min((game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) diff --git a/style.css b/style.css index 18b1c14..173b06c 100644 --- a/style.css +++ b/style.css @@ -100,6 +100,18 @@ summary { background-color: #efeff5; } +.cancel { + /* text-align: right; */ + position: absolute; + top: 15px; + right: 15px; + color: #fff; +} + +.cancel:hover { + color: #aaa; +} + #build-grid { padding: 16px; margin: 0px;