From 4e29a517fc097cf8b5a7630826dcb944f4dc8661 Mon Sep 17 00:00:00 2001 From: landgreen Date: Tue, 12 Jul 2022 07:52:13 -0700 Subject: [PATCH] fleas tech: fleas - replace spores with little hoppers frame-dragging - time dilation field stops time when you aren't moving or firing, +33% defense the odds of finding this tech is low because I find it kinda annoying, but maybe you will like it molecular assembler field energy meter is yellow wormhole is lavender perfect diamagnetism is blue time dilation is green blue pilot wave is black new room in labs: hopBossMom harpoon now auto targets by default, but disabled when crouched pulse + neocognitron auto targeting also disabled when crouched bug fixes --- .DS_Store | Bin 6148 -> 6148 bytes js/bullet.js | 200 +++++++++++++++++++++++++++++++++++------ js/index.js | 7 +- js/level.js | 169 ++++++++++++++++++++++++++++------- js/player.js | 143 ++++++++++++++++++++---------- js/powerup.js | 5 +- js/simulation.js | 56 ++++++++++++ js/spawn.js | 226 ++++++++++++++++++++++++++++++----------------- js/tech.js | 139 ++++++++++++++++++++--------- todo.txt | 63 +++++++------ 10 files changed, 738 insertions(+), 270 deletions(-) diff --git a/.DS_Store b/.DS_Store index 95a0799477da40b0778680f298a30d3066f70ee7..32b9bd676a56e05e6b5b8fdc81a31b91f77d7bbc 100644 GIT binary patch delta 104 zcmZoMXffEJ$;_;}{Lo|_W?v>Xfyvd(lD6*x7#J9s8FCmh8Ip4IU0jlK@{@of9E(z# k%9olRaYU9)L6@C;ky(O?NnrCwW-*qD1#FwyIsWnk0QEZ_5C8xG delta 104 zcmZoMXffEJ$;_;5_++vUvoDiM!sKdZN!w~81_lOZh8%`WhNRql7nh`*{3M_V$JN%% kM?X9cJ0i=bpvz9a$SlFcxM1@~W-*qD1#FwyIsWnk02{m@8~^|S diff --git a/js/bullet.js b/js/bullet.js index 535b65f..beff0dd 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -583,7 +583,7 @@ const b = { v1: null, v2: null }; - if (tech.isPulseAim) { //find mobs in line of sight + if (tech.isPulseAim && input.down) { //find mobs in line of sight let dist = 2200 for (let i = 0, len = mob.length; i < len; i++) { const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position)) @@ -2386,9 +2386,9 @@ const b = { if (best.who.alive) { best.who.locatePlayer(); if (best.who.damageReduction) { - if ( //crit + if ( //iridescence tech.laserCrit && !best.who.shield && - Vector.dot(Vector.normalise(Vector.sub(best.who.position, path[path.length - 1])), Vector.normalise(Vector.sub(path[path.length - 1], path[path.length - 2]))) > 0.99 - 0.6 / best.who.radius + Vector.dot(Vector.normalise(Vector.sub(best.who.position, path[path.length - 1])), Vector.normalise(Vector.sub(path[path.length - 1], path[path.length - 2]))) > 0.995 - 0.6 / best.who.radius ) { damage *= 1 + tech.laserCrit simulation.drawList.push({ //add dmg to draw queue @@ -2663,8 +2663,7 @@ const b = { !mob[i].isBadTarget && Vector.magnitude(Vector.sub(this.position, mob[i].position)) < 700 + mob[i].radius + random && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0 && - !mob[i].isInvulnerable + Matter.Query.ray(body, this.position, mob[i].position).length === 0 ) { if (tech.isStun) b.AoEStunEffect(this.position, 700 + mob[i].radius + random); //AoEStunEffect(where, range, cycles = 90 + 60 * Math.random()) { if (tech.isMineSentry) { @@ -2961,7 +2960,7 @@ const b = { friction: 0, frictionAir: 0.023, restitution: 0.9, - dmg: 1, //damage done in addition to the damage from momentum + dmg: 1.2, //damage done in addition to the damage from momentum lookFrequency: 14 + Math.floor(8 * Math.random()), endCycle: simulation.cycle + 100 * tech.isBulletsLastLonger + Math.floor(25 * Math.random()), classType: "bullet", @@ -3022,6 +3021,128 @@ const b = { // y: m.Vy / 2 + speed * Math.sin(dir) // }); }, + flea(where, velocity, radius = 7 + 3 * Math.random()) { + const me = bullet.length; + bullet[me] = Bodies.polygon(where.x, where.y, 5, radius, { + isFlea: true, + angle: 0.5 * Math.random(), + friction: 1, + frictionStatic: 1, + frictionAir: 0, //0.01, + restitution: 0, + density: 0.0005, // 0.001 is normal density + dmg: 9 * (tech.isMutualism ? 2.5 : 1), //damage done in addition to the damage from momentum //spores do 7 dmg, worms do 18 + lookFrequency: 19 + Math.floor(11 * Math.random()), + endCycle: simulation.cycle + Math.floor((780 * tech.isBulletsLastLonger + 360 * Math.random()) + Math.max(0, 150 - bullet.length)), // 13 - 19s + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield + }, + minDmgSpeed: 0, + lockedOn: null, + delay: 50, + cd: simulation.cycle + 10, + beforeDmg(who) { + // this.endCycle = 0 + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), 10 + 10 * Math.random())); //push away from target + + this.endCycle -= 180 + this.cd = simulation.cycle + this.delay; + // this.collisionFilter.mask = cat.map + if (tech.isSporeFreeze) mobs.statusSlow(who, 90) + if (tech.isSpawnBulletsOnDeath && who.alive && who.isDropPowerUp) { + setTimeout(() => { + if (!who.alive) { + for (let i = 0; i < 3; i++) { //spawn 3 more + const speed = 10 + 5 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) }) + } + } + this.endCycle = 0; + }, 1); + } + }, + onEnd() {}, + gravity: 0.002 + 0.002 * tech.isSporeFollow, + do() { + // if (true && this.lockedOn && this.cd < simulation.cycle) { //blink towards mobs + // //needs it's own cooldown variables + // // this.cd = simulation.cycle + this.delay; + + // const sub = Vector.sub(this.lockedOn.position, this.position); + // const distMag = Vector.magnitude(sub); + // if (distMag < 500) { + // const unit = Vector.normalise(sub) + // Matter.Body.setVelocity(this, Vector.mult(unit, Math.max(20, this.speed * 1.5))); + // ctx.beginPath(); + // ctx.moveTo(this.position.x, this.position.y); + // Matter.Body.translate(this, Vector.mult(unit, Math.min(350, distMag - this.lockedOn.radius + 10))); + // ctx.lineTo(this.position.x, this.position.y); + // ctx.lineWidth = radius * 2; + // ctx.strokeStyle = "rgba(0,0,0,0.5)"; + // ctx.stroke(); + // } + // } + + this.force.y += this.gravity * this.mass + if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { //if on the ground and not on jump cooldown // + // this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield + + this.cd = simulation.cycle + this.delay; + this.lockedOn = null; //find a target + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + !mob[i].isBadTarget && + !mob[i].isInvulnerable && + mob[i].alive && + this.position.y - mob[i].position.y < 1500 && //this is about how high fleas can jump with capMaxY = 0.12 + 0.04 * Math.random() + this.position.y - mob[i].position.y > -300 && //not too far below the flea (note that fleas should be on the ground most of the time when doing this check) + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + ) { + const TARGET_VECTOR = Vector.sub(this.position, mob[i].position) + const DIST = Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } + } + if (tech.isSporeFollow && !this.lockedOn && Matter.Query.ray(map, this.position, m.pos).length === 0) { + this.lockedOn = { //make target player if there are no mobs to target + position: m.pos, + velocity: { x: 0, y: 0 } + } + } + if (this.lockedOn) { //hop towards mob target + const where = Vector.add(this.lockedOn.position, Vector.mult(this.lockedOn.velocity, 5)) //estimate where the mob will be in 5 cycles + const Dy = Math.max(0, this.position.y - where.y) //can't be negative because you can't hop down + const Dx = this.position.x - where.x + const Vx = -0.06 * Dx / Math.sqrt(2 * Dy / this.gravity) //calibrated to hit target, don't mess with this + const Vy = 0.085 * Math.sqrt(this.gravity * Dy) //calibrated to hit target, don't mess with this + const capX = 0.07 + 0.02 * tech.isSporeFollow + const capMaxY = 0.12 + 0.04 * Math.random() + 0.05 * tech.isSporeFollow + const capMinY = closeDist > 500 ? 0.05 + 0.02 * Math.random() : 0.02 + 0.01 * Math.random() //don't jump super low, unless you are very close to mob target + this.force.x = Math.max(-capX, Math.min(capX, Vx)) * this.mass; + this.force.y = -Math.max(capMinY, Math.min(capMaxY, Vy)) * this.mass + } else { //random hops + if (Math.random() < 0.5) { //chance to continue in the same horizontal direction + this.force.x = (0.01 + 0.03 * Math.random()) * this.mass * (this.velocity.x > 0 ? 1 : -1); //random move + } else { + this.force.x = (0.01 + 0.03 * Math.random()) * this.mass * (Math.random() < 0.5 ? 1 : -1); //random move + } + this.force.y = -(0.03 + 0.08 * Math.random()) * this.mass + } + Matter.Body.setVelocity(this, { x: 0, y: 0 }); + } + } + }) + Composite.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], velocity); + }, drone(where = { x: m.pos.x + 30 * Math.cos(m.angle) + 20 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 20 * (Math.random() - 0.5) }, speed = 1) { const me = bullet.length; const THRUST = 0.0015 @@ -3774,7 +3895,13 @@ const b = { this.force.y += this.mass * tech.foamGravity; //gravity if (tech.isFoamAttract) { for (let i = 0, len = mob.length; i < len; i++) { - if (!mob[i].isBadTarget && Vector.magnitude(Vector.sub(mob[i].position, this.position)) < 375 && mob[i].alive && Matter.Query.ray(map, this.position, mob[i].position).length === 0) { + if ( + !mob[i].isBadTarget && + Vector.magnitude(Vector.sub(mob[i].position, this.position)) < 375 && + mob[i].alive && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { this.force = Vector.mult(Vector.normalise(Vector.sub(mob[i].position, this.position)), this.mass * 0.004) const slow = 0.9 Matter.Body.setVelocity(this, { @@ -3800,7 +3927,7 @@ const b = { targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { let closestMob, dist for (let i = 0, len = mob.length; i < len; i++) { - if (who !== mob[i] && !mob[i].isBadTarget) { + if (who !== mob[i] && !mob[i].isBadTarget && !mob[i].isInvulnerable) { dist = Vector.magnitude(Vector.sub(who.position, mob[i].position)); if (dist < range && Matter.Query.ray(map, who.position, mob[i].position).length === 0) { //&& Matter.Query.ray(body, position, mob[i].position).length === 0 closestMob = mob[i] @@ -4331,7 +4458,7 @@ const b = { minDmgSpeed: 2, // lookFrequency: 56 + Math.floor(17 * Math.random()) - isUpgraded * 20, lastLookCycle: simulation.cycle + 60 * Math.random(), - delay: Math.floor((tech.isNailBotUpgrade ? 21 : 110) * b.fireCDscale), + delay: Math.floor((tech.isNailBotUpgrade ? 20 : 105) * b.fireCDscale), acceleration: 0.005 * (1 + 0.5 * Math.random()), range: 60 * (1 + 0.3 * Math.random()) + 3 * b.totalBots(), endCycle: Infinity, @@ -4492,7 +4619,7 @@ const b = { let target for (let i = 0, len = mob.length; i < len; i++) { const dist2 = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); - if (dist2 < 1000000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0) { + if (dist2 < 1000000 && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && !mob[i].isInvulnerable) { this.cd = simulation.cycle + this.delay; target = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist2) / 60)) const radius = 6 + 7 * Math.random() @@ -4523,10 +4650,7 @@ const b = { restitution: 0.5 * (1 + 0.5 * Math.random()), acceleration: 0.0015 * (1 + 0.3 * Math.random()), playerRange: 140 + Math.floor(30 * Math.random()) + 2 * b.totalBots(), - offPlayer: { - x: 0, - y: 0, - }, + offPlayer: { x: 0, y: 0, }, dmg: 0, //damage done in addition to the damage from momentum minDmgSpeed: 2, lookFrequency: 40 + Math.floor(7 * Math.random()) - 10 * tech.isLaserBotUpgrade, @@ -4562,11 +4686,14 @@ const b = { let closeDist = this.range; for (let i = 0, len = mob.length; i < len; ++i) { const DIST = Vector.magnitude(Vector.sub(this.vertices[0], mob[i].position)); - if (DIST - mob[i].radius < closeDist && + if ( + DIST - mob[i].radius < closeDist && !mob[i].isShielded && (!mob[i].isBadTarget || mob[i].isMobBullet) && Matter.Query.ray(map, this.vertices[0], mob[i].position).length === 0 && - Matter.Query.ray(body, this.vertices[0], mob[i].position).length === 0) { + Matter.Query.ray(body, this.vertices[0], mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { closeDist = DIST; this.lockedOn = mob[i] } @@ -4686,10 +4813,13 @@ const b = { let closeDist = this.range; for (let i = 0, len = mob.length; i < len; ++i) { const DIST = Vector.magnitude(Vector.sub(this.position, mob[i].position)) - mob[i].radius; - if (DIST < closeDist && + if ( + DIST < closeDist && !mob[i].isBadTarget && Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0) { + Matter.Query.ray(body, this.position, mob[i].position).length === 0 && + !mob[i].isInvulnerable + ) { closeDist = DIST; this.lockedOn = mob[i] } @@ -4842,7 +4972,7 @@ const b = { y: best.y }; if (best.who.alive) { - const dmg = 0.6 * m.dmgScale; //********** SCALE DAMAGE HERE ********************* + const dmg = 0.65 * m.dmgScale; //********** SCALE DAMAGE HERE ********************* best.who.damage(dmg); best.who.locatePlayer(); //push mobs away @@ -4948,7 +5078,7 @@ const b = { }) for (let i = 0; i < q.length; i++) { if (!q[i].isShielded) { - mobs.statusStun(q[i], 180) + mobs.statusStun(q[i], 210) const dmg = 0.4 * m.dmgScale * (this.isUpgraded ? 4 : 1) * (tech.isCrit ? 4 : 1) q[i].damage(dmg); if (q[i].alive) q[i].foundPlayer(); @@ -6273,12 +6403,17 @@ const b = { this.stuck(); //runs different code based on what the bullet is stuck to let scale = 1.01 if (tech.isSporeGrowth && !(simulation.cycle % 40)) { //release a spore - if (tech.isSporeWorm) { + if (tech.isSporeFlea) { + if (!(simulation.cycle % 80)) { + const speed = 10 + 5 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) }) + } + } else if (tech.isSporeWorm) { if (!(simulation.cycle % 80)) b.worm(this.position) } else { b.spore(this.position) } - // this.totalSpores-- scale = 0.96 if (this.stuckTo && this.stuckTo.alive) scale = 0.9 Matter.Body.scale(this, scale, scale); @@ -6289,18 +6424,21 @@ const b = { this.radius *= scale if (this.radius > this.maxRadius) this.endCycle = 0; } - // this.force.y += this.mass * 0.00045; - //draw green glow ctx.fillStyle = "rgba(0,200,125,0.16)"; ctx.beginPath(); ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI); ctx.fill(); }; - //spawn bullets on end bullet[me].onEnd = function() { - if (tech.isSporeWorm) { + if (tech.isSporeFlea) { + for (let i = 0, len = this.totalSpores * 0.5; i < len; i++) { + const speed = 10 + 5 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) }) + } + } else if (tech.isSporeWorm) { for (let i = 0, len = this.totalSpores * 0.5; i < len; i++) b.worm(this.position) } else { for (let i = 0; i < this.totalSpores; i++) b.spore(this.position) @@ -6549,7 +6687,7 @@ const b = { if (dot > 0.95 - Math.min(dist * 0.00015, 0.3)) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target // if (this.ammo > -1) { // this.ammo-- - b.harpoon(where, input.down ? mob[i] : null, angle, harpoonSize, false) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 }) + b.harpoon(where, input.down ? null : mob[i], angle, harpoonSize, false) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 }) angle += SPREAD targetCount++ if (targetCount > tech.extraHarpoons) break @@ -6574,7 +6712,7 @@ const b = { //look for closest mob in player's LoS const dir = { x: Math.cos(m.angle), y: Math.sin(m.angle) }; //make a vector for the player's direction of length 1; used in dot product for (let i = 0, len = mob.length; i < len; ++i) { - if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0) { + if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0 && !mob[i].isInvulnerable) { const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target @@ -7066,7 +7204,11 @@ const b = { fire() {}, chooseFireMethod() { this.lensDamage = 1 - if (tech.isLaserLens) this.do = this.lens + if (tech.isLaserLens) { + this.do = this.lens + } else { + this.do = this.stuckOn + } if (tech.isPulseLaser) { this.fire = () => { const drain = 0.01 * tech.isLaserDiode * (tech.isCapacitor ? 10 : 1) diff --git a/js/index.js b/js/index.js index 76e4124..381303b 100644 --- a/js/index.js +++ b/js/index.js @@ -263,9 +263,8 @@ ${simulation.isCheating ? "

