From bf5f8661fc2506f62758343bee7269b9e8648811 Mon Sep 17 00:00:00 2001 From: landgreen Date: Mon, 25 Sep 2023 19:49:07 -0700 Subject: [PATCH] gun ammo descriptions missile Bot: +10% bigger explosions, +10% damage, +7% fire rate plasma Bot: +40% damage, and drains 2% less energy 1st ionization energy: 8->11 energy per heal mass-energy: defense reduction factor 0.13->0.33 neutron bomb: +25% damage non-renewables: 78->88% damage junk DNA: applies to all damage, not just spores pseudoscience: adds (1-4)->(1-3) JUNK to tech pool per free research futures exchange: 4.1->4.3 duplication per cancel default skin has slightly more narrow legs added ammo to gun descriptions --- js/bullet.js | 4228 ++++++++++++++++++++++++------------------------- js/index.js | 8 +- js/level.js | 4 +- js/player.js | 113 +- js/powerup.js | 12 +- js/tech.js | 213 +-- todo.txt | 22 +- 7 files changed, 2341 insertions(+), 2259 deletions(-) diff --git a/js/bullet.js b/js/bullet.js index a2b2300..ff80835 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -1108,10 +1108,7 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world } - grenadeNeutron = function (where = { - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - }, angle = m.angle, size = 1) { + grenadeNeutron = function (where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) }, angle = m.angle, size = 1) { const me = bullet.length; bullet[me] = Bodies.polygon(where.x, where.y, 10, 4, b.fireAttributes(angle, false)); b.fireProps((m.crouch ? 45 : 25) / Math.pow(0.92, tech.missileCount), m.crouch ? 35 : 20, angle, me); //cd , speed @@ -1132,15 +1129,9 @@ const b = { Matter.Body.scale(bullet[me], SCALE, SCALE); speed = m.crouch ? 25 : 15 // speed = m.crouch ? 43 : 32 - Matter.Body.setVelocity(bullet[me], { - x: m.Vx / 2 + speed * Math.cos(angle), - y: m.Vy / 2 + speed * Math.sin(angle) - }); + Matter.Body.setVelocity(bullet[me], { x: m.Vx / 2 + speed * Math.cos(angle), y: m.Vy / 2 + speed * Math.sin(angle) }); const MAG = 0.005 - bullet[me].thrust = { - x: bullet[me].mass * MAG * Math.cos(angle), - y: bullet[me].mass * MAG * Math.sin(angle) - } + bullet[me].thrust = { x: bullet[me].mass * MAG * Math.cos(angle), y: bullet[me].mass * MAG * Math.sin(angle) } } bullet[me].beforeDmg = function () { }; @@ -1148,26 +1139,17 @@ const b = { bullet[me].do = function () { const onCollide = () => { this.collisionFilter.mask = 0; //non collide with everything - Matter.Body.setVelocity(this, { - x: 0, - y: 0 - }); - if (tech.isRPG) this.thrust = { - x: 0, - y: 0 - } + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + if (tech.isRPG) this.thrust = { x: 0, y: 0 } this.do = this.radiationMode; } const mobCollisions = Matter.Query.collides(this, mob) if (mobCollisions.length) { onCollide() this.stuckTo = mobCollisions[0].bodyA - mobs.statusDoT(this.stuckTo, 0.5, 360) //apply radiation damage status effect on direct hits + mobs.statusDoT(this.stuckTo, 0.6, 360) //apply radiation damage status effect on direct hits if (this.stuckTo.isVerticesChange) { - this.stuckToRelativePosition = { - x: 0, - y: 0 - } + this.stuckToRelativePosition = { x: 0, y: 0 } } else { //find the relative position for when the mob is at angle zero by undoing the mobs rotation this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) @@ -1234,17 +1216,14 @@ const b = { } } //aoe damage to mobs - let dmg = m.dmgScale * 0.11 * tech.radioactiveDamage + let dmg = m.dmgScale * 0.15 * tech.radioactiveDamage for (let i = 0, len = mob.length; i < len; i++) { if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.damageRadius + mob[i].radius) { - if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way + if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.2 //reduce damage if a wall is in the way mob[i].damage(mob[i].shield ? dmg * 3 : dmg); mob[i].locatePlayer(); if (tech.isNeutronSlow && mob[i].speed > 4) { - Matter.Body.setVelocity(mob[i], { - x: mob[i].velocity.x * 0.97, - y: mob[i].velocity.y * 0.97 - }); + Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.97, y: mob[i].velocity.y * 0.97 }); } } } @@ -1255,16 +1234,12 @@ const b = { ctx.fill(); ctx.globalCompositeOperation = "source-over" if (tech.isNeutronSlow) { - let slow = (who, radius = this.explodeRad * 3.2) => { for (i = 0, len = who.length; i < len; i++) { const sub = Vector.sub(this.position, who[i].position); const dist = Vector.magnitude(sub); if (dist < radius) { - Matter.Body.setVelocity(who[i], { - x: who[i].velocity.x * 0.975, - y: who[i].velocity.y * 0.975 - }); + Matter.Body.setVelocity(who[i], { x: who[i].velocity.x * 0.975, y: who[i].velocity.y * 0.975 }); } } } @@ -1313,10 +1288,7 @@ const b = { if (!input.field && m.crouch) { const cycles = Math.floor(m.crouch ? 50 : 30) //30 const speed = m.crouch ? 44 : 35 - const v = { - x: speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - } + const v = { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) } ctx.strokeStyle = "rgba(68, 68, 68, 0.2)" //color.map ctx.lineWidth = 2 ctx.beginPath() @@ -1333,10 +1305,7 @@ const b = { if (!input.field && m.crouch) { const cycles = Math.floor(m.crouch ? 120 : 80) //30 const speed = m.crouch ? 43 : 32 - const v = { - x: speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - } //m.Vy / 2 + removed to make the path less jerky + const v = { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) } //m.Vy / 2 + removed to make the path less jerky ctx.strokeStyle = "rgba(68, 68, 68, 0.2)" //color.map ctx.lineWidth = 2 ctx.beginPath() @@ -3195,7 +3164,7 @@ const b = { thrust: (tech.isSporeFollow ? 0.0012 : 0.00055) * (1 + 0.5 * (Math.random() - 0.5)), wormSize: wormSize, wormTail: 1 + Math.max(4, Math.min(wormSize - 2 * tech.wormSize, 30)), - dmg: (tech.isMutualism ? 9.5 : 3.2) * wormSize * (tech.isJunkDNA ? 1 + 0.53 * tech.junkCount : 1), + dmg: (tech.isMutualism ? 9.5 : 3.2) * wormSize, lookFrequency: 100 + Math.floor(37 * Math.random()), classType: "bullet", collisionFilter: { @@ -3311,7 +3280,7 @@ const b = { friction: 0, frictionAir: 0.025, thrust: (tech.isSporeFollow ? 0.0011 : 0.0005) * (1 + 0.3 * (Math.random() - 0.5)), - dmg: (tech.isMutualism ? 20 : 7) * (tech.isJunkDNA ? 1 + 0.53 * tech.junkCount : 1), //bonus damage from tech.isMutualism + dmg: (tech.isMutualism ? 20 : 7), //bonus damage from tech.isMutualism lookFrequency: 100 + Math.floor(117 * Math.random()), classType: "bullet", isSpore: true, @@ -3335,6 +3304,7 @@ const b = { if (m.health > m.maxHealth) m.health = m.maxHealth; m.displayHealth(); } + console.log(this.dmg) }, do() { if (this.lockedOn && this.lockedOn.alive) { @@ -3533,7 +3503,7 @@ const b = { cd: simulation.cycle + 10, dmg: 0, setDamage() { //dmg is set to zero after doing damage once, and set back to normal after jumping - this.dmg = radius * (tech.isMutualism ? 2.9 : 1) * (tech.isJunkDNA ? 1 + 0.53 * tech.junkCount : 1) //damage done in addition to the damage from momentum //spores do 7 dmg, worms do 18 + this.dmg = radius * (tech.isMutualism ? 2.9 : 1) //damage done in addition to the damage from momentum //spores do 7 dmg, worms do 18 }, beforeDmg(who) { Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), 10 + 10 * Math.random())); //push away from target @@ -5335,10 +5305,7 @@ const b = { }) Composite.add(engine.world, bullet[me]); //add bullet to world }, - missileBot(position = { - x: player.position.x + 50 * (Math.random() - 0.5), - y: player.position.y + 50 * (Math.random() - 0.5) - }, isConsole = true) { + missileBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.missileBot()`); const me = bullet.length; bullet[me] = Bodies.rectangle(position.x, position.y, 28, 11, { @@ -5350,9 +5317,9 @@ const b = { restitution: 0.7, dmg: 0, // 0.14 //damage done in addition to the damage from momentum minDmgSpeed: 2, - lookFrequency: 27 + Math.ceil(6 * Math.random()), + lookFrequency: 26 + Math.ceil(6 * Math.random()), cd: 0, - delay: Math.floor(65 * b.fireCDscale), + delay: Math.floor(60 * b.fireCDscale), range: 70 + 3 * b.totalBots(), endCycle: Infinity, classType: "bullet", @@ -5387,11 +5354,8 @@ const b = { if (tech.missileCount > 1) { const countReduction = Math.pow(0.85, tech.missileCount) - const size = 0.9 * Math.sqrt(countReduction) - const direction = { - x: Math.cos(angle), - y: Math.sin(angle) - } + const size = Math.sqrt(countReduction) + const direction = { x: Math.cos(angle), y: Math.sin(angle) } const push = Vector.mult(Vector.perp(direction), 0.015 * countReduction / Math.sqrt(tech.missileCount)) for (let i = 0; i < tech.missileCount; i++) { setTimeout(() => { @@ -5401,7 +5365,7 @@ const b = { }, 40 * tech.missileCount * Math.random()); } } else { - b.missile(this.position, angle, -8, 0.9) + b.missile(this.position, angle, -8) // missile(where, angle, speed, size = 1) { } break; } @@ -6039,10 +6003,7 @@ const b = { }) Composite.add(engine.world, bullet[me]); //add bullet to world }, - plasmaBot(position = { - x: player.position.x + 50 * (Math.random() - 0.5), - y: player.position.y + 50 * (Math.random() - 0.5) - }, isConsole = true) { + plasmaBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.plasmaBot()`); const me = bullet.length; const dir = m.angle; @@ -6100,7 +6061,7 @@ const b = { const DIST = Vector.magnitude(sub); const unit = Vector.normalise(sub) if (DIST < tech.isPlasmaRange * 450 && m.energy > this.drainThreshold) { - m.energy -= 0.00135 //0.004; //normal plasma field is 0.00008 + m.fieldRegen = 0.00108 + m.energy -= 0.0013 //0.004; //normal plasma field is 0.00008 + m.fieldRegen = 0.00108 // if (m.energy < 0) { // m.fieldCDcycle = m.cycle + 120; // m.energy = 0; @@ -6150,33 +6111,23 @@ const b = { } }; //check for collisions - best = { - x: null, - y: null, - dist2: Infinity, - who: null, - v1: null, - v2: null - }; + 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 - }; + path[path.length - 1] = { x: best.x, y: best.y }; if (best.who.alive) { - const dmg = 0.9 * m.dmgScale; //********** SCALE DAMAGE HERE ********************* + const dmg = 1.4 * m.dmgScale; //********** SCALE DAMAGE HERE ********************* best.who.damage(dmg); best.who.locatePlayer(); //push mobs away - const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.01 * Math.min(5, best.who.mass)) + const force = Vector.mult(Vector.normalise(Vector.sub(m.pos, path[1])), -0.007 * Math.min(5, best.who.mass)) Matter.Body.applyForce(best.who, path[1], force) - if (best.who.speed > 3) { + if (best.who.speed > 2.5) { Matter.Body.setVelocity(best.who, { //friction - x: best.who.velocity.x * 0.7, - y: best.who.velocity.y * 0.7 + x: best.who.velocity.x * 0.75, + y: best.who.velocity.y * 0.75 }); } //draw mob damage circle @@ -6340,2230 +6291,2267 @@ const b = { //9 harpoon //10 mine //11 laser - guns: [{ - name: "nail gun", // 0 - description: "use compressed air to fire a stream of nails
delay after firing decreases as you shoot", - ammo: 0, - ammoPack: 60, - defaultAmmoPack: 60, - recordedAmmo: 0, - have: false, - nextFireCycle: 0, //use to remember how longs its been since last fire, used to reset count - startingHoldCycle: 0, - chooseFireMethod() { //set in simulation.startGame - if (tech.nailRecoil) { - if (tech.isRivets) { - this.fire = this.fireRecoilRivets + guns: [ + { + name: "nail gun", // 0 + // description: `use compressed air to shoot a stream of nails
fire rate increases the longer you fire
60 nails per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `use compressed air to rapidly drive nails
fire rate increases the longer you fire
${this.ammoPack.toFixed(0)} nails per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 60, + defaultAmmoPack: 60, + recordedAmmo: 0, + have: false, + nextFireCycle: 0, //use to remember how longs its been since last fire, used to reset count + startingHoldCycle: 0, + chooseFireMethod() { //set in simulation.startGame + if (tech.nailRecoil) { + if (tech.isRivets) { + this.fire = this.fireRecoilRivets + } else { + this.fire = this.fireRecoilNails + } + } else if (tech.isRivets) { + this.fire = this.fireRivets + } else if (tech.isNeedles) { + this.fire = this.fireNeedles + } else if (tech.nailInstantFireRate) { + this.fire = this.fireInstantFireRate + // } else if (tech.nailFireRate) { + // this.fire = this.fireNailFireRate } else { - this.fire = this.fireRecoilNails + this.fire = this.fireNormal } - } else if (tech.isRivets) { - this.fire = this.fireRivets - } else if (tech.isNeedles) { - this.fire = this.fireNeedles - } else if (tech.nailInstantFireRate) { - this.fire = this.fireInstantFireRate - // } else if (tech.nailFireRate) { - // this.fire = this.fireNailFireRate - } else { - this.fire = this.fireNormal - } - }, - do() { }, - fire() { }, - fireRecoilNails() { - if (this.nextFireCycle + 1 < m.cycle) this.startingHoldCycle = m.cycle //reset if not constantly firing - const CD = Math.max(11 - 0.06 * (m.cycle - this.startingHoldCycle), 0.99) //CD scales with cycles fire is held down - this.nextFireCycle = m.cycle + CD * b.fireCDscale //predict next fire cycle if the fire button is held down + }, + do() { }, + fire() { }, + fireRecoilNails() { + if (this.nextFireCycle + 1 < m.cycle) this.startingHoldCycle = m.cycle //reset if not constantly firing + const CD = Math.max(11 - 0.06 * (m.cycle - this.startingHoldCycle), 0.99) //CD scales with cycles fire is held down + this.nextFireCycle = m.cycle + CD * b.fireCDscale //predict next fire cycle if the fire button is held down - m.fireCDcycle = m.cycle + Math.floor(CD * b.fireCDscale); // cool down - this.baseFire(m.angle + (Math.random() - 0.5) * (m.crouch ? 0.04 : 0.13) / CD, 45 + 6 * Math.random()) - //very complex recoil system - if (m.onGround) { + m.fireCDcycle = m.cycle + Math.floor(CD * b.fireCDscale); // cool down + this.baseFire(m.angle + (Math.random() - 0.5) * (m.crouch ? 0.04 : 0.13) / CD, 45 + 6 * Math.random()) + //very complex recoil system + if (m.onGround) { + if (m.crouch) { + const KNOCK = 0.006 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.5, + y: player.velocity.y * 0.5 + }); + } else { + const KNOCK = 0.03 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.8, + y: player.velocity.y * 0.8 + }); + } + } else { + player.force.x -= 0.06 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) + player.force.y -= 0.006 * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + } + }, + fireNormal() { + if (this.nextFireCycle + 1 < m.cycle) this.startingHoldCycle = m.cycle //reset if not constantly firing + const CD = Math.max(11 - 0.06 * (m.cycle - this.startingHoldCycle), 1) //CD scales with cycles fire is held down + this.nextFireCycle = m.cycle + CD * b.fireCDscale //predict next fire cycle if the fire button is held down + + m.fireCDcycle = m.cycle + Math.floor(CD * b.fireCDscale); // cool down + this.baseFire(m.angle + (Math.random() - 0.5) * (m.crouch ? 0.05 : 0.3) / CD) + }, + fireNeedles() { if (m.crouch) { - const KNOCK = 0.006 - player.force.x -= KNOCK * Math.cos(m.angle) - player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.5, - y: player.velocity.y * 0.5 - }); + m.fireCDcycle = m.cycle + 30 * b.fireCDscale; // cool down + b.needle() + + function cycle() { + if (simulation.paused || m.isBodiesAsleep) { + requestAnimationFrame(cycle) + } else { + count++ + if (count % 2) b.needle() + if (count < 7 && m.alive) requestAnimationFrame(cycle); + } + } + let count = -1 + requestAnimationFrame(cycle); } else { - const KNOCK = 0.03 - player.force.x -= KNOCK * Math.cos(m.angle) - player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.8, - y: player.velocity.y * 0.8 - }); - } - } else { - player.force.x -= 0.06 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) - player.force.y -= 0.006 * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps - } - }, - fireNormal() { - if (this.nextFireCycle + 1 < m.cycle) this.startingHoldCycle = m.cycle //reset if not constantly firing - const CD = Math.max(11 - 0.06 * (m.cycle - this.startingHoldCycle), 1) //CD scales with cycles fire is held down - this.nextFireCycle = m.cycle + CD * b.fireCDscale //predict next fire cycle if the fire button is held down + m.fireCDcycle = m.cycle + 22 * b.fireCDscale; // cool down + b.needle() - m.fireCDcycle = m.cycle + Math.floor(CD * b.fireCDscale); // cool down - this.baseFire(m.angle + (Math.random() - 0.5) * (m.crouch ? 0.05 : 0.3) / CD) - }, - fireNeedles() { - if (m.crouch) { - m.fireCDcycle = m.cycle + 30 * b.fireCDscale; // cool down - b.needle() + function cycle() { + if (simulation.paused || m.isBodiesAsleep) { + requestAnimationFrame(cycle) + } else { + count++ + if (count % 2) b.needle() + if (count < 3 && m.alive) requestAnimationFrame(cycle); + } + } + let count = -1 + requestAnimationFrame(cycle); + } + }, + fireRivets() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 22 : 14) * b.fireCDscale); // cool down + const me = bullet.length; + const size = tech.bulletSize * 8 + bullet[me] = Bodies.rectangle(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 5 * size, size, b.fireAttributes(m.angle)); + bullet[me].dmg = tech.isNailRadiation ? 0 : 2.75 + Matter.Body.setDensity(bullet[me], 0.002); + Composite.add(engine.world, bullet[me]); //add bullet to world + const SPEED = m.crouch ? 60 : 44 + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + bullet[me].endCycle = simulation.cycle + 180 - function cycle() { - if (simulation.paused || m.isBodiesAsleep) { - requestAnimationFrame(cycle) - } else { - count++ - if (count % 2) b.needle() - if (count < 7 && m.alive) requestAnimationFrame(cycle); - } - } - let count = -1 - requestAnimationFrame(cycle); - } else { - m.fireCDcycle = m.cycle + 22 * b.fireCDscale; // cool down - b.needle() - - function cycle() { - if (simulation.paused || m.isBodiesAsleep) { - requestAnimationFrame(cycle) - } else { - count++ - if (count % 2) b.needle() - if (count < 3 && m.alive) requestAnimationFrame(cycle); - } - } - let count = -1 - requestAnimationFrame(cycle); - } - }, - fireRivets() { - m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 22 : 14) * b.fireCDscale); // cool down - const me = bullet.length; - const size = tech.bulletSize * 8 - bullet[me] = Bodies.rectangle(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 5 * size, size, b.fireAttributes(m.angle)); - bullet[me].dmg = tech.isNailRadiation ? 0 : 2.75 - Matter.Body.setDensity(bullet[me], 0.002); - Composite.add(engine.world, bullet[me]); //add bullet to world - const SPEED = m.crouch ? 60 : 44 - Matter.Body.setVelocity(bullet[me], { - x: SPEED * Math.cos(m.angle), - y: SPEED * Math.sin(m.angle) - }); - bullet[me].endCycle = simulation.cycle + 180 - - bullet[me].beforeDmg = function (who) { //beforeDmg is rewritten with ice crystal tech - if (tech.isIncendiary) { - this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion - b.explosion(this.position, 100 + (Math.random() - 0.5) * 20); //makes bullet do explosive damage at end - } - if (tech.isNailCrit) { - if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { - b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end - } - } else if (tech.isCritKill) b.crit(who, this) - if (tech.isNailRadiation) mobs.statusDoT(who, 7 * (tech.isFastRadiation ? 0.7 : 0.24), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles - if (this.speed > 4 && tech.fragments) { - b.targetedNail(this.position, 1.25 * tech.fragments * tech.bulletSize) - this.endCycle = 0 //triggers despawn - } - }; - - bullet[me].minDmgSpeed = 10 - bullet[me].frictionAir = 0.006; - bullet[me].rotateToVelocity = function () { //rotates bullet to face current velocity? - if (this.speed > 7) { - const facing = { - x: Math.cos(this.angle), - y: Math.sin(this.angle) - } - const mag = 0.002 * this.mass - if (Vector.cross(Vector.normalise(this.velocity), facing) < 0) { - this.torque += mag - } else { - this.torque -= mag - } - } - }; - if (tech.isIncendiary) { - bullet[me].do = function () { - this.force.y += this.mass * 0.0008 - this.rotateToVelocity() - //collide with map - if (Matter.Query.collides(this, map).length) { //penetrate walls - this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion - b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end - } - }; - } else { - bullet[me].do = function () { - this.force.y += this.mass * 0.0008 - this.rotateToVelocity() - }; - } - b.muzzleFlash(); - //very complex recoil system - if (m.onGround) { - if (m.crouch) { - const KNOCK = 0.01 - player.force.x -= KNOCK * Math.cos(m.angle) - player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps - } else { - const KNOCK = 0.02 - player.force.x -= KNOCK * Math.cos(m.angle) - player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps - } - } else { - const KNOCK = 0.01 - player.force.x -= KNOCK * Math.cos(m.angle) - player.force.y -= KNOCK * Math.sin(m.angle) * 0.5 //reduce knock back in vertical direction to stop super jumps - } - }, - fireRecoilRivets() { - // m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 25 : 17) * b.fireCDscale); // cool down - if (this.nextFireCycle + 1 < m.cycle) this.startingHoldCycle = m.cycle //reset if not constantly firing - const CD = Math.max(25 - 0.14 * (m.cycle - this.startingHoldCycle), 5) //CD scales with cycles fire is held down - this.nextFireCycle = m.cycle + CD * b.fireCDscale //predict next fire cycle if the fire button is held down - m.fireCDcycle = m.cycle + Math.floor(CD * b.fireCDscale); // cool down - - const me = bullet.length; - const size = tech.bulletSize * 8 - bullet[me] = Bodies.rectangle(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 5 * size, size, b.fireAttributes(m.angle)); - bullet[me].dmg = tech.isNailRadiation ? 0 : 2.75 - Matter.Body.setDensity(bullet[me], 0.002); - Composite.add(engine.world, bullet[me]); //add bullet to world - const SPEED = m.crouch ? 62 : 52 - Matter.Body.setVelocity(bullet[me], { - x: SPEED * Math.cos(m.angle), - y: SPEED * Math.sin(m.angle) - }); - bullet[me].endCycle = simulation.cycle + 180 - bullet[me].beforeDmg = function (who) { //beforeDmg is rewritten with ice crystal tech - if (tech.isIncendiary) { - this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion - b.explosion(this.position, 100 + (Math.random() - 0.5) * 20); //makes bullet do explosive damage at end - } - if (tech.isNailCrit) { - if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { - b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end - } - } else if (tech.isCritKill) b.crit(who, this) - if (tech.isNailRadiation) mobs.statusDoT(who, 7 * (tech.isFastRadiation ? 0.7 : 0.24), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles - if (this.speed > 4 && tech.fragments) { - b.targetedNail(this.position, 1.25 * tech.fragments * tech.bulletSize) - this.endCycle = 0 //triggers despawn - } - }; - - bullet[me].minDmgSpeed = 10 - bullet[me].frictionAir = 0.006; - bullet[me].rotateToVelocity = function () { //rotates bullet to face current velocity? - if (this.speed > 7) { - const facing = { - x: Math.cos(this.angle), - y: Math.sin(this.angle) - } - const mag = 0.002 * this.mass - if (Vector.cross(Vector.normalise(this.velocity), facing) < 0) { - this.torque += mag - } else { - this.torque -= mag - } - } - }; - if (tech.isIncendiary) { - bullet[me].do = function () { - this.force.y += this.mass * 0.0008 - this.rotateToVelocity() - //collide with map - if (Matter.Query.collides(this, map).length) { //penetrate walls + bullet[me].beforeDmg = function (who) { //beforeDmg is rewritten with ice crystal tech + if (tech.isIncendiary) { this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion b.explosion(this.position, 100 + (Math.random() - 0.5) * 20); //makes bullet do explosive damage at end } - }; - } else { - bullet[me].do = function () { - this.force.y += this.mass * 0.0008 - this.rotateToVelocity() - }; - } - - b.muzzleFlash(); - //very complex recoil system - if (m.onGround) { - if (m.crouch) { - const KNOCK = 0.03 - player.force.x -= KNOCK * Math.cos(m.angle) - player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.4, - y: player.velocity.y * 0.4 - }); - } else { - const KNOCK = 0.1 - player.force.x -= KNOCK * Math.cos(m.angle) - player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.7, - y: player.velocity.y * 0.7 - }); - } - } else { - player.force.x -= 0.2 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) - // player.force.x -= 0.06 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) - - player.force.y -= 0.02 * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps - } - }, - fireInstantFireRate() { - m.fireCDcycle = m.cycle + Math.floor(1 * b.fireCDscale); // cool down - this.baseFire(m.angle + (Math.random() - 0.5) * (Math.random() - 0.5) * (m.crouch ? 1.15 : 2) / 2) - }, - baseFire(angle, speed = 30 + 6 * Math.random()) { - b.nail({ - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - }, { - x: m.Vx / 2 + speed * Math.cos(angle), - y: m.Vy / 2 + speed * Math.sin(angle) - }) //position, velocity, damage - if (tech.isIceCrystals) { - bullet[bullet.length - 1].beforeDmg = function (who) { - mobs.statusSlow(who, 60) - if (tech.isNailRadiation) mobs.statusDoT(who, 1 * (tech.isFastRadiation ? 1.3 : 0.44), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles if (tech.isNailCrit) { if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { - b.explosion(this.position, 150 + 30 * Math.random()); //makes bullet do explosive damage at end + b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end } + } else if (tech.isCritKill) b.crit(who, this) + if (tech.isNailRadiation) mobs.statusDoT(who, 7 * (tech.isFastRadiation ? 0.7 : 0.24), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles + if (this.speed > 4 && tech.fragments) { + b.targetedNail(this.position, 1.25 * tech.fragments * tech.bulletSize) + this.endCycle = 0 //triggers despawn } - this.ricochet(who) }; - if (m.energy < 0.01) { - m.fireCDcycle = m.cycle + 60; // cool down - } else { - m.energy -= 0.01 - } - } - }, - }, - { - name: "shotgun", //1 - description: "fire a wide burst of short range bullets
with a low fire rate", - ammo: 0, - ammoPack: 3.5, - defaultAmmoPack: 3.5, - have: false, - do() { - //fade cross hairs - - - // draw loop around player head - // const left = m.fireCDcycle !== Infinity ? 0.05 * Math.max(m.fireCDcycle - m.cycle, 0) : 0 - // if (left > 0) { - // ctx.beginPath(); - // // ctx.arc(simulation.mouseInGame.x, simulation.mouseInGame.y, 30, 0, left); - // ctx.arc(m.pos.x, m.pos.y, 28, m.angle - left, m.angle); - // // ctx.fillStyle = "rgba(0,0,0,0.3)" //"#333" - // // ctx.fill(); - // ctx.strokeStyle = "#333"; - // ctx.lineWidth = 2; - // ctx.stroke(); - // } - - - //draw hip circle - // ctx.beginPath(); - // ctx.arc(m.pos.x + m.hip.x, m.pos.y + m.hip.y, 11, 0, 2 * Math.PI); - // ctx.fillStyle = "rgba(0,0,0,0.3)" //"#333" - // ctx.fill(); - }, - fire() { - let knock, spread - const coolDown = function () { - if (m.crouch) { - spread = 0.65 - m.fireCDcycle = m.cycle + Math.floor((73 + 36 * tech.shotgunExtraShots) * b.fireCDscale) // cool down - if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(60 * b.fireCDscale)) m.immuneCycle = m.cycle + Math.floor(60 * b.fireCDscale); //player is immune to damage for 30 cycles - knock = 0.01 - } else { - m.fireCDcycle = m.cycle + Math.floor((56 + 28 * tech.shotgunExtraShots) * b.fireCDscale) // cool down - if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(47 * b.fireCDscale)) m.immuneCycle = m.cycle + Math.floor(47 * b.fireCDscale); //player is immune to damage for 30 cycles - spread = 1.3 - knock = 0.1 - } - - if (tech.isShotgunReversed) { - player.force.x += 1.5 * knock * Math.cos(m.angle) - player.force.y += 1.5 * knock * Math.sin(m.angle) - 3 * player.mass * simulation.g - } else if (tech.isShotgunRecoil) { - m.fireCDcycle -= 0.66 * (56 * b.fireCDscale) - player.force.x -= 2 * knock * Math.cos(m.angle) - player.force.y -= 2 * knock * Math.sin(m.angle) - } else { - player.force.x -= knock * Math.cos(m.angle) - player.force.y -= knock * Math.sin(m.angle) * 0.5 //reduce knock back in vertical direction to stop super jumps - } - } - const spray = (num) => { - const side = 22 - for (let i = 0; i < num; i++) { - const me = bullet.length; - const dir = m.angle + (Math.random() - 0.5) * spread - bullet[me] = Bodies.rectangle(m.pos.x, m.pos.y, side, side, b.fireAttributes(dir)); - Composite.add(engine.world, bullet[me]); //add bullet to world - const SPEED = 52 + Math.random() * 8 - Matter.Body.setVelocity(bullet[me], { - x: SPEED * Math.cos(dir), - y: SPEED * Math.sin(dir) - }); - bullet[me].endCycle = simulation.cycle + 40 * tech.bulletsLastLonger - bullet[me].minDmgSpeed = 15 - if (tech.isShotgunReversed) Matter.Body.setDensity(bullet[me], 0.0015) - // bullet[me].restitution = 0.4 - bullet[me].frictionAir = 0.034; - bullet[me].do = function () { - const scale = 1 - 0.034 / tech.bulletsLastLonger - Matter.Body.scale(this, scale, scale); - }; - } - } - const chooseBulletType = function () { - if (tech.isRivets) { - const me = bullet.length; - // const dir = m.angle + 0.02 * (Math.random() - 0.5) - bullet[me] = Bodies.rectangle(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 56 * tech.bulletSize, 25 * tech.bulletSize, b.fireAttributes(m.angle)); - - Matter.Body.setDensity(bullet[me], 0.005 * (tech.isShotgunReversed ? 1.5 : 1)); - Composite.add(engine.world, bullet[me]); //add bullet to world - const SPEED = (m.crouch ? 50 : 43) - Matter.Body.setVelocity(bullet[me], { - x: SPEED * Math.cos(m.angle), - y: SPEED * Math.sin(m.angle) - }); - if (tech.isIncendiary) { - bullet[me].endCycle = simulation.cycle + 60 - bullet[me].onEnd = function () { - b.explosion(this.position, 360 + (Math.random() - 0.5) * 60); //makes bullet do explosive damage at end + bullet[me].minDmgSpeed = 10 + bullet[me].frictionAir = 0.006; + bullet[me].rotateToVelocity = function () { //rotates bullet to face current velocity? + if (this.speed > 7) { + const facing = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + } + const mag = 0.002 * this.mass + if (Vector.cross(Vector.normalise(this.velocity), facing) < 0) { + this.torque += mag + } else { + this.torque -= mag } - bullet[me].beforeDmg = function () { - this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion - }; - } else { - bullet[me].endCycle = simulation.cycle + 180 } - bullet[me].minDmgSpeed = 7 - // bullet[me].restitution = 0.4 - bullet[me].frictionAir = 0.004; - bullet[me].turnMag = 0.04 * Math.pow(tech.bulletSize, 3.75) + }; + if (tech.isIncendiary) { bullet[me].do = function () { - this.force.y += this.mass * 0.002 - if (this.speed > 6) { //rotates bullet to face current velocity? - const facing = { - x: Math.cos(this.angle), - y: Math.sin(this.angle) - } - if (Vector.cross(Vector.normalise(this.velocity), facing) < 0) { - this.torque += this.turnMag - } else { - this.torque -= this.turnMag - } - } - if (tech.isIncendiary && Matter.Query.collides(this, map).length) { + this.force.y += this.mass * 0.0008 + this.rotateToVelocity() + //collide with map + if (Matter.Query.collides(this, map).length) { //penetrate walls this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end } }; - bullet[me].beforeDmg = function (who) { - if (this.speed > 4) { - if (tech.fragments) { - b.targetedNail(this.position, 6 * tech.fragments * tech.bulletSize) - this.endCycle = 0 //triggers despawn - } - if (tech.isIncendiary) this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion - if (tech.isCritKill) b.crit(who, this) - } - } - spray(12); //fires normal shotgun bullets - } else if (tech.isIncendiary) { - spread *= 0.15 - const END = Math.floor(m.crouch ? 8 : 5); - const totalBullets = 9 - const angleStep = (m.crouch ? 0.3 : 0.8) / totalBullets - let dir = m.angle - angleStep * totalBullets / 2; - for (let i = 0; i < totalBullets; i++) { //5 -> 7 - dir += angleStep - const me = bullet.length; - bullet[me] = Bodies.rectangle(m.pos.x + 50 * Math.cos(m.angle), m.pos.y + 50 * Math.sin(m.angle), 17, 4, b.fireAttributes(dir)); - const end = END + Math.random() * 4 - bullet[me].endCycle = 2 * end * tech.bulletsLastLonger + simulation.cycle - const speed = 25 * end / END - const dirOff = dir + (Math.random() - 0.5) * spread - Matter.Body.setVelocity(bullet[me], { - x: speed * Math.cos(dirOff), - y: speed * Math.sin(dirOff) - }); - bullet[me].onEnd = function () { - b.explosion(this.position, 150 * (tech.isShotgunReversed ? 1.4 : 1) + (Math.random() - 0.5) * 30); //makes bullet do explosive damage at end - } - bullet[me].beforeDmg = function () { - this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion - }; - bullet[me].do = function () { - if (Matter.Query.collides(this, map).length) this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion - } - Composite.add(engine.world, bullet[me]); //add bullet to world - } - } else if (tech.isNailShot) { - spread *= 0.65 - const dmg = 2 * (tech.isShotgunReversed ? 1.5 : 1) + } else { + bullet[me].do = function () { + this.force.y += this.mass * 0.0008 + this.rotateToVelocity() + }; + } + b.muzzleFlash(); + //very complex recoil system + if (m.onGround) { if (m.crouch) { - for (let i = 0; i < 17; i++) { - speed = 38 + 15 * Math.random() - const dir = m.angle + (Math.random() - 0.5) * spread - const pos = { - x: m.pos.x + 35 * Math.cos(m.angle) + 15 * (Math.random() - 0.5), - y: m.pos.y + 35 * Math.sin(m.angle) + 15 * (Math.random() - 0.5) - } - b.nail(pos, { - x: speed * Math.cos(dir), - y: speed * Math.sin(dir) - }, dmg) - } + const KNOCK = 0.01 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps } else { - for (let i = 0; i < 17; i++) { - speed = 38 + 15 * Math.random() - const dir = m.angle + (Math.random() - 0.5) * spread - const pos = { - x: m.pos.x + 35 * Math.cos(m.angle) + 15 * (Math.random() - 0.5), - y: m.pos.y + 35 * Math.sin(m.angle) + 15 * (Math.random() - 0.5) - } - b.nail(pos, { - x: speed * Math.cos(dir), - y: speed * Math.sin(dir) - }, dmg) - } - } - } else if (tech.isSporeFlea) { - const where = { - x: m.pos.x + 35 * Math.cos(m.angle), - y: m.pos.y + 35 * Math.sin(m.angle) - } - const number = 2 * (tech.isShotgunReversed ? 1.5 : 1) - for (let i = 0; i < number; i++) { - const angle = m.angle + 0.2 * (Math.random() - 0.5) - const speed = (m.crouch ? 35 * (1 + 0.05 * Math.random()) : 30 * (1 + 0.15 * Math.random())) - b.flea(where, { - x: speed * Math.cos(angle), - y: speed * Math.sin(angle) - }) - bullet[bullet.length - 1].setDamage() - } - spray(10); //fires normal shotgun bullets - } else if (tech.isSporeWorm) { - const where = { - x: m.pos.x + 35 * Math.cos(m.angle), - y: m.pos.y + 35 * Math.sin(m.angle) - } - const spread = (m.crouch ? 0.02 : 0.07) - const number = 3 * (tech.isShotgunReversed ? 1.5 : 1) - let angle = m.angle - (number - 1) * spread * 0.5 - for (let i = 0; i < number; i++) { - b.worm(where) - const SPEED = (30 + 10 * m.crouch) * (1 + 0.2 * Math.random()) - Matter.Body.setVelocity(bullet[bullet.length - 1], { - x: player.velocity.x * 0.5 + SPEED * Math.cos(angle), - y: player.velocity.y * 0.5 + SPEED * Math.sin(angle) - }); - angle += spread - } - spray(7); //fires normal shotgun bullets - } else if (tech.isIceShot) { - const spread = (m.crouch ? 0.7 : 1.2) - for (let i = 0, len = 10 * (tech.isShotgunReversed ? 1.5 : 1); i < len; i++) { - b.iceIX(23 + 10 * Math.random(), m.angle + spread * (Math.random() - 0.5)) - } - spray(10); //fires normal shotgun bullets - } else if (tech.isFoamShot) { - const spread = (m.crouch ? 0.15 : 0.4) - const where = { - x: m.pos.x + 25 * Math.cos(m.angle), - y: m.pos.y + 25 * Math.sin(m.angle) - } - const number = 16 * (tech.isShotgunReversed ? 1.5 : 1) - for (let i = 0; i < number; i++) { - const SPEED = 13 + 4 * Math.random(); - const angle = m.angle + spread * (Math.random() - 0.5) - b.foam(where, { - x: SPEED * Math.cos(angle), - y: SPEED * Math.sin(angle) - }, 8 + 7 * Math.random()) - } - } else if (tech.isNeedles) { - const number = 9 * (tech.isShotgunReversed ? 1.5 : 1) - const spread = (m.crouch ? 0.03 : 0.05) - let angle = m.angle - (number - 1) * spread * 0.5 - for (let i = 0; i < number; i++) { - b.needle(angle) - angle += spread + const KNOCK = 0.02 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps } } else { - spray(16); //fires normal shotgun bullets + const KNOCK = 0.01 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) * 0.5 //reduce knock back in vertical direction to stop super jumps } - } + }, + fireRecoilRivets() { + // m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 25 : 17) * b.fireCDscale); // cool down + if (this.nextFireCycle + 1 < m.cycle) this.startingHoldCycle = m.cycle //reset if not constantly firing + const CD = Math.max(25 - 0.14 * (m.cycle - this.startingHoldCycle), 5) //CD scales with cycles fire is held down + this.nextFireCycle = m.cycle + CD * b.fireCDscale //predict next fire cycle if the fire button is held down + m.fireCDcycle = m.cycle + Math.floor(CD * b.fireCDscale); // cool down - - coolDown(); - b.muzzleFlash(35); - chooseBulletType(); - - if (tech.shotgunExtraShots) { - const delay = 7 - let count = tech.shotgunExtraShots * delay - - function cycle() { - count-- - if (!(count % delay)) { - coolDown(); - b.muzzleFlash(35); - chooseBulletType(); + const me = bullet.length; + const size = tech.bulletSize * 8 + bullet[me] = Bodies.rectangle(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 5 * size, size, b.fireAttributes(m.angle)); + bullet[me].dmg = tech.isNailRadiation ? 0 : 2.75 + Matter.Body.setDensity(bullet[me], 0.002); + Composite.add(engine.world, bullet[me]); //add bullet to world + const SPEED = m.crouch ? 62 : 52 + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + bullet[me].endCycle = simulation.cycle + 180 + bullet[me].beforeDmg = function (who) { //beforeDmg is rewritten with ice crystal tech + if (tech.isIncendiary) { + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + b.explosion(this.position, 100 + (Math.random() - 0.5) * 20); //makes bullet do explosive damage at end } - if (count > 0) { - requestAnimationFrame(cycle); + if (tech.isNailCrit) { + if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { + b.explosion(this.position, 300 + 40 * Math.random()); //makes bullet do explosive damage at end + } + } else if (tech.isCritKill) b.crit(who, this) + if (tech.isNailRadiation) mobs.statusDoT(who, 7 * (tech.isFastRadiation ? 0.7 : 0.24), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles + if (this.speed > 4 && tech.fragments) { + b.targetedNail(this.position, 1.25 * tech.fragments * tech.bulletSize) + this.endCycle = 0 //triggers despawn } + }; + + bullet[me].minDmgSpeed = 10 + bullet[me].frictionAir = 0.006; + bullet[me].rotateToVelocity = function () { //rotates bullet to face current velocity? + if (this.speed > 7) { + const facing = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + } + const mag = 0.002 * this.mass + if (Vector.cross(Vector.normalise(this.velocity), facing) < 0) { + this.torque += mag + } else { + this.torque -= mag + } + } + }; + if (tech.isIncendiary) { + bullet[me].do = function () { + this.force.y += this.mass * 0.0008 + this.rotateToVelocity() + //collide with map + if (Matter.Query.collides(this, map).length) { //penetrate walls + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + b.explosion(this.position, 100 + (Math.random() - 0.5) * 20); //makes bullet do explosive damage at end + } + }; + } else { + bullet[me].do = function () { + this.force.y += this.mass * 0.0008 + this.rotateToVelocity() + }; } - requestAnimationFrame(cycle); - } - } - }, { - name: "super balls", //2 - description: "fire 3 balls in a wide arc
balls bounce with no momentum loss", - ammo: 0, - ammoPack: 9, - have: false, - // num: 5, - do() { }, - foamBall() { + b.muzzleFlash(); + //very complex recoil system + if (m.onGround) { + if (m.crouch) { + const KNOCK = 0.03 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.4, + y: player.velocity.y * 0.4 + }); + } else { + const KNOCK = 0.1 + player.force.x -= KNOCK * Math.cos(m.angle) + player.force.y -= KNOCK * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.7, + y: player.velocity.y * 0.7 + }); + } + } else { + player.force.x -= 0.2 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) + // player.force.x -= 0.06 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) - }, - fireOne() { - m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 27 : 19) * b.fireCDscale); // cool down - const speed = m.crouch ? 43 : 36 - b.superBall({ - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - }, { - x: speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - }, 21 * tech.bulletSize) - }, - fireMulti() { - m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 23 : 15) * b.fireCDscale); // cool down - const SPREAD = m.crouch ? 0.08 : 0.13 - const num = 3 + Math.floor(tech.extraSuperBalls * Math.random()) - const speed = m.crouch ? 43 : 36 - let dir = m.angle - SPREAD * (num - 1) / 2; - for (let i = 0; i < num; i++) { - b.superBall({ - x: m.pos.x + 30 * Math.cos(dir), - y: m.pos.y + 30 * Math.sin(dir) + player.force.y -= 0.02 * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps + } + }, + fireInstantFireRate() { + m.fireCDcycle = m.cycle + Math.floor(1 * b.fireCDscale); // cool down + this.baseFire(m.angle + (Math.random() - 0.5) * (Math.random() - 0.5) * (m.crouch ? 1.15 : 2) / 2) + }, + baseFire(angle, speed = 30 + 6 * Math.random()) { + b.nail({ + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) }, { - x: speed * Math.cos(dir), - y: speed * Math.sin(dir) - }, 11 * tech.bulletSize) - dir += SPREAD; - } + x: m.Vx / 2 + speed * Math.cos(angle), + y: m.Vy / 2 + speed * Math.sin(angle) + }) //position, velocity, damage + if (tech.isIceCrystals) { + bullet[bullet.length - 1].beforeDmg = function (who) { + mobs.statusSlow(who, 60) + if (tech.isNailRadiation) mobs.statusDoT(who, 1 * (tech.isFastRadiation ? 1.3 : 0.44), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles + if (tech.isNailCrit) { + if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 1 / who.radius) { + b.explosion(this.position, 150 + 30 * Math.random()); //makes bullet do explosive damage at end + } + } + this.ricochet(who) + }; + if (m.energy < 0.01) { + m.fireCDcycle = m.cycle + 60; // cool down + } else { + m.energy -= 0.01 + } + } + }, }, - fireQueue() { - m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 23 : 15) * b.fireCDscale); // cool down - const num = 1 + 3 + Math.floor(tech.extraSuperBalls * Math.random()) //1 extra - const speed = m.crouch ? 43 : 36 + { + name: "shotgun", //1 + // description: `fire a wide burst of short range bullets
with a low fire rate
3-4 nails per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `fire a wide burst of short range bullets
has a slow fire rate
${this.ammoPack.toFixed(1)} nails per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 3.5, + defaultAmmoPack: 3.5, + have: false, + do() { + //fade cross hairs - const delay = Math.floor((m.crouch ? 18 : 12) * b.fireCDscale) - m.fireCDcycle = m.cycle + delay; // cool down - function cycle() { - count++ + + + // draw loop around player head + // const left = m.fireCDcycle !== Infinity ? 0.05 * Math.max(m.fireCDcycle - m.cycle, 0) : 0 + // if (left > 0) { + // ctx.beginPath(); + // // ctx.arc(simulation.mouseInGame.x, simulation.mouseInGame.y, 30, 0, left); + // ctx.arc(m.pos.x, m.pos.y, 28, m.angle - left, m.angle); + // // ctx.fillStyle = "rgba(0,0,0,0.3)" //"#333" + // // ctx.fill(); + // ctx.strokeStyle = "#333"; + // ctx.lineWidth = 2; + // ctx.stroke(); + // } + + + //draw hip circle + // ctx.beginPath(); + // ctx.arc(m.pos.x + m.hip.x, m.pos.y + m.hip.y, 11, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,0,0,0.3)" //"#333" + // ctx.fill(); + }, + fire() { + let knock, spread + const coolDown = function () { + if (m.crouch) { + spread = 0.65 + m.fireCDcycle = m.cycle + Math.floor((73 + 36 * tech.shotgunExtraShots) * b.fireCDscale) // cool down + if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(60 * b.fireCDscale)) m.immuneCycle = m.cycle + Math.floor(60 * b.fireCDscale); //player is immune to damage for 30 cycles + knock = 0.01 + } else { + m.fireCDcycle = m.cycle + Math.floor((56 + 28 * tech.shotgunExtraShots) * b.fireCDscale) // cool down + if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(47 * b.fireCDscale)) m.immuneCycle = m.cycle + Math.floor(47 * b.fireCDscale); //player is immune to damage for 30 cycles + spread = 1.3 + knock = 0.1 + } + + if (tech.isShotgunReversed) { + player.force.x += 1.5 * knock * Math.cos(m.angle) + player.force.y += 1.5 * knock * Math.sin(m.angle) - 3 * player.mass * simulation.g + } else if (tech.isShotgunRecoil) { + m.fireCDcycle -= 0.66 * (56 * b.fireCDscale) + player.force.x -= 2 * knock * Math.cos(m.angle) + player.force.y -= 2 * knock * Math.sin(m.angle) + } else { + player.force.x -= knock * Math.cos(m.angle) + player.force.y -= knock * Math.sin(m.angle) * 0.5 //reduce knock back in vertical direction to stop super jumps + } + } + const spray = (num) => { + const side = 22 + for (let i = 0; i < num; i++) { + const me = bullet.length; + const dir = m.angle + (Math.random() - 0.5) * spread + bullet[me] = Bodies.rectangle(m.pos.x, m.pos.y, side, side, b.fireAttributes(dir)); + Composite.add(engine.world, bullet[me]); //add bullet to world + const SPEED = 52 + Math.random() * 8 + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + }); + bullet[me].endCycle = simulation.cycle + 40 * tech.bulletsLastLonger + bullet[me].minDmgSpeed = 15 + if (tech.isShotgunReversed) Matter.Body.setDensity(bullet[me], 0.0015) + // bullet[me].restitution = 0.4 + bullet[me].frictionAir = 0.034; + bullet[me].do = function () { + const scale = 1 - 0.034 / tech.bulletsLastLonger + Matter.Body.scale(this, scale, scale); + }; + } + } + const chooseBulletType = function () { + if (tech.isRivets) { + const me = bullet.length; + // const dir = m.angle + 0.02 * (Math.random() - 0.5) + bullet[me] = Bodies.rectangle(m.pos.x + 35 * Math.cos(m.angle), m.pos.y + 35 * Math.sin(m.angle), 56 * tech.bulletSize, 25 * tech.bulletSize, b.fireAttributes(m.angle)); + + Matter.Body.setDensity(bullet[me], 0.005 * (tech.isShotgunReversed ? 1.5 : 1)); + Composite.add(engine.world, bullet[me]); //add bullet to world + const SPEED = (m.crouch ? 50 : 43) + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + if (tech.isIncendiary) { + bullet[me].endCycle = simulation.cycle + 60 + bullet[me].onEnd = function () { + b.explosion(this.position, 360 + (Math.random() - 0.5) * 60); //makes bullet do explosive damage at end + } + bullet[me].beforeDmg = function () { + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + }; + } else { + bullet[me].endCycle = simulation.cycle + 180 + } + bullet[me].minDmgSpeed = 7 + // bullet[me].restitution = 0.4 + bullet[me].frictionAir = 0.004; + bullet[me].turnMag = 0.04 * Math.pow(tech.bulletSize, 3.75) + bullet[me].do = function () { + this.force.y += this.mass * 0.002 + if (this.speed > 6) { //rotates bullet to face current velocity? + const facing = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + } + if (Vector.cross(Vector.normalise(this.velocity), facing) < 0) { + this.torque += this.turnMag + } else { + this.torque -= this.turnMag + } + } + if (tech.isIncendiary && Matter.Query.collides(this, map).length) { + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + } + }; + bullet[me].beforeDmg = function (who) { + if (this.speed > 4) { + if (tech.fragments) { + b.targetedNail(this.position, 6 * tech.fragments * tech.bulletSize) + this.endCycle = 0 //triggers despawn + } + if (tech.isIncendiary) this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + if (tech.isCritKill) b.crit(who, this) + } + } + spray(12); //fires normal shotgun bullets + } else if (tech.isIncendiary) { + spread *= 0.15 + const END = Math.floor(m.crouch ? 8 : 5); + const totalBullets = 9 + const angleStep = (m.crouch ? 0.3 : 0.8) / totalBullets + let dir = m.angle - angleStep * totalBullets / 2; + for (let i = 0; i < totalBullets; i++) { //5 -> 7 + dir += angleStep + const me = bullet.length; + bullet[me] = Bodies.rectangle(m.pos.x + 50 * Math.cos(m.angle), m.pos.y + 50 * Math.sin(m.angle), 17, 4, b.fireAttributes(dir)); + const end = END + Math.random() * 4 + bullet[me].endCycle = 2 * end * tech.bulletsLastLonger + simulation.cycle + const speed = 25 * end / END + const dirOff = dir + (Math.random() - 0.5) * spread + Matter.Body.setVelocity(bullet[me], { + x: speed * Math.cos(dirOff), + y: speed * Math.sin(dirOff) + }); + bullet[me].onEnd = function () { + b.explosion(this.position, 150 * (tech.isShotgunReversed ? 1.4 : 1) + (Math.random() - 0.5) * 30); //makes bullet do explosive damage at end + } + bullet[me].beforeDmg = function () { + this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + }; + bullet[me].do = function () { + if (Matter.Query.collides(this, map).length) this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion + } + Composite.add(engine.world, bullet[me]); //add bullet to world + } + } else if (tech.isNailShot) { + spread *= 0.65 + const dmg = 2 * (tech.isShotgunReversed ? 1.5 : 1) + if (m.crouch) { + for (let i = 0; i < 17; i++) { + speed = 38 + 15 * Math.random() + const dir = m.angle + (Math.random() - 0.5) * spread + const pos = { + x: m.pos.x + 35 * Math.cos(m.angle) + 15 * (Math.random() - 0.5), + y: m.pos.y + 35 * Math.sin(m.angle) + 15 * (Math.random() - 0.5) + } + b.nail(pos, { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }, dmg) + } + } else { + for (let i = 0; i < 17; i++) { + speed = 38 + 15 * Math.random() + const dir = m.angle + (Math.random() - 0.5) * spread + const pos = { + x: m.pos.x + 35 * Math.cos(m.angle) + 15 * (Math.random() - 0.5), + y: m.pos.y + 35 * Math.sin(m.angle) + 15 * (Math.random() - 0.5) + } + b.nail(pos, { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }, dmg) + } + } + } else if (tech.isSporeFlea) { + const where = { + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + } + const number = 2 * (tech.isShotgunReversed ? 1.5 : 1) + for (let i = 0; i < number; i++) { + const angle = m.angle + 0.2 * (Math.random() - 0.5) + const speed = (m.crouch ? 35 * (1 + 0.05 * Math.random()) : 30 * (1 + 0.15 * Math.random())) + b.flea(where, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }) + bullet[bullet.length - 1].setDamage() + } + spray(10); //fires normal shotgun bullets + } else if (tech.isSporeWorm) { + const where = { + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + } + const spread = (m.crouch ? 0.02 : 0.07) + const number = 3 * (tech.isShotgunReversed ? 1.5 : 1) + let angle = m.angle - (number - 1) * spread * 0.5 + for (let i = 0; i < number; i++) { + b.worm(where) + const SPEED = (30 + 10 * m.crouch) * (1 + 0.2 * Math.random()) + Matter.Body.setVelocity(bullet[bullet.length - 1], { + x: player.velocity.x * 0.5 + SPEED * Math.cos(angle), + y: player.velocity.y * 0.5 + SPEED * Math.sin(angle) + }); + angle += spread + } + spray(7); //fires normal shotgun bullets + } else if (tech.isIceShot) { + const spread = (m.crouch ? 0.7 : 1.2) + for (let i = 0, len = 10 * (tech.isShotgunReversed ? 1.5 : 1); i < len; i++) { + b.iceIX(23 + 10 * Math.random(), m.angle + spread * (Math.random() - 0.5)) + } + spray(10); //fires normal shotgun bullets + } else if (tech.isFoamShot) { + const spread = (m.crouch ? 0.15 : 0.4) + const where = { + x: m.pos.x + 25 * Math.cos(m.angle), + y: m.pos.y + 25 * Math.sin(m.angle) + } + const number = 16 * (tech.isShotgunReversed ? 1.5 : 1) + for (let i = 0; i < number; i++) { + const SPEED = 13 + 4 * Math.random(); + const angle = m.angle + spread * (Math.random() - 0.5) + b.foam(where, { + x: SPEED * Math.cos(angle), + y: SPEED * Math.sin(angle) + }, 8 + 7 * Math.random()) + } + } else if (tech.isNeedles) { + const number = 9 * (tech.isShotgunReversed ? 1.5 : 1) + const spread = (m.crouch ? 0.03 : 0.05) + let angle = m.angle - (number - 1) * spread * 0.5 + for (let i = 0; i < number; i++) { + b.needle(angle) + angle += spread + } + } else { + spray(16); //fires normal shotgun bullets + } + } + + + coolDown(); + b.muzzleFlash(35); + chooseBulletType(); + + if (tech.shotgunExtraShots) { + const delay = 7 + let count = tech.shotgunExtraShots * delay + + function cycle() { + count-- + if (!(count % delay)) { + coolDown(); + b.muzzleFlash(35); + chooseBulletType(); + } + if (count > 0) { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + } + } + }, { + name: "super balls", //2 + // description: `fire 3 balls in a wide arc
balls bounce with no momentum loss
9 balls per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `fire 3 balls in a wide arc
balls bounce with no momentum loss
${this.ammoPack.toFixed(0)} balls per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 9, + have: false, + // num: 5, + do() { }, + foamBall() { + + + }, + fireOne() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 27 : 19) * b.fireCDscale); // cool down + const speed = m.crouch ? 43 : 36 b.superBall({ x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) }, { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) - }, 11 * tech.bulletSize) - if (count < num && m.alive) requestAnimationFrame(cycle); - m.fireCDcycle = m.cycle + delay; // cool down - } - let count = 0 - requestAnimationFrame(cycle); - - - }, - chooseFireMethod() { //set in simulation.startGame - if (tech.oneSuperBall) { - this.fire = this.fireOne - } else if (tech.superBallDelay) { - this.fire = this.fireQueue - } else { - this.fire = this.fireMulti - } - }, - fire() { } - }, - { - name: "wave", //3 - description: "emit wave packets that propagate through solids
waves slow mobs", // of oscillating particles
- ammo: 0, - ammoPack: 115, - defaultAmmoPack: 115, - have: false, - wavePacketCycle: 0, - delay: 40, - phononWaveCD: 0, - waves: [], //used in longitudinal mode - chooseFireMethod() { //set in simulation.startGame - this.waves = []; - if (tech.isLongitudinal) { - if (tech.is360Longitudinal) { - this.fire = this.fire360Longitudinal - this.do = this.do360Longitudinal - } else { - this.fire = this.fireLongitudinal - this.do = this.doLongitudinal + }, 21 * tech.bulletSize) + }, + fireMulti() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 23 : 15) * b.fireCDscale); // cool down + const SPREAD = m.crouch ? 0.08 : 0.13 + const num = 3 + Math.floor(tech.extraSuperBalls * Math.random()) + const speed = m.crouch ? 43 : 36 + let dir = m.angle - SPREAD * (num - 1) / 2; + for (let i = 0; i < num; i++) { + b.superBall({ + x: m.pos.x + 30 * Math.cos(dir), + y: m.pos.y + 30 * Math.sin(dir) + }, { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }, 11 * tech.bulletSize) + dir += SPREAD; } - } else { - this.fire = this.fireTransverse - this.do = this.doTransverse - } - }, - do() { }, - do360Longitudinal() { - if (!m.isBodiesAsleep) { - ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; - ctx.lineWidth = 2 * tech.wavePacketDamage - ctx.beginPath(); - const end = 700 * Math.sqrt(tech.bulletsLastLonger) - const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.43 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer + }, + fireQueue() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 23 : 15) * b.fireCDscale); // cool down + const num = 1 + 3 + Math.floor(tech.extraSuperBalls * Math.random()) //1 extra + const speed = m.crouch ? 43 : 36 - for (let i = this.waves.length - 1; i > -1; i--) { - //draw wave - ctx.moveTo(this.waves[i].position.x + this.waves[i].radius, this.waves[i].position.y) - ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, 0, 2 * Math.PI); - // collisions - // if (tech.isBulletTeleport && Math.random() < 0.04) { - // const scale = 400 * Math.random() - // this.waves[i].position = Vector.add(this.waves[i].position, { x: scale * (Math.random() - 0.5), y: scale * (Math.random() - 0.5) }) - // } - for (let j = 0, len = mob.length; j < len; j++) { - if (!mob[j].isShielded) { - const dist = Vector.magnitude(Vector.sub(this.waves[i].position, mob[j].position)) - const r = mob[j].radius + 30 - if (dist + r > this.waves[i].radius && dist - r < this.waves[i].radius) { - //make them shake around - if (!mob[j].isBadTarget) { - mob[j].force.x += 0.01 * (Math.random() - 0.5) * mob[j].mass - mob[j].force.y += 0.01 * (Math.random() - 0.5) * mob[j].mass + const delay = Math.floor((m.crouch ? 18 : 12) * b.fireCDscale) + m.fireCDcycle = m.cycle + delay; // cool down + function cycle() { + count++ + b.superBall({ + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }, 11 * tech.bulletSize) + if (count < num && m.alive) requestAnimationFrame(cycle); + m.fireCDcycle = m.cycle + delay; // cool down + } + let count = 0 + requestAnimationFrame(cycle); + + + }, + chooseFireMethod() { //set in simulation.startGame + if (tech.oneSuperBall) { + this.fire = this.fireOne + } else if (tech.superBallDelay) { + this.fire = this.fireQueue + } else { + this.fire = this.fireMulti + } + }, + fire() { } + }, + { + name: "wave", //3 + // description: `emit wave packets that propagate through solids
waves slow mobs
115 packets per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `emit wave packets that propagate through solids
waves slow mobs
${this.ammoPack.toFixed(0)} wave packets per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 115, + defaultAmmoPack: 115, + have: false, + wavePacketCycle: 0, + delay: 40, + phononWaveCD: 0, + waves: [], //used in longitudinal mode + chooseFireMethod() { //set in simulation.startGame + this.waves = []; + if (tech.isLongitudinal) { + if (tech.is360Longitudinal) { + this.fire = this.fire360Longitudinal + this.do = this.do360Longitudinal + } else { + this.fire = this.fireLongitudinal + this.do = this.doLongitudinal + } + } else { + this.fire = this.fireTransverse + this.do = this.doTransverse + } + }, + do() { }, + do360Longitudinal() { + if (!m.isBodiesAsleep) { + ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; + ctx.lineWidth = 2 * tech.wavePacketDamage + ctx.beginPath(); + const end = 700 * Math.sqrt(tech.bulletsLastLonger) + const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.43 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer + + for (let i = this.waves.length - 1; i > -1; i--) { + //draw wave + ctx.moveTo(this.waves[i].position.x + this.waves[i].radius, this.waves[i].position.y) + ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, 0, 2 * Math.PI); + // collisions + // if (tech.isBulletTeleport && Math.random() < 0.04) { + // const scale = 400 * Math.random() + // this.waves[i].position = Vector.add(this.waves[i].position, { x: scale * (Math.random() - 0.5), y: scale * (Math.random() - 0.5) }) + // } + for (let j = 0, len = mob.length; j < len; j++) { + if (!mob[j].isShielded) { + const dist = Vector.magnitude(Vector.sub(this.waves[i].position, mob[j].position)) + const r = mob[j].radius + 30 + if (dist + r > this.waves[i].radius && dist - r < this.waves[i].radius) { + //make them shake around + if (!mob[j].isBadTarget) { + mob[j].force.x += 0.01 * (Math.random() - 0.5) * mob[j].mass + mob[j].force.y += 0.01 * (Math.random() - 0.5) * mob[j].mass + } + // if (!mob[j].isShielded) { + Matter.Body.setVelocity(mob[j], { //friction + x: mob[j].velocity.x * 0.95, + y: mob[j].velocity.y * 0.95 + }); + //draw vibes + let vertices = mob[j].vertices; + const vibe = 50 + mob[j].radius * 0.15 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let k = 1; k < vertices.length; k++) { + ctx.lineTo(vertices[k].x + vibe * (Math.random() - 0.5), vertices[k].y + vibe * (Math.random() - 0.5)); + } + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + //damage + mob[j].locatePlayer(); + mob[j].damage(damage / Math.sqrt(mob[j].radius)); + // } + if (tech.isPhononWave && this.phononWaveCD < m.cycle) { + this.phononWaveCD = m.cycle + 8 * (1 + this.waves[i].resonanceCount) + this.waves.push({ + position: mob[j].position, + radius: 25, + resonanceCount: this.waves[i].resonanceCount + 1, + }) + } } - // if (!mob[j].isShielded) { - Matter.Body.setVelocity(mob[j], { //friction - x: mob[j].velocity.x * 0.95, - y: mob[j].velocity.y * 0.95 - }); + } + } + // for (let j = 0, len = body.length; j < len; j++) { + for (let j = 0, len = Math.min(30, body.length); j < len; j++) { + const dist = Vector.magnitude(Vector.sub(this.waves[i].position, body[j].position)) + const r = 20 + if (dist + r > this.waves[i].radius && dist - r < this.waves[i].radius) { + const who = body[j] + //make them shake around + who.force.x += 0.01 * (Math.random() - 0.5) * who.mass + who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity //draw vibes - let vertices = mob[j].vertices; - const vibe = 50 + mob[j].radius * 0.15 + let vertices = who.vertices; + const vibe = 25 ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); for (let k = 1; k < vertices.length; k++) { ctx.lineTo(vertices[k].x + vibe * (Math.random() - 0.5), vertices[k].y + vibe * (Math.random() - 0.5)); } ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); - //damage - mob[j].locatePlayer(); - mob[j].damage(damage / Math.sqrt(mob[j].radius)); - // } + + if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { + if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { + // Matter.Body.setAngularVelocity(who, (0.25 + 0.1 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); + who.torque += who.inertia * 0.001 * (Math.random() - 0.5) + } + } + } + this.waves[i].radius += 0.9 * tech.waveBeamSpeed //expand / move + // if (this.waves[i].radius > end) this.waves.splice(i, 1) //end + if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) { //* Math.pow(0.9, this.waves[i].resonanceCount) + this.waves.splice(i, 1) //end + } + } + ctx.stroke(); + } + }, + fire360Longitudinal() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down + this.waves.push({ + position: { x: m.pos.x, y: m.pos.y, }, + radius: 25, + resonanceCount: 0 //used with tech.isPhononWave + }) + }, + doLongitudinal() { + if (!m.isBodiesAsleep) { + ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; + ctx.lineWidth = 2 * tech.wavePacketDamage + ctx.beginPath(); + const end = 1100 * tech.bulletsLastLonger + const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.4 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer + for (let i = this.waves.length - 1; i > -1; i--) { + const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius)) + const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius)) + //draw wave + ctx.moveTo(v1.x, v1.y) + ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc); + //using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector + let hits = Matter.Query.ray(mob, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) + for (let j = 0; j < hits.length; j++) { + const who = hits[j].body + if (!who.isShielded) { + who.force.x += 0.01 * (Math.random() - 0.5) * who.mass + who.force.y += 0.01 * (Math.random() - 0.5) * who.mass + Matter.Body.setVelocity(who, { x: who.velocity.x * 0.95, y: who.velocity.y * 0.95 }); + let vertices = who.vertices; + const vibe = 50 + who.radius * 0.15 + ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); + ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); + who.locatePlayer(); + who.damage(damage / Math.sqrt(who.radius)); + if (tech.isPhononWave && this.phononWaveCD < m.cycle) { this.phononWaveCD = m.cycle + 8 * (1 + this.waves[i].resonanceCount) + const halfArc = 0.27 //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions + let closestMob, dist + let range = end - 30 * this.waves[i].resonanceCount + for (let i = 0, len = mob.length; i < len; i++) { + if (who !== mob[i] && !mob[i].isBadTarget && !mob[i].isInvulnerable) { + dist = Vector.magnitude(Vector.sub(who.position, mob[i].position)); + if (dist < range) { + closestMob = mob[i] + range = dist + } + } + } + if (closestMob) { + const dir = Vector.normalise(Vector.sub(closestMob.position, who.position)) + var angle = Math.atan2(dir.y, dir.x) + } else { + var angle = 2 * Math.PI * Math.random() + } this.waves.push({ - position: mob[j].position, + position: who.position, + angle: angle - halfArc, //used in drawing ctx.arc + unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision + unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision + arc: halfArc * 2, radius: 25, - resonanceCount: this.waves[i].resonanceCount + 1, + resonanceCount: this.waves[i].resonanceCount + 1 }) } } } - } - // for (let j = 0, len = body.length; j < len; j++) { - for (let j = 0, len = Math.min(30, body.length); j < len; j++) { - const dist = Vector.magnitude(Vector.sub(this.waves[i].position, body[j].position)) - const r = 20 - if (dist + r > this.waves[i].radius && dist - r < this.waves[i].radius) { - const who = body[j] + + hits = Matter.Query.ray(body, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) + for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { + const who = hits[j].body //make them shake around who.force.x += 0.01 * (Math.random() - 0.5) * who.mass who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity - //draw vibes let vertices = who.vertices; const vibe = 25 ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); - for (let k = 1; k < vertices.length; k++) { - ctx.lineTo(vertices[k].x + vibe * (Math.random() - 0.5), vertices[k].y + vibe * (Math.random() - 0.5)); + for (let j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); } ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { - // Matter.Body.setAngularVelocity(who, (0.25 + 0.1 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); + // Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); who.torque += who.inertia * 0.001 * (Math.random() - 0.5) } } - } - this.waves[i].radius += 0.9 * tech.waveBeamSpeed //expand / move - // if (this.waves[i].radius > end) this.waves.splice(i, 1) //end - if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) { //* Math.pow(0.9, this.waves[i].resonanceCount) - this.waves.splice(i, 1) //end - } - } - ctx.stroke(); - } - }, - fire360Longitudinal() { - m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down - this.waves.push({ - position: { x: m.pos.x, y: m.pos.y, }, - radius: 25, - resonanceCount: 0 //used with tech.isPhononWave - }) - }, - doLongitudinal() { - if (!m.isBodiesAsleep) { - ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; - ctx.lineWidth = 2 * tech.wavePacketDamage - ctx.beginPath(); - const end = 1100 * tech.bulletsLastLonger - const damage = 2.3 * m.dmgScale * tech.wavePacketDamage * tech.waveBeamDamage * (tech.isBulletTeleport ? 1.4 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1) //damage is lower for large radius mobs, since they feel the waves longer - for (let i = this.waves.length - 1; i > -1; i--) { - const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius)) - const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius)) - //draw wave - ctx.moveTo(v1.x, v1.y) - ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc); - //using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector - let hits = Matter.Query.ray(mob, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) - for (let j = 0; j < hits.length; j++) { - const who = hits[j].body - if (!who.isShielded) { - who.force.x += 0.01 * (Math.random() - 0.5) * who.mass - who.force.y += 0.01 * (Math.random() - 0.5) * who.mass - Matter.Body.setVelocity(who, { x: who.velocity.x * 0.95, y: who.velocity.y * 0.95 }); - let vertices = who.vertices; - const vibe = 50 + who.radius * 0.15 - ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); - for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); - ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); - who.locatePlayer(); - who.damage(damage / Math.sqrt(who.radius)); - if (tech.isPhononWave && this.phononWaveCD < m.cycle) { - this.phononWaveCD = m.cycle + 8 * (1 + this.waves[i].resonanceCount) - const halfArc = 0.27 //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions - let closestMob, dist - let range = end - 30 * this.waves[i].resonanceCount - for (let i = 0, len = mob.length; i < len; i++) { - if (who !== mob[i] && !mob[i].isBadTarget && !mob[i].isInvulnerable) { - dist = Vector.magnitude(Vector.sub(who.position, mob[i].position)); - if (dist < range) { - closestMob = mob[i] - range = dist - } - } + this.waves[i].radius += tech.waveBeamSpeed * 1.8 //expand / move + if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) { + this.waves.splice(i, 1) //end + } + } + ctx.stroke(); + } + }, + fireLongitudinal() { + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down + const halfArc = (m.crouch ? 0.0785 : 0.275) * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions + const angle = m.angle + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5) + this.waves.push({ + position: { x: m.pos.x + 25 * Math.cos(m.angle), y: m.pos.y + 25 * Math.sin(m.angle), }, + angle: angle - halfArc, //used in drawing ctx.arc + unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision + unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision + arc: halfArc * 2, + radius: 25, + resonanceCount: 0 + }) + }, + doTransverse() { + // if (this.wavePacketCycle && !input.fire) { + // this.wavePacketCycle = 0; + // m.fireCDcycle = m.cycle + Math.floor(this.delay * b.fireCDscale); // cool down + // } + }, + fireTransverse() { + totalCycles = Math.floor((3.5) * 35 * tech.waveReflections * tech.bulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5)) + const me = bullet.length; + bullet[me] = Bodies.polygon(m.pos.x + 25 * Math.cos(m.angle), m.pos.y + 25 * Math.sin(m.angle), 5, 4, { + angle: m.angle, + cycle: -0.5, + endCycle: simulation.cycle + totalCycles, + inertia: Infinity, + frictionAir: 0, + slow: 0, + // amplitude: (m.crouch ? 5 : 10) * ((this.wavePacketCycle % 2) ? -1 : 1) * Math.sin((this.wavePacketCycle + 1) * 0.088), //0.0968 //0.1012 //0.11 //0.088 //shorten wave packet + amplitude: (m.crouch ? 6 : 12) * ((this.wavePacketCycle % 2) ? -1 : 1) * Math.sin(this.wavePacketCycle * 0.088) * Math.sin(this.wavePacketCycle * 0.04), //0.0968 //0.1012 //0.11 //0.088 //shorten wave packet + minDmgSpeed: 0, + dmg: m.dmgScale * tech.waveBeamDamage * tech.wavePacketDamage * (tech.isBulletTeleport ? 1.43 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1), //also control damage when you divide by mob.mass + dmgCoolDown: 0, + classType: "bullet", + collisionFilter: { + category: 0, + mask: 0, //cat.mob | cat.mobBullet | cat.mobShield + }, + beforeDmg() { }, + onEnd() { }, + do() { }, + query() { + let slowCheck = 1 + if (Matter.Query.point(map, this.position).length) { //check if inside map + slowCheck = waveSpeedMap + } else { //check if inside a body + let q = Matter.Query.point(body, this.position) + if (q.length) { + slowCheck = waveSpeedBody + Matter.Body.setPosition(this, Vector.add(this.position, q[0].velocity)) //move with the medium + } + } + if (slowCheck !== this.slow) { //toggle velocity based on inside and outside status change + this.slow = slowCheck + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(this.velocity), tech.waveBeamSpeed * slowCheck)); + } + + if (this.dmgCoolDown < 1) { + q = Matter.Query.point(mob, this.position) // check if inside a mob + for (let i = 0; i < q.length; i++) { + this.dmgCoolDown = 5 + Math.floor(8 * Math.random() * b.fireCDscale); + let dmg = this.dmg + q[i].damage(dmg); + if (q[i].alive) { + q[i].foundPlayer(); + Matter.Body.setVelocity(q[i], Vector.mult(q[i].velocity, 0.9)) } - if (closestMob) { - const dir = Vector.normalise(Vector.sub(closestMob.position, who.position)) - var angle = Math.atan2(dir.y, dir.x) - } else { - var angle = 2 * Math.PI * Math.random() + // this.endCycle = 0; //bullet ends cycle after doing damage + if (q[i].damageReduction) { + simulation.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: Math.log(dmg + 1.1) * 40 * q[i].damageReduction + 3, + color: 'rgba(0,0,0,0.4)', + time: simulation.drawTime + }); } - this.waves.push({ - position: who.position, - angle: angle - halfArc, //used in drawing ctx.arc - unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision - unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision - arc: halfArc * 2, - radius: 25, - resonanceCount: this.waves[i].resonanceCount + 1 - }) } + } else { + this.dmgCoolDown-- } - } - - hits = Matter.Query.ray(body, v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) - for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { - const who = hits[j].body - //make them shake around - who.force.x += 0.01 * (Math.random() - 0.5) * who.mass - who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity - let vertices = who.vertices; - const vibe = 25 - ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); - for (let j = 1; j < vertices.length; j++) { - ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); - } - ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); - - if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { - if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { - // Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); - who.torque += who.inertia * 0.001 * (Math.random() - 0.5) - } - } - - this.waves[i].radius += tech.waveBeamSpeed * 1.8 //expand / move - if (this.waves[i].radius > end - 30 * this.waves[i].resonanceCount) { - this.waves.splice(i, 1) //end - } - } - ctx.stroke(); - } - }, - fireLongitudinal() { - m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 4 : 8) * b.fireCDscale); // cool down - const halfArc = (m.crouch ? 0.0785 : 0.275) * (tech.isBulletTeleport ? 0.66 + (Math.random() - 0.5) : 1) //6.28 is a full circle, but these arcs needs to stay small because we are using small angle linear approximation, for collisions - const angle = m.angle + tech.isBulletTeleport * 0.3 * (Math.random() - 0.5) - this.waves.push({ - position: { x: m.pos.x + 25 * Math.cos(m.angle), y: m.pos.y + 25 * Math.sin(m.angle), }, - angle: angle - halfArc, //used in drawing ctx.arc - unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision - unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision - arc: halfArc * 2, - radius: 25, - resonanceCount: 0 - }) - }, - doTransverse() { - // if (this.wavePacketCycle && !input.fire) { - // this.wavePacketCycle = 0; - // m.fireCDcycle = m.cycle + Math.floor(this.delay * b.fireCDscale); // cool down - // } - }, - fireTransverse() { - totalCycles = Math.floor((3.5) * 35 * tech.waveReflections * tech.bulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5)) - const me = bullet.length; - bullet[me] = Bodies.polygon(m.pos.x + 25 * Math.cos(m.angle), m.pos.y + 25 * Math.sin(m.angle), 5, 4, { - angle: m.angle, - cycle: -0.5, - endCycle: simulation.cycle + totalCycles, - inertia: Infinity, - frictionAir: 0, - slow: 0, - // amplitude: (m.crouch ? 5 : 10) * ((this.wavePacketCycle % 2) ? -1 : 1) * Math.sin((this.wavePacketCycle + 1) * 0.088), //0.0968 //0.1012 //0.11 //0.088 //shorten wave packet - amplitude: (m.crouch ? 6 : 12) * ((this.wavePacketCycle % 2) ? -1 : 1) * Math.sin(this.wavePacketCycle * 0.088) * Math.sin(this.wavePacketCycle * 0.04), //0.0968 //0.1012 //0.11 //0.088 //shorten wave packet - minDmgSpeed: 0, - dmg: m.dmgScale * tech.waveBeamDamage * tech.wavePacketDamage * (tech.isBulletTeleport ? 1.43 : 1) * (tech.isInfiniteWaveAmmo ? 0.75 : 1), //also control damage when you divide by mob.mass - dmgCoolDown: 0, - classType: "bullet", - collisionFilter: { - category: 0, - mask: 0, //cat.mob | cat.mobBullet | cat.mobShield - }, - beforeDmg() { }, - onEnd() { }, - do() { }, - query() { - let slowCheck = 1 - if (Matter.Query.point(map, this.position).length) { //check if inside map - slowCheck = waveSpeedMap - } else { //check if inside a body - let q = Matter.Query.point(body, this.position) - if (q.length) { - slowCheck = waveSpeedBody - Matter.Body.setPosition(this, Vector.add(this.position, q[0].velocity)) //move with the medium - } - } - if (slowCheck !== this.slow) { //toggle velocity based on inside and outside status change - this.slow = slowCheck - Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(this.velocity), tech.waveBeamSpeed * slowCheck)); - } - - if (this.dmgCoolDown < 1) { - q = Matter.Query.point(mob, this.position) // check if inside a mob - for (let i = 0; i < q.length; i++) { - this.dmgCoolDown = 5 + Math.floor(8 * Math.random() * b.fireCDscale); - let dmg = this.dmg - q[i].damage(dmg); - if (q[i].alive) { - q[i].foundPlayer(); - Matter.Body.setVelocity(q[i], Vector.mult(q[i].velocity, 0.9)) - } - // this.endCycle = 0; //bullet ends cycle after doing damage - if (q[i].damageReduction) { - simulation.drawList.push({ //add dmg to draw queue - x: this.position.x, - y: this.position.y, - radius: Math.log(dmg + 1.1) * 40 * q[i].damageReduction + 3, - color: 'rgba(0,0,0,0.4)', - time: simulation.drawTime - }); - } - } - } else { - this.dmgCoolDown-- - } - }, - wiggle() { - this.cycle++ - const where = Vector.mult(transverse, this.amplitude * Math.cos(this.cycle * tech.waveFrequency)) - Matter.Body.setPosition(this, Vector.add(this.position, where)) - } - }); - if (tech.isBulletTeleport) { - bullet[me].wiggle = function () { - this.cycle++ - const where = Vector.mult(transverse, this.amplitude * Math.cos(this.cycle * tech.waveFrequency)) - if (Math.random() < 0.005) { - if (Math.random() < 0.33) { //randomize position - const scale = 500 * Math.random() - Matter.Body.setPosition(this, Vector.add({ - x: scale * (Math.random() - 0.5), - y: scale * (Math.random() - 0.5) - }, Vector.add(this.position, where))) - } else { //randomize position in velocity direction - const velocityScale = Vector.mult(this.velocity, 50 * (Math.random() - 0.5)) - Matter.Body.setPosition(this, Vector.add(velocityScale, Vector.add(this.position, where))) - } - - } else { + }, + wiggle() { + this.cycle++ + const where = Vector.mult(transverse, this.amplitude * Math.cos(this.cycle * tech.waveFrequency)) Matter.Body.setPosition(this, Vector.add(this.position, where)) } - } - } - let waveSpeedMap = 0.1 - let waveSpeedBody = 0.25 - if (tech.isPhaseVelocity) { - waveSpeedMap = 3.5 - waveSpeedBody = 2 - bullet[me].dmg *= 1.4 - } - if (tech.waveReflections) { - bullet[me].reflectCycle = totalCycles / tech.waveReflections //tech.waveLengthRange - bullet[me].do = function () { - this.query() - if (this.cycle > this.reflectCycle) { - this.reflectCycle += totalCycles / tech.waveReflections - Matter.Body.setVelocity(this, Vector.mult(this.velocity, -1)); - // if (this.reflectCycle > tech.waveLengthRange * (1 + tech.waveReflections)) this.endCycle = 0; + }); + if (tech.isBulletTeleport) { + bullet[me].wiggle = function () { + this.cycle++ + const where = Vector.mult(transverse, this.amplitude * Math.cos(this.cycle * tech.waveFrequency)) + if (Math.random() < 0.005) { + if (Math.random() < 0.33) { //randomize position + const scale = 500 * Math.random() + Matter.Body.setPosition(this, Vector.add({ + x: scale * (Math.random() - 0.5), + y: scale * (Math.random() - 0.5) + }, Vector.add(this.position, where))) + } else { //randomize position in velocity direction + const velocityScale = Vector.mult(this.velocity, 50 * (Math.random() - 0.5)) + Matter.Body.setPosition(this, Vector.add(velocityScale, Vector.add(this.position, where))) + } + + } else { + Matter.Body.setPosition(this, Vector.add(this.position, where)) + } } - this.wiggle() } - } else { - bullet[me].do = function () { - this.query() - this.wiggle(); + let waveSpeedMap = 0.1 + let waveSpeedBody = 0.25 + if (tech.isPhaseVelocity) { + waveSpeedMap = 3.5 + waveSpeedBody = 2 + bullet[me].dmg *= 1.4 } - } - Composite.add(engine.world, bullet[me]); //add bullet to world - Matter.Body.setVelocity(bullet[me], { - x: tech.waveBeamSpeed * Math.cos(m.angle), - y: tech.waveBeamSpeed * Math.sin(m.angle) - }); - const transverse = Vector.normalise(Vector.perp(bullet[me].velocity)) - this.wavePacketCycle++ + if (tech.waveReflections) { + bullet[me].reflectCycle = totalCycles / tech.waveReflections //tech.waveLengthRange + bullet[me].do = function () { + this.query() + if (this.cycle > this.reflectCycle) { + this.reflectCycle += totalCycles / tech.waveReflections + Matter.Body.setVelocity(this, Vector.mult(this.velocity, -1)); + // if (this.reflectCycle > tech.waveLengthRange * (1 + tech.waveReflections)) this.endCycle = 0; + } + this.wiggle() + } + } else { + bullet[me].do = function () { + this.query() + this.wiggle(); + } + } + Composite.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { + x: tech.waveBeamSpeed * Math.cos(m.angle), + y: tech.waveBeamSpeed * Math.sin(m.angle) + }); + const transverse = Vector.normalise(Vector.perp(bullet[me].velocity)) + this.wavePacketCycle++ + }, + fire() { } }, - fire() { } - }, - { - name: "missiles", //6 - description: "launch homing missiles that target mobs
missiles explode on contact with mobs", - ammo: 0, - ammoPack: 5, - have: false, - fireCycle: 0, - do() { }, - fire() { - const countReduction = Math.pow(0.86, tech.missileCount) - // if (m.crouch) { - // m.fireCDcycle = m.cycle + tech.missileFireCD * b.fireCDscale / countReduction; // cool down - // // for (let i = 0; i < tech.missileCount; i++) { - // // b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2, Math.sqrt(countReduction)) - // // bullet[bullet.length - 1].force.x += 0.004 * countReduction * (i - (tech.missileCount - 1) / 2); - // // } + { + name: "missiles", //6 + // description: `launch homing missiles that target mobs
missiles explode on contact with mobs
5 missiles per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `launch homing missiles that target mobs
missiles explode on contact with mobs
${this.ammoPack.toFixed(1)} missiles per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 5, + have: false, + fireCycle: 0, + do() { }, + fire() { + const countReduction = Math.pow(0.86, tech.missileCount) + // if (m.crouch) { + // m.fireCDcycle = m.cycle + tech.missileFireCD * b.fireCDscale / countReduction; // cool down + // // for (let i = 0; i < tech.missileCount; i++) { + // // b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2, Math.sqrt(countReduction)) + // // bullet[bullet.length - 1].force.x += 0.004 * countReduction * (i - (tech.missileCount - 1) / 2); + // // } - // if (tech.missileCount > 1) { - // for (let i = 0; i < tech.missileCount; i++) { - // setTimeout(() => { - // const where = { x: m.pos.x, y: m.pos.y - 40 } - // b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2, Math.sqrt(countReduction)) - // bullet[bullet.length - 1].force.x += 0.025 * countReduction * (i - (tech.missileCount - 1) / 2); - // }, 20 * tech.missileCount * Math.random()); - // } - // } else { - // const where = { - // x: m.pos.x, - // y: m.pos.y - 40 - // } - // b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5), -2) - // } - // } else { - m.fireCDcycle = m.cycle + tech.missileFireCD * b.fireCDscale / countReduction; // cool down - const direction = { - x: Math.cos(m.angle), - y: Math.sin(m.angle) - } - // const where = { - // x: m.pos.x + 30 * direction.x, - // y: m.pos.y + 30 * direction.y - // } - if (tech.missileCount > 1) { - const push = Vector.mult(Vector.perp(direction), 0.2 * countReduction / Math.sqrt(tech.missileCount)) - const sqrtCountReduction = Math.sqrt(countReduction) - // for (let i = 0; i < tech.missileCount; i++) { - // setTimeout(() => { - // if (m.crouch) { - // b.missile(where, m.angle, 20, sqrtCountReduction) - // // bullet[bullet.length - 1].force.x += 0.7 * push.x * (i - (tech.missileCount - 1) / 2); - // // bullet[bullet.length - 1].force.y += 0.7 * push.y * (i - (tech.missileCount - 1) / 2); - // } else { - // b.missile(where, m.angle, -10, sqrtCountReduction) - // bullet[bullet.length - 1].force.x += push.x * (i - (tech.missileCount - 1) / 2); - // bullet[bullet.length - 1].force.y += 0.005 + push.y * (i - (tech.missileCount - 1) / 2); + // if (tech.missileCount > 1) { + // for (let i = 0; i < tech.missileCount; i++) { + // setTimeout(() => { + // const where = { x: m.pos.x, y: m.pos.y - 40 } + // b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2, Math.sqrt(countReduction)) + // bullet[bullet.length - 1].force.x += 0.025 * countReduction * (i - (tech.missileCount - 1) / 2); + // }, 20 * tech.missileCount * Math.random()); // } - - // }, 1 + i * 10 * tech.missileCount); + // } else { + // const where = { + // x: m.pos.x, + // y: m.pos.y - 40 + // } + // b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5), -2) + // } + // } else { + m.fireCDcycle = m.cycle + tech.missileFireCD * b.fireCDscale / countReduction; // cool down + const direction = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + } + // const where = { + // x: m.pos.x + 30 * direction.x, + // y: m.pos.y + 30 * direction.y // } - const launchDelay = 4 - let count = 0 - const fireMissile = () => { + if (tech.missileCount > 1) { + const push = Vector.mult(Vector.perp(direction), 0.2 * countReduction / Math.sqrt(tech.missileCount)) + const sqrtCountReduction = Math.sqrt(countReduction) + // for (let i = 0; i < tech.missileCount; i++) { + // setTimeout(() => { + // if (m.crouch) { + // b.missile(where, m.angle, 20, sqrtCountReduction) + // // bullet[bullet.length - 1].force.x += 0.7 * push.x * (i - (tech.missileCount - 1) / 2); + // // bullet[bullet.length - 1].force.y += 0.7 * push.y * (i - (tech.missileCount - 1) / 2); + // } else { + // b.missile(where, m.angle, -10, sqrtCountReduction) + // bullet[bullet.length - 1].force.x += push.x * (i - (tech.missileCount - 1) / 2); + // bullet[bullet.length - 1].force.y += 0.005 + push.y * (i - (tech.missileCount - 1) / 2); + // } + + // }, 1 + i * 10 * tech.missileCount); + // } + const launchDelay = 4 + let count = 0 + const fireMissile = () => { + if (m.crouch) { + b.missile({ + x: m.pos.x + 30 * direction.x, + y: m.pos.y + 30 * direction.y + }, m.angle, 20, sqrtCountReduction) + bullet[bullet.length - 1].force.x += 0.5 * push.x * (Math.random() - 0.5) + bullet[bullet.length - 1].force.y += 0.004 + 0.5 * push.y * (Math.random() - 0.5) + } else { + b.missile({ + x: m.pos.x + 30 * direction.x, + y: m.pos.y + 30 * direction.y + }, m.angle, -15, sqrtCountReduction) + bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5) + bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) + } + } + const cycle = () => { + if ((simulation.paused || m.isBodiesAsleep) && m.alive) { + requestAnimationFrame(cycle) + } else { + count++ + if (!(count % launchDelay)) { + fireMissile() + } + if (count < tech.missileCount * launchDelay && m.alive) requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + } else { if (m.crouch) { b.missile({ - x: m.pos.x + 30 * direction.x, - y: m.pos.y + 30 * direction.y - }, m.angle, 20, sqrtCountReduction) - bullet[bullet.length - 1].force.x += 0.5 * push.x * (Math.random() - 0.5) - bullet[bullet.length - 1].force.y += 0.004 + 0.5 * push.y * (Math.random() - 0.5) + x: m.pos.x + 40 * direction.x, + y: m.pos.y + 40 * direction.y + }, m.angle, 25) } else { b.missile({ - x: m.pos.x + 30 * direction.x, - y: m.pos.y + 30 * direction.y - }, m.angle, -15, sqrtCountReduction) - bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5) - bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) + x: m.pos.x + 40 * direction.x, + y: m.pos.y + 40 * direction.y + }, m.angle, -12) + bullet[bullet.length - 1].force.y += 0.04 * (Math.random() - 0.2) } } - const cycle = () => { - if ((simulation.paused || m.isBodiesAsleep) && m.alive) { - requestAnimationFrame(cycle) - } else { - count++ - if (!(count % launchDelay)) { - fireMissile() - } - if (count < tech.missileCount * launchDelay && m.alive) requestAnimationFrame(cycle); - } + } + }, { + name: "grenades", //5 + // description: `lob a single bouncy projectile
explodes on contact or after one second
7 grenades per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `lob a single bouncy projectile
explodes on contact or after one second
${this.ammoPack.toFixed(0)} grenades per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 7, + have: false, + do() { }, //do is set in b.setGrenadeMode() + fire() { + const countReduction = Math.pow(0.93, tech.missileCount) + m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 35 : 27) * b.fireCDscale / countReduction); // cool down + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) } - requestAnimationFrame(cycle); - } else { - if (m.crouch) { - b.missile({ - x: m.pos.x + 40 * direction.x, - y: m.pos.y + 40 * direction.y - }, m.angle, 25) + const SPREAD = m.crouch ? 0.12 : 0.2 + let angle = m.angle - SPREAD * (tech.missileCount - 1) / 2; + for (let i = 0; i < tech.missileCount; i++) { + b.grenade(where, angle, countReduction) //function(where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) }, angle = m.angle, size = 1) + angle += SPREAD + } + }, + }, { + name: "spores", //6 + // description: `toss a sporangium that discharges spores
spores seek out nearby mobs
2-3 sporangium per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `toss a sporangium that discharges ${b.guns[6].nameString("s")}
${b.guns[6].nameString("s")} seek out nearby mobs
${this.ammoPack.toFixed(1)} sporangium per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 2.6, + have: false, + nameString(suffix = "") { + if (tech.isSporeFlea) { + return `flea${suffix}` + } else if (tech.isSporeWorm) { + return `worm${suffix}` } else { - b.missile({ - x: m.pos.x + 40 * direction.x, - y: m.pos.y + 40 * direction.y - }, m.angle, -12) - bullet[bullet.length - 1].force.y += 0.04 * (Math.random() - 0.2) + return `spore${suffix}` } - } - } - }, { - name: "grenades", //5 - description: "lob a single bouncy projectile
explodes on contact or after one second", - ammo: 0, - ammoPack: 7, - have: false, - do() { }, //do is set in b.setGrenadeMode() - fire() { - const countReduction = Math.pow(0.93, tech.missileCount) - m.fireCDcycle = m.cycle + Math.floor((m.crouch ? 35 : 27) * b.fireCDscale / countReduction); // cool down - const where = { - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - } - const SPREAD = m.crouch ? 0.12 : 0.2 - let angle = m.angle - SPREAD * (tech.missileCount - 1) / 2; - for (let i = 0; i < tech.missileCount; i++) { - b.grenade(where, angle, countReduction) //function(where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) }, angle = m.angle, size = 1) - angle += SPREAD - } - }, - }, { - name: "spores", //6 - description: "fire a sporangium that discharges spores
spores seek out nearby mobs", - ammo: 0, - ammoPack: 2.6, - have: false, - nameString(suffix = "") { - if (tech.isSporeFlea) { - return `flea${suffix}` - } else if (tech.isSporeWorm) { - return `worm${suffix}` - } else { - return `spore${suffix}` - } - }, - do() { }, - fire() { - const me = bullet.length; - const dir = m.angle; - bullet[me] = Bodies.polygon(m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle), 20, 4.5, b.fireAttributes(dir, false)); - b.fireProps(m.crouch ? 40 : 20, m.crouch ? 30 : 16, dir, me); //cd , speed - Matter.Body.setDensity(bullet[me], 0.000001); - bullet[me].endCycle = simulation.cycle + 480 + Math.max(0, 120 - 2 * bullet.length); - bullet[me].frictionAir = 0; - bullet[me].friction = 0.5; - bullet[me].radius = 4.5; - bullet[me].maxRadius = 30; - bullet[me].restitution = 0.3; - bullet[me].minDmgSpeed = 0; - bullet[me].totalSpores = 8 + 2 * tech.isSporeFreeze + 4 * tech.isSporeColony - bullet[me].stuck = function () { }; - bullet[me].beforeDmg = function () { }; - bullet[me].do = function () { - function onCollide(that) { - that.collisionFilter.mask = 0; //non collide with everything - Matter.Body.setVelocity(that, { - x: 0, - y: 0 - }); - that.do = that.grow; - } - const mobCollisions = Matter.Query.collides(this, mob) - if (mobCollisions.length) { - onCollide(this) - this.stuckTo = mobCollisions[0].bodyA - if (tech.isZombieMobs) this.stuckTo.isSoonZombie = true - if (this.stuckTo.isVerticesChange) { - this.stuckToRelativePosition = { x: 0, y: 0 } - } else { - //find the relative position for when the mob is at angle zero by undoing the mobs rotation - this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) + }, + do() { }, + fire() { + const me = bullet.length; + const dir = m.angle; + bullet[me] = Bodies.polygon(m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle), 20, 4.5, b.fireAttributes(dir, false)); + b.fireProps(m.crouch ? 40 : 20, m.crouch ? 30 : 16, dir, me); //cd , speed + Matter.Body.setDensity(bullet[me], 0.000001); + bullet[me].endCycle = simulation.cycle + 480 + Math.max(0, 120 - 2 * bullet.length); + bullet[me].frictionAir = 0; + bullet[me].friction = 0.5; + bullet[me].radius = 4.5; + bullet[me].maxRadius = 30; + bullet[me].restitution = 0.3; + bullet[me].minDmgSpeed = 0; + bullet[me].totalSpores = 8 + 2 * tech.isSporeFreeze + 4 * tech.isSporeColony + bullet[me].stuck = function () { }; + bullet[me].beforeDmg = function () { }; + bullet[me].do = function () { + function onCollide(that) { + that.collisionFilter.mask = 0; //non collide with everything + Matter.Body.setVelocity(that, { + x: 0, + y: 0 + }); + that.do = that.grow; } - this.stuck = function () { - if (this.stuckTo && this.stuckTo.alive) { - const rotate = Vector.rotate(this.stuckToRelativePosition, this.stuckTo.angle) //add in the mob's new angle to the relative position vector - Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.stuckTo.velocity), this.stuckTo.position)) - Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck + const mobCollisions = Matter.Query.collides(this, mob) + if (mobCollisions.length) { + onCollide(this) + this.stuckTo = mobCollisions[0].bodyA + if (tech.isZombieMobs) this.stuckTo.isSoonZombie = true + if (this.stuckTo.isVerticesChange) { + this.stuckToRelativePosition = { x: 0, y: 0 } } else { - this.collisionFilter.mask = cat.map; //non collide with everything but map - this.stuck = function () { - this.force.y += this.mass * 0.0006; - } - } - } - } else { - const bodyCollisions = Matter.Query.collides(this, body) - if (bodyCollisions.length) { - if (!bodyCollisions[0].bodyA.isNonStick) { - onCollide(this) - this.stuckTo = bodyCollisions[0].bodyA //find the relative position for when the mob is at angle zero by undoing the mobs rotation this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) - } else { - this.do = this.grow; } this.stuck = function () { - if (this.stuckTo) { + if (this.stuckTo && this.stuckTo.alive) { const rotate = Vector.rotate(this.stuckToRelativePosition, this.stuckTo.angle) //add in the mob's new angle to the relative position vector Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.stuckTo.velocity), this.stuckTo.position)) - // Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck + Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck } else { - this.force.y += this.mass * 0.0006; + this.collisionFilter.mask = cat.map; //non collide with everything but map + this.stuck = function () { + this.force.y += this.mass * 0.0006; + } } } } else { - if (Matter.Query.collides(this, map).length) { - onCollide(this) - } else { //if colliding with nothing just fall - this.force.y += this.mass * 0.0006; - simulation.mouseInGame.x + const bodyCollisions = Matter.Query.collides(this, body) + if (bodyCollisions.length) { + if (!bodyCollisions[0].bodyA.isNonStick) { + onCollide(this) + this.stuckTo = bodyCollisions[0].bodyA + //find the relative position for when the mob is at angle zero by undoing the mobs rotation + this.stuckToRelativePosition = Vector.rotate(Vector.sub(this.position, this.stuckTo.position), -this.stuckTo.angle) + } else { + this.do = this.grow; + } + this.stuck = function () { + if (this.stuckTo) { + const rotate = Vector.rotate(this.stuckToRelativePosition, this.stuckTo.angle) //add in the mob's new angle to the relative position vector + Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.stuckTo.velocity), this.stuckTo.position)) + // Matter.Body.setVelocity(this, this.stuckTo.velocity); //so that it will move properly if it gets unstuck + } else { + this.force.y += this.mass * 0.0006; + } + } + } else { + if (Matter.Query.collides(this, map).length) { + onCollide(this) + } else { //if colliding with nothing just fall + this.force.y += this.mass * 0.0006; + simulation.mouseInGame.x + } } } + //draw green glow + ctx.fillStyle = "rgba(0,200,125,0.16)"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI); + ctx.fill(); } - //draw green glow - ctx.fillStyle = "rgba(0,200,125,0.16)"; - ctx.beginPath(); - ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI); - ctx.fill(); - } - bullet[me].grow = function () { - this.stuck(); //runs different code based on what the bullet is stuck to - let scale = 1.01 - if (tech.isSporeGrowth && !(simulation.cycle % 40)) { //release a spore - if (tech.isSporeFlea) { - if (!(simulation.cycle % 80)) { + bullet[me].grow = function () { + this.stuck(); //runs different code based on what the bullet is stuck to + let scale = 1.01 + if (tech.isSporeGrowth && !(simulation.cycle % 40)) { //release a spore + if (tech.isSporeFlea) { + if (!(simulation.cycle % 80)) { + const speed = 10 + 5 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea(this.position, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }) + } + } else if (tech.isSporeWorm) { + if (!(simulation.cycle % 80)) b.worm(this.position) + } else { + b.spore(this.position) + } + scale = 0.96 + if (this.stuckTo && this.stuckTo.alive) scale = 0.9 + Matter.Body.scale(this, scale, scale); + this.radius *= scale + } else { + if (this.stuckTo && this.stuckTo.alive) scale = 1.03 + Matter.Body.scale(this, scale, scale); + this.radius *= scale + if (this.radius > this.maxRadius) this.endCycle = 0; + } + //draw green glow + ctx.fillStyle = "rgba(0,200,125,0.16)"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI); + ctx.fill(); + }; + //spawn bullets on end + bullet[me].onEnd = function () { + let count = 0 //used in for loop below + const things = [ + () => { //spore + b.spore(this.position) + }, + () => { //worm + count++ //count as 2 things + b.worm(this.position) + }, + () => { //flea + count++ //count as 2 things const speed = 10 + 5 * Math.random() const angle = 2 * Math.PI * Math.random() b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) }) + }, + () => { // drones + b.drone(this.position) + }, + () => { // ice IX + b.iceIX(1, Math.random() * 2 * Math.PI, this.position) + }, + () => { //missile + count++ //count as 2 things + b.missile(this.position, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) + }, + () => { //nail + b.targetedNail(this.position, 1, 39 + 6 * Math.random()) + }, + () => { //super ball + const speed = 36 + const angle = 2 * Math.PI * Math.random() + b.superBall(this.position, { + x: speed * Math.cos(angle), + y: speed * Math.sin(angle) + }, 11 * tech.bulletSize) + }, + ] + + for (len = this.totalSpores; count < len; count++) { + if (tech.isSporeColony && Math.random() < 0.5) { + things[Math.floor(Math.random() * things.length)]() + } else if (tech.isSporeFlea) { + things[2]() + } else if (tech.isSporeWorm) { + things[1]() + } else { + things[0]() //spores } - } else if (tech.isSporeWorm) { - if (!(simulation.cycle % 80)) b.worm(this.position) - } else { - b.spore(this.position) } - scale = 0.96 - if (this.stuckTo && this.stuckTo.alive) scale = 0.9 - Matter.Body.scale(this, scale, scale); - this.radius *= scale + // } else if (tech.isSporeFlea) { + // for (let i = 0, len = this.totalSpores; i < len; i++) things[2]() + // } else if (tech.isSporeWorm) { + // for (let i = 0, len = this.totalSpores; i < len; i++) things[1]() + // } else { + // for (let i = 0; i < this.totalSpores; i++) things[0]() + // } + if (tech.isStun) b.AoEStunEffect(this.position, 600, 270 + 120 * Math.random()); //AoEStunEffect(where, range, cycles = 120 + 60 * Math.random()) { + } + } + }, { + name: "drones", //7 + // description: `deploy autonomous drones that smash into mobs
and collect nearby power ups
16 drones per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `deploy autonomous drones that smash into mobs
drones collect nearby power ups
${this.ammoPack.toFixed(0)} drones per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 16, + defaultAmmoPack: 16, + have: false, + do() { }, + fire() { + if (tech.isDroneRadioactive) { + if (m.crouch) { + b.droneRadioactive({ + x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) + }, 45) + m.fireCDcycle = m.cycle + Math.floor(45 * b.fireCDscale); // cool down + } else { + b.droneRadioactive({ + x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) + }, 10) + m.fireCDcycle = m.cycle + Math.floor(25 * b.fireCDscale); // cool down + } } else { - if (this.stuckTo && this.stuckTo.alive) scale = 1.03 - Matter.Body.scale(this, scale, scale); - this.radius *= scale - if (this.radius > this.maxRadius) this.endCycle = 0; - } - //draw green glow - ctx.fillStyle = "rgba(0,200,125,0.16)"; - ctx.beginPath(); - ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI); - ctx.fill(); - }; - //spawn bullets on end - bullet[me].onEnd = function () { - let count = 0 //used in for loop below - const things = [ - () => { //spore - b.spore(this.position) - }, - () => { //worm - count++ //count as 2 things - b.worm(this.position) - }, - () => { //flea - count++ //count as 2 things - const speed = 10 + 5 * Math.random() - const angle = 2 * Math.PI * Math.random() - b.flea(this.position, { - x: speed * Math.cos(angle), - y: speed * Math.sin(angle) - }) - }, - () => { // drones - b.drone(this.position) - }, - () => { // ice IX - b.iceIX(1, Math.random() * 2 * Math.PI, this.position) - }, - () => { //missile - count++ //count as 2 things - b.missile(this.position, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) - }, - () => { //nail - b.targetedNail(this.position, 1, 39 + 6 * Math.random()) - }, - () => { //super ball - const speed = 36 - const angle = 2 * Math.PI * Math.random() - b.superBall(this.position, { - x: speed * Math.cos(angle), - y: speed * Math.sin(angle) - }, 11 * tech.bulletSize) - }, - ] - - for (len = this.totalSpores; count < len; count++) { - if (tech.isSporeColony && Math.random() < 0.5) { - things[Math.floor(Math.random() * things.length)]() - } else if (tech.isSporeFlea) { - things[2]() - } else if (tech.isSporeWorm) { - things[1]() + if (m.crouch) { + b.drone({ + x: m.pos.x + 30 * Math.cos(m.angle) + 5 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 5 * (Math.random() - 0.5) + }, 50) + m.fireCDcycle = m.cycle + Math.floor(7 * b.fireCDscale); // cool down } else { - things[0]() //spores + b.drone({ + x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) + }, 15) + m.fireCDcycle = m.cycle + Math.floor(4 * b.fireCDscale); // cool down } } - // } else if (tech.isSporeFlea) { - // for (let i = 0, len = this.totalSpores; i < len; i++) things[2]() - // } else if (tech.isSporeWorm) { - // for (let i = 0, len = this.totalSpores; i < len; i++) things[1]() + } + }, + { + name: "foam", //8 + // description: `spray bubbly foam that sticks to mobs
slows mobs and does damage over time
24 bubbles per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `spray bubbly foam that sticks to mobs
slows mobs and does damage over time
${this.ammoPack.toFixed(0)} bubbles per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 24, + have: false, + charge: 0, + isDischarge: false, + knockBack: 0.0005, //set in tech: cavitation + applyKnock(velocity) { + player.force.x -= this.knockBack * velocity.x + player.force.y -= 2 * this.knockBack * velocity.y + }, + chooseFireMethod() { + if (tech.isFoamPressure) { + this.do = this.doCharges + this.fire = this.fireCharges + } else { + this.do = this.doStream + this.fire = this.fireStream + } + }, + doStream() { }, + fireStream() { + const spread = (m.crouch ? + 0.04 * (Math.random() - 0.5) + 0.09 * Math.sin(m.cycle * 0.12) : + 0.23 * (Math.random() - 0.5) + 0.15 * Math.sin(m.cycle * 0.12) + ) + const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12 + const SPEED = (m.crouch ? 1.2 : 1) * Math.max(2, 14 - radius * 0.25) + const dir = m.angle + 0.15 * (Math.random() - 0.5) + const velocity = { x: SPEED * Math.cos(dir), y: SPEED * Math.sin(dir) } + const position = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) } + b.foam(position, Vector.rotate(velocity, spread), radius) + this.applyKnock(velocity) + m.fireCDcycle = m.cycle + Math.floor(1.5 * b.fireCDscale); + }, + doCharges() { + if (this.charge > 0) { + //draw charge level + ctx.fillStyle = "rgba(0,50,50,0.3)"; + ctx.beginPath(); + const radius = 5 * Math.sqrt(this.charge) + const mag = 11 + radius + ctx.arc(m.pos.x + mag * Math.cos(m.angle), m.pos.y + mag * Math.sin(m.angle), radius, 0, 2 * Math.PI); + ctx.fill(); + + if (this.isDischarge && m.cycle % 2) { + const spread = (m.crouch ? 0.04 : 0.5) * (Math.random() - 0.5) + const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12 + const SPEED = (m.crouch ? 1.2 : 1) * 10 - radius * 0.4 + Math.min(5, Math.sqrt(this.charge)); + const dir = m.angle + 0.15 * (Math.random() - 0.5) + const velocity = { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + } + const position = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + b.foam(position, Vector.rotate(velocity, spread), radius) + this.applyKnock(velocity) + this.charge -= 0.75 + m.fireCDcycle = m.cycle + 2; //disable firing and adding more charge until empty + } else if (!input.fire) { + this.isDischarge = true; + } + } else { + if (this.isDischarge) { + m.fireCDcycle = m.cycle + Math.floor(25 * b.fireCDscale); + } + this.isDischarge = false + } + }, + fireCharges() { + const spread = (m.crouch ? + 0.04 * (Math.random() - 0.5) + 0.09 * Math.sin(m.cycle * 0.12) : + 0.23 * (Math.random() - 0.5) + 0.15 * Math.sin(m.cycle * 0.12) + ) + const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12 + const SPEED = (m.crouch ? 1.2 : 1) * Math.max(2, 14 - radius * 0.25) + const dir = m.angle + 0.15 * (Math.random() - 0.5) + const velocity = { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + } + const position = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + // if (tech.foamFutureFire) { + // simulation.drawList.push({ //add dmg to draw queue + // x: position.x, + // y: position.y, + // radius: 5, + // color: "rgba(0,50,50,0.3)", + // time: 15 * tech.foamFutureFire + // }); + // setTimeout(() => { + // if (!simulation.paused) { + // b.foam(position, Vector.rotate(velocity, spread), radius) + // bullet[bullet.length - 1].damage *= (1 + 0.7 * tech.foamFutureFire) + // } + // }, 210 * tech.foamFutureFire); // } else { - // for (let i = 0; i < this.totalSpores; i++) things[0]() // } - if (tech.isStun) b.AoEStunEffect(this.position, 600, 270 + 120 * Math.random()); //AoEStunEffect(where, range, cycles = 120 + 60 * Math.random()) { - } - } - }, { - name: "drones", //7 - description: "deploy autonomous drones that smash into mobs
and collect nearby power ups", //crashes reduce their lifespan by 1 second - ammo: 0, - ammoPack: 16, - defaultAmmoPack: 16, - have: false, - do() { }, - fire() { - if (tech.isDroneRadioactive) { - if (m.crouch) { - b.droneRadioactive({ - x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), - y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) - }, 45) - m.fireCDcycle = m.cycle + Math.floor(45 * b.fireCDscale); // cool down + b.foam(position, Vector.rotate(velocity, spread), radius) + this.applyKnock(velocity) + m.fireCDcycle = m.cycle + Math.floor(1.5 * b.fireCDscale); + this.charge += 1 + tech.isCapacitor + }, + fire() { }, + do() { }, + }, + { + name: "harpoon", //9 + // description: `throw a self-steering harpoon that uses energy
to retract and refund its ammo cost
1-2 harpoons per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `throw a self-steering harpoon that uses energy
to retract and refund its ammo cost
${this.ammoPack.toFixed(1)} harpoons per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 1.7, //update this in railgun tech + have: false, + fire() { }, + do() { }, + chooseFireMethod() { + if (tech.isRailGun) { + this.do = this.railDo + this.fire = this.railFire + } else if (tech.isGrapple) { + this.do = () => { } + this.fire = this.grappleFire } else { - b.droneRadioactive({ - x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), - y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) - }, 10) - m.fireCDcycle = m.cycle + Math.floor(25 * b.fireCDscale); // cool down + this.do = () => { } + this.fire = this.harpoonFire } - } else { - if (m.crouch) { - b.drone({ - x: m.pos.x + 30 * Math.cos(m.angle) + 5 * (Math.random() - 0.5), - y: m.pos.y + 30 * Math.sin(m.angle) + 5 * (Math.random() - 0.5) - }, 50) - m.fireCDcycle = m.cycle + Math.floor(7 * b.fireCDscale); // cool down - } else { - b.drone({ - x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), - y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) - }, 15) - m.fireCDcycle = m.cycle + Math.floor(4 * b.fireCDscale); // cool down - } - } - } - }, - { - name: "foam", //8 - description: "spray bubbly foam that sticks to mobs
slows mobs and does damage over time", - ammo: 0, - ammoPack: 24, - have: false, - charge: 0, - isDischarge: false, - knockBack: 0.0005, //set in tech: cavitation - applyKnock(velocity) { - player.force.x -= this.knockBack * velocity.x - player.force.y -= 2 * this.knockBack * velocity.y - }, - chooseFireMethod() { - if (tech.isFoamPressure) { - this.do = this.doCharges - this.fire = this.fireCharges - } else { - this.do = this.doStream - this.fire = this.fireStream - } - }, - doStream() { }, - fireStream() { - const spread = (m.crouch ? - 0.04 * (Math.random() - 0.5) + 0.09 * Math.sin(m.cycle * 0.12) : - 0.23 * (Math.random() - 0.5) + 0.15 * Math.sin(m.cycle * 0.12) - ) - const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12 - const SPEED = (m.crouch ? 1.2 : 1) * Math.max(2, 14 - radius * 0.25) - const dir = m.angle + 0.15 * (Math.random() - 0.5) - const velocity = { x: SPEED * Math.cos(dir), y: SPEED * Math.sin(dir) } - const position = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) } - b.foam(position, Vector.rotate(velocity, spread), radius) - this.applyKnock(velocity) - m.fireCDcycle = m.cycle + Math.floor(1.5 * b.fireCDscale); - }, - doCharges() { - if (this.charge > 0) { - //draw charge level - ctx.fillStyle = "rgba(0,50,50,0.3)"; - ctx.beginPath(); - const radius = 5 * Math.sqrt(this.charge) - const mag = 11 + radius - ctx.arc(m.pos.x + mag * Math.cos(m.angle), m.pos.y + mag * Math.sin(m.angle), radius, 0, 2 * Math.PI); - ctx.fill(); + }, + charge: 0, + railDo() { + if (this.charge > 0) { + const DRAIN = (tech.isRailEnergy ? 0.0002 : 0.002) + //exit railgun charging without firing + if (m.energy < DRAIN) { + // m.energy += 0.025 + this.charge * 22 * this.drain + // m.energy -= this.drain + m.fireCDcycle = m.cycle + 120; // cool down if out of energy + this.endCycle = 0; + this.charge = 0 + b.refundAmmo() + return + } + //fire + if ((!input.fire && this.charge > 0.6)) { + // tech.harpoonDensity = 0.0065 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const closest = { + distance: 10000, + target: null + } + //push away blocks and mobs + const range = 600 + 500 * this.charge + for (let i = 0, len = mob.length; i < len; ++i) { //push away mobs when firing + if (!mob[i].isUnblockable) { + const SUB = Vector.sub(mob[i].position, m.pos) + const DISTANCE = Vector.magnitude(SUB) + if (DISTANCE < range + mob[i].radius) { + const DEPTH = 100 + Math.min(range - DISTANCE + mob[i].radius, 1500) + const FORCE = Vector.mult(Vector.normalise(SUB), 0.0015 * Math.sqrt(DEPTH) * mob[i].mass) + mob[i].force.x += FORCE.x; + mob[i].force.y += FORCE.y; - if (this.isDischarge && m.cycle % 2) { - const spread = (m.crouch ? 0.04 : 0.5) * (Math.random() - 0.5) - const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12 - const SPEED = (m.crouch ? 1.2 : 1) * 10 - radius * 0.4 + Math.min(5, Math.sqrt(this.charge)); - const dir = m.angle + 0.15 * (Math.random() - 0.5) - const velocity = { - x: SPEED * Math.cos(dir), - y: SPEED * Math.sin(dir) - } - const position = { - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - } - b.foam(position, Vector.rotate(velocity, spread), radius) - this.applyKnock(velocity) - this.charge -= 0.75 - m.fireCDcycle = m.cycle + 2; //disable firing and adding more charge until empty - } else if (!input.fire) { - this.isDischarge = true; - } - } else { - if (this.isDischarge) { - m.fireCDcycle = m.cycle + Math.floor(25 * b.fireCDscale); - } - this.isDischarge = false - } - }, - fireCharges() { - const spread = (m.crouch ? - 0.04 * (Math.random() - 0.5) + 0.09 * Math.sin(m.cycle * 0.12) : - 0.23 * (Math.random() - 0.5) + 0.15 * Math.sin(m.cycle * 0.12) - ) - const radius = 5 + 8 * Math.random() + (tech.isAmmoFoamSize && this.ammo < 300) * 12 - const SPEED = (m.crouch ? 1.2 : 1) * Math.max(2, 14 - radius * 0.25) - const dir = m.angle + 0.15 * (Math.random() - 0.5) - const velocity = { - x: SPEED * Math.cos(dir), - y: SPEED * Math.sin(dir) - } - const position = { - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - } - // if (tech.foamFutureFire) { - // simulation.drawList.push({ //add dmg to draw queue - // x: position.x, - // y: position.y, - // radius: 5, - // color: "rgba(0,50,50,0.3)", - // time: 15 * tech.foamFutureFire - // }); - // setTimeout(() => { - // if (!simulation.paused) { - // b.foam(position, Vector.rotate(velocity, spread), radius) - // bullet[bullet.length - 1].damage *= (1 + 0.7 * tech.foamFutureFire) - // } - // }, 210 * tech.foamFutureFire); - // } else { - // } - b.foam(position, Vector.rotate(velocity, spread), radius) - this.applyKnock(velocity) - m.fireCDcycle = m.cycle + Math.floor(1.5 * b.fireCDscale); - this.charge += 1 + tech.isCapacitor - }, - fire() { }, - do() { }, - }, - { - name: "harpoon", //9 - description: "fire a self-steering harpoon that uses energy
to retract and refund its ammo cost", - ammo: 0, - ammoPack: 1.7, //update this in railgun tech - have: false, - fire() { }, - do() { }, - chooseFireMethod() { - if (tech.isRailGun) { - this.do = this.railDo - this.fire = this.railFire - } else if (tech.isGrapple) { - this.do = () => { } - this.fire = this.grappleFire - } else { - this.do = () => { } - this.fire = this.harpoonFire - } - }, - charge: 0, - railDo() { - if (this.charge > 0) { - const DRAIN = (tech.isRailEnergy ? 0.0002 : 0.002) - //exit railgun charging without firing - if (m.energy < DRAIN) { - // m.energy += 0.025 + this.charge * 22 * this.drain - // m.energy -= this.drain - m.fireCDcycle = m.cycle + 120; // cool down if out of energy - this.endCycle = 0; - this.charge = 0 - b.refundAmmo() - return - } - //fire - if ((!input.fire && this.charge > 0.6)) { - // tech.harpoonDensity = 0.0065 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed - const where = { - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - } - const closest = { - distance: 10000, - target: null - } - //push away blocks and mobs - const range = 600 + 500 * this.charge - for (let i = 0, len = mob.length; i < len; ++i) { //push away mobs when firing - if (!mob[i].isUnblockable) { - const SUB = Vector.sub(mob[i].position, m.pos) - const DISTANCE = Vector.magnitude(SUB) - if (DISTANCE < range + mob[i].radius) { - const DEPTH = 100 + Math.min(range - DISTANCE + mob[i].radius, 1500) - const FORCE = Vector.mult(Vector.normalise(SUB), 0.0015 * Math.sqrt(DEPTH) * mob[i].mass) - mob[i].force.x += FORCE.x; - mob[i].force.y += FORCE.y; - - let dmg = m.dmgScale * (mob[i].isDropPowerUp ? 350 : 1100) * tech.harpoonDensity * this.charge - simulation.drawList.push({ //add dmg to draw queue - x: mob[i].position.x, - y: mob[i].position.y, - radius: Math.log(dmg + 1.1) * 40 * mob[i].damageReduction + 3, - color: 'rgba(100, 0, 200, 0.4)', - time: 15 - }); - mob[i].damage(dmg); + let dmg = m.dmgScale * (mob[i].isDropPowerUp ? 350 : 1100) * tech.harpoonDensity * this.charge + simulation.drawList.push({ //add dmg to draw queue + x: mob[i].position.x, + y: mob[i].position.y, + radius: Math.log(dmg + 1.1) * 40 * mob[i].damageReduction + 3, + color: 'rgba(100, 0, 200, 0.4)', + time: 15 + }); + mob[i].damage(dmg); + } } } - } - for (let i = 0, len = body.length; i < len; ++i) { //push away blocks when firing - const SUB = Vector.sub(body[i].position, m.pos) - const DISTANCE = Vector.magnitude(SUB) - if (DISTANCE < range) { - const DEPTH = Math.min(range - DISTANCE, 500) - const FORCE = Vector.mult(Vector.normalise(SUB), 0.003 * Math.sqrt(DEPTH) * body[i].mass) - body[i].force.x += FORCE.x; - body[i].force.y += FORCE.y - body[i].mass * simulation.g * 1.5; //kick up a bit to give them some arc + for (let i = 0, len = body.length; i < len; ++i) { //push away blocks when firing + const SUB = Vector.sub(body[i].position, m.pos) + const DISTANCE = Vector.magnitude(SUB) + if (DISTANCE < range) { + const DEPTH = Math.min(range - DISTANCE, 500) + const FORCE = Vector.mult(Vector.normalise(SUB), 0.003 * Math.sqrt(DEPTH) * body[i].mass) + body[i].force.x += FORCE.x; + body[i].force.y += FORCE.y - body[i].mass * simulation.g * 1.5; //kick up a bit to give them some arc + } + } + for (let i = 0, len = powerUp.length; i < len; ++i) { //push away blocks when firing + const SUB = Vector.sub(powerUp[i].position, m.pos) + const DISTANCE = Vector.magnitude(SUB) + if (DISTANCE < range) { + const DEPTH = Math.min(range - DISTANCE, 500) + const FORCE = Vector.mult(Vector.normalise(SUB), 0.002 * Math.sqrt(DEPTH) * powerUp[i].mass) + powerUp[i].force.x += FORCE.x; + powerUp[i].force.y += FORCE.y - powerUp[i].mass * simulation.g * 1.5; //kick up a bit to give them some arc + } + } + //draw little dots near the edge of range + for (let i = 0, len = 10 + 25 * this.charge; i < len; i++) { + const unit = Vector.rotate({ + x: 1, + y: 0 + }, 6.28 * Math.random()) + const where = Vector.add(m.pos, Vector.mult(unit, range * (0.6 + 0.3 * Math.random()))) + simulation.drawList.push({ + x: where.x, + y: where.y, + radius: 5 + 12 * Math.random(), + color: "rgba(100, 0, 200, 0.1)", + time: Math.floor(5 + 35 * Math.random()) + }); + } + + const recoil = Vector.mult(Vector.normalise(Vector.sub(where, m.pos)), m.crouch ? 0.03 : 0.06) + player.force.x -= recoil.x + player.force.y -= recoil.y + // tech.harpoonDensity = 0.0065 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + + const harpoonSize = tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1 + const thrust = 0.15 * (this.charge) + if (tech.extraHarpoons) { + let targetCount = 0 + const SPREAD = 0.06 + 0.05 * (!m.crouch) + let angle = m.angle - SPREAD * tech.extraHarpoons / 2; + const dir = { + x: Math.cos(angle), + y: Math.sin(angle) + }; //make a vector for the player's direction of length 1; used in dot product + + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && !mob[i].shield && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + if (dot > 0.95 - Math.min(dist * 0.00015, 0.3)) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target + // if (this.ammo > -1) { + // this.ammo-- + b.harpoon(where, m.crouch ? null : mob[i], angle, harpoonSize, false, 35, false, thrust) //harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { + angle += SPREAD + targetCount++ + if (targetCount > tech.extraHarpoons) break + // } + } + } + } + //if more harpoons and no targets left + if (targetCount < tech.extraHarpoons + 1) { + const num = tech.extraHarpoons + 1 - targetCount + for (let i = 0; i < num; i++) { + b.harpoon(where, null, angle, harpoonSize, false, 35, false, thrust) + angle += SPREAD + } + } + simulation.updateGunHUD(); + } else { + //look for closest mob in player's LoS + const dir = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + }; //make a vector for the player's direction of length 1; used in dot product + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target + closest.distance = dist + closest.target = mob[i] + } + } + } + b.harpoon(where, closest.target, m.angle, harpoonSize, false, 35, false, thrust) + } + + this.charge = 0; + } else { //charging + if (tech.isFireMoveLock) { + Matter.Body.setVelocity(player, { + x: 0, + y: -55 * player.mass * simulation.g //undo gravity before it is added + }); + player.force.x = 0 + player.force.y = 0 + } + m.fireCDcycle = m.cycle + 10 //can't fire until mouse is released + // const previousCharge = this.charge + + //small b.fireCDscale = faster shots, b.fireCDscale=1 = normal shot, big b.fireCDscale = slower chot + // let smoothRate = tech.isCapacitor ? 0.85 : Math.min(0.998, 0.985 * (0.98 + 0.02 * b.fireCDscale)) + const rate = Math.sqrt(b.fireCDscale) * tech.railChargeRate * (tech.isCapacitor ? 0.6 : 1) * (m.crouch ? 0.8 : 1) + let smoothRate = Math.min(0.998, 0.94 + 0.05 * rate) + + + this.charge = 1 - smoothRate + this.charge * smoothRate + if (m.energy > DRAIN) m.energy -= DRAIN + + //draw magnetic field + const X = m.pos.x + const Y = m.pos.y + const unitVector = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + } + const unitVectorPerp = 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)`; + const magSize = 8 * this.charge * tech.railChargeRate ** 3 + const arcSize = 6 * this.charge * tech.railChargeRate ** 3 + for (let i = 3; i < 7; i++) { + const MAG = magSize * i * i * (0.93 + 0.07 * Math.random()) + const ARC = arcSize * i * i * (0.93 + 0.07 * Math.random()) + ctx.beginPath(); + magField(MAG, ARC) + magField(MAG, -ARC) + ctx.fill(); } } - for (let i = 0, len = powerUp.length; i < len; ++i) { //push away blocks when firing - const SUB = Vector.sub(powerUp[i].position, m.pos) - const DISTANCE = Vector.magnitude(SUB) - if (DISTANCE < range) { - const DEPTH = Math.min(range - DISTANCE, 500) - const FORCE = Vector.mult(Vector.normalise(SUB), 0.002 * Math.sqrt(DEPTH) * powerUp[i].mass) - powerUp[i].force.x += FORCE.x; - powerUp[i].force.y += FORCE.y - powerUp[i].mass * simulation.g * 1.5; //kick up a bit to give them some arc + } + }, + railFire() { + m.fireCDcycle = m.cycle + 10 //can't fire until mouse is released + this.charge += 0.00001 + }, + grappleFire() { + const harpoonSize = (tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1) //* (m.crouch ? 0.7 : 1) + const where = { + x: m.pos.x + harpoonSize * 40 * Math.cos(m.angle), + y: m.pos.y + harpoonSize * 40 * Math.sin(m.angle) + } + const num = Math.min(this.ammo, tech.extraHarpoons + 1) + if (!m.crouch && num > 1) { //multiple harpoons + const SPREAD = 0.06 + let angle = m.angle - SPREAD * num / 2; + for (let i = 0; i < num; i++) { + if (this.ammo > 0) { + this.ammo-- + b.grapple(where, angle, true, harpoonSize) + angle += SPREAD } } - //draw little dots near the edge of range - for (let i = 0, len = 10 + 25 * this.charge; i < len; i++) { - const unit = Vector.rotate({ - x: 1, - y: 0 - }, 6.28 * Math.random()) - const where = Vector.add(m.pos, Vector.mult(unit, range * (0.6 + 0.3 * Math.random()))) - simulation.drawList.push({ - x: where.x, - y: where.y, - radius: 5 + 12 * Math.random(), - color: "rgba(100, 0, 200, 0.1)", - time: Math.floor(5 + 35 * Math.random()) - }); - } + this.ammo++ //make up for the ammo used up in fire() + simulation.updateGunHUD(); + m.fireCDcycle = m.cycle + Math.floor(75 * b.fireCDscale) // cool down + // } else if (m.crouch) { + // b.harpoon(where, null, m.angle, harpoonSize, false, 70) + } else { + if (tech.crouchAmmoCount) tech.crouchAmmoCount = 1 + b.grapple(where, m.angle, harpoonSize) + } + // m.fireCDcycle = m.cycle + Math.floor(75 * b.fireCDscale) // cool down + m.fireCDcycle = m.cycle + 5 + 40 * b.fireCDscale + 60 * (m.energy < 0.05) - const recoil = Vector.mult(Vector.normalise(Vector.sub(where, m.pos)), m.crouch ? 0.03 : 0.06) - player.force.x -= recoil.x - player.force.y -= recoil.y - // tech.harpoonDensity = 0.0065 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed + }, + harpoonFire() { + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const closest = { + distance: 10000, + target: null + } + //look for closest mob in player's LoS + const harpoonSize = (tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1) //* (m.crouch ? 0.7 : 1) + const totalCycles = 6.5 * (tech.isFilament ? 1 + 0.013 * Math.min(110, this.ammo) : 1) * Math.sqrt(harpoonSize) - const harpoonSize = tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1 - const thrust = 0.15 * (this.charge) - if (tech.extraHarpoons) { - let targetCount = 0 - const SPREAD = 0.06 + 0.05 * (!m.crouch) - let angle = m.angle - SPREAD * tech.extraHarpoons / 2; - const dir = { - x: Math.cos(angle), - y: Math.sin(angle) - }; //make a vector for the player's direction of length 1; used in dot product - - for (let i = 0, len = mob.length; i < len; ++i) { - if (mob[i].alive && !mob[i].isBadTarget && !mob[i].shield && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { - const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors - const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) - if (dot > 0.95 - Math.min(dist * 0.00015, 0.3)) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target - // if (this.ammo > -1) { - // this.ammo-- - b.harpoon(where, m.crouch ? null : mob[i], angle, harpoonSize, false, 35, false, thrust) //harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { + if (tech.extraHarpoons && !m.crouch) { //multiple harpoons + const SPREAD = 0.2 + let angle = m.angle - SPREAD * tech.extraHarpoons / 2; + const dir = { + x: Math.cos(angle), + y: Math.sin(angle) + }; //make a vector for the player's direction of length 1; used in dot product + const range = 450 * (tech.isFilament ? 1 + 0.012 * Math.min(110, this.ammo) : 1) + let targetCount = 0 + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && !mob[i].shield && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + if (dist < range && dot > 0.9) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target + if (this.ammo > 0) { + this.ammo-- + b.harpoon(where, mob[i], angle, harpoonSize, true, totalCycles) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 }) angle += SPREAD targetCount++ if (targetCount > tech.extraHarpoons) break - // } } } } - //if more harpoons and no targets left - if (targetCount < tech.extraHarpoons + 1) { - const num = tech.extraHarpoons + 1 - targetCount - for (let i = 0; i < num; i++) { - b.harpoon(where, null, angle, harpoonSize, false, 35, false, thrust) - angle += SPREAD - } - } - simulation.updateGunHUD(); - } else { - //look for closest mob in player's LoS - const dir = { - x: Math.cos(m.angle), - y: Math.sin(m.angle) - }; //make a vector for the player's direction of length 1; used in dot product - for (let i = 0, len = mob.length; i < len; ++i) { - if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { - const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors - const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) - if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target - closest.distance = dist - closest.target = mob[i] + } + //if more harpoons and no targets left + if (targetCount < tech.extraHarpoons + 1) { + const num = tech.extraHarpoons - targetCount + const delay = 1 //Math.floor(Math.max(4, 8 - 0.5 * tech.extraHarpoons)) + let angle = m.angle - SPREAD * tech.extraHarpoons / 2; + let count = -1 + let harpoonDelay = () => { + if (simulation.paused) { + requestAnimationFrame(harpoonDelay) + } else { + count++ + if (!(count % delay) && this.ammo > 0) { + this.ammo-- + b.harpoon({ + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }, null, angle, harpoonSize, true, totalCycles) + angle += SPREAD + tech.harpoonDensity = 0.004 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed } + if (count < num * delay && m.alive) requestAnimationFrame(harpoonDelay); } } - b.harpoon(where, closest.target, m.angle, harpoonSize, false, 35, false, thrust) + requestAnimationFrame(harpoonDelay) } + this.ammo++ //make up for the ammo used up in fire() + simulation.updateGunHUD(); - this.charge = 0; - } else { //charging - if (tech.isFireMoveLock) { - Matter.Body.setVelocity(player, { - x: 0, - y: -55 * player.mass * simulation.g //undo gravity before it is added - }); - player.force.x = 0 - player.force.y = 0 - } - m.fireCDcycle = m.cycle + 10 //can't fire until mouse is released - // const previousCharge = this.charge - - //small b.fireCDscale = faster shots, b.fireCDscale=1 = normal shot, big b.fireCDscale = slower chot - // let smoothRate = tech.isCapacitor ? 0.85 : Math.min(0.998, 0.985 * (0.98 + 0.02 * b.fireCDscale)) - const rate = Math.sqrt(b.fireCDscale) * tech.railChargeRate * (tech.isCapacitor ? 0.6 : 1) * (m.crouch ? 0.8 : 1) - let smoothRate = Math.min(0.998, 0.94 + 0.05 * rate) - - - this.charge = 1 - smoothRate + this.charge * smoothRate - if (m.energy > DRAIN) m.energy -= DRAIN - - //draw magnetic field - const X = m.pos.x - const Y = m.pos.y - const unitVector = { + } else { //m.crouch makes a single harpoon with longer range + const dir = { x: Math.cos(m.angle), y: Math.sin(m.angle) - } - const unitVectorPerp = 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)`; - const magSize = 8 * this.charge * tech.railChargeRate ** 3 - const arcSize = 6 * this.charge * tech.railChargeRate ** 3 - for (let i = 3; i < 7; i++) { - const MAG = magSize * i * i * (0.93 + 0.07 * Math.random()) - const ARC = arcSize * i * i * (0.93 + 0.07 * Math.random()) - ctx.beginPath(); - magField(MAG, ARC) - magField(MAG, -ARC) - ctx.fill(); - } - } - } - }, - railFire() { - m.fireCDcycle = m.cycle + 10 //can't fire until mouse is released - this.charge += 0.00001 - }, - grappleFire() { - const harpoonSize = (tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1) //* (m.crouch ? 0.7 : 1) - const where = { - x: m.pos.x + harpoonSize * 40 * Math.cos(m.angle), - y: m.pos.y + harpoonSize * 40 * Math.sin(m.angle) - } - const num = Math.min(this.ammo, tech.extraHarpoons + 1) - if (!m.crouch && num > 1) { //multiple harpoons - const SPREAD = 0.06 - let angle = m.angle - SPREAD * num / 2; - for (let i = 0; i < num; i++) { - if (this.ammo > 0) { - this.ammo-- - b.grapple(where, angle, true, harpoonSize) - angle += SPREAD - } - } - this.ammo++ //make up for the ammo used up in fire() - simulation.updateGunHUD(); - m.fireCDcycle = m.cycle + Math.floor(75 * b.fireCDscale) // cool down - // } else if (m.crouch) { - // b.harpoon(where, null, m.angle, harpoonSize, false, 70) - } else { - if (tech.crouchAmmoCount) tech.crouchAmmoCount = 1 - b.grapple(where, m.angle, harpoonSize) - } - // m.fireCDcycle = m.cycle + Math.floor(75 * b.fireCDscale) // cool down - m.fireCDcycle = m.cycle + 5 + 40 * b.fireCDscale + 60 * (m.energy < 0.05) - - }, - harpoonFire() { - const where = { - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - } - const closest = { - distance: 10000, - target: null - } - //look for closest mob in player's LoS - const harpoonSize = (tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1) //* (m.crouch ? 0.7 : 1) - const totalCycles = 6.5 * (tech.isFilament ? 1 + 0.013 * Math.min(110, this.ammo) : 1) * Math.sqrt(harpoonSize) - - if (tech.extraHarpoons && !m.crouch) { //multiple harpoons - const SPREAD = 0.2 - let angle = m.angle - SPREAD * tech.extraHarpoons / 2; - const dir = { - x: Math.cos(angle), - y: Math.sin(angle) - }; //make a vector for the player's direction of length 1; used in dot product - const range = 450 * (tech.isFilament ? 1 + 0.012 * Math.min(110, this.ammo) : 1) - let targetCount = 0 - for (let i = 0, len = mob.length; i < len; ++i) { - if (mob[i].alive && !mob[i].isBadTarget && !mob[i].shield && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { - const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors - const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) - if (dist < range && dot > 0.9) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target - if (this.ammo > 0) { - this.ammo-- - b.harpoon(where, mob[i], angle, harpoonSize, true, totalCycles) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 }) - angle += SPREAD - targetCount++ - if (targetCount > tech.extraHarpoons) break + }; //make a vector for the player's direction of length 1; used in dot product + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target + closest.distance = dist + closest.target = mob[i] } } } - } - //if more harpoons and no targets left - if (targetCount < tech.extraHarpoons + 1) { - const num = tech.extraHarpoons - targetCount - const delay = 1 //Math.floor(Math.max(4, 8 - 0.5 * tech.extraHarpoons)) - let angle = m.angle - SPREAD * tech.extraHarpoons / 2; - let count = -1 - let harpoonDelay = () => { - if (simulation.paused) { - requestAnimationFrame(harpoonDelay) - } else { - count++ - if (!(count % delay) && this.ammo > 0) { - this.ammo-- - b.harpoon({ - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - }, null, angle, harpoonSize, true, totalCycles) - angle += SPREAD - tech.harpoonDensity = 0.004 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed - } - if (count < num * delay && m.alive) requestAnimationFrame(harpoonDelay); - } + if (m.crouch && m.onGround) { + b.harpoon(where, null, m.angle, harpoonSize, true, 1.6 * totalCycles, (m.crouch && tech.crouchAmmoCount && (tech.crouchAmmoCount - 1) % 2) ? false : true) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true) { + } else { + b.harpoon(where, closest.target, m.angle, harpoonSize, true, totalCycles) } - requestAnimationFrame(harpoonDelay) + tech.harpoonDensity = 0.004 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed } - this.ammo++ //make up for the ammo used up in fire() - simulation.updateGunHUD(); - - } else { //m.crouch makes a single harpoon with longer range - const dir = { - x: Math.cos(m.angle), - y: Math.sin(m.angle) - }; //make a vector for the player's direction of length 1; used in dot product - for (let i = 0, len = mob.length; i < len; ++i) { - if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { - const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors - const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) - if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target - closest.distance = dist - closest.target = mob[i] - } - } - } - if (m.crouch && m.onGround) { - b.harpoon(where, null, m.angle, harpoonSize, true, 1.6 * totalCycles, (m.crouch && tech.crouchAmmoCount && (tech.crouchAmmoCount - 1) % 2) ? false : true) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true) { + m.fireCDcycle = m.cycle + 5 + 35 * b.fireCDscale + 60 * (m.energy < 0.05) + tech.extraHarpoons // cool down is set when harpoon bullet returns to player + const recoil = Vector.mult(Vector.normalise(Vector.sub(where, m.pos)), m.crouch ? 0.015 : 0.035) + player.force.x -= recoil.x + player.force.y -= recoil.y + }, + }, { + name: "mine", //10 + // description: `toss a proximity mine that sticks to walls
refund undetonated mines on exiting a level
1-2 mines per ${powerUps.orb.ammo()}`, + descriptionFunction() { + return `toss a proximity mine that sticks to walls
refund undetonated mines on exiting a level
${this.ammoPack.toFixed(1)} mines per ${powerUps.orb.ammo()}` + }, + ammo: 0, + ammoPack: 1.7, + have: false, + nameString(suffix = "") { + if (tech.isFoamMine) { + return `foam` + } else if (tech.isSuperMine) { + return `super ball${suffix}` } else { - b.harpoon(where, closest.target, m.angle, harpoonSize, true, totalCycles) + return `nail${suffix}` } - tech.harpoonDensity = 0.004 //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed - } - m.fireCDcycle = m.cycle + 5 + 35 * b.fireCDscale + 60 * (m.energy < 0.05) + tech.extraHarpoons // cool down is set when harpoon bullet returns to player - const recoil = Vector.mult(Vector.normalise(Vector.sub(where, m.pos)), m.crouch ? 0.015 : 0.035) - player.force.x -= recoil.x - player.force.y -= recoil.y - }, - }, { - name: "mine", //10 - description: "toss a proximity mine that sticks to walls
refund undetonated mines on exiting a level", //fires nails at mobs within range - ammo: 0, - ammoPack: 1.7, - have: false, - nameString(suffix = "") { - if (tech.isFoamMine) { - return `foam` - } else if (tech.isSuperMine) { - return `super ball${suffix}` - } else { - return `nail${suffix}` - } - }, - do() { - if (!input.field && m.crouch && !tech.isLaserMine) { - const cycles = 60 //30 - const speed = 40 - const v = { - x: speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - } //m.Vy / 2 + removed to make the path less jerky - ctx.strokeStyle = "rgba(68, 68, 68, 0.2)" //color.map - ctx.lineWidth = 2 - ctx.beginPath() - for (let i = 1.5, len = 19; i < len + 1; i++) { - const time = cycles * i / len - ctx.lineTo(m.pos.x + time * v.x, m.pos.y + time * v.y + 0.34 * time * time) - } - ctx.stroke() - } - }, - fire() { - if (m.crouch) { - if (tech.isLaserMine) { - const speed = 30 - const velocity = { + }, + do() { + if (!input.field && m.crouch && !tech.isLaserMine) { + const cycles = 60 //30 + const speed = 40 + const v = { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) + } //m.Vy / 2 + removed to make the path less jerky + ctx.strokeStyle = "rgba(68, 68, 68, 0.2)" //color.map + ctx.lineWidth = 2 + ctx.beginPath() + for (let i = 1.5, len = 19; i < len + 1; i++) { + const time = cycles * i / len + ctx.lineTo(m.pos.x + time * v.x, m.pos.y + time * v.y + 0.34 * time * time) + } + ctx.stroke() + } + }, + fire() { + if (m.crouch) { + if (tech.isLaserMine) { + const speed = 30 + const velocity = { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + } + b.laserMine(m.pos, velocity) + m.fireCDcycle = m.cycle + Math.floor(65 * b.fireCDscale); // cool down + } else { + const pos = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + let speed = 36 + if (Matter.Query.point(map, pos).length > 0) speed = -2 //don't launch if mine will spawn inside map + b.mine(pos, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }, 0) + m.fireCDcycle = m.cycle + Math.floor(55 * b.fireCDscale); // cool down } - b.laserMine(m.pos, velocity) - m.fireCDcycle = m.cycle + Math.floor(65 * b.fireCDscale); // cool down } else { const pos = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) } - let speed = 36 + let speed = 23 if (Matter.Query.point(map, pos).length > 0) speed = -2 //don't launch if mine will spawn inside map b.mine(pos, { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) }, 0) - m.fireCDcycle = m.cycle + Math.floor(55 * b.fireCDscale); // cool down - } - } else { - const pos = { - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - } - let speed = 23 - if (Matter.Query.point(map, pos).length > 0) speed = -2 //don't launch if mine will spawn inside map - b.mine(pos, { - x: speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - }, 0) - m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down - } - } - }, - { - name: "laser", //11 - description: "emit a beam of collimated coherent light
drains energy instead of ammunition", - ammo: 0, - ammoPack: Infinity, - have: false, - charge: 0, - isStuckOn: false, - angle: 0, - isInsideArc(angle) { - const mod = (a, n) => { - return a - Math.floor(a / n) * n - } - let diff = mod(angle - this.angle + Math.PI, 2 * Math.PI) - Math.PI - return Math.abs(diff) < this.arcRange - }, - arcRange: 0.78, //1.57, - lensDamage: 1, - lensDamageOn: 0, //set in tech - lens() { - this.stuckOn(); - this.angle += 0.02 - if (this.isInsideArc(m.angle)) { - this.lensDamage = this.lensDamageOn - ctx.lineWidth = 6 + this.lensDamageOn - } else { - this.lensDamage = 1 - ctx.lineWidth = 2 - } - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, 60, this.angle - this.arcRange, this.angle + this.arcRange); - ctx.strokeStyle = '#fff' //'rgba(255,255,255,0.9)' //'hsl(189, 100%, 95%)' - ctx.stroke(); - // const a = { x: radius * Math.cos(this.angle + this.arcRange), y: radius * Math.sin(this.angle + this.arcRange) } - // const b = Vector.add(m.pos, a) - // ctx.lineTo(b.x, b.y) - // ctx.fillStyle = '#fff' - // ctx.fill() - }, - stuckOn() { - if (tech.isStuckOn) { - if (this.isStuckOn) { - if (!input.fire) this.fire(); - if (m.energy < tech.laserDrain) this.isStuckOn = false - } else if (input.fire) { - this.isStuckOn = true + m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down } } }, - do() { }, - fire() { }, - chooseFireMethod() { - this.lensDamage = 1 - if (tech.isLaserLens) { - this.do = this.lens - } else { - this.do = this.stuckOn - } - if (tech.isPulseLaser) { - this.fire = () => { - const drain = Math.min(0.9 * m.maxEnergy, 0.01 * (tech.isCapacitor ? 10 : 1) / b.fireCDscale) - if (m.energy > drain && this.charge < 50 * m.maxEnergy) { - m.energy -= drain - this.charge += drain * 100 - } + { + name: "laser", //11 + // description: `emit a beam of collimated coherent light
drains energy instead of ammo
drains ${(0.001 + tech.laserDrain) * 100}% energy per second`, + descriptionFunction() { + return `emit a beam of collimated coherent light
drains ${((0.001 + tech.laserDrain) * 600).toFixed(2)} energy per second
doesn't use ammo` + }, + ammo: 0, + ammoPack: Infinity, + have: false, + charge: 0, + isStuckOn: false, + angle: 0, + isInsideArc(angle) { + const mod = (a, n) => { + return a - Math.floor(a / n) * n } - if (tech.historyLaser) { - const len = 1 + tech.historyLaser - const spacing = Math.ceil(30 - 2 * tech.historyLaser) - this.do = () => { - if (tech.isLaserLens) this.lens() - if (this.charge > 0) { - //draw charge level - const mag = 4.1 * Math.sqrt(this.charge) - ctx.beginPath(); - for (let i = 0; i < len; i++) { - const history = m.history[(m.cycle - i * spacing) % 600] - const off = history.yOff - 24.2859 - ctx.moveTo(history.position.x, history.position.y - off); - ctx.ellipse(history.position.x, history.position.y - off, mag, mag * 0.65, history.angle, 0, 2 * Math.PI) - } - ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`; - ctx.fill(); - //fire - if (!input.fire) { - if (this.charge > 5) { - m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down - for (let i = 0; i < len; i++) { - const history = m.history[(m.cycle - i * spacing) % 600] - const off = history.yOff - 24.2859 - b.pulse(1.65 * this.charge * this.lensDamage, history.angle, { - x: history.position.x, - y: history.position.y - off - }) - } - } - this.charge = 0; - } - } - }; + let diff = mod(angle - this.angle + Math.PI, 2 * Math.PI) - Math.PI + return Math.abs(diff) < this.arcRange + }, + arcRange: 0.78, //1.57, + lensDamage: 1, + lensDamageOn: 0, //set in tech + lens() { + this.stuckOn(); + this.angle += 0.02 + if (this.isInsideArc(m.angle)) { + this.lensDamage = this.lensDamageOn + ctx.lineWidth = 6 + this.lensDamageOn } else { - this.do = () => { - if (tech.isLaserLens) this.lens() - if (this.charge > 0) { - //draw charge level - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, 4.2 * Math.sqrt(this.charge), 0, 2 * Math.PI); - // ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`; - ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`; - ctx.fill(); - //fire - if (!input.fire) { - if (this.charge > 5) { - m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down - if (tech.beamSplitter) { - const divergence = m.crouch ? 0.15 : 0.35 - const angle = m.angle - tech.beamSplitter * divergence / 2 - for (let i = 0; i < 1 + tech.beamSplitter; i++) b.pulse(this.charge, angle + i * divergence) - } else { - b.pulse(1.8 * this.charge * this.lensDamage, m.angle) - } - } - this.charge = 0; - } + this.lensDamage = 1 + ctx.lineWidth = 2 + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 60, this.angle - this.arcRange, this.angle + this.arcRange); + ctx.strokeStyle = '#fff' //'rgba(255,255,255,0.9)' //'hsl(189, 100%, 95%)' + ctx.stroke(); + // const a = { x: radius * Math.cos(this.angle + this.arcRange), y: radius * Math.sin(this.angle + this.arcRange) } + // const b = Vector.add(m.pos, a) + // ctx.lineTo(b.x, b.y) + // ctx.fillStyle = '#fff' + // ctx.fill() + }, + stuckOn() { + if (tech.isStuckOn) { + if (this.isStuckOn) { + if (!input.fire) this.fire(); + if (m.energy < tech.laserDrain) this.isStuckOn = false + } else if (input.fire) { + this.isStuckOn = true + } + } + }, + do() { }, + fire() { }, + chooseFireMethod() { + this.lensDamage = 1 + if (tech.isLaserLens) { + this.do = this.lens + } else { + this.do = this.stuckOn + } + if (tech.isPulseLaser) { + this.fire = () => { + const drain = Math.min(0.9 * m.maxEnergy, 0.01 * (tech.isCapacitor ? 10 : 1) / b.fireCDscale) + if (m.energy > drain && this.charge < 50 * m.maxEnergy) { + m.energy -= drain + this.charge += drain * 100 } - }; - } + } + if (tech.historyLaser) { + const len = 1 + tech.historyLaser + const spacing = Math.ceil(30 - 2 * tech.historyLaser) + this.do = () => { + if (tech.isLaserLens) this.lens() + if (this.charge > 0) { + //draw charge level + const mag = 4.1 * Math.sqrt(this.charge) + ctx.beginPath(); + for (let i = 0; i < len; i++) { + const history = m.history[(m.cycle - i * spacing) % 600] + const off = history.yOff - 24.2859 + ctx.moveTo(history.position.x, history.position.y - off); + ctx.ellipse(history.position.x, history.position.y - off, mag, mag * 0.65, history.angle, 0, 2 * Math.PI) + } + ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`; + ctx.fill(); + //fire + if (!input.fire) { + if (this.charge > 5) { + m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down + for (let i = 0; i < len; i++) { + const history = m.history[(m.cycle - i * spacing) % 600] + const off = history.yOff - 24.2859 + b.pulse(1.65 * this.charge * this.lensDamage, history.angle, { + x: history.position.x, + y: history.position.y - off + }) + } + } + this.charge = 0; + } + } + }; + } else { + this.do = () => { + if (tech.isLaserLens) this.lens() + if (this.charge > 0) { + //draw charge level + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 4.2 * Math.sqrt(this.charge), 0, 2 * Math.PI); + // ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`; + ctx.fillStyle = `rgba(255,0,0,${0.09 * Math.sqrt(this.charge)})`; + ctx.fill(); + //fire + if (!input.fire) { + if (this.charge > 5) { + m.fireCDcycle = m.cycle + Math.floor(35 * b.fireCDscale); // cool down + if (tech.beamSplitter) { + const divergence = m.crouch ? 0.15 : 0.35 + const angle = m.angle - tech.beamSplitter * divergence / 2 + for (let i = 0; i < 1 + tech.beamSplitter; i++) b.pulse(this.charge, angle + i * divergence) + } else { + b.pulse(1.8 * this.charge * this.lensDamage, m.angle) + } + } + this.charge = 0; + } + } + }; + } - } else if (tech.beamSplitter) { - this.fire = this.fireSplit - } else if (tech.historyLaser) { - this.fire = this.fireHistory - } else if (tech.isWideLaser) { - this.fire = this.fireWideBeam - } else { - this.fire = this.fireLaser - } - // this.fire = this.firePhoton - }, - fireLaser() { - const drain = 0.001 + tech.laserDrain / b.fireCDscale - if (m.energy < drain) { - m.fireCDcycle = m.cycle + 100; // cool down if out of energy - } else { - m.fireCDcycle = m.cycle - m.energy -= drain - const where = { - x: m.pos.x + 20 * Math.cos(m.angle), - y: m.pos.y + 20 * Math.sin(m.angle) + } else if (tech.beamSplitter) { + this.fire = this.fireSplit + } else if (tech.historyLaser) { + this.fire = this.fireHistory + } else if (tech.isWideLaser) { + this.fire = this.fireWideBeam + } else { + this.fire = this.fireLaser } - b.laser(where, { - x: where.x + 3000 * Math.cos(m.angle), - y: where.y + 3000 * Math.sin(m.angle) - }, tech.laserDamage / b.fireCDscale * this.lensDamage); - } - }, - firePulse() { }, - fireSplit() { - const drain = 0.001 + tech.laserDrain / b.fireCDscale - if (m.energy < drain) { - m.fireCDcycle = m.cycle + 100; // cool down if out of energy - } else { - m.fireCDcycle = m.cycle - m.energy -= drain - // const divergence = m.crouch ? 0.15 : 0.2 - // const scale = Math.pow(0.9, tech.beamSplitter) - // const pushScale = scale * scale - let dmg = tech.laserDamage / b.fireCDscale * this.lensDamage // * scale //Math.pow(0.9, tech.laserDamage) - const where = { - x: m.pos.x + 20 * Math.cos(m.angle), - y: m.pos.y + 20 * Math.sin(m.angle) - } - const divergence = m.crouch ? 0.15 : 0.35 - const angle = m.angle - tech.beamSplitter * divergence / 2 - for (let i = 0; i < 1 + tech.beamSplitter; i++) { + // this.fire = this.firePhoton + }, + fireLaser() { + const drain = 0.001 + tech.laserDrain / b.fireCDscale + if (m.energy < drain) { + m.fireCDcycle = m.cycle + 100; // cool down if out of energy + } else { + m.fireCDcycle = m.cycle + m.energy -= drain + const where = { + x: m.pos.x + 20 * Math.cos(m.angle), + y: m.pos.y + 20 * Math.sin(m.angle) + } b.laser(where, { - x: where.x + 3000 * Math.cos(angle + i * divergence), - y: where.y + 3000 * Math.sin(angle + i * divergence) - }, dmg, tech.laserReflections, false) + x: where.x + 3000 * Math.cos(m.angle), + y: where.y + 3000 * Math.sin(m.angle) + }, tech.laserDamage / b.fireCDscale * this.lensDamage); } - } - }, - fireWideBeam() { - const drain = 0.001 + tech.laserDrain / b.fireCDscale - if (m.energy < drain) { - m.fireCDcycle = m.cycle + 100; // cool down if out of energy - } else { - m.fireCDcycle = m.cycle - m.energy -= drain - const range = { - x: 5000 * Math.cos(m.angle), - y: 5000 * Math.sin(m.angle) - } - const rangeOffPlus = { - x: 7.5 * Math.cos(m.angle + Math.PI / 2), - y: 7.5 * Math.sin(m.angle + Math.PI / 2) - } - const rangeOffMinus = { - x: 7.5 * Math.cos(m.angle - Math.PI / 2), - y: 7.5 * Math.sin(m.angle - Math.PI / 2) - } - const dmg = 0.70 * tech.laserDamage / b.fireCDscale * this.lensDamage // 3.5 * 0.55 = 200% more damage - const where = { - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - } - const eye = { - x: m.pos.x + 15 * Math.cos(m.angle), - y: m.pos.y + 15 * Math.sin(m.angle) - } - ctx.strokeStyle = tech.laserColor; - ctx.lineWidth = 8 - ctx.globalAlpha = 0.5; - ctx.beginPath(); - if (Matter.Query.ray(map, eye, where).length === 0 && Matter.Query.ray(body, eye, where).length === 0) { - b.laser(eye, { - x: eye.x + range.x, - y: eye.y + range.y - }, dmg, 0, true, 0.3) - } - for (let i = 1; i < tech.wideLaser; i++) { - let whereOff = Vector.add(where, { - x: i * rangeOffPlus.x, - y: i * rangeOffPlus.y - }) - if (Matter.Query.ray(map, eye, whereOff).length === 0 && Matter.Query.ray(body, eye, whereOff).length === 0) { - ctx.moveTo(eye.x, eye.y) - ctx.lineTo(whereOff.x, whereOff.y) - b.laser(whereOff, { - x: whereOff.x + range.x, - y: whereOff.y + range.y - }, dmg, 0, true, 0.3) + }, + firePulse() { }, + fireSplit() { + const drain = 0.001 + tech.laserDrain / b.fireCDscale + if (m.energy < drain) { + m.fireCDcycle = m.cycle + 100; // cool down if out of energy + } else { + m.fireCDcycle = m.cycle + m.energy -= drain + // const divergence = m.crouch ? 0.15 : 0.2 + // const scale = Math.pow(0.9, tech.beamSplitter) + // const pushScale = scale * scale + let dmg = tech.laserDamage / b.fireCDscale * this.lensDamage // * scale //Math.pow(0.9, tech.laserDamage) + const where = { + x: m.pos.x + 20 * Math.cos(m.angle), + y: m.pos.y + 20 * Math.sin(m.angle) } - whereOff = Vector.add(where, { - x: i * rangeOffMinus.x, - y: i * rangeOffMinus.y - }) - if (Matter.Query.ray(map, eye, whereOff).length === 0 && Matter.Query.ray(body, eye, whereOff).length === 0) { - ctx.moveTo(eye.x, eye.y) - ctx.lineTo(whereOff.x, whereOff.y) - b.laser(whereOff, { - x: whereOff.x + range.x, - y: whereOff.y + range.y - }, dmg, 0, true, 0.3) + const divergence = m.crouch ? 0.15 : 0.35 + const angle = m.angle - tech.beamSplitter * divergence / 2 + for (let i = 0; i < 1 + tech.beamSplitter; i++) { + b.laser(where, { + x: where.x + 3000 * Math.cos(angle + i * divergence), + y: where.y + 3000 * Math.sin(angle + i * divergence) + }, dmg, tech.laserReflections, false) } } - ctx.stroke(); - if (tech.isLaserLens && b.guns[11].lensDamage !== 1) { - ctx.lineWidth = 20 + 3 * b.guns[11].lensDamageOn - ctx.globalAlpha = 0.3 - ctx.stroke(); - } - ctx.globalAlpha = 1; - } - }, - fireHistory() { - drain = 0.001 + tech.laserDrain / b.fireCDscale - if (m.energy < drain) { - m.fireCDcycle = m.cycle + 100; // cool down if out of energy - } else { - m.fireCDcycle = m.cycle - m.energy -= drain - const dmg = 0.5 * tech.laserDamage / b.fireCDscale * this.lensDamage // 3.5 * 0.55 = 200% more damage - const spacing = Math.ceil(10 - 0.4 * tech.historyLaser) - ctx.beginPath(); - b.laser({ - x: m.pos.x + 20 * Math.cos(m.angle), - y: m.pos.y + 20 * Math.sin(m.angle) - }, { - x: m.pos.x + 3000 * Math.cos(m.angle), - y: m.pos.y + 3000 * Math.sin(m.angle) - }, dmg, 0, true, 0.2); - for (let i = 1, len = 3 + tech.historyLaser * 3; i < len; i++) { - const history = m.history[(m.cycle - i * spacing) % 600] - const off = history.yOff - 24.2859 - b.laser({ - x: history.position.x + 20 * Math.cos(history.angle), - y: history.position.y + 20 * Math.sin(history.angle) - off - }, { - x: history.position.x + 3000 * Math.cos(history.angle), - y: history.position.y + 3000 * Math.sin(history.angle) - off - }, dmg, 0, true, 0.2); - } - ctx.strokeStyle = tech.laserColor; - ctx.lineWidth = 1 - ctx.stroke(); - if (tech.isLaserLens && b.guns[11].lensDamage !== 1) { + }, + fireWideBeam() { + const drain = 0.001 + tech.laserDrain / b.fireCDscale + if (m.energy < drain) { + m.fireCDcycle = m.cycle + 100; // cool down if out of energy + } else { + m.fireCDcycle = m.cycle + m.energy -= drain + const range = { + x: 5000 * Math.cos(m.angle), + y: 5000 * Math.sin(m.angle) + } + const rangeOffPlus = { + x: 7.5 * Math.cos(m.angle + Math.PI / 2), + y: 7.5 * Math.sin(m.angle + Math.PI / 2) + } + const rangeOffMinus = { + x: 7.5 * Math.cos(m.angle - Math.PI / 2), + y: 7.5 * Math.sin(m.angle - Math.PI / 2) + } + const dmg = 0.70 * tech.laserDamage / b.fireCDscale * this.lensDamage // 3.5 * 0.55 = 200% more damage + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const eye = { + x: m.pos.x + 15 * Math.cos(m.angle), + y: m.pos.y + 15 * Math.sin(m.angle) + } ctx.strokeStyle = tech.laserColor; - ctx.lineWidth = 10 + 2 * b.guns[11].lensDamageOn - ctx.globalAlpha = 0.2 - ctx.stroke(); //glow + ctx.lineWidth = 8 + ctx.globalAlpha = 0.5; + ctx.beginPath(); + if (Matter.Query.ray(map, eye, where).length === 0 && Matter.Query.ray(body, eye, where).length === 0) { + b.laser(eye, { + x: eye.x + range.x, + y: eye.y + range.y + }, dmg, 0, true, 0.3) + } + for (let i = 1; i < tech.wideLaser; i++) { + let whereOff = Vector.add(where, { + x: i * rangeOffPlus.x, + y: i * rangeOffPlus.y + }) + if (Matter.Query.ray(map, eye, whereOff).length === 0 && Matter.Query.ray(body, eye, whereOff).length === 0) { + ctx.moveTo(eye.x, eye.y) + ctx.lineTo(whereOff.x, whereOff.y) + b.laser(whereOff, { + x: whereOff.x + range.x, + y: whereOff.y + range.y + }, dmg, 0, true, 0.3) + } + whereOff = Vector.add(where, { + x: i * rangeOffMinus.x, + y: i * rangeOffMinus.y + }) + if (Matter.Query.ray(map, eye, whereOff).length === 0 && Matter.Query.ray(body, eye, whereOff).length === 0) { + ctx.moveTo(eye.x, eye.y) + ctx.lineTo(whereOff.x, whereOff.y) + b.laser(whereOff, { + x: whereOff.x + range.x, + y: whereOff.y + range.y + }, dmg, 0, true, 0.3) + } + } + ctx.stroke(); + if (tech.isLaserLens && b.guns[11].lensDamage !== 1) { + ctx.lineWidth = 20 + 3 * b.guns[11].lensDamageOn + ctx.globalAlpha = 0.3 + ctx.stroke(); + } ctx.globalAlpha = 1; } - } + }, + fireHistory() { + drain = 0.001 + tech.laserDrain / b.fireCDscale + if (m.energy < drain) { + m.fireCDcycle = m.cycle + 100; // cool down if out of energy + } else { + m.fireCDcycle = m.cycle + m.energy -= drain + const dmg = 0.5 * tech.laserDamage / b.fireCDscale * this.lensDamage // 3.5 * 0.55 = 200% more damage + const spacing = Math.ceil(10 - 0.4 * tech.historyLaser) + ctx.beginPath(); + b.laser({ + x: m.pos.x + 20 * Math.cos(m.angle), + y: m.pos.y + 20 * Math.sin(m.angle) + }, { + x: m.pos.x + 3000 * Math.cos(m.angle), + y: m.pos.y + 3000 * Math.sin(m.angle) + }, dmg, 0, true, 0.2); + for (let i = 1, len = 3 + tech.historyLaser * 3; i < len; i++) { + const history = m.history[(m.cycle - i * spacing) % 600] + const off = history.yOff - 24.2859 + b.laser({ + x: history.position.x + 20 * Math.cos(history.angle), + y: history.position.y + 20 * Math.sin(history.angle) - off + }, { + x: history.position.x + 3000 * Math.cos(history.angle), + y: history.position.y + 3000 * Math.sin(history.angle) - off + }, dmg, 0, true, 0.2); + } + ctx.strokeStyle = tech.laserColor; + ctx.lineWidth = 1 + ctx.stroke(); + if (tech.isLaserLens && b.guns[11].lensDamage !== 1) { + ctx.strokeStyle = tech.laserColor; + ctx.lineWidth = 10 + 2 * b.guns[11].lensDamageOn + ctx.globalAlpha = 0.2 + ctx.stroke(); //glow + ctx.globalAlpha = 1; + } + } + }, }, - }, ], }; \ No newline at end of file diff --git a/js/index.js b/js/index.js index 05b0913..6d5d999 100644 --- a/js/index.js +++ b/js/index.js @@ -394,7 +394,7 @@ const build = {

damage: ${((tech.damageFromTech())).toPrecision(4)}     difficulty: ${((m.dmgScale)).toPrecision(4)} -
defense: ${tech.isEnergyHealth ? (1 - Math.pow(m.defense(), 0.13)).toPrecision(5) : (1 - m.defense()).toPrecision(5)}     difficulty: ${(1 / simulation.dmgScale).toPrecision(4)} +
defense: ${(1 - m.defense()).toPrecision(5)}     difficulty: ${(1 / simulation.dmgScale).toPrecision(4)}
fire rate: ${((1 - b.fireCDscale) * 100).toFixed(b.fireCDscale < 0.1 ? 2 : 0)}% ${tech.duplicationChance() ? `
duplication: ${(tech.duplicationChance() * 100).toFixed(0)}%` : ""} ${m.coupling ? `
` + m.couplingDescription(m.coupling) + ` from ${(m.coupling).toFixed(0)} ${powerUps.orb.coupling(1)}` : ""} @@ -437,7 +437,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} text += `
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
- ${b.guns[b.inventory[i]].description}
` + ${b.guns[b.inventory[i]].descriptionFunction()} ` } if (!localSettings.isHideHUD) text += `
${document.getElementById("text-log").innerHTML}
` //show last in game console message let el = document.getElementById("pause-grid-left") @@ -842,7 +842,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} text += `
  ${build.nameLink(b.guns[i].name)}
