diff --git a/js/bullet.js b/js/bullet.js index ba731dd..c0198b1 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -168,11 +168,9 @@ const b = { for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isGunTech && tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable && !tech.tech[i].isRemoveGun) { const remove = tech.removeTech(i) - // console.log(remove, tech.tech[i].count, tech.tech[i].name) gunTechCount += remove } } - // console.log(gunTechCount) //get a random gun tech for your gun for (let i = 0; i < gunTechCount; i++) { @@ -353,7 +351,7 @@ const b = { radius *= tech.explosiveRadius let dist, sub, knock; - let dmg = radius * 0.019 * (tech.isExplosionStun ? 0.7 : 1); //* 0.013 * (tech.isExplosionStun ? 0.7 : 1); + let dmg = radius * 0.019 if (tech.isExplosionHarm) radius *= 1.7 // 1/sqrt(2) radius -> area if (tech.isSmallExplosion) { // color = "rgba(255,0,30,0.7)" @@ -393,7 +391,7 @@ const b = { if (mob[i].shield) dmg *= 2.5 //balancing explosion dmg to shields if (Matter.Query.ray(map, mob[i].position, where).length > 0) dmg *= 0.5 //reduce damage if a wall is in the way mobs.statusDoT(mob[i], dmg * damageScale * 0.25, 240) //apply radiation damage status effect on direct hits - if (tech.isExplosionStun) mobs.statusStun(mob[i], 60) + if (tech.isStun) mobs.statusStun(mob[i], 30) mob[i].locatePlayer(); damageScale *= 0.87 //reduced damage for each additional explosion target } @@ -429,7 +427,6 @@ const b = { // const mitigate = Math.min(1, Math.max(1 - m.energy * 0.5, 0)) m.energy -= 0.12 // m.damage(0.01 * harm); //remove 99% of the damage 1-0.99 - // console.log(Math.max(0, Math.min(0.15 - 0.01 * player.speed, 0.15))) knock = Vector.mult(Vector.normalise(sub), -0.6 * player.mass * Math.max(0, Math.min(0.15 - 0.002 * player.speed, 0.15))); player.force.x = knock.x; // not += so crazy forces can't build up with MIRV player.force.y = knock.y - 0.3; //some extra vertical kick @@ -502,8 +499,8 @@ const b = { mob[i].damage(dmg * damageScale * m.dmgScale); mob[i].locatePlayer(); knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg * damageScale) * mob[i].mass * (mob[i].isBoss ? 0.003 : 0.01)); - if (tech.isExplosionStun) { - mobs.statusStun(mob[i], 120) + if (tech.isStun) { + mobs.statusStun(mob[i], 30) } else if (!mob[i].isInvulnerable) { mob[i].force.x += knock.x; mob[i].force.y += knock.y; @@ -513,8 +510,8 @@ const b = { } else if (!mob[i].seePlayer.recall && dist < alertRange) { mob[i].locatePlayer(); knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg * damageScale) * mob[i].mass * (mob[i].isBoss ? 0 : 0.006)); - if (tech.isExplosionStun) { - mobs.statusStun(mob[i], 60) + if (tech.isStun) { + mobs.statusStun(mob[i], 30) } else if (!mob[i].isInvulnerable) { mob[i].force.x += knock.x; mob[i].force.y += knock.y; @@ -2463,7 +2460,7 @@ const b = { ctx.globalAlpha = 1; } }, - AoEStunEffect(where, range, cycles = 90 + 60 * Math.random()) { + AoEStunEffect(where, range, cycles = 120 + 60 * Math.random()) { for (let i = 0, len = mob.length; i < len; ++i) { if (mob[i].alive && !mob[i].isShielded && !mob[i].shield && !mob[i].isBadTarget) { if (Vector.magnitude(Vector.sub(where, mob[i].position)) - mob[i].radius < range) mobs.statusStun(mob[i], cycles) @@ -2509,7 +2506,7 @@ const b = { Matter.Query.ray(map, this.position, mob[i].position).length === 0 && Matter.Query.ray(body, this.position, mob[i].position).length === 0 ) { - if (tech.isExplosionStun) b.AoEStunEffect(this.position, 1300); + if (tech.isStun) b.AoEStunEffect(this.position, 1300); //AoEStunEffect(where, range, cycles = 90 + 60 * Math.random()) { this.do = this.laserSpin if (this.angularSpeed < 0.5) this.torque += this.inertia * this.torqueMagnitude * 200 //spin this.endCycle = simulation.cycle + 360 + 120 @@ -2644,7 +2641,7 @@ const b = { Matter.Query.ray(body, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable ) { - if (tech.isExplosionStun) b.AoEStunEffect(this.position, 700 + mob[i].radius + random); + if (tech.isStun) b.AoEStunEffect(this.position, 700 + mob[i].radius + random); //AoEStunEffect(where, range, cycles = 90 + 60 * Math.random()) { if (tech.isMineSentry) { this.lookFrequency = 8 + Math.floor(3 * Math.random()) this.endCycle = simulation.cycle + 1020 @@ -2691,7 +2688,7 @@ const b = { // angle: Math.random() * 2 * Math.PI, friction: 0, frictionAir: 0.025, - thrust: (tech.isFastSpores ? 0.0012 : 0.00055) * (1 + 0.5 * (Math.random() - 0.5)), + 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 ? 7 : 2.9) * wormSize, //bonus damage from tech.isMutualism //2.5 is extra damage as worm @@ -2809,7 +2806,7 @@ const b = { angle: Math.random() * 2 * Math.PI, friction: 0, frictionAir: 0.025, - thrust: (tech.isFastSpores ? 0.0011 : 0.0005) * (1 + 0.3 * (Math.random() - 0.5)), + thrust: (tech.isSporeFollow ? 0.0011 : 0.0005) * (1 + 0.3 * (Math.random() - 0.5)), dmg: tech.isMutualism ? 16.8 : 7, //bonus damage from tech.isMutualism lookFrequency: 100 + Math.floor(117 * Math.random()), classType: "bullet", @@ -2901,8 +2898,17 @@ const b = { // this.force.y += this.mass * 0.0001; //gravity // } + // if (this.nextPortCycle < simulation.cycle) { //teleport around if you have tech.isBulletTeleport + // this.nextPortCycle = simulation.cycle + this.portFrequency + // const range = 50 * Math.random() + // Matter.Body.setPosition(this, Vector.add(this.position, Vector.rotate({ x: range, y: 0 }, 2 * Math.PI * Math.random()))) + // } }, }); + // if (tech.isBulletTeleport) { + // bullet[bIndex].portFrequency = 10 + Math.floor(5 * Math.random()) + // bullet[bIndex].nextPortCycle = simulation.cycle + bullet[bIndex].portFrequency + // } const SPEED = 4 + 8 * Math.random(); const ANGLE = 2 * Math.PI * Math.random() @@ -2941,12 +2947,12 @@ const b = { minDmgSpeed: 0, lockedOn: null, beforeDmg(who) { - mobs.statusSlow(who, 180) - this.endCycle = simulation.cycle - // if (tech.isHeavyWater) mobs.statusDoT(who, 0.15, 300) if (tech.iceEnergy && !who.shield && !who.isShielded && who.isDropPowerUp && who.alive && m.immuneCycle < m.cycle) { setTimeout(() => { if (!who.alive) m.energy += tech.iceEnergy * 0.8 }, 10); } + mobs.statusSlow(who, 180) + this.endCycle = simulation.cycle + // if (tech.isHeavyWater) mobs.statusDoT(who, 0.15, 300) }, onEnd() {}, do() { @@ -3816,6 +3822,38 @@ const b = { } } }, + crit(mob, bullet) { + if (!mob.shield && Vector.dot(Vector.normalise(Vector.sub(mob.position, bullet.position)), Vector.normalise(bullet.velocity)) > 0.99 - 4 / mob.radius) { + let cycle = () => { //makes this run after damage + if (mob.health < 0.5 && mob.damageReduction > 0 && mob.alive) { + const color = 'rgb(255,255,255)' + simulation.drawList.push({ + x: mob.position.x, + y: mob.position.y, + radius: mob.radius * 1.2, + color: color, //"rgba(0,0,0,0.6)", + time: 8 + }); + simulation.drawList.push({ + x: mob.position.x, + y: mob.position.y, + radius: mob.radius * 0.75, + color: color, //"rgba(0,0,0,0.85)", + time: 15 + }); + simulation.drawList.push({ + x: mob.position.x, + y: mob.position.y, + radius: mob.radius * 0.4, + color: color, //"rgb(0,0,0)", + time: 20 + }); + mob.death(); + } + } + requestAnimationFrame(cycle); + } + }, nail(pos, velocity, dmg = 1) { dmg *= tech.bulletSize const me = bullet.length; @@ -3826,11 +3864,10 @@ const b = { bullet[me].dmg = tech.isNailRadiation ? 0 : dmg bullet[me].beforeDmg = function(who) { //beforeDmg is rewritten with ice crystal tech if (tech.isNailRadiation) mobs.statusDoT(who, dmg * (tech.isFastRadiation ? 1.3 : 0.44), tech.isSlowRadiation ? 360 : (tech.isFastRadiation ? 60 : 180)) // one tick every 30 cycles - if (tech.isNailCrit && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.94) { - b.explosion(this.position, 150 + 30 * Math.random()); //makes bullet do explosive damage at end - } - if (true && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.94) { - b.targetedNail(this.position, 1, 39 + 6 * Math.random()) + if (tech.isNailCrit) { + if (!who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.97 - 2 / who.radius) { + b.explosion(this.position, 150 + 30 * Math.random()); //makes bullet do explosive damage at end + } } }; bullet[me].do = function() {}; @@ -3861,9 +3898,12 @@ const b = { } } if (!immune) { - if (tech.isNailCrit && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.94) { - b.explosion(this.position, 220 * tech.bulletSize + 50 * Math.random()); //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 - 2 / who.radius) { + b.explosion(this.position, 220 + 50 * Math.random()); //makes bullet do explosive damage at end + } + } else if (tech.isCritKill) b.crit(who, this) + this.immuneList.push(who.id) //remember that this needle has hit this mob once already let dmg = this.dmg * tech.bulletSize * m.dmgScale if (tech.isNailRadiation) { @@ -3916,9 +3956,12 @@ const b = { } } if (!immune) { - if (tech.isNailCrit && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.94) { - b.explosion(this.position, 220 * tech.bulletSize + 50 * Math.random()); //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 - 2 / who.radius) { + b.explosion(this.position, 220 + 50 * Math.random()); //makes bullet do explosive damage at end + } + } else if (tech.isCritKill) b.crit(who, this) + this.immuneList.push(who.id) //remember that this needle has hit this mob once already let dmg = this.dmg * tech.bulletSize * m.dmgScale if (tech.isNailRadiation) { @@ -5060,9 +5103,11 @@ const b = { 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 && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.94) { - b.explosion(this.position, 300 + 30 * Math.random()); //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 - 2 / 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) @@ -5131,9 +5176,12 @@ const b = { 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 && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.94) { - b.explosion(this.position, 300 + 30 * Math.random()); //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 - 2 / 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) @@ -5203,14 +5251,16 @@ const b = { 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 && !who.shield && Vector.dot(Vector.normalise(Vector.sub(who.position, this.position)), Vector.normalise(this.velocity)) > 0.94) { - b.explosion(this.position, 150 + 30 * Math.random()); //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 - 2 / who.radius) { + b.explosion(this.position, 150 + 30 * Math.random()); //makes bullet do explosive damage at end + } } }; if (m.energy < 0.01) { m.fireCDcycle = m.cycle + 60; // cool down } else { - m.energy -= m.fieldRegen + 0.008 + m.energy -= 0.01 } } }, @@ -5289,12 +5339,13 @@ const b = { } } }; - if (tech.fragments) { - bullet[me].beforeDmg = function() { - if (this.speed > 4) { + 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.isCritKill) b.crit(who, this) } } } else if (tech.isIncendiary) { @@ -6043,7 +6094,7 @@ const b = { bullet[me].maxRadius = 30; bullet[me].restitution = 0.3; bullet[me].minDmgSpeed = 0; - bullet[me].totalSpores = 8 + 2 * tech.isFastSpores + 2 * tech.isSporeFreeze * (tech.isSporeWorm ? 0.5 : 1) + bullet[me].totalSpores = 8 + 2 * tech.isSporeFreeze * (tech.isSporeWorm ? 0.5 : 1) bullet[me].stuck = function() {}; bullet[me].beforeDmg = function() {}; bullet[me].do = function() { @@ -6152,6 +6203,7 @@ const b = { } else { for (let i = 0; i < this.totalSpores; i++) b.spore(this.position) } + if (tech.isStun) b.AoEStunEffect(this.position, 600, 270 + 120 * Math.random()); //AoEStunEffect(where, range, cycles = 120 + 60 * Math.random()) { } } }, { @@ -6392,7 +6444,6 @@ const b = { 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)) - // console.log(dot, 0.95 - Math.min(dist * 0.00015, 0.3)) 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-- diff --git a/js/index.js b/js/index.js index 895810a..76e4124 100644 --- a/js/index.js +++ b/js/index.js @@ -242,11 +242,13 @@ const build = {
` text += `
damage: ${((tech.damageFromTech())).toPrecision(3)}     difficulty: ${((m.dmgScale)).toPrecision(3)} -
defense: ${(1-m.harmReduction()).toPrecision(3)}     difficulty: ${(simulation.dmgScale).toPrecision(3)} +
defense: ${(1-m.harmReduction()).toPrecision(3)}     difficulty: ${(1/simulation.dmgScale).toPrecision(3)}
fire rate: ${((1-b.fireCDscale)*100).toFixed(b.fireCDscale < 0.1 ? 2 : 0)}%
duplication: ${(tech.duplicationChance()*100).toFixed(0)}% ${botText} -

health: (${(m.health*100).toFixed(0)} / ${(m.maxHealth*100).toFixed(0)})   energy: (${(m.energy*100).toFixed(0)} / ${(m.maxEnergy*100).toFixed(0)}) +
+
health: (${(m.health*100).toFixed(0)} / ${(m.maxHealth*100).toFixed(0)}) +
energy: (${(m.energy*100).toFixed(0)} / ${(m.maxEnergy*100).toFixed(0)})
gun: ${b.activeGun === null || b.activeGun === undefined ? "undefined":b.guns[b.activeGun].name}   ammo: ${b.activeGun === null || b.activeGun === undefined ? "0":b.guns[b.activeGun].ammo}
tech: ${tech.totalCount}   research: ${powerUps.research.count}
diff --git a/js/level.js b/js/level.js index b6238a5..4a78987 100644 --- a/js/level.js +++ b/js/level.js @@ -7,9 +7,9 @@ const level = { defaultZoom: 1400, onLevel: -1, levelsCleared: 0, + // playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"], //see level.populateLevels: (intro, ... , reservoir, reactor, ... , gauntlet, final) added later playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion"], - // playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"], communityLevels: ["stronghold", "basement", "crossfire", "vats", "run", "n-gon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"], levels: [], @@ -20,12 +20,13 @@ const level = { // m.setField("time dilation") // b.giveGuns("nail gun") // b.giveGuns("mine") - // tech.giveTech("laser-mines") - // tech.giveTech("free-electron laser") - // tech.giveTech("energy conservation") - // tech.giveTech("6s half-life") - // for (let i = 0; i < 10; i++) tech.giveTech("replication") - // tech.giveTech("eternalism") + // tech.giveTech("needle gun") + // tech.giveTech("stress concentration") + // for (let i = 0; i < 100; ++i) tech.giveTech("nail-bot") + // tech.giveTech("rivet gun") + // tech.giveTech("needle ice") + // tech.giveTech("flash freeze") + // tech.giveTech("superfluidity") // m.maxHealth = 100 // m.health = m.maxHealth // for (let i = 0; i < 10; i++) tech.giveTech("tungsten carbide") @@ -36,12 +37,14 @@ const level = { // powerUps.research.changeRerolls(100000) // tech.tech[297].frequency = 100 // m.immuneCycle = Infinity //you can't take damage - // level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why + // level.difficultyIncrease(40) //30 is near max on hard //60 is near max on why // simulation.enableConstructMode() //used to build maps in testing mode // level.perplex(); // spawn.cellBossCulture(1900, -500) // level.testing(); //not in rotation, used for testing - // spawn.starter(1900, -500) + // spawn.starter(1900, -500, 300) + // for (let i = 0; i < 50; ++i) spawn.starter(1900, -500) + // spawn.powerUpBoss(1900, -500) if (simulation.isTraining) { level.walk(); } else { level.intro(); } //normal starting level ************************************************ // powerUps.research.changeRerolls(3000) @@ -1327,10 +1330,10 @@ const level = { if (this.height > 0 && Matter.Query.region([player], this).length) { if (m.immuneCycle < m.cycle) { - const DRAIN = 0.002 * (tech.isRadioactiveResistance ? 0.25 : 1) + m.fieldRegen + const DRAIN = 0.0022 * (tech.isRadioactiveResistance ? 0.25 : 1) + m.fieldRegen if (m.energy > DRAIN) { m.energy -= DRAIN - m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1) * 0.03) //still take 2% damage while you have energy + // m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1) * 0.03) //still take 2% damage while you have energy } else { m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1)) } @@ -8849,7 +8852,7 @@ const level = { } else { tech.addJunkTechToPool(0.49) } - // spawn.randomLevelBoss(x, y, ["historyBoss"]); + spawn.randomLevelBoss(x, y, ["historyBoss"]); } } diff --git a/js/mob.js b/js/mob.js index 0d3a501..dad0ff7 100644 --- a/js/mob.js +++ b/js/mob.js @@ -59,9 +59,32 @@ const mobs = { } function applySlow(whom) { - if (!whom.shield && !whom.isShielded && who.alive) { - if (tech.isIceMaxHealthLoss && who.health > 0.66 && who.damageReduction > 0) who.health = 0.66 - if (tech.isIceKill && who.health < 0.33 && who.damageReduction > 0) who.death(); + if (!whom.shield && !whom.isShielded && whom.alive) { + if (tech.isIceMaxHealthLoss && whom.health > 0.65 && whom.damageReduction > 0) whom.health = 0.66 + if (tech.isIceKill && whom.health < 0.34 && whom.damageReduction > 0 && whom.alive) { + simulation.drawList.push({ + x: whom.position.x, + y: whom.position.y, + radius: whom.radius * 1.2, + color: "rgb(0,100,255)", + time: 8 + }); + simulation.drawList.push({ + x: whom.position.x, + y: whom.position.y, + radius: whom.radius * 0.7, + color: "rgb(0,100,255)", + time: 12 + }); + simulation.drawList.push({ + x: whom.position.x, + y: whom.position.y, + radius: whom.radius * 0.4, + color: "rgb(0,100,255)", + time: 16 + }); + whom.death(); + } if (whom.isBoss) cycles = Math.floor(cycles * 0.25) let i = whom.status.length while (i--) { diff --git a/js/player.js b/js/player.js index 5cc49ac..08bd892 100644 --- a/js/player.js +++ b/js/player.js @@ -858,7 +858,7 @@ const m = { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); - ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 + 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"); @@ -2622,7 +2622,7 @@ const m = { } else { m.fieldFire = true; m.isBodiesAsleep = false; - m.drain = 0.0025 + m.drain = 0.002 m.hold = function() { if (m.isHolding) { m.wakeCheck(); diff --git a/js/simulation.js b/js/simulation.js index 4ddf271..d73b16f 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -105,6 +105,7 @@ const simulation = { simulation.isTimeSkipping = true; for (let i = 0; i < cycles; i++) { simulation.cycle++; + // m.walk_cycle += (m.flipLegs * m.Vx) * 0.5; //makes the legs look like they are moving fast (it's times 0.5 because when they move too fast it's a blur) simulation.gravity(); Engine.update(engine, simulation.delta); // level.custom(); diff --git a/js/spawn.js b/js/spawn.js index f84383a..48e0fa4 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -47,13 +47,13 @@ const spawn = { }, randomMob(x, y, chance = 1) { if (spawn.spawnChance(chance) || chance === Infinity) { - const pick = this.pickList[Math.floor(Math.random() * this.pickList.length)]; - this[pick](x, y); + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x, y); } if (tech.isMoreMobs || (tech.isDuplicateBoss && Math.random() < tech.duplicationChance())) { - const pick = this.pickList[Math.floor(Math.random() * this.pickList.length)]; - this[pick](x, y); + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x, y); } }, randomSmallMob(x, y, @@ -62,14 +62,14 @@ const spawn = { chance = 1) { if (spawn.spawnChance(chance)) { for (let i = 0; i < num; ++i) { - const pick = this.pickList[Math.floor(Math.random() * this.pickList.length)]; - this[pick](x + Math.round((Math.random() - 0.5) * 20) + i * size * 2.5, y + Math.round((Math.random() - 0.5) * 20), size); + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + Math.round((Math.random() - 0.5) * 20) + i * size * 2.5, y + Math.round((Math.random() - 0.5) * 20), size); } } if (tech.isMoreMobs || (tech.isDuplicateBoss && Math.random() < tech.duplicationChance())) { for (let i = 0; i < num; ++i) { - const pick = this.pickList[Math.floor(Math.random() * this.pickList.length)]; - this[pick](x + Math.round((Math.random() - 0.5) * 20) + i * size * 2.5, y + Math.round((Math.random() - 0.5) * 20), size); + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](x + Math.round((Math.random() - 0.5) * 20) + i * size * 2.5, y + Math.round((Math.random() - 0.5) * 20), size); } } }, @@ -3576,7 +3576,7 @@ const spawn = { ctx.setLineDash([]); } }, - sprayBoss(x, y, radius = 30, isSpawnBossPowerUp = true) { + sprayBoss(x, y, radius = 35, isSpawnBossPowerUp = true) { mobs.spawn(x, y, 16, radius, "rgb(255,255,255)"); let me = mob[mob.length - 1]; me.isBoss = true; @@ -3587,7 +3587,7 @@ const spawn = { me.friction = 0; me.frictionAir = 0; me.restitution = 1 - spawn.spawnOrbitals(me, radius + 50 + 150 * Math.random(), 1) + spawn.spawnOrbitals(me, radius + 50 + 125 * Math.random(), 1) Matter.Body.setDensity(me, 0.0022 + 0.0001 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger me.damageReduction = 0.09 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.startingDamageReduction = me.damageReduction @@ -3630,8 +3630,8 @@ const spawn = { if (this.speed < 0.01) { Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(Vector.sub(player.position, this.position)), 0.1)); } else { - if (Math.abs(this.velocity.y) < 12) Matter.Body.setVelocity(this, { x: this.velocity.x, y: this.velocity.y * 1.07 }); - if (Math.abs(this.velocity.x) < 9) Matter.Body.setVelocity(this, { x: this.velocity.x * 1.07, y: this.velocity.y }); + if (Math.abs(this.velocity.y) < 11) Matter.Body.setVelocity(this, { x: this.velocity.x, y: this.velocity.y * 1.07 }); + if (Math.abs(this.velocity.x) < 8) Matter.Body.setVelocity(this, { x: this.velocity.x * 1.07, y: this.velocity.y }); } } me.burstFire = function() { @@ -5188,12 +5188,12 @@ const spawn = { me.eventHorizon = 0; //set in mob loop me.frictionStatic = 0; me.friction = 0; - me.frictionAir = 0.004; + me.frictionAir = 0.005; me.accelMag = 0.00008 + 0.00002 * simulation.accelScale spawn.shield(me, x, y, 1); spawn.spawnOrbitals(me, radius + 50 + 100 * Math.random()) - Matter.Body.setDensity(me, 0.003); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 0.0025); //extra dense //normal is 0.001 //makes effective life much larger me.damageReduction = 0.07 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.startingDamageReduction = me.damageReduction me.isInvulnerable = false @@ -5217,10 +5217,14 @@ const spawn = { this.attraction(); this.damageReduction = this.startingDamageReduction this.isInvulnerable = false - if (!(simulation.cycle % 15)) requestAnimationFrame(() => { - simulation.timePlayerSkip(5) - simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations - }); //wrapping in animation frame prevents errors, probably + // if (!(simulation.cycle % 15)) requestAnimationFrame(() => { + // simulation.timePlayerSkip(5) + // // simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations + // }); //wrapping in animation frame prevents errors, probably + requestAnimationFrame(() => { + simulation.timePlayerSkip(1) + m.walk_cycle += m.flipLegs * m.Vx //makes the legs look like they are moving fast + }); //wrapping in animation frame prevents errors, probably ctx.beginPath(); ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); diff --git a/js/tech.js b/js/tech.js index 07ef630..b174b70 100644 --- a/js/tech.js +++ b/js/tech.js @@ -868,7 +868,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.isOrbitBotUpgrade || tech.isExplosionStun + return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.isOrbitBotUpgrade || tech.isStun }, requires: "a stun effect", effect() { @@ -1666,7 +1666,7 @@ const tech = { }, { name: "Pauli exclusion", - description: `after mob collisions
become invulnerable for +2.5 seconds`, + description: `after mob collisions
become invulnerable for +3 seconds`, maxCount: 9, count: 0, frequency: 1, @@ -1674,7 +1674,7 @@ const tech = { allowed() { return true }, requires: "", effect() { - tech.collisionImmuneCycles += 150; + tech.collisionImmuneCycles += 180; if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage }, remove() { @@ -1683,7 +1683,7 @@ const tech = { }, { name: "spin–statistics theorem", - description: `every 7 seconds
become invulnerable for 1.75 seconds`, + description: `every 7 seconds
become invulnerable for +1.75 seconds`, maxCount: 3, count: 0, frequency: 1, @@ -1888,96 +1888,6 @@ const tech = { tech.relayIce = 0 } }, - { - name: "freezer burn", - description: "mobs frozen while below 33% durability die", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 - }, - requires: "a freeze effect", - effect() { - tech.isIceKill = true - }, - remove() { - tech.isIceKill = false - } - }, - { - name: "flash freeze", - description: "mobs frozen while above 66% durability
have their durability reduced to 66%", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 - }, - requires: "a freeze effect", - effect() { - tech.isIceMaxHealthLoss = true - }, - remove() { - tech.isIceMaxHealthLoss = false - } - }, - { - name: "crystallizer", - description: "after frozen mobs die they
shatter into ice IX crystals", - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob - }, - requires: "a localized freeze effect, no other mob death tech", - effect() { - tech.iceIXOnDeath++ - }, - remove() { - tech.iceIXOnDeath = 0 - } - }, - { - name: "thermoelectric effect", - description: "killing mobs with ice IX
generates 100 energy", - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.blockingIce || tech.iceIXOnDeath || tech.isIceShot - }, - requires: "ice IX", - effect() { - tech.iceEnergy++ - }, - remove() { - tech.iceEnergy = 0; - } - }, - { - name: "superfluidity", - description: "freeze effects are applied
to a small area around the target", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 || tech.iceIXOnDeath || tech.isIceShot - }, - requires: "a localized freeze effect", - effect() { - tech.isAoESlow = true - }, - remove() { - tech.isAoESlow = false - } - }, { name: "liquid cooling", description: `after losing health
freeze all mobs for 7 seconds`, @@ -2429,7 +2339,7 @@ const tech = { }, { name: "inductive coupling", - description: "if crouched +700% passive energy generation
if not crouched energy generation is disabled", + description: "if crouched +600% passive energy generation
if not crouched energy generation is disabled", maxCount: 1, count: 0, frequency: 1, @@ -3245,7 +3155,7 @@ const tech = { }, requires: "at least 4 research, not parthenogenesis", effect() { - tech.isResearchBoss = true; //abiogenesis + tech.isResearchBoss = true; }, remove() { tech.isResearchBoss = false; @@ -3891,7 +3801,7 @@ const tech = { }, { name: "caliber", - description: `rivets, needles, super balls, and nails
have +16% mass and physical damage`, + description: `rivets, needles, super balls, and nails
have +25% mass and physical damage`, isGunTech: true, maxCount: 9, count: 0, @@ -3902,7 +3812,7 @@ const tech = { }, requires: "nails, nail gun, rivets, shotgun", effect() { - tech.bulletSize += 0.16 + tech.bulletSize += 0.25 }, remove() { tech.bulletSize = 1; @@ -3938,7 +3848,7 @@ const tech = { { name: "ice crystal nucleation", link: `ice crystal nucleation`, - description: "the nail gun uses energy to condense
unlimited freezing ice shards", + description: "nail gun uses energy to condense
unlimited freezing ice shards", isGunTech: true, maxCount: 1, count: 0, @@ -4002,17 +3912,36 @@ const tech = { } }, { - name: "supercritical fission", - description: "nails, needles, and rivets can explode
if they strike mobs near their center", + name: "stress concentration", + description: "mobs below 50% durability die after you shoot
them near their center with needles or rivets", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return (tech.isNailShot || tech.isNeedles || tech.isNailBotUpgrade || tech.haveGunCheck("nail gun") || tech.isRivets) && !tech.isIncendiary + return (tech.isNeedles || tech.isRivets) && !tech.isNailCrit && !tech.isIncendiary }, - requires: "nail gun, needles, nails, rivets, not incendiary", + requires: "needles, rivets, not incendiary, supercritical fission", + effect() { + tech.isCritKill = true + }, + remove() { + tech.isCritKill = false + } + }, + { + name: "supercritical fission", + description: "if bullets strike mobs near their center
they can explode (nails, needles, rivets)", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isNailShot || tech.isNeedles || tech.isNailBotUpgrade || tech.haveGunCheck("nail gun") || tech.isRivets) && !tech.isIncendiary && !tech.isCritKill + }, + requires: "nail gun, needles, nails, rivets, not incendiary, fire-control system", effect() { tech.isNailCrit = true }, @@ -4219,6 +4148,101 @@ const tech = { tech.isIceShot = false; } }, + { + name: "freezer burn", + description: "mobs frozen while below 33% durability die", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 + }, + requires: "a freeze effect", + effect() { + tech.isIceKill = true + }, + remove() { + tech.isIceKill = false + } + }, + { + name: "flash freeze", + description: "mobs frozen while above 66% durability
have their durability reduced to 66%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 + }, + requires: "a freeze effect", + effect() { + tech.isIceMaxHealthLoss = true + }, + remove() { + tech.isIceMaxHealthLoss = false + } + }, + { + name: "crystallizer", + description: "after frozen mobs die they
shatter into ice IX crystals", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob + }, + requires: "a localized freeze effect, no other mob death tech", + effect() { + tech.iceIXOnDeath++ + }, + remove() { + tech.iceIXOnDeath = 0 + } + }, + { + name: "thermoelectric effect", + description: "killing mobs with ice IX
generates 100 energy", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.blockingIce || tech.iceIXOnDeath || tech.isIceShot + }, + requires: "ice IX", + effect() { + tech.iceEnergy++ + }, + remove() { + tech.iceEnergy = 0; + } + }, + { + name: "superfluidity", + description: "freeze effects are applied
to a small area around the target", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 || tech.iceIXOnDeath || tech.isIceShot + }, + requires: "a localized freeze effect", + effect() { + tech.isAoESlow = true + }, + remove() { + tech.isAoESlow = false + } + }, { name: "incendiary ammunition", description: "shotgun, rivets, super balls, and drones
are loaded with explosives", @@ -4687,26 +4711,26 @@ const tech = { }, { name: "shock wave", - description: "mines and explosions stun for 1-2 seconds
–30% explosive damage", + description: "mines and sporangium stun for 3-5 seconds
explosions stun for 0.5 seconds", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return tech.haveGunCheck("mine") || (!tech.isExplodeRadio && tech.hasExplosiveDamageCheck()) + return tech.haveGunCheck("spores") || tech.haveGunCheck("mine") || (!tech.isExplodeRadio && tech.hasExplosiveDamageCheck()) }, - requires: "an explosive damage source, not iridium-192", + requires: "mine, spores, an explosive damage source, not iridium-192", effect() { - tech.isExplosionStun = true; + tech.isStun = true; }, remove() { - tech.isExplosionStun = false; + tech.isStun = false; } }, { name: "controlled explosion", - description: `use ${powerUps.orb.research(4)} to dynamically reduce
all explosive radius to prevent health loss`, + description: `use ${powerUps.orb.research(4)} to dynamically reduce
all explosions to prevent health loss`, isGunTech: true, maxCount: 1, count: 0, @@ -5067,24 +5091,43 @@ const tech = { tech.isSporeGrowth = false } }, + // { + // name: "tinsellated flagella", + // link: `tinsellated flagella`, + // description: "sporangium release +2 spores
spores accelerate 50% faster", + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField + // }, + // requires: "spores", + // effect() { + // tech.isFastSpores = true + // }, + // remove() { + // tech.isFastSpores = false + // } + // }, { - name: "tinsellated flagella", - link: `tinsellated flagella`, - description: "sporangium release +2 spores
spores accelerate 50% faster", + name: "flagella", + description: "+50% spores acceleration
if they can't find a target spores follow you", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm }, requires: "spores", effect() { - tech.isFastSpores = true + tech.isSporeFollow = true //isSporeFollow }, remove() { - tech.isFastSpores = false + tech.isSporeFollow = false //isFastSpores } }, { @@ -5107,25 +5150,25 @@ const tech = { tech.isSporeFreeze = false } }, - { - name: "diplochory", - description: "if spores can't locate a viable host
they use you for dispersal", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm - }, - requires: "spores", - effect() { - tech.isSporeFollow = true - }, - remove() { - tech.isSporeFollow = false - } - }, + // { + // name: "diplochory", + // description: "if spores can't locate a viable host
they use you for dispersal", + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm + // }, + // requires: "spores", + // effect() { + // tech.isSporeFollow = true + // }, + // remove() { + // tech.isSporeFollow = false + // } + // }, { name: "mutualism", description: "+150% spore damage
spores borrow 0.5 health until they die", @@ -7023,7 +7066,7 @@ const tech = { }, { name: "time crystals", - description: "+400% passive energy generation", + description: "+300% passive energy generation", isFieldTech: true, maxCount: 1, count: 0, @@ -8958,6 +9001,30 @@ const tech = { }, remove() {} }, + { + name: "reincarnation", + description: "kill all mobs and spawn new ones
(also spawn a few extra mobs for fun)", + maxCount: 3, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + spawn.setSpawnList(); + spawn.setSpawnList(); + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].alive && !mob[i].shield && !mob[i].isBadTarget) { + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](mob[i].position.x, mob[i].position.y); + if (Math.random() < 0.5) spawn[pick](mob[i].position.x, mob[i].position.y); + mob[i].death(); + } + } + }, + remove() {} + }, { name: "expert system", description: "spawn a tech power up
+64% JUNK to tech pool", @@ -9902,7 +9969,6 @@ const tech = { isBlockRadiation: null, isPiezo: null, isFastDrones: null, - isFastSpores: null, oneSuperBall: null, laserReflections: null, laserDamage: null, @@ -9934,7 +10000,7 @@ const tech = { isSporeFollow: null, isNailRadiation: null, isEnergyHealth: null, - isExplosionStun: null, + isStun: null, restDamage: null, isRPG: null, missileCount: null, @@ -10179,5 +10245,6 @@ const tech = { isPetalsExplode: null, isDeathSkipTime: null, isIceMaxHealthLoss: null, - isIceKill: null + isIceKill: null, + isCritKill: null } \ No newline at end of file diff --git a/todo.txt b/todo.txt index ef894fc..c102707 100644 --- a/todo.txt +++ b/todo.txt @@ -1,24 +1,63 @@ ******************************************************** NEXT PATCH ************************************************** +nail gun tech: stress concentration - if a mob has below 50% durability after taking damage + from needles or rivets near the center of it's body it dies + +caliber 16->25% nail, needle, rivet size/damage +combined tech: flagella - spores move +50% faster + spores follow you if they can't find a target +shock wave stun also applies to sporangium + no longer reduces explosion size + +JUNK tech: reincarnation - kill all mobs and spawn new ones + (also spawn a few extra mobs for fun) updated pause menu and fields descriptions to new wording style +bug fixes + *********************************************************** TODO ***************************************************** +Tech: florescence +Hitting a mob makes it emit lasers from its vertices + all laser but pulse? + should trigger a global cd so it can't occur more then once per cycle + +tech spawn bots that last until you get hit + bots can check you health every cycle and see if it lower + it it's high update the health check value + JUNK? + +after killing a mob go invulnerable for 1 second + critical hit with laser, harpoon, nails, needle, rivet + +spawn 2 bots after exiting 1 level + or spawn 3 after 2 levels? + (randomize 2-5 bots) + replace all bot tech with this? + +cloaking field + for 6 seconds after de-cloaking tell all active mobs that the player is in the wrong position? + how to code? + just delay setting the m.isCloak for a couple seconds + and also set all active bots to remember player in the de-cloaked stop + +give laser gun _____ if you fire in an angle range + draw angle range as a slowly rotation circle arc around player + effect: + bonus damage + extra beams + extra reflections + scrap bots can't move? only works for nail, foam, laser might be tricky code? make a new bot type called scrap bot? -nail gun needs a small damage buff - JUNK tech: Pacifism You cannot attack mobs, mobs cannot attack you over write the mob.damage and player.damage methods -JUNK tech: incubation: spawn something after 5 minutes - 4 bots? - mob mechanic: beacon periodically add locations to an array teleport back to a location in the array