diff --git a/.DS_Store b/.DS_Store index de36ad2..8bcfebd 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/index.html b/index.html index 436e3bd..6f14d4c 100644 --- a/index.html +++ b/index.html @@ -81,11 +81,9 @@ experiment - - - - 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