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?