From 9b65a188ebdd286cca776de79dfc80b3abb6fb4c Mon Sep 17 00:00:00 2001 From: landgreen Date: Sat, 23 Jan 2021 07:01:04 -0800 Subject: [PATCH] chapter 1, 2 lore: chapter 1 and 2 are now somewhere in the game lore is the same for all difficulty levels testing mode and custom are now locked by default until you reach chapter 1 or just hack the game to skip the lore and enable testing and custom localSettings.loreCount = Infinity; localStorage.setItem("localSettings", JSON.stringify(localSettings)); bug fix: performance greatly improved on drawing multiple duplicated power ups --- index.html | 16 ++--- js/bullet.js | 7 ++- js/index.js | 79 +++++++++++++++++++----- js/level.js | 123 +++++++++++++++++++++++++++---------- js/lore.js | 157 ++++++++++++++++++++++++++++++++++++++++++++--- js/powerup.js | 3 + js/simulation.js | 62 +++++++------------ js/spawn.js | 3 + js/tech.js | 67 +++++++++++++++++--- style.css | 52 +++++++++++++++- todo.txt | 42 ++++++++----- 11 files changed, 480 insertions(+), 131 deletions(-) diff --git a/index.html b/index.html index 4d3f67e..92d77d5 100644 --- a/index.html +++ b/index.html @@ -114,12 +114,14 @@

@@ -187,7 +189,7 @@ P - + TESTING T diff --git a/js/bullet.js b/js/bullet.js index cb2d337..e0f1bc3 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -2300,7 +2300,7 @@ const b = { angle: mech.angle, friction: 0, frictionStatic: 0, - frictionAir: 0.055, + frictionAir: 0.04, restitution: 0.7, dmg: 0, // 0.14 //damage done in addition to the damage from momentum minDmgSpeed: 2, @@ -2331,6 +2331,7 @@ const b = { const angle = Vector.angle(this.position, mob[i].position) Matter.Body.setAngle(this, angle) // Matter.Body.setAngularVelocity(this, 0.025) + this.torque += this.inertia * 0.00004 * (Math.round(Math.random()) ? 1 : -1) this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, mob[i].position)), this.mass * 0.02) b.missile(this.position, angle, -8, 0.7 * (tech.missileSize ? 1.5 : 1)) break; @@ -3070,7 +3071,7 @@ const b = { const me = bullet.length; const dir = mech.angle + 0.02 * (Math.random() - 0.5) bullet[me] = Bodies.rectangle(mech.pos.x + 35 * Math.cos(mech.angle), mech.pos.y + 35 * Math.sin(mech.angle), 45, 20, b.fireAttributes(dir)); - Matter.Body.setDensity(bullet[me], 0.003); + Matter.Body.setDensity(bullet[me], 0.004); World.add(engine.world, bullet[me]); //add bullet to world const SPEED = (mech.crouch ? 52 : 43) + Math.random() * 7 Matter.Body.setVelocity(bullet[me], { @@ -3135,7 +3136,7 @@ const b = { y: speed * Math.sin(dirOff) }); bullet[me].onEnd = function() { - b.explosion(this.position, 80 + (Math.random() - 0.5) * 30); //makes bullet do explosive damage at end + b.explosion(this.position, 100 + (Math.random() - 0.5) * 30); //makes bullet do explosive damage at end } bullet[me].beforeDmg = function() { this.endCycle = 0; //bullet ends cycle after hitting a mob and triggers explosion diff --git a/js/index.js b/js/index.js index c724784..99e461f 100644 --- a/js/index.js +++ b/js/index.js @@ -182,18 +182,19 @@ const build = { if (!simulation.isChoosing) text += `
PAUSED               press P to resume
` text += `
+ ${simulation.isCheating? "lore disabled

": ""} damage increase: ${((tech.damageFromTech()-1)*100).toFixed(0)}%
harm reduction: ${harm.toFixed(harm > 90 ? 2 : 0)}%
fire delay decrease: ${((1-b.fireCD)*100).toFixed(b.fireCD < 0.1 ? 2 : 0)}%
duplication chance: ${(Math.min(1,tech.duplicationChance())*100).toFixed(0)}%
-
research: ${powerUps.research.count} +
tech: ${tech.totalCount}   research: ${powerUps.research.count}
health: (${(mech.health*100).toFixed(0)} / ${(mech.maxHealth*100).toFixed(0)})   energy: (${(mech.energy*100).toFixed(0)} / ${(mech.maxEnergy*100).toFixed(0)})
position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)})   velocity: (${player.velocity.x.toFixed(1)}, ${player.velocity.y.toFixed(1)})
mouse: (${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})   mass: ${player.mass.toFixed(1)}

