From 8bee75cf6e7eaee5cce61efd9769d95ead18f099 Mon Sep 17 00:00:00 2001 From: landgreen Date: Sun, 30 Jan 2022 19:15:09 -0800 Subject: [PATCH] level - reactor new level: reactor - midBoss fight it's not well balanced yet Let me know if there are any impossible gun combinations for new players the training button at the start screen now cycles colors effect shows if you haven't cleared the first training level, and you haven't done at least a few normal runs standing wave expansion tech is 40% larger and gives 25% deflecting efficiency ammonium nitrate gives 30 -> 27% damage and range heuristics gives 30 -> 33% fire rate wormhole invariant tech drains energy much slower while time is paused bug fixes null level now longer progresses level.onLevel --- .DS_Store | Bin 6148 -> 6148 bytes index.html | 8 +-- js/index.js | 40 +++++++++--- js/level.js | 165 +++++++++++++++++++++++++++++++++++++++++--------- js/mob.js | 12 +++- js/player.js | 6 +- js/powerup.js | 27 ++++----- js/spawn.js | 112 ++++++++++++++++++++++++++++++++-- js/tech.js | 44 ++++++++------ todo.txt | 50 +++++++++++---- 10 files changed, 365 insertions(+), 99 deletions(-) diff --git a/.DS_Store b/.DS_Store index de36ad27121008112da126ab86a5b99268b90933..8bcfebdac5361e02dbb18ee9be304f42bb079f12 100644 GIT binary patch delta 21 ccmZoMXffEJ#mrRmXR;2nALE71)y$nD08*a^ZvX%Q delta 21 ccmZoMXffEJ#mwaQaexperiment - - - - training - + + + training
diff --git a/js/index.js b/js/index.js index 00d449a..715d31f 100644 --- a/js/index.js +++ b/js/index.js @@ -132,6 +132,25 @@ window.addEventListener('load', () => { if (property === "level") document.getElementById("starting-level").value = Math.max(Number(set[property]) - 1, 0) if (property === "noPower") document.getElementById("no-power-ups").checked = Number(set[property]) } + } else if (localSettings.isTrainingNotAttempted && localSettings.runCount < 30) { //make training button more obvious for new players + // document.getElementById("training-button").style.border = "0px #333 solid"; + // document.getElementById("training-button").style.fill = "rgb(0, 150, 235)" //"#fff"; + // document.getElementById("training-button").style.background = "rgb(0, 200, 255)"; + + //css classes not working for some reason + // document.getElementById("training-button").classList.add('lore-text'); + + let myanim = document.createElementNS("http://www.w3.org/2000/svg", 'animate'); + myanim.setAttribute("id", "myAnimation"); + myanim.setAttribute("attributeType", "XML"); + myanim.setAttribute("attributeName", "fill"); + // myanim.setAttribute("values", "#f55;#cc5;#5c5;#5dd;#66f;#5dd;#5c5;#cc5;#f55"); //rainbow + myanim.setAttribute("values", "#5dd;#66f;#5dd"); + // myanim.setAttribute("values", "#333;rgb(0, 170, 255);#333"); + myanim.setAttribute("dur", "3s"); + myanim.setAttribute("repeatCount", "indefinite"); + document.getElementById("training-button").appendChild(myanim); + document.getElementById("myAnimation").beginElement(); } }); @@ -252,25 +271,25 @@ ${simulation.isCheating ? "

lore disabled": ""} text += `
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
${m.fieldUpgrades[m.fieldMode].description}
` for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) { - const isCount = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; + const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; if (tech.tech[i].isFieldTech) { text += `
-         ${tech.tech[i].link} ${isCount}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` +         ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}` } else if (tech.tech[i].isGunTech) { text += `
-         ${tech.tech[i].link} ${isCount}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` +         ${tech.tech[i].link} ${techCountText}${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}` } else if (tech.tech[i].isLore) { - text += `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + text += `
  ${tech.tech[i].name} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` } else { - text += `
  ${tech.tech[i].link} ${isCount}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + text += `
  ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` } } else if (tech.tech[i].isLost) { text += `
${tech.tech[i].link}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` @@ -355,7 +374,7 @@ ${simulation.isCheating ? "

lore disabled": ""} if (!tech.tech[i].isExperimentHide && (!tech.tech[i].isNonRefundable || tech.tech[i].isExperimentalMode || (localSettings.isJunkExperiment && tech.tech[i].isJunk))) { if (tech.tech[i].allowed() || isAllowed || tech.tech[i].count > 0) { // console.log(tech.tech[i].name, isAllowed, tech.tech[i].count, tech.haveGunCheck("nail gun")) - const isCount = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; + const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; //
if (tech.tech[i].isFieldTech) { @@ -367,7 +386,7 @@ ${simulation.isCheating ? "

lore disabled": ""}
-         ${tech.tech[i].link} ${isCount}${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description} +         ${tech.tech[i].link} ${techCountText}${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description} ` //
//
@@ -380,15 +399,15 @@ ${simulation.isCheating ? "

lore disabled": ""}
-         ${tech.tech[i].link} ${isCount}${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description} +         ${tech.tech[i].link} ${techCountText}${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description} ` } else if (tech.tech[i].isJunk) { - techID.innerHTML = `
  ${tech.tech[i].link} ${isCount}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}` + techID.innerHTML = `
  ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` } else if (tech.tech[i].isExperimentalMode) { techID.innerHTML = `
${tech.tech[i].name}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}` } else { - techID.innerHTML = `
  ${tech.tech[i].link} ${isCount}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}` + techID.innerHTML = `
  ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` } //deselect selected tech options if you don't have the tech any more // for example: when bot techs are converted after a bot upgrade tech is taken if (tech.tech[i].count === 0 && techID.classList.contains("build-tech-selected")) techID.classList.remove("build-tech-selected"); @@ -1161,6 +1180,7 @@ if (localSettings) { difficultyMode: '2', fpsCapDefault: 'max', runCount: 0, + isTrainingNotAttempted: true, levelsClearedLastGame: 0, loreCount: 0, isHuman: false, diff --git a/js/level.js b/js/level.js index a06d451..e39f970 100644 --- a/js/level.js +++ b/js/level.js @@ -7,7 +7,8 @@ const level = { defaultZoom: 1400, onLevel: -1, levelsCleared: 0, - playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion"], //see level.populateLevels: (intro, ... , reservoir, ... , gauntlet, final) added later + //see level.populateLevels: (intro, ... , reservoir, reactor, ... , gauntlet, final) added later + playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion"], communityLevels: ["stronghold", "basement", "crossfire", "vats", "run", "n-gon", "house", "perplex", "coliseum", "tunnel"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"], levels: [], @@ -15,23 +16,23 @@ const level = { if (level.levelsCleared === 0) { //this code only runs on the first level // m.immuneCycle = Infinity //you can't take damage // localSettings.levelsClearedLastGame = 10 - // level.difficultyIncrease(1) //30 is near max on hard //60 is near max on why + // spawn.setSpawnList();spawn.setSpawnList(); + // level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why // simulation.isHorizontalFlipped = true - // m.setField("time dilation") - // b.giveGuns("harpoon") - // for (let i = 0; i < 9; i++) tech.giveTech("slow light") - // tech.giveTech("grenade production") + // m.setField("standing wave") + // b.giveGuns("laser") + // for (let i = 0; i < 100; i++) tech.giveTech("slow light") + // tech.giveTech("expansion") // for (let i = 0; i < 2; i++) powerUps.directSpawn(0, 0, "tech"); - // tech.giveTech("charmed baryons") // tech.giveTech("tinsellated flagella") - // for (let i = 0; i < 2; i++) tech.giveTech("refractory metal") - // tech.giveTech("antiscience") + // for (let i = 0; i < 3; i++) tech.giveTech("undefined") + // tech.giveTech("nail-bot") // for (let i = 0; i < 1; i++) tech.giveTech("reticulum") // for (let i = 0; i < 2; i++) tech.giveTech("laser-bot") // tech.tech[297].frequency = 100 // simulation.enableConstructMode() //used to build maps in testing mode - // level.pavilion(); + // level.reactor(); // level.testing(); //not in rotation, used for testing if (simulation.isTraining) { level.walk(); } else { level.intro(); } @@ -41,7 +42,7 @@ const level = { // for (let i = 0; i < 3; i++) tech.giveTech("undefined") // lore.techCount = 3 // simulation.isCheating = false //true; - // localSettings.loreCount = 0; //this sets what conversation is heard + // localSettings.loreCount = 2; //this sets what conversation is heard // localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage // level.onLevel = -1 //this sets level.levels[level.onLevel] = undefined which is required to run the conversation // level.null() @@ -127,29 +128,29 @@ const level = { b.dmgScale = 1; //damage done by player decreases each level simulation.accelScale = 1 //mob acceleration increases each level simulation.CDScale = 1 //mob CD time decreases each level - simulation.dmgScale = 0.41 * simulation.difficulty //damage done by mobs increases each level + simulation.dmgScale = 0.38 * simulation.difficulty //damage done by mobs increases each level simulation.healScale = 1 / (1 + simulation.difficulty * 0.055) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; }, difficultyIncrease(num = 1) { for (let i = 0; i < num; i++) { simulation.difficulty++ - b.dmgScale *= 0.914; //damage done by player decreases each level + b.dmgScale *= 0.917; //damage done by player decreases each level if (simulation.accelScale < 6) simulation.accelScale *= 1.025 //mob acceleration increases each level if (simulation.CDScale > 0.15) simulation.CDScale *= 0.965 //mob CD time decreases each level } - simulation.dmgScale = 0.39 * simulation.difficulty //damage done by mobs scales with total levels + simulation.dmgScale = 0.38 * simulation.difficulty //damage done by mobs scales with total levels simulation.healScale = 1 / (1 + simulation.difficulty * 0.055) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; // console.log(`CD = ${simulation.CDScale}`) }, difficultyDecrease(num = 1) { //used in easy mode for simulation.reset() for (let i = 0; i < num; i++) { simulation.difficulty-- - b.dmgScale /= 0.914; //damage done by player decreases each level + b.dmgScale /= 0.917; //damage done by player decreases each level if (simulation.accelScale > 1) simulation.accelScale /= 1.025 //mob acceleration increases each level if (simulation.CDScale < 1) simulation.CDScale /= 0.965 //mob CD time decreases each level } if (simulation.difficulty < 1) simulation.difficulty = 0; - simulation.dmgScale = 0.39 * simulation.difficulty //damage done by mobs scales with total levels + simulation.dmgScale = 0.38 * simulation.difficulty //damage done by mobs scales with total levels if (simulation.dmgScale < 0.1) simulation.dmgScale = 0.1; simulation.healScale = 1 / (1 + simulation.difficulty * 0.055) }, @@ -235,6 +236,7 @@ const level = { level.levels = shuffle(level.levels); //shuffles order of maps } level.levels.splice(Math.floor(level.levels.length * (0.4 + 0.6 * Math.random())), 0, "reservoir"); //add level to the back half of the randomized levels list + level.levels.splice(Math.floor(level.levels.length * (0.4 + 0.6 * Math.random())), 0, "reactor"); //add level to the back half of the randomized levels list level.levels.splice(0, 2); //remove 2 levels from the start of the array if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech level.levels.unshift("intro"); //add level to the start of the randomized levels list @@ -2364,6 +2366,8 @@ const level = { }, null() { level.levels.pop(); //remove lore level from rotation + level.onLevel-- + console.log(level.onLevel, level.levels) //start a conversation based on the number of conversations seen if (localSettings.loreCount < lore.conversation.length && !simulation.isCheating) { lore.testSpeechAPI() //see if speech is working @@ -2588,6 +2592,116 @@ const level = { // spawn.suckerBoss(2900, -500) // spawn.randomMob(1600, -500) }, + reactor() { + level.setPosToSpawn(-50, -800); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = 3000; + level.exit.y = -35; + spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25); + level.defaultZoom = 2000 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#d0d5df" //"#d8dadf"; + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + spawn.mapRect(-1525, -2825, 1250, 3925); + spawn.mapRect(-400, -2025, 625, 925); + spawn.mapRect(-400, -750, 625, 1200); + + spawn.mapRect(-425, 0, 4200, 1100); + spawn.mapRect(175, -1250, 50, 300); + spawn.mapRect(-475, -2825, 4250, 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 + + //exit + spawn.mapRect(3300, -2825, 1125, 3925); + spawn.mapRect(2750, -2150, 1025, 1775); + spawn.mapRect(2750, -475, 50, 300); + + + + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // const doorIn = level.door(187, -450, 25, 250, 2) //x, y, width, height, distance, speed = 1 + // doorIn.isClosing = false + // level.custom = () => { + // level.exit.drawAndCheck(); + // // if (mob.length > 0) { + // // doorIn.isClosing = true + // // } else { + // // doorIn.isClosing = false + // // } + // doorIn.isClosing = !(mob.length > 0) + // doorIn.openClose(); + + 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 + doorOut.isClosing = true + let isDoorsLocked = false + let isFightOver = false + let isSpawnedBoss = false + + level.custom = () => { + if (isDoorsLocked) { + if (!isFightOver && !(simulation.cycle % 120)) { //once a second + let isFoundBoss = false + for (let i = 0; i < mob.length; i++) { + if (mob[i].isBoss) { + isFoundBoss = true + break + } + } + if (!isFoundBoss) { + isFightOver = true + doorIn.isClosing = false + doorOut.isClosing = false + powerUps.spawnBossPowerUp(2900, -100) + for (let i = 0; i < 3; ++i) powerUps.spawn(2900 + 30 * i, -300, "ammo"); + } + } + + // if (mob.length > 0) { + // doorIn.isClosing = true + // doorOut.isClosing = true + // } else if (!isFightOver) { + // isFightOver = true + // doorIn.isClosing = false + // doorOut.isClosing = false + // powerUps.spawnBossPowerUp(2900, -200) + // } + if (player.position.x < 0) { //if player gets trapped inside starting room open up again + isDoorsLocked = false + doorIn.isClosing = false + } + } else if (player.position.x > 225) { + isDoorsLocked = true + doorIn.isClosing = true + doorOut.isClosing = true + if (!isSpawnedBoss) { + isSpawnedBoss = true + for (let i = 0, len = simulation.difficulty / 20; i < len; ++i) spawn.bounceBoss(1487 + 300 * i, -1525, 80, false); + } + } + + doorIn.openClose(); + doorOut.openClose(); + level.enter.draw(); + level.exit.drawAndCheck(); + }; + level.customTopLayer = () => { + // if (isDoorsLocked) { + // ctx.fillStyle = "#333" + // ctx.fillRect(2800, -375, 500, 375); + // } + doorIn.draw(); + doorOut.draw(); + }; + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, template() { level.custom = () => { level.exit.drawAndCheck(); @@ -3309,12 +3423,7 @@ const level = { vanish.push(level.vanish(-350, -225, 150, 225)) vanish.push(level.vanish(-350, -450, 150, 223)) spawn.mapRect(2475, -1800, 250, 2300); - // vanish.push(level.vanish(1300, -375, 150, 225)) - // vanish.push(level.vanish(1300, -450, 150, 223)) - // spawn.mapRect(1200, -375, 250, 50); - // spawn.mapRect(1200, -375, 250, 225); - // spawn.mapRect(1200, -375, 175, 25); spawn.mapRect(1200, -750, 100, 450); spawn.mapRect(1200, -375, 250, 75); powerUps.spawnStartingPowerUps(550, -100); @@ -3322,11 +3431,6 @@ const level = { spawn.mapRect(175, -25, 750, 50); spawn.bodyRect(1350, -175, 150, 175, 0.5); spawn.bodyRect(1350, -600, 125, 225, 0.2); - //entrance - // vanish.push(level.vanish(-300, -500, 100, 25)) - // vanish.push(level.vanish(-450, -200, 100, 25)) - // spawn.bodyRect(-450, -175, 100, 175, 0.7); - // spawn.bodyRect(-250, -550, 50, 50, 0.7); //middle floor spawn.bodyRect(215, -1175, 100, 100, 0.3); @@ -5428,7 +5532,6 @@ const level = { ctx.fillRect(3650, -110, 1000, 170); ctx.fillRect(4865, -55, 100, 55); level.exit.drawAndCheck(); - level.enter.draw(); }; level.customTopLayer = () => { @@ -7378,8 +7481,6 @@ const level = { level.custom = () => { boost1.query(); - level.exit.drawAndCheck() - buttonGreen.query() buttonYellow.query() buttonRed.query() @@ -7521,6 +7622,7 @@ const level = { ctx.fillStyle = "#d4f4f4" ctx.fillRect(3500, 675, 600, 450) level.enter.draw() + level.exit.drawAndCheck() } level.customTopLayer = () => { @@ -8864,6 +8966,11 @@ const level = { spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, crouch() { //learn to crouch + if (localSettings.isTrainingNotAttempted) { //after making it to the second training level + localSettings.isTrainingNotAttempted = false // this makes the training button less obvious at the start screen + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + m.addHealth(Infinity) level.setPosToSpawn(75, -100); //normal spawn spawn.mapRect(25, -60, 100, 20); //small platform for player diff --git a/js/mob.js b/js/mob.js index 7b2ab66..8f0eb74 100644 --- a/js/mob.js +++ b/js/mob.js @@ -1154,7 +1154,17 @@ const mobs = { } if (tech.isBotSpawnerReset) { for (let i = 0, len = bullet.length; i < len; i++) { - if (bullet[i].botType && bullet[i].endCycle !== Infinity) bullet[i].endCycle = simulation.cycle + 840 //14 seconds + if (bullet[i].botType && bullet[i].endCycle !== Infinity) { + bullet[i].endCycle = simulation.cycle + 840 //14 seconds + // //draw a flash on top of bot + // ctx.beginPath(); + // const v = bullet[i].vertices; + // ctx.moveTo(v[0].x, v[0].y); + // for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y); + // ctx.lineTo(v[0].x, v[0].y); + // ctx.fillStyle = "#fff" + // ctx.fill(); + } } } if (Math.random() < tech.botSpawner) { diff --git a/js/player.js b/js/player.js index cb8006d..ea0e21b 100644 --- a/js/player.js +++ b/js/player.js @@ -1604,10 +1604,10 @@ const m = { if (tech.isStandingWaveExpand) { if (input.field) { // const oldHarmonicRadius = m.harmonicRadius - m.harmonicRadius = 0.985 * m.harmonicRadius + 0.015 * 2.5 + m.harmonicRadius = 0.99 * m.harmonicRadius + 0.01 * 4 // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius) } else { - m.harmonicRadius = 0.995 * m.harmonicRadius + 0.005 + m.harmonicRadius = 0.994 * m.harmonicRadius + 0.006 } } m.harmonicShield() @@ -3064,7 +3064,7 @@ const m = { if (input.field) { if (tech.isWormHolePause) { - const drain = m.fieldRegen + 0.0007 + const drain = m.fieldRegen + 0.0004 if (m.energy > drain) { m.energy -= drain if (m.immuneCycle < m.cycle + 1) m.immuneCycle = m.cycle + 1; //player is immune to damage for 1/4 seconds // and can't regen diff --git a/js/powerup.js b/js/powerup.js index 87c1e47..007d603 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -397,8 +397,7 @@ const powerUps = { const banishLength = tech.isDeterminism ? 1 : 3 + tech.isExtraChoice * 2 for (let i = 0; i < banishLength; i++) { const index = powerUps.tech.choiceLog.length - i - 1 - // console.log(index, powerUps.tech.choiceLog[index], tech.tech[powerUps.tech.choiceLog[index]].name) - if (powerUps.tech.choiceLog[index] !== undefined) tech.tech[powerUps.tech.choiceLog[index]].isBanished = true + if (powerUps.tech.choiceLog[index] !== undefined || powerUps.tech.choiceLog[index] !== null) tech.tech[powerUps.tech.choiceLog[index]].isBanished = true } simulation.makeTextLog(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - banishLength)}`) } @@ -470,7 +469,7 @@ const powerUps = { } if (tech.healGiveMaxEnergy) { - tech.healMaxEnergyBonus += 0.07 + tech.healMaxEnergyBonus += 0.08 m.setMaxEnergy(); } }, @@ -580,11 +579,14 @@ const powerUps = { if (!tech.isSuperDeterminism) text += `
` text += `

field

` text += `
  ${m.fieldUpgrades[choice1].name}
${m.fieldUpgrades[choice1].description}
` + powerUps.field.choiceLog.push(choice1) if (!tech.isDeterminism) { choice2 = powerUps.field.pick(m.fieldUpgrades, choice1) if (choice2 > -1) text += `
  ${m.fieldUpgrades[choice2].name}
${m.fieldUpgrades[choice2].description}
` choice3 = powerUps.field.pick(m.fieldUpgrades, choice1, choice2) if (choice3 > -1) text += `
  ${m.fieldUpgrades[choice3].name}
${m.fieldUpgrades[choice3].description}
` + powerUps.field.choiceLog.push(choice2) + powerUps.field.choiceLog.push(choice3) } if (tech.isExtraChoice) { let choice4 = powerUps.field.pick(m.fieldUpgrades, choice1, choice2, choice3) @@ -594,9 +596,6 @@ const powerUps = { powerUps.field.choiceLog.push(choice4) powerUps.field.choiceLog.push(choice5) } - powerUps.field.choiceLog.push(choice1) - powerUps.field.choiceLog.push(choice2) - powerUps.field.choiceLog.push(choice3) if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { tech.junkResearchNumber = Math.floor(4 * Math.random()) @@ -627,7 +626,7 @@ const powerUps = { // banishLog: [], //records all tech permanently removed from the selection pool effect() { if (m.alive) { - function pick(skip1 = -1, skip2 = -1, skip3 = -1, skip4 = -1) { + function pick(skip1 = null, skip2 = null, skip3 = null, skip4 = null) { let options = []; for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].count < tech.tech[i].maxCount && i !== skip1 && i !== skip2 && i !== skip3 && i !== skip4 && tech.tech[i].allowed() && !tech.tech[i].isBanished) { @@ -685,14 +684,17 @@ const powerUps = { if (!tech.isSuperDeterminism) text += `
` text += `

tech

` let choice1 = pick() - let choice2 = -1 - let choice3 = -1 - if (choice1 > -1) { + let choice2 = null + let choice3 = null + if (choice1 !== null) { + powerUps.tech.choiceLog.push(choice1) if (!tech.isDeterminism) { choice2 = pick(choice1) // if (choice2 > -1) text += `
  ${tech.tech[choice2].name}
${tech.tech[choice2].description}
` choice3 = pick(choice1, choice2) // if (choice3 > -1) text += `
  ${tech.tech[choice3].name}
${tech.tech[choice3].description}
` + powerUps.tech.choiceLog.push(choice2) + powerUps.tech.choiceLog.push(choice3) } if (tech.isExtraChoice) { let choice4 = pick(choice1, choice2, choice3) @@ -702,9 +704,6 @@ const powerUps = { powerUps.tech.choiceLog.push(choice4) powerUps.tech.choiceLog.push(choice5) } - powerUps.tech.choiceLog.push(choice1) - powerUps.tech.choiceLog.push(choice2) - powerUps.tech.choiceLog.push(choice3) // if (powerUps.research.count) text += `
  research ${powerUps.research.count}
` if (tech.isExtraGunField) { @@ -857,8 +856,6 @@ const powerUps = { for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += `
` text += `  ${tech.isResearchReality?"alternate reality": "research"}` } - // console.log(powerUps.gun.choiceLog) - // console.log(choice1, choice2, choice3) if (tech.isOneGun && b.inventory.length > 0) text += `
replaces your current gun
` document.getElementById("choose-grid").innerHTML = text powerUps.showDraft(); diff --git a/js/spawn.js b/js/spawn.js index 63b2042..e17927c 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -1197,7 +1197,7 @@ const spawn = { } else if (!m.isCloak) { me.foundPlayer(); } - me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.22 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.isInvulnerable = true me.startingDamageReduction = me.damageReduction me.damageReduction = 0 @@ -1276,7 +1276,7 @@ const spawn = { me.foundPlayer(); } - me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.damageReduction = 0.22 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) // me.isInvulnerable = true // me.startingDamageReduction = me.damageReduction // me.damageReduction = 0 @@ -2030,7 +2030,7 @@ const spawn = { this.cons2.length = 100 + 1.5 * this.radius; this.isInvulnerable = false - this.invulnerabilityCountDown = 60 + Math.max(0, 70 - simulation.difficulty * 0.5) + this.invulnerabilityCountDown = 70 + Math.max(0, 70 - simulation.difficulty * 0.5) this.damageReduction = this.startingDamageReduction for (let i = 0; i < this.babyList.length; i++) { if (this.babyList[i].alive) this.babyList[i].damageReduction = this.startingDamageReduction @@ -2044,7 +2044,7 @@ const spawn = { this.cons2.length = -200; this.isInvulnerable = false - this.invulnerabilityCountDown = 60 + Math.max(0, 70 - simulation.difficulty) + this.invulnerabilityCountDown = 70 + Math.max(0, 70 - simulation.difficulty) this.damageReduction = this.startingDamageReduction for (let i = 0; i < this.babyList.length; i++) { if (this.babyList[i].alive) this.babyList[i].damageReduction = this.startingDamageReduction @@ -3518,6 +3518,110 @@ const spawn = { ctx.setLineDash([]); } }, + bounceBoss(x, y, radius = 80, isSpawnBossPOwerUp = true) { + mobs.spawn(x, y, 0, radius, "rgb(255,255,255)") // "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.003); //normal is 0.001 + me.inertia = Infinity; + me.damageReduction = 0.1 / (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.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.fireCount = 60 + simulation.difficulty * 1.5 + this.isInvulnerable = true + this.damageReduction = 0 + } + }; + if (isSpawnBossPOwerUp) me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; + me.cycle = 0 + me.nextHealthThreshold = 0.75 + me.fireCount = 0 + // console.log(me.mass) //100 + me.do = function() { + if (this.isInvulnerable) { + this.fireCount-- + if (this.fireCount < 0) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + } + + if (this.mass > 10) Matter.Body.scale(this, 0.99, 0.99); + + // for (let i = 0; i < 1; i++) { + const velocity = Vector.rotate(Vector.mult(Vector.normalise(this.velocity), -10 - 10 * Math.random()), 1 * (Math.random() - 0.5)) + spawn.bounceBullet(this.position.x, this.position.y, velocity) + // } + //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 = 20; + ctx.strokeStyle = "rgba(255,255,255,0.7)"; + ctx.stroke(); + } else if (this.mass < 100) { + Matter.Body.scale(this, 1.01, 1.01); //grow back to normal size + } + + 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; + // } + + //maintain speed //faster in the vertical to help avoid repeating patterns + if (Math.abs(this.velocity.y) < 15) { + Matter.Body.setVelocity(this, { x: this.velocity.x, y: this.velocity.y * 1.05 }); + } + if (Math.abs(this.velocity.x) < 11) { + Matter.Body.setVelocity(this, { x: this.velocity.x * 1.05, y: this.velocity.y }); + } + }; + }, + bounceBullet(x, y, velocity = { x: 0, y: 0 }, radius = 10, sides = 6) { + //bullets + mobs.spawn(x, y, sides, radius, "rgb(255,0,155)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + Matter.Body.setDensity(me, 0.00003); //normal is 0.001 + me.timeLeft = 360 + Math.floor(180 * Math.random()) + me.inertia = Infinity; + me.damageReduction = 1 + me.frictionAir = 0 + me.friction = 0 + me.restitution = 1 + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob + + me.leaveBody = false; + me.isDropPowerUp = false; + me.isBadTarget = true; + me.isMobBullet = true; + me.showHealthBar = false; + me.onHit = function() { + this.explode(this.mass * 20); + }; + me.do = function() { + this.timeLimit(); + }; + Matter.Body.setVelocity(me, velocity); + }, slashBoss(x, y, radius = 80) { mobs.spawn(x, y, 5, radius, "rgb(201,202,225)"); let me = mob[mob.length - 1]; diff --git a/js/tech.js b/js/tech.js index c6831e4..c96875b 100644 --- a/js/tech.js +++ b/js/tech.js @@ -795,7 +795,7 @@ const tech = { }, { name: "heuristics", - description: "30% decreased delay after firing", + description: "33% decreased delay after firing", maxCount: 9, count: 0, frequency: 1, @@ -803,7 +803,7 @@ const tech = { allowed() { return true }, requires: "", effect() { - tech.fireRate *= 0.7 + tech.fireRate *= 0.67 b.setFireCD(); }, remove() { @@ -4671,7 +4671,7 @@ const tech = { }, { name: "ammonium nitrate", - description: "increase explosive damage by 30%
increase explosive radius by 30%", + description: "increase explosive damage by 27%
increase explosive radius by 27%", isGunTech: true, maxCount: 9, count: 0, @@ -4682,7 +4682,7 @@ const tech = { }, requires: "an explosive damage source, not iridium-192", effect: () => { - tech.explosiveRadius += 0.3; + tech.explosiveRadius += 0.27; }, remove() { tech.explosiveRadius = 1; @@ -5955,7 +5955,7 @@ const tech = { }, { name: "spherical harmonics", - description: "standing wave oscillates in a 3rd dimension
increasing deflecting efficiency by 40%", + description: "standing wave oscillates in a 3rd dimension
increase deflecting efficiency by 40%", isFieldTech: true, maxCount: 9, count: 0, @@ -5967,18 +5967,18 @@ const tech = { requires: "standing wave", effect() { tech.harmonics++ - m.fieldShieldingScale = 1.3 * Math.pow(0.6, (tech.harmonics - 2)) + m.fieldShieldingScale = (tech.isStandingWaveExpand ? 1.1 : 1.3) * Math.pow(0.6, (tech.harmonics - 2)) m.harmonicShield = m.harmonicAtomic }, remove() { tech.harmonics = 2 - m.fieldShieldingScale = 1.3 * Math.pow(0.6, (tech.harmonics - 2)) + m.fieldShieldingScale = (tech.isStandingWaveExpand ? 1.1 : 1.3) * Math.pow(0.6, (tech.harmonics - 2)) m.harmonicShield = m.harmonic3Phase } }, { name: "expansion", - description: "using standing wave field
temporarily expands its radius", + description: "using standing wave field expands its radius
increase deflecting efficiency by 25%", // description: "use energy to expand standing wave
the field slowly contracts when not used", isFieldTech: true, maxCount: 1, @@ -5991,9 +5991,11 @@ const tech = { requires: "standing wave", effect() { tech.isStandingWaveExpand = true + m.fieldShieldingScale = (tech.isStandingWaveExpand ? 1.1 : 1.3) * Math.pow(0.6, (tech.harmonics - 2)) }, remove() { tech.isStandingWaveExpand = false + m.fieldShieldingScale = (tech.isStandingWaveExpand ? 1.1 : 1.3) * Math.pow(0.6, (tech.harmonics - 2)) m.harmonicRadius = 1 } }, @@ -8468,7 +8470,7 @@ const tech = { }, { name: "energy investment", - description: "every 10 seconds drain your energy
return it doubled 10 seconds later
lasts 180 seconds", + description: "every 10 seconds drain your energy
return it doubled 5 seconds later", maxCount: 9, count: 0, frequency: 0, @@ -8477,20 +8479,20 @@ const tech = { allowed() { return true }, requires: "", effect() { - for (let i = 0; i < 18; i++) { - setTimeout(() => { //drain energy + setInterval(() => { + if (!simulation.paused) { const energy = m.energy m.energy = 0 setTimeout(() => { //return energy m.energy += 2 * energy }, 5000); - }, i * 10000); - } + } + }, 10000); }, remove() {} }, { - name: "missile Launching System", + name: "missile launching system", description: "fire missiles for the next 120 seconds", maxCount: 9, count: 0, @@ -8524,12 +8526,14 @@ const tech = { requires: "", effect() { setInterval(() => { - b.grenade(Vector.add(m.pos, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }), -Math.PI / 2) //fire different angles for each grenade - const who = bullet[bullet.length - 1] - Matter.Body.setVelocity(who, { - x: who.velocity.x * 0.1, - y: who.velocity.y * 0.1 - }); + if (!simulation.paused && document.visibilityState !== "hidden") { + b.grenade(Vector.add(m.pos, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }), -Math.PI / 2) //fire different angles for each grenade + const who = bullet[bullet.length - 1] + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.1, + y: who.velocity.y * 0.1 + }); + } }, 2000); }, remove() {} diff --git a/todo.txt b/todo.txt index ab72189..07f946e 100644 --- a/todo.txt +++ b/todo.txt @@ -1,20 +1,46 @@ ******************************************************** NEXT PATCH ************************************************** -JUNK tech: tinker - unlock JUNK tech in experiment mode - this effect is stored for future visits on the same browser -several JUNK tech text have been cleaned up +new level: reactor - midBoss fight + it's not well balanced yet + Let me know if there are any impossible gun combinations -your explosions do 8% more damage, but not more harm -grenade gun fires 5% faster -ruins/pavilion map is adjusted to be a bit easier to move around -crouching speeds up exit door animation +for new players the training button at the start screen now cycles colors + effect shows if you haven't cleared the first training level, and you haven't done at least a few normal runs -removed google analytics -bug fix with power ups and frozen time -bug fix with elevators and map collision +standing wave expansion tech is 40% larger and gives 25% deflecting efficiency +ammonium nitrate gives 30 -> 27% damage and range +heuristics gives 30 -> 33% fire rate +wormhole invariant tech drains energy much slower while time is paused + +bug fixes + null level now longer progresses level.onLevel ******************************************************** TODO ******************************************************** +reactor + 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) + game crashed but recovered + +go non-collide with mobs when immune to damage? + +mobs that are invulnerable from the front + +vertical reversed version of reservoir level, start at top and press buttons to lower slime + mechanic: push a very large block into slime in order to stand on it and avoid slime + add anticipation to more mob attacks stabber striker @@ -28,8 +54,6 @@ path finding system figure out how to get friction effects on map/body to apply to player -vertical reversed version of reservoir level, start at top and press buttons to lower slime - growBoss and cellBoss are too similar variant of Occam's razor - remove 50% of your tech for each removed get: @@ -540,6 +564,8 @@ possible names for tech hall effect thrusters spaghettification particle accelerator + superluminal signalling + NP-complete a tutorial / lore intro needs to be optional so it doesn't slow experienced players