lore disabled": ""} text += `
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
${b.guns[b.inventory[i]].description}
` } let el = document.getElementById("pause-grid-left") - el.style.display = "grid" + el.style.display = tech.isNoDraftPause ? "none" : "grid" //disabled for eternalism because eternalism lets the player play while this menu is up but the menu doesn't update el.innerHTML = text - //right side text = ""; if (tech.isPauseSwitchField && !simulation.isChoosing) { @@ -304,7 +303,7 @@ ${simulation.isCheating ? "

lore disabled": ""} } } el = document.getElementById("pause-grid-right") - el.style.display = "grid" + el.style.display = tech.isNoDraftPause ? "none" : "grid" //disabled for eternalism because eternalism lets the player play while this menu is up but the menu doesn't update el.innerHTML = text document.getElementById("tech").style.display = "none" @@ -933,7 +932,7 @@ window.addEventListener("keydown", function(event) { if (m.alive && localSettings.loreCount > 0) { if (simulation.difficultyMode > 4) { simulation.makeTextLog("testing mode disabled for this difficulty"); - break + // break } if (simulation.testing) { simulation.testing = false; diff --git a/js/level.js b/js/level.js index 93aa7ec..2d38281 100644 --- a/js/level.js +++ b/js/level.js @@ -15,40 +15,31 @@ const level = { levels: [], start() { if (level.levelsCleared === 0) { //this code only runs on the first level + // simulation.enableConstructMode() //used to build maps in testing mode // simulation.isHorizontalFlipped = true // m.addHealth(Infinity) - // m.setField("time dilation") - // b.giveGuns("laser") + // m.setField("molecular assembler") + // b.giveGuns("spores") + // tech.giveTech("fleas") + // tech.giveTech("flagella") // b.guns[0].ammo = 10000 - // // b.giveGuns("mine") - // tech.giveTech("lens") - // for (let i = 0; i < 2; ++i) tech.giveTech("diffraction grating") - // for (let i = 0; i < 9; ++i) tech.giveTech("propagator") + // for (let i = 0; i < 1; ++i) tech.giveTech("mycelium manufacturing") + // for (let i = 0; i < 9; ++i) tech.giveTech("WIMPs") // for (let i = 0; i < 100; ++i) tech.giveTech("nail-bot") - // for (let i = 0; i < 9; ++i) tech.giveTech("emergence") - // tech.giveTech("laser-bot") - // tech.giveTech("slow light") - // tech.giveTech("iridescence") - // m.maxHealth = 100 - // m.health = m.maxHealth - // for (let i = 0; i < 10; i++) tech.giveTech("tungsten carbide") + // for (let i = 0; i < 1; ++i) tech.giveTech("necrophage") + // for (let i = 0; i < 1; i++) tech.giveTech("cryodesiccation") // for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "tech"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "research"); - // for (let i = 0; i < 15; i++) tech.giveTech() - // for (let i = 10; i < tech.tech.length; i++) { tech.tech[i].isBanished = true } + // m.maxHealth = m.health = 100 // powerUps.research.changeRerolls(100000) - // tech.tech[297].frequency = 100 // m.immuneCycle = Infinity //you can't take damage - // simulation.enableConstructMode() //used to build maps in testing mode - // level.temple(); - // spawn.cellBossCulture(1900, -500) // powerUps.research.changeRerolls(100) - // spawn.starter(1900, -500, 40) - // spawn.starter(1900, -500, 20) - // spawn.starter(1900, -500, 100) - // for (let i = 0; i < 20; ++i) spawn.exploder(1900, -500) - // spawn.timeSkipBoss(1900, -500) - // level.difficultyIncrease(50) //30 is near max on hard //60 is near max on why + // tech.tech[297].frequency = 100 + // spawn.starter(1900, -500, 200) + // for (let i = 0; i < 10; ++i) spawn.hopBullet(1900, -500) + // spawn.hopMomBoss(1900, -500) + // spawn.grenadier(1900, -1450, 10) + // level.difficultyIncrease(8 * 4) //30 is near max on hard //60 is near max on why // level.testing(); //not in rotation, used for testing // for (let i = 0; i < 7; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research"); // for (let i = 0; i < 4; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech"); @@ -2215,11 +2206,124 @@ const level = { // spawn.randomLevelBoss(x + 950, y + -2200); // }, + // (x = offset.x, y = offset.y) => { //hopBoss1 + // const button = level.button(x + 935, y + 0) + // button.isUp = true + // // spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges + // // spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges + // doCustomTopLayer.push( + // () => { + // button.draw(); + // if (button.isUp) { + // button.query(); + // if (!button.isUp) { + // // doCustomTopLayer.push(() => { + // // ctx.fillStyle = "rgba(150,255,220,0.15)" + // // ctx.fillRect(x + 250, y + -2725, 625, 725) + // // }) + // const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress + // addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually + // who.collisionFilter.category = cat.map; + // who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + // Matter.Body.setStatic(who, true); //make static + // Composite.add(engine.world, who); //add to world + // } + // //map elements go here + // // spawn.mapRect(x + -50, y + -1875, 875, 200); + // // spawn.mapRect(x + 650, y + -2700, 125, 625); + // // spawn.mapRect(x + 1200, y + -2250, 250, 25); + + // spawn.mapRect(x + -25, y + -1875, 1250, 200); + // // spawn.mapRect(x + 1075, y + -2700, 100, 650); + // spawn.mapRect(x + 1325, y + -1875, 475, 200); + // // spawn.mapRect(x + 1900, y + -1600, 125, 25); + // // spawn.mapRect(x + 900, y + -1875, 325, 25); + // // spawn.mapRect(x + 1375, y + -1875, 350, 25); + + // // spawn.mapRect(x + 675, y + -2725, 50, 650); + // spawn.mapRect(x + 1900, y + -1675, 125, 25); + // spawn.mapRect(x + 1700, y + -1400, 325, 25); + // spawn.mapRect(x + -50, y + -1400, 325, 25); + + + // spawn.mapRect(x + -25, y + -700, 500, 25); + // spawn.mapRect(x + 675, y + -700, 600, 25); + // spawn.mapRect(x + 1475, y + -700, 500, 25); + + // spawn.mapRect(x + 475, y + -1025, 200, 25); + // spawn.mapRect(x + 1275, y + -1025, 200, 25); + // spawn.mapRect(x + 475, y + -300, 200, 25); + // spawn.mapRect(x + 1275, y + -300, 200, 25); + + + // for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) + // simulation.draw.setPaths() //update map graphics + // //mobs go here + // powerUps.directSpawn(x + 50, y - 1525, "ammo"); + // powerUps.directSpawn(x + 1950, y - 1525, "ammo"); + // spawn.hopMomBoss(x + 550, y + -2325) + // for (let i = 0; i < 20; ++i) spawn.hopBullet(x + 50 + 1900 * Math.random(), y + -2325) + // // spawn.hopper(x + 1500, y + -775); + // // spawn.hopper(x + 525, y + -775); + // } + // } + // } + // ) + // }, + (x = offset.x, y = offset.y) => { //hopBoss2 + const button = level.button(x + 935, y + 0) + button.isUp = true + // spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges + // spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges + doCustomTopLayer.push( + () => { + button.draw(); + if (button.isUp) { + button.query(); + if (!button.isUp) { + const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress + addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually + who.collisionFilter.category = cat.map; + who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(who, true); //make static + Composite.add(engine.world, who); //add to world + } + //map elements go here + spawn.mapRect(x + 150, y + -1400, 750, 50); + spawn.mapRect(x + 1100, y + -1400, 750, 50); + spawn.mapRect(x + 1825, y + -1050, 200, 50); + spawn.mapRect(x + -25, y + -1050, 200, 50); + spawn.mapRect(x + 1825, y + -325, 200, 50); + spawn.mapRect(x + -25, y + -325, 200, 50); + spawn.mapRect(x + 275, y + -700, 525, 50); + spawn.mapRect(x + 1200, y + -700, 525, 50); + spawn.mapRect(x + -25, y + -1400, 125, 1125); //side walls + spawn.mapRect(x + 1900, y + -1400, 150, 1125); + spawn.mapRect(x + 1900, y + -2700, 125, 1000); + spawn.mapRect(x + -50, y + -2725, 150, 1025); + spawn.mapRect(x + -25, y + -1750, 450, 50); + spawn.mapRect(x + 1575, y + -1750, 450, 50); + spawn.mapRect(x + 525, y + -1750, 950, 50); + for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) + simulation.draw.setPaths() //update map graphics + //mobs go here + powerUps.directSpawn(x + 50, y - 1525, "ammo"); + powerUps.directSpawn(x + 1950, y - 1525, "ammo"); + spawn.hopMomBoss(x + 800, y + -2200) + for (let i = 0; i < 10; ++i) spawn.hopBullet(x + 150 + 750 * Math.random(), y + -1600) + for (let i = 0; i < 10; ++i) spawn.hopBullet(x + 1100 + 750 * Math.random(), y + -1600) + spawn.hopper(x + 1500, y + -775); + spawn.hopper(x + 525, y + -775); + } + } + } + ) + }, (x = offset.x, y = offset.y) => { // const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) { // toggle.isAddedElements = false - const button = level.button(x + 950, y + 0) + const button = level.button(x + 935, y + 0) button.isUp = true @@ -2310,7 +2414,7 @@ const level = { (x = offset.x, y = offset.y) => { // const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) { // toggle.isAddedElements = false - const button = level.button(x + 950, y + 0) + const button = level.button(x + 935, y + 0) button.isUp = true //left ledges spawn.mapVertex(x + 5, y + -1868, "0 0 0 -250 125 -250"); @@ -2412,7 +2516,7 @@ const level = { empty = emptyOptions[Math.floor(Math.random() * emptyOptions.length)]; loot = lootOptions[Math.floor(Math.random() * lootOptions.length)]; upDown = upDownOptions[Math.floor(Math.random() * upDownOptions.length)]; - // upDown = upDownOptions[1] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION *************************************** + // upDown = upDownOptions[0] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION *************************************** //3x2: 4 short rooms (3000x1500), 1 double tall room (3000x3000) //rooms let rooms = ["exit", "loot", "enter", "empty"] @@ -2661,7 +2765,7 @@ const level = { document.body.style.backgroundColor = "#ddd"; spawn.mapRect(-950, 0, 8200, 800); //ground spawn.mapRect(-950, -1200, 800, 1400); //left wall - spawn.mapRect(-950, -1800, 8200, 800); //roof + // spawn.mapRect(-950, -1800, 8200, 800); //roof spawn.mapRect(-250, -400, 1000, 600); // shelf spawn.mapRect(-250, -1200, 1000, 550); // shelf roof // for (let i = 0; i < 10; ++i) powerUps.spawn(550, -800, "ammo", false); @@ -2968,10 +3072,10 @@ const level = { }; level.setPosToSpawn(0, -250); //normal spawn - spawn.mapRect(5500, -330 + 20, 100, 20); //spawn this because the real exit is in the wrong spot spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); - level.exit.x = 550000; - level.exit.y = -330; + spawn.mapRect(5500, -330 + 20, 100, 20); //spawn this because the real exit is in the wrong spot + level.exit.x = 0; + level.exit.y = -8000; level.defaultZoom = 2500 simulation.zoomTransition(level.defaultZoom) @@ -7892,6 +7996,7 @@ const level = { spawn.bodyRect(-2100, 2050, 290, 30) //Portal platform let b = body[body.length - 1]; + b.isNotHoldable = true cons[cons.length] = Constraint.create({ pointA: { x: -1820, diff --git a/js/player.js b/js/player.js index 0840f95..171162a 100644 --- a/js/player.js +++ b/js/player.js @@ -1658,6 +1658,9 @@ const m = { // description: "attract power ups from far away
deflecting doesn't drain energy
thrown blocks have", // description: "gain energy when blocking
no recoil when blocking", effect: () => { + m.fieldMeterColor = "#48f" //"#0c5" + m.eyeFillColor = m.fieldMeterColor + m.fieldShieldingScale = 0; m.fieldBlockCD = 3; m.grabPowerUpRange2 = 10000000 @@ -1724,9 +1727,8 @@ const m = { ctx.strokeStyle = "#f0f"; ctx.stroke(); } else if (isFree) { - //when blocking draw this graphic - ctx.fillStyle = "rgba(110,170,200," + (0.2 + 0.4 * Math.random()) + ")"; - ctx.lineWidth = 2; + ctx.lineWidth = 2; //when blocking draw this graphic + ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` ctx.strokeStyle = "#000"; const len = mob[i].vertices.length - 1; const mag = mob[i].radius @@ -1739,11 +1741,11 @@ const m = { ctx.fill(); ctx.stroke(); } else { - //when blocking draw this graphic - const eye = 15; + + const eye = 15; //when blocking draw this graphic const len = mob[i].vertices.length - 1; - ctx.fillStyle = "rgba(110,170,200," + (0.2 + 0.4 * Math.random()) + ")"; ctx.lineWidth = 1; + ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` ctx.strokeStyle = "#000"; ctx.beginPath(); ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); @@ -1800,11 +1802,11 @@ const m = { m.fieldAngle = m.angle //draw field attached to player if (m.holdingTarget) { - ctx.fillStyle = "rgba(110,170,200," + (0.06 + 0.03 * Math.random()) + ")"; - ctx.strokeStyle = "rgba(110, 200, 235, " + (0.35 + 0.05 * Math.random()) + ")" + ctx.fillStyle = `rgba(110,150,220, ${0.06 + 0.03 * Math.random()})` + ctx.strokeStyle = `rgba(110,150,220, ${0.35 + 0.05 * Math.random()})` } else { - ctx.fillStyle = "rgba(110,170,200," + (0.27 + 0.2 * Math.random() - 0.1 * wave) + ")"; - ctx.strokeStyle = "rgba(110, 200, 235, " + (0.4 + 0.5 * Math.random()) + ")" + ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` + ctx.strokeStyle = `rgba(110,150,220, ${0.4 + 0.5 * Math.random()})` } ctx.beginPath(); ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false); @@ -1828,8 +1830,8 @@ const m = { m.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 (!input.field) { //&& tech.isFieldFree //draw field free of player - ctx.fillStyle = "rgba(110,170,200," + (0.27 + 0.2 * Math.random() - 0.1 * wave) + ")"; - ctx.strokeStyle = "rgba(110, 200, 235, " + (0.4 + 0.5 * Math.random()) + ")" + ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` + ctx.strokeStyle = `rgba(110,180,255, ${0.4 + 0.5 * Math.random()})` ctx.beginPath(); ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false); ctx.lineWidth = 2.5 - 1.5 * wave; @@ -1842,7 +1844,8 @@ const m = { m.perfectPush(true); } } - m.drawFieldMeter() + // m.drawFieldMeter() + m.drawFieldMeter("rgba(0,0,0,0.2)") if (tech.isPerfectBrake) { //cap mob speed around player const range = 200 + 140 * wave + 150 * m.energy for (let i = 0; i < mob.length; i++) { @@ -2018,17 +2021,19 @@ const m = { description: "excess energy used to build drones
use energy to deflect mobs
generate 12 energy per second", //double your default energy regeneration effect: () => { - // m.fieldMeterColor = "#0c5" - // m.eyeFillColor = m.fieldMeterColor + m.fieldMeterColor = "#ff0" + m.eyeFillColor = m.fieldMeterColor m.hold = function() { if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 300 && (m.cycle % 2)) { - // if (tech.isBotField) { - // b.randomBot(this.position, false) - // bullet[bullet.length - 1].endCycle = simulation.cycle + 840 //14 seconds - // m.energy -= 0.35 - // } else if (tech.isSporeField) { - if (tech.isSporeWorm) { + if (tech.isSporeFlea) { + const drain = 0.16 + (Math.max(bullet.length, 130) - 130) * 0.02 + if (m.energy > drain) { + m.energy -= drain + const speed = m.crouch ? 20 + 8 * Math.random() : 10 + 3 * Math.random() + b.flea({ x: m.pos.x + 35 * Math.cos(m.angle), y: m.pos.y + 35 * Math.sin(m.angle) }, { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) }) + } + } else if (tech.isSporeWorm) { const drain = 0.16 + (Math.max(bullet.length, 130) - 130) * 0.02 if (m.energy > drain) { m.energy -= drain @@ -2260,7 +2265,7 @@ const m = { const damageRadius = circleRadiusScale * this.circleRadius const dischargeRange = 150 + 1600 * tech.plasmaDischarge + 1.3 * damageRadius for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet)) { + if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet) && !mob[i].isInvulnerable) { const sub = Vector.magnitude(Vector.sub(this.position, mob[i].position)) if (sub < damageRadius + mob[i].radius) { // if (!this.isAttached && !mob[i].isMobBullet) this.isPopping = true @@ -2530,11 +2535,43 @@ const m = { // description: "use energy to stop time
while time is stopped you can move and fire
and collisions do 50% less harm", description: "use energy to stop time
+25% movement, jumping, and fire rate
generate 18 energy per second", set() { + // m.fieldMeterColor = "#0fc" + // m.fieldMeterColor = "#ff0" + m.fieldMeterColor = "#3fe" + m.eyeFillColor = m.fieldMeterColor + m.fieldFireRate = 0.75 b.setFireCD(); m.fieldFx = 1.2 m.fieldJump = 1.09 m.setMovement(); + + const timeStop = () => { + m.immuneCycle = m.cycle + 10; //immune to harm while time is stopped, this also disables regen + //draw field everywhere + ctx.globalCompositeOperation = "saturation" + ctx.fillStyle = "#ccc"; + ctx.fillRect(-100000, -100000, 200000, 200000) + ctx.globalCompositeOperation = "source-over" + //stop time + m.isBodiesAsleep = true; + + function sleep(who) { + for (let i = 0, len = who.length; i < len; ++i) { + if (!who[i].isSleeping) { + who[i].storeVelocity = who[i].velocity + who[i].storeAngularVelocity = who[i].angularVelocity + } + Matter.Sleeping.set(who[i], true) + } + } + sleep(mob); + sleep(body); + sleep(bullet); + + simulation.cycle--; //pause all functions that depend on game cycle increasing + } + if (tech.isRewindField) { this.rewindCount = 0 m.grabPowerUpRange2 = 300000 @@ -2562,6 +2599,7 @@ const m = { m.drawHold(m.holdingTarget); m.holding(); m.throwBlock(); + m.wakeCheck(); } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed m.grabPowerUp(); if (this.rewindCount === 0) m.lookForPickUp(); @@ -2612,12 +2650,18 @@ const m = { } } } + m.wakeCheck(); } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released m.pickUp(); this.rewindCount = 0; + m.wakeCheck(); + } else if (tech.isTimeStop && player.speed < 1 && m.onGround && !input.fire) { + timeStop(); + this.rewindCount = 0; } else { m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) this.rewindCount = 0; + m.wakeCheck(); } if (m.energy < m.maxEnergy) m.regenEnergy(); //extra energy regen m.drawFieldMeter() // this calls m.regenEnergy(); also @@ -2637,37 +2681,30 @@ const m = { m.lookForPickUp(); if (m.energy > m.drain) { m.energy -= m.drain; - if (m.energy < m.drain) { + if (m.energy < m.drain) { //out of energy m.fieldCDcycle = m.cycle + 120; m.energy = 0; m.wakeCheck(); } - m.immuneCycle = m.cycle + 10; //immune to harm while time is stopped, this also disables regen - //draw field everywhere - ctx.globalCompositeOperation = "saturation" - ctx.fillStyle = "#ccc"; - ctx.fillRect(-100000, -100000, 200000, 200000) - ctx.globalCompositeOperation = "source-over" - //stop time - m.isBodiesAsleep = true; - - function sleep(who) { - for (let i = 0, len = who.length; i < len; ++i) { - if (!who[i].isSleeping) { - who[i].storeVelocity = who[i].velocity - who[i].storeAngularVelocity = who[i].angularVelocity - } - Matter.Sleeping.set(who[i], true) - } - } - sleep(mob); - sleep(body); - sleep(bullet); - - simulation.cycle--; //pause all functions that depend on game cycle increasing + timeStop(); } else { //holding, but field button is released m.wakeCheck(); } + } else if (tech.isTimeStop && player.speed < 1 && m.onGround && m.fireCDcycle < m.cycle && !input.fire) { + timeStop(); + //makes things move at 1/5 time rate, but has an annoying flicker for mob graphics, and other minor bugs + // if (!(m.cycle % 4)) { + // // requestAnimationFrame(() => { + // m.wakeCheck(); + // // simulation.timePlayerSkip(1) + // // }); //wrapping in animation frame prevents errors, probably + // ctx.globalCompositeOperation = "saturation" + // ctx.fillStyle = "#ccc"; + // ctx.fillRect(-100000, -100000, 200000, 200000) + // ctx.globalCompositeOperation = "source-over" + // } else { + // timeStop(); + // } } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released m.wakeCheck(); m.pickUp(); @@ -2682,6 +2719,11 @@ const m = { } }, effect() { + if (tech.isTimeStop) { + m.fieldHarmReduction = 0.66; //33% reduction + } else { + m.fieldHarmReduction = 1; + } this.set(); } }, @@ -2750,7 +2792,6 @@ const m = { } else { m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) } - //not shooting (or using field) enable cloak if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle if (m.fireCDcycle + 30 < m.cycle && !input.fire) { //automatically cloak if not firing @@ -2992,6 +3033,9 @@ const m = { //field radius decreases out of line of sight description: "use energy to guide blocks
unlock tech from other fields
generate 6 energy per second", effect: () => { + m.fieldMeterColor = "#333" + m.eyeFillColor = m.fieldMeterColor + m.fieldPhase = 0; m.fieldPosition = { x: simulation.mouseInGame.x, @@ -3179,7 +3223,7 @@ const m = { m.fieldOn = false m.fieldRadius = 0 } - m.drawFieldMeter() + m.drawFieldMeter("rgba(0,0,0,0.2)") } } }, @@ -3189,6 +3233,9 @@ const m = { description: "use energy to tunnel through a wormhole
+4% chance to duplicate spawned power ups
generate 6 energy per second", //
bullets may also traverse wormholes drain: 0, effect: function() { + m.fieldMeterColor = "#bbf" //"#0c5" + m.eyeFillColor = m.fieldMeterColor + m.duplicateChance = 0.04 m.fieldRange = 0 powerUps.setDupChance(); //needed after adjusting duplication chance diff --git a/js/powerup.js b/js/powerup.js index 17fd7e8..374177b 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -303,7 +303,7 @@ const powerUps = { }, endDraft(type, isCanceled = false) { //type should be a gun, tech, or field if (isCanceled) { - if (tech.isCancelTech && Math.random() < 0.96) { + if (tech.isCancelTech && Math.random() < 0.94) { // powerUps.research.use('tech') powerUps[type].effect(); return @@ -396,9 +396,6 @@ const powerUps = { } }, delay); } - // for (let i = 0, len = tech.tech.length; i < len; i++) { - // if (tech.tech[i].name === "bot fabrication") tech.tech[i].description = `if you collect ${powerUps.orb.research(2 + Math.floor(0.2 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` - // } } if (tech.isDeathAvoid && document.getElementById("tech-anthropic")) { document.getElementById("tech-anthropic").innerHTML = `-${powerUps.research.count}` diff --git a/js/simulation.js b/js/simulation.js index c7987d5..dfc0cad 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -120,6 +120,44 @@ const simulation = { } simulation.isTimeSkipping = false; }, + // timeMobSkip() { + // simulation.gravity(); + // Engine.update(engine, simulation.delta); + // simulation.wipe(); + // simulation.textLog(); + // if (m.onGround) { + // m.groundControl() + // } else { + // m.airControl() + // } + // m.move(); + // m.look(); + // simulation.camera(); + // level.custom(); + // powerUps.do(); + // mobs.draw(); + // simulation.draw.cons(); + // simulation.draw.body(); + // if (!m.isBodiesAsleep) { + // simulation.checks(); + // // mobs.loop(); + // } + // mobs.healthBar(); + // m.draw(); + // m.hold(); + // // v.draw(); //working on visibility work in progress + // level.customTopLayer(); + // simulation.draw.drawMapPath(); + // b.fire(); + // b.bulletRemove(); + // b.bulletDraw(); + // if (!m.isBodiesAsleep) b.bulletDo(); + // simulation.drawCircle(); + // // simulation.clip(); + // ctx.restore(); + // simulation.drawCursor(); + // // simulation.pixelGraphics(); + // }, mouse: { x: canvas.width / 2, y: canvas.height / 2 @@ -857,6 +895,7 @@ const simulation = { let droneCount = 0 let sporeCount = 0 let wormCount = 0 + let fleaCount = 0 let deliveryCount = 0 for (let i = 0; i < bullet.length; ++i) { if (bullet[i].isDrone) { @@ -866,6 +905,8 @@ const simulation = { sporeCount++ } else if (bullet[i].wormSize) { wormCount++ + } else if (bullet[i].isFlea) { + fleaCount++ } } @@ -920,6 +961,21 @@ const simulation = { } } requestAnimationFrame(respawnWorms); + + //respawn fleas in animation frame + let respawnFleas = () => { + if (fleaCount > 0) { + requestAnimationFrame(respawnFleas); + if (!simulation.paused && !simulation.isChoosing) { + fleaCount-- + const where = { x: level.enter.x + 50, y: level.enter.y - 60 } + const speed = 6 + 3 * Math.random() + const angle = 2 * Math.PI * Math.random() + b.flea({ x: where.x + 100 * (Math.random() - 0.5), y: where.y + 120 * (Math.random() - 0.5) }, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) }) + } + } + } + requestAnimationFrame(respawnFleas); } if (tech.isQuantumEraser) { diff --git a/js/spawn.js b/js/spawn.js index c7d4b33..93b17ae 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -1,7 +1,7 @@ //main object for spawning things in a level const spawn = { nonCollideBossList: ["cellBossCulture", "bomberBoss", "powerUpBoss", "growBossCulture"], - // other bosses: suckerBoss, laserBoss, tetherBoss, bounceBoss, sprayBoss, mineBoss //these need a particular level to work so they are not included in the random pool + // other bosses: suckerBoss, laserBoss, tetherBoss, bounceBoss, sprayBoss, mineBoss, hopMomBoss //these need a particular level to work so they are not included in the random pool randomBossList: [ "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "powerUpBossBaby", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss", @@ -51,13 +51,13 @@ const spawn = { for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].isDropPowerUp && mob[i].alive) { //&& !mob[i].isBoss if (mob[i].isFinalBoss) { - mob[i].health = 0.66 + tech.quantumEraserCount = 0; + return } else { tech.isQuantumEraserDuplication = true mob[i].death() tech.isQuantumEraserDuplication = false } - //graphics const color = 'rgba(255,255,255, 0.8)' simulation.drawList.push({ @@ -81,7 +81,6 @@ const spawn = { color: color, //"rgb(0,0,0)", time: 120 }); - tech.quantumEraserCount-- simulation.makeTextLog(`tech.quantumEraserCount = ${tech.quantumEraserCount}`) if (tech.quantumEraserCount < 1) break @@ -1580,99 +1579,166 @@ const spawn = { } }; }, - hopBoss(x, y, radius = 90) { + hopBullet(x, y, radius = 10 + Math.ceil(Math.random() * 8)) { mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); let me = mob[mob.length - 1]; - me.isBoss = true; + me.stroke = "transparent"; + me.leaveBody = false; + me.isDropPowerUp = false; + // me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.timeLeft = 1500 + Math.floor(600 * Math.random()); - me.g = 0.005; //required if using this.gravity + me.isRandomMove = Math.random() < 0.3 //most chase player, some don't + me.accelMag = 0.01; //jump height + me.g = 0.0015; //required if using this.gravity me.frictionAir = 0.01; me.friction = 1 me.frictionStatic = 1 me.restitution = 0; - me.accelMag = 0.07; - me.delay = 120 * simulation.CDScale; - me.randomHopFrequency = 200 - me.randomHopCD = simulation.cycle + me.randomHopFrequency; - // me.memory = 420; - me.isInAir = false - Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger + me.delay = 130 + 60 * simulation.CDScale; + // Matter.Body.rotate(me, Math.random() * Math.PI); + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + me.onHit = function() { + this.explode(this.mass * 2); + }; + me.do = function() { + this.gravity(); + this.checkStatus(); + if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + this.cd = simulation.cycle + this.delay; + if (this.isRandomMove || Math.random() < 0.2) { + this.force.x += (0.01 + 0.03 * Math.random()) * this.mass * (Math.random() < 0.5 ? 1 : -1); //random move + } else { + this.force.x += (0.01 + 0.03 * Math.random()) * this.mass * (player.position.x > this.position.x ? 1 : -1); //chase player + } + this.force.y -= (0.04 + 0.04 * Math.random()) * this.mass + } + this.timeLimit(); + }; + }, + hopMomBoss(x, y, radius = 120) { + mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.accelMag = 0.05; //jump height + me.g = 0.003; //required if using this.gravity + me.frictionAir = 0.01; + me.friction = 1 + me.frictionStatic = 1 + me.restitution = 0; + me.delay = 100 + 40 * simulation.CDScale; + Matter.Body.rotate(me, Math.random() * Math.PI); spawn.shield(me, x, y, 1); - spawn.spawnOrbitals(me, radius + 60, 1) me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // for (let i = 0, len = 3 + 0.1 * simulation.difficulty; i < len; ++i) spawn.hopBullet(this.position.x + 100 * (Math.random() - 0.5), this.position.y + 100 * (Math.random() - 0.5)) }; - me.lastSpeed = me.speed - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.do = function() { - // this.armor(); this.gravity(); this.seePlayerCheck(); this.checkStatus(); - if (this.seePlayer.recall) { - const deltaSpeed = this.lastSpeed - this.speed - this.lastSpeed = this.speed - if (deltaSpeed > 13 && this.speed < 5) { //if the player slows down greatly in one cycle - //damage and push player away, push away blocks - const range = 800 //Math.min(800, 50 * deltaSpeed) - for (let i = body.length - 1; i > -1; i--) { - if (!body[i].isNotHoldable) { - sub = Vector.sub(body[i].position, this.position); - dist = Vector.magnitude(sub); - if (dist < range) { - knock = Vector.mult(Vector.normalise(sub), Math.min(20, 50 * body[i].mass / dist)); - body[i].force.x += knock.x; - body[i].force.y += knock.y; - } - } - } + if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + this.cd = simulation.cycle + this.delay; + //spawn hopBullets after each jump + for (let i = 0, len = 1 + 0.05 * simulation.difficulty; i < len; ++i) spawn.hopBullet(this.position.x + 100 * (Math.random() - 0.5), this.position.y + 100 * (Math.random() - 0.5)) - simulation.drawList.push({ //draw radius - x: this.position.x, - y: this.position.y, - radius: range, - color: "rgba(0,200,180,0.6)", - time: 4 - }); - } - - if (this.isInAir) { - if (this.velocity.y > -0.01 && Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { //not moving up, and has hit the map or a body - this.isInAir = false //landing - this.cd = simulation.cycle + this.delay - - } - } else { //on ground - if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { //jump - this.isInAir = true - const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; - const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); - this.force.x += forceMag * Math.cos(angle); - this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity - } - } - - // if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { - // this.cd = simulation.cycle + this.delay; - // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; - // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); - // this.force.x += forceMag * Math.cos(angle); - // this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity - // } - } else { - //randomly hob if not aware of player - if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { - this.randomHopCD = simulation.cycle + this.randomHopFrequency; - //slowly change randomHopFrequency after each hop - this.randomHopFrequency = Math.max(100, this.randomHopFrequency + 200 * (0.5 - Math.random())); - const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.5 + Math.random() * 0.2); - const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI; - this.force.x += forceMag * Math.cos(angle); - this.force.y += forceMag * Math.sin(angle) - (0.1 + 0.08 * Math.random()) * this.mass; //antigravity - } + this.force.x += (0.02 + 0.06 * Math.random()) * this.mass * (player.position.x > this.position.x ? 1 : -1); + this.force.y -= (0.08 + 0.08 * Math.random()) * this.mass } }; }, + // hopBoss(x, y, radius = 90) { + // mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); + // let me = mob[mob.length - 1]; + // me.isBoss = true; + // me.g = 0.005; //required if using this.gravity + // me.frictionAir = 0.01; + // me.friction = 1 + // me.frictionStatic = 1 + // me.restitution = 0; + // me.accelMag = 0.07; + // me.delay = 120 * simulation.CDScale; + // me.randomHopFrequency = 200 + // me.randomHopCD = simulation.cycle + me.randomHopFrequency; + // // me.memory = 420; + // me.isInAir = false + // Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger + // spawn.shield(me, x, y, 1); + // spawn.spawnOrbitals(me, radius + 60, 1) + // me.onDeath = function() { + // powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // }; + // me.lastSpeed = me.speed + // me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.do = function() { + // // this.armor(); + // this.gravity(); + // this.seePlayerCheck(); + // this.checkStatus(); + // if (this.seePlayer.recall) { + // const deltaSpeed = this.lastSpeed - this.speed + // this.lastSpeed = this.speed + // if (deltaSpeed > 13 && this.speed < 5) { //if the player slows down greatly in one cycle + // //damage and push player away, push away blocks + // const range = 800 //Math.min(800, 50 * deltaSpeed) + // for (let i = body.length - 1; i > -1; i--) { + // if (!body[i].isNotHoldable) { + // sub = Vector.sub(body[i].position, this.position); + // dist = Vector.magnitude(sub); + // if (dist < range) { + // knock = Vector.mult(Vector.normalise(sub), Math.min(20, 50 * body[i].mass / dist)); + // body[i].force.x += knock.x; + // body[i].force.y += knock.y; + // } + // } + // } + // simulation.drawList.push({ //draw radius + // x: this.position.x, + // y: this.position.y, + // radius: range, + // color: "rgba(0,200,180,0.6)", + // time: 4 + // }); + // } + // if (this.isInAir) { + // if (this.velocity.y > -0.01 && Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length) { //not moving up, and has hit the map or a body + // this.isInAir = false //landing + // this.cd = simulation.cycle + this.delay + // } + // } else { //on ground + // if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { //jump + // this.isInAir = true + // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; + // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + // this.force.x += forceMag * Math.cos(angle); + // this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity + // } + // } + // // if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + // // this.cd = simulation.cycle + this.delay; + // // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; + // // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + // // this.force.x += forceMag * Math.cos(angle); + // // this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.05 + 0.04) * this.mass; //antigravity + // // } + // } else { + // //randomly hob if not aware of player + // if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { + // this.randomHopCD = simulation.cycle + this.randomHopFrequency; + // //slowly change randomHopFrequency after each hop + // this.randomHopFrequency = Math.max(100, this.randomHopFrequency + 200 * (0.5 - Math.random())); + // const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.5 + Math.random() * 0.2); + // const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI; + // this.force.x += forceMag * Math.cos(angle); + // this.force.y += forceMag * Math.sin(angle) - (0.1 + 0.08 * Math.random()) * this.mass; //antigravity + // } + // } + // }; + // }, spinner(x, y, radius = 30 + Math.ceil(Math.random() * 35)) { mobs.spawn(x, y, 5, radius, "#000000"); let me = mob[mob.length - 1]; @@ -5118,7 +5184,7 @@ const spawn = { let me = mob[mob.length - 1]; me.stroke = "transparent"; me.onHit = function() { - this.explode(this.mass * 20); + this.explode(this.mass); }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 @@ -5135,7 +5201,7 @@ const spawn = { //damage player if in range if (Vector.magnitude(Vector.sub(player.position, this.position)) < pulseRadius && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage - m.damage(0.02 * simulation.dmgScale); + m.damage(0.015 * simulation.dmgScale); } simulation.drawList.push({ //add dmg to draw queue x: this.position.x, diff --git a/js/tech.js b/js/tech.js index 210e444..ed55bf9 100644 --- a/js/tech.js +++ b/js/tech.js @@ -256,7 +256,7 @@ const tech = { return dmg * tech.slowFire * tech.aimDamage }, duplicationChance() { - return Math.max(0, (tech.isPowerUpsVanish ? 0.12 : 0) + (tech.isStimulatedEmission ? 0.15 : 0) + tech.cancelCount * 0.045 + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0) + tech.isQuantumEraserDuplication * (1 - 0.01 * (simulation.difficultyMode ** 2))) + return Math.max(0, (tech.isPowerUpsVanish ? 0.12 : 0) + (tech.isStimulatedEmission ? 0.15 : 0) + tech.cancelCount * 0.045 + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.5 : 0) + tech.isQuantumEraserDuplication * (1 - 0.016 * (simulation.difficultyMode ** 2))) }, isScaleMobsWithDuplication: false, maxDuplicationEvent() { @@ -2977,7 +2977,7 @@ const tech = { effect() { tech.isBrainstorm = true tech.isBrainstormActive = false - tech.brainStormDelay = 145 - simulation.difficultyMode * 10 + tech.brainStormDelay = 150 - simulation.difficultyMode * 7 }, remove() { tech.isBrainstorm = false @@ -3319,7 +3319,7 @@ const tech = { { name: "options exchange", link: `options exchange`, - description: `clicking × for a field, tech, or gun has a 96%
chance to randomize choices and not cancel`, + description: `clicking × for a field, tech, or gun has a 94%
chance to randomize choices and not cancel`, maxCount: 1, count: 0, frequency: 1, @@ -5205,7 +5205,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm || tech.isSporeFlea }, requires: "spores", effect() { @@ -5225,7 +5225,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm || tech.isSporeFlea }, requires: "spores", effect() { @@ -5263,7 +5263,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isEnergyHealth || tech.isSporeWorm + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isEnergyHealth || tech.isSporeWorm || tech.isSporeFlea }, requires: "spores, not mass-energy", effect() { @@ -5273,26 +5273,55 @@ const tech = { tech.isMutualism = false } }, - // { - // name: "worm-shot", - // link: `worm-shot`, - // description: "shotgun hatches 3-4 mob seeking worms
worms benefit from spore technology", //
worms seek out nearby mobs - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles - // }, - // requires: "shotgun, not incendiary, nail-shot, rivets, foam-shot, ice-shot, needles", - // effect() { - // tech.isWormShot = true; - // }, - // remove() { - // tech.isWormShot = false; + { + name: "fleas", + description: "sporangium hatch fleas
spore tech applies to fleas", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isSporeWorm + }, + requires: "spores, not worms", + effect() { + tech.isSporeFlea = true + }, + remove() { + tech.isSporeFlea = false + + } + }, + + // ammoBonus: 8, + // effect() { + // tech.isRailGun = true; + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // if (b.guns[i].name === "harpoon") { + // b.guns[i].chooseFireMethod() + // b.guns[i].ammoPack = this.ammoBonus; + // b.guns[i].ammo = b.guns[i].ammo * this.ammoBonus; + // simulation.updateGunHUD(); + // break + // } // } // }, + // remove() { + // if (tech.isRailGun) { + // tech.isRailGun = false; + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // if (b.guns[i].name === "harpoon") { + // b.guns[i].chooseFireMethod() + // b.guns[i].ammoPack = 0.6; + // b.guns[i].ammo = Math.ceil(b.guns[i].ammo / this.ammoBonus); + // simulation.updateGunHUD(); + // break + // } + // } + // } + // } + { name: "nematodes", description: "shotgun and sporangium hatch worms
spore tech applies to worms", @@ -5302,9 +5331,9 @@ const tech = { frequency: 3, frequencyDefault: 3, allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot) + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeFlea }, - requires: "spores", + requires: "spores, not fleas", effect() { tech.isSporeWorm = true }, @@ -5333,7 +5362,7 @@ const tech = { }, { name: "path integration", - description: "drones, spores, and worms
travel with you through levels", + description: "drones, spores, fleas, and worms
travel with you through levels", isGunTech: true, maxCount: 1, count: 0, @@ -5342,7 +5371,7 @@ const tech = { allowed() { return (tech.isSporeFollow && (tech.haveGunCheck("spores") || (tech.haveGunCheck("shotgun") && tech.isSporeWorm))) || tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isMissileField || tech.isIceField)) }, - requires: "spores, worms, diplochory, drones", + requires: "spores, worms, flagella, drones", effect() { tech.isDronesTravel = true }, @@ -5353,7 +5382,7 @@ const tech = { { name: "anti-shear topology", link: `anti-shear topology`, - description: "+30% projectile duration
drone, spore, worm, missile, foam, wave, neutron, ice", + description: "+30% projectile duration
drone spore worm flea missile foam wave neutron ice", isGunTech: true, maxCount: 3, count: 0, @@ -5362,7 +5391,7 @@ const tech = { allowed() { return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || tech.haveGunCheck("spores") || tech.haveGunCheck("drones") || tech.haveGunCheck("missiles") || tech.haveGunCheck("foam") || tech.haveGunCheck("matter wave") || tech.isNeutronBomb || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 || tech.isSporeWorm || tech.isFoamBotUpgrade || tech.isFoamBall }, - requires: "drones, spores, missiles, foam, matter wave, neutron bomb, ice IX", + requires: "drones, spores, missiles, foam, matter wave, neutron bomb, ice IX, flea", effect() { tech.isBulletsLastLonger += 0.3 }, @@ -5662,14 +5691,14 @@ const tech = { }, { name: "necrophage", - description: "if foam or worms kill their target
grow 3 copies", + description: "if foam, fleas, or worms kill their target
grow 3 copies", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("foam") || tech.isFoamBall || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isSporeWorm + return tech.haveGunCheck("foam") || tech.isFoamBall || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isSporeWorm || tech.isSporeFlea }, requires: "foam, worms", effect() { @@ -5818,7 +5847,7 @@ const tech = { }, { name: "railgun", - description: `+50% harpoon density, but they don't retract
+800% harpoon ammo per ${powerUps.orb.ammo(1)}`, + description: `+50% harpoon density, but they don't retract
+900% harpoon ammo per ${powerUps.orb.ammo(1)}`, isGunTech: true, maxCount: 1, count: 0, @@ -5828,7 +5857,7 @@ const tech = { return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isGrapple }, requires: "harpoon, not UHMWPE, induction furnace, grappling hook", - ammoBonus: 8, + ammoBonus: 9, effect() { tech.isRailGun = true; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun @@ -6109,7 +6138,7 @@ const tech = { }, { name: "laser diode", - description: "+30% laser energy efficiency
affects laser-gun, laser-bot, laser-mines, pulse", + description: "+33% laser energy efficiency", isGunTech: true, maxCount: 1, count: 0, @@ -6120,7 +6149,7 @@ const tech = { }, requires: "laser, not free-electron", effect() { - tech.isLaserDiode = 0.70; //100%-37% + tech.isLaserDiode = 0.66; //100%-37% tech.laserColor = "rgb(0, 11, 255)" tech.laserColorAlpha = "rgba(0, 11, 255,0.5)" }, @@ -6177,7 +6206,7 @@ const tech = { { name: "iridescence", // description: "if a laser hits a mob at a low angle of illumination
+66% laser damage", - description: "if mobs are struck near their center
+88% laser damage", + description: "if laser beams hit mobs near their center
+88% laser damage", isGunTech: true, maxCount: 3, count: 0, @@ -6196,7 +6225,7 @@ const tech = { }, { name: "lens", - description: "if directed through a revolving +π / 4 circular arc
+150% laser gun damage", + description: "+150% laser gun damage if it passes
through a revolving +90° arc circular lens", //π / 2 isGunTech: true, maxCount: 3, count: 0, @@ -6255,7 +6284,7 @@ const tech = { }, { name: "diffraction grating", - description: `+1 laser gun beam`, + description: `+1 diverging laser gun beam`, isGunTech: true, maxCount: 9, count: 0, @@ -6384,7 +6413,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.isPulseLaser && !tech.beamSplitter + return tech.haveGunCheck("laser") && tech.isPulseLaser && !tech.beamSplitter }, requires: "laser gun, pulse, not diffraction grating", effect() { @@ -7090,7 +7119,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.isExtruder + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && tech.isExtruder }, requires: "extruder", effect() { @@ -7146,8 +7175,8 @@ const tech = { isFieldTech: true, maxCount: 1, count: 0, - frequency: 2, - frequencyDefault: 2, + frequency: 1, + frequencyDefault: 1, allowed() { return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isEnergyHealth && !tech.isTimeSkip }, @@ -7162,6 +7191,27 @@ const tech = { if (this.count) m.fieldUpgrades[m.fieldMode].set() } }, + { + name: "frame-dragging", //"non-inertial frame", + description: "when not moving time dilation stops time
+33% defense", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "time dilation" + }, + requires: "time dilation", + effect() { + tech.isTimeStop = true; + m.fieldHarmReduction = 0.66; //33% reduction + }, + remove() { + tech.isTimeStop = false; + if (m.fieldUpgrades[m.fieldMode].name === "time dilation") m.fieldHarmReduction = 1; + } + }, { name: "Lorentz transformation", description: `use ${powerUps.orb.research(3)}
+50% movement, jumping, and fire rate`, @@ -7235,7 +7285,7 @@ const tech = { }, { name: "quantum eraser", - descriptionFunction() { return `for each mob left alive after you exit a level
kill a mob as they spawn at ${100-simulation.difficultyMode**2}% duplication
` }, + descriptionFunction() { return `for each mob left alive after you exit a level
kill a mob as they spawn at +${100-1.6*simulation.difficultyMode**2}% duplication
` }, description: `for each mob left alive after you exit a level
kill a mob as they spawn at 100% duplication
`, isFieldTech: true, @@ -10403,6 +10453,7 @@ const tech = { isFastTime: null, isAnthropicTech: null, isSporeWorm: null, + isSporeFlea: null, isFoamShot: null, isIceShot: null, isBlockRestitution: null, diff --git a/todo.txt b/todo.txt index a607401..183e888 100644 --- a/todo.txt +++ b/todo.txt @@ -1,32 +1,46 @@ ******************************************************** NEXT PATCH ************************************************** -tech: iridescence - laser does 100% damage to mobs hit near their center -tech: lens - laser does extra damage if you fire through a lens that revolves around you - tech: arc length - increase the size of the lens +tech: fleas - replace spores with little hoppers -virtual particles costs 4->6 research for 11% duplication -quantum eraser has less duplication chance at higher difficulty modes -community map temple updates +frame-dragging - time dilation field stops time when you aren't moving or firing, +33% defense + the odds of finding this tech is low because I find it kinda annoying, but maybe you will like it + +molecular assembler field energy meter is yellow + wormhole is lavender + perfect diamagnetism is blue + time dilation is green blue + pilot wave is black + +new room in labs: hopBossMom +harpoon now auto targets by default, but disabled when crouched +pulse + neocognitron auto targeting also disabled when crouched bug fixes *********************************************************** TODO ***************************************************** -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 +fleas + add very short (2 cycle) delay after each landing -> hop + zero velocity while on short delay? try it? + flea tech: + add a delay to flea jumping also +dmg + reduce lifespan after hitting mob, like drones + but add in a long cooldown after a hit + mid flight attraction towards nearby mobs? + if close to mob and line of sight: set isAttached flag = true + draw a line to mob, attraction + reset isAttached to false after: 1-2s or distance gets too big + tech for shotgun? -laser tech: critical hits do _______ damage? - name? polarized light, iridescence +anti-shear topology apply to grenades too? and other less useful bullets + then open up tech requirements -hopBossMom - spawns lots of small hopBullets - drops eggs, that hatch into hopBullets - like sporangium - normally runs away from player, but goes closer to drop eggs +menagerie: sporangium release a variety of things + spores, worms, hoppers, drones, iceIX?, foam?, missiles? + benefit: they release more stuff than normal + +swim through slime + hold up to float while in slime? level element: exploding barrels @@ -34,22 +48,12 @@ improve mob invincible graphic opacity oscillates from 100% to 0%? make different from stun -junk tech give strange CSS to a keyword - @keyframes bounce-munch { - 0% {vertical-align: -40px;} - 100% {vertical-align: 40px;} - } - make plasma ball power up and block pick up still work when you have no no energy make a unique CD var for plasma ball? wormhole tech: entropic gravity - gain defense for each research requires wormhole or negative mass field or pilot wave -shrapnel: a new bullet type: a very small bullet with no targeting that has a high gravity arc - maybe 1 bounce or no bounce before it ends on map or mob collision - often spawns in groups - testChamber2 mechanics companion cube @@ -184,6 +188,7 @@ tech expansion: should also make other fields do things perfect diamagnetism moves forward when you hold down the shield it's great, but maybe annoying? maybe only with crouch? + perfect diamagnetism just replace Messier effect time dilation drains 1/2 as much energy when paused grow plasma torch as you hold it down negative mass effects much more space