- ${b.guns[i].description}
` + ${b.guns[i].descriptionFunction()} ` } for (let i = 0, len = tech.tech.length; i < len; i++) { if ((!tech.tech[i].isJunk || localSettings.isJunkExperiment) && !tech.tech[i].isLore) { @@ -1667,7 +1667,7 @@ if (localSettings.isAllowed && !localSettings.isEmpty) { if (localSettings.isHideImages === undefined) localSettings.isHideImages = true //default to hide images document.getElementById("hide-images").checked = localSettings.isHideImages - if (localSettings.isHideHUD === undefined) localSettings.isHideHUD = false + if (localSettings.isHideHUD === undefined) localSettings.isHideHUD = true document.getElementById("hide-hud").checked = localSettings.isHideHUD } else { diff --git a/js/level.js b/js/level.js index 76ae11e..83ee29c 100644 --- a/js/level.js +++ b/js/level.js @@ -8,7 +8,7 @@ const level = { onLevel: -1, levelsCleared: 0, // playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"], - //see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , gauntlet, final) added later + //see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , subway, final) added later playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"], communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "dojo", "tlinat", "ruins", "ace", "crimsonTowers"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon", "diamagnetism"], @@ -36,7 +36,7 @@ const level = { // b.guns[3].ammo = 100000000 // requestAnimationFrame(() => { tech.giveTech("MACHO") }); // for (let i = 0; i < 1; ++i) tech.giveTech("additive manufacturing") - // for (let i = 0; i < 1; ++i) tech.giveTech("dark star") + // for (let i = 0; i < 1; ++i) tech.giveTech("flatland") // for (let i = 0; i < 1; ++i) tech.giveTech("foam-bot") // for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot") // for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot upgrade") diff --git a/js/player.js b/js/player.js index 2045371..f295e38 100644 --- a/js/player.js +++ b/js/player.js @@ -579,7 +579,7 @@ const m = { if (tech.isTurret && m.crouch) dmg *= 0.34; if (tech.isFirstDer && b.inventory[0] === b.activeGun) dmg *= 0.85 ** b.inventory.length if (tech.isEnergyHealth) { - return Math.pow(dmg, 0.19) //defense has less effect + return Math.pow(dmg, 0.33) //defense has less effect } else { return dmg } @@ -729,7 +729,7 @@ const m = { powerUps.research.changeRerolls(-1) simulation.makeTextLog(`m.research--
${powerUps.research.count}`) for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); - m.energy = m.maxEnergy + m.energy = m.maxEnergy + 0.1 if (m.immuneCycle < m.cycle + 300) m.immuneCycle = m.cycle + 300 //disable this.immuneCycle bonus seconds simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0.03)"; @@ -910,27 +910,27 @@ const m = { ctx.lineTo(m.knee.x, m.knee.y); ctx.lineTo(m.foot.x, m.foot.y); ctx.strokeStyle = stroke; - ctx.lineWidth = 7; + ctx.lineWidth = 6; ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.lineTo(m.foot.x - 14, m.foot.y + 5); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + ctx.lineTo(m.foot.x + 14, m.foot.y + 5); ctx.lineWidth = 4; ctx.stroke(); //hip joint ctx.beginPath(); - ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + ctx.arc(m.hip.x, m.hip.y, 10, 0, 2 * Math.PI); //knee joint - ctx.moveTo(m.knee.x + 7, m.knee.y); - ctx.arc(m.knee.x, m.knee.y, 7, 0, 2 * Math.PI); + ctx.moveTo(m.knee.x + 6, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 6, 0, 2 * Math.PI); //foot joint - ctx.moveTo(m.foot.x + 6, m.foot.y); - ctx.arc(m.foot.x, m.foot.y, 6, 0, 2 * Math.PI); + ctx.moveTo(m.foot.x + 5, m.foot.y + 1); + ctx.arc(m.foot.x, m.foot.y + 1, 5, 0, 2 * Math.PI); ctx.fillStyle = m.fillColor; ctx.fill(); ctx.lineWidth = 2; @@ -938,6 +938,83 @@ const m = { ctx.restore(); } }, + // resetSkin() { + // simulation.isAutoZoom = true; + // m.yOffWhen.jump = 70 + // m.yOffWhen.stand = 49 + // m.yOffWhen.crouch = 22 + // m.isAltSkin = false + // m.color = { + // hue: 0, + // sat: 0, + // light: 100, + // } + // m.setFillColors(); + // m.draw = function () { + // ctx.fillStyle = m.fillColor; + // m.walk_cycle += m.flipLegs * m.Vx; + // ctx.save(); + // ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + // ctx.translate(m.pos.x, m.pos.y); + // m.calcLeg(Math.PI, -3); + // m.drawLeg("#4a4a4a"); + // m.calcLeg(0, 0); + // m.drawLeg("#333"); + // ctx.rotate(m.angle); + // ctx.beginPath(); + // ctx.arc(0, 0, 30, 0, 2 * Math.PI); + // ctx.fillStyle = m.bodyGradient + // ctx.fill(); + // ctx.arc(15, 0, 4, 0, 2 * Math.PI); + // ctx.strokeStyle = "#333"; + // ctx.lineWidth = 2; + // ctx.stroke(); + // ctx.restore(); + // m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + // powerUps.boost.draw() + // } + // m.drawLeg = function (stroke) { + // // if (simulation.mouseInGame.x > m.pos.x) { + // if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + // m.flipLegs = 1; + // } else { + // m.flipLegs = -1; + // } + // ctx.save(); + // ctx.scale(m.flipLegs, 1); //leg lines + // ctx.beginPath(); + // ctx.moveTo(m.hip.x, m.hip.y); + // ctx.lineTo(m.knee.x, m.knee.y); + // ctx.lineTo(m.foot.x, m.foot.y); + // ctx.strokeStyle = stroke; + // ctx.lineWidth = 7; + // ctx.stroke(); + + // //toe lines + // ctx.beginPath(); + // ctx.moveTo(m.foot.x, m.foot.y); + // ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + // ctx.moveTo(m.foot.x, m.foot.y); + // ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + // ctx.lineWidth = 4; + // ctx.stroke(); + + // //hip joint + // ctx.beginPath(); + // ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + // //knee joint + // ctx.moveTo(m.knee.x + 7, m.knee.y); + // ctx.arc(m.knee.x, m.knee.y, 7, 0, 2 * Math.PI); + // //foot joint + // ctx.moveTo(m.foot.x + 6, m.foot.y); + // ctx.arc(m.foot.x, m.foot.y, 6, 0, 2 * Math.PI); + // ctx.fillStyle = m.fillColor; + // ctx.fill(); + // ctx.lineWidth = 2; + // ctx.stroke(); + // ctx.restore(); + // } + // }, skin: { none() { m.isAltSkin = true @@ -1122,27 +1199,27 @@ const m = { ctx.lineTo(m.knee.x, m.knee.y); ctx.lineTo(m.foot.x, m.foot.y); ctx.strokeStyle = stroke; - ctx.lineWidth = 7; + ctx.lineWidth = 6; ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x - 15, m.foot.y + 5); + ctx.lineTo(m.foot.x - 14, m.foot.y + 5); ctx.moveTo(m.foot.x, m.foot.y); - ctx.lineTo(m.foot.x + 15, m.foot.y + 5); + ctx.lineTo(m.foot.x + 14, m.foot.y + 5); ctx.lineWidth = 4; ctx.stroke(); //hip joint ctx.beginPath(); - ctx.arc(m.hip.x, m.hip.y, 11, 0, 2 * Math.PI); + ctx.arc(m.hip.x, m.hip.y, 10, 0, 2 * Math.PI); //knee joint - ctx.moveTo(m.knee.x + 7, m.knee.y); - ctx.arc(m.knee.x, m.knee.y, 7, 0, 2 * Math.PI); + ctx.moveTo(m.knee.x + 6, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 6, 0, 2 * Math.PI); //foot joint - ctx.moveTo(m.foot.x + 6, m.foot.y); - ctx.arc(m.foot.x, m.foot.y, 6, 0, 2 * Math.PI); + ctx.moveTo(m.foot.x + 5, m.foot.y); + ctx.arc(m.foot.x, m.foot.y + 1, 5, 0, 2 * Math.PI); ctx.strokeStyle = "rgba(0,255,255,0.25)"; ctx.lineWidth = 5; ctx.stroke(); diff --git a/js/powerup.js b/js/powerup.js index cbc9b4d..da882e1 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -326,7 +326,7 @@ const powerUps = { return } if (tech.isCancelDuplication) { - tech.duplication += 0.041 + tech.duplication += 0.043 tech.maxDuplicationEvent() simulation.makeTextLog(`tech.duplicationChance() += ${0.043}`) simulation.circleFlare(0.043); @@ -579,7 +579,7 @@ const powerUps = { } } if (powerUps.healGiveMaxEnergy) { - tech.healMaxEnergyBonus += 0.08 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1) + tech.healMaxEnergyBonus += 0.11 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1) m.setMaxEnergy(); } }, @@ -667,7 +667,7 @@ const powerUps = { text += `
entanglement
` } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { text += `
` // style = "margin-left: 192px; margin-right: -192px;" - tech.junkResearchNumber = Math.ceil(4 * Math.random()) + tech.junkResearchNumber = Math.ceil(3 * Math.random()) text += `
` for (let i = 0; i < tech.junkResearchNumber; i++) { text += `
` @@ -689,7 +689,7 @@ const powerUps = { text += `entanglement` //‌ } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { text += `` // style = "margin-left: 192px; margin-right: -192px;" - tech.junkResearchNumber = Math.ceil(4 * Math.random()) + tech.junkResearchNumber = Math.ceil(3 * Math.random()) text += `
` for (let i = 0, len = tech.junkResearchNumber; i < len; i++) { text += `
` @@ -775,7 +775,7 @@ const powerUps = { return `
  ${b.guns[choose].name}
- ${b.guns[choose].description}
` + ${b.guns[choose].descriptionFunction()}
` }, fieldText(choose, click) { const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/field/${m.fieldUpgrades[choose].name}${choose === 0 ? Math.floor(Math.random() * 10) : ""}.webp');"` @@ -1169,7 +1169,7 @@ const powerUps = { document.body.style.cursor = "auto"; document.getElementById("choose-grid").style.transitionDuration = "0s"; } - if (count < 5 && simulation.isChoosing) { + if (count < 10 && simulation.isChoosing) { requestAnimationFrame(cycle); } else { tech.isBrainstormActive = false diff --git a/js/tech.js b/js/tech.js index ed3614e..2a32bcf 100644 --- a/js/tech.js +++ b/js/tech.js @@ -83,6 +83,7 @@ const tech = { // } // }, addJunkTechToPool(percent) { //percent is number between 0-1 + tech.junkPoolPercent += percent //make an array for possible junk tech to add let options = []; for (let i = 0; i < tech.tech.length; i++) { @@ -110,6 +111,7 @@ const tech = { } } } + tech.junkPoolPercent = 0 }, giveRandomJUNK() { const list = [] @@ -120,7 +122,6 @@ const tech = { tech.giveTech(name) simulation.makeTextLog(`tech.giveTech("${name}")`); }, - giveTech(index = 'random') { if (index === 'random') { let options = []; @@ -161,6 +162,7 @@ const tech = { simulation.updateTechHUD(); } }, + junkPoolPercent: 0, junkCount: 0, countJunkTech() { tech.junkCount = 0 @@ -257,6 +259,7 @@ const tech = { if (tech.isHarmDamage && m.lastHarmCycle + 480 > m.cycle) dmg *= 3; if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit * (2 - m.defense()) // if (!simulation.paused) m.lastHit = 0 if (tech.isLowHealthDmg) dmg *= 1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health)) + if (tech.isJunkDNA) dmg *= 1 + tech.junkPoolPercent return dmg }, duplicationChance() { @@ -408,7 +411,7 @@ const tech = { { name: "mass-energy equivalence", // description: "energy protects you instead of health
√ of defense reduction reduces max energy", - description: "energy protects you instead of health
exponentially reduced defense (~ x^0.19)", + description: `energy protects you instead of health
defensive upgrades reduced by ~66%`, maxCount: 1, count: 0, frequency: 1, @@ -450,7 +453,7 @@ const tech = { // description: `after you collect ${powerUps.orb.heal()}
+${0.1 * tech.largerHeals} maximum energy`, // descriptionFunction: `convert current and future ${powerUps.orb.heal()} into

give +${10 * tech.largerHeals} maximum energy`, descriptionFunction() { - return `convert current and future
into

give +${8 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)} maximum energy` + return `convert current and future
into

give +${11 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)} maximum energy` }, maxCount: 1, count: 0, @@ -854,7 +857,7 @@ const tech = { }, { name: "non-renewables", - description: `+78% damage
${powerUps.orb.ammo()} can't spawn`, + description: `+88% damage
${powerUps.orb.ammo()} can't spawn`, maxCount: 1, count: 0, frequency: 1, @@ -863,7 +866,7 @@ const tech = { return !tech.isAmmoFromHealth && !tech.isBoostReplaceAmmo }, requires: "not catabolism, quasiparticles", - damage: 1.78, + damage: 1.88, effect() { tech.damage *= this.damage tech.isEnergyNoAmmo = true; @@ -875,7 +878,6 @@ const tech = { }, { name: "desublimated ammunition", - link: `desublimated ammunition`, description: `if crouching
alternating shots use no ammo`, maxCount: 1, count: 0, @@ -1406,7 +1408,7 @@ const tech = { { name: "nail-bot upgrade", link: `nail-bot upgrade`, - description: "convert your bots to nail-bots
+500% fire rate and +40% nail velocity", + description: "convert your bots to nail-bots
+500% fire rate and +40% nail velocity", maxCount: 1, count: 0, frequency: 3, @@ -1438,7 +1440,7 @@ const tech = { { name: "foam-bot", link: `foam-bot`, - description: "a bot fires foam at nearby mobs", + description: "a bot sprays sticky foam at nearby mobs", maxCount: 9, count: 0, frequency: 1, @@ -3547,7 +3549,7 @@ const tech = { }, { name: "pseudoscience", - description: "when selecting a power up, research 3 times
for free, but add 1-4% JUNK to the tech pool", + description: "when selecting a power up, research 3 times
for free, but add 1-3% JUNK to the tech pool", maxCount: 1, count: 0, frequency: 1, @@ -3783,6 +3785,34 @@ const tech = { } } }, + { + name: "junk DNA", + descriptionFunction() { + // return ` +100% ${b.guns[6].nameString()} damage per JUNKtech (${(100 * tech.junkCount).toFixed(0)}%)
+33% JUNK to tech pool` + return `damage scales with JUNK tech pool percent` + }, + // isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.junkPoolPercent > 0 + }, + requires: "JUNK in tech pool", + effect() { + tech.isJunkDNA = true + // this.refundAmount += tech.addJunkTechToPool(0.20) + }, + // refundAmount: 0, + remove() { + tech.isJunkDNA = false + // if (this.count > 0 && this.refundAmount > 0) { + // tech.removeJunkTechFromPool(this.refundAmount) + // this.refundAmount = 0 + // } + } + }, { name: "exciton", descriptionFunction() { @@ -4079,7 +4109,7 @@ const tech = { }, { name: "futures exchange", - description: "clicking cancel for a field, tech, or gun
gives +4.1% power up duplication chance", + description: "clicking cancel for a field, tech, or gun
gives +4.3% power up duplication chance", // descriptionFunction() { // return `clicking × to cancel a field, tech, or gun
gives +${4.9 - 0.15*simulation.difficultyMode}% power up duplication chance` // }, @@ -4229,7 +4259,7 @@ const tech = { { name: "strange attractor", descriptionFunction() { - return `+7% damage
removing this increases duplication by +10%` + return `+7% damage
removing this increases duplication by +11%` }, maxCount: 1, count: 0, @@ -4246,7 +4276,7 @@ const tech = { }, remove() { if (this.count > 0 && m.alive) { - tech.duplication += 0.1 + tech.duplication += 0.11 powerUps.setPowerUpMode(); //needed after adjusting duplication chance tech.damage /= this.damage this.frequency = 0 @@ -6168,33 +6198,6 @@ const tech = { tech.isSporeFollow = false } }, - { - name: "junk DNA", - descriptionFunction() { - return `+53% ${b.guns[6].nameString()} damage per JUNKtech (${(53 * tech.junkCount).toFixed(0)}%)
+50% JUNK to tech pool` - }, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea - }, - requires: "spores", - effect() { - tech.isJunkDNA = true - this.refundAmount += tech.addJunkTechToPool(0.5) - }, - refundAmount: 0, - remove() { - tech.isJunkDNA = false - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, // { // name: "junk DNA", // //increase damage by 10% for each JUNK tech percent in the tech pool, remove all JUNK tech, @@ -6264,7 +6267,7 @@ const tech = { }, { name: "siphonaptera", - description: "spores metamorphose into fleas", + description: "spores metamorphose into fleas
shotgun fires fleas", isGunTech: true, maxCount: 1, count: 0, @@ -6284,7 +6287,7 @@ const tech = { }, { name: "nematodes", - description: "spores metamorphose into worms", + description: "spores metamorphose into worms
shotgun fires worms", isGunTech: true, maxCount: 1, count: 0, @@ -7669,7 +7672,10 @@ const tech = { }, { name: "radiative equilibrium", - description: "after losing health
+200% damage for 8 seconds", + descriptionFunction() { + return `after losing ${tech.isEnergyHealth ? "energy" : "health"}
+200% damage for 8 seconds` + }, + // description: `after losing ${tech.isEnergyHealth ? "energy" : "health"}
+200% damage for 8 seconds`, isFieldTech: true, maxCount: 1, count: 0, @@ -9377,7 +9383,7 @@ const tech = { effect() { tech.isBrainstorm = true tech.isBrainstormActive = false - tech.brainStormDelay = 500 + tech.brainStormDelay = 500 //show each option for 0.5 seconds }, remove() { tech.isBrainstorm = false @@ -10261,67 +10267,70 @@ const tech = { }, remove() { } }, - { - name: "flatland", - description: "map blocks line of sight", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - simulation.ephemera.push({ - name: "LoS", count: 0, do() { - const pos = m.pos - const radius = 3000 - if (!simulation.isTimeSkipping) { - const vertices = simulation.sight.circleLoS(pos, radius); - if (vertices.length) { - ctx.beginPath(); - ctx.moveTo(vertices[0].x, vertices[0].y); - for (var i = 1; i < vertices.length; i++) { - var currentDistance = Math.sqrt((vertices[i - 1].x - pos.x) ** 2 + (vertices[i - 1].y - pos.y) ** 2); - var newDistance = Math.sqrt((vertices[i].x - pos.x) ** 2 + (vertices[i].y - pos.y) ** 2); - if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { - const currentAngle = Math.atan2(vertices[i - 1].y - pos.y, vertices[i - 1].x - pos.x); - const newAngle = Math.atan2(vertices[i].y - pos.y, vertices[i].x - pos.x); - ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); - } else { - ctx.lineTo(vertices[i].x, vertices[i].y) - } - } - newDistance = Math.sqrt((vertices[0].x - pos.x) ** 2 + (vertices[0].y - pos.y) ** 2); - currentDistance = Math.sqrt((vertices[vertices.length - 1].x - pos.x) ** 2 + (vertices[vertices.length - 1].y - pos.y) ** 2); - if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { - const currentAngle = Math.atan2(vertices[vertices.length - 1].y - pos.y, vertices[vertices.length - 1].x - pos.x); - const newAngle = Math.atan2(vertices[0].y - pos.y, vertices[0].x - pos.x); - ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); - } else { - ctx.lineTo(vertices[0].x, vertices[0].y) - } + // { + // name: "flatland", + // description: "map blocks line of sight", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isJunk: true, + // allowed() { return true }, + // requires: "", + // effect() { + // simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight + // simulation.draw.drawMapPath = simulation.draw.drawMapSight - //stroke the map, so it looks different form the line of sight - ctx.strokeStyle = "#234"; - ctx.lineWidth = 9; - ctx.stroke(simulation.draw.mapPath); //this has a pretty large impact on performance, maybe 5% worse performance + // simulation.ephemera.push({ + // name: "LoS", count: 0, do() { + // const pos = m.pos + // const radius = 3000 + // if (!simulation.isTimeSkipping) { + // const vertices = simulation.sight.circleLoS(pos, radius); + // if (vertices.length) { + // ctx.beginPath(); + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (var i = 1; i < vertices.length; i++) { + // var currentDistance = Math.sqrt((vertices[i - 1].x - pos.x) ** 2 + (vertices[i - 1].y - pos.y) ** 2); + // var newDistance = Math.sqrt((vertices[i].x - pos.x) ** 2 + (vertices[i].y - pos.y) ** 2); + // if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { + // const currentAngle = Math.atan2(vertices[i - 1].y - pos.y, vertices[i - 1].x - pos.x); + // const newAngle = Math.atan2(vertices[i].y - pos.y, vertices[i].x - pos.x); + // ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); + // } else { + // ctx.lineTo(vertices[i].x, vertices[i].y) + // } + // } + // newDistance = Math.sqrt((vertices[0].x - pos.x) ** 2 + (vertices[0].y - pos.y) ** 2); + // currentDistance = Math.sqrt((vertices[vertices.length - 1].x - pos.x) ** 2 + (vertices[vertices.length - 1].y - pos.y) ** 2); + // if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { + // const currentAngle = Math.atan2(vertices[vertices.length - 1].y - pos.y, vertices[vertices.length - 1].x - pos.x); + // const newAngle = Math.atan2(vertices[0].y - pos.y, vertices[0].x - pos.x); + // ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); + // } else { + // ctx.lineTo(vertices[0].x, vertices[0].y) + // } - ctx.globalCompositeOperation = "destination-in"; - ctx.fillStyle = "#000"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; - // also see the map - // ctx.fill(simulation.draw.mapPath); - // ctx.fillStyle = "#000"; - ctx.clip(); - } - } - }, - }) - }, - remove() { } - }, + // //stroke the map, so it looks different form the line of sight + // ctx.strokeStyle = "#234"; + // ctx.lineWidth = 9; + // ctx.stroke(simulation.draw.mapPath); //this has a pretty large impact on performance, maybe 5% worse performance + + // ctx.globalCompositeOperation = "destination-in"; + // ctx.fillStyle = "#000"; + // ctx.fill(); + // ctx.globalCompositeOperation = "source-over"; + // // also see the map + // // ctx.fill(simulation.draw.mapPath); + // // ctx.fillStyle = "#000"; + // ctx.clip(); + // } + // } + // }, + // }) + // }, + // remove() { } + // }, { name: "umbra", description: "produce a blue glow around everything
and probably some simulation lag", diff --git a/todo.txt b/todo.txt index 19f18e7..ee0e928 100644 --- a/todo.txt +++ b/todo.txt @@ -1,12 +1,20 @@ ******************************************************** NEXT PATCH ************************************************** -on later levels spawn 2 bosses, but 1 power up each - starts at simulation.difficulty > 23 //on hard mode level 6, level 12 on easy, level 4 on why? - makes combat harder, but also means that you will always get at least 2 drops per level - you used to have a chance to only get 1 near the end of the game - I'm guessing this will lead to about 3 more total tech by the final boss - abiogenesis - removed - parthenogenesis - doesn't duplicate bosses anymore, +8% duplication +missile Bot: +10% bigger explosions, +10% damage, +7% fire rate +plasma Bot: +40% damage, and drains 2% less energy +1st ionization energy: 8->11 energy per heal +mass-energy: defense reduction factor 0.13->0.33 +neutron bomb: +25% damage +non-renewables: 78->88% damage +junk DNA: applies to all damage, not just spores +pseudoscience: adds (1-4)->(1-3) JUNK to tech pool per free research +futures exchange: 4.1->4.3 duplication per cancel + +default skin has slightly more narrow legs + +added ammo to gun descriptions + calculate the ammo drop rate for each gun live based on ammo tech? + need to make a generic function *********************************************************** TODO *****************************************************