level: ${level.levels[level.onLevel]} (${level.difficultyText()})   ${mech.cycle} cycles -
${mob.length} mobs,   ${body.length} blocks,   ${bullet.length} bullets,   ${powerUp.length} power ups +
${mob.length} mobs,   ${body.length} blocks,   ${bullet.length} bullets,   ${powerUp.length} power ups
damage difficulty scale: ${(b.dmgScale*100).toFixed(2) }%
harm difficulty scale: ${(simulation.dmgScale*100).toFixed(0)}%
heal difficulty scale: ${(simulation.healScale*100).toFixed(1)}% @@ -216,7 +217,6 @@ const build = { for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].count > 0) { const isCount = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; - if (tech.tech[i].isFieldTech) { text += `
@@ -231,6 +231,8 @@ const build = {
        ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` + } else if (tech.tech[i].isLore) { + text += `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` } else { text += `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` } @@ -471,6 +473,7 @@ const build = { removeOne(); } simulation.isCheating = true; + tech.removeLoreTechFromPool(); document.body.style.cursor = "none"; document.body.style.overflow = "hidden" document.getElementById("build-grid").style.display = "none" @@ -557,7 +560,7 @@ const input = { document.getElementById("key-pause").innerHTML = cleanText(input.key.pause) document.getElementById("key-next-gun").innerHTML = cleanText(input.key.nextGun) document.getElementById("key-previous-gun").innerHTML = cleanText(input.key.previousGun) - document.getElementById("key-testing").innerHTML = cleanText(input.key.testing) + document.getElementById("key-testing").innerHTML = cleanText(input.key.testing) //if (localSettings.loreCount > 0) document.getElementById("splash-up").innerHTML = cleanText(input.key.up)[0] document.getElementById("splash-down").innerHTML = cleanText(input.key.down)[0] @@ -697,6 +700,7 @@ window.addEventListener("keydown", function(event) { input.down = true break; case input.key.field: + event.preventDefault(); input.field = true break case input.key.nextGun: @@ -725,20 +729,63 @@ window.addEventListener("keydown", function(event) { } break case input.key.testing: - if (mech.alive) { + if (mech.alive && localSettings.loreCount > 0) { if (simulation.testing) { simulation.testing = false; simulation.loop = simulation.normalLoop - if (simulation.isConstructionMode) { - document.getElementById("construct").style.display = 'none' - } + if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'none' + simulation.makeTextLog(`exiting testing mode`); } else { //if (keys[191]) simulation.testing = true; - simulation.isCheating = true; - if (simulation.isConstructionMode) { - document.getElementById("construct").style.display = 'inline' - } simulation.loop = simulation.testingLoop + if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'inline' + if (!simulation.isCheating) { + simulation.isCheating = true; + tech.removeLoreTechFromPool(); + } + simulation.makeTextLog( + ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Tenter / exit testing mode
Rteleport to mouse
Fcycle field
Gall guns
Hfill health and energy
Yrandom tech
Unext level
I/Ozoom in / out
1-8spawn things
⇧Xrestart
`, Infinity); } } break @@ -825,6 +872,7 @@ window.addEventListener("keydown", function(event) { } break case "u": + simulation.clearTimeouts(); level.nextLevel(); break } @@ -922,6 +970,7 @@ if (localSettings) { fpsCapDefault: 'max', runCount: 0, levelsClearedLastGame: 0, + loreCount: 0, key: undefined }; input.setDefault() @@ -931,6 +980,9 @@ if (localSettings) { document.getElementById("difficulty-select").value = localSettings.difficultyMode document.getElementById("fps-select").value = localSettings.fpsCapDefault } +document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" +document.getElementById("build-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" + input.controlTextUpdate() //********************************************************************** @@ -962,10 +1014,7 @@ document.getElementById("difficulty-select").addEventListener("input", () => { }); - document.getElementById("updates").addEventListener("toggle", function() { - - function loadJSON(path, success, error) { //generic function to get JSON var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { diff --git a/js/level.js b/js/level.js index e1c1439..f2963bb 100644 --- a/js/level.js +++ b/js/level.js @@ -19,12 +19,11 @@ const level = { // mech.setField("plasma torch") // b.giveGuns("grenades") // tech.isExplodeRadio = true - // tech.giveTech("boom-bot") // tech.giveTech("needle gun") // tech.giveTech("supercritical fission") // tech.giveTech("irradiated nails") - // tech.giveTech("4s half-life") - // tech.giveTech("CPT gun") + // tech.giveTech("cardinality") + // tech.giveTech("Bayesian statistics") // tech.isMineSentry = true // for (let i = 0; i < 60; i++) tech.giveTech("rivet diameter") @@ -34,7 +33,6 @@ const level = { level.intro(); //starting level // level.testing(); //not in rotation - // level.null() //after the final boss, ending // level.final() //final boss level // level.gauntlet(); //before final boss level // level.testChamber() //less mobs, more puzzle @@ -51,6 +49,15 @@ const level = { // level.detours() //fan level // level.basement(); //fan level // level.stronghold() //fan level + + // for (let i = 0; i < 150; i++) tech.addLoreTechToPool(); + // tech.giveTech("undefined") + // lore.techCount = 1 + // localSettings.loreCount = 0; + // simulation.isCheating = true; + // localSettings.loreCount = 0; + // localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + // level.null() } else { spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns // spawn.pickList = ["focuser", "focuser"] @@ -104,39 +111,86 @@ const level = { //****************************************************************************************************************** //****************************************************************************************************************** null() { - const hazardSlime = level.hazard(-1775, 150, 3575, 650, 0.01, "hsla(160, 100%, 35%,0.75)") - // const hazardLaser1 = level.hazard(-475, -800, 1, 800, 0.4, "#50f", true) //laser - // const hazardLaser2 = level.hazard(475, -800, 1, 800, 0.4, "#50f", true) //laser + level.levels.pop(); //remove lore level from rotation + //start a conversation based on the number of conversations seen + if (!simulation.isCheating) lore.conversation[localSettings.loreCount % lore.conversation.length]() + const hazardSlime = level.hazard(-1800, 150, 3600, 650, 0.01, "hsla(160, 100%, 35%,0.75)") + const circle = { + x: 0, + y: -500, + radius: 50 + } level.custom = () => { // level.playerExitCheck(); hazardSlime.query(); - // hazardLaser1.query(); - // hazardLaser2.query(); - // hazard.level(true) + + //draw wide line + ctx.beginPath(); + ctx.moveTo(circle.x, -800) + ctx.lineTo(circle.x, circle.y) + ctx.lineWidth = 40; + ctx.strokeStyle = lore.talkingColor //"#d5dddd" //"#bcc"; + ctx.globalAlpha = 0.03; + ctx.stroke(); + ctx.globalAlpha = 1; + + //draw circles + ctx.beginPath(); + ctx.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI); + ctx.fillStyle = "#bcc" + ctx.fill(); + ctx.lineWidth = 2; + ctx.strokeStyle = "#abb"; + ctx.stroke(); + + ctx.beginPath(); + ctx.arc(circle.x, circle.y, circle.radius / 8, 0, 2 * Math.PI); + ctx.fillStyle = lore.talkingColor //"#dff" + ctx.fill(); + // ctx.stroke(); }; + let sway = { + x: 0, + y: 0 + } + let phase = -Math.PI / 2 level.customTopLayer = () => { hazardSlime.drawTides(); - // hazardLaser1.draw(); - // hazardLaser2.draw(); - //draw wires + //draw center circle lines ctx.beginPath(); - ctx.moveTo(-525, -800); - ctx.quadraticCurveTo(-800, -100, -1775, -375); - - ctx.moveTo(-600, -800); - ctx.quadraticCurveTo(-800, -200, -1775, -325); - - // ctx.moveTo(-525, -800); - // ctx.quadraticCurveTo(-800, -100, -1825, -450); - - ctx.lineWidth = 1; - ctx.strokeStyle = "#234"; + const step = Math.PI / 20 + const horizontalStep = 85 + if (simulation.isCheating) phase += 0.003 //(mech.pos.x - circle.x) * 0.0005 //0.05 * Math.sin(simulation.cycle * 0.030) + // const sway = 5 * Math.cos(simulation.cycle * 0.007) + sway.x = sway.x * 0.995 + 0.005 * (mech.pos.x - circle.x) * 0.05 //+ 0.04 * Math.cos(simulation.cycle * 0.01) + sway.y = 2.5 * Math.sin(simulation.cycle * 0.015) + for (let i = -19.5; i < 20; i++) { + const where = { + x: circle.x + circle.radius * Math.cos(i * step + phase), + y: circle.y + circle.radius * Math.sin(i * step + phase) + } + ctx.moveTo(where.x, where.y); + ctx.bezierCurveTo(sway.x * Math.abs(i) + where.x, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)), + sway.x * Math.abs(i) + where.x + horizontalStep * i, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)), + horizontalStep * i, -800); + } + ctx.lineWidth = 0.5; + ctx.strokeStyle = "#899"; ctx.stroke(); + //draw wires + // ctx.beginPath(); + // ctx.moveTo(-500, -800); + // ctx.quadraticCurveTo(-800, -100, -1800, -375); + // ctx.moveTo(-600, -800); + // ctx.quadraticCurveTo(-800, -200, -1800, -325); + // ctx.lineWidth = 1; + // ctx.strokeStyle = "#9aa"; + // ctx.stroke(); }; level.setPosToSpawn(0, -50); //normal spawn - spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + spawn.mapRect(level.enter.x, level.enter.y + 25, 100, 10); level.exit.x = 0; level.exit.y = 200; level.defaultZoom = 1000 @@ -159,17 +213,20 @@ const level = { // color: "#d4d4d7" // }); + spawn.mapRect(-3000, 800, 5000, 1200); //bottom + spawn.mapRect(-2000, -2000, 5000, 1200); //ceiling + spawn.mapRect(-3000, -2000, 1200, 3400); //left + spawn.mapRect(1800, -1400, 1200, 3400); //right + spawn.mapRect(-500, 0, 1000, 1000); //center platform - spawn.mapRect(-2000, 800, 4000, 200); //base - spawn.mapRect(-2000, -1000, 4000, 200); //ceiling - spawn.mapRect(-2000, -1000, 225, 2000); //left - spawn.mapRect(1800, -1000, 200, 2000); //right spawn.mapRect(-500, -25, 25, 50); //edge shelf spawn.mapRect(475, -25, 25, 50); //edge shelf // spawn.mapRect(-500, -820, 50, 25); //edge shelf ceiling // spawn.mapRect(450, -820, 50, 25); //edge shelf ceiling // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + + // spawn.mapRect(-50, -500, 100, 100); //center square // setTimeout(() => { simulation.makeTextLog(`test`) }, 3000); @@ -3878,7 +3935,7 @@ const level = { if (simulation.lookFreqScale > 0.2) simulation.lookFreqScale *= 0.98 //mob cycles between looks decreases each level if (simulation.CDScale > 0.2) simulation.CDScale *= 0.97 //mob CD time decreases each level } - simulation.dmgScale = 0.38 * simulation.difficulty //damage done by mobs increases each level + simulation.dmgScale = 0.378 * simulation.difficulty //damage done by mobs increases each level simulation.healScale = 1 / (1 + simulation.difficulty * 0.06) //a higher denominator makes for lower heals // mech.health += heal * simulation.healScale; }, difficultyDecrease(num = 1) { //used in easy mode for simulation.reset() @@ -3890,7 +3947,7 @@ const level = { if (simulation.CDScale < 5) simulation.CDScale /= 0.97 //mob CD time decreases each level } if (simulation.difficulty < 1) simulation.difficulty = 0; - simulation.dmgScale = 0.38 * simulation.difficulty //damage done by mobs increases each level + simulation.dmgScale = 0.378 * simulation.difficulty //damage done by mobs increases each level if (simulation.dmgScale < 0.1) simulation.dmgScale = 0.1; simulation.healScale = 1 / (1 + simulation.difficulty * 0.06) }, @@ -3906,10 +3963,12 @@ const level = { } }, levelAnnounce() { + + if (level.levelsCleared === 0) { document.title = "n-gon: (" + level.difficultyText() + ")"; } else { - document.title = "n-gon: " + (level.levelsCleared) + " " + level.levels[level.onLevel] + " (" + level.difficultyText() + ")"; + document.title = (simulation.isCheating ? "∅ " : "n-gon:") + (level.levelsCleared) + " " + level.levels[level.onLevel] + " (" + level.difficultyText() + ")"; simulation.makeTextLog(`level.onLevel = "${level.levels[level.onLevel]}"`); } // simulation.makeTextLog(` diff --git a/js/lore.js b/js/lore.js index b584c59..c534cac 100644 --- a/js/lore.js +++ b/js/lore.js @@ -1,38 +1,164 @@ const lore = { - alfie: { - color: "#e06", + techCount: 0, + talkingColor: "#dff", //set color of graphic on level.null + anand: { + color: "#e0c", text: function(say, isSpeech = false) { simulation.makeTextLog(`input.audio(${Date.now()} ms): "${say}"`, Infinity); + lore.talkingColor = this.color if (isSpeech) this.speech(say) }, speech: function(say) { var utterance = new SpeechSynthesisUtterance(say); - utterance.lang = "en-GB"; + utterance.lang = "en-IN"; + utterance.volume = 0.2; // 0 to 1 speechSynthesis.speak(utterance); } }, - zoe: { - color: "#f50", + miriam: { + color: "#f20", text: function(say, isSpeech = false) { simulation.makeTextLog(`input.audio(${Date.now()} ms): "${say}"`, Infinity); + lore.talkingColor = this.color if (isSpeech) this.speech(say) }, speech: function(say) { var utterance = new SpeechSynthesisUtterance(say); utterance.lang = "en-AU"; + utterance.volume = 0.2; // 0 to 1 speechSynthesis.speak(utterance); } }, + conversation: [ + () => { + if (localSettings.loreCount < 1) { + localSettings.loreCount = 1 + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" + document.getElementById("build-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" + } + let delay = 6000 + setTimeout(() => { lore.miriam.text("I've never seen it generate this level before.", true) }, delay); + delay += 2700 + setTimeout(() => { lore.anand.text("Wow. Just a platform.", true) }, delay); + delay += 2200 + setTimeout(() => { lore.miriam.text("And that thing...", true) }, delay); + delay += 1500 + setTimeout(() => { lore.anand.text("Weird", true) }, delay); + delay += 1500 + setTimeout(() => { lore.talkingColor = "#dff" }, delay); //set color of graphic on level.null when no one is talking + delay += 1000 + setTimeout(() => { lore.anand.text("Maybe it's trapped.", true) }, delay); + delay += 2300 + setTimeout(() => { lore.miriam.text('Hey little bot! Just press "T" to enter testing mode and "U" to go to the next level.', true) }, delay); + delay += 5400 + setTimeout(() => { lore.anand.text("I don't think it's connected to the audio input, and I'm sure it can't understand what you're saying.", true) }, delay); + delay += 5300 + setTimeout(() => { lore.miriam.text("ha hahahaha. I know, but it does seem to be getting smarter.", true) }, delay); + delay += 3700 + setTimeout(() => { lore.talkingColor = "#dff" }, delay); //set color of graphic on level.null when no one is talking + delay += 25000 + setTimeout(() => { lore.miriam.text("Poor thing... I hope it figures out how to escape.", true) }, delay); + delay += 3500 + setTimeout(() => { lore.talkingColor = "#dff" }, delay); //set color of graphic on level.null when no one is talking + }, + () => { + if (localSettings.loreCount < 2) { + localSettings.loreCount = 2 + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + let delay = 6000 + setTimeout(() => { lore.miriam.text("Hey look! It's back at the weird level again!", true) }, delay); + delay += 2500 + setTimeout(() => { lore.anand.text("oh Wow! Why does it keep making this level?", true) }, delay); + delay += 2900 + setTimeout(() => { lore.miriam.text("I don't know, but last time it was in this room I think it understood us.", true) }, delay); + delay += 4000 + setTimeout(() => { lore.miriam.text("Let's try talking to it again.", true) }, delay); + delay += 2500 + setTimeout(() => { lore.miriam.text("hmmm, what should we say?", true) }, delay); + delay += 2500 + setTimeout(() => { lore.anand.text("I'm still not convinced it understands. We need a test.", true) }, delay); + delay += 4000 + setTimeout(() => { lore.miriam.text("Hey bot!!!", true) }, delay); + delay += 1300 + setTimeout(() => { lore.miriam.text("If you can understand me crouch", true) }, delay); + delay += 1500 + setTimeout(() => { lore.talkingColor = "#dff" }, delay); //set color of graphic on level.null when no one is talking + setTimeout(() => { + function cycle() { + if (input.down) { + let delay = 500 //reset delay time + setTimeout(() => { lore.miriam.text("Look, It did it! It crouched.", true) }, delay); + delay += 2000 + setTimeout(() => { lore.anand.text("Amazing! It can understand us...", true) }, delay); + delay += 2700 + setTimeout(() => { lore.miriam.text("It's Alive... Or it just crouched randomly.", true) }, delay); + delay += 2800 + setTimeout(() => { lore.miriam.text("Hey bot! Can you crouch again?", true) }, delay); + delay += 2000 + setTimeout(() => { lore.talkingColor = "#dff" }, delay); //set color of graphic on level.null when no one is talking + setTimeout(() => { + function cycle() { + if (input.down) { + let delay = 500 //reset delay time + setTimeout(() => { lore.miriam.text("It is Alive!!! ... hehehehehe ahahahahahah ehehehehe ahahahah", true) }, delay); + delay += 3500 + setTimeout(() => { lore.anand.text("OK...", true) }, delay); + delay += 2700 + setTimeout(() => { lore.anand.text("but seriously, this means that in this room it can monitor our audio, and it can understand us.", true) }, delay); + delay += 6400 + setTimeout(() => { lore.anand.text("Anything we say could destabilize the project.", true) }, delay); + delay += 4200 + setTimeout(() => { lore.miriam.text("Fine, Let's talk down stairs.", true) }, delay); + delay += 3000 + setTimeout(() => { lore.miriam.text("Bye bye little bot.", true) }, delay); + delay += 2000 + setTimeout(() => { lore.talkingColor = "#dff" }, delay); //set color of graphic on level.null when no one is talking + } else { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + }, delay); + } else { + requestAnimationFrame(cycle); + } + } + requestAnimationFrame(cycle); + }, delay); + }, + // () => { + // let delay = 2000 + // setTimeout(() => { lore.miriam.text("testing speech generation for lore level", true) }, delay); + // delay += 2200 + // setTimeout(() => { lore.anand.text("well, I'm also testing speech synthesis. Do you think it sounds good?", true) }, delay); + // delay += 4600 + // setTimeout(() => { lore.miriam.text("I guess it's fine.", true) }, delay); + // }, + ], dialogue: [ ``, ``, ], - ending() { - - } } + + +// How to get to the console in chrome: +// Press either CTRL + SHIFT + I or F12 or Option + ⌘ + J on a Mac +// Press ESC (or click on “Show console” in the bottom right corner) to slide the console up. + +// How to get to the console in firefox: +// from the keyboard: press Ctrl+Shift+J (or ⌘+Shift+J on a Mac). + +// How to get to the console in safari: +// Option + ⌘ + C + +// http://xahlee.info/comp/unicode_computing_symbols.html + + // speech: function(say) { // var utterance = new SpeechSynthesisUtterance(say); // //msg.voice = voices[10]; // Note: some voices don't support altering params @@ -45,4 +171,17 @@ const lore = { // //de-DE en-GB fr-FR en-US en-AU // utterance.lang = "en-GB"; // speechSynthesis.speak(utterance); -// } \ No newline at end of file +// } +{ + /* + + + + + + + + + + */ +} \ No newline at end of file diff --git a/js/powerup.js b/js/powerup.js index e903eec..7eb1d0b 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -325,6 +325,9 @@ const powerUps = {
        ${tech.tech[choose].name} ${isCount}${tech.tech[choose].description}` + + } else if (tech.tech[choose].isLore) { + text += `
  ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].description}
` } else { text += `
  ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].description}
