diff --git a/.DS_Store b/.DS_Store index 95a0799..32b9bd6 100644 Binary files a/.DS_Store and b/.DS_Store differ 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