From 779500ce21cd827971eda8f1fc3da12f1f95c34f Mon Sep 17 00:00:00 2001 From: landgreen Date: Wed, 25 May 2022 19:15:34 -0700 Subject: [PATCH] timeSkip new reactor boss: timeBoss - after taking some damage it speeds up the passage of time reactor level has big doors nonrefundable tech show up in the pause menu time dilation field - move, jump, and fire 25% faster JUNK tech: closed timelike curve - spawn 5 field power ups, but every 12 seconds teleport a second into your future --- .DS_Store | Bin 6148 -> 6148 bytes js/bullet.js | 2 +- js/index.js | 6 +- js/level.js | 129 +++++++++-------- js/player.js | 117 +++------------ js/powerup.js | 14 +- js/simulation.js | 21 ++- js/spawn.js | 359 +++++++++++++++++++++++++++++++---------------- js/tech.js | 60 ++++---- todo.txt | 128 ++++++++--------- 10 files changed, 450 insertions(+), 386 deletions(-) diff --git a/.DS_Store b/.DS_Store index fabd69ae018868f3ad30b3a0a8adeae78c926f06..461e3353cbecdd215b602891f8039b3a4ed37021 100644 GIT binary patch delta 21 ccmZoMXffEJ#mw~f&tx5DKgNX3)y$nD09CvPZ~y=R delta 21 ccmZoMXffEJ#mw~J=wuycKgJ82tC>4R0979coB#j- diff --git a/js/bullet.js b/js/bullet.js index 4af4d32..0e117bd 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -271,7 +271,7 @@ const b = { }, fireCDscale: 1, setFireCD() { - b.fireCDscale = tech.fireRate * tech.slowFire * tech.researchHaste * tech.aimDamage + b.fireCDscale = tech.fireRate * tech.slowFire * tech.researchHaste * tech.aimDamage * m.fieldFireRate if (tech.isFastTime) b.fireCDscale *= 0.5 if (tech.isFireRateForGuns) b.fireCDscale *= Math.pow(0.8, b.inventory.length) if (tech.isFireMoveLock) b.fireCDscale *= 0.5 diff --git a/js/index.js b/js/index.js index 55c5bc6..89aa3e2 100644 --- a/js/index.js +++ b/js/index.js @@ -275,9 +275,11 @@ ${simulation.isCheating ? "

