diff --git a/js/bullets.js b/js/bullets.js index 2591a1f..191ccfd 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -27,7 +27,8 @@ const b = { isModEntanglement: null, isModMassEnergy: null, isModFourOptions: null, - modGuardianCount: null, + modLaserBotCount: null, + modNailBotCount: null, modCollisionImmuneCycles: null, modBlockDmg: null, modPiezo: null, @@ -55,7 +56,8 @@ const b = { b.isModFarAwayDmg = false; b.isModEntanglement = false; b.isModMassEnergy = false; - b.modGuardianCount = 0; + b.modLaserBotCount = 0; + b.modNailBotCount = 0; b.modCollisionImmuneCycles = 30; b.modBlockDmg = 0; b.modPiezo = 0; @@ -97,11 +99,29 @@ const b = { }, { name: "kinetic bombardment", //3 - description: "do extra damage from a distance
up to 50% increase at about 30 steps away", + description: "do up to 33% more damage at a distance
increase starts at about 6 steps away", maxCount: 1, count: 0, effect() { b.isModFarAwayDmg = true; //used in mob.damage() + + game.drawList.push({ //draw range + //add dmg to draw queue + x: player.position.x, + y: player.position.y, + radius: 3000, + color: "rgba(255,0,0,0.05)", + time: 120 + }); + game.drawList.push({ //draw range + //add dmg to draw queue + x: player.position.x, + y: player.position.y, + radius: 500, + color: "rgba(0,0,0,0.2)", + time: 120 + }); + } }, { @@ -113,6 +133,16 @@ const b = { b.isModLowHealthDmg = true; //used in mob.damage() } }, + { + name: "high explosives", //15 + description: "the radius of explosions are +20% larger
immune to harm from explosions", + maxCount: 4, + count: 0, + effect: () => { + b.modExplosionRadius += 0.2; + b.isModImmuneExplosion = true; + } + }, { name: "auto-loading heuristics", //5 description: "your delay after firing is +12% shorter", @@ -147,6 +177,29 @@ const b = { count: 0, effect() { b.modSpores += 0.11; + for (let i = 0; i < 10; i++) { + b.spore(player) //spawn drone + } + } + }, + { + name: "laser-bot", //10 + description: "a bot protects the space around you
uses a short range laser that drains energy", + maxCount: 4, + count: 0, + effect() { + b.modLaserBotCount++; + b.guardian(); + } + }, + { + name: "nail-bot", //11 + description: "a bot protects the space around you
fires a nail at targets in range", + maxCount: 4, + count: 0, + effect() { + b.modNailBotCount++; + b.nailBot(); } }, { @@ -156,29 +209,13 @@ const b = { count: 0, effect() { b.isModDroneOnDamage = true; + for (let i = 0; i < 3; i++) { + b.drone() //spawn drone + } } }, { - name: "guardian", //10 - description: "a bot protects the space around you
uses a short range laser that drains energy", - maxCount: 4, - count: 0, - effect() { - b.modGuardianCount++; - b.guardian(); - } - }, - { - name: "Pauli exclusion", //11 - description: "unable to collide with enemies for +2 seconds
activates after being harmed from a collision", - maxCount: 1, - count: 0, - effect() { - b.modCollisionImmuneCycles += 120; - } - }, - { - name: "bremsstrahlung radiation", //12 + name: "bremsstrahlung radiation", //13 description: "when your field blocks it also does damage", maxCount: 4, count: 0, @@ -187,27 +224,8 @@ const b = { } }, { - name: "annihilation", //13 - description: "after touching enemies, they are annihilated", - maxCount: 1, - count: 0, - effect() { - b.isModAnnihilation = true - } - }, - { - name: "high explosives", //14 - description: "the radius of explosions are +20% larger
immune to harm from explosions", - maxCount: 4, - count: 0, - effect: () => { - b.modExplosionRadius += 0.2; - b.isModImmuneExplosion = true; - } - }, - { - name: "entanglement", //15 - description: "using your first gun reduces harm
scales by 7% for each gun in your inventory", + name: "entanglement", //16 + description: "using your first gun reduces harm
scales by 10% for each gun in your inventory", maxCount: 1, count: 0, effect() { @@ -215,88 +233,7 @@ const b = { } }, { - name: "piezoelectricity", //16 - description: "gain energy proportional to harm received", - maxCount: 4, - count: 0, - effect() { - b.modPiezo += 2 - } - }, - { - name: "energy conservation", //17 - description: "gain energy proportional to damage done", - maxCount: 4, - count: 0, - effect() { - b.modEnergySiphon += 0.18; - } - }, - { - name: "entropy exchange", //18 - description: "heal proportional to damage done", - maxCount: 4, - count: 0, - effect() { - b.modHealthDrain += 0.015; - } - }, - { - name: "overcharge", //19 - description: "charge energy +33% beyond your maximum", - maxCount: 4, - count: 0, - effect() { - mech.fieldEnergyMax += 0.33 - } - }, - { - name: "supersaturation", //20 - description: "heal +33% beyond your max health", - maxCount: 4, - count: 0, - effect() { - mech.maxHealth += 0.33 - } - }, - { - name: "recursive healing", //21 - description: "healing power ups trigger an extra time.", - maxCount: 4, - count: 0, - effect() { - b.modRecursiveHealing += 1 - } - }, - { - name: "mass-energy equivalence", //22 - description: "convert the mass of power ups into energy
power ups fill your energy and heal for +5%", - maxCount: 1, - count: 0, - effect: () => { - b.isModMassEnergy = true // used in mech.usePowerUp - } - }, - { - name: "+1 cardinality", //23 - description: "one extra choice when selecting power ups", - maxCount: 1, - count: 0, - effect: () => { - b.isModFourOptions = true; - } - }, - { - name: "Bayesian inference", //24 - description: "20% chance for double power ups to drop
one fewer choice when selecting power ups", - maxCount: 1, - count: 0, - effect: () => { - b.isModBayesian = 0.20; - } - }, - { - name: "squirrel-cage rotor", //25 + name: "squirrel-cage rotor", //27 description: "jump higher and move faster
reduced harm from falling ", maxCount: 1, count: 0, @@ -307,8 +244,124 @@ const b = { } }, { - name: "quantum immortality", //26 - description: "after dying, continue in an alternate reality
guns, ammo, and field are randomized", + name: "Pauli exclusion", //12 + description: "unable to collide with enemies for +2 seconds
activates after being harmed from a collision", + maxCount: 1, + count: 0, + effect() { + b.modCollisionImmuneCycles += 120; + mech.collisionImmune = mech.cycle + b.modCollisionImmuneCycles; //player is immune to collision damage for 30 cycles + } + }, + { + name: "annihilation", //14 + description: "after touching enemies, they are annihilated", + maxCount: 1, + count: 0, + effect() { + b.isModAnnihilation = true + } + }, + { + name: "piezoelectricity", //17 + description: "after colliding with enemies gain 50% energy", + maxCount: 1, + count: 0, + effect() { + b.modPiezo = 0.50 + } + }, + { + name: "energy conservation", //18 + description: "gain energy proportional to damage done", + maxCount: 4, + count: 0, + effect() { + b.modEnergySiphon += 0.18; + mech.fieldMeter = mech.fieldEnergyMax + } + }, + { + name: "entropy exchange", //19 + description: "heal proportional to damage done", + maxCount: 4, + count: 0, + effect() { + b.modHealthDrain += 0.015; + } + }, + { + name: "overcharge", //20 + description: "charge energy +33% beyond your maximum", + maxCount: 4, + count: 0, + effect() { + mech.fieldEnergyMax += 0.33 + mech.fieldMeter += 0.33 + } + }, + { + name: "supersaturation", //21 + description: "heal +33% beyond your max health", + maxCount: 4, + count: 0, + effect() { + mech.maxHealth += 0.33 + mech.addHealth(0.33) + } + }, + { + name: "recursive healing", //22 + description: "healing power ups trigger an extra time.", + maxCount: 4, + count: 0, + effect() { + b.modRecursiveHealing += 1 + } + }, + { + name: "mass-energy equivalence", //23 + description: "convert the mass of power ups into energy
power ups fill your energy and heal for +5%", + maxCount: 1, + count: 0, + effect: () => { + b.isModMassEnergy = true // used in mech.usePowerUp + mech.fieldMeter = mech.fieldEnergyMax + } + }, + { + name: "+1 cardinality", //24 + description: "one extra choice when selecting power ups", + maxCount: 1, + count: 0, + effect: () => { + b.isModFourOptions = true; + } + }, + { + name: "Bayesian inference", //25 + description: "20% chance for double power ups to drop
one fewer choice when selecting power ups", + maxCount: 1, + count: 0, + effect: () => { + b.isModBayesian = 0.20; + } + }, + { + name: "Born rule", //26 + description: "remove all current mods
spawn new mods to replace them", + maxCount: 1, + count: 0, + effect: () => { + for (let i = 0; i < b.modCount; i++) { // spawn new mods + powerUps.spawn(mech.pos.x, mech.pos.y, "mod"); + } + b.setModDefaults(); // remove all mods + } + }, + { + name: "quantum immortality", //28 + description: "after dying, continue in an alternate reality
guns, ammo, field, and mods are randomized", maxCount: 1, count: 0, effect() { @@ -461,6 +514,21 @@ const b = { } } }, + onCollision(event) { + const pairs = event.pairs; + for (let i = 0, j = pairs.length; i != j; i++) { + //map + bullet collisions + if (pairs[i].bodyA.collisionFilter.category === cat.map && pairs[i].bodyB.collisionFilter.category === cat.bullet) { + collideBulletStatic(pairs[i].bodyB) + } else if (pairs[i].bodyB.collisionFilter.category === cat.map && pairs[i].bodyA.collisionFilter.category === cat.bullet) { + collideBulletStatic(pairs[i].bodyA) + } + + function collideBulletStatic(obj) { + if (obj.onWallHit) obj.onWallHit(); + } + } + }, explosion(where, radius) { radius *= b.modExplosionRadius // typically explode is used for some bullets with .onEnd @@ -718,7 +786,7 @@ const b = { const THRUST = 0.0015 const dir = mech.angle + 0.2 * (Math.random() - 0.5); const RADIUS = (4.5 + 3 * Math.random()) * b.modBulletSize - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), RADIUS, { + bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 8, RADIUS, { angle: dir, inertia: Infinity, friction: 0.05, @@ -813,6 +881,75 @@ const b = { y: speed * Math.sin(dir) }); }, + nail(pos, velocity, dmg = 0) { + const me = bullet.length; + bullet[me] = Bodies.rectangle(pos.x, pos.y, 25 * b.modBulletSize, 2 * b.modBulletSize, b.fireAttributes(Math.atan2(velocity.y, velocity.x))); + Matter.Body.setVelocity(bullet[me], velocity); + World.add(engine.world, bullet[me]); //add bullet to world + bullet[me].endCycle = game.cycle + 60 + 18 * Math.random(); + bullet[me].dmg = dmg + bullet[me].do = function () {}; + }, + nailBot(speed = 1) { + const me = bullet.length; + const dir = mech.angle; + const RADIUS = (10 + 5 * Math.random()) * b.modBulletSize + bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 4, RADIUS, { + angle: dir, + friction: 0, + frictionStatic: 0, + restitution: 0.4 + 0.5 * Math.random(), + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 2, + lookFrequency: 45 + Math.floor(17 * Math.random()), + acceleration: 0.0025 + 0.001 * Math.random(), + range: 300 + Math.floor(70 * Math.random()), + endCycle: Infinity, + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield + }, + lockedOn: null, + onDmg() { + this.lockedOn = null + }, + onEnd() {}, + do() { + if (!(game.cycle % this.lookFrequency)) { + let target + for (let i = 0, len = mob.length; i < len; i++) { + const dist = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); + if (dist < 2000000 && //1400*1400 + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0) { + target = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60)) + const SPEED = 50 + b.nail(this.position, Vector.mult(Vector.normalise(Vector.sub(target, this.position)), SPEED), 0.5) + break; + } + } + } + + + + const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, mech.pos)) + if (distanceToPlayer > this.range * 0.2) { //if far away move towards player + this.force = Vector.mult(Vector.normalise(Vector.sub(mech.pos, this.position)), this.mass * this.acceleration) + this.frictionAir = 0.04 + } else { //close to player + this.frictionAir = 0.005 + //add player's velocity + Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 1), Vector.mult(player.velocity, 0.02))); + } + } + }) + World.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }); + }, guardian(speed = 1) { const me = bullet.length; const dir = mech.angle; @@ -920,7 +1057,9 @@ const b = { gun = options[Math.floor(Math.random() * options.length)] } if (gun === "all") { + if (b.guns[b.activeGun].switchOff) b.guns[b.activeGun].switchOff(); //run code when switching away from a gun b.activeGun = 0; + if (b.guns[b.activeGun].switchOn) b.guns[b.activeGun].switchOn(); //run code when switching to a new gun b.inventoryGun = 0; for (let i = 0; i < b.guns.length; i++) { b.inventory[i] = i; @@ -930,6 +1069,7 @@ const b = { } else { if (!b.guns[gun].have) b.inventory.push(gun); if (b.activeGun === null) b.activeGun = gun //if no active gun switch to new gun + if (b.guns[b.activeGun].switchOn) b.guns[b.activeGun].switchOn(); //run code when switching to a new gun b.guns[gun].have = true; b.guns[gun].ammo = b.guns[gun].ammoPack * ammoPacks; } @@ -1009,7 +1149,7 @@ const b = { let dir = mech.angle - SPREAD * 2; for (let i = 0; i < 5; i++) { const me = bullet.length; - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 7 * b.modBulletSize, b.fireAttributes(dir, false)); + bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 10, 7 * b.modBulletSize, b.fireAttributes(dir, false)); World.add(engine.world, bullet[me]); //add bullet to world Matter.Body.setVelocity(bullet[me], { x: SPEED * Math.cos(dir), @@ -1077,7 +1217,7 @@ const b = { const dir = mech.angle const SCALE = (mech.crouch ? 0.963 : 0.95) const wiggleMag = ((mech.crouch) ? 0.004 : 0.005) * ((mech.flipLegs === 1) ? 1 : -1) - bullet[me] = Bodies.circle(mech.pos.x + 25 * Math.cos(dir), mech.pos.y + 25 * Math.sin(dir), 10 * b.modBulletSize, { + bullet[me] = Bodies.polygon(mech.pos.x + 25 * Math.cos(dir), mech.pos.y + 25 * Math.sin(dir), 10, 10 * b.modBulletSize, { angle: dir, cycle: -0.43, //adjust this number until the bullets line up with the cross hairs endCycle: game.cycle + Math.floor((mech.crouch ? 155 : 120) * b.isModBulletsLastLonger), @@ -1461,19 +1601,19 @@ const b = { name: "grenades", //8 description: "lob a single bouncy projectile
explodes on contact or after one second", ammo: 0, - ammoPack: 4, + ammoPack: 6, have: false, isStarterGun: false, fire() { const me = bullet.length; const dir = mech.angle; // + Math.random() * 0.05; - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 20 * b.modBulletSize, b.fireAttributes(dir, false)); + bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 20 * b.modBulletSize, b.fireAttributes(dir, true)); b.fireProps(mech.crouch ? 30 : 20, mech.crouch ? 43 : 32, dir, me); //cd , speed Matter.Body.setDensity(bullet[me], 0.0005); bullet[me].totalCycles = 100; bullet[me].endCycle = game.cycle + Math.floor(mech.crouch ? 120 : 80); - bullet[me].restitution = 0.5; - bullet[me].explodeRad = 310; + bullet[me].restitution = 0.2; + bullet[me].explodeRad = 275; bullet[me].onEnd = b.explode; //makes bullet do explosive damage before despawn bullet[me].minDmgSpeed = 1; bullet[me].onDmg = function () { @@ -1595,87 +1735,110 @@ const b = { } }, { name: "mine", //10 - description: "drop a proximity mine that ejects nails
arms after being still for 1 second", + description: "drop a proximity mine that sticks to walls
fires nails at enemies within range", ammo: 0, - ammoPack: 8, + ammoPack: 6, have: false, isStarterGun: false, fire() { const me = bullet.length; const dir = mech.angle; - bullet[me] = Bodies.rectangle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 45 * b.modBulletSize, 16 * b.modBulletSize, { - angle: 0, - friction: 0.3, - frictionAir: 0, - dmg: 0, //damage done in addition to the damage from momentum - classType: "bullet", - collisionFilter: { - category: cat.bullet, - mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield | cat.bullet - }, - minDmgSpeed: 5, - onDmg() {}, - onEnd() {} - }); - b.fireProps(mech.crouch ? 40 : 20, mech.crouch ? 24 : 7, dir, me); //cd , speed - bullet[me].torque += bullet[me].inertia * 0.0001 * (0.5 - Math.random()) + if (mech.crouch) { + bullet[me] = Bodies.rectangle(mech.pos.x + 35 * Math.cos(mech.angle), mech.pos.y + 35 * Math.sin(mech.angle), 45 * b.modBulletSize, 16 * b.modBulletSize, { + angle: 0, + friction: 1, + frictionAir: 0, + dmg: 0, //damage done in addition to the damage from momentum + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield | cat.bullet + }, + minDmgSpeed: 5, + onDmg() {}, + onEnd() {} + }); + mech.fireCDcycle = mech.cycle + Math.floor(35 * b.modFireRate); // cool down + Matter.Body.setVelocity(bullet[me], { + x: mech.Vx / 2 + 26 * Math.cos(dir), + y: mech.Vy / 2 + 26 * Math.sin(dir) + }); + bullet[me].torque += bullet[me].inertia * 0.0001 * (0.5 - Math.random()) + } else { + bullet[me] = Bodies.rectangle(mech.pos.x, mech.pos.y + 25, 45 * b.modBulletSize, 16 * b.modBulletSize, { + angle: 0, + friction: 1, + frictionAir: 0, + dmg: 0, //damage done in addition to the damage from momentum + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield | cat.bullet + }, + minDmgSpeed: 5, + onDmg() {}, + onEnd() {} + }); + mech.fireCDcycle = mech.cycle + Math.floor(20 * b.modFireRate); // cool down + Matter.Body.setVelocity(bullet[me], { + x: mech.Vx, + y: mech.Vy + }); + } + World.add(engine.world, bullet[me]); //add bullet to world bullet[me].endCycle = game.cycle + 1800 + 360 * Math.random(); - bullet[me].cycle = 0 + bullet[me].seeFrom = null; bullet[me].restitution = 0; - bullet[me].lookFrequency = 37 + Math.floor(37 * Math.random()) + bullet[me].lookFrequency = 41 + Math.floor(23 * Math.random()) bullet[me].range = 700 + bullet[me].arm = function () { + this.do = function () { //overwrite the do method for this bullet + this.force.y += this.mass * 0.002; //extra gravity + if (!(game.cycle % this.lookFrequency)) { //find mob targets + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitudeSquared(Vector.sub(this.seeFrom, mob[i].position)) < 500000 && + mob[i].dropPowerUp && + Matter.Query.ray(map, this.seeFrom, mob[i].position).length === 0 && + Matter.Query.ray(body, this.seeFrom, mob[i].position).length === 0) { + this.endCycle = 0 //end life if mob is near and visible + } + } + } + } + } bullet[me].do = function () { this.force.y += this.mass * 0.002; //extra gravity - - ctx.beginPath(); //draw block as grey - ctx.moveTo(this.vertices[0].x, this.vertices[0].y); - for (let j = 1; j < this.vertices.length; j += 1) { - ctx.lineTo(this.vertices[j].x, this.vertices[j].y); - } - ctx.lineTo(this.vertices[0].x, this.vertices[0].y); - ctx.fillStyle = "#457"; - ctx.fill(); - - if (this.speed < 1 && !mech.isBodiesAsleep) this.cycle++ - if (this.cycle > 40) { - this.do = function () { - this.force.y += this.mass * 0.002; //extra gravity - if (!(game.cycle % this.lookFrequency)) { //find mob targets - const pos = { - x: this.position.x, - y: this.position.y - 8 //look from a bit higher to see things even with debris - } - - for (let i = 0, len = mob.length; i < len; ++i) { - const dist = Vector.magnitudeSquared(Vector.sub(pos, mob[i].position)); - if (dist < 500000 && //range2 = 700*700 - mob[i].dropPowerUp && - Matter.Query.ray(map, pos, mob[i].position).length === 0 && - Matter.Query.ray(body, pos, mob[i].position).length === 0) { - this.endCycle = 0 //end life if mob is near and visible - } + let collide = Matter.Query.collides(this, map) //check if collides with map + if (collide.length > 0) { + for (let i = 0; i < collide.length; i++) { + if (collide[i].bodyA.collisionFilter.category === cat.map || collide[i].bodyB.collisionFilter.category === cat.map) { + this.seeFrom = Vector.add(this.position, Vector.mult(collide[i].normal, -20)) //find the location 20 pixels perpendicular to the map + Matter.Body.setStatic(this, true) + this.arm(); + } + } + } else { //check if collides with a body + collide = Matter.Query.collides(this, body) + if (collide.length > 0) { + for (let i = 0; i < collide.length; i++) { + if (collide[i].bodyA.collisionFilter.category === cat.body || collide[i].bodyB.collisionFilter.category === cat.body) { + this.seeFrom = this.position + this.arm(); } } } } } bullet[me].onEnd = function () { - const pos = { - x: this.position.x, - y: this.position.y - 8 //aim from a bit higher to hit things even with debris - } - ctx.beginPath(); - ctx.arc(pos.x, pos.y, 5, 0, 2 * Math.PI); - ctx.fill(); + if (!this.seeFrom) this.seeFrom = this.position const targets = [] //target nearby mobs for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].dropPowerUp) { - const dist = Vector.magnitudeSquared(Vector.sub(pos, mob[i].position)); + const dist = Vector.magnitudeSquared(Vector.sub(this.seeFrom, mob[i].position)); if (dist < 1440000 && //1200*1200 - Matter.Query.ray(map, pos, mob[i].position).length === 0 && - Matter.Query.ray(body, pos, mob[i].position).length === 0) { - //predict where the mob will be in a few cycles - targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60))) + Matter.Query.ray(map, this.seeFrom, mob[i].position).length === 0 && + Matter.Query.ray(body, this.seeFrom, mob[i].position).length === 0) { + targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60))) //predict where the mob will be in a few cycles } } } @@ -1688,28 +1851,19 @@ const b = { x: targets[index].x + SPREAD * (Math.random() - 0.5), y: targets[index].y + SPREAD * (Math.random() - 0.5) } - needle(pos, Vector.mult(Vector.normalise(Vector.sub(WHERE, pos)), speed)) + b.nail(this.seeFrom, Vector.mult(Vector.normalise(Vector.sub(WHERE, this.seeFrom)), speed), 0.5) } else { // aim in random direction const ANGLE = 2 * Math.PI * Math.random() - needle(pos, { + b.nail(this.seeFrom, { x: speed * Math.cos(ANGLE), y: speed * Math.sin(ANGLE) }) } - - function needle(pos, velocity) { - const me = bullet.length; - bullet[me] = Bodies.rectangle(pos.x, pos.y, 25 * b.modBulletSize, 2 * b.modBulletSize, b.fireAttributes(Math.atan2(velocity.y, velocity.x))); - Matter.Body.setVelocity(bullet[me], velocity); - World.add(engine.world, bullet[me]); //add bullet to world - bullet[me].endCycle = game.cycle + 60 + 18 * Math.random(); - bullet[me].dmg = 0.8 - bullet[me].do = function () {}; - } } } } - }, { + }, + { name: "spores", //11 description: "fire orbs that discharge spores
spores seek out enemies", ammo: 0, @@ -2034,7 +2188,7 @@ const b = { }, { name: "pulse", //15 - description: "pump a laser that initiates a fusion explosion
each pulse drains 25% of your current energy", + description: "convert 25% of your energy into a pulsed laser
instantly initiates a fusion explosion", ammo: 0, ammoPack: Infinity, have: false, diff --git a/js/engine.js b/js/engine.js index 8fc27cd..f28f3eb 100644 --- a/js/engine.js +++ b/js/engine.js @@ -74,10 +74,22 @@ function playerHeadCheck(event) { } } -function mobCollisionChecks(event) { +function collisionChecks(event) { const pairs = event.pairs; for (let i = 0, j = pairs.length; i != j; i++) { + // //map + bullet collisions + // if (pairs[i].bodyA.collisionFilter.category === cat.map && pairs[i].bodyB.collisionFilter.category === cat.bullet) { + // collideBulletStatic(pairs[i].bodyB) + // } else if (pairs[i].bodyB.collisionFilter.category === cat.map && pairs[i].bodyA.collisionFilter.category === cat.bullet) { + // collideBulletStatic(pairs[i].bodyA) + // } + // //triggers when the bullets hits something static + // function collideBulletStatic(obj, speedThreshold = 12, massThreshold = 2) { + // if (obj.onWallHit) obj.onWallHit(); + // } + + //body + player collision if (game.isBodyDamage) { if (pairs[i].bodyA === playerBody || pairs[i].bodyA === playerHead) { @@ -126,6 +138,7 @@ function mobCollisionChecks(event) { let dmg = Math.min(Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05), 0.3) * game.dmgScale; //player damage is capped at 0.3*dmgScale of 1.0 mech.damage(dmg); if (mob[k].onHit) mob[k].onHit(k); + mech.fieldMeter += b.modPiezo if (b.isModAnnihilation && mob[k].dropPowerUp && !mob[k].isShielded) { mob[k].death(); game.drawList.push({ @@ -205,7 +218,7 @@ function mobCollisionChecks(event) { Events.on(engine, "collisionStart", function (event) { playerOnGroundCheck(event); playerHeadCheck(event); - mobCollisionChecks(event); + collisionChecks(event); }); Events.on(engine, "collisionActive", function (event) { playerOnGroundCheck(event); diff --git a/js/game.js b/js/game.js index 4f3e9ff..780dde7 100644 --- a/js/game.js +++ b/js/game.js @@ -213,7 +213,9 @@ const game = { }, switchGun() { if (b.modNoAmmo) b.modNoAmmo = 1 //this prevents hacking the mod by switching guns + // if (b.guns[b.activeGun].switchOff) b.guns[b.activeGun].switchOff(); //run code when switching away from a gun b.activeGun = b.inventory[b.inventoryGun]; + // if (b.guns[b.activeGun].switchOn) b.guns[b.activeGun].switchOn(); //run code when switching to a new gun game.updateGunHUD(); game.boldActiveGunHUD(); // mech.drop(); @@ -435,6 +437,7 @@ const game = { b.guns[i].have = false; if (b.guns[i].ammo != Infinity) b.guns[i].ammo = 0; } + // if (b.activeGun && b.guns[b.activeGun].switchOff) b.guns[b.activeGun].switchOff(); //run code when switching away from a gun b.activeGun = null; b.setModDefaults(); //remove mods game.updateModHUD(); diff --git a/js/index.js b/js/index.js index f1c25f2..3f70747 100644 --- a/js/index.js +++ b/js/index.js @@ -2,12 +2,30 @@ /* TODO: ******************************************* ***************************************************** -add new power up class: rerolls - let you repopulate a power up selection menu +add setting for random drops instead of choosing + +mines: move targeting position to be perpendicular to stuck wall +mines change shape of mine (maybe a octagon?) +mines: collide with bots + probably don't have to fix... +mines: add high friction, like vacuum bomb and trigger static mode after not moving for a bit + no need to watch collisions + +rework custom mode + custom mode grey out mods that are bad, like selection based mods + enable recursive mods + remove 5 mod cap on custom mode + +change nail-bot's movement + maybe have it move in a circle around player? + high friction very high acceleration towards circle location + +add mouse constraint in testing mode + https://github.com/liabru/matter-js/blob/master/examples/events.js weekly random challenge where everyone playing each week gets the same random setup. The randomness would be determined by the date so it would sync everyone. - powerups still drop, but the drops are determined by a preset list that changes each week. + power ups still drop, but the drops are determined by a preset list that changes each week. mod: do something at the end of each level heal to full @@ -18,28 +36,10 @@ mod: do something at the end of each level take no damage don't shoot -selection options for field to stay with current - -custom mode grey out mods that are bad, like selection based mods - not important - -custom mode can't use recursive mods - -field graphics too dark at high energy - not important - rewrite pause as a change to the main loop not important like testing loop -game setting for slower computers - fewer blocks - fewer debris - fewer mobs - -mod: gain energy after taking damage - use piezoelectric name (already used) - mod: ground stomp on enterLand() mod: if you fire when out of ammo you gain 1 ammo pack at the cost of @@ -89,7 +89,7 @@ new map with repeating endlessness field power up effects field allows player to hold and throw living mobs - of hack mobs + and hack mobs give mobs more animal-like behaviors like rain world diff --git a/js/level.js b/js/level.js index c9730c9..4f3df45 100644 --- a/js/level.js +++ b/js/level.js @@ -16,7 +16,7 @@ const level = { // game.difficulty = 6; //for testing to simulate possible mobs spawns // b.giveGuns(10) // mech.setField(3) - // b.giveMod(16); + // b.giveMod(3); level.intro(); //starting level // level.testingMap(); @@ -37,9 +37,12 @@ const level = { game.setZoom(); level.addToWorld(); //add bodies to game engine game.draw.setPaths(); - for (let i = 0; i < b.modGuardianCount; i++) { + for (let i = 0; i < b.modLaserBotCount; i++) { b.guardian() } + for (let i = 0; i < b.modNailBotCount; i++) { + b.nailBot() + } }, isBuildRun: false, difficultyIncrease(num = 1) { diff --git a/js/mobs.js b/js/mobs.js index e9677c3..c716a63 100644 --- a/js/mobs.js +++ b/js/mobs.js @@ -936,7 +936,9 @@ const mobs = { dmg /= Math.sqrt(this.mass) if (this.shield) dmg *= 0.04 if (b.isModLowHealthDmg) dmg *= (3 / (2 + mech.health)) //up to 50% dmg at zero player health - if (b.isModFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(1000, Math.min(3500, this.distanceToPlayer())) - 1000) * 0.01 //up to 50% dmg at max range of 3500 + + // if (b.isModFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(1000, Math.min(3500, this.distanceToPlayer())) - 1000) * 0.01 //up to 50% dmg at max range of 3500 + if (b.isModFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 50% dmg at max range of 3500 if (b.modEnergySiphon && dmg !== Infinity) mech.fieldMeter += Math.min(this.health, dmg) * b.modEnergySiphon if (b.modHealthDrain && dmg !== Infinity) mech.addHealth(Math.min(this.health, dmg) * b.modHealthDrain) this.health -= dmg diff --git a/js/player.js b/js/player.js index a935203..26bc956 100644 --- a/js/player.js +++ b/js/player.js @@ -311,8 +311,10 @@ const mech = { //find what mods I don't have let options = []; for (let i = 0, len = b.mods.length; i < len; i++) { - //can't get quantum immortality again - if (b.mods[i].name !== "quantum immortality" && b.mods[i].count < b.mods[i].maxCount) options.push(i); + //can't get quantum immortality or multiverse + if (b.mods[i].name !== "quantum immortality" && + b.mods[i].name !== "level II multiverse" && + b.mods[i].count < b.mods[i].maxCount) options.push(i); } //add a new mod if (options.length > 0) { @@ -439,12 +441,9 @@ const mech = { damage(dmg) { if (b.isModEntanglement && b.inventory[0] === b.activeGun) { for (let i = 0, len = b.inventory.length; i < len; i++) { - dmg *= 0.93 + dmg *= 0.9 } } - if (b.modPiezo) { - mech.fieldMeter += dmg * b.modPiezo - } mech.health -= dmg; if (mech.health < 0) { mech.health = 0; diff --git a/js/powerups.js b/js/powerups.js index 158b747..7915ecb 100644 --- a/js/powerups.js +++ b/js/powerups.js @@ -217,12 +217,12 @@ const powerUps = { if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "ammo"); return; } - if (Math.random() < 0.0035 * (3 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun up to 4 + if (Math.random() < 0.0025 * (3 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun up to 3 powerUps.spawn(x, y, "gun"); if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "gun"); return; } - if (Math.random() < 0.0032 * (10 - b.modCount)) { //a new mod has a low chance for each not acquired mod up to 7 + if (Math.random() < 0.003 * (11 - b.modCount)) { //a new mod has a low chance for each not acquired mod up to 7 powerUps.spawn(x, y, "mod"); if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "mod"); return; @@ -237,13 +237,13 @@ const powerUps = { if (mech.fieldMode === 0) { powerUps.spawn(x, y, "field") if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "field") - } else if (Math.random() < 0.6) { + } else if (Math.random() < 0.65) { powerUps.spawn(x, y, "mod") if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "mod") - } else if (Math.random() < 0.1) { + } else if (Math.random() < 0.06) { powerUps.spawn(x, y, "gun") if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "gun") - } else if (Math.random() < 0.1) { + } else if (Math.random() < 0.15) { powerUps.spawn(x, y, "field"); if (Math.random() < b.isModBayesian) powerUps.spawn(x, y, "field"); } else if (mech.health < 0.65) { diff --git a/style.css b/style.css index 173b06c..e82112d 100644 --- a/style.css +++ b/style.css @@ -330,6 +330,13 @@ em { letter-spacing: 1px; } +.color-m { + color: rgb(115, 61, 202); + letter-spacing: 1px; +} + + + .faded { opacity: 0.7; font-size: 90%;