diff --git a/img/coherence.webp b/img/coherence.webp new file mode 100644 index 0000000..c433b17 Binary files /dev/null and b/img/coherence.webp differ diff --git a/js/bullet.js b/js/bullet.js index 5459cac..49e6fd3 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -6723,10 +6723,7 @@ const b = { // } // } else { m.fireCDcycle = m.cycle + tech.missileFireCD * b.fireCDscale / countReduction; // cool down - const direction = { - x: Math.cos(m.angle), - y: Math.sin(m.angle) - } + const direction = { x: Math.cos(m.angle), y: Math.sin(m.angle) } // const where = { // x: m.pos.x + 30 * direction.x, // y: m.pos.y + 30 * direction.y @@ -6752,23 +6749,17 @@ const b = { let count = 0 const fireMissile = () => { if (m.crouch) { - b.missile({ - x: m.pos.x + 30 * direction.x, - y: m.pos.y + 30 * direction.y - }, m.angle, 20, sqrtCountReduction) + b.missile({ x: m.pos.x + 30 * direction.x, y: m.pos.y + 30 * direction.y }, m.angle, 20, sqrtCountReduction) bullet[bullet.length - 1].force.x += 0.5 * push.x * (Math.random() - 0.5) bullet[bullet.length - 1].force.y += 0.004 + 0.5 * push.y * (Math.random() - 0.5) } else { - b.missile({ - x: m.pos.x + 30 * direction.x, - y: m.pos.y + 30 * direction.y - }, m.angle, -15, sqrtCountReduction) + b.missile({ x: m.pos.x + 30 * direction.x, y: m.pos.y + 30 * direction.y }, m.angle, -15, sqrtCountReduction) bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5) bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) } } const cycle = () => { - if ((simulation.paused || m.isBodiesAsleep) && m.alive) { + if ((simulation.paused) && m.alive) { requestAnimationFrame(cycle) } else { count++ @@ -6781,15 +6772,9 @@ const b = { requestAnimationFrame(cycle); } else { if (m.crouch) { - b.missile({ - x: m.pos.x + 40 * direction.x, - y: m.pos.y + 40 * direction.y - }, m.angle, 25) + b.missile({ x: m.pos.x + 40 * direction.x, y: m.pos.y + 40 * direction.y }, m.angle, 25) } else { - b.missile({ - x: m.pos.x + 40 * direction.x, - y: m.pos.y + 40 * direction.y - }, m.angle, -12) + b.missile({ x: m.pos.x + 40 * direction.x, y: m.pos.y + 40 * direction.y }, m.angle, -12) bullet[bullet.length - 1].force.y += 0.04 * (Math.random() - 0.2) } } diff --git a/js/index.js b/js/index.js index d36ff1b..ad9d3fd 100644 --- a/js/index.js +++ b/js/index.js @@ -1421,7 +1421,7 @@ window.addEventListener("keydown", function (event) { // level.levelAnnounce(); document.body.style.cursor = "none"; requestAnimationFrame(cycle); - } else if (!tech.isNoDraftPause) { + } else { //if (!tech.isNoDraftPause) simulation.paused = true; build.pauseGrid() document.body.style.cursor = "auto"; diff --git a/js/level.js b/js/level.js index 18ee245..d990d5b 100644 --- a/js/level.js +++ b/js/level.js @@ -17,7 +17,9 @@ const level = { if (level.levelsCleared === 0) { //this code only runs on the first level // simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode // simulation.isHorizontalFlipped = true - // level.levelsCleared = 4 + // spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns + // spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns + // level.levelsCleared = 11 // level.updateDifficulty() // tech.giveTech("performance") // m.maxHealth = m.health = 1//00000000 @@ -44,17 +46,20 @@ const level = { // b.guns[8].ammo = 100000000 // requestAnimationFrame(() => { tech.giveTech("stimulated emission") }); // tech.giveTech("Hilbert space") - // for (let i = 0; i < 1; ++i) tech.giveTech("decoherence") - // for (let i = 0; i < 1; ++i) tech.giveTech("mass-energy equivalence") + // tech.addJunkTechToPool(0.5) + // for (let i = 0; i < 1; ++i) tech.giveTech("coherence") + // for (let i = 0; i < 1; ++i) tech.giveTech("nitinol") + // m.skin.egg(); + // for (let i = 0; i < 1; ++i) tech.giveTech("depolarization") // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("wikipedia") }); // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("field coupling") }); // for (let i = 0; i < 1; i++) tech.giveTech("interest") // m.lastKillCycle = m.cycle - // for (let i = 0; i < 1; i++) powerUps.directSpawn(450, -50, "tech"); - // for (let i = 0; i < 3; i++) powerUps.directSpawn(m.pos.x + 200, m.pos.y - 50, "boost", false); + // for (let i = 0; i < 4; i++) powerUps.directSpawn(450, -50, "tech"); + // for (let i = 0; i < 7; i++) powerUps.directSpawn(m.pos.x + 200, m.pos.y - 250, "research", false); // spawn.bodyRect(575, -700, 150, 150); //block mob line of site on testing - // level.heal(); + // level.subway(); level[simulation.isTraining ? "walk" : "initial"]() //normal starting level ************************************************** @@ -334,6 +339,30 @@ const level = { constraintDescription1: "", //used in pause menu and console constraintDescription2: "", constraint: [ + { + description: "no pause while choosing", + effect() { + level.isNoPause = true + }, + remove() { + level.isNoPause = false + } + }, + { + description: "no health bars", + effect() { + mobs.healthBar = () => { } + level.isHideHealth = true + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + }, + remove() { + mobs.healthBar = mobs.defaultHealthBar + level.isHideHealth = false + document.getElementById("health").style.display = "inline" + document.getElementById("health-bg").style.display = "inline" + } + }, { description: "0.5x energy regen", effect() { @@ -561,6 +590,8 @@ const level = { reducedHealthLost: 0, isReducedHealth: false, isReducedRegen: 1, + isHideHealth: false, + isNoPause: false, levelAnnounce() { const cheating = simulation.isCheating ? "(testing)" : "" if (level.levelsCleared === 0) { @@ -2604,7 +2635,6 @@ const level = { //start a conversation based on the number of conversations seen if (localSettings.loreCount > lore.conversation.length - 1) localSettings.loreCount = lore.conversation.length - 1; //repeat final conversation if lore count is too high if (!simulation.isCheating && localSettings.loreCount < lore.conversation.length) { - tech.isNoDraftPause = true //disable pause lore.testSpeechAPI() //see if speech is working lore.chapter = localSettings.loreCount //set the chapter to listen to to be the lore level (you can't use the lore level because it changes during conversations) lore.sentence = 0 //what part of the conversation to start on @@ -3185,7 +3215,7 @@ const level = { train[train.length - 1].stops = { left: -7225, right: -1725 } const stationList = [] //use to randomize station order - for (let i = 1, totalNumberOfStations = 8; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station + for (let i = 1, totalNumberOfStations = 10; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station shuffle(stationList); stationList.splice(0, 3); //remove some stations to keep it to 4 stations stationList.unshift(0) //add index zero to the front of the array @@ -3201,6 +3231,8 @@ const level = { } removeAll(map); map = []; + removeAll(composite); + composite = [] //remove any powerUp that is too far from player for (let i = 0; i < powerUp.length; ++i) { if (Vector.magnitudeSquared(Vector.sub(player.position, powerUp[i].position)) > 9000000) { //remove any powerUp farther then 3000 pixels from player @@ -3244,7 +3276,25 @@ const level = { } } } - gateButton.draw(); + // gateButton.draw(); + if (gateButton.isUp) { + //aura around button + ctx.beginPath(); + ctx.ellipse(gateButton.min.x + gateButton.width * 0.5, gateButton.min.y + 6, 0.75 * gateButton.width, 0.5 * gateButton.width, 0, Math.PI, 0); //ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) + ctx.fillStyle = `hsla(345, 100%, 80%,${0.1 + 0.4 * Math.random()})` + ctx.fill(); + ctx.fillStyle = "hsl(345, 100%, 75%)" + ctx.fillRect(gateButton.min.x, gateButton.min.y - 10, gateButton.width, 25) + ctx.strokeStyle = "#000"//"rgba(255,255,255,0.2)" + ctx.lineWidth = 2 + ctx.strokeRect(gateButton.min.x, gateButton.min.y - 10, gateButton.width, 25) + } else { + ctx.fillStyle = "hsl(345, 100%, 75%)" + ctx.fillRect(gateButton.min.x, gateButton.min.y, gateButton.width, 10) + ctx.strokeStyle = "#000"//"rgba(255,255,255,0.2)" + ctx.lineWidth = 2 + ctx.strokeRect(gateButton.min.x, gateButton.min.y, gateButton.width, 10) + } } } const stations = [ //update totalNumberOfStations as you add more stations @@ -3940,8 +3990,235 @@ const level = { ctx.fillRect(x + 950, -675, 400, 125); } }, + () => { //angled jumps + const buttonsCoords = [{ x: x + 50, y: -1395 }, { x: x - 625, y: -2945 }, { x: x + 900, y: -2945 }] + const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array + + spawn.mapRect(x + -1500, -210, 3000, 400);//station floor + boosts = [] + boosts.push(level.boost(x - 311, -218, 1200, 1.85)) + spawn.mapRect(x + -225, -525, 675, 375); + spawn.mapRect(x + -1350, -1175, 400, 675); + spawn.mapRect(x + -225, -2125, 675, 400); + + // spawn.mapRect(x + -225, -1325, 675, 550); + spawn.mapRect(x + -225, -1400, 675, 650); + + boosts.push(level.boost(x - 1335, -1200, 1800, 1)) + boosts.push(level.boost(x + 1272, -1300, 1550, 2.75)) //far right + //high up walls + boosts.push(level.boost(x + 1455, -2048, 1450, 2.5)) + spawn.mapRect(x + 1500, -3825, 325, 1900); + boosts.push(level.boost(x - 1555, -2048, 1450, 0.64)) + // spawn.mapRect(x + -1625, -3975, 3450, 325); + spawn.mapRect(x + -1825, -4000, 325, 2150); + spawn.mapRect(x + -1825, -4070, 3650, 375);//roof + + spawn.randomMob(x + 100, -2125, 0); + boosts.push(level.boost(x + 75, -2175, 2800)) + spawn.mapRect(x + -100, -3900, 400, 400); + Matter.Body.setAngle(map[map.length - 1], map[map.length - 1].angle - Math.PI / 4); + + spawn.mapRect(x + 225, -2950, 1100, 150); + spawn.mapRect(x + -1325, -2950, 1325, 150); + + if (isExitOpen) { + level.exit.x = buttonsCoords[buttonsCoordsIndex].x; + level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25; + } else { + var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber > gatesOpenRight) { + var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } else if (stationNumber < gatesOpenLeft) { + var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } + } + + if (!isExitOpen) { + spawn.randomMob(x + 350, -600, 0); + spawn.randomMob(x + -25, -600, 0); + spawn.randomMob(x + 600, -300, 0); + spawn.randomMob(x + 1050, -300, 0); + spawn.randomMob(x + 350, -1525, 0); + spawn.randomMob(x + -75, -1525, 0); + spawn.randomMob(x + -1075, -1275, 0); + spawn.randomMob(x + -1350, -2050, 0); + spawn.randomMob(x + -50, -2250, 0); + spawn.randomMob(x + -200, -3050, 0); + spawn.randomMob(x + -925, -3150, 0); + spawn.randomMob(x + 450, -3125, 0); + spawn.randomMob(x + 1075, -3025, 0); + spawn.randomMob(x + 750, -3125, 0); + spawn.randomMob(x + -725, -3125, 0); + } + stationCustom = () => { + for (let i = 0; i < boosts.length; i++) { + boosts[i].query() + } + } + stationCustomTopLayer = () => { + checkGate(gate, gateButton) + ctx.fillStyle = "rgba(0,0,0,0.08)" + ctx.fillRect(x - 225, -775, 675, 275); + ctx.fillRect(x - 225, -1750, 675, 375); + } + }, + () => { //people movers + simulation.removeEphemera("zoom")//stop previous zooms + simulation.zoomTransition(2000) + const buttonsCoords = [{ x: x - 65, y: -2045 }] //only one button location? + const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array + const moverDirection = stationNumber > 0 ? 1 : -1 + console.log(stationNumber) + if (isExitOpen) { + level.exit.x = buttonsCoords[buttonsCoordsIndex].x; + level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25; + } else { + var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true + gateButton.isUp = true + if (stationNumber > gatesOpenRight) { + var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } else if (stationNumber < gatesOpenLeft) { + var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20 + } + } + + //floor 0 + spawn.mapRect(x + -1500, -210, 3000, 400);//station floor + const movers = [] + movers.push(level.mover(x + -1200, -220, 900, 50, 3 * moverDirection)) + movers.push(level.mover(x + 300, -220, 900, 50, 3 * moverDirection)) + spawn.mapRect(x + -4700, -7000, 700, 5200);//Left wall + spawn.mapRect(x + 4000, -7000, 500, 5200);//Right wall + const portals = [] + portals.push(level.portal({ x: x - 315, y: -310 }, Math.PI, { x: x - 3985, y: -2110 }, 0)) + spawn.mapRect(x - 1375, -1100, 2750, 300); + spawn.mapRect(x + -300, -525, 600, 550); + + //floor 1 fast with jump in middle + movers.push(level.mover(x - 4000, -2025, 2700, 50, 30 * moverDirection)) + movers.push(level.mover(x + 1300, -2025, 2700, 50, 30 * moverDirection)) + portals.push(level.portal({ x: x + 3985, y: -2110 }, Math.PI, { x: x - 3985, y: -3410 }, 0)) + spawn.mapRect(x + -500, -2050, 1000, 150); + spawn.mapRect(x + -4200, -2300, 1225, 125); + spawn.mapRect(x + 2675, -2350, 1625, 150); + //up mode triggered by player contact + const elevator0 = level.elevator(x - 1300, -1175, 175, 50, -1600, 0.011, { up: 0.01, down: 0.7 }) + const elevator1 = level.elevator(x + 1125, -1175, 175, 50, -1600, 0.011, { up: 0.01, down: 0.7 }) + + //floor 2 slow with some things to jump on and mobs + portals.push(level.portal({ x: x + 3985, y: -3410 }, Math.PI, { x: x - 3985, y: -5110 }, 0)) + movers.push(level.mover(x - 4000, -3325, 8000, 50, 7 * moverDirection)) + if (Math.random() < 0.5) { + spawn.mapRect(x + 1125, -3625, 325, 200); + spawn.mapRect(x - 1350, -3600, 375, 175); + spawn.mapRect(x + 325, -3825, 325, 100); + spawn.mapRect(x - 675, -3800, 450, 75); + spawn.mapRect(x - 1775, -3900, 175, 400); + spawn.mapRect(x - 2100, -4275, 325, 775); + spawn.mapRect(x + 2625, -3700, 450, 125); + spawn.mapRect(x - 3350, -3335, 175, 50); + spawn.mapRect(x - 200, -3335, 500, 50); + spawn.mapRect(x + 3200, -3335, 325, 50); + } else { + spawn.mapRect(x + -325, -3550, 425, 125); + spawn.mapRect(x + -1100, -3750, 425, 75); + spawn.mapRect(x + -2175, -3500, 200, 200); + spawn.mapRect(x + 675, -3700, 175, 75); + spawn.mapRect(x + 2375, -3425, 275, 125); + spawn.mapRect(x + 1750, -3650, 275, 75); + spawn.mapRect(x + 1125, -3850, 175, 550); + spawn.mapRect(x + -3300, -4175, 675, 550); + } + spawn.mapRect(x + 3550, -3625, 550, 100); + spawn.mapRect(x + -4100, -3650, 325, 100); + if (!isExitOpen) { + spawn.randomMob(x + 3900, -3725, 0); + spawn.randomMob(x + 3675, -3700, 0); + spawn.randomMob(x + 2075, -3400, 0); + spawn.randomMob(x + 2500, -3500, 0); + spawn.randomMob(x + 1975, -3700, 0); + spawn.randomMob(x + 1250, -3900, 0); + spawn.randomMob(x + 800, -3750, 0); + spawn.randomMob(x + 2700, -4700, 0); + spawn.randomMob(x + -75, -3650, 0); + spawn.randomMob(x + 575, -3500, 0); + spawn.randomMob(x + -850, -3900, 0); + spawn.randomMob(x + -2725, -4350, 0); + spawn.randomMob(x + -2975, -4300, 0); + spawn.randomMob(x + -3950, -3675, 0); + spawn.randomMob(x + -2950, -3450, 0); + spawn.randomMob(x + -2075, -3575, 0); + spawn.randomMob(x + -1650, -3450, 0); + spawn.randomMob(x + -2825, -4400, 0); + spawn.randomMob(x + -900, -4475, 0); + spawn.randomMob(x + -75, -3575, 0); + spawn.randomMob(x + 3900, -3775, 0); + spawn.randomMob(x + 2825, -3375, 0); + spawn.randomMob(x + 2075, -3425, 0); + spawn.randomMob(x + 1525, -3425, 0); + spawn.randomMob(x + 350, -3500, 0); + spawn.randomMob(x + -1675, -3650, 0); + spawn.randomMob(x + -3025, -3450, 0); + spawn.randomMob(x + -3850, -3750, 0); + } + + //floor 3 fast with bumps + spawn.mapRect(x + -4250, -7000, 8475, 325);//roof + portals.push(level.portal({ x: x + 3985, y: -5110 }, Math.PI, { x: x + 320, y: -310 }, 0)) + movers.push(level.mover(x - 4000, -5025, 8000, 50, 50 * moverDirection)) + if (Math.random() < 0.5) { + spawn.mapVertex(x - 2100, -5050, "-150 0 150 0 5 -150 -5 -150") + spawn.mapVertex(x - 0, -5100, "-500 0 500 0 25 -300 -25 -300") + spawn.mapVertex(x + 2100, -5050, "-300 0 300 0 100 -100 -100 -100") + } else { + spawn.mapVertex(x - 2100, -5050, "-100 0 100 0 25 -100 -25 -100") + spawn.mapVertex(x - 0, -5050, "-400 0 400 0 100 -100 -100 -100") + spawn.mapVertex(x + 2100, -5050, "-400 0 400 0 100 -100 -100 -100") + } + spawn.mapRect(x + 2000, -6700, 200, 1250); + spawn.mapRect(x + -100, -6700, 200, 1075); + spawn.mapRect(x + -2125, -6700, 50, 925); + // spawn.mapRect(x + -4150, -5325, 975, 125); //portal over hang + // spawn.mapRect(x + 3325, -5300, 850, 100);//portal over hang + + stationCustom = () => { + for (let i = 0; i < movers.length; i++) movers[i].push(); + for (let i = 0; i < portals.length; i++) { + portals[i][2].query() + portals[i][3].query() + } + } + stationCustomTopLayer = () => { + for (let i = 0; i < portals.length; i++) { + portals[i][0].draw() + portals[i][1].draw() + portals[i][2].draw() + portals[i][3].draw() + } + elevator0.moveOnTouch() + elevator1.moveOnTouch() + + //custom draw so you can see the mover tracks on subway map with the Line of sight graphics + ctx.strokeStyle = "#000" + ctx.lineWidth = 4; + ctx.setLineDash([40, 40]); + for (let i = 0; i < movers.length; i++) { + ctx.beginPath(); + ctx.moveTo(movers[i].vertices[0].x + 2, movers[i].vertices[0].y - 3); + ctx.lineTo(movers[i].vertices[1].x - 2, movers[i].vertices[1].y - 3); + ctx.lineDashOffset = (-simulation.cycle * movers[i].VxGoal) % 80; + ctx.stroke(); + } + ctx.setLineDash([0, 0]); + checkGate(gate, gateButton) + } + }, ] - // stations[4]() //for testing a specific station + //update totalNumberOfStations to a higher number when adding new maps + simulation.zoomTransition(level.defaultZoom) + // stations[9]() //for testing a specific station stations[stationList[Math.abs(stationNumber % stationList.length)]]() //*************** run this one when uploading //add in standard station map infrastructure spawn.mapRect(x + -8000, 0, 16000, 800);//tunnel floor diff --git a/js/mob.js b/js/mob.js index 01a7f04..71ca03d 100644 --- a/js/mob.js +++ b/js/mob.js @@ -30,6 +30,20 @@ const mobs = { // } } }, + defaultHealthBar() { + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].seePlayer.recall && mob[i].showHealthBar) { + const h = mob[i].radius * 0.3; + const w = mob[i].radius * 2; + const x = mob[i].position.x - w / 2; + const y = mob[i].position.y - w * 0.7; + ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; + ctx.fillRect(x, y, w, h); + ctx.fillStyle = "rgba(255,0,0,0.7)"; + ctx.fillRect(x, y, w * mob[i].health, h); + } + } + }, healthBar() { for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].seePlayer.recall && mob[i].showHealthBar) { @@ -1010,8 +1024,9 @@ const mobs = { this.death(); //death with no power up } }, - healthBar() { //draw health by mob //most health bars are drawn in mobs.healthbar(); - if (this.seePlayer.recall) { + //draw health by mob //most health bars are drawn in mobs.healthBar(); , not this + healthBar() { + if (this.seePlayer.recall && !level.isHideHealth) { const h = this.radius * 0.3; const w = this.radius * 2; const x = this.position.x - w / 2; diff --git a/js/player.js b/js/player.js index 9bd7a42..6b0cb91 100644 --- a/js/player.js +++ b/js/player.js @@ -987,6 +987,121 @@ const m = { powerUps.boost.draw() } }, + egg() { + m.isAltSkin = true + m.yOffWhen.stand = 52 + m.yOffWhen.jump = 72 + m.coyoteCycles = 11 + m.hardLandCDScale = 0.5 + m.hardLanding = 160 + m.squirrelFx = 1.4; + m.squirrelJump = 1.16; + m.setMovement() + + m.draw = function () { + if (powerUps.boost.endCycle > simulation.cycle) { + //gel that acts as if the wind is blowing it when player moves + ctx.save(); + ctx.translate(m.pos.x, m.pos.y); + m.velocitySmooth = Vector.add(Vector.mult(m.velocitySmooth, 0.8), Vector.mult(player.velocity, 0.2)) + ctx.rotate(Math.atan2(m.velocitySmooth.y, m.velocitySmooth.x)) + ctx.beginPath(); + const radius = 39 + const mag = 14 * Vector.magnitude(m.velocitySmooth) + radius + ctx.arc(0, 0, radius, -Math.PI / 2, Math.PI / 2); + ctx.bezierCurveTo(-radius, radius, -radius, 0, -mag, 0); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + ctx.bezierCurveTo(-radius, 0, -radius, -radius, 0, -radius); + + // const time = (powerUps.boost.endCycle - m.cycle) / powerUps.boost.duration + const time = Math.min(0.5, (powerUps.boost.endCycle - simulation.cycle) / powerUps.boost.duration) + + ctx.fillStyle = `rgba(0,0,0,${0.04 + 0.3 * time})` + ctx.fill() + // ctx.strokeStyle = "#333" + // ctx.lineWidth = 1 + // ctx.stroke(); + ctx.restore(); + } + + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -1.25); + m.drawLeg("#606060"); + m.calcLeg(0, 0); + m.drawLeg("#444"); + + ctx.rotate(m.angle); + ctx.beginPath(); + // ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.ellipse(0, 0, 0.9 * 31, 1.05 * 31, 0, 0, 2 * Math.PI); + ctx.fillStyle = m.bodyGradient + ctx.fill(); + // ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.ellipse(15, 0, 0.8 * 4, 1.1 * 4, 0, 0, 2 * Math.PI); + + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.75 + m.yOffGoal * 0.25; //smoothly move leg height towards height goal + } + m.drawLeg = function (stroke) { + if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { + m.flipLegs = 1; + } else { + m.flipLegs = -1; + } + const hip = { x: m.hip.x - 5, y: m.hip.y + 5 } + const sub = Vector.sub(m.knee, hip) + const off = Vector.mult(Vector.rotate(Vector.normalise(sub), Math.PI / 2), 8) + const kneeBraceHigh = Vector.add(hip, off) + const kneeBraceLow = Vector.add(kneeBraceHigh, Vector.mult(sub, 0.9)) + const foot = { x: m.foot.x - 10, y: m.foot.y - 15 } + ctx.save(); + ctx.scale(m.flipLegs, 1); //leg lines + ctx.beginPath(); + ctx.moveTo(hip.x, hip.y); + ctx.lineTo(m.knee.x, m.knee.y); + ctx.lineTo(foot.x, foot.y); + //extra upper leg brace + ctx.moveTo(kneeBraceHigh.x, kneeBraceHigh.y); + ctx.lineTo(kneeBraceLow.x, kneeBraceLow.y); + ctx.lineTo(m.knee.x, m.knee.y); + + ctx.strokeStyle = stroke; + ctx.lineWidth = 3; + ctx.stroke(); + //foot + ctx.beginPath(); + ctx.moveTo(foot.x, foot.y); + ctx.quadraticCurveTo(m.foot.x - 30, m.foot.y + 12, m.foot.x + 13, m.foot.y + 3); + ctx.lineWidth = 1.5; + ctx.stroke(); + + //hip joint + ctx.beginPath(); + ctx.arc(m.hip.x, m.hip.y - 2, 11, 0, 2 * Math.PI); + //knee joint + ctx.moveTo(m.knee.x + 3, m.knee.y); + ctx.arc(m.knee.x, m.knee.y, 3, 0, 2 * Math.PI); + //knee brace + // ctx.moveTo(kneeBraceHigh.x + 4, kneeBraceHigh.y); + // ctx.arc(kneeBraceHigh.x, kneeBraceHigh.y, 4, 0, 2 * Math.PI); + ctx.moveTo(kneeBraceLow.x + 2.5, kneeBraceLow.y); + ctx.arc(kneeBraceLow.x, kneeBraceLow.y, 2.5, 0, 2 * Math.PI); + //foot joint + ctx.moveTo(foot.x + 2.5, foot.y); + ctx.arc(foot.x, foot.y, 2.5, 0, 2 * Math.PI); + ctx.fillStyle = "#f6f6f6"//m.fillColor; + ctx.fill(); + ctx.lineWidth = 1; + // ctx.strokeStyle = "#333" + ctx.stroke(); + ctx.restore(); + } + }, mech() { m.isAltSkin = true m.yOffWhen.stand = 52 @@ -1031,6 +1146,7 @@ const m = { m.drawLeg("#606060"); m.calcLeg(0, 0); m.drawLeg("#444"); + ctx.rotate(m.angle); ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); diff --git a/js/powerup.js b/js/powerup.js index 4f3af63..6931d74 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -250,7 +250,6 @@ const powerUps = { } else if (type === "field") { m.setField(index) } else if (type === "tech") { - // if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false simulation.inGameConsole(`
  tech.giveTech("${tech.tech[index].name}")`); tech.giveTech(index) } @@ -261,7 +260,6 @@ const powerUps = { document.getElementById("choose-grid").style.pointerEvents = "none"; document.body.style.cursor = "none"; setTimeout(() => { - // if (!tech.isNoDraftPause) document.body.style.cursor = "auto"; document.getElementById("choose-grid").style.pointerEvents = "auto"; document.getElementById("choose-grid").style.transitionDuration = "0s"; @@ -269,7 +267,7 @@ const powerUps = { simulation.isChoosing = true; //stops p from un pausing on key down if (!simulation.paused) { - if (tech.isNoDraftPause) { + if (tech.isNoDraftPause || level.isNoPause) { document.getElementById("choose-grid").style.opacity = "1" } else { simulation.paused = true; @@ -610,14 +608,6 @@ const powerUps = { tech.addJunkTechToPool(0.01) } powerUps.research.currentRerollCount++ - // if (tech.isBanish && type === 'tech') { // banish researched tech - // const banishLength = tech.isDeterminism ? 1 : 3 + tech.extraChoices * 2 - // for (let i = 0; i < banishLength; i++) { - // const index = powerUps.tech.choiceLog.length - i - 1 - // if (powerUps.tech.choiceLog[index] && tech.tech[powerUps.tech.choiceLog[index]]) tech.tech[powerUps.tech.choiceLog[index]].isBanished = true - // } - // simulation.inGameConsole(`powerUps.tech.length: ${Math.max(0,powerUps.tech.lastTotalChoices - banishLength)}`) - // } if (tech.isResearchReality) { m.switchWorlds() simulation.trails() @@ -769,24 +759,24 @@ const powerUps = { if (tech.isSuperDeterminism) { return `
` } else if (tech.isCancelTech && tech.cancelTechCount === 0) { - return `
randomize
` + return `
randomize
` } else if (level.levelsCleared === 0 && localSettings.isTrainingNotAttempted && b.inventory.length === 0) { //don't show cancel if on initial level and haven't done tutorial - return `` + return `` } else { - return `
cancel
` + return `
cancel
` } }, researchText(type) { let text = "" if (type === "entanglement") { - text += `
entanglement
` + text += `
entanglement
` } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 2) { - text += `
` // style = "margin-left: 192px; margin-right: -192px;" + text += `
` // style = "margin-left: 192px; margin-right: -192px;" text += `
` text += `
` text += `
  pseudoscience
` } else if (powerUps.research.count > 0) { - text += `
` // style = "margin-left: 192px; margin-right: -192px;" + text += `
` // style = "margin-left: 192px; margin-right: -192px;" text += `
` for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += `
` text += `
  ${tech.isResearchReality ? "alternate reality" : "research"}
` @@ -798,7 +788,7 @@ const powerUps = { researchAndCancelText(type) { let text = `
` if (type === "entanglement") { - text += `entanglement` //‌ + text += `entanglement` } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 2) { text += `` // style = "margin-left: 192px; margin-right: -192px;" text += `
` @@ -1239,37 +1229,55 @@ const powerUps = { for (let i = 0; i < tech.tech.length; i++) tech.tech[i].isRecentlyShown = false //reset recently shown back to zero if (options.length > 0) { let text = powerUps.buildColumns(totalChoices, "tech") + + addTech = (choose) => { + if (tech.tech[choose].isFieldTech) { + text += powerUps.fieldTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isGunTech) { + text += powerUps.gunTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isJunk) { + text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isSkin) { + text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isInstant) { + text += powerUps.instantTechText(choose, `powerUps.choose('tech',${choose})`) + } else { //normal tech + text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) + } + } + if (tech.isRetain) { + for (let i = 0, len = powerUps.retainList.length; i < len; i++) { + //find index from name and add tech to options + for (let j = 0, len = tech.tech.length; j < len; j++) { + if (tech.tech[j].name === powerUps.retainList[i] && tech.tech[j].count < tech.tech[j].maxCount && tech.tech[j].allowed()) { //&& !tech.tech[j].isRecentlyShown + addTech(j) + } + } + } + } for (let i = 0; i < totalChoices; i++) { if (options.length < 1) break - const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options - if (tech.isBanish) { - tech.tech[choose].isBanished = true - if (i === 0) simulation.inGameConsole(`options.length = ${optionLengthNoDuplicates} //removed from pool by decoherence`) - } - removeOption(choose) //move from future options pool to avoid repeats on this selection - tech.tech[choose].isRecentlyShown = true //this flag prevents this option from being shown the next time you pick up a tech power up if (Math.random() < tech.junkChance + level.junkAdded) { // choose is set to a random JUNK tech const list = [] for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].isJunk) list.push(i) } chooseJUNK = list[Math.floor(Math.random() * list.length)] + if (tech.isRetain) powerUps.retainList.push(tech.tech[chooseJUNK].name) text += powerUps.junkTechText(chooseJUNK, `powerUps.choose('tech',${chooseJUNK})`) } else { - if (tech.tech[choose].isFieldTech) { - text += powerUps.fieldTechText(choose, `powerUps.choose('tech',${choose})`) - } else if (tech.tech[choose].isGunTech) { - text += powerUps.gunTechText(choose, `powerUps.choose('tech',${choose})`) - } else if (tech.tech[choose].isJunk) { - text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`) - } else if (tech.tech[choose].isSkin) { - text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`) - } else if (tech.tech[choose].isInstant) { - text += powerUps.instantTechText(choose, `powerUps.choose('tech',${choose})`) - } else { //normal tech - text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) + const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options + if (tech.isBanish) { + tech.tech[choose].isBanished = true + if (i === 0) simulation.inGameConsole(`options.length = ${optionLengthNoDuplicates} //removed from pool by decoherence`) } - if (options.length < 1) break + removeOption(choose) //remove from future options pool to avoid repeats on this selection + + //this flag prevents this option from being shown the next time you pick up a tech power up + //check if not extra choices from "path integral" + tech.tech[choose].isRecentlyShown = true + if (tech.isRetain) powerUps.retainList.push(tech.tech[choose].name) + addTech(choose) } } if (tech.isExtraBotOption) { @@ -1360,6 +1368,7 @@ const powerUps = { } }, }, + retainList: [], entanglement: { name: "entanglement", color: "#fff", //"hsl(248,100%,65%)", diff --git a/js/tech.js b/js/tech.js index fc7ca79..239e2ee 100644 --- a/js/tech.js +++ b/js/tech.js @@ -1497,7 +1497,7 @@ const tech = { descriptionFunction() { return `${powerUps.orb.boost(1)} also give 0.3x damage taken
for ${(powerUps.boost.duration / 60).toFixed(0)} seconds
` }, - maxCount: 9, + maxCount: 1, count: 1, frequency: 2, frequencyDefault: 2, @@ -3598,7 +3598,7 @@ const tech = { allowed() { return !tech.isSuperDeterminism }, - requires: "not superdeterminism", + requires: "not, superdeterminism", bonusResearch: 7, effect() { tech.isBanish = true @@ -3614,6 +3614,24 @@ const tech = { tech.isBanish = false } }, + { + name: "coherence", + description: `after observing a ${powerUps.orb.tech()} choice
that choice is available for all all future ${powerUps.orb.tech()}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.isBanish + }, + requires: "decoherence", + effect() { + tech.isRetain = true + }, + remove() { + tech.isRetain = false + } + }, { name: "peer review", description: `after you research gain 1.05x damage
and +1% JUNK choices`, @@ -3743,9 +3761,9 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isSuperDeterminism && !tech.isNoDraftPause + return !tech.isSuperDeterminism }, - requires: "not superdeterminism, eternalism", + requires: "not superdeterminism", effect() { tech.isPauseSwitchField = true; for (let i = 0, len = tech.tech.length; i < len; i++) { @@ -3763,7 +3781,7 @@ const tech = { }, { name: "eternalism", - description: `1.25x damage
time can't be paused (time can still be dilated)`, + description: `1.3x damage, but time doesn't pause
while choosing ${powerUps.orb.field()}, ${powerUps.orb.tech()}, or ${powerUps.orb.gun()}`, maxCount: 1, count: 0, frequency: 1, @@ -3772,7 +3790,7 @@ const tech = { return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause }, requires: "not unified field theory, paradigm shift, invariant", - damage: 1.25, + damage: 1.3, effect() { tech.damage *= this.damage tech.isNoDraftPause = true @@ -3960,7 +3978,7 @@ const tech = { { name: "dark patterns", description: "1.3x damage
+15% JUNK choices", - maxCount: 9, + maxCount: 3, count: 0, frequency: 1, frequencyDefault: 1, @@ -4604,9 +4622,9 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isSuperDeterminism && !tech.isNoDraftPause + return !tech.isSuperDeterminism }, - requires: "not superdeterminism, eternalism", + requires: "not superdeterminism", effect() { tech.isPauseEjectTech = true; }, @@ -6181,7 +6199,7 @@ const tech = { { name: "water shielding", link: `water shielding`, - description: "reduce radioactive effects on you by 0.8x
neutron bomb, drones, explosions, slime", + description: "reduce radioactive effects on you by 0.2x
neutron bomb, drones, explosions, slime", isGunTech: true, maxCount: 1, count: 0, @@ -7735,7 +7753,7 @@ const tech = { // }, name: "electronegativity", descriptionFunction() { - return `1.0023x damage per energy
(${(1 + 0.23 * m.energy).toFixed(2)} at current energy, ${(1 + 0.23 * m.maxEnergy).toFixed(2)}x at maximum energy)` + return `1.0023x damage per energy
(${(1 + 0.23 * m.energy).toFixed(2)}x, ${(1 + 0.23 * m.maxEnergy).toFixed(2)}x at max energy)` }, // description: "+1% damage per 8 stored energy", isFieldTech: true, @@ -11020,7 +11038,7 @@ const tech = { }, { name: "rhythm", - description: "you oscillate up and down", + description: "you oscillate up and down
also you look like an egg", maxCount: 1, count: 0, frequency: 0, @@ -11031,6 +11049,7 @@ const tech = { }, requires: "", effect() { + m.skin.egg(); setInterval(() => { m.yOffWhen.stand = 53 + 28 * Math.sin(simulation.cycle * 0.2) if (m.onGround && !m.crouch) m.yOffGoal = m.yOffWhen.stand @@ -11929,6 +11948,7 @@ const tech = { cancelTechCount: null, isBotDamage: null, isBanish: null, + isRetain: null, isMaxEnergyTech: null, isLowEnergyDamage: null, isRewindBot: null, diff --git a/style.css b/style.css index 965a2fb..f751233 100644 --- a/style.css +++ b/style.css @@ -434,6 +434,15 @@ summary { line-height: 160%; /* background-color: var(--card-color); */ font-size: 1em; + position: sticky; + top: 0px; + z-index: 10; +} + +.sticky { + position: sticky; + top: 0px; + z-index: 10; } /* keeps 5 columns at 1440px */ diff --git a/todo.txt b/todo.txt index e90b56d..3fa073a 100644 --- a/todo.txt +++ b/todo.txt @@ -1,33 +1,21 @@ ******************************************************** NEXT PATCH ************************************************** -tech: hidden-variable theory - after choosing a fieldtech gain 1.15x damage - for pilot wave only -polariton - boosts also give 0.3x damage taken +subway + 2 new subway stations + more visible button graphics on subway -constraints - removed - full damage taken after boss dies - wording is too unclear - new - 0.5x energy regen from all sources - balanced - slow bots - bots have roughly 15% reduction in damage in addition to a slow follow speed - mob death heals mobs - has 1000->700 range and 1->0.33 healing - periodically spawn WIMPs - has a 30s delay and a 15->6s spawn rate - 50->40% JUNK chance +new constraints + no health bars + no pause while choosing - -heuristics gives (1-1.5x)->(1-2x) fire rate and +5% JUNK -autonomous defense harpoon now scale from Bessemer process - but at half the rate since there are 6 harpoons -Bessemer process and rail gun scale at 0.1->0.07 +tech: coherence - past choices are added to all future tech + requires decoherence + research and cancel buttons have a sticky scroll positioning +eternalism: you can't pause while choosing, but you can otherwise pause now + 1.25->1.3 damage bugs - crash with training level "heal" and power ups - set difficulty mode level 2 for training + MIRV missiles now interact with time dilation properly ******************************************************** BUGS ******************************************************** @@ -65,48 +53,44 @@ procedural animation maybe no constraints on final boss and reactor? constraints balance - 50% JUNK chance - 4x shielded mob chance - power ups are sent to next level - +33% chance for mobs to respawn + 40% JUNK chance -1 choice + no health bars + 4x shielded mob chance + +33% chance for mobs to respawn 2x ammo costs - maybe nerf... + too hard 0 duplication + power ups in stasis + 0.1x damage after a power up + 0.5x energy regen 50% max energy 50% max health bots follow slow - 0.1x damage after a power up + periodically spawn WIMPs mob death heals mobs mobs heal for your lost health - periodically spawn WIMPs - 0.5x energy regen -each difficulty setting adds a chance for a random effect - make some effects only possible on certain levels, or with certain bosses? - not implemented random constraint ideas________________________ - mob death spawns something - mob bullets - bosses heals nearby mobs - ammo power ups give 0.7x ammo - 2x research costs - mobs slowly regen health - exit door takes 10x time to open, - and while door is opening (ghosters, suckers) attack? - can't pause while choosing tech, gun, field similar to eternalism - can't have more then 15 bullets - bots do 0.5x damage - remove 2 random tech and return them next level - too niche - player damage is 0.25x while player is invulnerable - all hazards: lasers and slime do 3x damage - player lasers and radiation do 0.5x damage - explosions do 0.5x damage - freeze effects last 0.25x time +not implemented random constraint ideas________________________ + mob death spawns something + mob bullets + bosses heals nearby mobs + ammo power ups give 0.7x ammo + 2x research costs + mobs slowly regen health + exit door takes 10x time to open, + and while door is opening (ghosters, suckers) attack? + can't have more then 15 bullets + how to code? + remove 2 random tech and return them next level + too niche + player damage is 0.25x while player is invulnerable + all hazards: lasers and slime do 3x damage + player lasers and radiation do 0.5x damage + explosions do 0.5x damage + freeze effects last 0.25x time -tech: - lower damage taken over 10s to 0x but after taking damage increase damage taken to 1x - isn't this just CPT skin with less steps? - maybe skin +tech: ice-VII - 1.5x duration for ice-IX tech: - freezing grenades/explosions @@ -117,28 +101,10 @@ tech: - randomize constraints somehow tech: - when you get a bot, get a second bot -tech: - boost power ups also give 0.1x damage taken - -tech: - tech have +3 choices to eject your tech - tech name - no description? - -copy negative-player as a boss? or a JUNK tech? - code is in community map (not sure which) - -harpoon tech: - after firing 4 harpoons your next harpoon has 4x damage - need to track number fired - tech: - after killing a Boss heal to full gain 3x damage for the rest of the level -tech: clicking on this tech in the pause menu will teleport you to the next level - -tech: - instead of reeling in grappling hook teleport to the hook after releasing field button - this might need another buff? - give damage immunity after teleport for 1+ seconds to balance - new level - rework testChamber skin with wheel instead of legs @@ -174,9 +140,6 @@ tech: atomic pile - lose 1 health if you are above the maximum energy do damage? plasma torch tech? -field tech: molecular assembler - every time you spawn a drone/spore/... become immune to damage for time - scales with how much energy was used to spawn drone/... - figure out how to put instructions for controls in background on initial level mouse smooth makes the text position jitter when it moves sub pixels hide the jitter with artificial jitter to make it seem intentional @@ -1392,6 +1355,8 @@ possible names for tech cryocoolers - freezing effects metaphysics - maybe this changes something deep and universal about physics? not sure cork - used as a heat shield for rockets + P = NP - something with speeding up calculation times + transistivity - something where a>b and b>c -> a>c ******************************************************* DESIGN ******************************************************