lore disabled": ""} const style = (tech.isPauseEjectTech && !simulation.isChoosing) ? 'style="animation: techColorCycle 1s linear infinite alternate;"' : '' for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) { + if (tech.tech[i].count > 0) { const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; - if (tech.tech[i].isFieldTech) { + if (tech.tech[i].isNonRefundable) { + text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + } else if (tech.tech[i].isFieldTech) { text += `
diff --git a/js/level.js b/js/level.js index 954eef4..b859f56 100644 --- a/js/level.js +++ b/js/level.js @@ -16,12 +16,13 @@ const level = { start() { if (level.levelsCleared === 0) { //this code only runs on the first level // // simulation.isHorizontalFlipped = true - // m.setField("wormhole") - // b.giveGuns("matter wave") - // tech.giveTech("chain reaction") - // tech.giveTech("fireworks") - // tech.giveTech("flame test") - // tech.giveTech("pyrotechnics") + // m.addHealth(Infinity) + // m.setField("standing wave") + // b.giveGuns("nail gun") + // tech.giveTech("closed timelike curve") + // tech.giveTech("irradiated nails") + // tech.giveTech("pneumatic actuator") + // tech.giveTech("6s half-life") // for (let i = 0; i < 10; i++) tech.giveTech("replication") // tech.giveTech("eternalism") // for (let i = 0; i < 10; i++) tech.giveTech("ammonium nitrate") @@ -30,18 +31,13 @@ const level = { // for (let i = 0; i < 15; i++) tech.giveTech() // for (let i = 10; i < tech.tech.length; i++) { tech.tech[i].isBanished = true } // powerUps.research.changeRerolls(100000) - // for (let i = 0; i < 5; i++) tech.giveTech("corona discharge") // tech.tech[297].frequency = 100 - // m.setField("plasma torch") - // tech.giveTech("plasma ball") - // tech.giveTech("extruder") - // m.immuneCycle = Infinity //you can't take damage - // level.difficultyIncrease(15) //30 is near max on hard //60 is near max on why + // level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why // simulation.enableConstructMode() //used to build maps in testing mode - // level.temple(); - // level.testing(); //not in rotation, used for testing - // spawn.slashBoss(1900, -500) + // level.testing(); + // level.reactor(); //not in rotation, used for testing + // spawn.timeBoss(1900, -500) if (simulation.isTraining) { level.walk(); } else { level.intro(); } //normal starting level ************************************************ // powerUps.research.changeRerolls(3000) @@ -1054,6 +1050,9 @@ const level = { } } }, + isClosed() { + return this.position.y > y - 1 + }, draw() { ctx.fillStyle = "#555" ctx.beginPath(); @@ -2596,7 +2595,11 @@ const level = { testing() { const button = level.button(1000, 0) spawn.bodyRect(1000, -50, 50, 50); + level.custom = () => { + ctx.fillStyle = "#d4d4d4" + ctx.fillRect(2500, -475, 200, 300) + ctx.fillStyle = "rgba(0,255,255,0.1)"; ctx.fillRect(6400, -550, 300, 350); level.exit.drawAndCheck(); @@ -2605,6 +2608,8 @@ const level = { level.customTopLayer = () => { button.query(); button.draw(); + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-150, -650, 900, 250) }; level.setPosToSpawn(0, -450); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); @@ -2686,10 +2691,10 @@ const level = { // spawn.randomMob(1600, -500) }, reactor() { - level.setPosToSpawn(-50, -800); //normal spawn + level.setPosToSpawn(-550, -800); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); - level.exit.x = 3000; - level.exit.y = -35; + level.exit.x = 3500; + level.exit.y = -42; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25); level.defaultZoom = 2000 simulation.zoomTransition(level.defaultZoom) @@ -2699,25 +2704,26 @@ const level = { // spawn.debris(750, -2200, 3700, 16); //16 debris per level const button = level.button(1400, 0) button.isUp = true - spawn.mapRect(-1525, -2825, 1250, 4925); - spawn.mapRect(-400, -2025, 625, 925); - spawn.mapRect(-400, -750, 625, 1200); spawn.bodyRect(250, -70, 100, 70, 1); - spawn.mapRect(-425, 0, 4200, 2100); - spawn.mapRect(175, -1250, 50, 300); - spawn.mapRect(-475, -2825, 4250, 1025); + spawn.mapRect(-425, 0, 4500, 2100); + spawn.mapRect(-475, -2825, 4500, 1025); // spawn.mapRect(1200, -1300, 600, 800); const a = 400 //side length const c = 100 //corner offset spawn.mapVertex(1487, -900, `${-a} ${-a+c} ${-a+c} ${-a} ${a-c} ${-a} ${a} ${-a+c} ${a} ${a-c} ${a-c} ${a} ${-a+c} ${a} ${-a} ${a-c}`); //square with edges cut off - + //entrance + spawn.mapRect(-2025, -2825, 1250, 4925); + spawn.mapRect(-900, -2825, 1125, 1725); + spawn.mapRect(-900, -750, 1125, 2850); + spawn.mapRect(-325, -1250, 550, 300); //exit - spawn.mapRect(3300, -2825, 1125, 4925); - spawn.mapRect(2750, -2150, 1025, 1775); - spawn.mapRect(2750, -475, 50, 300); + spawn.mapRect(3800, -2825, 1225, 4925); + spawn.mapRect(2750, -2150, 1325, 1775); + spawn.mapRect(2750, -475, 550, 300); + spawn.mapRect(2750, -7, 1050, 150); //exit room floor - const doorIn = level.door(187, -950, 25, 200, 190, 2) //x, y, width, height, distance, speed = 1 - const doorOut = level.door(2762, -175, 25, 200, 190, 2) //x, y, width, height, distance, speed = 1 + const doorIn = level.door(-313, -950, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1 + const doorOut = level.door(2762, -175, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1 // doorOut.isClosing = true let isDoorsLocked = false let isFightOver = false @@ -2725,7 +2731,7 @@ const level = { level.custom = () => { if (isDoorsLocked) { - if (player.position.x < 0) { //if player gets trapped inside starting room open up again + if (player.position.x < -300) { //if player gets trapped inside starting room open up again isDoorsLocked = false doorIn.isClosing = false } @@ -2738,35 +2744,45 @@ const level = { doorIn.openClose(); doorOut.openClose(); ctx.fillStyle = "#d5ebef" - ctx.fillRect(2750, -375, 550, 375) + ctx.fillRect(2750, -375, 1050, 375) level.enter.draw(); level.exit.drawAndCheck(); button.draw(); if (button.isUp) { button.query(); } else if (!isSpawnedBoss) { - isSpawnedBoss = true - isDoorsLocked = true - doorIn.isClosing = true - doorOut.isClosing = true - for (let i = 0; i < 9; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "ammo") - for (let i = 0; i < 3; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "heal"); - const scale = Math.pow(simulation.difficulty, 0.73) //hard around 30, why around 54 - if (Math.random() < 0.07 && simulation.difficulty > 22) { - for (let i = 0, len = scale * 0.1 / 3; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); - for (let i = 0, len = scale * 0.17 / 3; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) - for (let i = 0, len = scale * 0.23 / 3; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); - } else { - if (Math.random() < 0.33) { - for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 - } else if (Math.random() < 0.5) { - for (let i = 0, len = scale * 0.16; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15 + if (player.position.x > 0) { + if (!doorOut.isClosed() || !doorIn.isClosed()) { + doorIn.isClosing = true + doorOut.isClosing = true } else { - for (let i = 0, len = scale * 0.23; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15 + isSpawnedBoss = true + isDoorsLocked = true + for (let i = 0; i < 9; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "ammo") + for (let i = 0; i < 3; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "heal"); + const scale = Math.pow(simulation.difficulty, 0.73) //hard around 30, why around 54 + if (Math.random() < 0.07 && simulation.difficulty > 24) { + for (let i = 0, len = scale * 0.25 / 4; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15 + for (let i = 0, len = scale * 0.1 / 4; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); + for (let i = 0, len = scale * 0.16 / 4; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) + for (let i = 0, len = scale * 0.23 / 4; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); + } else { + if (Math.random() < 0.25) { + for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 + } else if (Math.random() < 0.33) { + for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 + } else if (Math.random() < 0.5) { + for (let i = 0, len = scale * 0.16; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15 + } else { + for (let i = 0, len = scale * 0.23; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15 + } + } + spawn.secondaryBossChance(2200, -800) } + } else { + doorIn.isClosing = false } - spawn.secondaryBossChance(2200, -800) - } else if (!isFightOver && !(simulation.cycle % 120)) { //once a second look for any bosses + } else if (!isFightOver && !(simulation.cycle % 180)) { let isFoundBoss = false for (let i = 0; i < mob.length; i++) { if (mob[i].isBoss) { @@ -2778,8 +2794,9 @@ const level = { isFightOver = true doorIn.isClosing = false doorOut.isClosing = false - powerUps.spawnBossPowerUp(2900, -100) - powerUps.spawn(3050, -200, "tech") + powerUps.spawnBossPowerUp(3600, -100) + powerUps.spawn(3650, -200, "tech") + // if (player.position.x < 2760 && player.position.x > 210) {} } } }; @@ -2790,8 +2807,8 @@ const level = { // } doorIn.draw(); doorOut.draw(); - ctx.fillStyle = "rgba(0,0,0,0.05)" - ctx.fillRect(-275, -1100, 500, 350); + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-775, -1100, 1000, 350); // ctx.fillStyle = "rgba(0,255,255,0.1)" // ctx.fillRect(2750, -375, 550, 375) }; @@ -4082,7 +4099,7 @@ const level = { spawn.randomMob(3600, 1725, 0.9); spawn.randomMob(4100, 1225, 0.9); spawn.randomMob(2825, 400, 0.9); - if (simulation.difficulty > 1) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "historyBoss", "orbitalBoss", "grenadierBoss"]); + if (simulation.difficulty > 1) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss", "blinkBoss", "streamBoss", "historyBoss", "orbitalBoss", "grenadierBoss", "blockBoss", "revolutionBoss", "slashBoss"]); powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(7725, 2275) diff --git a/js/player.js b/js/player.js index b43c7e0..615ae94 100644 --- a/js/player.js +++ b/js/player.js @@ -77,8 +77,8 @@ const m = { setMovement() { // m.Fx = 0.08 / mass * tech.squirrelFx // m.FxAir = 0.4 / mass / mass - m.Fx = tech.baseFx * tech.squirrelFx * (tech.isFastTime ? 1.5 : 1) / player.mass //base player mass is 5 - m.jumpForce = tech.baseJumpForce * tech.squirrelJump * (tech.isFastTime ? 1.13 : 1) / player.mass / player.mass //base player mass is 5 + m.Fx = tech.baseFx * m.fieldFx * tech.squirrelFx * (tech.isFastTime ? 1.5 : 1) / player.mass //base player mass is 5 + m.jumpForce = tech.baseJumpForce * m.fieldJump * tech.squirrelJump * (tech.isFastTime ? 1.13 : 1) / player.mass / player.mass //base player mass is 5 }, FxAir: 0.016, // 0.4/5/5 run Force in Air yOff: 70, @@ -893,6 +893,9 @@ const m = { holdingTarget: null, timeSkipLastCycle: 0, // these values are set on reset by setHoldDefaults() + fieldFx: 1, + fieldJump: 1, + fieldFireRate: 1, blockingRecoil: 4, grabPowerUpRange2: 0, isFieldActive: false, @@ -951,6 +954,11 @@ const m = { m.isCloak = false; player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield m.airSpeedLimit = 125 + m.fieldFireRate = 1 + b.setFireCD(); + m.fieldFx = 1 + m.fieldJump = 1 + m.setMovement(); m.drop(); m.holdingMassScale = 0.5; m.fieldArc = 0.2; //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) @@ -1045,10 +1053,6 @@ const m = { m.holdingTarget = null; } }, - // setMovement() { - // m.Fx = tech.baseFx * tech.squirrelFx * (tech.isFastTime ? 1.5 : 1); - // m.jumpForce = tech.baseJumpForce * tech.squirrelJump * (tech.isFastTime ? 1.13 : 1) - // }, definePlayerMass(mass = m.defaultMass) { Matter.Body.setMass(player, mass); //reduce air and ground move forces @@ -2514,8 +2518,13 @@ const m = { { name: "time dilation", // 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
for everything except you
generate 18 energy/second", + description: "use energy to stop time
move, jump, and fire 25% faster
generate 18 energy/second", set() { + m.fieldFireRate = 0.75 + b.setFireCD(); + m.fieldFx = 1.2 + m.fieldJump = 1.09 + m.setMovement(); if (tech.isRewindField) { this.rewindCount = 0 m.grabPowerUpRange2 = 300000 @@ -2643,26 +2652,6 @@ const m = { sleep(bullet); simulation.cycle--; //pause all functions that depend on game cycle increasing - // if (tech.isTimeSkip) { - // m.immuneCycle = 0; - // m.drain += 0.0000025 - // m.regenEnergy(); //immunity disables normal regen, so turn off immunity for just this function - // m.immuneCycle = m.cycle + 10; - // simulation.isTimeSkipping = true; - // m.cycle++; - // simulation.gravity(); - // if (tech.isFireMoveLock && input.fire) { - // player.force.x = 0 - // player.force.y = 0 - // } - // Engine.update(engine, simulation.delta); - // m.move(); - // simulation.checks(); - // m.walk_cycle += m.flipLegs * m.Vx; - // b.fire(); - // b.bulletDo(); - // simulation.isTimeSkipping = false; - // } } else { //holding, but field button is released m.wakeCheck(); } @@ -2678,80 +2667,6 @@ const m = { m.drawFieldMeter() } } - // } else { - // m.fieldFire = true; - // m.isBodiesAsleep = false; - // m.isTimeStopped = false; - // m.drain = 0.005 - // let isFieldInputDown = false; - // m.hold = function() { - // if (m.isHolding) { - // m.drawHold(m.holdingTarget); - // m.holding(); - // m.throwBlock(); - // isFieldInputDown = false - // } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // if (!m.holdingTarget) isFieldInputDown = true; - // m.grabPowerUp(); - // m.lookForPickUp(); - // // if (m.energy > 0.05) { //deflecting - // // m.drawField(); - // // m.pushMobsFacing(); - // // } - // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - // m.pickUp(); - // } 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) - // } - - // if (isFieldInputDown && !input.field && !m.holdingTarget && !m.isHolding) { - // isFieldInputDown = false; - // m.isTimeStopped = true; - // } - // m.drawFieldMeter() - // if (m.energy < m.maxEnergy) { //extra energy regen - // m.regenEnergy(); - // m.regenEnergy(); - // } - // if (m.isTimeStopped) { - // if (m.energy > m.drain) { - // // if (player.speed > 0.01 || input.fire) - // m.energy -= m.drain; - // m.immuneCycle = m.cycle + 10; //immune to harm while time is stopped, this also disables regen - // simulation.cycle--; //pause all functions that depend on game cycle increasing - // m.isBodiesAsleep = true; - // ctx.globalCompositeOperation = "saturation" //draw field everywhere - // ctx.fillStyle = "#ccc"; - // ctx.fillRect(-100000, -100000, 200000, 200000) - // ctx.globalCompositeOperation = "source-over" - - // 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); - // } else { //restart time - // m.fieldCDcycle = m.cycle + 60; - // m.energy = 0; - // m.isTimeStopped = false - // m.wakeCheck(); - // } - // if (simulation.isChoosing) { - // // m.fieldCDcycle = m.cycle + 60; - // m.isTimeStopped = false - // m.wakeCheck(); - // } - // } - - // } - // } }, effect() { this.set(); diff --git a/js/powerup.js b/js/powerup.js index b8b5de0..a726a19 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -280,13 +280,11 @@ const powerUps = { if (!simulation.paused) { if (tech.isNoDraftPause) { - const cycle = () => { - m.fireCDcycle = m.cycle + 5; //fire cooldown - if (simulation.isChoosing && m.alive) requestAnimationFrame(cycle) - } - - requestAnimationFrame(cycle); - + // const cycle = () => { + // m.fireCDcycle = m.cycle + 1; //fire cooldown + // if (simulation.isChoosing && m.alive) requestAnimationFrame(cycle) + // } + // requestAnimationFrame(cycle); document.getElementById("choose-grid").style.opacity = "0.8" } else { @@ -1081,7 +1079,7 @@ const powerUps = { } }, pauseEjectTech(index) { - if ((tech.isPauseEjectTech || simulation.testing) && !simulation.isChoosing) { + if ((tech.isPauseEjectTech || simulation.testing) && !simulation.isChoosing && !tech.tech[index].isNonRefundable) { if (Math.random() < 0.1 || tech.tech[index].isFromAppliedScience || (tech.tech[index].bonusResearch !== undefined && tech.tech[index].bonusResearch > powerUps.research.count)) { tech.removeTech(index) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); diff --git a/js/simulation.js b/js/simulation.js index 253b5d5..80ab6aa 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -89,18 +89,35 @@ const simulation = { m.airControl() } m.move(); + level.custom(); simulation.checks(); mobs.loop(); - // m.draw(); m.walk_cycle += m.flipLegs * m.Vx; - m.hold(); + level.customTopLayer(); b.fire(); b.bulletRemove(); b.bulletDo(); } simulation.isTimeSkipping = false; }, + timePlayerSkip(cycles = 60) { + simulation.isTimeSkipping = true; + for (let i = 0; i < cycles; i++) { + simulation.cycle++; + simulation.gravity(); + Engine.update(engine, simulation.delta); + // level.custom(); + // level.customTopLayer(); + if (!m.isBodiesAsleep) { + simulation.checks(); + mobs.loop(); + } + b.bulletRemove(); + if (!m.isBodiesAsleep) b.bulletDo(); + } + simulation.isTimeSkipping = false; + }, mouse: { x: canvas.width / 2, y: canvas.height / 2 diff --git a/js/spawn.js b/js/spawn.js index 7a22cc6..8f12bd5 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -1,6 +1,6 @@ //main object for spawning things in a level const spawn = { - nonCollideBossList: ["cellBossCulture", "bomberBoss", "powerUpBoss", "orbitalBoss", "spawnerBossCulture", "growBossCulture"], + nonCollideBossList: ["cellBossCulture", "bomberBoss", "powerUpBoss", "growBossCulture"], // other bosses: suckerBoss, laserBoss, tetherBoss, mantisBoss, bounceBoss, sprayBoss //these need a particular level to work so they are not included in the random pool randomBossList: ["shieldingBoss", "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "powerUpBossBaby", "snakeBoss", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss", @@ -1031,7 +1031,7 @@ const spawn = { me.isSpawnBoss = true; me.spawnID = spawnID - me.accelMag = 0.00018 * simulation.accelScale; + me.accelMag = 0.00022 * simulation.accelScale; me.memory = Infinity; me.showHealthBar = false; me.isVerticesChange = true @@ -1039,7 +1039,7 @@ const spawn = { me.seePlayerFreq = Math.floor(14 + 7 * Math.random()) me.seeAtDistance2 = 200000 //1400000; me.stroke = "transparent" - me.collisionFilter.mask = cat.player | cat.bullet //| cat.body //| cat.map //"rgba(255,60,0,0.3)" + me.collisionFilter.mask = cat.player | cat.bullet | cat.body | cat.map //"rgba(255,60,0,0.3)" // Matter.Body.setDensity(me, 0.0014) // normal density is 0.001 Matter.Body.setAngularVelocity(me, 0.12 * (Math.random() - 0.5)) // spawn.shield(me, x, y, 1); @@ -1047,7 +1047,7 @@ const spawn = { me.onHit = function() { //run this function on hitting player this.explode(); }; - me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.damageReduction = 0.14 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); me.doAwake = function() { this.alwaysSeePlayer(); this.checkStatus(); @@ -1741,6 +1741,68 @@ const spawn = { } } }, + // timeBoss(x, y, radius = 25) { + // mobs.spawn(x, y, 12, radius, "#000"); + // let me = mob[mob.length - 1]; + // me.isBoss = true; + // me.stroke = "transparent"; //used for drawSneaker + // me.eventHorizon = 1100; //required for black hole + // me.eventHorizonGoal = 1100; //required for black hole + // me.seeAtDistance2 = (me.eventHorizon + 1200) * (me.eventHorizon + 1200); //vision limit is event horizon + // me.accelMag = 0.00006 * simulation.accelScale; + // // me.collisionFilter.mask = cat.player | cat.bullet //| cat.body + // // me.frictionAir = 0.005; + // me.memory = Infinity; + // Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger + // me.onDeath = function() { + // powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // }; + // me.damageReduction = 0.23 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + // me.do = function() { + // //keep it slow, to stop issues from explosion knock backs + // if (!(simulation.cycle % this.seePlayerFreq)) { + // if (this.distanceToPlayer2() < this.seeAtDistance2) { //&& !m.isCloak ignore cloak for black holes + // this.locatePlayer(); + // if (!this.seePlayer.yes) this.seePlayer.yes = true; + // } else if (this.seePlayer.recall) { + // this.lostPlayer(); + // } + // } + // this.checkStatus(); + // if (this.seePlayer.recall) { + // //accelerate towards the player + // const forceMag = this.accelMag * this.mass; + // const dx = this.seePlayer.position.x - this.position.x + // const dy = this.seePlayer.position.y - this.position.y + // const mag = Math.sqrt(dx * dx + dy * dy) + // this.force.x += forceMag * dx / mag; + // this.force.y += forceMag * dy / mag; + + // //eventHorizon waves in and out + // const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)) + // // zoom camera in and out with the event horizon + + // //draw darkness + // if (!simulation.isTimeSkipping) { + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,0,0,0.05)"; + // ctx.fill(); + // //when player is inside event horizon + // if (Vector.magnitude(Vector.sub(this.position, player.position)) < eventHorizon) { + // // if (!(simulation.cycle % 10)) simulation.timePlayerSkip(5) + // // if (!(simulation.cycle % 2)) simulation.timePlayerSkip(1) + // simulation.timePlayerSkip(2) + + // // if (m.immuneCycle < m.cycle) { + // // if (m.energy > 0) m.energy -= 0.004 + // // if (m.energy < 0.1) m.damage(0.00017 * simulation.dmgScale); + // // } + // } + // } + // } + // } + // }, suckerBoss(x, y, radius = 25) { mobs.spawn(x, y, 12, radius, "#000"); let me = mob[mob.length - 1]; @@ -1808,7 +1870,6 @@ const spawn = { y: this.velocity.y + velocity.y }); } - //accelerate towards the player const forceMag = this.accelMag * this.mass; const dx = this.seePlayer.position.x - this.position.x @@ -1816,11 +1877,8 @@ const spawn = { const mag = Math.sqrt(dx * dx + dy * dy) this.force.x += forceMag * dx / mag; this.force.y += forceMag * dy / mag; - //eventHorizon waves in and out - const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)) - // zoom camera in and out with the event horizon - + const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)) // zoom camera in and out with the event horizon //draw darkness ctx.beginPath(); ctx.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI); @@ -2229,88 +2287,88 @@ const spawn = { } spawn.allowShields = true; }, - // timeSkipBoss(x, y, radius = 55) { - // mobs.spawn(x, y, 6, radius, '#000'); - // let me = mob[mob.length - 1]; - // me.isBoss = true; - // // me.stroke = "transparent"; //used for drawSneaker - // me.timeSkipLastCycle = 0 - // me.eventHorizon = 1800; //required for black hole - // me.seeAtDistance2 = (me.eventHorizon + 2000) * (me.eventHorizon + 2000); //vision limit is event horizon + 2000 - // me.accelMag = 0.0004 * simulation.accelScale; - // // me.frictionAir = 0.005; - // // me.memory = 1600; - // // Matter.Body.setDensity(me, 0.02); //extra dense //normal is 0.001 //makes effective life much larger - // Matter.Body.setDensity(me, 0.0005 + 0.00018 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - // spawn.shield(me, x, y, 1); + timeSkipBoss(x, y, radius = 55) { + mobs.spawn(x, y, 6, radius, '#000'); + let me = mob[mob.length - 1]; + me.isBoss = true; + // me.stroke = "transparent"; //used for drawSneaker + me.timeSkipLastCycle = 0 + me.eventHorizon = 1800; //required for black hole + me.seeAtDistance2 = (me.eventHorizon + 2000) * (me.eventHorizon + 2000); //vision limit is event horizon + 2000 + me.accelMag = 0.0004 * simulation.accelScale; + // me.frictionAir = 0.005; + // me.memory = 1600; + // Matter.Body.setDensity(me, 0.02); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 0.0005 + 0.00018 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + spawn.shield(me, x, y, 1); - // me.onDeath = function() { - // //applying forces to player doesn't seem to work inside this method, not sure why - // powerUps.spawnBossPowerUp(this.position.x, this.position.y) - // }; - // me.do = function() { - // //keep it slow, to stop issues from explosion knock backs - // if (this.speed > 8) { - // Matter.Body.setVelocity(this, { - // x: this.velocity.x * 0.99, - // y: this.velocity.y * 0.99 - // }); - // } - // this.seePlayerCheck(); - // this.checkStatus(); - // this.attraction() - // if (!simulation.isTimeSkipping) { - // const compress = 1 - // if (this.timeSkipLastCycle < simulation.cycle - compress && - // Vector.magnitude(Vector.sub(this.position, player.position)) < this.eventHorizon) { - // this.timeSkipLastCycle = simulation.cycle - // simulation.timeSkip(compress) + me.onDeath = function() { + //applying forces to player doesn't seem to work inside this method, not sure why + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.do = function() { + //keep it slow, to stop issues from explosion knock backs + if (this.speed > 8) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.99, + y: this.velocity.y * 0.99 + }); + } + this.seePlayerCheck(); + this.checkStatus(); + this.attraction() + if (!simulation.isTimeSkipping) { + const compress = 1 + if (this.timeSkipLastCycle < simulation.cycle - compress && + Vector.magnitude(Vector.sub(this.position, player.position)) < this.eventHorizon) { + this.timeSkipLastCycle = simulation.cycle + simulation.timeSkip(compress) - // this.fill = `rgba(0,0,0,${0.4+0.6*Math.random()})` - // this.stroke = "#014" - // this.isShielded = false; - // this.isDropPowerUp = true; - // this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can't touch bullets + this.fill = `rgba(0,0,0,${0.4+0.6*Math.random()})` + this.stroke = "#014" + this.isShielded = false; + this.isDropPowerUp = true; + this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can't touch bullets - // ctx.beginPath(); - // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); - // ctx.fillStyle = "#fff"; - // ctx.globalCompositeOperation = "destination-in"; //in or atop - // ctx.fill(); - // ctx.globalCompositeOperation = "source-over"; - // ctx.beginPath(); - // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); - // ctx.clip(); + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + ctx.fillStyle = "#fff"; + ctx.globalCompositeOperation = "destination-in"; //in or atop + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + ctx.clip(); - // // ctx.beginPath(); - // // ctx.arc(this.position.x, this.position.y, 9999, 0, 2 * Math.PI); - // // ctx.fillStyle = "#000"; - // // ctx.fill(); - // // ctx.strokeStyle = "#000"; - // // ctx.stroke(); + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, 9999, 0, 2 * Math.PI); + // ctx.fillStyle = "#000"; + // ctx.fill(); + // ctx.strokeStyle = "#000"; + // ctx.stroke(); - // // ctx.beginPath(); - // // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); - // // ctx.fillStyle = `rgba(0,0,0,${0.05*Math.random()})`; - // // ctx.fill(); - // // ctx.strokeStyle = "#000"; - // // ctx.stroke(); - // } else { - // this.isShielded = true; - // this.isDropPowerUp = false; - // this.seePlayer.recall = false - // this.fill = "transparent" - // this.stroke = "transparent" - // this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob; //can't touch bullets - // ctx.beginPath(); - // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); - // ctx.fillStyle = `rgba(0,0,0,${0.05*Math.random()})`; - // ctx.fill(); - // } - // } - // } - // }, + // ctx.beginPath(); + // ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + // ctx.fillStyle = `rgba(0,0,0,${0.05*Math.random()})`; + // ctx.fill(); + // ctx.strokeStyle = "#000"; + // ctx.stroke(); + } else { + this.isShielded = true; + this.isDropPowerUp = false; + this.seePlayer.recall = false + this.fill = "transparent" + this.stroke = "transparent" + this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob; //can't touch bullets + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, this.eventHorizon, 0, 2 * Math.PI); + ctx.fillStyle = `rgba(0,0,0,${0.05*Math.random()})`; + ctx.fill(); + } + } + } + }, beamer(x, y, radius = 15 + Math.ceil(Math.random() * 15)) { mobs.spawn(x, y, 4, radius, "rgb(255,0,190)"); let me = mob[mob.length - 1]; @@ -3456,33 +3514,6 @@ const spawn = { } } } - - //invulnerable every other revolution - // me.isInvulnerable = false - // me.invulnerable = function() { - // //draw trigger angle - // if (this.laserAngle % (4 * Math.PI) > 2 * Math.PI) { - // if (!this.isInvulnerable) { - // this.isInvulnerable = true - // if (this.damageReduction) this.startingDamageReduction = this.damageReduction - // this.damageReduction = 0 - // } - // } else if (this.isInvulnerable) { - // this.isInvulnerable = false - // this.damageReduction = this.startingDamageReduction - // } - // if (this.isInvulnerable) { - // ctx.beginPath(); - // let vertices = this.vertices; - // ctx.moveTo(vertices[0].x, vertices[0].y); - // for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); - // ctx.lineTo(vertices[0].x, vertices[0].y); - // ctx.lineWidth = 20; - // ctx.strokeStyle = "rgba(255,255,255,0.7)"; - // ctx.stroke(); - // } - // } - me.do = function() { this.invulnerable(); this.checkStatus(); @@ -3893,6 +3924,93 @@ const spawn = { // } }; }, + timeBoss(x, y, radius = 50, isSpawnBossPowerUp = true) { + mobs.spawn(x, y, 0, radius, `hsl(0, 100%, 50%)`) // "rgb(201,202,225)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, 2 * Math.PI * Math.random()); + me.isBoss = true; + Matter.Body.setDensity(me, 0.001); //normal is 0.001 + me.inertia = Infinity; + me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.frictionAir = 0.01 + me.restitution = 1 + me.friction = 0 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob + Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); + me.seePlayer.recall = 1; + spawn.spawnOrbitals(me, radius + 15, 1) + + // me.skipRate = 1 + Math.floor(simulation.difficulty*0.02) + // spawn.shield(me, x, y, 1); + + me.onDamage = function() { + if (this.health < this.nextHealthThreshold) { + this.health = this.nextHealthThreshold - 0.01 + this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 + this.invulnerableCount = 360 + simulation.difficulty * 4 //how long does invulnerable time last + this.isInvulnerable = true + this.damageReduction = 0 + // requestAnimationFrame(() => { simulation.timePlayerSkip(300) }); //wrapping in animation frame prevents errors, probably + } + }; + me.onDeath = function() { + if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) + requestAnimationFrame(() => { simulation.timePlayerSkip(30) }); //wrapping in animation frame prevents errors, probably + }; + + me.cycle = Math.floor(360 * Math.random()) + me.nextHealthThreshold = 0.75 + me.invulnerableCount = 0 + // console.log(me.mass) //100 + me.do = function() { + this.cycle++ + this.fill = `hsl(${this.cycle*0.5}, 100%, 80%)`; + // this.fill = `hsl(${270 + 50*Math.sin(this.cycle*0.02)}, 100%, 60%)`; + this.seePlayer.recall = 1 + //maintain speed //faster in the vertical to help avoid repeating patterns + if (this.speed < 0.01) { + const unit = Vector.sub(player.position, this.position) + Matter.Body.setVelocity(this, Vector.mult(Vector.normalise(unit), 0.1)); + } else { + if (Math.abs(this.velocity.y) < 9) Matter.Body.setVelocity(this, { x: this.velocity.x, y: this.velocity.y * 1.02 }); + if (Math.abs(this.velocity.x) < 6.5) Matter.Body.setVelocity(this, { x: this.velocity.x * 1.02, y: this.velocity.y }); + } + + if (this.isInvulnerable) { + this.invulnerableCount-- + if (this.invulnerableCount < 0) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + } + //time dilation + if (!simulation.isTimeSkipping) { + requestAnimationFrame(() => { simulation.timePlayerSkip(1) }); //wrapping in animation frame prevents errors, probably + // if (!(simulation.cycle % 10)) + + //draw invulnerable + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 15 + 6 * Math.random(); + ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.stroke(); + } + } + + this.checkStatus(); + //horizontal attraction + // const xMag = 0.0005 + // if (player.position.x > this.position.x + 200) { + // this.force.x += xMag * this.mass; + // } else if (player.position.x < this.position.x - 200) { + // this.force.x -= xMag * this.mass; + // } + }; + }, bounceBullet(x, y, velocity = { x: 0, y: 0 }, radius = 11, sides = 6) { //bullets mobs.spawn(x, y, sides, radius, "rgb(255,0,155)"); @@ -5668,7 +5786,7 @@ const spawn = { x: Math.cos(time), y: Math.sin(time) } - Matter.Body.setPosition(this, Vector.add(who.position, Vector.mult(orbit, radius))) //bullets move with player + Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius))) //bullets move with player //damage player if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles @@ -5685,26 +5803,24 @@ const spawn = { } }; }, - orbitalBoss(x, y, radius = 88) { + orbitalBoss(x, y, radius = 70) { const nodeBalance = Math.random() const nodes = Math.min(15, Math.floor(2 + 4 * nodeBalance + 0.75 * Math.sqrt(simulation.difficulty))) mobs.spawn(x, y, nodes, radius, "rgb(255,0,150)"); let me = mob[mob.length - 1]; me.isBoss = true; - Matter.Body.setDensity(me, 0.0017 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.stroke = "transparent"; //used for drawGhost me.seeAtDistance2 = 2000000; + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map me.memory = Infinity; me.frictionAir = 0.04; - me.accelMag = 0.0003 * simulation.accelScale - me.collisionFilter.mask = cat.player | cat.bullet //| cat.body + me.accelMag = 0.0007 * simulation.accelScale spawn.shield(me, x, y, 1); const rangeInnerVsOuter = Math.random() let speed = (0.009 + 0.0011 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) - let range = radius + 400 + 200 * rangeInnerVsOuter + nodes * 7 + let range = radius + 300 + 200 * rangeInnerVsOuter + nodes * 7 for (let i = 0; i < nodes; i++) spawn.orbital(me, range, i / nodes * 2 * Math.PI, speed) const orbitalIndexes = [] //find indexes for all the current nodes for (let i = 0; i < nodes; i++) orbitalIndexes.push(mob.length - 1 - i) @@ -5718,10 +5834,9 @@ const spawn = { me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; - me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.18 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.do = function() { - // this.armor(); - this.seePlayerCheckByDistance(); + this.seePlayerByHistory(); this.checkStatus(); this.attraction(); }; diff --git a/js/tech.js b/js/tech.js index f570c90..da34755 100644 --- a/js/tech.js +++ b/js/tech.js @@ -602,9 +602,9 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isAmmoFromHealth && !tech.isEnergyNoAmmo + return !tech.isAmmoFromHealth }, - requires: "not catabolism, ideal gas law", + requires: "not catabolism", effect() { tech.isEnergyNoAmmo = true; }, @@ -4661,9 +4661,9 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.implosion === -1 && tech.explosiveRadius === 1 && !tech.isSmallExplosion && !tech.isBlockExplode && !tech.fragments && (tech.haveGunCheck("missiles") || tech.missileBotCount || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.isPulseLaser || tech.isMissileField || tech.isBoomBotUpgrade || tech.isTokamak) + return tech.explosiveRadius === 1 && !tech.isSmallExplosion && !tech.isBlockExplode && !tech.fragments && (tech.haveGunCheck("missiles") || tech.missileBotCount || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.isPulseLaser || tech.isMissileField || tech.isBoomBotUpgrade || tech.isTokamak) }, - requires: "an explosive damage source, not ammonium nitrate, nitroglycerin, chain reaction, fragmentation, implosion", + requires: "an explosive damage source, not ammonium nitrate, nitroglycerin, chain reaction, fragmentation", effect: () => { tech.isExplodeRadio = true; //iridium-192 }, @@ -4976,9 +4976,9 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb && !tech.isExplodeRadio && !tech.isBlockExplode && !tech.isClusterExplode && tech.implosion === -1 + return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb && !tech.isExplodeRadio && !tech.isBlockExplode && !tech.isClusterExplode }, - requires: "grenades, not fragmentation, vacuum bomb, iridium-192, pyrotechnics, implosion", + requires: "grenades, not fragmentation, vacuum bomb, iridium-192, pyrotechnics", effect() { tech.isNeutronBomb = true; b.setGrenadeMode() @@ -7106,7 +7106,7 @@ const tech = { allowed() { return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isEnergyHealth && !tech.isTimeSkip }, - requires: "time dilation, not CPT symmetry, mass-energy, timelike", + requires: "time dilation, not CPT symmetry, mass-energy", effect() { tech.isRewindField = true; m.fieldUpgrades[m.fieldMode].set() @@ -7117,25 +7117,6 @@ const tech = { if (this.count) m.fieldUpgrades[m.fieldMode].set() } }, - // { - // name: "timelike", - // description: "time dilation doubles your relative time rate
and makes you immune to harm", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindField - // }, - // requires: "time dilation, not retrocausality", - // effect() { - // tech.isTimeSkip = true; - // }, - // remove() { - // tech.isTimeSkip = false; - // } - // }, { name: "Lorentz transformation", description: `use ${powerUps.orb.research(3)}to increase your time rate
move, jump, and shoot 50% faster`, @@ -7887,6 +7868,33 @@ const tech = { }, remove() {} }, + { + name: "closed timelike curve", + description: "spawn 5 field power ups, but every 12 seconds
teleport a second into your future
", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); + + function loop() { + if (!simulation.paused && m.alive) { + if (!(simulation.cycle % 720)) { + requestAnimationFrame(() => { simulation.timePlayerSkip(60) }); //wrapping in animation frame prevents errors, probably + } + } + requestAnimationFrame(loop); + } + requestAnimationFrame(loop); + }, + remove() {} + }, { name: "discount", description: "get 3 random JUNK tech for the price of 1!", diff --git a/todo.txt b/todo.txt index 71f4bab..8f983c2 100644 --- a/todo.txt +++ b/todo.txt @@ -1,16 +1,69 @@ ******************************************************** NEXT PATCH ************************************************** -ideal gas law - lose all your current foam ammo; foam gun gets 1200% more ammo from ammo power ups -pressure vessel - has 25% more charges produced as you hold +new reactor boss: timeBoss - after taking some damage it speeds up the passage of time +reactor level has big doors -slashBoss is a bit slower and has a bit longer between slashes -reactor SprayBoss: slower to fire, slower to move, smaller bullets, fewer in number - just a tiny nerf though - -bug fixes +nonrefundable tech show up in the pause menu +time dilation field - move, jump, and fire 25% faster +JUNK tech: closed timelike curve - spawn 5 field power ups, but every 12 seconds teleport a second into your future ******************************************************** TODO ******************************************************** +if a mob dies in skiptime it doesn't register? + is this only for the ondeath event? + so far, but needs more tests + is this only for timeskip > 1 + yes I think + +make MEE work with harm reduction + how to nerf MEE + maybe harm reduction could also reduce energy regen + +timeSkipBoss + sends out waves of 60s time skip every 5 seconds? + or 1 cycle skip every other cycle + only active when player is inside range + immune to harm outside range? + +simulation.timeSkip(60) + skip every other cycle inside black holes? + maybe run simulation.timeSkip(60) in a map + in labs? + reactor boss + mob fires laser/bullets that timeSkip player + maybe an effect when in range of the historyBoss + JUNK tech simulation.timeSkip(60) + add CPT graphics to simulation.timeSkip(60) + for player + tech requires eternalism - 2x time while choosing, gain more damage + +Currently, the mob just deals higher damage on impact, which is annoying although not hard to compete with nor unique +By "redesign" I mean replacing instances of the regular mob, since the same code is used for the tiny red projectiles (I think) just add a new mob and remove the old one from the rotation +The new mob should be as such, a "real" exploding mob: +Deals regular damage on impact, but breaks apart on touch into several red dots (like the ones thrown out by the going through walls boss) and a chance for also throwing a bomb or two (increases with difficulty) +If the mob is close to the player or heading into the player fast it will shatter as well (and the projectiles will inherit its speed) +By a formula such as +ShouldShatter(distToPlayer, speed) = distToPlayer * speed > threshold +Optionally (and a part I can do as I'm good at it and it doesn't revolve around a lot of functional code which you don't like other people doing): +Color changing based on the mob explosion status +Regular state: red +About to explode: animation to dark red +Exploding: several shockwaves from the explosion points and tiny trails given to the shrapnel for a second or two until they deaccelerate + + + +make laser gain damage and energy drain from fire delay tech + wording? put it in the gun description + +reactor: add horizontal flip mode + +pause time like invariant for other things... + throwing blocks + charging railgun + charging anything? + +give retrocausality a short delay before it rewinds, so you can pick up powerups without going back + tech expansion: should also make other fields do things how to make the description work change description based on your current field? @@ -24,9 +77,6 @@ tech expansion: should also make other fields do things reduces the cloaking vision effect? needs more benefit? -nonrefundable tech don't display, this is confusing - maybe they can show up but greyed out or something - guntech fire a bullet that fires nail fragments after 1s in the same direction as the original bullet like overwatch roadhog @@ -146,72 +196,14 @@ setting to remove UI, except health bar except active gun? to see ammo checkbox in pause and in settings -pause time like invariant for other things... - throwing blocks - charging railgun - charging anything? - bug - url sharing still broken sometimes - -maybe? timing QTE for charging effects, if you fire right before the charge gets full it's better - -+damage for each different bot type you have - disables bot upgrades? - -tech: doing damage to mobs refunds up to 50% of damage taken in last 10 seconds - use history[] to manage this? - -make a seed/hash system that controls only the tech/guns/fields shown - URL sharing could include a seed - seed controls: - seeded random at start - random level boss, mob types list, level order, horizontal flip - seeded random during run - tech, gun, field choices - not seeded random - mob spawns, mob size, minor level differences, custom level boss choices, ammo rewards, tech effect randomness - better to only seed things at the start of the run so it doesn't mess with power up choices - make option for a daily seed: seed = day+year - give 1 extra tech for doing the daily seeded run - make the option for the daily run, a secret exit in the intro level? tech upgrade to anthropic principle to make it trigger at 50% life and 0% once per map -tech: spontaneous collapse - very low chance of something to occur - JUNK tech - https://bindingofisaacrebirth.fandom.com/wiki/Damocles - bug? cloaking field doesn't show energy over max run more profiles of n-gon to fix performance issues -reactor - life-like cellular automata boss - https://scratch.mit.edu/projects/77724260/ - night/day? - boss on far right produces more "on" cells - tailBoss - story the last 5 seconds of position, draw damage circles over those positions, like a tail - limit mantis boss to only reactor? - laserBoss? - swoopBoss: hides in top center - take a lap around the map that you have to dodge - spawn mobs - fire sniper bullets? - remove center map element? - add some blocks to the reactor level - add ammo? - boss speeds up as time goes on? - slow bullet speed? - foam hits all the bullets and makes this fight easy - give map some background graphics - it's a little short - add alternate boss - add a boss that spawns after the first one dies - balance - boss damage reduction - bullet size - bullet spawn number - bounce speed - add horizontal flip mode - bug: possibly clearing away all bullets causes a problem bullet.js 255 (.do() is missing) I died and quantum immortality triggered (I had needles and ice-IX)