From 12ab87ea1d790044b39db960c702a562797f2e2e Mon Sep 17 00:00:00 2001 From: landgreen Date: Wed, 27 Nov 2019 08:54:39 -0800 Subject: [PATCH] replace slugs with rail gun, rebalanced flechettes, added falling dmg, new mod: fast movement, added mouse icons --- js/bullets.js | 2057 ++++++++++++++++++++++++++---------------------- js/engine.js | 13 +- js/game.js | 5 + js/index.js | 21 + js/level.js | 6 +- js/mobs.js | 2 +- js/player.js | 121 +-- js/powerups.js | 10 +- js/spawn.js | 10 +- style.css | 56 +- 10 files changed, 1222 insertions(+), 1079 deletions(-) diff --git a/js/bullets.js b/js/bullets.js index c71690b..bdcbeb2 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -11,30 +11,32 @@ const b = { modEnergySiphon: null, modHealthDrain: null, modNoAmmo: null, - modBulletsLastLonger: null, + isModBulletsLastLonger: null, modIsImmortal: null, modSpores: null, - AoEImmunity: null, - makeDroneOnDamage: null, - extraDmg: null, + isModAoEImmunity: null, + isModDroneOnDamage: null, + modExtraDmg: null, annihilation: null, fullHeal: null, setModDefaults() { b.modCount = 0; b.modFireRate = 1; b.modExplosionRadius = 1; - b.AoEImmunity = false; + b.isModAoEImmunity = false; b.modBulletSize = 1; - b.makeDroneOnDamage = false; + b.isModDroneOnDamage = false; b.modEnergySiphon = 0; b.modHealthDrain = 0; b.modNoAmmo = 0; - b.modBulletsLastLonger = 1; + b.isModBulletsLastLonger = 1; b.modIsImmortal = false; b.modSpores = 0; - b.extraDmg = 0; - b.annihilation = false; - b.fullHeal = false; + b.modExtraDmg = 0; + b.modAnnihilation = false; + b.isModFullHeal = false; + mech.Fx = 0.015; + mech.jumpForce = 0.38; mech.throwChargeRate = 2; mech.throwChargeMax = 50; for (let i = 0; i < b.mods.length; i++) { @@ -72,7 +74,7 @@ const b = { description: "your bullets last 40% longer", have: false, //3 effect: () => { //good with: drones, super balls, spore, missiles, wave beam(range), rapid fire(range), flak(range) - b.modBulletsLastLonger = 1.40 + b.isModBulletsLastLonger = 1.40 } }, { @@ -89,7 +91,7 @@ const b = { description: "you take no damage from area effects
immune to explosions and enemy fields", have: false, //5 effect: () => { - b.AoEImmunity = true; //good for guns with explosions + b.isModAoEImmunity = true; //good for guns with explosions } }, { @@ -97,7 +99,7 @@ const b = { description: "after taking damage, there is a chance that your damaged parts will be rebuilt as drones", have: false, //6 effect: () => { //makes dangerous situations more survivable - b.makeDroneOnDamage = true; + b.isModDroneOnDamage = true; } }, { @@ -138,7 +140,7 @@ const b = { description: "your bullets do extra chemical damage each time they make contact", have: false, //11 effect: () => { //good with guns that fire many bullets at low speeds, minigun, drones, junk-bots, shotgun, superballs, wavebeam - b.extraDmg = 0.1 + b.modExtraDmg = 0.1 } }, { @@ -146,7 +148,7 @@ const b = { description: "after you touch any enemy, they are annihilated
touching enemies damages you, but destroys them", have: false, //12 effect: () => { //good with mods that heal: superconductive healing, entropy transfer - b.annihilation = true + b.modAnnihilation = true } }, { @@ -154,19 +156,30 @@ const b = { description: "heals bring you to full health", have: false, //13 effect: () => { // good with ablative synthesis, electrostatic field - b.fullHeal = true + b.isModFullHeal = true } }, { - name: "superconductive rail gun", - description: "throw blocks at very high speeds
to charge a throw, hold right click while holding a block
release right click to fire
", + name: "Gauss rifle", + description: "magnetically launch blocks at much higher speeds
carry more massive blocks
hold right click to charge up a throw and release to fire", have: false, //14 effect: () => { // good with ablative synthesis, electrostatic field - b.fullHeal = true + b.isModFullHeal = true 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", + have: false, //15 + effect: () => { // + mech.Fx = 0.015 * 1.2; + mech.jumpForce = 0.38 * 1.1; + } + }, + ], giveMod(i) { b.mods[i].effect(); //give specific mod @@ -263,7 +276,7 @@ const b = { angle: dir, friction: 0.5, frictionAir: 0, - dmg: b.extraDmg, //damage done in addition to the damage from momentum + dmg: b.modExtraDmg, //damage done in addition to the damage from momentum classType: "bullet", collisionFilter: { category: 0x000100, @@ -351,7 +364,7 @@ const b = { sub = Matter.Vector.sub(bullet[me].position, player.position); dist = Matter.Vector.magnitude(sub); if (dist < radius) { - if (!b.AoEImmunity) mech.damage(radius * 0.0002); + if (!b.isModAoEImmunity) mech.damage(radius * 0.0002); knock = Matter.Vector.mult(Matter.Vector.normalise(sub), -Math.sqrt(dmg) * player.mass / 30); player.force.x += knock.x; player.force.y += knock.y; @@ -433,7 +446,7 @@ const b = { category: 0x000100, mask: 0x000011 //no collide with body }, - endCycle: game.cycle + Math.floor((360 + Math.floor(Math.random() * 240)) * b.modBulletsLastLonger), + endCycle: game.cycle + Math.floor((360 + Math.floor(Math.random() * 240)) * b.isModBulletsLastLonger), minDmgSpeed: 0, onDmg() { this.endCycle = 0; //bullet ends cycle after doing damage @@ -479,57 +492,40 @@ const b = { World.add(engine.world, bullet[bIndex]); //add bullet to world }, guns: [{ - name: "laser", //0 - description: "fire a beam of coherent light
reflects off walls at 75% intensity
uses energy instead of ammunition", - ammo: 0, - // ammoPack: 350, - ammoPack: Infinity, - have: false, - isStarterGun: true, - fire() { - // mech.fireCDcycle = mech.cycle + 1 - //laser drains energy as well as bullets - const FIELD_DRAIN = 0.002 - 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]); + name: "laser", //0 + description: "fire a beam of coherent light
reflects off walls at 75% intensity
uses energy instead of ammunition", + ammo: 0, + // ammoPack: 350, + ammoPack: Infinity, + have: false, + isStarterGun: true, + fire() { + // mech.fireCDcycle = mech.cycle + 1 + //laser drains energy as well as bullets + const FIELD_DRAIN = 0.002 + 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; @@ -540,48 +536,78 @@ const b = { y: results.y, dist2: dist2, who: domain[i], - v1: vertices[0], - v2: vertices[len] + v1: vertices[j], + v2: vertices[j + 1] }; } } } - }; - 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(); + 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 = Matter.Vector.perp(Matter.Vector.normalise(Matter.Vector.sub(best.v1, best.v2))); - const d = Matter.Vector.sub(path[path.length - 1], path[path.length - 2]); - const nn = Matter.Vector.mult(n, 2 * Matter.Vector.dot(d, n)); - const r = Matter.Vector.normalise(Matter.Vector.sub(d, nn)); - path[path.length] = Matter.Vector.add(Matter.Vector.mult(r, range), path[path.length - 1]); + const reflection = function () { + // https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector + const n = Matter.Vector.perp(Matter.Vector.normalise(Matter.Vector.sub(best.v1, best.v2))); + const d = Matter.Vector.sub(path[path.length - 1], path[path.length - 2]); + const nn = Matter.Vector.mult(n, 2 * Matter.Vector.dot(d, n)); + const r = Matter.Vector.normalise(Matter.Vector.sub(d, nn)); + path[path.length] = Matter.Vector.add(Matter.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 }; - //beam before reflection + 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 @@ -589,879 +615,1062 @@ const b = { x: best.x, y: best.y }; - laserHitMob(1); + laserHitMob(0.75); - //1st reflection beam - reflection(); + //2nd reflection beam //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.75); - - //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.5); - } + 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.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.5; //reflections are less intense - // ctx.globalAlpha -= 0.1; //reflections are less intense - } - ctx.setLineDash([0, 0]); - ctx.globalAlpha = 1; } - } - }, { - name: "kinetic slugs", //1 - description: "fire a large rod that does excessive physical damage
high recoil", - ammo: 0, - ammoPack: 5, - have: false, - isStarterGun: true, - fire() { - b.muzzleFlash(45); - // mobs.alert(800); - const me = bullet.length; - const dir = mech.angle; - bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), 70 * b.modBulletSize, 30 * b.modBulletSize, b.fireAttributes(dir)); - b.fireProps(mech.crouch ? 55 : 40, 50, dir, me); //cd , speed - bullet[me].endCycle = game.cycle + Math.floor(180 * b.modBulletsLastLonger); - bullet[me].do = function () { - this.force.y += this.mass * 0.0005; - }; + ctx.fillStyle = color; + ctx.strokeStyle = color; + ctx.lineWidth = 2; + ctx.lineDashOffset = 300 * Math.random() + // ctx.setLineDash([200 * Math.random(), 250 * Math.random()]); - //knock back - const KNOCK = ((mech.crouch) ? 0.025 : 0.25) * b.modBulletSize * b.modBulletSize - player.force.x -= KNOCK * Math.cos(dir) - player.force.y -= KNOCK * Math.sin(dir) * 0.4 //reduce knock back in vertical direction to stop super jumps + 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.5; //reflections are less intense + // ctx.globalAlpha -= 0.1; //reflections are less intense + } + ctx.setLineDash([0, 0]); + ctx.globalAlpha = 1; } - }, - { - name: "minigun", //2 - description: "rapidly fire a stream of small bullets", - ammo: 0, - ammoPack: 105, - have: false, - isStarterGun: true, - fire() { - const me = bullet.length; - b.muzzleFlash(15); - // if (Math.random() > 0.2) mobs.alert(500); - const dir = mech.angle + (Math.random() - 0.5) * ((mech.crouch) ? 0.03 : 0.14); - bullet[me] = Bodies.rectangle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 17 * b.modBulletSize, 5 * b.modBulletSize, b.fireAttributes(dir)); - b.fireProps(mech.crouch ? 11 : 5, mech.crouch ? 44 : 36, dir, me); //cd , speed - bullet[me].endCycle = game.cycle + Math.floor(65 * b.modBulletsLastLonger); - bullet[me].frictionAir = mech.crouch ? 0.007 : 0.01; - bullet[me].do = function () { - this.force.y += this.mass * 0.0005; - }; - } - }, - { - name: "wave beam", //3 - description: "fire a stream of oscillating particles
propagates through solids", - ammo: 0, - ammoPack: 85, - have: false, - isStarterGun: true, - fire() { - const me = bullet.length; - const DIR = mech.angle - const SCALE = (mech.crouch ? 0.963 : 0.95) - const wiggleMag = ((mech.flipLegs === 1) ? 1 : -1) * ((mech.crouch) ? 0.004 : 0.005) - bullet[me] = Bodies.circle(mech.pos.x + 25 * Math.cos(DIR), mech.pos.y + 25 * Math.sin(DIR), 10 * b.modBulletSize, { - angle: DIR, - cycle: -0.43, //adjust this number until the bullets line up with the cross hairs - endCycle: game.cycle + Math.floor((mech.crouch ? 155 : 120) * b.modBulletsLastLonger), - inertia: Infinity, - frictionAir: 0, - minDmgSpeed: 0, - dmg: 0.13 + b.extraDmg, //damage done in addition to the damage from momentum - classType: "bullet", - collisionFilter: { - category: 0x000100, - mask: 0x000010 - }, - onDmg() {}, - onEnd() {}, - do() { - if (!mech.isBodiesAsleep) { - this.cycle++ - const THRUST = wiggleMag * Math.cos(this.cycle * 0.3) - this.force = Matter.Vector.mult(Matter.Vector.normalise(this.direction), this.mass * THRUST) //wiggle - - if (this.cycle > 0 && !(Math.floor(this.cycle) % 6)) Matter.Body.scale(this, SCALE, SCALE); //shrink + } + }, { + name: "rail gun", //15 + description: "hold left mouse to charge and release to fire
charging repels enemies and drains field energy
crouching improves charging and reduces recoil", + ammo: 0, + ammoPack: 9, + have: false, + isStarterGun: false, + fire() { + const me = bullet.length; + bullet[me] = Bodies.rectangle(9, -90, 0.01 * b.modBulletSize, 0.0015 * b.modBulletSize, { + // density: 0.0015, //frictionAir: 0.01, //restitution: 0, + angle: 0, + friction: 0.5, + frictionAir: 0, + dmg: 3 + b.modExtraDmg, //damage done in addition to the damage from momentum + classType: "bullet", + collisionFilter: { + category: 0x000000, + mask: 0x010011 //mask: 0x000101, //for self collision + }, + minDmgSpeed: 5, + onDmg() {}, //this.endCycle = 0 //triggers despawn + onEnd() {} + }); + mech.fireCDcycle = Infinity; // cool down + World.add(engine.world, bullet[me]); //add bullet to world + bullet[me].endCycle = Infinity + bullet[me].isCharging = true; + bullet[me].charge = 0; + bullet[me].do = function () { + if (this.isCharging) { + const DRAIN = 0.0001 + mech.fieldRegen + if ((!game.mouseDown && this.charge > 0.5) || mech.fieldMeter < DRAIN) { //fire on mouse release + this.isCharging = false + if (mech.fieldMeter < DRAIN) { + mech.fireCDcycle = mech.cycle + 120; // long CD if out of mana + } else { + mech.fireCDcycle = mech.cycle + 2; // set fire cool down } - } - }); - World.add(engine.world, bullet[me]); //add bullet to world - mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 8 : 4) * b.modFireRate); // cool down - const SPEED = mech.crouch ? 5.2 : 4.5; - Matter.Body.setVelocity(bullet[me], { - x: SPEED * Math.cos(DIR), - y: SPEED * Math.sin(DIR) - }); - bullet[me].direction = Matter.Vector.perp(bullet[me].velocity) - // if (mech.angle + Math.PI / 2 > 0) { - // bullet[me].direction = Matter.Vector.perp(bullet[me].velocity, true) - // } else { - // bullet[me].direction = Matter.Vector.perp(bullet[me].velocity) - // } + Matter.Body.scale(this, 10000, 10000) // un hide the bullet by scaling it up (don't judge me... I know this is a bad way to do it) + this.endCycle = game.cycle + Math.floor(140 * b.isModBulletsLastLonger) + this.collisionFilter.category = 0x000100 + Matter.Body.setPosition(this, { + x: mech.pos.x, + y: mech.pos.y + }) + Matter.Body.setAngle(this, mech.angle) + const speed = 80 + Matter.Body.setVelocity(this, { + x: mech.Vx / 2 + speed * this.charge * Math.cos(mech.angle), + y: mech.Vy / 2 + speed * this.charge * Math.sin(mech.angle) + }); - World.add(engine.world, bullet[me]); //add bullet to world - } - }, - { - name: "super balls", //4 - description: "fire 3 very bouncy balls", - ammo: 0, - ammoPack: 11, - have: false, - isStarterGun: true, - fire() { - b.muzzleFlash(20); - // mobs.alert(450); - const SPREAD = mech.crouch ? 0.04 : 0.14 - let dir = mech.angle - SPREAD; - for (let i = 0; i < 3; i++) { - const me = bullet.length; - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 7 * b.modBulletSize, b.fireAttributes(dir, false)); - b.fireProps(mech.crouch ? 40 : 20, mech.crouch ? 34 : 26, dir, me); //cd , speed - Matter.Body.setDensity(bullet[me], 0.0001); - bullet[me].endCycle = game.cycle + Math.floor(360 * b.modBulletsLastLonger); - bullet[me].dmg = 0.5 + b.extraDmg; - bullet[me].minDmgSpeed = 0; - bullet[me].restitution = 0.96; - bullet[me].friction = 0; - bullet[me].do = function () { - this.force.y += this.mass * 0.001; - }; - dir += SPREAD; - } - } - }, - { - name: "shotgun", //5 - description: "fire a burst of bullets
high recoil", - ammo: 0, - ammoPack: 8, - have: false, - isStarterGun: true, - fire() { - b.muzzleFlash(35); - // mobs.alert(650); - const side = 11 * b.modBulletSize - for (let i = 0; i < 9; i++) { - const me = bullet.length; - const dir = mech.angle + (Math.random() - 0.5) * (mech.crouch ? 0.22 : 0.7) - bullet[me] = Bodies.rectangle(mech.pos.x + 35 * Math.cos(mech.angle) + 15 * (Math.random() - 0.5), mech.pos.y + 35 * Math.sin(mech.angle) + 15 * (Math.random() - 0.5), side, side, b.fireAttributes(dir)); - b.fireProps(mech.crouch ? 60 : 30, 40 + Math.random() * 11, dir, me); //cd , speed - bullet[me].endCycle = game.cycle + Math.floor(55 * b.modBulletsLastLonger); - bullet[me].frictionAir = 0.03; - bullet[me].do = function () { - this.force.y += this.mass * 0.001; - }; - } + //knock back + const KNOCK = ((mech.crouch) ? 0.1 : 0.7) * b.modBulletSize * b.modBulletSize * this.charge * this.charge + player.force.x -= KNOCK * Math.cos(mech.angle) + player.force.y -= KNOCK * Math.sin(mech.angle) * 0.3 //reduce knock back in vertical direction to stop super jumps - //knock back - const KNOCK = ((mech.crouch) ? 0.015 : 0.15) * b.modBulletSize * b.modBulletSize - player.force.x -= KNOCK * Math.cos(mech.angle) - player.force.y -= KNOCK * Math.sin(mech.angle) * 0.4 //reduce knock back in vertical direction to stop super jumps - } - }, - { - name: "fléchettes", //6 - description: "fire accurate high speed needles", - ammo: 0, - ammoPack: 20, - have: false, - isStarterGun: true, - fire() { - function spawnFlechette(dir = mech.angle, speed, size = 1) { - const me = bullet.length; - bullet[me] = Bodies.rectangle(mech.pos.x + 40 * Math.cos(dir), mech.pos.y + 40 * Math.sin(dir), 65 * size * b.modBulletSize, 1.5 * size * b.modBulletSize, b.fireAttributes(dir)); - bullet[me].endCycle = game.cycle + Math.floor(180 * b.modBulletsLastLonger); - bullet[me].dmg = 0.25 * size + b.extraDmg; - b.drawOneBullet(bullet[me].vertices); - bullet[me].do = function () { - this.force.y += this.mass * 0.0002; //low gravity - }; - Matter.Body.setVelocity(bullet[me], { - x: mech.Vx / 2 + speed * Math.cos(dir), - y: mech.Vy / 2 + speed * Math.sin(dir) - }); - World.add(engine.world, bullet[me]); //add bullet to world - } - - if (mech.crouch) { - spawnFlechette(mech.angle, 55, 1.2) - } else { - for (let i = 0; i < 7; i++) { - spawnFlechette(mech.angle + 0.14 * (Math.random() - 0.5), 30 + 8 * Math.random(), 0.5) - } - } - mech.fireCDcycle = mech.cycle + Math.floor(30 * b.modFireRate); // cool down - } - }, - { - name: "missiles", //7 - description: "fire a missile that accelerates towards nearby targets
explodes when near target", - ammo: 0, - ammoPack: 8, - have: false, - isStarterGun: false, - fireCycle: 0, - ammoLoaded: 0, - fire() { - const thrust = 0.0003; - let dir = mech.angle + (0.5 - Math.random()) * (mech.crouch ? 0 : 0.2); - const me = bullet.length; - bullet[me] = Bodies.rectangle(mech.pos.x + 40 * Math.cos(mech.angle), mech.pos.y + 40 * Math.sin(mech.angle) - 3, 30 * b.modBulletSize, 4 * b.modBulletSize, b.fireAttributes(dir)); - b.fireProps(mech.crouch ? 70 : 30, -3 * (0.5 - Math.random()) + (mech.crouch ? 25 : -8), dir, me); //cd , speed - - b.drawOneBullet(bullet[me].vertices); - // Matter.Body.setDensity(bullet[me], 0.01) //doesn't help with reducing explosion knock backs - bullet[me].force.y += 0.00045; //a small push down at first to make it seem like the missile is briefly falling - bullet[me].frictionAir = 0 - bullet[me].endCycle = game.cycle + Math.floor((265 + Math.random() * 20) * b.modBulletsLastLonger); - bullet[me].explodeRad = 170 + 60 * Math.random(); - bullet[me].lookFrequency = Math.floor(8 + Math.random() * 7); - bullet[me].onEnd = b.explode; //makes bullet do explosive damage at end - bullet[me].onDmg = function () { - this.endCycle = 0; //bullet ends cycle after doing damage // also triggers explosion - }; - bullet[me].lockedOn = null; - bullet[me].do = function () { - if (!mech.isBodiesAsleep) { - if (!(mech.cycle % this.lookFrequency)) { - this.closestTarget = null; - this.lockedOn = null; - let closeDist = Infinity; - - //look for targets - for (let i = 0, len = mob.length; i < len; ++i) { - if ( - mob[i].alive && - mob[i].dropPowerUp && - Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0 - ) { - const dist = Matter.Vector.magnitude(Matter.Vector.sub(this.position, mob[i].position)); - if (dist < closeDist) { - this.closestTarget = mob[i].position; - closeDist = dist; - this.lockedOn = mob[i]; - } - } - } - //explode when bullet is close enough to target - if (this.closestTarget && closeDist < this.explodeRad) { - this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion - } - - if (this.lockedOn) { - this.frictionAir = 0.04; //extra friction - - //draw locked on targeting - ctx.beginPath(); - const vertices = this.lockedOn.vertices; - ctx.moveTo(this.position.x, this.position.y); - const mod = Math.floor((game.cycle / 3) % vertices.length); - ctx.lineTo(vertices[mod].x, vertices[mod].y); - ctx.strokeStyle = "rgba(0,0,155,0.35)"; //"#2f6"; - ctx.lineWidth = 1; - ctx.stroke(); + //push away blocks on fire + const RANGE = 450 * this.charge + for (let i = 0, len = body.length; i < len; ++i) { + const SUB = Matter.Vector.sub(body[i].position, mech.pos) + const DISTANCE = Matter.Vector.magnitude(SUB) + if (DISTANCE < RANGE) { + const DEPTH = RANGE - DISTANCE + const FORCE = Matter.Vector.mult(Matter.Vector.normalise(SUB), 0.005 * Math.sqrt(DEPTH) * Math.sqrt(body[i].mass)) + body[i].force.x += FORCE.x + body[i].force.y += FORCE.y - body[i].mass * (game.g * 25); //kick up a bit to give them some arc } } - //rotate missile towards the target - if (this.closestTarget) { - const face = { - x: Math.cos(this.angle), - y: Math.sin(this.angle) - }; - const target = Matter.Vector.normalise(Matter.Vector.sub(this.position, this.closestTarget)); - if (Matter.Vector.dot(target, face) > -0.98) { - if (Matter.Vector.cross(target, face) > 0) { - Matter.Body.rotate(this, 0.08); - } else { - Matter.Body.rotate(this, -0.08); - } + } else { // charging on mouse down + mech.fieldMeter -= DRAIN; + mech.fireCDcycle = Infinity //can't fire until mouse is released + if (mech.crouch) { + this.charge = this.charge * 0.97 + 0.03 // this.charge converges to 1 + } else { + this.charge = this.charge * 0.99 + 0.01 // this.charge converges to 1 + } + + //gently push away mobs while charging + const RANGE = 300 * this.charge + for (let i = 0, len = mob.length; i < len; ++i) { + const SUB = Matter.Vector.sub(mob[i].position, mech.pos) + const DISTANCE = Matter.Vector.magnitude(SUB) + // if (DISTANCE < RANGE) { + // Matter.Body.setVelocity(mob[i], Matter.Vector.rotate(mob[i].velocity, 0.1)) + // } + if (DISTANCE < RANGE) { + const DEPTH = RANGE - DISTANCE + const FORCE = Matter.Vector.mult(Matter.Vector.normalise(SUB), 0.000000001 * DEPTH * DEPTH * DEPTH * Math.sqrt(mob[i].mass)) + mob[i].force.x += FORCE.x + mob[i].force.y += FORCE.y } } - //accelerate in direction bullet is facing - const dir = this.angle; // + (Math.random() - 0.5); - this.force.x += Math.cos(dir) * thrust; - this.force.y += Math.sin(dir) * thrust; - //draw rocket - ctx.beginPath(); - ctx.arc(this.position.x - Math.cos(this.angle) * 27 + (Math.random() - 0.5) * 4, this.position.y - Math.sin(this.angle) * 27 + (Math.random() - 0.5) * 4, 11, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(255,155,0,0.5)"; - ctx.fill(); - } else { - //draw rocket with time stop - ctx.beginPath(); - ctx.arc(this.position.x - Math.cos(this.angle) * 27, this.position.y - Math.sin(this.angle) * 27, 11, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(255,155,0,0.5)"; - ctx.fill(); - } - } - } - }, - { - name: "flak", //8 - description: "fire a cluster of short range projectiles
explode on contact or after half a second", - ammo: 0, - ammoPack: 20, - have: false, - isStarterGun: true, - fire() { - b.muzzleFlash(30); - const totalBullets = 5 - const angleStep = (mech.crouch ? 0.06 : 0.15) / totalBullets - const SPEED = mech.crouch ? 30 : 25 - const CD = mech.crouch ? 45 : 11 - const END = Math.floor((mech.crouch ? 30 : 18) * b.modBulletsLastLonger); - let dir = mech.angle - angleStep * totalBullets / 2; - const side1 = 17 * b.modBulletSize - const side2 = 4 * b.modBulletSize - - for (let i = 0; i < totalBullets; i++) { //5 -> 7 - dir += angleStep - const me = bullet.length; - bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), side1, side2, b.fireAttributes(dir)); - b.fireProps(CD, SPEED + 15 * Math.random() - 2 * i, dir, me); //cd , speed - // Matter.Body.setDensity(bullet[me], 0.005); - bullet[me].endCycle = 2 * i + game.cycle + END - bullet[me].restitution = 0; - bullet[me].friction = 1; - // bullet[me].dmg = 0.15; - bullet[me].explodeRad = (mech.crouch ? 70 : 45) + (Math.random() - 0.5) * 50; - bullet[me].onEnd = b.explode; - bullet[me].onDmg = function () { - this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion - }; - bullet[me].do = function () { - this.force.y += this.mass * 0.0004; - // if (this.speed < 10) { //if slow explode - // for (let i = 0, len = bullet.length; i < len; i++) { - // bullet[i].endCycle = 0 //all other bullets explode + // //draw laser targeting + // let best; + // let range = 3000 + // const dir = mech.angle + // const path = [{ + // x: mech.pos.x + 20 * Math.cos(dir), + // y: mech.pos.y + 20 * Math.sin(dir) + // }, + // { + // x: mech.pos.x + range * Math.cos(dir), + // y: mech.pos.y + range * Math.sin(dir) // } + // ]; + // 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) { + // 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) { + // 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 + // }; // } + + // //draw laser beam + // ctx.beginPath(); + // ctx.moveTo(path[0].x, path[0].y); + // ctx.lineTo(path[1].x, path[1].y); + // ctx.strokeStyle = `rgba(50,0,100,0.1)`; + // ctx.lineWidth = this.charge * 3 + // ctx.stroke(); + + //draw magnetic field + const X = mech.pos.x + const Y = mech.pos.y + const unitVector = Matter.Vector.normalise(Matter.Vector.sub(game.mouseInGame, mech.pos)) + const unitVectorPerp = Matter.Vector.perp(unitVector) + + function magField(mag, arc) { + ctx.moveTo(X, Y); + ctx.bezierCurveTo( + X + unitVector.x * mag, Y + unitVector.y * mag, + X + unitVector.x * mag + unitVectorPerp.x * arc, Y + unitVector.y * mag + unitVectorPerp.y * arc, + X + unitVectorPerp.x * arc, Y + unitVectorPerp.y * arc) + ctx.bezierCurveTo( + X - unitVector.x * mag + unitVectorPerp.x * arc, Y - unitVector.y * mag + unitVectorPerp.y * arc, + X - unitVector.x * mag, Y - unitVector.y * mag, + X, Y) + } + ctx.fillStyle = `rgba(50,0,100,0.05)`; + for (let i = 3; i < 7; i++) { + const MAG = 8 * i * i * this.charge * (0.93 + 0.07 * Math.random()) + const ARC = 6 * i * i * this.charge * (0.93 + 0.07 * Math.random()) + ctx.beginPath(); + magField(MAG, ARC) + magField(MAG, -ARC) + ctx.fill(); + } } + } else { //normal bullet behavior + this.force.y += this.mass * 0.00015 / this.charge; // low gravity that scales with charge } } - }, - { - name: "grenades", //9 - description: "fire a projectile that explodes on contact or after one second", - ammo: 0, - ammoPack: 9, - have: false, - isStarterGun: true, - fire() { + } + // }, { + // name: "kinetic slugs", //1 + // description: "fire a large rod that does excessive physical damage
high recoil", + // ammo: 0, + // ammoPack: 5, + // have: false, + // isStarterGun: true, + // fire() { + // b.muzzleFlash(45); + // // mobs.alert(800); + // const me = bullet.length; + // const dir = mech.angle; + // bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), 70 * b.modBulletSize, 30 * b.modBulletSize, b.fireAttributes(dir)); + // b.fireProps(mech.crouch ? 55 : 40, 50, dir, me); //cd , speed + // bullet[me].endCycle = game.cycle + Math.floor(180 * b.isModBulletsLastLonger); + // bullet[me].do = function () { + // this.force.y += this.mass * 0.0005; + // }; + + // //knock back + // const KNOCK = ((mech.crouch) ? 0.025 : 0.25) * b.modBulletSize * b.modBulletSize + // player.force.x -= KNOCK * Math.cos(dir) + // player.force.y -= KNOCK * Math.sin(dir) * 0.3 //reduce knock back in vertical direction to stop super jumps + // } + }, { + name: "minigun", //2 + description: "rapidly fire a stream of small bullets", + ammo: 0, + ammoPack: 105, + have: false, + isStarterGun: true, + fire() { + const me = bullet.length; + b.muzzleFlash(15); + // if (Math.random() > 0.2) mobs.alert(500); + const dir = mech.angle + (Math.random() - 0.5) * ((mech.crouch) ? 0.03 : 0.14); + bullet[me] = Bodies.rectangle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 17 * b.modBulletSize, 5 * b.modBulletSize, b.fireAttributes(dir)); + b.fireProps(mech.crouch ? 11 : 5, mech.crouch ? 44 : 36, dir, me); //cd , speed + bullet[me].endCycle = game.cycle + Math.floor(65 * b.isModBulletsLastLonger); + bullet[me].frictionAir = mech.crouch ? 0.007 : 0.01; + bullet[me].do = function () { + this.force.y += this.mass * 0.0005; + }; + } + }, { + name: "wave beam", //3 + description: "fire a stream of oscillating particles
propagates through solids", + ammo: 0, + ammoPack: 85, + have: false, + isStarterGun: true, + fire() { + const me = bullet.length; + const DIR = mech.angle + const SCALE = (mech.crouch ? 0.963 : 0.95) + const wiggleMag = ((mech.flipLegs === 1) ? 1 : -1) * ((mech.crouch) ? 0.004 : 0.005) + bullet[me] = Bodies.circle(mech.pos.x + 25 * Math.cos(DIR), mech.pos.y + 25 * Math.sin(DIR), 10 * b.modBulletSize, { + angle: DIR, + cycle: -0.43, //adjust this number until the bullets line up with the cross hairs + endCycle: game.cycle + Math.floor((mech.crouch ? 155 : 120) * b.isModBulletsLastLonger), + inertia: Infinity, + frictionAir: 0, + minDmgSpeed: 0, + dmg: 0.13 + b.modExtraDmg, //damage done in addition to the damage from momentum + classType: "bullet", + collisionFilter: { + category: 0x000100, + mask: 0x000010 + }, + onDmg() {}, + onEnd() {}, + do() { + if (!mech.isBodiesAsleep) { + this.cycle++ + const THRUST = wiggleMag * Math.cos(this.cycle * 0.3) + this.force = Matter.Vector.mult(Matter.Vector.normalise(this.direction), this.mass * THRUST) //wiggle + + if (this.cycle > 0 && !(Math.floor(this.cycle) % 6)) Matter.Body.scale(this, SCALE, SCALE); //shrink + } + } + }); + World.add(engine.world, bullet[me]); //add bullet to world + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 8 : 4) * b.modFireRate); // cool down + const SPEED = mech.crouch ? 5.2 : 4.5; + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(DIR), + y: SPEED * Math.sin(DIR) + }); + bullet[me].direction = Matter.Vector.perp(bullet[me].velocity) + // if (mech.angle + Math.PI / 2 > 0) { + // bullet[me].direction = Matter.Vector.perp(bullet[me].velocity, true) + // } else { + // bullet[me].direction = Matter.Vector.perp(bullet[me].velocity) + // } + + World.add(engine.world, bullet[me]); //add bullet to world + } + }, { + name: "super balls", //4 + description: "fire 3 very bouncy balls", + ammo: 0, + ammoPack: 11, + have: false, + isStarterGun: true, + fire() { + b.muzzleFlash(20); + // mobs.alert(450); + const SPREAD = mech.crouch ? 0.04 : 0.14 + let dir = mech.angle - SPREAD; + for (let i = 0; i < 3; i++) { const me = bullet.length; - const dir = mech.angle; // + Math.random() * 0.05; - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 15 * b.modBulletSize, b.fireAttributes(dir, false)); - b.fireProps(mech.crouch ? 40 : 20, mech.crouch ? 43 : 32, dir, me); //cd , speed - b.drawOneBullet(bullet[me].vertices); - // Matter.Body.setDensity(bullet[me], 0.000001); - bullet[me].totalCycles = 100; - bullet[me].endCycle = game.cycle + Math.floor((mech.crouch ? 120 : 60) * b.modBulletsLastLonger); - bullet[me].restitution = 0.5; - bullet[me].explodeRad = 210; - bullet[me].onEnd = b.explode; //makes bullet do explosive damage before despawn - bullet[me].minDmgSpeed = 1; - bullet[me].onDmg = function () { - this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion - }; + bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 7 * b.modBulletSize, b.fireAttributes(dir, false)); + b.fireProps(mech.crouch ? 40 : 20, mech.crouch ? 34 : 26, dir, me); //cd , speed + Matter.Body.setDensity(bullet[me], 0.0001); + bullet[me].endCycle = game.cycle + Math.floor(360 * b.isModBulletsLastLonger); + bullet[me].dmg = 0.5 + b.modExtraDmg; + bullet[me].minDmgSpeed = 0; + bullet[me].restitution = 0.96; + bullet[me].friction = 0; bullet[me].do = function () { - //extra gravity for harder arcs - this.force.y += this.mass * 0.002; + this.force.y += this.mass * 0.001; + }; + dir += SPREAD; + } + } + }, { + name: "shotgun", //5 + description: "fire a burst of bullets
crouch to reduce recoil", + ammo: 0, + ammoPack: 8, + have: false, + isStarterGun: true, + fire() { + b.muzzleFlash(35); + // mobs.alert(650); + const side = 11 * b.modBulletSize + for (let i = 0; i < 9; i++) { + const me = bullet.length; + const dir = mech.angle + (Math.random() - 0.5) * (mech.crouch ? 0.22 : 0.7) + bullet[me] = Bodies.rectangle(mech.pos.x + 35 * Math.cos(mech.angle) + 15 * (Math.random() - 0.5), mech.pos.y + 35 * Math.sin(mech.angle) + 15 * (Math.random() - 0.5), side, side, b.fireAttributes(dir)); + b.fireProps(mech.crouch ? 60 : 30, 40 + Math.random() * 11, dir, me); //cd , speed + bullet[me].endCycle = game.cycle + Math.floor(55 * b.isModBulletsLastLonger); + bullet[me].frictionAir = 0.03; + bullet[me].do = function () { + this.force.y += this.mass * 0.001; }; } - }, - { - name: "vacuum bomb", //10 - description: "fire a huge bomb that sucks before it explodes
click left mouse again to detonate", - ammo: 0, - ammoPack: 4, - have: false, - isStarterGun: false, - fire() { - const me = bullet.length; - const dir = mech.angle; - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 26 * b.modBulletSize, b.fireAttributes(dir, false)); - bullet[me].radius = 22; //used from drawing timer - b.fireProps(10, mech.crouch ? 42 : 26, dir, me); //cd , speed + //knock back + const KNOCK = ((mech.crouch) ? 0.015 : 0.15) * b.modBulletSize * b.modBulletSize + player.force.x -= KNOCK * Math.cos(mech.angle) + player.force.y -= KNOCK * Math.sin(mech.angle) * 0.3 //reduce knock back in vertical direction to stop super jumps + } + }, { + name: "fléchettes", //6 + description: "fire accurate high speed needles", + ammo: 0, + ammoPack: 25, + have: false, + isStarterGun: true, + fire() { + function spawnFlechette(dir = mech.angle, speed, size = 1) { + const me = bullet.length; + bullet[me] = Bodies.rectangle(mech.pos.x + 40 * Math.cos(dir), mech.pos.y + 40 * Math.sin(dir), 32 * size * b.modBulletSize, 0.8 * size * b.modBulletSize, b.fireAttributes(dir)); + bullet[me].endCycle = game.cycle + Math.floor(180 * b.isModBulletsLastLonger); + bullet[me].dmg = 0.15 * size + b.modExtraDmg; b.drawOneBullet(bullet[me].vertices); - bullet[me].endCycle = Infinity - bullet[me].endCycle = Infinity - // bullet[me].restitution = 0.3; - // bullet[me].frictionAir = 0.01; - // bullet[me].friction = 0.15; - bullet[me].inertia = Infinity; //prevents rotation + bullet[me].do = function () { + this.force.y += this.mass * 0.0002; //low gravity + }; + Matter.Body.setVelocity(bullet[me], { + x: mech.Vx / 2 + speed * Math.cos(dir), + y: mech.Vy / 2 + speed * Math.sin(dir) + }); + World.add(engine.world, bullet[me]); //add bullet to world + } + + if (mech.crouch) { + for (let i = 0; i < 3; i++) { + spawnFlechette(mech.angle + 0.02 * (Math.random() - 0.5), 35 + 4 * i, 1.55) + } + } else { + for (let i = 0; i < 9; i++) { + spawnFlechette(mech.angle + 0.12 * (Math.random() - 0.5), 30 + 8 * Math.random()) + } + } + mech.fireCDcycle = mech.cycle + Math.floor(40 * b.modFireRate); // cool down + } + }, { + name: "missiles", //7 + description: "fire a missile that accelerates towards nearby targets
explodes when near target", + ammo: 0, + ammoPack: 8, + have: false, + isStarterGun: false, + fireCycle: 0, + ammoLoaded: 0, + fire() { + const thrust = 0.0003; + let dir = mech.angle + (0.5 - Math.random()) * (mech.crouch ? 0 : 0.2); + const me = bullet.length; + bullet[me] = Bodies.rectangle(mech.pos.x + 40 * Math.cos(mech.angle), mech.pos.y + 40 * Math.sin(mech.angle) - 3, 30 * b.modBulletSize, 4 * b.modBulletSize, b.fireAttributes(dir)); + b.fireProps(mech.crouch ? 70 : 30, -3 * (0.5 - Math.random()) + (mech.crouch ? 25 : -8), dir, me); //cd , speed + + b.drawOneBullet(bullet[me].vertices); + // Matter.Body.setDensity(bullet[me], 0.01) //doesn't help with reducing explosion knock backs + bullet[me].force.y += 0.00045; //a small push down at first to make it seem like the missile is briefly falling + bullet[me].frictionAir = 0 + bullet[me].endCycle = game.cycle + Math.floor((265 + Math.random() * 20) * b.isModBulletsLastLonger); + bullet[me].explodeRad = 170 + 60 * Math.random(); + bullet[me].lookFrequency = Math.floor(8 + Math.random() * 7); + bullet[me].onEnd = b.explode; //makes bullet do explosive damage at end + bullet[me].onDmg = function () { + this.endCycle = 0; //bullet ends cycle after doing damage // also triggers explosion + }; + bullet[me].lockedOn = null; + bullet[me].do = function () { + if (!mech.isBodiesAsleep) { + if (!(mech.cycle % this.lookFrequency)) { + this.closestTarget = null; + this.lockedOn = null; + let closeDist = Infinity; + + //look for targets + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + mob[i].alive && + mob[i].dropPowerUp && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + ) { + const dist = Matter.Vector.magnitude(Matter.Vector.sub(this.position, mob[i].position)); + if (dist < closeDist) { + this.closestTarget = mob[i].position; + closeDist = dist; + this.lockedOn = mob[i]; + } + } + } + //explode when bullet is close enough to target + if (this.closestTarget && closeDist < this.explodeRad) { + this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion + } + + if (this.lockedOn) { + this.frictionAir = 0.04; //extra friction + + //draw locked on targeting + ctx.beginPath(); + const vertices = this.lockedOn.vertices; + ctx.moveTo(this.position.x, this.position.y); + const mod = Math.floor((game.cycle / 3) % vertices.length); + ctx.lineTo(vertices[mod].x, vertices[mod].y); + ctx.strokeStyle = "rgba(0,0,155,0.35)"; //"#2f6"; + ctx.lineWidth = 1; + ctx.stroke(); + } + } + + //rotate missile towards the target + if (this.closestTarget) { + const face = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + }; + const target = Matter.Vector.normalise(Matter.Vector.sub(this.position, this.closestTarget)); + if (Matter.Vector.dot(target, face) > -0.98) { + if (Matter.Vector.cross(target, face) > 0) { + Matter.Body.rotate(this, 0.08); + } else { + Matter.Body.rotate(this, -0.08); + } + } + } + //accelerate in direction bullet is facing + const dir = this.angle; // + (Math.random() - 0.5); + this.force.x += Math.cos(dir) * thrust; + this.force.y += Math.sin(dir) * thrust; + + //draw rocket + ctx.beginPath(); + ctx.arc(this.position.x - Math.cos(this.angle) * 27 + (Math.random() - 0.5) * 4, this.position.y - Math.sin(this.angle) * 27 + (Math.random() - 0.5) * 4, 11, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(255,155,0,0.5)"; + ctx.fill(); + } else { + //draw rocket with time stop + ctx.beginPath(); + ctx.arc(this.position.x - Math.cos(this.angle) * 27, this.position.y - Math.sin(this.angle) * 27, 11, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(255,155,0,0.5)"; + ctx.fill(); + } + } + } + }, { + name: "flak", //8 + description: "fire a cluster of short range projectiles
explode on contact or after half a second", + ammo: 0, + ammoPack: 20, + have: false, + isStarterGun: true, + fire() { + b.muzzleFlash(30); + const totalBullets = 5 + const angleStep = (mech.crouch ? 0.06 : 0.15) / totalBullets + const SPEED = mech.crouch ? 30 : 25 + const CD = mech.crouch ? 45 : 11 + const END = Math.floor((mech.crouch ? 30 : 18) * b.isModBulletsLastLonger); + let dir = mech.angle - angleStep * totalBullets / 2; + const side1 = 17 * b.modBulletSize + const side2 = 4 * b.modBulletSize + + for (let i = 0; i < totalBullets; i++) { //5 -> 7 + dir += angleStep + const me = bullet.length; + bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), side1, side2, b.fireAttributes(dir)); + b.fireProps(CD, SPEED + 15 * Math.random() - 2 * i, dir, me); //cd , speed + // Matter.Body.setDensity(bullet[me], 0.005); + bullet[me].endCycle = 2 * i + game.cycle + END bullet[me].restitution = 0; bullet[me].friction = 1; - - bullet[me].explodeRad = 380 + Math.floor(Math.random() * 60); - bullet[me].onEnd = b.explode; //makes bullet do explosive damage before despawn + // bullet[me].dmg = 0.15; + bullet[me].explodeRad = (mech.crouch ? 70 : 45) + (Math.random() - 0.5) * 50; + bullet[me].onEnd = b.explode; bullet[me].onDmg = function () { - // this.endCycle = 0; //bullet ends cycle after doing damage //this triggers explosion + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion }; - bullet[me].isArmed = false; - bullet[me].isSucking = false; bullet[me].do = function () { - //extra gravity for harder arcs - this.force.y += this.mass * 0.0022; - mech.fireCDcycle = mech.cycle + 10 //can't fire until after the explosion - - //set armed and sucking status - if (!this.isArmed && !game.mouseDown) { - this.isArmed = true - } else if (this.isArmed && game.mouseDown && !this.isSucking) { - this.isSucking = true; - this.endCycle = game.cycle + 35; - } - - if (this.isSucking) { - if (!mech.isBodiesAsleep) { - const that = this - let mag = 0.1 - - function suck(who, radius = that.explodeRad * 2) { - for (i = 0, len = who.length; i < len; i++) { - const sub = Matter.Vector.sub(that.position, who[i].position); - const dist = Matter.Vector.magnitude(sub); - if (dist < radius && dist > 150) { - knock = Matter.Vector.mult(Matter.Vector.normalise(sub), mag * who[i].mass / Math.sqrt(dist)); - who[i].force.x += knock.x; - who[i].force.y += knock.y; - } - } - } - if (game.cycle > this.endCycle - 5) { - mag = -0.22 - suck(body) - suck(mob) - suck(powerUp) - suck(bullet) - suck([player]) - } else { - mag = 0.1 - suck(body) - suck(mob) - suck(powerUp) - suck(bullet) - suck([player]) - } - //keep bomb in place - Matter.Body.setVelocity(this, { - x: 0, - y: 0 - }); - //draw suck - const radius = 2.5 * this.explodeRad * (this.endCycle - game.cycle) / 35 - ctx.fillStyle = "rgba(0,0,0,0.1)"; - ctx.beginPath(); - ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); - ctx.fill(); - } - } else { - // flashing lights to show armed - if (!(game.cycle % 10)) { - if (this.isFlashOn) { - this.isFlashOn = false; - } else { - this.isFlashOn = true; - } - } - if (this.isFlashOn) { - ctx.fillStyle = "#000"; - ctx.beginPath(); - ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI); - ctx.fill(); - //draw clock on timer - ctx.fillStyle = "#f04"; - ctx.beginPath(); - ctx.arc(this.position.x, this.position.y, this.radius * 0.5, 0, 2 * Math.PI); - ctx.fill(); - } - } + this.force.y += this.mass * 0.0004; + // if (this.speed < 10) { //if slow explode + // for (let i = 0, len = bullet.length; i < len; i++) { + // bullet[i].endCycle = 0 //all other bullets explode + // } + // } } } - }, - { - name: "ferro frag", //11 - description: "fire a grenade that ejects magnetized nails
nails are attracted to enemy targets", - ammo: 0, - ammoPack: 8, - have: false, - isStarterGun: false, - fire() { - const me = bullet.length; - const dir = mech.angle; - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 15 * b.modBulletSize, b.fireAttributes(dir, false)); - b.fireProps(mech.crouch ? 40 : 30, mech.crouch ? 34 : 22, dir, me); //cd , speed - b.drawOneBullet(bullet[me].vertices); - bullet[me].endCycle = game.cycle + Math.floor(60 * b.modBulletsLastLonger); - bullet[me].restitution = 0.3; - // bullet[me].frictionAir = 0.01; - // bullet[me].friction = 0.15; - // bullet[me].friction = 1; - bullet[me].onEnd = () => {} - bullet[me].do = function () { - this.force.y += this.mass * 0.0018; //extra gravity for grenades + } + }, { + name: "grenades", //9 + description: "fire a projectile that explodes on contact or after one second", + ammo: 0, + ammoPack: 9, + have: false, + isStarterGun: true, + fire() { + const me = bullet.length; + const dir = mech.angle; // + Math.random() * 0.05; + bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 15 * b.modBulletSize, b.fireAttributes(dir, false)); + b.fireProps(mech.crouch ? 40 : 20, mech.crouch ? 43 : 32, dir, me); //cd , speed + b.drawOneBullet(bullet[me].vertices); + // Matter.Body.setDensity(bullet[me], 0.000001); + bullet[me].totalCycles = 100; + bullet[me].endCycle = game.cycle + Math.floor((mech.crouch ? 120 : 60) * b.isModBulletsLastLonger); + bullet[me].restitution = 0.5; + bullet[me].explodeRad = 210; + bullet[me].onEnd = b.explode; //makes bullet do explosive damage before despawn + bullet[me].minDmgSpeed = 1; + bullet[me].onDmg = function () { + this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion + }; + bullet[me].do = function () { + //extra gravity for harder arcs + this.force.y += this.mass * 0.002; + }; + } + }, { + name: "vacuum bomb", //10 + description: "fire a huge bomb that sucks before it explodes
click left mouse again to detonate", + ammo: 0, + ammoPack: 5, + have: false, + isStarterGun: false, + fire() { + const me = bullet.length; + const dir = mech.angle; + bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 26 * b.modBulletSize, b.fireAttributes(dir, false)); + bullet[me].radius = 22; //used from drawing timer + b.fireProps(10, mech.crouch ? 42 : 26, dir, me); //cd , speed - if (game.cycle > this.endCycle - 1) { - if (!mech.isBodiesAsleep) { - //target nearby mobs - const targets = [] - for (let i = 0, len = mob.length; i < len; i++) { - const sub = Matter.Vector.sub(this.position, mob[i].position); - const dist = Matter.Vector.magnitude(sub); - if (dist < 1400 && - Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0) { - targets.push(mob[i].position) - } - } - for (let i = 0; i < 14; i++) { - const SPEED = 35 + 20 * Math.random() - if (targets.length > 0) { // aim near a random target - const SPREAD = 100 - const INDEX = Math.floor(Math.random() * targets.length) - const WHERE = { - x: targets[INDEX].x + SPREAD * (Math.random() - 0.5), - y: targets[INDEX].y + SPREAD * (Math.random() - 0.5) - } - needle(this.position, Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(WHERE, this.position)), SPEED)) - } else { // aim in random direction - const ANGLE = 2 * Math.PI * Math.random() - needle(this.position, { - x: SPEED * Math.cos(ANGLE), - y: SPEED * Math.sin(ANGLE) - }) - } + b.drawOneBullet(bullet[me].vertices); + bullet[me].endCycle = Infinity + // bullet[me].restitution = 0.3; + // bullet[me].frictionAir = 0.01; + // bullet[me].friction = 0.15; + bullet[me].inertia = Infinity; //prevents rotation + bullet[me].restitution = 0; + bullet[me].friction = 1; - function needle(pos, velocity) { - const me = bullet.length; - bullet[me] = Bodies.rectangle(pos.x, pos.y, 23 * b.modBulletSize, 2 * b.modBulletSize, b.fireAttributes(Math.atan2(velocity.y, velocity.x))); - Matter.Body.setVelocity(bullet[me], velocity); - World.add(engine.world, bullet[me]); //add bullet to world - bullet[me].endCycle = game.cycle + 60 + Math.floor(15 * Math.random()); - // bullet[me].dmg = 1.1+b.extraDmg; - bullet[me].do = function () {}; - } - } - } - } + bullet[me].explodeRad = 380 + Math.floor(Math.random() * 60); + bullet[me].onEnd = b.explode; //makes bullet do explosive damage before despawn + bullet[me].onDmg = function () { + // this.endCycle = 0; //bullet ends cycle after doing damage //this triggers explosion + }; + bullet[me].isArmed = false; + bullet[me].isSucking = false; + bullet[me].do = function () { + //extra gravity for harder arcs + this.force.y += this.mass * 0.0022; + mech.fireCDcycle = mech.cycle + 10 //can't fire until after the explosion + + //set armed and sucking status + if (!this.isArmed && !game.mouseDown) { + this.isArmed = true + } else if (this.isArmed && game.mouseDown && !this.isSucking) { + this.isSucking = true; + this.endCycle = game.cycle + 35; } - } - }, - { - name: "spores", //12 - description: "release an orb that discharges spores after 2 seconds
seeks out targets
passes through blocks", - ammo: 0, - ammoPack: 5, - have: false, - isStarterGun: false, - fire() { - const me = bullet.length; - const dir = mech.angle; - bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 20, 4.5, b.fireAttributes(dir, false)); - b.fireProps(mech.crouch ? 75 : 55, mech.crouch ? 25 : 14, dir, me); //cd , speed - b.drawOneBullet(bullet[me].vertices); - Matter.Body.setDensity(bullet[me], 0.000001); - bullet[me].endCycle = game.cycle + 100; - bullet[me].frictionAir = 0; - bullet[me].friction = 0.5; - bullet[me].restitution = 0.3; - bullet[me].minDmgSpeed = 0; - bullet[me].onDmg = function () {}; - bullet[me].do = function () { + + if (this.isSucking) { if (!mech.isBodiesAsleep) { - const SCALE = 1.017 - Matter.Body.scale(this, SCALE, SCALE); - this.frictionAir += 0.00023; + const that = this + let mag = 0.1 + + function suck(who, radius = that.explodeRad * 2) { + for (i = 0, len = who.length; i < len; i++) { + const sub = Matter.Vector.sub(that.position, who[i].position); + const dist = Matter.Vector.magnitude(sub); + if (dist < radius && dist > 150) { + knock = Matter.Vector.mult(Matter.Vector.normalise(sub), mag * who[i].mass / Math.sqrt(dist)); + who[i].force.x += knock.x; + who[i].force.y += knock.y; + } + } + } + if (game.cycle > this.endCycle - 5) { + mag = -0.22 + suck(body) + suck(mob) + suck(powerUp) + suck(bullet) + suck([player]) + } else { + mag = 0.1 + suck(body) + suck(mob) + suck(powerUp) + suck(bullet) + suck([player]) + } + //keep bomb in place + Matter.Body.setVelocity(this, { + x: 0, + y: 0 + }); + //draw suck + const radius = 2.5 * this.explodeRad * (this.endCycle - game.cycle) / 35 + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); + ctx.fill(); } - - this.force.y += this.mass * 0.00045; - - //draw green glow - ctx.fillStyle = "rgba(0,200,125,0.16)"; - ctx.beginPath(); - ctx.arc(this.position.x, this.position.y, 26, 0, 2 * Math.PI); - ctx.fill(); - }; - - //spawn bullets on end - bullet[me].onEnd = function () { - const NUM = 9; - for (let i = 0; i < NUM; i++) { - const bIndex = bullet.length; - const RADIUS = 3 * b.modBulletSize; - bullet[bIndex] = Bodies.circle(this.position.x, this.position.y, RADIUS, { - // density: 0.0015, //frictionAir: 0.01, - inertia: Infinity, - restitution: 0.5, - angle: dir, - friction: 0, - frictionAir: 0.011, - dmg: 1.8 + b.extraDmg, //damage done in addition to the damage from momentum - classType: "bullet", - collisionFilter: { - category: 0x000100, - mask: 0x000011 //no collide with body - }, - endCycle: game.cycle + Math.floor((360 + Math.floor(Math.random() * 240)) * b.modBulletsLastLonger), - minDmgSpeed: 0, - onDmg() { - this.endCycle = 0; //bullet ends cycle after doing damage - }, - onEnd() {}, - lookFrequency: 67 + Math.floor(47 * Math.random()), - do() { - //find mob targets - if (!(game.cycle % this.lookFrequency)) { - this.closestTarget = null; - this.lockedOn = null; - let closeDist = Infinity; - for (let i = 0, len = mob.length; i < len; ++i) { - if (Matter.Query.ray(map, this.position, mob[i].position).length === 0) { - // Matter.Query.ray(body, this.position, mob[i].position).length === 0 - const targetVector = Matter.Vector.sub(this.position, mob[i].position) - const dist = Matter.Vector.magnitude(targetVector); - if (dist < closeDist) { - this.closestTarget = mob[i].position; - closeDist = dist; - this.lockedOn = Matter.Vector.normalise(targetVector); - if (0.3 > Math.random()) break //doesn't always target the closest mob - } - } - } - } - //accelerate towards mobs - const THRUST = this.mass * 0.0009 - if (this.lockedOn) { - this.force.x -= THRUST * this.lockedOn.x - this.force.y -= THRUST * this.lockedOn.y - } else { - this.force.y += this.mass * 0.00025; //gravity - } - }, - }); - const SPEED = 9; - const ANGLE = 2 * Math.PI * Math.random() - Matter.Body.setVelocity(bullet[bIndex], { - x: SPEED * Math.cos(ANGLE), - y: SPEED * Math.sin(ANGLE) - }); - World.add(engine.world, bullet[bIndex]); //add bullet to world + } else { + // flashing lights to show armed + if (!(game.cycle % 10)) { + if (this.isFlashOn) { + this.isFlashOn = false; + } else { + this.isFlashOn = true; + } + } + if (this.isFlashOn) { + ctx.fillStyle = "#000"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI); + ctx.fill(); + //draw clock on timer + ctx.fillStyle = "#f04"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.radius * 0.5, 0, 2 * Math.PI); + ctx.fill(); } } - } - }, - { - name: "drones", //13 - description: "release drones that seek out targets for 16 seconds
follows mouse if no targets are found", - ammo: 0, - ammoPack: 20, - have: false, - isStarterGun: true, - fire() { - const THRUST = 0.0015 - const dir = mech.angle + 0.2 * (Math.random() - 0.5); - const me = bullet.length; - const RADIUS = (4.5 + 3 * Math.random()) * b.modBulletSize - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), RADIUS, { - angle: dir, - inertia: Infinity, - friction: 0, - frictionAir: 0.0005, - restitution: 1, - dmg: 0.14 + b.extraDmg, //damage done in addition to the damage from momentum - lookFrequency: 79 + Math.floor(37 * Math.random()), - endCycle: game.cycle + Math.floor((780 + 360 * Math.random()) * b.modBulletsLastLonger), - classType: "bullet", - collisionFilter: { - category: 0x000100, - mask: 0x010111 //self collide - }, - minDmgSpeed: 0, - lockedOn: null, - isFollowMouse: true, - onDmg() { - this.lockedOn = null - }, - onEnd() {}, - do() { - this.force.y += this.mass * 0.0002; - //find mob targets - if (!(game.cycle % this.lookFrequency)) { - this.lockedOn = null; - let closeDist = Infinity; - for (let i = 0, len = mob.length; i < len; ++i) { - if ( - Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0 - ) { - const TARGET_VECTOR = Matter.Vector.sub(this.position, mob[i].position) - const DIST = Matter.Vector.magnitude(TARGET_VECTOR); - if (DIST < closeDist) { - closeDist = DIST; - this.lockedOn = mob[i] - } - } + } + }, { + name: "ferro frag", //11 + description: "fire a grenade that ejects magnetized nails
nails are attracted to enemy targets", + ammo: 0, + ammoPack: 8, + have: false, + isStarterGun: false, + fire() { + const me = bullet.length; + const dir = mech.angle; + bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 15 * b.modBulletSize, b.fireAttributes(dir, false)); + b.fireProps(mech.crouch ? 40 : 30, mech.crouch ? 34 : 22, dir, me); //cd , speed + b.drawOneBullet(bullet[me].vertices); + bullet[me].endCycle = game.cycle + Math.floor(60 * b.isModBulletsLastLonger); + bullet[me].restitution = 0.3; + // bullet[me].frictionAir = 0.01; + // bullet[me].friction = 0.15; + // bullet[me].friction = 1; + bullet[me].onEnd = () => {} + bullet[me].do = function () { + this.force.y += this.mass * 0.0018; //extra gravity for grenades + + if (game.cycle > this.endCycle - 1) { + if (!mech.isBodiesAsleep) { + //target nearby mobs + const targets = [] + for (let i = 0, len = mob.length; i < len; i++) { + const sub = Matter.Vector.sub(this.position, mob[i].position); + const dist = Matter.Vector.magnitude(sub); + if (dist < 1400 && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0) { + targets.push(mob[i].position) } - if (!this.lockedOn) { - //grab a power up if it is (ammo) or (a heal when player is low) + } + for (let i = 0; i < 14; i++) { + const SPEED = 35 + 20 * Math.random() + if (targets.length > 0) { // aim near a random target + const SPREAD = 100 + const INDEX = Math.floor(Math.random() * targets.length) + const WHERE = { + x: targets[INDEX].x + SPREAD * (Math.random() - 0.5), + y: targets[INDEX].y + SPREAD * (Math.random() - 0.5) + } + needle(this.position, Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(WHERE, this.position)), SPEED)) + } else { // aim in random direction + const ANGLE = 2 * Math.PI * Math.random() + needle(this.position, { + x: SPEED * Math.cos(ANGLE), + y: SPEED * Math.sin(ANGLE) + }) + } + + function needle(pos, velocity) { + const me = bullet.length; + bullet[me] = Bodies.rectangle(pos.x, pos.y, 23 * b.modBulletSize, 2 * b.modBulletSize, b.fireAttributes(Math.atan2(velocity.y, velocity.x))); + Matter.Body.setVelocity(bullet[me], velocity); + World.add(engine.world, bullet[me]); //add bullet to world + bullet[me].endCycle = game.cycle + 60 + Math.floor(15 * Math.random()); + // bullet[me].dmg = 1.1+b.modExtraDmg; + bullet[me].do = function () {}; + } + } + } + } + } + } + }, { + name: "spores", //12 + description: "release an orb that discharges spores after 2 seconds
seeks out targets
passes through blocks", + ammo: 0, + ammoPack: 5, + have: false, + isStarterGun: false, + fire() { + const me = bullet.length; + const dir = mech.angle; + bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 20, 4.5, b.fireAttributes(dir, false)); + b.fireProps(mech.crouch ? 75 : 55, mech.crouch ? 25 : 14, dir, me); //cd , speed + b.drawOneBullet(bullet[me].vertices); + Matter.Body.setDensity(bullet[me], 0.000001); + bullet[me].endCycle = game.cycle + 100; + bullet[me].frictionAir = 0; + bullet[me].friction = 0.5; + bullet[me].restitution = 0.3; + bullet[me].minDmgSpeed = 0; + bullet[me].onDmg = function () {}; + bullet[me].do = function () { + if (!mech.isBodiesAsleep) { + const SCALE = 1.017 + Matter.Body.scale(this, SCALE, SCALE); + this.frictionAir += 0.00023; + } + + this.force.y += this.mass * 0.00045; + + //draw green glow + ctx.fillStyle = "rgba(0,200,125,0.16)"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, 26, 0, 2 * Math.PI); + ctx.fill(); + }; + + //spawn bullets on end + bullet[me].onEnd = function () { + const NUM = 9; + for (let i = 0; i < NUM; i++) { + const bIndex = bullet.length; + const RADIUS = 3 * b.modBulletSize; + bullet[bIndex] = Bodies.circle(this.position.x, this.position.y, RADIUS, { + // density: 0.0015, //frictionAir: 0.01, + inertia: Infinity, + restitution: 0.5, + angle: dir, + friction: 0, + frictionAir: 0.011, + dmg: 1.8 + b.modExtraDmg, //damage done in addition to the damage from momentum + classType: "bullet", + collisionFilter: { + category: 0x000100, + mask: 0x000011 //no collide with body + }, + endCycle: game.cycle + Math.floor((360 + Math.floor(Math.random() * 240)) * b.isModBulletsLastLonger), + minDmgSpeed: 0, + onDmg() { + this.endCycle = 0; //bullet ends cycle after doing damage + }, + onEnd() {}, + lookFrequency: 67 + Math.floor(47 * Math.random()), + do() { + //find mob targets + if (!(game.cycle % this.lookFrequency)) { + this.closestTarget = null; + this.lockedOn = null; let closeDist = Infinity; - for (let i = 0, len = powerUp.length; i < len; ++i) { - if ( - ((powerUp[i].name !== "field" && powerUp[i].name !== "heal") || (powerUp[i].name === "heal" && mech.health < 0.8)) && - Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 && - Matter.Query.ray(body, this.position, powerUp[i].position).length === 0 - ) { - const TARGET_VECTOR = Matter.Vector.sub(this.position, powerUp[i].position) - const DIST = Matter.Vector.magnitude(TARGET_VECTOR); - if (DIST < closeDist) { - if (DIST < 50) { //eat the power up if close enough - powerUp[i].effect(); - Matter.World.remove(engine.world, powerUp[i]); - powerUp.splice(i, 1); - break; - } - closeDist = DIST; - this.lockedOn = powerUp[i] + for (let i = 0, len = mob.length; i < len; ++i) { + if (Matter.Query.ray(map, this.position, mob[i].position).length === 0) { + // Matter.Query.ray(body, this.position, mob[i].position).length === 0 + const targetVector = Matter.Vector.sub(this.position, mob[i].position) + const dist = Matter.Vector.magnitude(targetVector); + if (dist < closeDist) { + this.closestTarget = mob[i].position; + closeDist = dist; + this.lockedOn = Matter.Vector.normalise(targetVector); + if (0.3 > Math.random()) break //doesn't always target the closest mob } } } } - } - if (this.lockedOn) { //accelerate towards mobs - this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST) - } else { //accelerate towards mouse - this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, game.mouseInGame)), -this.mass * THRUST) - } - // speed cap instead of friction to give more agility - if (this.speed > 6) { - Matter.Body.setVelocity(this, { - x: this.velocity.x * 0.97, - y: this.velocity.y * 0.97 - }); - } - } - }) - b.fireProps(mech.crouch ? 19 : 15, mech.crouch ? 35 : 1, dir, me); //cd , speed - b.drawOneBullet(bullet[me].vertices); + //accelerate towards mobs + const THRUST = this.mass * 0.0009 + if (this.lockedOn) { + this.force.x -= THRUST * this.lockedOn.x + this.force.y -= THRUST * this.lockedOn.y + } else { + this.force.y += this.mass * 0.00025; //gravity + } + }, + }); + const SPEED = 9; + const ANGLE = 2 * Math.PI * Math.random() + Matter.Body.setVelocity(bullet[bIndex], { + x: SPEED * Math.cos(ANGLE), + y: SPEED * Math.sin(ANGLE) + }); + World.add(engine.world, bullet[bIndex]); //add bullet to world + } } - }, - { - //draw a halo, since there will only be 1-3 balls - name: "junk-bots", //14 - description: "release large drones that defend the space around the player
despawn after not doing damage for 3 seconds", - ammo: 0, - ammoPack: 20, - have: false, - isStarterGun: true, - fire() { - const THRUST = 0.004 - const dir = mech.angle + 0.2 * (Math.random() - 0.5); - const me = bullet.length; - const RADIUS = (18 + 5 * Math.random()) * b.modBulletSize - const LENGTH = 0.6 + 0.8 * Math.random() - bullet[me] = Bodies.rectangle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), RADIUS * LENGTH, RADIUS / LENGTH, { - isOrb: true, - angle: dir, - // inertia: Infinity, - friction: 0, - frictionAir: 0.06, - restitution: 1, - dmg: b.extraDmg, // 0.14 //damage done in addition to the damage from momentum - minDmgSpeed: 2, - lookFrequency: 37 + Math.floor(37 * Math.random()), - endCycle: game.cycle + Math.floor((170 + 120 * Math.random()) * b.modBulletsLastLonger), - classType: "bullet", - collisionFilter: { - category: 0x000100, - mask: 0x010111 //self, mob,map,body collide - }, - range: 500 + 150 * Math.random(), - lockedOn: null, - onDmg() { - // this.endCycle = 0; - this.lockedOn = null - this.endCycle = game.cycle + Math.floor(180 * b.modBulletsLastLonger) - }, - onEnd() {}, - do() { - if (!(game.cycle % this.lookFrequency)) { - this.lockedOn = null; - let closeDist = Infinity; - for (let i = 0, len = mob.length; i < len; ++i) { - const TARGET_VECTOR = Matter.Vector.sub(mech.pos, mob[i].position) + + } + }, { + name: "drones", //13 + description: "release drones that seek out targets for 16 seconds
follows mouse if no targets are found", + ammo: 0, + ammoPack: 20, + have: false, + isStarterGun: true, + fire() { + const THRUST = 0.0015 + const dir = mech.angle + 0.2 * (Math.random() - 0.5); + const me = bullet.length; + const RADIUS = (4.5 + 3 * Math.random()) * b.modBulletSize + bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), RADIUS, { + angle: dir, + inertia: Infinity, + friction: 0, + frictionAir: 0.0005, + restitution: 1, + dmg: 0.14 + b.modExtraDmg, //damage done in addition to the damage from momentum + lookFrequency: 79 + Math.floor(37 * Math.random()), + endCycle: game.cycle + Math.floor((780 + 360 * Math.random()) * b.isModBulletsLastLonger), + classType: "bullet", + collisionFilter: { + category: 0x000100, + mask: 0x010111 //self collide + }, + minDmgSpeed: 0, + lockedOn: null, + isFollowMouse: true, + onDmg() { + this.lockedOn = null + }, + onEnd() {}, + do() { + this.force.y += this.mass * 0.0002; + //find mob targets + if (!(game.cycle % this.lookFrequency)) { + this.lockedOn = null; + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + ) { + const TARGET_VECTOR = Matter.Vector.sub(this.position, mob[i].position) const DIST = Matter.Vector.magnitude(TARGET_VECTOR); - if (DIST < this.range && DIST < closeDist && Matter.Query.ray(map, this.position, mob[i].position).length === 0) { + if (DIST < closeDist) { closeDist = DIST; this.lockedOn = mob[i] } } } - - const distanceToPlayer = Matter.Vector.magnitude(Matter.Vector.sub(this.position, mech.pos)) - if (this.lockedOn) { //accelerate towards mobs - this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST) - this.frictionAir = 0.06 - } else if (distanceToPlayer > 0.2 * this.range) { - this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, mech.pos)), -this.mass * THRUST * 0.3) - this.frictionAir = 0.02 - // // speed cap instead of friction to give more agility - // if (this.speed > 14) { - // Matter.Body.setVelocity(this, { - // x: this.velocity.x * 0.97, - // y: this.velocity.y * 0.97 - // }); - // } - } else { //must be close to player //add some random motion - this.frictionAir = 0 + if (!this.lockedOn) { + //grab a power up if it is (ammo) or (a heal when player is low) + let closeDist = Infinity; + for (let i = 0, len = powerUp.length; i < len; ++i) { + if ( + ((powerUp[i].name !== "field" && powerUp[i].name !== "heal") || (powerUp[i].name === "heal" && mech.health < 0.8)) && + Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 && + Matter.Query.ray(body, this.position, powerUp[i].position).length === 0 + ) { + const TARGET_VECTOR = Matter.Vector.sub(this.position, powerUp[i].position) + const DIST = Matter.Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + if (DIST < 50) { //eat the power up if close enough + powerUp[i].effect(); + Matter.World.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + break; + } + closeDist = DIST; + this.lockedOn = powerUp[i] + } + } + } } } - }) - b.fireProps(mech.crouch ? 40 : 10, mech.crouch ? 40 : 10, dir, me); //cd , speed - b.drawOneBullet(bullet[me].vertices); - } - }, - ] + if (this.lockedOn) { //accelerate towards mobs + this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST) + } else { //accelerate towards mouse + this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, game.mouseInGame)), -this.mass * THRUST) + } + // speed cap instead of friction to give more agility + if (this.speed > 6) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.97, + y: this.velocity.y * 0.97 + }); + } + } + }) + b.fireProps(mech.crouch ? 19 : 15, mech.crouch ? 35 : 1, dir, me); //cd , speed + b.drawOneBullet(bullet[me].vertices); + } + }, { + //draw a halo, since there will only be 1-3 balls + name: "junk-bots", //14 + description: "release large drones that defend the space around the player
despawn after not doing damage for 3 seconds", + ammo: 0, + ammoPack: 20, + have: false, + isStarterGun: true, + fire() { + const THRUST = 0.004 + const dir = mech.angle + 0.2 * (Math.random() - 0.5); + const me = bullet.length; + const RADIUS = (18 + 5 * Math.random()) * b.modBulletSize + const LENGTH = 0.6 + 0.8 * Math.random() + bullet[me] = Bodies.rectangle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), RADIUS * LENGTH, RADIUS / LENGTH, { + isOrb: true, + angle: dir, + // inertia: Infinity, + friction: 0, + frictionAir: 0.06, + restitution: 1, + dmg: b.modExtraDmg, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 2, + lookFrequency: 37 + Math.floor(37 * Math.random()), + endCycle: game.cycle + Math.floor((170 + 120 * Math.random()) * b.isModBulletsLastLonger), + classType: "bullet", + collisionFilter: { + category: 0x000100, + mask: 0x010111 //self, mob,map,body collide + }, + range: 700, + lockedOn: null, + onDmg() { + // this.endCycle = 0; + this.lockedOn = null + this.endCycle = game.cycle + Math.floor(180 * b.isModBulletsLastLonger) + }, + onEnd() {}, + do() { + if (!(game.cycle % this.lookFrequency)) { + this.lockedOn = null; + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + const TARGET_VECTOR = Matter.Vector.sub(mech.pos, mob[i].position) + const DIST = Matter.Vector.magnitude(TARGET_VECTOR); + if (DIST < this.range && DIST < closeDist && Matter.Query.ray(map, this.position, mob[i].position).length === 0) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + + const distanceToPlayer = Matter.Vector.magnitude(Matter.Vector.sub(this.position, mech.pos)) + if (this.lockedOn) { //accelerate towards mobs + this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST) + this.frictionAir = 0.06 + } else if (distanceToPlayer > 0.2 * this.range) { + this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, mech.pos)), -this.mass * THRUST * 0.3) + this.frictionAir = 0.02 + // // speed cap instead of friction to give more agility + // if (this.speed > 14) { + // Matter.Body.setVelocity(this, { + // x: this.velocity.x * 0.97, + // y: this.velocity.y * 0.97 + // }); + // } + } else { //must be close to player //add some random motion + this.frictionAir = 0 + } + } + }) + b.fireProps(mech.crouch ? 40 : 10, mech.crouch ? 40 : 10, dir, me); //cd , speed + b.drawOneBullet(bullet[me].vertices); + } + }, ] }; \ No newline at end of file diff --git a/js/engine.js b/js/engine.js index a58242e..511397e 100644 --- a/js/engine.js +++ b/js/engine.js @@ -91,13 +91,10 @@ function mobCollisionChecks(event) { const v = Matter.Vector.magnitude(Matter.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.02; - dmg = Math.min(Math.max(dmg, 0.02), 0.2); - // console.log(`mass = ${obj.mass} \n`, `dmg = ${dmg}\n`, `v = ${v}\n`) - // console.log(v, dmg) + 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); - game.drawList.push({ - //add dmg to draw queue + 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, @@ -144,7 +141,7 @@ function mobCollisionChecks(event) { 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); if (mob[k].onHit) mob[k].onHit(k); - if (b.annihilation && mob[k].dropPowerUp) { + if (b.modAnnihilation && mob[k].dropPowerUp) { mob[k].death(); game.drawList.push({ //add dmg to draw queue @@ -182,7 +179,7 @@ function mobCollisionChecks(event) { if (obj.classType === "bullet" && obj.speed > obj.minDmgSpeed) { mob[k].foundPlayer(); // const dmg = b.dmgScale * (obj.dmg + 0.15 * obj.mass * Matter.Vector.magnitude(Matter.Vector.sub(mob[k].velocity, obj.velocity))); - const dmg = b.dmgScale * (obj.dmg + b.extraDmg + 0.15 * obj.mass * Matter.Vector.magnitude(Matter.Vector.sub(mob[k].velocity, obj.velocity))) + const dmg = b.dmgScale * (obj.dmg + b.modExtraDmg + 0.15 * obj.mass * Matter.Vector.magnitude(Matter.Vector.sub(mob[k].velocity, obj.velocity))) mob[k].damage(dmg); obj.onDmg(); //some bullets do actions when they hits things, like despawn game.drawList.push({ diff --git a/js/game.js b/js/game.js index e730974..919edeb 100644 --- a/js/game.js +++ b/js/game.js @@ -167,6 +167,10 @@ const game = { document.getElementById("mods").innerHTML = text }, replaceTextLog: true, + // + SVGleftMouse: ' ', + SVGrightMouse: ' ', makeTextLog(text, time = 180) { if (game.replaceTextLog) { document.getElementById("text-log").innerHTML = text; @@ -508,6 +512,7 @@ const game = { if (mech.holdingTarget) { holdTarget = mech.holdingTarget; } + mech.fireCDcycle = 0 mech.drop(); level.fill = []; level.fillBG = []; diff --git a/js/index.js b/js/index.js index 981236f..868c572 100644 --- a/js/index.js +++ b/js/index.js @@ -2,6 +2,27 @@ /* TODO: ******************************************* ***************************************************** +gun: Spirit Bomb + use charge up like rail gun + +left and right click mouse icons for text displays + +mod: auto pick up guns, heals, ammo + use the same rule for drones + maybe give some other bonus too? + +mod: + move speed and jump height + will leg animations look strange? + that's OK for a mod + this could just slow the mobs down instead? + how? + +rework junk bot, or remove it + it's behavior is too unpredictable + range is unclear + having the bullets last long after doing dmg isn't fun + we want a fun gun that acts like a melee weapon + mouse can get suck as clicked if the user clicks off the window can lead to gun lock up until player pressed mouse again should I really need to fix this? diff --git a/js/level.js b/js/level.js index 7230804..da81052 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(6) + // b.giveGuns(1) // mech.fieldUpgrades[7].effect(); - // b.giveMod(13) + b.giveMod(15) // spawn.pickList = ["ghoster", "ghoster"] this.intro(); //starting level @@ -1475,7 +1475,7 @@ const level = { level.onLevel++; if (level.onLevel > level.levels.length - 1) level.onLevel = 0; - game.clearNow = true; //triggers in the physics engine to remove all physics bodies + game.clearNow = true; //triggers in game.clearMap to remove all physics bodies and setup for new map } }, death() { diff --git a/js/mobs.js b/js/mobs.js index 16daa84..c2ad0dc 100644 --- a/js/mobs.js +++ b/js/mobs.js @@ -231,7 +231,7 @@ const mobs = { // ctx.lineDashOffset = 6*(game.cycle % 215); if (this.distanceToPlayer() < this.laserRange) { //if (Math.random()>0.2 && this.seePlayer.yes && this.distanceToPlayer2()<800000) { - if (!b.AoEImmunity) { + if (!b.isModAoEImmunity) { mech.damage(0.0003 * game.dmgScale); if (mech.fieldMeter > 0.1) mech.fieldMeter -= 0.005 } diff --git a/js/player.js b/js/player.js index ca62462..548ce6c 100644 --- a/js/player.js +++ b/js/player.js @@ -6,15 +6,15 @@ const mech = { spawn() { //load player in matter.js physic engine // let vector = Vertices.fromPath("0 40 50 40 50 115 0 115 30 130 20 130"); //player as a series of vertices - let vector = Vertices.fromPath("0,40, 50,40, 50,115, 30,130, 20,130, 0,115, 0,40"); //player as a series of vertices - playerBody = Matter.Bodies.fromVertices(0, 0, vector); + let vertices = Vertices.fromPath("0,40, 50,40, 50,115, 30,130, 20,130, 0,115, 0,40"); //player as a series of vertices + playerBody = Matter.Bodies.fromVertices(0, 0, vertices); jumpSensor = Bodies.rectangle(0, 46, 36, 6, { //this sensor check if the player is on the ground to enable jumping sleepThreshold: 99999999999, isSensor: true }); - vector = Vertices.fromPath("16 -82 2 -66 2 -37 43 -37 43 -66 30 -82"); - playerHead = Matter.Bodies.fromVertices(0, -55, vector); //this part of the player lowers on crouch + vertices = Vertices.fromPath("16 -82 2 -66 2 -37 43 -37 43 -66 30 -82"); + playerHead = Matter.Bodies.fromVertices(0, -55, vertices); //this part of the player lowers on crouch headSensor = Bodies.rectangle(0, -57, 48, 45, { //senses if the player's head is empty and can return after crouching sleepThreshold: 99999999999, @@ -65,7 +65,7 @@ const mech = { }, defaultMass: 5, mass: 5, - Fx: 0.015, //run Force on ground + Fx: 0.015, //run Force on ground //this is reset in b.setModDefaults() FxAir: 0.015, //run Force in Air definePlayerMass(mass = mech.defaultMass) { Matter.Body.setMass(player, mass); @@ -110,7 +110,7 @@ const mech = { Sy: 0, //adds a smoothing effect to vertical only Vx: 0, Vy: 0, - jumpForce: 0.38, + jumpForce: 0.38, //this is reset in b.setModDefaults() gravity: 0.0019, friction: { ground: 0.01, @@ -211,6 +211,14 @@ const mech = { this.doCrouch(); this.yOff = this.yOffWhen.jump; this.hardLandCD = mech.cycle + Math.min(momentum / 6 - 6, 40) + + if (game.isBodyDamage && momentum > 200 && player.velocity.y > 20) { //falling damage + mech.damageImmune = mech.cycle + 30; //player is immune to collision damage for 30 cycles + let dmg = Math.sqrt(momentum - 200) * 0.01 + console.log(dmg, momentum, player.velocity.y) + dmg = Math.min(Math.max(dmg, 0.02), 0.15); + mech.damage(dmg); + } } else { this.yOffGoal = this.yOffWhen.stand; } @@ -242,11 +250,17 @@ const mech = { //horizontal move on ground //apply a force to move if (keys[65] || keys[37]) { //left / a - player.force.x -= this.Fx - if (player.velocity.x > -2) player.force.x -= this.Fx * 0.5 + if (player.velocity.x > -2) { + player.force.x -= this.Fx * 1.5 + } else { + player.force.x -= this.Fx + } } else if (keys[68] || keys[39]) { //right / d - player.force.x += this.Fx - if (player.velocity.x < 2) player.force.x += this.Fx * 0.5 + if (player.velocity.x < 2) { + player.force.x += this.Fx * 1.5 + } else { + player.force.x += this.Fx + } } else { const stoppingFriction = 0.92; Matter.Body.setVelocity(player, { @@ -287,76 +301,6 @@ const mech = { //smoothly move leg height towards height goal this.yOff = this.yOff * 0.85 + this.yOffGoal * 0.15; }, - gamepadMove() { - if (this.onGround) { //on ground ********************** - if (this.crouch) { - if (game.gamepad.leftAxis.y !== -1 && this.isHeadClear && this.hardLandCD < mech.cycle) this.undoCrouch(); - } else if (game.gamepad.leftAxis.y === -1 || this.hardLandCD > mech.cycle) { - this.doCrouch(); //on ground && not crouched and pressing s or down - } else if (game.gamepad.jump && this.buttonCD_jump + 20 < mech.cycle && this.yOffWhen.stand > 23) { - this.buttonCD_jump = mech.cycle; //can't jump again until 20 cycles pass - - //apply a fraction of the jump force to the body the player is jumping off of - Matter.Body.applyForce(mech.standingOn, mech.pos, { - x: 0, - y: this.jumpForce * 0.12 * Math.min(mech.standingOn.mass, 5) - }); - - player.force.y = -this.jumpForce; //player jump force - Matter.Body.setVelocity(player, { //zero player y-velocity for consistent jumps - x: player.velocity.x, - y: 0 - }); - } - - //horizontal move on ground - //apply a force to move - if (game.gamepad.leftAxis.x === -1) { //left / a - player.force.x -= this.Fx - if (player.velocity.x > -2) player.force.x -= this.Fx * 0.5 - } else if (game.gamepad.leftAxis.x === 1) { //right / d - player.force.x += this.Fx - if (player.velocity.x < 2) player.force.x += this.Fx * 0.5 - } else { - const stoppingFriction = 0.92; - Matter.Body.setVelocity(player, { - x: player.velocity.x * stoppingFriction, - y: player.velocity.y * stoppingFriction - }); - } - //come to a stop if fast or if no move key is pressed - if (player.speed > 4) { - const stoppingFriction = (this.crouch) ? 0.65 : 0.89; - Matter.Body.setVelocity(player, { - x: player.velocity.x * stoppingFriction, - y: player.velocity.y * stoppingFriction - }); - } - - } else { // in air ********************************** - //check for short jumps - if ( - this.buttonCD_jump + 60 > mech.cycle && //just pressed jump - !game.gamepad.jump && //but not pressing jump key - this.Vy < 0 //moving up - ) { - Matter.Body.setVelocity(player, { - //reduce player y-velocity every cycle - x: player.velocity.x, - y: player.velocity.y * 0.94 - }); - } - const limit = 125 / player.mass / player.mass - if (game.gamepad.leftAxis.x === -1) { - if (player.velocity.x > -limit) player.force.x -= this.FxAir; // move player left / a - } else if (game.gamepad.leftAxis.x === 1) { - if (player.velocity.x < limit) player.force.x += this.FxAir; //move player right / d - } - } - - //smoothly move leg height towards height goal - this.yOff = this.yOff * 0.85 + this.yOffGoal * 0.15; - }, alive: true, death() { if (b.modIsImmortal) { //if player has the immortality buff, spawn on the same level with randomized stats @@ -517,7 +461,7 @@ const mech = { document.getElementById("dmg").style.opacity = 0.1 + Math.min(0.6, dmg * 4); //chance to build a drone on damage from mod - if (b.makeDroneOnDamage) { + if (b.isModDroneOnDamage) { const len = (dmg - 0.08 + 0.05 * Math.random()) / 0.05 for (let i = 0; i < len; i++) { if (Math.random() < 0.6) b.guns[13].fire() //spawn drone @@ -730,7 +674,7 @@ const mech = { mech.fieldMeter += mech.fieldRegen; ctx.fillStyle = "rgba(0, 0, 0, 0.4)"; ctx.fillRect(this.pos.x - this.radius, this.pos.y - 50, range, 10); - ctx.fillStyle = "rgb(50,220,255)"; + ctx.fillStyle = "#0af"; ctx.fillRect(this.pos.x - this.radius, this.pos.y - 50, range * this.fieldMeter, 10); } else { mech.fieldMeter = 1 @@ -1071,7 +1015,7 @@ const mech = { hold() {}, fieldText() { game.replaceTextLog = true; - game.makeTextLog(`
  ${mech.fieldUpgrades[mech.fieldMode].name}
(right click or space bar)

${mech.fieldUpgrades[mech.fieldMode].description}`, 1000); + game.makeTextLog(`${game.SVGrightMouse} ${mech.fieldUpgrades[mech.fieldMode].name}

${mech.fieldUpgrades[mech.fieldMode].description}`, 1000); game.replaceTextLog = false; document.getElementById("field").innerHTML = mech.fieldUpgrades[mech.fieldMode].name //add field }, @@ -1179,13 +1123,13 @@ const mech = { }, { name: "plasma torch", - description: "field emits a beam of destructive ionized gas
decreased shield range and efficiency", + description: "field emits a beam of destructive ionized gas
decreased shield range and efficiency", effect: () => { mech.fieldMode = 2; mech.fieldText(); mech.setHoldDefaults(); // mech.fieldShieldingScale = 2; - mech.grabRange = 125; + // mech.grabRange = 125; mech.fieldArc = 0.05 //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) mech.calculateFieldThreshold(); //run after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) mech.hold = function () { @@ -1346,13 +1290,12 @@ const mech = { }, { name: "negative mass field", - description: "field nullifies  gravity
player can hold more massive objects
can fire while field is active", + description: "field nullifies  gravity
can fire while field is active", effect: () => { mech.fieldMode = 3; mech.fieldText(); mech.setHoldDefaults(); mech.fieldFire = true; - mech.holdingMassScale = 0.05; //can hold heavier blocks with lower cost to jumping mech.hold = function () { if (mech.isHolding) { @@ -1434,7 +1377,7 @@ const mech = { }, { name: "standing wave harmonics", - description: "you are surrounded by oscillating shields
decreased field regeneration", + description: "you are surrounded by oscillating shields
decreased field regeneration", effect: () => { mech.fieldMode = 4; mech.fieldText(); @@ -1479,7 +1422,7 @@ const mech = { name: "nano-scale manufacturing", description: "excess field energy used to build drones
increased energy regeneration", effect: () => { - let gunIndex = Math.random() < 0.5 ? 13 : 14 + let gunIndex = 13 //Math.random() < 0.5 ? 13 : 14 mech.fieldMode = 5; mech.fieldText(); mech.setHoldDefaults(); diff --git a/js/powerups.js b/js/powerups.js index 559ca89..60549bb 100644 --- a/js/powerups.js +++ b/js/powerups.js @@ -9,10 +9,10 @@ const powerUps = { }, effect() { let heal = (this.size / 40) ** 2 - if (b.fullHeal) heal = Infinity + if (b.isModFullHeal) heal = Infinity heal = Math.min(1 - mech.health, heal) mech.addHealth(heal); - if (heal > 0) game.makeTextLog(" heal " + (heal * 100).toFixed(0) + "%", 300) + if (heal > 0) game.makeTextLog("
  heal " + (heal * 100).toFixed(0) + "%", 300) } }, ammo: { @@ -48,13 +48,13 @@ const powerUps = { const ammo = Math.ceil((target.ammoPack * (0.45 + 0.08 * Math.random())) / b.dmgScale); target.ammo += ammo; game.updateGunHUD(); - game.makeTextLog("+" + ammo + " ammo for " + target.name + "", 300); + game.makeTextLog("
  +" + ammo + " ammo for " + target.name + "", 300); } } }, field: { name: "field", - color: "#0bf", + color: "#0af", size() { return 45; }, @@ -124,7 +124,7 @@ const powerUps = { if (options.length > 0) { let newGun = options[Math.floor(Math.random() * options.length)]; if (b.activeGun === null) b.activeGun = newGun //if no active gun switch to new gun - game.makeTextLog(`
  ${b.guns[newGun].name}
(left click)

${b.guns[newGun].description}`, 900); + game.makeTextLog(`${game.SVGleftMouse} ${b.guns[newGun].name}

${b.guns[newGun].description}`, 900); b.guns[newGun].have = true; b.inventory.push(newGun); b.guns[newGun].ammo += b.guns[newGun].ammoPack * 2; diff --git a/js/spawn.js b/js/spawn.js index 1f551d4..c09528b 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -457,7 +457,7 @@ const spawn = { this.healthBar(); //when player is inside event horizon if (Matter.Vector.magnitude(Matter.Vector.sub(this.position, player.position)) < eventHorizon) { - if (!b.AoEImmunity) { + if (!b.isModAoEImmunity) { mech.damage(0.00015 * game.dmgScale); if (mech.fieldMeter > 0.1) mech.fieldMeter -= 0.01 } @@ -548,7 +548,7 @@ const spawn = { ctx.fill(); //when player is inside event horizon if (Matter.Vector.magnitude(Matter.Vector.sub(this.position, player.position)) < eventHorizon) { - if (!b.AoEImmunity) { + if (!b.isModAoEImmunity) { mech.damage(0.00015 * game.dmgScale); if (mech.fieldMeter > 0.1) mech.fieldMeter -= 0.01 } @@ -680,14 +680,14 @@ const spawn = { this.laser(); }; }, - striker(x, y, radius = 15 + Math.ceil(Math.random() * 25)) { + striker(x, y, radius = 14 + Math.ceil(Math.random() * 25)) { mobs.spawn(x, y, 5, radius, "rgb(221,102,119)"); let me = mob[mob.length - 1]; - me.accelMag = 0.0004 * game.accelScale; + me.accelMag = 0.0003 * game.accelScale; me.g = 0.0002; //required if using 'gravity' me.frictionStatic = 0; me.friction = 0; - me.delay = 90; + me.delay = 100; Matter.Body.rotate(me, Math.PI * 0.1); me.onDamage = function () { this.cd = game.cycle + this.delay; diff --git a/style.css b/style.css index 47d1f61..1dc53a6 100644 --- a/style.css +++ b/style.css @@ -208,8 +208,12 @@ em { opacity: 0.7; } +.mouse-icon { + margin-bottom: -20px; +} + .color-f { - color: #0bf; + color: #0af; } .color-b { @@ -255,7 +259,7 @@ em { } .field { - background: #0bf; + background: #0af; } .mod { @@ -264,6 +268,12 @@ em { .gun { background: #149; + margin-bottom: -2px; +} + +.heal { + background: #0d9; + margin-bottom: -2px; } .box { @@ -285,48 +295,6 @@ em { justify-self: center; } -.mouse { - color: #ccc; - position: relative; - padding: 37px 30px 37px 30px; - border-radius: 25px; - border: 2px solid #444; - background-color: rgba(255, 255, 255, 0.5); -} - -.mouse:after { - content: ""; - position: absolute; - z-index: 1; - top: 4px; - left: 26px; - border-radius: 25px; - /* background: #444; */ - border: 2px solid #444; - - width: 4px; - height: 20px; - border-radius: 10px / 25px; -} - -.mouse-line { - position: relative; - top: 30px; - left: 0px; -} - -.mouse-line:after { - content: ""; - position: absolute; - z-index: 1; - top: -35px; - left: -30px; - width: 60px; - height: 2px; - border-radius: 8px; - background: #444; -} - .right { text-align: right; } \ No newline at end of file