From 42b2cde9a2e45be4003a1e13f18c5d8c1e60d7a7 Mon Sep 17 00:00:00 2001 From: landgreen Date: Fri, 3 Jan 2020 09:29:25 -0800 Subject: [PATCH] shields are shields are more rare, but much stronger, negative mass field is combined with Gauss rifle mod, negative mass field moves player faster, new mod piezoelectric plating,You may now choose to cancel the power up selection screen. (no recursive mods in custon yet, it's an annoying UI rewrite) --- js/bullets.js | 1115 ++++++++++++++++++++++++------------------------ js/engine.js | 47 +- js/index.js | 57 ++- js/level.js | 8 +- js/mobs.js | 2 +- js/player.js | 52 ++- js/powerups.js | 40 +- js/spawn.js | 14 +- style.css | 12 + 9 files changed, 706 insertions(+), 641 deletions(-) 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;