From 64c81cd8022c2193159b99311dde80f4fa6825df Mon Sep 17 00:00:00 2001 From: landgreen Date: Mon, 11 Mar 2024 20:19:37 -0700 Subject: [PATCH] JUNK % and pause sort new tech are sorted to the top of the tech list pause menu tech list matches the tech list in game JUNK tech icons are properly displayed in pause Sort interface works with enter key bug fix: you can no longer pause or enter testing in experiment selection on text input pressing P in power up selection menu brings up the pause menu so you can see your tech and stats blocks stuck in vertical portals have a bit of randomness added to their velocity to help them escape JUNK tech chance is a raw percent chance to display a random JUNK it was previously a specific JUNK tech directly added to the pool this might cause bugs JUNK tech: beforeunload - asks if you want to cancel if you exit game, if you cancel gain 25% damage, but there is a 25% chance to exit anyways JUNK tech: what the block? - trying to throw a block, throws you instead reinforcement learning converted into a JUNK tech JUNK DNA - scale damage by 100->200% of JUNK pool tech percent dark patterns 33->22% JUNK replication 33->22% JUNK overcharge 66->88 max energy residual dipolar coupling 6->8 coupling per cancel futures exchange 4.7->5% duplication per cancel hyperpolarization reduces polarization time by 1->1.25 seconds reel +75->100 energy updated physics engine to matter.js 0.19 (from 0.18) --- js/bullet.js | 2 +- js/index.js | 113 ++++++++++++----------- js/level.js | 31 ++++--- js/player.js | 116 ++++++++++++++++++++++- js/powerup.js | 48 +++++----- js/simulation.js | 9 ++ js/tech.js | 234 ++++++++++++++++++++++++++--------------------- todo.txt | 49 +++++++--- 8 files changed, 389 insertions(+), 213 deletions(-) diff --git a/js/bullet.js b/js/bullet.js index 72ba461..7f50098 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -1519,7 +1519,7 @@ const b = { if (this.pickUpTarget) { if (tech.isReel && this.blockDist > 150) { // console.log(0.0003 * Math.min(this.blockDist, 1000)) - m.energy += 0.0009 * Math.min(this.blockDist, 800) //max 0.352 energy + m.energy += 0.00113 * Math.min(this.blockDist, 800) //max 0.352 energy simulation.drawList.push({ //add dmg to draw queue x: m.pos.x, y: m.pos.y, diff --git a/js/index.js b/js/index.js index 3c216a6..633155b 100644 --- a/js/index.js +++ b/js/index.js @@ -129,6 +129,19 @@ function vertexCollision(v1, v1End, domains) { //= [map, body, [playerBody, pla return best } +// prompts to reload and exit for JUNK tech named "beforeunload" +function beforeUnloadEventListener(event) { + event.preventDefault(); + if (tech.isExitPrompt) { + tech.damage *= 1.25 + simulation.makeTextLog(`damage *= ${1.25}`) + if (Math.random() < 0.25) { + removeEventListener('beforeunload', beforeUnloadEventListener); + } + } +} +// addEventListener('beforeunload', beforeUnloadEventListener); + //collision groups // cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.mobShield | cat.phased @@ -425,6 +438,7 @@ const build = { pauseGrid() { build.generatePauseLeft() //makes the left side of the pause menu with the tech build.generatePauseRight() //makes the right side of the pause menu with the tech + // build.sortTech('') //sorts tech into the order the player got them using tech.tech[i].cycle = m.cycle document.getElementById("tech").style.display = "none" document.getElementById("guns").style.display = "none" document.getElementById("field").style.display = "none" @@ -436,15 +450,6 @@ const build = { simulation.lastLogTime = m.cycle //hide in game console }, generatePauseLeft() { - //used for junk estimation - let junkCount = 0 - let totalCount = 1 //start at one to avoid NaN issues - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBanished) { - totalCount += tech.tech[i].frequency - if (tech.tech[i].isJunk) junkCount += tech.tech[i].frequency - } - } //left side let botText = "" if (tech.nailBotCount) botText += `
nail-bots: ${tech.nailBotCount}` @@ -487,7 +492,7 @@ ${botText} mouse: (${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})
tech: ${tech.totalCount}   research: ${powerUps.research.count} velocity: (${player.velocity.x.toFixed(3)}, ${player.velocity.y.toFixed(3)}) -${junkCount ? `
JUNK: ${(junkCount / totalCount * 100).toFixed(1)}% ` : ""} +${tech.junkChance ? `
JUNK: ${(100 * tech.junkChance).toFixed(1)}% ` : ""}

level: ${level.levelsCleared} ${level.levels[level.onLevel]} (${level.difficultyText()})
mobs: ${spawn.pickList[0]}, ${spawn.pickList[0]} @@ -542,14 +547,6 @@ ${simulation.isCheating ? "

lore disabled" : ""} el.innerHTML = text }, generatePauseRight() { - //right side - // - // - - // - // - - // let text = `
@@ -560,38 +557,13 @@ ${simulation.isCheating ? "

lore disabled" : ""}
`; - // const style = (tech.isPauseEjectTech && !simulation.isChoosing) ? 'style="animation: techColorCycle 1s linear infinite alternate;"' : '' const ejectClass = (tech.isPauseEjectTech && !simulation.isChoosing) ? 'pause-eject' : '' for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].count > 0) { - // const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; - // if (tech.tech[i].isNonRefundable) { - // text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` - // } else if (tech.tech[i].isFieldTech) { - // text += `
- // - //
- //
- //
- //         ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` - // } else if (tech.tech[i].isGunTech) { - // text += `
- // - //
- //
- //
- //         ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` - // } else if (tech.tech[i].isLore) { - // text += `
  ${tech.tech[i].name} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` - // } else { - // text += `
  ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` - // } const style = (localSettings.isHideImages || tech.tech[i].isJunk || tech.tech[i].isLore) ? `style="height:auto;"` : `style = "background-image: url('img/${tech.tech[i].name}.webp');"` const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; if (tech.tech[i].isNonRefundable) { text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` - // } else if (tech.tech[i].isLore) { - // text += `
  ${tech.tech[i].name} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` } else if (tech.tech[i].isFieldTech) { text += `
` text += build.fieldTechText(i) + "
" @@ -601,6 +573,9 @@ ${simulation.isCheating ? "

lore disabled" : ""} } else if (tech.tech[i].isSkin) { text += `
` text += build.skinTechText(i) + "
" + } else if (tech.tech[i].isJunk) { + text += `
` + text += build.junkTechText(i) + "
" } else { text += `
` text += build.techText(i) + "
" @@ -612,6 +587,17 @@ ${simulation.isCheating ? "

lore disabled" : ""} const el = document.getElementById("pause-grid-right") el.style.display = "grid" el.innerHTML = text + + //add event listener for pressing enter key when in sort + function pressEnterSort(event) { + if (event.key === 'Enter') { + requestAnimationFrame(() => { document.getElementById("sort-input").focus(); }); + // event.preventDefault(); // Prevent the default action to avoid form submission or any other default action + build.sortTech('input') + } + } + document.getElementById("sort-input").addEventListener('keydown', pressEnterSort); + requestAnimationFrame(() => { document.getElementById("sort-input").focus(); }); }, sortTech(find, isExperiment = false) { const sortKeyword = (a, b) => { @@ -621,6 +607,16 @@ ${simulation.isCheating ? "

lore disabled" : ""} if (!aHasKeyword && bHasKeyword) return 1; return 0; } + + // if (find === '') { + // tech.tech.sort((a, b) => { //sorts tech into the order the player got them using tech.tech[i].cycle = m.cycle + // console.log(a.cycle, b.cycle) + // if (a.cycle === undefined && b.cycle !== undefined) return -1; + // if (a.cycle !== undefined && b.cycle === undefined) return 1; + // if (a.cycle === undefined && b.cycle === undefined) return 0; + // if (a.cycle !== b.cycle) return a.cycle - b.cycle; + // }); + // } else if (find === 'guntech') { tech.tech.sort((a, b) => { if (a.isGunTech && b.isGunTech) { @@ -704,6 +700,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} build.generatePauseRight() //makes the right side of the pause menu with the tech } document.getElementById("sort-input").value = find; //make the sorted string display in the keyword search input field + simulation.updateTechHUD(); }, unPauseGrid() { document.getElementById("guns").style.display = "inline" @@ -961,10 +958,17 @@ ${simulation.isCheating ? "

lore disabled" : ""} } } document.getElementById("experiment-grid").innerHTML = text - // for (let i = 0, len = tech.tech.length; i < len; i++) { - // if (tech.tech[i].count) - // document.getElementById("tech-" + i).classList.add("build-tech-selected") - // } + + + + //add event listener for pressing enter key when in sort + function pressEnterSort(event) { + if (event.key === 'Enter') { + // event.preventDefault(); // Prevent the default action to avoid form submission or any other default action + build.sortTech('input', true) + } + } + document.getElementById("sort-input").addEventListener('keydown', pressEnterSort); document.getElementById("difficulty-select-experiment").value = document.getElementById("difficulty-select").value document.getElementById("difficulty-select-experiment").addEventListener("input", () => { @@ -1348,12 +1352,15 @@ window.addEventListener("keydown", function (event) { simulation.previousGun(); break case input.key.pause: - if (!simulation.isChoosing && input.isPauseKeyReady && m.alive) { + + if (input.isPauseKeyReady && m.alive && !build.isExperimentSelection) { input.isPauseKeyReady = false - setTimeout(function () { - input.isPauseKeyReady = true - }, 300); - if (simulation.paused) { + setTimeout(function () { input.isPauseKeyReady = true }, 300); + if (simulation.isChoosing) { + + build.pauseGrid() + + } else if (simulation.paused) { build.unPauseGrid() simulation.paused = false; // level.levelAnnounce(); @@ -1404,7 +1411,7 @@ window.addEventListener("keydown", function (event) { } break case input.key.testing: - if (m.alive && localSettings.loreCount > 0 && !simulation.paused) { + if (m.alive && localSettings.loreCount > 0 && !simulation.paused && !build.isExperimentSelection) { if (simulation.difficultyMode > 4) { simulation.makeTextLog("testing mode disabled for this difficulty"); break diff --git a/js/level.js b/js/level.js index cfb4fba..884f39a 100644 --- a/js/level.js +++ b/js/level.js @@ -33,26 +33,25 @@ const level = { // m.energy = 0 // simulation.molecularMode = 2 // m.damage(0.1); - // b.giveGuns("drones") //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("mine") //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 // b.guns[8].ammo = 100000000 - // requestAnimationFrame(() => { tech.giveTech("Higgs mechanism") }); - // for (let i = 0; i < 1; ++i) tech.giveTech("flame test") - // for (let i = 0; i < 1; ++i) tech.giveTech("dazzler") - // for (let i = 0; i < 1; ++i) tech.giveTech("mass production") - // requestAnimationFrame(() => { for (let i = 0; i < 10; i++) tech.giveTech("orbital-bot") }); + // requestAnimationFrame(() => { tech.giveTech("eternalism") }); + // for (let i = 0; i < 1; ++i) tech.giveTech("beforeunload") + // for (let i = 0; i < 1; ++i) tech.giveTech("Sleipnir") + // for (let i = 0; i < 1; ++i) tech.giveTech("dark patterns") + // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("paradigm shift") }); // requestAnimationFrame(() => { for (let i = 0; i < 10; i++) b.orbitBot(m.pos, false) }); // m.skin.hexagon(); - // for (let i = 0; i < 1; i++) tech.giveTech("tungsten carbide") // m.lastKillCycle = m.cycle - // for (let i = 0; i < 1; ++i) tech.giveTech("swarf") + // for (let i = 0; i < 1; ++i) tech.giveTech("what the block?") // for (let i = 0; i < 1; ++i) tech.giveTech("unified field theory") // for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research"); // for (let i = 0; i < 100; i++) powerUps.directSpawn(1750, -500, "coupling"); // spawn.mapRect(575, -700, 25, 425); //block mob line of site on testing - // level.testing(); + // level.testChamber(); // for (let i = 0; i < 1; ++i) spawn.laserLayer(1400, -500) // Matter.Body.setPosition(player, { x: -200, y: -3330 }); @@ -75,7 +74,7 @@ const level = { // simulation.isAutoZoom = false; //look in close // simulation.zoomScale *= 0.5; // simulation.setZoom(); - // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech"); + // for (let i = 0; i < 10; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech"); // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 450, m.pos.y + 50 * Math.random(), "boost"); // for (let i = 0; i < 20; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "heal"); // for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "field", false); @@ -1570,7 +1569,6 @@ const level = { } } } - // if (body.length) { for (let i = 0, len = body.length; i < len; i++) { if (body[i] !== m.holdingTarget) { // body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100 @@ -1591,17 +1589,20 @@ const level = { } //rotate velocity let mag - if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up + if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires up mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11 + let v = Vector.mult(this.portalPair.unit, mag) + //rotate the velocity vector of blocks fired directly up to keep them from getting stuck endlessly in vertical portals + Matter.Body.setVelocity(body[i], Vector.rotate(v, 0.5 * (Math.random() - 0.5))); } else { mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity))) + let v = Vector.mult(this.portalPair.unit, mag) + Matter.Body.setVelocity(body[i], v); } - let v = Vector.mult(this.portalPair.unit, mag) - Matter.Body.setVelocity(body[i], v); + } } } - // } //remove block if touching // if (body.length) { diff --git a/js/player.js b/js/player.js index afdab53..e65d5af 100644 --- a/js/player.js +++ b/js/player.js @@ -362,10 +362,10 @@ const m = { // tech.removeLoreTechFromPool(); // tech.addLoreTechToPool(); // tech.removeJunkTechFromPool(); + tech.junkChance = 0; tech.duplication = 0; tech.extraMaxHealth = 0; tech.totalCount = 0; - tech.countJunkTech(); const randomBotCount = b.totalBots() b.zeroBotCount() //remove all bullets, respawn bots @@ -547,7 +547,6 @@ const m = { if (m.health > m.maxHealth) m.health = m.maxHealth; m.displayHealth(); }, - defaultFPSCycle: 0, //tracks when to return to normal fps immuneCycle: 0, //used in engine lastCalculatedDamage: 0, //used to decided if damage bar needs to be redrawn (in simulation.checks) @@ -2329,7 +2328,8 @@ const m = { m.isHolding = false } }, - throwBlock() { + throwBlock() { }, + throwBlockDefault() { if (m.holdingTarget) { if (input.field) { if (m.energy > 0.001) { @@ -2478,6 +2478,116 @@ const m = { m.isHolding = false } }, + throwSelf() { + if (m.holdingTarget) { + if (input.field) { + if (m.energy > 0.001) { + if (m.fireCDcycle < m.cycle) m.fireCDcycle = m.cycle + if (tech.isCapacitor && m.throwCharge < 4) m.throwCharge = 4 + m.throwCharge += 0.5 / m.holdingTarget.mass / b.fireCDscale + if (m.throwCharge < 6) m.energy -= 0.001 / b.fireCDscale; // m.throwCharge caps at 5 + + //trajectory path prediction + //draw charge + const x = m.pos.x + 15 * Math.cos(m.angle); + const y = m.pos.y + 15 * Math.sin(m.angle); + const len = m.holdingTarget.vertices.length - 1; + const edge = m.throwCharge * m.throwCharge * m.throwCharge; + const grd = ctx.createRadialGradient(x, y, edge, x, y, edge + 5); + grd.addColorStop(0, "rgba(255,50,150,0.3)"); + grd.addColorStop(1, "transparent"); + ctx.fillStyle = grd; + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(m.holdingTarget.vertices[len].x, m.holdingTarget.vertices[len].y); + ctx.lineTo(m.holdingTarget.vertices[0].x, m.holdingTarget.vertices[0].y); + ctx.fill(); + for (let i = 0; i < len; i++) { + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(m.holdingTarget.vertices[i].x, m.holdingTarget.vertices[i].y); + ctx.lineTo(m.holdingTarget.vertices[i + 1].x, m.holdingTarget.vertices[i + 1].y); + ctx.fill(); + } + //trajectory prediction + const cycles = 30 + const charge = Math.min(m.throwCharge / 5, 1) + const speed = (tech.isPrinter ? 15 + 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.1)) : 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.25))) + const v = { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) } + ctx.beginPath() + for (let i = 1, len = 10; i < len + 1; i++) { + const time = cycles * i / len + ctx.lineTo(m.pos.x + time * v.x, m.pos.y + time * v.y + 0.34 * time * time) + } + ctx.strokeStyle = "rgba(68, 68, 68, 0.15)" //color.map + ctx.lineWidth = 2 + ctx.stroke() + + } else { + m.drop() + } + } else if (m.throwCharge > 0) { //Matter.Query.region(mob, player.bounds) + if (m.holdingTarget.isPrinted) m.holdingTarget.isPrinted = undefined + //throw the body + m.fieldCDcycle = m.cycle + 20; + m.fireCDcycle = m.cycle + 20; + + m.isHolding = false; + //bullet-like collisions + m.holdingTarget.collisionFilter.category = cat.bullet + m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; + if (tech.isBlockRestitution) { + m.holdingTarget.restitution = 0.999 //extra bouncy + m.holdingTarget.friction = m.holdingTarget.frictionStatic = m.holdingTarget.frictionAir = 0.001 + } + //check every second to see if player is away from thrown body, and make solid + const solid = function (that) { + const dx = that.position.x - player.position.x; + const dy = that.position.y - player.position.y; + // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { + if (dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { + that.collisionFilter.category = cat.body; //make solid + that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now + } else { + setTimeout(solid, 40, that); + } + }; + setTimeout(solid, 200, m.holdingTarget); + + const charge = Math.min(m.throwCharge / 5, 1) + //***** scale throw speed with the first number, 80 ***** + // let speed = 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.25)); + let speed = (tech.isPrinter ? 15 + 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.1)) : 80 * charge * Math.min(0.85, 0.8 / Math.pow(m.holdingTarget.mass, 0.25))) + + + m.throwCharge = 0; + m.throwCycle = m.cycle + 180 //used to detect if a block was thrown in the last 3 seconds + Matter.Body.setVelocity(m.holdingTarget, { + x: Math.cos(m.angle) * speed / (m.crouch ? 30 : 10) * Math.sqrt(m.holdingTarget.mass), + y: player.velocity.y - Math.sin(m.angle) * speed / 30 * Math.sqrt(m.holdingTarget.mass) + }); + Matter.Body.setVelocity(player, { + x: player.velocity.x - player.velocity.x * 0.5 + Math.cos(m.angle) * speed, + y: player.velocity.y - player.velocity.y * 0.5 + Math.sin(m.angle) * speed + }); + m.definePlayerMass() //return to normal player mass + + if (tech.isAddBlockMass) { + const expand = function (that, massLimit) { + if (that.mass < massLimit) { + const scale = 1.04; + Matter.Body.scale(that, scale, scale); + setTimeout(expand, 20, that, massLimit); + } + }; + expand(m.holdingTarget, Math.min(20, m.holdingTarget.mass * 3)) + } + + } + } else { + m.isHolding = false + } + }, drawField() { if (m.holdingTarget) { ctx.fillStyle = "rgba(110,170,200," + (m.energy * (0.05 + 0.05 * Math.random())) + ")"; diff --git a/js/powerup.js b/js/powerup.js index c836f6e..d70b30a 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -326,7 +326,7 @@ const powerUps = { return } if (tech.isCancelDuplication) { - tech.duplication += 0.047 + tech.duplication += 0.05 tech.maxDuplicationEvent() simulation.makeTextLog(`tech.duplicationChance() += ${0.043}`) simulation.circleFlare(0.043); @@ -344,7 +344,7 @@ const powerUps = { powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), spawnType, false); } } - if (tech.isCancelCouple) powerUps.spawnDelay("coupling", 6) + if (tech.isCancelCouple) powerUps.spawnDelay("coupling", 8) // if (tech.isCancelTech && Math.random() < 0.3) { // powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "tech", false); // simulation.makeTextLog(`options exchange: returns 1 tech`) @@ -958,9 +958,6 @@ const powerUps = { if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed()) botTech.push(i) } if (botTech.length > 0) { //pick random bot tech - // const choose = botTech[Math.floor(Math.random() * botTech.length)]; - // const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; - // text += `
⭓▸●■   ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` const choose = botTech[Math.floor(Math.random() * botTech.length)]; const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` @@ -1024,9 +1021,6 @@ const powerUps = { if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed()) botTech.push(i) } if (botTech.length > 0) { //pick random bot tech - // const choose = botTech[Math.floor(Math.random() * botTech.length)]; - // const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; - // text += `
⭓▸●■   ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` const choose = botTech[Math.floor(Math.random() * botTech.length)]; const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` @@ -1051,14 +1045,10 @@ const powerUps = { effect() { if (m.alive) { // powerUps.animatePowerUpGrab('hsla(246, 100%, 77%,0.5)') - let junkCount = 0 //used for junk estimation - let totalCount = 0 //used for junk estimation let options = []; //generate all options optionLengthNoDuplicates = 0 for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBanished) { - totalCount += tech.tech[i].frequency - if (tech.tech[i].isJunk) junkCount += tech.tech[i].frequency if (tech.tech[i].frequency > 0) optionLengthNoDuplicates++ for (let j = 0, len = tech.tech[i].frequency; j < len; j++) options.push(i); } @@ -1110,20 +1100,28 @@ const powerUps = { if (i === 0) simulation.makeTextLog(`options.length = ${optionLengthNoDuplicates} //tech 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 - const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; - 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 { //normal tech - text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) + 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) { // 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)] + 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 { //normal tech + text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) + } + if (options.length < 1) break } - if (options.length < 1) break } if (tech.isExtraBotOption) { const botTech = [] //make an array of bot options diff --git a/js/simulation.js b/js/simulation.js index 1dfeb9c..70d5bb4 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -418,6 +418,15 @@ const simulation = { simulation.boldActiveGunHUD(); }, updateTechHUD() { + + // tech.tech.sort((a, b) => { + // console.log(a.cycle, b.cycle) + // if (a.cycle === undefined && b.cycle !== undefined) return -1; + // if (a.cycle !== undefined && b.cycle === undefined) return 1; + // if (a.cycle === undefined && b.cycle === undefined) return 0; + // if (a.cycle !== b.cycle) return a.cycle - b.cycle; + // }); + let text = "" for (let i = 0, len = tech.tech.length; i < len; i++) { //add tech if (tech.tech[i].isLost) { diff --git a/js/tech.js b/js/tech.js index aaf08b3..533ba52 100644 --- a/js/tech.js +++ b/js/tech.js @@ -29,9 +29,9 @@ const tech = { // tech.removeJunkTechFromPool(); // tech.removeLoreTechFromPool(); // tech.addLoreTechToPool(); + tech.junkChance = 0; tech.extraMaxHealth = 0; tech.totalCount = 0; - tech.junkCount = 0 //tech.countJunkTech(); simulation.updateTechHUD(); simulation.updateGunHUD(); }, @@ -63,7 +63,6 @@ const tech = { tech.tech[index].remove(); tech.tech[index].count = 0; tech.totalCount -= totalRemoved - tech.countJunkTech(); simulation.updateTechHUD(); tech.tech[index].isLost = true simulation.updateTechHUD(); @@ -82,36 +81,41 @@ const tech = { // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1) // } // }, + junkChance: 0, addJunkTechToPool(percent) { //percent is number between 0-1 - tech.junkPoolPercent += percent + tech.junkChance += (1 - tech.junkChance) * percent + return percent //make an array for possible junk tech to add - let options = []; - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].isJunk) options.push(i); - } - if (options.length) { - let countNonJunk = 0 // count total non junk tech - 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].isJunk) countNonJunk += tech.tech[i].frequency - } - const num = Math.ceil(percent * countNonJunk) //scale number added - for (let i = 0; i < num; i++) tech.tech[options[Math.floor(Math.random() * options.length)]].frequency++ //add random array options to tech pool - simulation.makeTextLog(`tech.tech.push(${num.toFixed(0)} JUNK)`) - return num - } else { - return 0 - } + // let options = []; + // for (let i = 0; i < tech.tech.length; i++) { + // if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].isJunk) options.push(i); + // } + // if (options.length) { + // let countNonJunk = 0 // count total non junk tech + // 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].isJunk) countNonJunk += tech.tech[i].frequency + // } + // const num = Math.ceil(percent * countNonJunk) //scale number added + // for (let i = 0; i < num; i++) tech.tech[options[Math.floor(Math.random() * options.length)]].frequency++ //add random array options to tech pool + // simulation.makeTextLog(`tech.tech.push(${num.toFixed(0)} JUNK)`) + // return num + // } else { + // return 0 + // } }, - removeJunkTechFromPool(num = 1) { - for (let j = 0; j < num; j++) { - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].isJunk && tech.tech[i].frequency > 0 && tech.tech[i].count < tech.tech[i].maxCount) { - tech.tech[i].frequency-- - break - } - } + removeJunkTechFromPool(percent) { + // for (let j = 0; j < num; j++) { + // for (let i = 0; i < tech.tech.length; i++) { + // if (tech.tech[i].isJunk && tech.tech[i].frequency > 0 && tech.tech[i].count < tech.tech[i].maxCount) { + // tech.tech[i].frequency-- + // break + // } + // } + // } + if (percent > 0) { + tech.junkChance = (tech.junkChance - percent) / (1 - percent) + if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 } - tech.junkPoolPercent = 0 }, giveRandomJUNK() { const list = [] @@ -123,6 +127,10 @@ const tech = { simulation.makeTextLog(`tech.giveTech("${name}")`); }, giveTech(index = 'random') { + // if (Math.random() < tech.junkChance) { + // tech.giveRandomJUNK(); + // return + // } if (index === 'random') { let options = []; for (let i = 0; i < tech.tech.length; i++) { @@ -157,27 +165,20 @@ const tech = { if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false //stops the bug where you can't gets stacks of tech you take with decoherence, I think tech.tech[index].effect(); //give specific tech tech.tech[index].count++ + // tech.tech[index].cycle = m.cycle + console.log(tech.tech[index].cycle) tech.totalCount++ //used in power up randomization - tech.countJunkTech(); + //move new tech to the top of the tech list + if (index > 0) { + // Remove the element from the array + const [item] = tech.tech.splice(index, 1); + // Add the element to the front of the array + tech.tech.unshift(item); + } + simulation.updateTechHUD(); } }, - junkPoolPercent: 0, - junkCount: 0, - countJunkTech() { - tech.junkCount = 0 - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count > 0 && tech.tech[i].isJunk) tech.junkCount++ - } - }, - // setTechoNonRefundable(name) { - // for (let i = 0; i < tech.tech.length; i++) { - // if (tech.tech.name === name) { - // tech.tech[i].isNonRefundable = true; - // return - // } - // } - // }, setCheating() { if (!simulation.isCheating) { simulation.isCheating = true; @@ -261,7 +262,7 @@ const tech = { if (tech.isHarmDamage && m.lastHarmCycle + 480 > m.cycle) dmg *= 3; if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit * (2 - m.defense()) // if (!simulation.paused) m.lastHit = 0 if (tech.isLowHealthDmg) dmg *= 1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health)) - if (tech.isJunkDNA) dmg *= 1 + tech.junkPoolPercent + if (tech.isJunkDNA) dmg *= 1 + 2 * tech.junkChance return dmg }, duplicationChance() { @@ -536,7 +537,9 @@ const tech = { name: "depolarization", descriptionFunction() { // return `+300% damage or -50% damage
if a mob has died in the last 5 seconds` - return `+333% damage if no mobs died in the last ${(tech.isDamageCooldownTime / 60).toFixed(0)} seconds
-55% damage if a mob died in the last ${(tech.isDamageCooldownTime / 60).toFixed(0)} seconds
` + // return `+333% damage if no mobs died in the last ${(tech.isDamageCooldownTime / 60).toFixed(1)} seconds
-55% damage if a mob died in the last ${(tech.isDamageCooldownTime / 60).toFixed(0)} seconds
` + // return `-55% damage if a mob died in the last ${(tech.isDamageCooldownTime / 60).toFixed(1)} seconds
otherwise do +333% damage
` + return `-55% damage for ${(tech.isDamageCooldownTime / 60).toFixed(1)} seconds after a mob dies
+333% damage otherwise
` }, maxCount: 1, count: 0, @@ -559,7 +562,7 @@ const tech = { { name: "hyperpolarization", descriptionFunction() { - return `the damage from depolarization
resets 1 second sooner after a mob has died` + return `the damage from depolarization
resets 1.25 seconds sooner after a mob dies` }, maxCount: 3, count: 0, @@ -570,7 +573,7 @@ const tech = { }, requires: "depolarization", effect() { - tech.isDamageCooldownTime -= 60 + tech.isDamageCooldownTime -= 75 }, remove() { tech.isDamageCooldownTime = 240 @@ -661,7 +664,7 @@ const tech = { }, { name: "ordnance", - description: "double the frequency of finding guntech
spawn a gun and +7% JUNK to tech pool", + description: "double the frequency of finding guntech
spawn a gun and +6% JUNK to tech pool", maxCount: 1, count: 0, frequency: 1, @@ -675,7 +678,7 @@ const tech = { for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2 } - this.refundAmount += tech.addJunkTechToPool(0.07) + this.refundAmount += tech.addJunkTechToPool(0.06) }, refundAmount: 0, remove() { @@ -2915,7 +2918,7 @@ const tech = { }, { name: "overcharge", - description: "+66 maximum energy
+5% JUNK to tech pool", + description: "+88 maximum energy
+5% JUNK to tech pool", maxCount: 9, count: 0, frequency: 1, @@ -3942,7 +3945,7 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return tech.junkCount > 0 + return tech.junkChance > 0 }, requires: "some JUNK tech", effect() { @@ -3954,7 +3957,7 @@ const tech = { }, { name: "dark patterns", - description: "+22% damage
+22% JUNK to tech pool", + description: "+22% damage
+11% JUNK to tech pool", maxCount: 9, count: 0, frequency: 1, @@ -3966,7 +3969,7 @@ const tech = { damage: 1.22, effect() { tech.damage *= this.damage - this.refundAmount += tech.addJunkTechToPool(0.22) + this.refundAmount += tech.addJunkTechToPool(0.11) }, refundAmount: 0, remove() { @@ -3979,29 +3982,21 @@ const tech = { { name: "junk DNA", descriptionFunction() { - // return ` +100% ${b.guns[6].nameString()} damage per JUNKtech (${(100 * tech.junkCount).toFixed(0)}%)
+33% JUNK to tech pool` - return `damage scales with JUNK tech pool percent` + return `increase damage by twice the
JUNK tech pool percent (${(200 * tech.junkChance).toFixed(0)}%)` }, - // isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { - return tech.junkPoolPercent > 0 + return tech.junkChance > 0 }, requires: "JUNK in tech pool", effect() { tech.isJunkDNA = true - // this.refundAmount += tech.addJunkTechToPool(0.20) }, - // refundAmount: 0, remove() { tech.isJunkDNA = false - // if (this.count > 0 && this.refundAmount > 0) { - // tech.removeJunkTechFromPool(this.refundAmount) - // this.refundAmount = 0 - // } } }, { @@ -4246,7 +4241,7 @@ const tech = { name: "residual dipolar coupling", descriptionFunction() { // return `clicking cancel for a field, tech, or gun
spawns ${powerUps.orb.coupling(5)}that each give +0.1 coupling`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} - return `clicking cancel spawns ${powerUps.orb.coupling(6)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` + return `clicking cancel spawns ${powerUps.orb.coupling(8)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` }, maxCount: 1, count: 0, @@ -4304,7 +4299,7 @@ const tech = { }, { name: "futures exchange", - description: "clicking cancel for a field, tech, or gun
gives +4.7% power up duplication chance", + description: "clicking cancel for a field, tech, or gun
gives +5% power up duplication chance", // descriptionFunction() { // return `clicking × to cancel a field, tech, or gun
gives +${4.9 - 0.15*simulation.difficultyMode}% power up duplication chance` // }, @@ -4327,7 +4322,7 @@ const tech = { }, { name: "replication", - description: "+10% chance to duplicate spawned power ups
+33% JUNK to tech pool", + description: "+10% chance to duplicate spawned power ups
+22% JUNK to tech pool", maxCount: 9, count: 0, frequency: 1, @@ -4339,8 +4334,8 @@ const tech = { effect() { tech.duplicateChance += 0.1 powerUps.setPowerUpMode(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); - this.refundAmount += tech.addJunkTechToPool(0.33) + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.1); + this.refundAmount += tech.addJunkTechToPool(0.22) }, refundAmount: 0, remove() { @@ -4677,30 +4672,6 @@ const tech = { }, remove() { } }, - { - name: "reinforcement learning", - description: "increase the frequency of finding copies of
your current tech by 1000%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.totalCount > 9 - }, - requires: "at least 10 tech", - effect() { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].count > 0) tech.tech[i].frequency *= 10 - } - }, - remove() { - if (this.count) { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].count > 0 && tech.tech[i].frequency > 1) tech.tech[i].frequency /= 10 - } - } - } - }, // { // name: "backward induction", // descriptionFunction() { @@ -6230,10 +6201,7 @@ const tech = { requires: "mines", effect() { tech.isMineDrop = true; - if (tech.isMineDrop) b.mine(m.pos, { - x: 0, - y: 0 - }, 0) + if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0) this.refundAmount += tech.addJunkTechToPool(0.30) }, refundAmount: 0, @@ -9057,7 +9025,7 @@ const tech = { }, { name: "reel", - description: "+400% block collision damage
up to +75 energy after reeling in blocks", + description: "+400% block collision damage
up to +100 energy after reeling in blocks", isFieldTech: true, maxCount: 1, count: 0, @@ -9549,6 +9517,30 @@ const tech = { }, remove() { } }, + { + name: "reinforcement learning", + description: "+1000% frequency of finding copies of current tech
", + maxCount: 1, + count: 0, + frequency: 1, + isJunk: true, + allowed() { + return tech.totalCount > 9 + }, + requires: "at least 10 tech", + effect() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0) tech.tech[i].frequency *= 10 + } + }, + remove() { + if (this.count) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0 && tech.tech[i].frequency > 1) tech.tech[i].frequency /= 10 + } + } + } + }, { name: "startle response", description: `if a threat is nearby, activate a ${powerUps.orb.boost(1)}
and lock your mouse until you press escape`, @@ -10228,6 +10220,25 @@ const tech = { }, remove() { } }, + { + name: "what the block?", + description: "throwing a block throws you instead", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return m.fieldMode !== 8 && m.fieldMode !== 9 && !tech.isTokamak + }, + requires: "not pilot wave, tokamak, wormhole", + effect() { + + }, + remove() { + m.throwBlock = m.throwBlockDefault + } + }, { name: "spinor", description: "the direction you aim is determined by your position", @@ -10599,9 +10610,7 @@ const tech = { }, requires: "", effect() { - for (let i = tech.tech.length - 1; i > 0; i--) { - if (tech.tech[i].isJunk) tech.tech[i].frequency = 0 - } + tech.junkChance = 0; }, remove() { } }, @@ -11592,6 +11601,26 @@ const tech = { }, remove() { } }, + { + name: "beforeunload", + description: "75% of the time when you attempt to exit n-gon
you are prompted to cancel or continue.
Each time you cancel gain +25% damage.", + maxCount: 1, + count: 0, + frequency: 1, + isJunk: true, + allowed() { + return tech.totalCount > 9 + }, + requires: "at least 10 tech", + effect() { + tech.isExitPrompt = true + addEventListener('beforeunload', beforeUnloadEventListener); + }, + remove() { + tech.isExitPrompt = false + if (this.count > 0) removeEventListener('beforeunload', beforeUnloadEventListener); + } + }, { name: "planetesimals", description: `play planetesimals (an asteroids-like game)
clear levels in planetesimals to spawn tech
if you die in planetesimals you die in n-gon`, @@ -12189,4 +12218,5 @@ const tech = { isDamageCooldown: null, isDamageCooldownTime: null, isPowerUpDamage: null, + isExitPrompt: null, } \ No newline at end of file diff --git a/todo.txt b/todo.txt index e6d3a66..651378c 100644 --- a/todo.txt +++ b/todo.txt @@ -1,19 +1,41 @@ ******************************************************** NEXT PATCH ************************************************** -laserLayerBoss - sends lasers to player's location -snakes bosses - after you kill a tail piece the rest of the tails lose 5% life - this makes targeting any part of the tail a viable strategy +new tech are sorted to the top of the tech list + pause menu tech list matches the tech list in game +JUNK tech icons are properly displayed in pause +Sort interface works with enter key +bug fix: you can no longer pause or enter testing in experiment selection on text input +pressing P in power up selection menu brings up the pause menu + so you can see your tech and stats +blocks stuck in vertical portals have a bit of randomness added to their velocity to help them escape -accretion disk - 5% damage for every power up on this level - requires accretion -unified field theory has buttons to cycle field forwards or backwards when paused -iceIX range and damage increased 15% -electric tech in labs level is finally nerfed - drains energy, but doesn't do damage - pushes player away +JUNK tech chance is a raw percent chance to display a random JUNK + it was previously a specific JUNK tech directly added to the pool + this might cause bugs +JUNK tech: beforeunload - asks if you want to cancel if you exit game, if you cancel gain 25% damage, but there is a 25% chance to exit anyways +JUNK tech: what the block? - trying to throw a block, throws you instead +reinforcement learning converted into a JUNK tech +JUNK DNA - scale damage by 100->200% of JUNK pool tech percent +dark patterns 33->22% JUNK +replication 33->22% JUNK + +overcharge 66->88 max energy +residual dipolar coupling 6->8 coupling per cancel +futures exchange 4.7->5% duplication per cancel +hyperpolarization reduces polarization time by 1->1.25 seconds +reel +75->100 energy + +updated physics engine to matter.js 0.19 (from 0.18) *********************************************************** TODO ***************************************************** + +List of ways to break the game + CPT + high energy regen + research->bot fabrication-> ersatz bots -> various bot upgrades + duplication 100% + electronegativity and high energy? + boss - tracks the position, velocity, angle of power ups, blocks, and bullets it fires reactor only? will rewind time @@ -32,7 +54,7 @@ button/switch input ideas from Cocoon game draw field effect make it still functional without the field button -tech: - getting a new gun also gives you 2 random tech for that gun +tech - getting a new gun also gives you 2 random tech for that gun or a field? can these guntech tech be converted into a player choice? @@ -149,6 +171,7 @@ super-bot: fires super balls tech: after a needle hits a mobs the needle splits into 3 needles? reset your fire CD? + fire again for zero ammo 2x damage for each consecutive mob hit? mob non-combat behaviors, like Rain World @@ -277,9 +300,6 @@ Tech:when relay switch/flip flop is on, turn ammo powerups into boosts, when rel or toggle other power ups health/ammo -JUNK: what the golf? - trying to throw a block throws you instead - complete blowSuckBoss... or don't tech: laser reflections increase damage @@ -1136,6 +1156,7 @@ possible names for tech Unruh effect - accelerating makes heat/thermal particles configuration space - holds the position of everything stress–energy tensor + radioisotope thermoelectric generator - ******************************************************** CARS IMAGES ********************************************************