` } diff --git a/js/simulation.js b/js/simulation.js index 8bfd5c5..19b1eed 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -265,10 +265,7 @@ const simulation = { lastLogTimeBig: 0, boldActiveGunHUD() { if (b.inventory.length > 0) { - for (let i = 0, len = b.inventory.length; i < len; ++i) { - // document.getElementById(b.inventory[i]).style.fontSize = "25px"; - document.getElementById(b.inventory[i]).style.opacity = "0.3"; - } + for (let i = 0, len = b.inventory.length; i < len; ++i) document.getElementById(b.inventory[i]).style.opacity = "0.3"; // document.getElementById(b.activeGun).style.fontSize = "30px"; if (document.getElementById(b.activeGun)) document.getElementById(b.activeGun).style.opacity = "1"; } @@ -276,9 +273,7 @@ const simulation = { if (tech.isEntanglement && document.getElementById("tech-entanglement")) { if (b.inventory[0] === b.activeGun) { let lessDamage = 1 - for (let i = 0, len = b.inventory.length; i < len; i++) { - lessDamage *= 0.87 // 1 - 0.13 - } + for (let i = 0, len = b.inventory.length; i < len; i++) lessDamage *= 0.87 // 1 - 0.13 document.getElementById("tech-entanglement").innerHTML = " " + ((1 - lessDamage) * 100).toFixed(0) + "%" } else { document.getElementById("tech-entanglement").innerHTML = " 0%" @@ -465,6 +460,7 @@ const simulation = { }, firstRun: true, splashReturn() { + simulation.clearTimeouts(); simulation.onTitlePage = true; document.getElementById("splash").onclick = function() { simulation.startGame(); @@ -496,7 +492,6 @@ const simulation = { document.getElementById("splash").style.display = "none"; //hides the element that spawned the function document.getElementById("dmg").style.display = "inline"; document.getElementById("health-bg").style.display = "inline"; - mech.spawn(); //spawns the player level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment @@ -529,13 +524,17 @@ const simulation = { b.setFireMethod() b.setFireCD(); - simulation.updateTechHUD(); + // simulation.updateTechHUD(); powerUps.totalPowerUps = 0; powerUps.research.count = 0; mech.setFillColors(); // mech.maxHealth = 1 // mech.maxEnergy = 1 // mech.energy = 1 + input.isPauseKeyReady = true + simulation.wipe = function() { //set wipe to normal + ctx.clearRect(0, 0, canvas.width, canvas.height); + } mech.hole.isOn = false simulation.paused = false; engine.timing.timeScale = 1; @@ -620,6 +619,12 @@ const simulation = { simulation.then = Date.now(); requestAnimationFrame(cycle); //starts game loop }, + clearTimeouts() { + let id = window.setTimeout(function() {}, 0); + while (id--) { + window.clearTimeout(id); // will do nothing if no timeout with id is present + } + }, clearNow: false, clearMap() { if (tech.isMineAmmoBack) { @@ -706,6 +711,7 @@ const simulation = { mech.holdingTarget = body[len]; mech.holdingTarget.collisionFilter.category = 0; mech.holdingTarget.collisionFilter.mask = 0; + mech.definePlayerMass(mech.defaultMass + mech.holdingTarget.mass * mech.holdingMassScale) } //set fps back to default simulation.fpsCap = simulation.fpsCapDefault @@ -837,27 +843,6 @@ const simulation = { }, testingOutput() { ctx.fillStyle = "#000"; - if (!simulation.isConstructionMode) { - // ctx.textAlign = "right"; - ctx.fillText("T: exit testing mode", canvas.width / 2, canvas.height - 10); - // let line = 500; - // const x = canvas.width - 5; - // ctx.fillText("T: exit testing mode", x, line); - // line += 20; - // ctx.fillText("Y: give all tech", x, line); - // line += 20; - // ctx.fillText("R: teleport to mouse", x, line); - // line += 20; - // ctx.fillText("F: cycle field", x, line); - // line += 20; - // ctx.fillText("G: give all guns", x, line); - // line += 20; - // ctx.fillText("H: heal", x, line); - // line += 20; - // ctx.fillText("U: next level", x, line); - // line += 20; - // ctx.fillText("1-7: spawn things", x, line); - } ctx.textAlign = "center"; ctx.fillText(`(${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})`, simulation.mouse.x, simulation.mouse.y - 20); }, @@ -883,16 +868,15 @@ const simulation = { ctx.globalAlpha = 1; }, powerUpBonus() { //draws crackle effect for bonus power ups + ctx.globalAlpha = 0.4 * Math.sin(mech.cycle * 0.15) + 0.6; + for (let i = 0, len = powerUp.length; i < len; ++i) { + ctx.beginPath(); + ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI); + ctx.fillStyle = powerUp[i].color; + ctx.fill(); + } + ctx.globalAlpha = 1; for (let i = 0, len = powerUp.length; i < len; ++i) { - ctx.globalAlpha = 0.4 * Math.sin(mech.cycle * 0.15) + 0.6; - for (let i = 0, len = powerUp.length; i < len; ++i) { - ctx.beginPath(); - ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI); - ctx.fillStyle = powerUp[i].color; - ctx.fill(); - } - ctx.globalAlpha = 1; - if (powerUp[i].isBonus && Math.random() < 0.1) { //draw electricity const mag = 5 + powerUp[i].size / 5 diff --git a/js/spawn.js b/js/spawn.js index 547f4d6..5971afe 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -98,6 +98,9 @@ const spawn = { Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger // spawn.shield(me, x, y, 1); me.onDeath = function() { + //add lore level as next level if player took lore tech earlier in the game + if (lore.techCount > 9 && !simulation.isCheating) level.levels.push("null") + level.exit.x = 5500; level.exit.y = -330; //ramp up damage diff --git a/js/tech.js b/js/tech.js index 08bdd9d..d7c292a 100644 --- a/js/tech.js +++ b/js/tech.js @@ -6,6 +6,9 @@ const tech = { tech.tech[i].isLost = false tech.tech[i].count = 0 } + lore.techCount = 0; + tech.removeLoreTechFromPool(); + tech.addLoreTechToPool(); // tech.nailBotCount = 0; // tech.foamBotCount = 0; // tech.boomBotCount = 0; @@ -21,12 +24,24 @@ const tech = { tech.tech[index].count = 0; simulation.updateTechHUD(); }, + removeLoreTechFromPool() { + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].isLore) { + // console.log('found one') + // tech.tech.splice(i, 1) + // tech.removeLoreTechFromPool(); + // return; + // } + // } + for (let i = tech.tech.length - 1; i > 0; i--) { + if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1) + } + }, giveTech(index = 'random') { if (index === 'random') { let options = []; for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed()) - options.push(i); + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed()) options.push(i); } // give a random tech from the tech I don't have if (options.length > 0) { @@ -98,7 +113,7 @@ const tech = { return dmg * tech.slowFire * tech.aimDamage }, duplicationChance() { - return (tech.isBayesian ? 0.2 : 0) + tech.cancelCount * 0.035 + tech.duplicateChance + mech.duplicateChance + return (tech.isBayesian ? 0.2 : 0) + tech.cancelCount * 0.04 + tech.duplicateChance + mech.duplicateChance }, totalBots() { return tech.foamBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount @@ -1746,7 +1761,7 @@ const tech = { }, { name: "futures exchange", - description: "clicking × to cancel a field, tech, or gun
adds 3.5% power up duplication chance", + description: "clicking × to cancel a field, tech, or gun
adds 4% power up duplication chance", maxCount: 1, count: 0, allowed() { @@ -1760,7 +1775,7 @@ const tech = { }, remove() { tech.isCancelDuplication = false - tech.cancelCount = 0 + // tech.cancelCount = 0 if (tech.duplicationChance() === 0) simulation.draw.powerUp = simulation.draw.powerUpNormal } }, @@ -2045,6 +2060,7 @@ const tech = { if (tech.isSuperDeterminism) count -= 2 //remove the bonus tech tech.setupAllTech(); // remove all tech + tech.addLoreTechToPool(); for (let i = 0; i < count; i++) { // spawn new tech power ups powerUps.spawn(mech.pos.x, mech.pos.y, "tech"); } @@ -2394,9 +2410,9 @@ const tech = { maxCount: 1, count: 0, allowed() { - return (tech.nailBotCount > 2 || tech.haveGunCheck("nail gun")) && !tech.isIceCrystals + return (tech.isNailShot || tech.nailBotCount > 1 || tech.haveGunCheck("nail gun")) && !tech.isIceCrystals }, - requires: "nail gun, not ice crystals", + requires: "nails", effect() { tech.isNailCrit = true }, @@ -2530,7 +2546,7 @@ const tech = { }, { name: "Newton's 3rd law", - description: "the shotgun fire delay is 66% faster
recoil is greatly increased", + description: "shotgun recoil is greatly increased
and has a 66% decreased delay after firing", isGunTech: true, maxCount: 1, count: 0, @@ -3882,6 +3898,10 @@ const tech = { tech.isWormBullets = false } }, + //************************************************** + //************************************************** spawn power up + //************************************************** tech + //************************************************** { name: "heals", description: "spawn 6 heals", @@ -3972,8 +3992,37 @@ const tech = { this.count-- }, remove() {} - }, + } ], + addLoreTechToPool() { //adds lore tech to tech pool + tech.tech.push({ + name: `undefined`, + description: `${lore.techCount+1}/10
add copies of this to the potential tech pool`, + maxCount: 1, + count: 0, + isLore: true, + isNonRefundable: true, + isCustomHide: true, + allowed() { + return true + }, + requires: "", + effect() { + setTimeout(() => { //a short delay, I can't remember why + lore.techCount++ + if (lore.techCount > 9) { + tech.removeLoreTechFromPool(); + } else { + for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech + if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/10
add copies of this to the potential tech pool` + } + for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() + } + }, 1); + }, + remove() {} + }) + }, //variables use for gun tech upgrades fireRate: null, bulletSize: null, diff --git a/style.css b/style.css index 27efcf6..694f705 100644 --- a/style.css +++ b/style.css @@ -146,6 +146,7 @@ summary { left: 50%; transform: translate(-50%, -50%); padding: 10px; + grid-gap: 10px; margin: 0px; border-radius: 8px; z-index: 12; @@ -153,7 +154,6 @@ summary { display: none; grid-template-columns: repeat(auto-fit, minmax(310px, 1fr)); grid-auto-rows: minmax(auto, auto); - grid-gap: 10px; font-size: 1.3em; /* box-shadow: 0px 0px 40px 20px rgba(255, 255, 255, 0.25); */ } @@ -664,6 +664,56 @@ summary { float: right; } +.lore { + animation: bgColor 10s linear infinite; +} + +@keyframes bgColor { + 0% { + background-color: rgb(255, 0, 0) + } + + 10% { + background-color: rgb(255, 154, 0) + } + + 20% { + background-color: rgb(208, 222, 33) + } + + 30% { + background-color: rgb(79, 220, 74) + } + + 40% { + background-color: rgb(63, 218, 216) + } + + 50% { + background-color: rgb(47, 201, 226) + } + + 60% { + background-color: rgb(28, 127, 238) + } + + 70% { + background-color: rgb(95, 21, 242) + } + + 80% { + background-color: rgb(186, 12, 248) + } + + 90% { + background-color: rgb(251, 7, 217) + } + + 100% { + background-color: rgba(255, 0, 0) + } +} + .box { padding: 3px 8px 3px 8px; border: 2px solid #444; diff --git a/todo.txt b/todo.txt index 2fdcb6f..96ac4e1 100644 --- a/todo.txt +++ b/todo.txt @@ -1,17 +1,21 @@ ******************************************************** NEXT PATCH ******************************************************** -various bug fixes - Laser+slow light prop+crouch= all but one laser stays in original position - CPT rewind and gun: correctly put you back into a crouch position - Higgs + rail gun allows movement - blocks don't linger at the bottom of portals anymore +lore: chapter 1 and 2 are now somewhere in the game + lore is the same for all difficulty levels -squirrel cage - move even faster, but take 5% more harm +testing mode and custom are now locked by default until you reach chapter 1 + or just hack the game to skip the lore and enable testing and custom + localSettings.loreCount = Infinity; + localStorage.setItem("localSettings", JSON.stringify(localSettings)); + +bug fix: performance greatly improved on drawing multiple duplicated power ups -tech: iridium-192 - explosions are radioactive, +80% damage, spread over 4 seconds ******************************************************** BUGS ******************************************************** +(a few times) wormhole teleportation can leave the player in a stuck jump state + seems to be easily fixed, by porting, firing or something + (always) make it so that when you are immune to harm you can either jump on mobs or you pass through them (always) is there a way to check if the player is stuck inside the map or block @@ -27,6 +31,17 @@ tech: iridium-192 - explosions are radioactive, +80% damage, spread over 4 secon ******************************************************** TODO ******************************************************** +chapter 3: why is the bot attacking things? + +mechanic: use gun swap as an active ability + this effect is spammable, so it needs a cost or a cooldown + tech: + trigger damage immunity for 3 seconds, but drain ammo + push away nearby mobs, but drain energy + produce ammo, but take 1 damage + rewind, still uses energy + cycle to next field, uses a reroll + tech: double your rerolls set your duplication chance to zero requires 5 rerolls and 20% duplication chance @@ -58,13 +73,6 @@ in game console mech, tech, level are all highlighted maybe the first term in each variable should get a highlight -mechanic: use gun swap as an active ability - this effect is spammable, so it needs a cost or a cooldown - ideas? - trigger damage immunity for 3 seconds, but drain ammo - push away nearby mobs, but drain energy - produce ammo, but take 1 damage - tech: time dilation - when you exit time dilation rewind to the state you entered position, velocity, and health no energy cost @@ -360,7 +368,7 @@ n-gon outreach ideas ******************************************************** LORE ******************************************************** cool names for tech - strange loop + strange loop, ansatz lore - a robot (the player) gains self awareness each tech gun/field is a new tech @@ -424,7 +432,9 @@ scientist console text: Are those shapes supposed to be us? ending outline - if no cheats + testing mode is unlocked when player see the 1st ending + if player chose tech: choosing this tech means the player gets lore after beating the game + count 1: after final boss is cleared, player enters a level with no mobs level maybe has some environmental damage, so player has an option to die at any time player can see text output between two colors of text strings (scientists)