From 8bb8222b73323d152775d70c6b7f877c0a423ec0 Mon Sep 17 00:00:00 2001 From: landgreen Date: Sun, 20 Oct 2024 16:01:05 -0700 Subject: [PATCH] Halbach array tech: Halbach array - throwing a block will also throw other nearby blocks tech non-renewables now spawns ammo, but ammo can't be picked up grenade tech that cause multiple explosions have less knock back for mobs constraint: 0->0.5x healing wormhole 7->8% duplication many worlds takes a few frames between each tech given bug fixes harpoon ammo gain on autonomous defense fixed constraints are properly randomized again --- img/Halbach array.webp | Bin 0 -> 38978 bytes js/bullet.js | 59 +++++---- js/engine.js | 7 +- js/level.js | 26 ++-- js/mob.js | 2 +- js/player.js | 291 +++++++++++++++++++++++------------------ js/powerup.js | 27 ++-- js/simulation.js | 203 +++++++++++++++++++--------- js/tech.js | 42 ++++-- todo.txt | 57 +++----- 10 files changed, 416 insertions(+), 298 deletions(-) create mode 100644 img/Halbach array.webp diff --git a/img/Halbach array.webp b/img/Halbach array.webp new file mode 100644 index 0000000000000000000000000000000000000000..5d166c383532eb974357c44512805bc678cbe9ca GIT binary patch literal 38978 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�lX%X?LeInjB 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 0) dmg *= 0.5 //reduce damage if a wall is in the way mob[i].damage(dmg * damageScale * m.dmgScale); mob[i].locatePlayer(); - knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg * damageScale) * mob[i].mass * (mob[i].isBoss ? 0.003 : 0.01)); + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg * damageScale) * mob[i].mass * (mob[i].isBoss ? 0.003 : 0.01) * reducedKnock); if (tech.isStun) { mobs.statusStun(mob[i], 30) } else if (!mob[i].isInvulnerable) { @@ -536,7 +536,7 @@ const b = { damageScale *= 0.87 //reduced damage for each additional explosion target } else if (!mob[i].seePlayer.recall && dist < alertRange) { mob[i].locatePlayer(); - knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg * damageScale) * mob[i].mass * (mob[i].isBoss ? 0 : 0.006)); + knock = Vector.mult(Vector.normalise(sub), -Math.sqrt(dmg * damageScale) * mob[i].mass * (mob[i].isBoss ? 0 : 0.006 * reducedKnock)); if (tech.isStun) { mobs.statusStun(mob[i], 30) } else if (!mob[i].isInvulnerable) { @@ -630,11 +630,8 @@ const b = { count++ if (count < 84) requestAnimationFrame(cycle); if (!(count % 7)) { - const unit = Vector.rotate({ - x: 1, - y: 0 - }, 6.28 * Math.random()) - b.explosion(Vector.add(where, Vector.mult(unit, size * (count * 0.01 + 0.03 * Math.random()))), size * (0.4 + Math.random() * 0.35), `hsla(${360 * Math.random()},100%,66%,0.6)`); //makes bullet do explosive damage at end + const unit = Vector.rotate({ x: 1, y: 0 }, 6.28 * Math.random()) + b.explosion(Vector.add(where, Vector.mult(unit, size * (count * 0.01 + 0.03 * Math.random()))), size * (0.4 + Math.random() * 0.35), `hsla(${360 * Math.random()},100%,66%,0.6)`, 0.2); //makes bullet do explosive damage at end } } } @@ -656,7 +653,7 @@ const b = { x: 1, y: 0 }, curl * 6.28 * count / 18 + off) - b.explosion(Vector.add(where, Vector.mult(unit, size * 0.75)), size * 0.7, color); //makes bullet do explosive damage at end + b.explosion(Vector.add(where, Vector.mult(unit, size * 0.75)), size * 0.7, color, 0.5); //makes bullet do explosive damage at end } } } @@ -677,7 +674,7 @@ const b = { if (count < 30 && m.alive) requestAnimationFrame(cycle); if (count === 0) { const color = `hsla(${360 * Math.random()},100%,66%,0.6)` - b.explosion(where, size * 0.8, color); + b.explosion(where, size * 0.8, color, 0.5); } if (count === 8) { const color = `hsla(${360 * Math.random()},100%,66%,0.6)` @@ -686,7 +683,7 @@ const b = { x: 1, y: 0 }, 6.28 * i / len) - b.explosion(Vector.add(where, Vector.mult(unit, 1.1 * range)), size * 0.6, color); //makes bullet do explosive damage at end + b.explosion(Vector.add(where, Vector.mult(unit, 1.1 * range)), size * 0.6, color, 0.5); //makes bullet do explosive damage at end } } if (count === 16) { @@ -696,7 +693,7 @@ const b = { x: 1, y: 0 }, 6.28 * i / len) - b.explosion(Vector.add(where, Vector.mult(unit, 1.4 * range)), size * 0.45, color); //makes bullet do explosive damage at end + b.explosion(Vector.add(where, Vector.mult(unit, 1.4 * range)), size * 0.45, color, 0.5); //makes bullet do explosive damage at end } } count++ @@ -1611,6 +1608,7 @@ const b = { Matter.Body.setVelocity(this.caughtPowerUp, { x: 0, y: 0 }) } else { for (let i = 0, len = powerUp.length; i < len; ++i) { + if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue const radius = powerUp[i].circleRadius + 50 if (Vector.magnitudeSquared(Vector.sub(this.vertices[2], powerUp[i].position)) < radius * radius) { if (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) { @@ -1906,6 +1904,7 @@ const b = { Matter.Body.setVelocity(this.caughtPowerUp, { x: 0, y: 0 }) } else { //&& simulation.cycle % 2 for (let i = 0, len = powerUp.length; i < len; ++i) { + if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue const radius = powerUp[i].circleRadius + 50 if (Vector.magnitudeSquared(Vector.sub(this.vertices[2], powerUp[i].position)) < radius * radius && !powerUp[i].isGrabbed) { if (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) { @@ -3307,9 +3306,11 @@ const b = { for (let i = 0, len = powerUp.length; i < len; ++i) { //grab, but don't lock onto nearby power up if ( Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && - (powerUp[i].name !== "heal" || m.health < 0.97 * m.maxHealth || tech.isDroneGrab) && - (powerUp[i].name !== "field" || !tech.isSuperDeterminism) - // &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab) + !( + (m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") || + (tech.isSuperDeterminism && powerUp[i].name === "field") || + ((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo") + ) ) { //draw pickup for a single cycle ctx.beginPath(); @@ -3337,11 +3338,11 @@ const b = { //look for power ups to lock onto let closeDist = Infinity; for (let i = 0, len = powerUp.length; i < len; ++i) { - if ( - (powerUp[i].name !== "heal" || m.health < 0.97 * m.maxHealth || tech.isDroneGrab) && - (powerUp[i].name !== "field" || !tech.isSuperDeterminism) - // &&(b.inventory.length > 1 || powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab) - ) { + if (!( + (m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") || + (tech.isSuperDeterminism && powerUp[i].name === "field") || + ((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo") + )) { if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) { //draw pickup for a single cycle ctx.beginPath(); @@ -3543,9 +3544,11 @@ const b = { for (let i = 0, len = powerUp.length; i < len; ++i) { if ( Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && - (powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) && - (powerUp[i].name !== "field" || !tech.isSuperDeterminism) - // &&(powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab) + !( + (m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") || + (tech.isSuperDeterminism && powerUp[i].name === "field") || + ((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo") + ) ) { //draw pickup for a single cycle ctx.beginPath(); @@ -3574,11 +3577,11 @@ const b = { //look for power ups to lock onto let closeDist = Infinity; for (let i = 0, len = powerUp.length; i < len; ++i) { - if ( - (powerUp[i].name !== "heal" || m.health < 0.93 * m.maxHealth || tech.isDroneGrab) && - (powerUp[i].name !== "field" || !tech.isSuperDeterminism) - // &&(powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity || tech.isDroneGrab) - ) { + if (!( + (m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") || + (tech.isSuperDeterminism && powerUp[i].name === "field") || + ((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo") + )) { if (Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !simulation.isChoosing) { //draw pickup for a single cycle ctx.beginPath(); diff --git a/js/engine.js b/js/engine.js index c60ec97..e3a655d 100644 --- a/js/engine.js +++ b/js/engine.js @@ -163,10 +163,10 @@ function collisionChecks(event) { let count = maxCount - 1 const angle = Math.atan2(mob[k].position.y - player.position.y, mob[k].position.x - player.position.x); const mass = 0.75 * ((tech.isLargeHarpoon) ? 1 + Math.min(0.05 * Math.sqrt(b.guns[9].ammo), 10) : 1) - b.harpoon(m.pos, mob[k], angle, mass, true, 7) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { + b.harpoon(m.pos, mob[k], angle, mass, true, 7, false) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { bullet[bullet.length - 1].drain = 0 for (; count > 0; count--) { - b.harpoon(m.pos, mob[k], angle + count * 2 * Math.PI / maxCount, mass, true, 7) + b.harpoon(m.pos, mob[k], angle + count * 2 * Math.PI / maxCount, mass, true, 7, false) bullet[bullet.length - 1].drain = 0 } } @@ -245,8 +245,7 @@ function collisionChecks(event) { mob[k].damage(dmg, true); if (tech.isBlockPowerUps && !mob[k].alive && mob[k].isDropPowerUp && Math.random() < 0.5) { - options = ["coupling", "boost", "heal", "research"] - if (!tech.isEnergyNoAmmo) options.push("ammo") + options = ["coupling", "boost", "heal", "research", "ammo"] powerUps.spawn(mob[k].position.x, mob[k].position.y, options[Math.floor(Math.random() * options.length)]); } diff --git a/js/level.js b/js/level.js index df02b0c..1b71906 100644 --- a/js/level.js +++ b/js/level.js @@ -30,7 +30,7 @@ const level = { // tech.tech[297].frequency = 100 // tech.addJunkTechToPool(0.5) // m.couplingChange(10) - // m.setField("standing wave") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole 10 grappling hook + // m.setField("negative mass") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole 10 grappling hook // m.energy = 0 // powerUps.research.count = 3 // tech.isHookWire = true @@ -38,21 +38,21 @@ const level = { // simulation.molecularMode = 2 // m.damage(0.1); // b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser - // b.giveGuns("spores") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser + // b.giveGuns("harpoon") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.giveGuns("laser") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // tech.laserColor = "#fff" // tech.laserColorAlpha = "rgba(255, 255, 255, 0.5)" // b.guns[8].ammo = 100000000 - // requestAnimationFrame(() => { tech.giveTech("stimulated emission") }); + // requestAnimationFrame(() => { tech.giveTech("non-renewables") }); // tech.giveTech("dark matter") // tech.addJunkTechToPool(0.5) - // for (let i = 0; i < 1; ++i) tech.giveTech("entropic gravity") - // for (let i = 0; i < 1; ++i) tech.giveTech("nitinol") + // for (let i = 0; i < 1; ++i) tech.giveTech("many-worlds") + // for (let i = 0; i < 1; ++i) tech.giveTech("quantum immortality") // m.skin.egg(); - // for (let i = 0; i < 1; ++i) tech.giveTech("depolarization") - // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("wikipedia") }); + // for (let i = 0; i < 1; ++i) tech.giveTech("non-renewables") + // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("quasiparticles") }); // 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 @@ -64,7 +64,7 @@ const level = { level[simulation.isTraining ? "walk" : "initial"]() //normal starting level ************************************************** - // for (let i = 0; i < 1; ++i) spawn.snakeBoss(1900, -500) + // for (let i = 0; i < 5; ++i) spawn.sneaker(1900, -500) // for (let i = 0; i < 1; i++) spawn.mantisBoss(1900, -500) // for (let i = 0; i < 1; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "entanglement"); @@ -77,7 +77,7 @@ const level = { // for (let i = 0; i < 5; i++) tech.giveTech("undefined") // lore.techCount = 1 // level.levelsCleared = 10 - // localSettings.loreCount = 2 //this sets what conversation is heard + // localSettings.loreCount = 1 //this sets what conversation is heard // localSettings.levelsClearedLastGame = 10 // if (localSettings.isAllowed) 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 @@ -333,12 +333,12 @@ const level = { constraintDescription2: "", constraint: [ { - description: "healing disabled", + description: "0.5x healing", effect() { - level.isNoHeal = true + level.isLowHeal = true }, remove() { - level.isNoHeal = false + level.isLowHeal = false } }, { @@ -599,7 +599,7 @@ const level = { isReducedRegen: 1, isHideHealth: false, isNoPause: false, - isNoHeal: false, + isLowHeal: false, levelAnnounce() { const cheating = simulation.isCheating ? "(testing)" : "" if (level.levelsCleared === 0) { diff --git a/js/mob.js b/js/mob.js index 35eb6bd..102434b 100644 --- a/js/mob.js +++ b/js/mob.js @@ -1299,7 +1299,7 @@ const mobs = { powerUps.setPowerUpMode(); //needed after adjusting duplication chance } } else if (tech.isShieldAmmo && this.shield && this.shieldCount === 1) { - let type = tech.isEnergyNoAmmo ? "heal" : "ammo" + let type = "ammo" if (Math.random() < 0.4) { type = "heal" } else if (Math.random() < 0.3 && !tech.isSuperDeterminism) { diff --git a/js/player.js b/js/player.js index 861e610..e204643 100644 --- a/js/player.js +++ b/js/player.js @@ -316,101 +316,127 @@ const m = { m.isHolding = true; }, alive: false, + isSwitchingWorlds: false, switchWorlds() { - powerUps.boost.endCycle = 0 - const totalGuns = b.inventory.length - //track ammo/ ammoPack count - let ammoCount = 0 - for (let i = 0, len = b.inventory.length; i < len; i++) { - if (b.guns[b.inventory[i]].ammo !== Infinity) { - ammoCount += b.guns[b.inventory[i]].ammo / b.guns[b.inventory[i]].ammoPack - } else { - ammoCount += 5 - } - } - - simulation.isTextLogOpen = false; //prevent console spam - //remove all tech and count current tech total - let totalTech = 0; - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isJunk) tech.tech[i].frequency = 0 - if (tech.tech[i].count > 0 && !tech.tech[i].isLore) { - if (tech.tech[i].frequencyDefault) { - tech.tech[i].frequency = tech.tech[i].frequencyDefault + if (!m.isSwitchingWorlds) { + powerUps.boost.endCycle = 0 + const totalGuns = b.inventory.length + //track ammo/ ammoPack count + let ammoCount = 0 + for (let i = 0, len = b.inventory.length; i < len; i++) { + if (b.guns[b.inventory[i]].ammo !== Infinity) { + ammoCount += b.guns[b.inventory[i]].ammo / b.guns[b.inventory[i]].ammoPack } else { - tech.tech[i].frequency = 1 - } - if ( - !tech.tech[i].isNonRefundable && - // !tech.tech[i].isFromAppliedScience && - !tech.tech[i].isAltRealityTech - ) { - totalTech += tech.tech[i].count - tech.tech[i].remove(); - tech.tech[i].isLost = false - tech.tech[i].count = 0 + ammoCount += 5 } } - } - // lore.techCount = 0; - // tech.removeLoreTechFromPool(); - // tech.addLoreTechToPool(); - // tech.removeJunkTechFromPool(); - tech.junkChance = 0; - tech.duplication = 0; - tech.extraMaxHealth = 0; - tech.totalCount = 0; - tech.removeCount = 0; - const randomBotCount = b.totalBots() - b.zeroBotCount() - //remove all bullets, respawn bots - for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); - bullet = []; - //randomize health - m.health = m.health * (1 + 0.5 * (Math.random() - 0.5)) - if (m.health > 1) m.health = 1; - m.displayHealth(); - //randomize field - m.setField(Math.ceil(Math.random() * (m.fieldUpgrades.length - 1))) - //removes guns and ammo - b.inventory = []; - b.activeGun = null; - b.inventoryGun = 0; - for (let i = 0, len = b.guns.length; i < len; ++i) { - b.guns[i].have = false; - if (b.guns[i].ammo !== Infinity) { - b.guns[i].ammo = 0; - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; - } - } - //give random guns - for (let i = 0; i < totalGuns; i++) b.giveGuns() - - //randomize ammo based on ammo/ammoPack count - for (let i = 0, len = b.inventory.length; i < len; i++) { - if (b.guns[b.inventory[i]].ammo !== Infinity) b.guns[b.inventory[i]].ammo = Math.max(0, Math.floor(ammoCount / b.inventory.length * b.guns[b.inventory[i]].ammoPack * (2.5 + 0.3 * (Math.random() - 0.5)))) - } - // console.log(b.activeGun) - //randomize tech - for (let i = 0; i < totalTech; i++) { - //find what tech I could get - let options = []; + simulation.isTextLogOpen = false; //prevent console spam + //remove all tech and count current tech total + let totalTech = 0; for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBadRandomOption && !tech.tech[i].isLore && !tech.tech[i].isJunk) { - for (let j = 0; j < tech.tech[i].frequency; j++) options.push(i); + if (tech.tech[i].isJunk) tech.tech[i].frequency = 0 + if (tech.tech[i].count > 0 && !tech.tech[i].isLore) { + if (tech.tech[i].frequencyDefault) { + tech.tech[i].frequency = tech.tech[i].frequencyDefault + } else { + tech.tech[i].frequency = 1 + } + if ( + !tech.tech[i].isNonRefundable && + // !tech.tech[i].isFromAppliedScience && + !tech.tech[i].isAltRealityTech + ) { + totalTech += tech.tech[i].count + tech.tech[i].remove(); + tech.tech[i].isLost = false + tech.tech[i].count = 0 + } } } - //add a new tech from options pool - if (options.length > 0) tech.giveTech(options[Math.floor(Math.random() * options.length)]) + // lore.techCount = 0; + // tech.removeLoreTechFromPool(); + // tech.addLoreTechToPool(); + // tech.removeJunkTechFromPool(); + tech.junkChance = 0; + tech.duplication = 0; + tech.extraMaxHealth = 0; + tech.totalCount = 0; + tech.removeCount = 0; + const randomBotCount = b.totalBots() + b.zeroBotCount() + //remove all bullets, respawn bots + for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); + bullet = []; + + //randomize health + m.health = m.health * (1 + 0.5 * (Math.random() - 0.5)) + if (m.health > 1) m.health = 1; + m.displayHealth(); + //randomize field + m.setField(Math.ceil(Math.random() * (m.fieldUpgrades.length - 1))) + //removes guns and ammo + b.inventory = []; + b.activeGun = null; + b.inventoryGun = 0; + for (let i = 0, len = b.guns.length; i < len; ++i) { + b.guns[i].have = false; + if (b.guns[i].ammo !== Infinity) { + b.guns[i].ammo = 0; + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; + } + } + //give random guns + for (let i = 0; i < totalGuns; i++) b.giveGuns() + + //randomize ammo based on ammo/ammoPack count + for (let i = 0, len = b.inventory.length; i < len; i++) { + if (b.guns[b.inventory[i]].ammo !== Infinity) b.guns[b.inventory[i]].ammo = Math.max(0, Math.floor(ammoCount / b.inventory.length * b.guns[b.inventory[i]].ammoPack * (2.5 + 0.3 * (Math.random() - 0.5)))) + } + + + //randomize tech + // for (let i = 0; i < totalTech; i++) { + // let options = []; + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBadRandomOption && !tech.tech[i].isLore && !tech.tech[i].isJunk) { + // for (let j = 0; j < tech.tech[i].frequency; j++) options.push(i); + // } + // } + // if (options.length > 0) tech.giveTech(options[Math.floor(Math.random() * options.length)]) //add a new tech from options pool + + // } + let loop = () => { + if (!(m.cycle % 10)) { + if (totalTech > 0 && m.alive) { + totalTech-- + let options = []; + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBadRandomOption && !tech.tech[i].isLore && !tech.tech[i].isJunk) { + for (let j = 0; j < tech.tech[i].frequency; j++) options.push(i); + } + } + if (options.length > 0) tech.giveTech(options[Math.floor(Math.random() * options.length)]) //add a new tech from options pool + requestAnimationFrame(loop); + } else { + m.isSwitchingWorlds = false + } + } else if (m.alive) { + requestAnimationFrame(loop); + } else { + m.isSwitchingWorlds = false + } + } + requestAnimationFrame(loop); + + b.respawnBots(); + for (let i = 0; i < randomBotCount; i++) b.randomBot() + simulation.makeGunHUD(); //update gun HUD + simulation.updateTechHUD(); + simulation.isTextLogOpen = true; + m.drop(); + if (simulation.paused) build.pauseGrid() //update the build when paused } - b.respawnBots(); - for (let i = 0; i < randomBotCount; i++) b.randomBot() - simulation.makeGunHUD(); //update gun HUD - simulation.updateTechHUD(); - simulation.isTextLogOpen = true; - m.drop(); - if (simulation.paused) build.pauseGrid() //update the build when paused }, dmgScale: null, //scales all damage, but not raw .dmg death() { @@ -440,8 +466,8 @@ const m = { ctx.fillRect(0, 0, canvas.width, canvas.height); } spawn.setSpawnList(); //new mob types - simulation.clearNow = true; //triggers a map reset - m.switchWorlds() + // simulation.clearNow = true; //triggers a map reset + // m.switchWorlds() simulation.isTextLogOpen = true; simulation.inGameConsole(`simulation.amplitude = 0.${len - i - 1}`, swapPeriod); simulation.isTextLogOpen = false; @@ -528,8 +554,8 @@ const m = { // } }, addHealth(heal) { - if (!tech.isEnergyHealth && !level.isNoHeal) { - m.health += heal * simulation.healScale; + if (!tech.isEnergyHealth) { + m.health += heal * simulation.healScale * (level.isLowHeal ? 0.5 : 1); if (m.health > m.maxHealth) m.health = m.maxHealth; m.displayHealth(); } @@ -2765,6 +2791,29 @@ const m = { } } else { + if (tech.isGroupThrow) { + const range = 810000 + + for (let i = 0; i < body.length; i++) { + const sub = Vector.sub(m.pos, body[i].position) + const dist2 = Vector.magnitudeSquared(sub) + if (dist2 < range) { + body[i].force.y -= body[i].mass * (simulation.g * 1.01); //remove a bit more then standard gravity + if (dist2 > 40000) { + const f = Vector.mult(Vector.normalise(sub), 0.0008 * body[i].mass) + body[i].force.x += f.x + body[i].force.y += f.y + Matter.Body.setVelocity(body[i], { x: 0.96 * body[i].velocity.x, y: 0.96 * body[i].velocity.y }); + } + } + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, Math.sqrt(range), 0, 2 * Math.PI); + ctx.fillStyle = "rgba(245,245,255,0.15)"; + ctx.fill(); + // ctx.globalCompositeOperation = "difference"; + // ctx.globalCompositeOperation = "source-over"; + } //draw charge const x = m.pos.x + 15 * Math.cos(m.angle); const y = m.pos.y + 15 * Math.sin(m.angle); @@ -2885,6 +2934,21 @@ const m = { }; expand(m.holdingTarget, Math.min(20, m.holdingTarget.mass * 3)) } + if (tech.isGroupThrow) { + const range = 810000 + for (let i = 0; i < body.length; i++) { + if (body[i] !== m.holdingTarget) { + const dist2 = Vector.magnitudeSquared(Vector.sub(m.pos, body[i].position)) + if (dist2 < range) { + const blockSpeed = 90 * charge * Math.min(0.85, 0.8 / Math.pow(body[i].mass, 0.25)) * Math.pow((range - dist2) / range, 0.2) + Matter.Body.setVelocity(body[i], { + x: body[i].velocity.x * 0.5 + Math.cos(m.angle) * blockSpeed, + y: body[i].velocity.y * 0.5 + Math.sin(m.angle) * blockSpeed + }); + } + } + } + } } } } else { @@ -3041,6 +3105,7 @@ const m = { grabPowerUp() { //look for power ups to grab with field if (m.fireCDcycle < m.cycle) m.fireCDcycle = m.cycle - 1 for (let i = 0, len = powerUp.length; i < len; ++i) { + if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue const dxP = m.pos.x - powerUp[i].position.x; const dyP = m.pos.y - powerUp[i].position.y; const dist2 = dxP * dxP + dyP * dyP + 10; @@ -3868,15 +3933,9 @@ const m = { } //add extra friction for horizontal motion if (input.down || input.up || input.left || input.right) { - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.99, - y: player.velocity.y * 0.98 - }); + Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 }); } else { //slow rise and fall - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.99, - y: player.velocity.y * 0.98 - }); + Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 }); } // if (tech.isFreezeMobs) { // const ICE_DRAIN = 0.0005 @@ -4967,6 +5026,8 @@ const m = { //grab power ups into the field for (let i = 0, len = powerUp.length; i < len; ++i) { + if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue + const dxP = m.fieldPosition.x - powerUp[i].position.x; const dyP = m.fieldPosition.y - powerUp[i].position.y; const dist2 = dxP * dxP + dyP * dyP + 200; @@ -4978,14 +5039,11 @@ const m = { powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity //extra friction - Matter.Body.setVelocity(powerUp[i], { - x: powerUp[i].velocity.x * 0.11, - y: powerUp[i].velocity.y * 0.11 - }); + Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.11, y: powerUp[i].velocity.y * 0.11 }); if ( dist2 < 5000 && !simulation.isChoosing && - (powerUp[i].name !== "heal" || m.maxHealth - m.health > 0.01 || tech.isOverHeal) + (tech.isOverHeal || powerUp[i].name !== "heal" || m.maxHealth - m.health > 0.01) // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth) // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity) ) { //use power up if it is close enough @@ -5104,13 +5162,13 @@ const m = { { name: "wormhole", //wormholes attract blocks and power ups
- description: "use energy to tunnel through a wormhole
+7% chance to duplicate spawned power ups
7 energy per second", //
bullets may also traverse wormholes + description: "use energy to tunnel through a wormhole
+8% chance to duplicate spawned power ups
7 energy per second", //
bullets may also traverse wormholes drain: 0, effect: function () { m.fieldMeterColor = "#bbf" //"#0c5" m.eyeFillColor = m.fieldMeterColor - m.duplicateChance = 0.07 + m.duplicateChance = 0.08 m.fieldRange = 0 powerUps.setPowerUpMode(); //needed after adjusting duplication chance @@ -5146,6 +5204,7 @@ const m = { //suck power ups for (let i = 0, len = powerUp.length; i < len; ++i) { + if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue //which hole is closer const dxP1 = m.hole.pos1.x - powerUp[i].position.x; const dyP1 = m.hole.pos1.y - powerUp[i].position.y; @@ -5165,26 +5224,6 @@ const m = { powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.05, y: powerUp[i].velocity.y * 0.05 }); if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough - - // if (true) { //AoE radiation effect - // const range = 800 - - // for (let i = 0, len = mob.length; i < len; ++i) { - // if (mob[i].alive && !mob[i].isShielded) { - // dist = Vector.magnitude(Vector.sub(powerUp[i].position, mob[i].position)) - mob[i].radius; - // if (dist < range) mobs.statusDoT(mob[i], 0.5) //apply radiation damage status effect on direct hits - // } - // } - - // simulation.drawList.push({ - // x: powerUp[i].position.x, - // y: powerUp[i].position.y, - // radius: range, - // color: "rgba(0,150,200,0.3)", - // time: 4 - // }); - // } - m.fieldRange *= 0.8 powerUps.onPickUp(powerUp[i]); powerUp[i].effect(); @@ -6053,7 +6092,7 @@ const m = { if (mob[k].isShielded) dmg *= 0.7 mob[k].damage(dmg, true); if (tech.isBlockPowerUps && !mob[k].alive && mob[k].isDropPowerUp && Math.random() < 0.5) { - let type = tech.isEnergyNoAmmo ? "heal" : "ammo" + let type = "ammo" if (Math.random() < 0.4) { type = "heal" } else if (Math.random() < 0.4 && !tech.isSuperDeterminism) { diff --git a/js/powerup.js b/js/powerup.js index 64aa91c..6fedd09 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -293,7 +293,7 @@ const powerUps = { if (tech.isCancelRerolls) { for (let i = 0, len = 8 + 4 * Math.random(); i < len; i++) { let spawnType - if (Math.random() < 0.4 && !tech.isEnergyNoAmmo) { + if (Math.random() < 0.4) { spawnType = "ammo" } else if (Math.random() < 0.33 && !tech.isSuperDeterminism) { spawnType = "research" @@ -461,24 +461,24 @@ const powerUps = { document.getElementById("choose-grid").classList.add('choose-grid-no-images'); document.getElementById("choose-grid").classList.remove('choose-grid'); document.getElementById("choose-grid").style.gridTemplateColumns = "200px"//adjust this to increase the width of the whole menu, but mostly the center column - let levelChoices = `
WARP
` - levelChoices += `
level.uniqueLevels
` + let text = `
WARP
` + text += `
cancel
` + text += `
level.uniqueLevels
` for (let i = 0; i < level.uniqueLevels.length; i++) { - levelChoices += `
${level.uniqueLevels[i]}
` //id="uniqueLevels-warp-${i}" + text += `
${level.uniqueLevels[i]}
` //id="uniqueLevels-warp-${i}" } - levelChoices += `
level.playableLevels
` + text += `
level.playableLevels
` for (let i = 0; i < level.playableLevels.length; i++) { - levelChoices += `
${level.playableLevels[i]}
` + text += `
${level.playableLevels[i]}
` } - levelChoices += `
level.communityLevels
` + text += `
level.communityLevels
` for (let i = 0; i < level.communityLevels.length; i++) { - levelChoices += `
${level.communityLevels[i]}
` + text += `
${level.communityLevels[i]}
` } - levelChoices += `
level.trainingLevels
` + text += `
level.trainingLevels
` for (let i = 0; i < level.trainingLevels.length; i++) { - levelChoices += `
${level.trainingLevels[i]}
` + text += `
${level.trainingLevels[i]}
` } - let text = `${levelChoices}
exit
` document.getElementById("choose-grid").innerHTML = text //show level info document.getElementById("choose-grid").style.opacity = "1" @@ -1777,10 +1777,7 @@ const powerUps = { } }, spawn(x, y, name, moving = true, size = powerUps[name].size()) { - if ( - (!tech.isSuperDeterminism || (name !== 'research')) && - !(tech.isEnergyNoAmmo && name === 'ammo') - ) { + if ((!tech.isSuperDeterminism || (name !== 'research'))) { if (tech.isBoostReplaceAmmo && name === 'ammo') { name = 'boost' size = powerUps[name].size() diff --git a/js/simulation.js b/js/simulation.js index 2a38814..3ba0920 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -3,73 +3,151 @@ const simulation = { loop() { }, //main game loop, gets set to normal or testing loop normalLoop() { - simulation.gravity(); - Engine.update(engine, simulation.delta); - simulation.wipe(); - simulation.textLog(); - if (m.onGround) { - m.groundControl() - } else { - m.airControl() + try { + simulation.gravity(); + Engine.update(engine, simulation.delta); + simulation.wipe(); + simulation.textLog(); + if (m.onGround) { + m.groundControl() + } else { + m.airControl() + } + m.move(); + m.look(); + simulation.camera(); + level.custom(); + powerUps.do(); + mobs.draw(); + simulation.draw.cons(); + simulation.draw.body(); + if (!m.isBodiesAsleep) mobs.loop(); + mobs.healthBar(); + m.draw(); + m.hold(); + level.customTopLayer(); + simulation.draw.drawMapPath(); + b.fire(); + b.bulletRemove(); + b.bulletDraw(); + if (!m.isBodiesAsleep) b.bulletDo(); + simulation.drawCircle(); + simulation.runEphemera(); + ctx.restore(); + } catch (error) { + simulation.inGameConsole(`ERROR: ${(error.stack && error.stack.replace(/\n/g, "
")) || (error.message + ` ${error.filename}:${error.lineno}`)}`); + } finally { + simulation.drawCursor(); } - m.move(); - m.look(); - simulation.camera(); - level.custom(); - powerUps.do(); - mobs.draw(); - simulation.draw.cons(); - simulation.draw.body(); - if (!m.isBodiesAsleep) mobs.loop(); - mobs.healthBar(); - m.draw(); - m.hold(); - level.customTopLayer(); - simulation.draw.drawMapPath(); - b.fire(); - b.bulletRemove(); - b.bulletDraw(); - if (!m.isBodiesAsleep) b.bulletDo(); - simulation.drawCircle(); - simulation.runEphemera(); - ctx.restore(); - simulation.drawCursor(); }, testingLoop() { - simulation.gravity(); - Engine.update(engine, simulation.delta); - simulation.wipe(); - simulation.textLog(); - if (m.onGround) { - m.groundControl() - } else { - m.airControl() - } - m.move(); - m.look(); - simulation.camera(); - level.custom(); - m.draw(); - m.hold(); - level.customTopLayer(); - simulation.draw.wireFrame(); - if (input.fire && m.fireCDcycle < m.cycle) { - m.fireCDcycle = m.cycle + 15; //fire cooldown - for (let i = 0, len = mob.length; i < len; i++) { - if (Vector.magnitudeSquared(Vector.sub(mob[i].position, simulation.mouseInGame)) < mob[i].radius * mob[i].radius) { - console.log(mob[i]) + try { + simulation.gravity(); + Engine.update(engine, simulation.delta); + simulation.wipe(); + simulation.textLog(); + if (m.onGround) { + m.groundControl() + } else { + m.airControl() + } + m.move(); + m.look(); + simulation.camera(); + level.custom(); + m.draw(); + m.hold(); + level.customTopLayer(); + simulation.draw.wireFrame(); + if (input.fire && m.fireCDcycle < m.cycle) { + m.fireCDcycle = m.cycle + 15; //fire cooldown + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitudeSquared(Vector.sub(mob[i].position, simulation.mouseInGame)) < mob[i].radius * mob[i].radius) { + console.log(mob[i]) + } } } + simulation.draw.cons(); + simulation.draw.testing(); + simulation.drawCircle(); + simulation.runEphemera(); + simulation.constructCycle() + } catch (error) { + simulation.inGameConsole(`ERROR: ${(error.stack && error.stack.replace(/\n/g, "
")) || (error.message + ` ${error.filename}:${error.lineno}`)}`); + } finally { + ctx.restore(); + simulation.testingOutput(); + simulation.drawCursor(); } - simulation.draw.cons(); - simulation.draw.testing(); - simulation.drawCircle(); - simulation.runEphemera(); - simulation.constructCycle() - ctx.restore(); - simulation.testingOutput(); - simulation.drawCursor(); }, + // normalLoop() { + // simulation.gravity(); + // Engine.update(engine, simulation.delta); + // simulation.wipe(); + // simulation.textLog(); + // if (m.onGround) { + // m.groundControl() + // } else { + // m.airControl() + // } + // m.move(); + // m.look(); + // simulation.camera(); + // level.custom(); + // powerUps.do(); + // mobs.draw(); + // simulation.draw.cons(); + // simulation.draw.body(); + // if (!m.isBodiesAsleep) mobs.loop(); + // mobs.healthBar(); + // m.draw(); + // m.hold(); + // level.customTopLayer(); + // simulation.draw.drawMapPath(); + // b.fire(); + // b.bulletRemove(); + // b.bulletDraw(); + // if (!m.isBodiesAsleep) b.bulletDo(); + // simulation.drawCircle(); + // simulation.runEphemera(); + // ctx.restore(); + // simulation.drawCursor(); + // }, + // testingLoop() { + // simulation.gravity(); + // Engine.update(engine, simulation.delta); + // simulation.wipe(); + // simulation.textLog(); + // if (m.onGround) { + // m.groundControl() + // } else { + // m.airControl() + // } + // m.move(); + // m.look(); + // simulation.camera(); + // level.custom(); + // m.draw(); + // m.hold(); + // level.customTopLayer(); + // simulation.draw.wireFrame(); + // if (input.fire && m.fireCDcycle < m.cycle) { + // m.fireCDcycle = m.cycle + 15; //fire cooldown + // for (let i = 0, len = mob.length; i < len; i++) { + // if (Vector.magnitudeSquared(Vector.sub(mob[i].position, simulation.mouseInGame)) < mob[i].radius * mob[i].radius) { + // console.log(mob[i]) + // } + // } + // } + // simulation.draw.cons(); + // simulation.draw.testing(); + // simulation.drawCircle(); + // simulation.runEphemera(); + // simulation.constructCycle() + // ctx.restore(); + // simulation.testingOutput(); + // simulation.drawCursor(); + // }, isTimeSkipping: false, timeSkip(cycles = 60) { simulation.isTimeSkipping = true; @@ -864,7 +942,7 @@ const simulation = { } else { Composite.add(engine.world, [player]) } - // shuffle(level.constraint) + shuffle(level.constraint) level.populateLevels() input.endKeySensing(); simulation.ephemera = [] @@ -889,6 +967,7 @@ const simulation = { tech.plasmaBotCount = 0; tech.missileBotCount = 0; + m.isSwitchingWorlds = false simulation.isChoosing = false; b.setFireMethod() b.setFireCD(); @@ -935,7 +1014,7 @@ const simulation = { m.onGround = false m.lastOnGroundCycle = 0 m.health = 0; - level.isNoHeal = false + level.isLowHeal = false m.addHealth(0.25) m.drop(); m.holdingTarget = null diff --git a/js/tech.js b/js/tech.js index f98eae7..0073bf4 100644 --- a/js/tech.js +++ b/js/tech.js @@ -917,7 +917,7 @@ const tech = { //give the tech that was found for this gun if (gunTechPool.length) { const index = Math.floor(Math.random() * gunTechPool.length) - simulation.inGameConsole(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360) + simulation.inGameConsole(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360) tech.giveTech(gunTechPool[index]) // choose from the gun pool simulation.boldActiveGunHUD(); } @@ -1103,7 +1103,7 @@ const tech = { }, { name: "non-renewables", - description: `2x damage
${powerUps.orb.ammo()} can't spawn`, + description: `2x damage
you can't pickup ${powerUps.orb.ammo()}`, maxCount: 1, count: 0, frequency: 1, @@ -1116,15 +1116,17 @@ const tech = { effect() { tech.damage *= this.damage tech.isEnergyNoAmmo = true; + powerUps.ammo.color = "#c1c6c9"//"#abb3b8"// "#535e63" }, remove() { if (this.count && m.alive) tech.damage /= this.damage tech.isEnergyNoAmmo = false; + powerUps.ammo.color = "#467" } }, { name: "desublimated ammunition", - description: `if crouching
alternating shots use no ammo`, + description: `if crouching
alternating shots cost no ammo`, maxCount: 1, count: 0, frequency: 1, @@ -2419,6 +2421,24 @@ const tech = { tech.blockDamage = 0.075 } }, + { + name: "Halbach array", + description: "throwing a block will
also throw other nearby blocks", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldMode !== 8 && m.fieldMode !== 9 && !tech.isTokamak + }, + requires: "mass driver, printer, not wormhole, pilot wave, tokamak", + effect() { + tech.isGroupThrow = true + }, + remove() { + tech.isGroupThrow = false + } + }, { name: "inflation", link: `inflation`, @@ -2430,7 +2450,7 @@ const tech = { allowed() { return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldMode !== 8 && m.fieldMode !== 9 && !tech.isTokamak }, - requires: "mass driver, not pilot wave, tokamak, wormhole", + requires: "mass driver, printer, not pilot wave, tokamak, wormhole", effect() { tech.isAddBlockMass = true }, @@ -2448,7 +2468,7 @@ const tech = { allowed() { return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak }, - requires: "mass driver, not pilot wave, tokamak, wormhole", + requires: "mass driver, printer, not pilot wave, tokamak, wormhole", effect() { tech.isBlockRestitution = true }, @@ -2466,7 +2486,7 @@ const tech = { allowed() { return (tech.blockDamage > 0.075 || tech.isPrinter) && !tech.nailsDeathMob && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.iceIXOnDeath }, - requires: "mass driver, no other mob death tech", + requires: "mass driver, printer, no other mob death tech", effect() { tech.isMobBlockFling = true }, @@ -2486,7 +2506,7 @@ const tech = { allowed() { return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && !tech.isTokamak }, - requires: "mass driver, not pilot wave, tokamak", + requires: "mass driver, printer, not pilot wave, tokamak", effect() { tech.isBlockPowerUps = true }, @@ -2688,7 +2708,7 @@ const tech = { }, requires: "", effect() { - m.collisionImmuneCycles += 480; + m.collisionImmuneCycles += 420; if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage }, remove() { @@ -5994,9 +6014,9 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("grenades") && !tech.isVacuumBomb && !tech.isSmartRadius + return tech.haveGunCheck("grenades") && !tech.isVacuumBomb && !tech.isSmartRadius && !tech.isEnergyHealth }, - requires: "grenades, not vacuum bomb, shaped charges", + requires: "grenades, not vacuum bomb, shaped charges, mass-energy", effect() { tech.isImmuneExplosion = true; tech.isRPG = true; @@ -7281,7 +7301,7 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return ((tech.haveGunCheck("wave") && tech.isInfiniteWaveAmmo) || tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun)) && !tech.isEnergyNoAmmo + return ((tech.haveGunCheck("wave") && tech.isInfiniteWaveAmmo) || tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun)) }, requires: "harpoon, laser, wave, frequency, not railgun, non-renewables", effect() { diff --git a/todo.txt b/todo.txt index 48c2cbf..f521596 100644 --- a/todo.txt +++ b/todo.txt @@ -1,17 +1,26 @@ ******************************************************** NEXT PATCH ************************************************** -images are back as an option due to public outcry +tech: Halbach array - throwing a block will also throw other nearby blocks + +tech non-renewables now spawns ammo, but ammo can't be picked up +grenade tech that cause multiple explosions have less knock back for mobs +constraint: 0->0.5x healing +wormhole 7->8% duplication +many worlds takes a few frames between each tech given bug fixes - getting a power up quickly after warp, difficulty or instructions power ups was making the screen go blank - heuristics resets on death properly now - + harpoon ammo gain on autonomous defense fixed + constraints are properly randomized again ******************************************************** BUGS ******************************************************** - - can't consistenly reproduce, but it happened several times this way - on initial level I press T and took the warp and exited it, then I too a field and the screen went blank + couple reports of crashes from many-worlds + around level 5-7 + for me crash on factory level 7 + might be linked to having all the guns? + the crash was in matter.js something about collisions and undefined + maybe too many things were spawned in the same spot? + also occurs when you just gain many random tech in testing mode figure out why seeded random isn't making runs the same: shuffle is being used for a wide variety of things that don't need a seeded random @@ -41,30 +50,14 @@ player can become crouched while not touching the ground if they exit the ground *********************************************************** TODO ***************************************************** +make a text orb for JUNK text to make JUNK more clear + extended vertical flip to edge cases: !!stored circular graphics simulation.drawList.push -add more tips: - download latest version of n-gon - https://codeload.github.com/landgreen/n-gon/zip/refs/heads/master - - new level based laser element !!update new version into other levels -level technique: pairs of touch activated elevators jump on one to get high enough to jump on the next one - -new vertical flip level - long horizontal - several buttons - shorten flip time? - - - -constraints should show future constraints in pause menu - pre calculate all constraints for up to 13 levels? - loop constraints after that - procedural animation https://www.youtube.com/watch?v=qlfh_rv6khY @@ -93,7 +86,7 @@ tech: - after killing a Boss new level - rework testChamber -Boss (or mob) that quickly moves towards player, but they moves perpendicularly to player, like dodging +Boss (or mob) that quickly moves towards player, but then moves perpendicularly to player, like dodging could respond to when player presses fire key or to when it takes damage new snakeBoss type that eats mobs @@ -139,12 +132,6 @@ increase mass and movement speed at the same time possible player.mass bad interactions grapple -bullets should trigger shrinking platforms level element? - -level element - player activated elevators - could be fast and throw player - could just rise up slow (slow might have a bad jerky animation) - rework energy and health HUD make both diegetic? how? not sure there is a good way to do this... @@ -158,12 +145,6 @@ tech - after a power up is duplicated gain 1.01x damage permanently cool name: -field tech: negative mass - quickly pull/teleport in all nearby blocks and then fire them away from player - how does player triggers effect? - picking up a block pulls in all nearby blocks, throwing block fires all nearby blocks - taking damage - auto aim 50% of blocks at mobs - after picking up heals gain ____ 0.1x damage taken for 12s after picking up ammo gain ____