From 92b264cea9d3294a742cb22618e60b954f313b93 Mon Sep 17 00:00:00 2001 From: landgreen Date: Sun, 10 May 2020 05:28:24 -0700 Subject: [PATCH] foam bots --- js/bullets.js | 320 +++++++++++++++++++++++++++++++++----------------- js/level.js | 11 +- js/mobs.js | 16 ++- js/player.js | 13 +- todo.txt | 11 +- 5 files changed, 242 insertions(+), 129 deletions(-) diff --git a/js/bullets.js b/js/bullets.js index a5bf92d..40820e2 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -32,6 +32,7 @@ const b = { isModExtraChoice: null, modLaserBotCount: null, modNailBotCount: null, + modFoamBotCount: null, modCollisionImmuneCycles: null, modBlockDmg: null, isModPiezo: null, @@ -47,7 +48,6 @@ const b = { isModEnergyRecovery: null, isModHealthRecovery: null, isModEnergyLoss: null, - isModFoamShieldSKip: null, isModDeathAvoid: null, isModDeathAvoidOnCD: null, modWaveSpeedMap: null, @@ -334,6 +334,23 @@ const b = { b.modNailBotCount = 0; } }, + { + name: "foam-bot", + description: "a bot fires foam at targets in line of sight", + maxCount: 9, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + b.modFoamBotCount++; + b.foamBot(); + }, + remove() { + b.modFoamBotCount = 0; + } + }, { name: "scrap bots", description: "+12% chance to build a bot after killing a mob
the bot will follow you until you exit the map", @@ -1358,8 +1375,8 @@ const b = { } }, { - name: "quantum tunneling", - description: "foam bypasses shields and sticks to mobs", + name: "diffusiophoresis", + description: "foam's radius increases by 3x
after the mob it's stuck to dies", maxCount: 1, count: 0, allowed() { @@ -1367,12 +1384,13 @@ const b = { }, requires: "foam", effect() { - b.isModFoamShieldSKip = true; + b.isModFoamGrowOnDeath = true }, remove() { - b.isModFoamShieldSKip = false; + b.isModFoamGrowOnDeath = false; } }, + { name: "fragmenting projectiles", description: "rail gun fragments into nails
after hitting mobs at high speeds", @@ -2427,6 +2445,129 @@ const b = { y: speed * Math.sin(dir) }); }, + foam(position, velocity, radius) { + const me = bullet.length; + bullet[me] = Bodies.polygon(position.x, position.y, 20, radius, { + angle: 0, + density: 0.00005, // 0.001 is normal density + inertia: Infinity, + frictionAir: 0.003, + friction: 0.2, + restitution: 0.2, + dmg: 0.1, //damage done in addition to the damage from momentum + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.mob | cat.mobBullet // cat.map | cat.body | cat.mob | cat.mobShield + }, + minDmgSpeed: 0, + endCycle: Infinity, + count: 0, + radius: radius, + target: null, + targetVertex: null, + targetRelativePosition: null, + onDmg(who) { + if (!this.target && who.alive) { + this.target = who; + if (Matter.Query.collides(this, [who]).length > 0) { + const normal = Matter.Query.collides(this, [who])[0].normal + this.targetRelativePosition = Vector.rotate(Vector.sub(Vector.sub(this.position, who.position), Vector.mult(normal, -this.radius)), -who.angle) //find relative position vector for zero mob rotation + } else { + this.targetRelativePosition = Vector.rotate(Vector.sub(this.position, who.position), -who.angle) //find relative position vector for zero mob rotation + } + this.collisionFilter.category = cat.body; + this.collisionFilter.mask = null; + + let bestVertexDistance = Infinity + let bestVertex = null + for (let i = 0; i < this.target.vertices.length; i++) { + const dist = Vector.magnitude(Vector.sub(this.position, this.target.vertices[i])); + if (dist < bestVertexDistance) { + bestVertex = i + bestVertexDistance = dist + } + } + this.targetVertex = bestVertex + } + }, + onEnd() {}, + do() { + if (!mech.isBodiesAsleep) { //if time dilation isn't active + //check for touching map + + // if (Matter.Query.collides(this, map).length > 0) { + if (Matter.Query.point(map, this.position).length > 0) { + const slow = 0.85 + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + const SCALE = 0.96 + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + // } else if (Matter.Query.collides(this, body).length > 0) { + } else if (Matter.Query.point(body, this.position).length > 0) { + const slow = 0.9 + Matter.Body.setVelocity(this, { + x: this.velocity.x * slow, + y: this.velocity.y * slow + }); + const SCALE = 0.96 + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + } else { + this.force.y += this.mass * 0.00008; //gravity + } + if (this.count < 20) { + this.count++ + //grow + const SCALE = 1.06 + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + } else { + //shrink + const SCALE = 1 - 0.005 / b.isModBulletsLastLonger + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + if (this.radius < 8) this.endCycle = 0; + } + + if (this.target && this.target.alive) { //if stuck to a target + const rotate = Vector.rotate(this.targetRelativePosition, this.target.angle) //add in the mob's new angle to the relative position vector + if (this.target.isVerticesChange) { + Matter.Body.setPosition(this, this.target.vertices[this.targetVertex]) + } else { + Matter.Body.setPosition(this, Vector.add(Vector.add(rotate, this.target.velocity), this.target.position)) + } + Matter.Body.setVelocity(this.target, Vector.mult(this.target.velocity, 0.9)) + // Matter.Body.setAngularVelocity(this.target, this.target.angularVelocity * 0.9) + if (this.target.isShielded) { + this.target.damage(b.dmgScale * 0.005, true); //shield damage bypass + //shrink if mob is shielded + const SCALE = 1 - 0.025 / b.isModBulletsLastLonger + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + } else { + this.target.damage(b.dmgScale * 0.005); + } + } else if (this.target !== null) { //look for a new target + this.target = null + this.collisionFilter.category = cat.bullet; + this.collisionFilter.mask = cat.mob //| cat.mobShield //cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield + if (b.isModFoamGrowOnDeath && this.radius < 30) { + const SCALE = 3 + Matter.Body.scale(this, SCALE, SCALE); + this.radius *= SCALE; + } + + } + } + } + }); + World.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], velocity); + }, targetedNail(position, num = 1, speed = 50 + 10 * Math.random(), range = 1200) { const targets = [] //target nearby mobs for (let i = 0, len = mob.length; i < len; i++) { @@ -2526,6 +2667,61 @@ const b = { }) World.add(engine.world, bullet[me]); //add bullet to world }, + foamBot(position = mech.pos) { + const me = bullet.length; + const dir = mech.angle; + const RADIUS = (10 + 5 * Math.random()) + bullet[me] = Bodies.polygon(position.x, position.y, 6, RADIUS, { + angle: dir, + friction: 0, + frictionStatic: 0, + frictionAir: 0.05, + restitution: 0.6 * (1 + 0.5 * Math.random()), + dmg: 0, // 0.14 //damage done in addition to the damage from momentum + minDmgSpeed: 2, + lookFrequency: 27 + Math.floor(11 * Math.random()), + cd: 0, + delay: 110, + acceleration: 0.005 * (1 + 0.5 * Math.random()), + range: 70 * (1 + 0.3 * 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 (this.cd < game.cycle && !(game.cycle % this.lookFrequency) && !mech.isStealth) { + 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 < 1000000 && Matter.Query.ray(map, this.position, mob[i].position).length === 0) { + this.cd = game.cycle + this.delay; + target = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60)) + const radius = 6 + 6 * Math.random() + const SPEED = 27 - radius * 0.4; //(mech.crouch ? 32 : 20) - radius * 0.7; + const velocity = Vector.mult(Vector.normalise(Vector.sub(target, this.position)), SPEED) + b.foam(this.position, velocity, radius) + break; + } + } + } + + const distanceToPlayer = Vector.magnitude(Vector.sub(this.position, mech.pos)) + if (distanceToPlayer > this.range) { //if far away move towards player + this.force = Vector.mult(Vector.normalise(Vector.sub(mech.pos, this.position)), this.mass * this.acceleration) + } else { //close to player + Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity + } + } + }) + World.add(engine.world, bullet[me]); //add bullet to world + }, laserBot(position = mech.pos) { const me = bullet.length; const dir = mech.angle; @@ -3624,116 +3820,24 @@ const b = { name: "foam", description: "spray bubbly foam that sticks to mobs
slows mobs and does damage over time", ammo: 0, - ammoPack: 35, + ammoPack: 50, have: false, isStarterGun: true, isEasyToAim: false, fire() { - mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 13 : 6) * b.modFireRate); // cool down - const me = bullet.length; + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 20 : 6) * b.modFireRate); // cool down + const radius = mech.crouch ? 10 + 7 * Math.random() : 4 + 6 * Math.random() //(4 + (mech.crouch ? 15 : 6) * Math.random()) const dir = mech.angle + 0.2 * (Math.random() - 0.5) - const RADIUS = (8 + 16 * Math.random()) - bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 20, RADIUS, { - angle: dir, - density: 0.00005, // 0.001 is normal density - inertia: Infinity, - frictionAir: 0.003, - friction: 0.2, - restitution: 0.2, - dmg: 0.1, //damage done in addition to the damage from momentum - classType: "bullet", - collisionFilter: { - category: cat.bullet, - mask: cat.mob | cat.mobShield //cat.map | cat.body | cat.mob | cat.mobShield - }, - minDmgSpeed: 0, - endCycle: Infinity, - count: 0, - radius: RADIUS, - target: null, - targetVertex: null, - onDmg(who) { - if (!this.target && who.alive && who.dropPowerUp && (!who.isShielded || b.isModFoamShieldSKip)) { - this.target = who; - this.collisionFilter.category = cat.body; - this.collisionFilter.mask = null; - - let bestVertexDistance = Infinity - let bestVertex = null - for (let i = 0; i < this.target.vertices.length; i++) { - const dist = Vector.magnitude(Vector.sub(this.position, this.target.vertices[i])); - if (dist < bestVertexDistance) { - bestVertex = i - bestVertexDistance = dist - } - } - this.targetVertex = bestVertex - } - }, - onEnd() {}, - do() { - // ctx.beginPath() //draw white circle - // ctx.arc(this.position.x, this.position.y, this.radius * 0.97 - 1.6, 0, 2 * Math.PI); - // ctx.fillStyle = "#fff" - // ctx.fill() - - if (!mech.isBodiesAsleep) { //if time dilation isn't active - - //check for touching map - if (Matter.Query.collides(this, map).length > 0) { - const slow = 0.94 - Matter.Body.setVelocity(this, { - x: this.velocity.x * slow, - y: this.velocity.y * slow - }); - } else if (Matter.Query.collides(this, body).length > 0) { - const slow = 0.97 - Matter.Body.setVelocity(this, { - x: this.velocity.x * slow, - y: this.velocity.y * slow - }); - } else { - this.force.y += this.mass * 0.00006; //gravity - } - - if (this.count < 17) { - this.count++ - //grow - const SCALE = 1.08 - Matter.Body.scale(this, SCALE, SCALE); - this.radius *= SCALE; - } else { - //shrink - const SCALE = 1 - 0.0033 / b.isModBulletsLastLonger - Matter.Body.scale(this, SCALE, SCALE); - this.radius *= SCALE; - if (this.radius < 14) this.endCycle = 0; - } - - if (this.target && this.target.alive) { //if stuck to a target - Matter.Body.setPosition(this, this.target.vertices[this.targetVertex]) - Matter.Body.setVelocity(this.target, Vector.mult(this.target.velocity, 0.9)) - Matter.Body.setAngularVelocity(this.target, this.target.angularVelocity * 0.9) - if (b.isModFoamShieldSKip && this.target.isShielded) { - this.target.damage(b.dmgScale * 0.0025, true); //shield damage bypass - } else { - this.target.damage(b.dmgScale * 0.005); - } - } else if (this.target !== null) { //look for a new target - this.target = null - this.collisionFilter.category = cat.bullet; - this.collisionFilter.mask = cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield - } - } - } - }); - World.add(engine.world, bullet[me]); //add bullet to world - if (b.isModFoamShieldSKip) bullet[me].collisionFilter.mask = cat.mob // | cat.mobShield - const SPEED = (mech.crouch ? 17 : 12) - RADIUS * 0.25; - Matter.Body.setVelocity(bullet[me], { + const position = { + x: mech.pos.x + 30 * Math.cos(mech.angle), + y: mech.pos.y + 30 * Math.sin(mech.angle) + } + const SPEED = 21 - radius * 0.7; //(mech.crouch ? 32 : 20) - radius * 0.7; + const velocity = { x: SPEED * Math.cos(dir), y: SPEED * Math.sin(dir) - }); + } + b.foam(position, velocity, radius) } }, { diff --git a/js/level.js b/js/level.js index 6e024b6..63a3d50 100644 --- a/js/level.js +++ b/js/level.js @@ -20,9 +20,9 @@ const level = { // mech.setField("time dilation field") // b.giveMod("renormalization"); // b.giveMod("impact shear"); - // b.giveMod("clock gating"); + // b.giveMod("foam-bot"); // b.giveGuns("neutron bomb") - // b.giveGuns("spores") + // b.giveGuns("foam") // mech.setField("pilot wave") // mech.setField("phase decoherence field") @@ -54,6 +54,9 @@ const level = { for (let i = 0; i < b.modNailBotCount; i++) { b.nailBot() } + for (let i = 0; i < b.modFoamBotCount; i++) { + b.foamBot() + } }, isBuildRun: false, difficultyIncrease(num = 1) { @@ -158,8 +161,8 @@ const level = { // spawn.bomberBoss(2900, -500) // spawn.shooterBoss(1200, -500) - // spawn.spinner(1200, -500) - spawn.stabber(1600, -500) + spawn.spinner(1200, -500, 40) + // spawn.stabber(1600, -500) // spawn.cellBossCulture(1600, -500) // spawn.shooter(1600, -500) // spawn.shield(mob[mob.length - 1], 1200, -500, 1); diff --git a/js/mobs.js b/js/mobs.js index a33b514..b0a89ef 100644 --- a/js/mobs.js +++ b/js/mobs.js @@ -44,7 +44,7 @@ const mobs = { } }, statusSlow(who, cycles = 60) { - if (!who.shield && !who.isShielded) { + if (!who.shield && !who.isShielded && !mech.isBodiesAsleep) { //remove other "slow" effects on this mob let i = who.status.length while (i--) { @@ -75,7 +75,7 @@ const mobs = { } }, statusStun(who, cycles = 180) { - if (!who.shield && !who.isShielded) { + if (!who.shield && !who.isShielded && !mech.isBodiesAsleep) { Matter.Body.setVelocity(who, { x: who.velocity.x * 0.5, y: who.velocity.y * 0.5 @@ -112,7 +112,7 @@ const mobs = { } }, statusDoT(who, tickDamage, cycles = 180) { - if (!who.isShielded) { + if (!who.isShielded && !mech.isBodiesAsleep) { who.status.push({ effect() { if ((game.cycle - this.startCycle) % 30 === 0) { @@ -982,8 +982,14 @@ const mobs = { } } if (Math.random() < b.isModBotSpawner) { - (Math.random() < 0.5) ? b.nailBot(this.position): b.laserBot(this.position) - if (mech.energy > 0.33) mech.energy -= 0.33 + if (Math.random() < 0.33) { + b.nailBot(this.position) + } else if (Math.random() < 0.5) { + b.laserBot(this.position) + } else { + b.foamBot(this.position) + } + // if (mech.energy > 0.33) mech.energy -= 0.33 } if (b.isModExplodeMob) b.explosion(this.position, Math.min(450, Math.sqrt(this.mass + 3) * 80)) if (b.modNailsDeathMob) b.targetedNail(this.position, b.modNailsDeathMob) diff --git a/js/player.js b/js/player.js index b5d1dd3..9f8088c 100644 --- a/js/player.js +++ b/js/player.js @@ -1036,12 +1036,15 @@ const mech = { pushMass(who) { const speed = Vector.magnitude(Vector.sub(who.velocity, player.velocity)) const fieldBlockCost = (0.03 + Math.sqrt(who.mass) * speed * 0.003) * mech.fieldShieldingScale; + const unit = Vector.normalise(Vector.sub(player.position, who.position)) + if (mech.energy > fieldBlockCost * 0.2) { //shield needs at least some of the cost to block mech.energy -= fieldBlockCost - if (mech.energy < 0) mech.energy = 0; + if (mech.energy < 0) { + mech.energy = 0; + } if (mech.energy > mech.maxEnergy) mech.energy = mech.maxEnergy; - const unit = Vector.normalise(Vector.sub(player.position, who.position)) if (b.modBlockDmg) { who.damage(b.modBlockDmg) //draw electricity @@ -1293,9 +1296,9 @@ const mech = { mech.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) } if (mech.energy > 0.1 && mech.fieldCDcycle < mech.cycle) { - const fieldRange1 = (0.55 + 0.35 * Math.sin(mech.cycle / 23)) * mech.fieldRange - const fieldRange2 = (0.5 + 0.4 * Math.sin(mech.cycle / 37)) * mech.fieldRange - const fieldRange3 = (0.45 + 0.45 * Math.sin(mech.cycle / 47)) * mech.fieldRange + const fieldRange1 = (0.6 + 0.35 * Math.sin(mech.cycle / 23)) * mech.fieldRange + const fieldRange2 = (0.55 + 0.4 * Math.sin(mech.cycle / 37)) * mech.fieldRange + const fieldRange3 = (0.5 + 0.45 * Math.sin(mech.cycle / 47)) * mech.fieldRange const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) ctx.fillStyle = "rgba(110,170,200," + (0.04 + mech.energy * (0.12 + 0.13 * Math.random())) + ")"; ctx.beginPath(); diff --git a/todo.txt b/todo.txt index 6e18747..9304e40 100644 --- a/todo.txt +++ b/todo.txt @@ -1,16 +1,13 @@ -sporangium gun now sticks to things, takes longer to germinate, ammo and fire rate are increased. -plasma field does 50% more damage, pushes 20% harder, but has 20% less range +foam bullets are faster, smaller, shrink slower, stick to mobs better +foam can now stick to shielded mobs, but shrinks faster +mod: foam-bots ************** TODO - n-gon ************** -mod - if energy goes zero after shield block, knock back and stun nearby mobs +sticking bullets don't always gain the correct speed from mobs after they die mod - frozen mobs take +33% damage -bot that punches nearby mobs - bot could have a regeneration phase, and a punching phase - indicate phase by the size, shape of bot - mod heal to full at the end of each level heal mods no longer drop?