From 79e27978ecf81349b1b06ac23d6ddb480d207f14 Mon Sep 17 00:00:00 2001 From: landgreen Date: Wed, 27 Oct 2021 19:21:51 -0700 Subject: [PATCH] JUNK percents JUNK tech: facsimile - copy current level and insert into level queue JUNK tech: planetesimals - spawn a tech, and play planetesimals, an annoying asteroids like game with realistic physics tech that previously added 1 junk tech now adds 1% (of current non-JUNK tech options) the percent values may seem like more but I actually lowered the JUNK chance about 10% meta-analysis no longer requires replication - (if you choose a JUNK tech get a random tech and 3 research) symbiosis: only gives 1 bonus tech (was 1 or 2) blink boss rebalanced (it's easier, and has two modes) bug fixes --- .DS_Store | Bin 6148 -> 6148 bytes index.html | 4 + js/bullet.js | 18 +- js/level.js | 13 +- js/mob.js | 2 +- js/planetesimals.js | 807 ++++++++++++++++++++++++++++++++++++++++++++ js/player.js | 8 +- js/powerup.js | 2 +- js/spawn.js | 14 +- js/tech.js | 173 +++++++--- todo.txt | 30 +- 11 files changed, 985 insertions(+), 86 deletions(-) create mode 100644 js/planetesimals.js diff --git a/.DS_Store b/.DS_Store index 8f387a5dc5a4d057bec8af34f978d0cbed948a5c..bd2a295d3e21378f3ad21707ad4c004d3c00b536 100644 GIT binary patch delta 94 zcmZoMXffEJ#T+>Q3 + + + \ No newline at end of file diff --git a/js/bullet.js b/js/bullet.js index 5713296..84e7341 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -1231,7 +1231,7 @@ const b = { } } } else { - if (m.energy > 0.005) m.energy -= 0.005 + if (m.energy > 0.005 && !m.isBodiesAsleep) m.energy -= 0.005 const sub = Vector.sub(this.position, m.pos) const rangeScale = 1 + 0.000001 * Vector.magnitude(sub) * Vector.magnitude(sub) //return faster when far from player const returnForce = Vector.mult(Vector.normalise(sub), rangeScale * this.thrustMag * this.mass) @@ -1478,7 +1478,6 @@ const b = { y: Math.sin(this.angle) }; const target = Vector.normalise(Vector.sub(this.position, this.lockedOn.position)); - // const target = Vector.normalise(Vector.sub(this.position, this.lockedOn.position)); const dot = Vector.dot(target, face) const aim = Math.min(0.08, (1 + dot) * 1) if (Vector.cross(target, face) > 0) { @@ -1486,7 +1485,7 @@ const b = { } else { Matter.Body.rotate(this, -aim); } - this.frictionAir = Math.min(0.1, Math.max(0.04, (1 + dot) * 1)) //0.08; //extra friction if turning + this.frictionAir = Math.min(0.1, Math.max(0.04, 1 + dot)) //0.08; //extra friction if turning } //accelerate in direction bullet is facing const dir = this.angle; @@ -4073,7 +4072,7 @@ const b = { }); } } else { - if (Math.abs(player.velocity.x) < 12) player.force.x -= 0.025 * Math.cos(m.angle) + player.force.x -= 0.06 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) player.force.y -= 0.006 * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps } }, @@ -4239,7 +4238,9 @@ const b = { }); } } else { - if (Math.abs(player.velocity.x) < 12) player.force.x -= 0.06 * Math.cos(m.angle) + player.force.x -= 0.2 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) + // player.force.x -= 0.06 * Math.cos(m.angle) * Math.min(1, 3 / (0.1 + Math.abs(player.velocity.x))) + player.force.y -= 0.02 * Math.sin(m.angle) //reduce knock back in vertical direction to stop super jumps } }, @@ -5333,7 +5334,6 @@ const b = { m.fireCDcycle = m.cycle + 50 * b.fireCDscale; // cool down // } } else if (tech.extraHarpoons) { - const harpoons = tech.extraHarpoons + 1 const range = 450 * (tech.isFilament ? 1 + 0.005 * Math.min(110, this.ammo) : 1) let targetCount = 0 for (let i = 0, len = mob.length; i < len; ++i) { @@ -5345,15 +5345,15 @@ const b = { this.ammo-- b.harpoon(where, mob[i], m.angle, length, true, totalCycles) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 }) targetCount++ - if (targetCount > harpoons) break + if (targetCount > tech.extraHarpoons) break } } } } //if more harpoons and no targets left - if (targetCount < harpoons) { + if (targetCount < tech.extraHarpoons + 1) { const SPREAD = 0.1 - const num = harpoons - targetCount + const num = tech.extraHarpoons + 1 - targetCount let dir = m.angle - SPREAD * (num - 1) / 2; for (let i = 0; i < num; i++) { if (this.ammo > 0) { diff --git a/js/level.js b/js/level.js index d9733d0..ff42fac 100644 --- a/js/level.js +++ b/js/level.js @@ -22,7 +22,7 @@ const level = { // tech.giveTech("rivet gun") // for (let i = 0; i < 2; i++) tech.giveTech("refractory metal") // tech.giveTech("all-stars") - // for (let i = 0; i < 9; i++) tech.giveTech("reticulum") + // for (let i = 0; i < 1; i++) tech.giveTech("reticulum") // for (let i = 0; i < 2; i++) tech.giveTech("laser-bot") // tech.isCancelDuplication = true @@ -2316,7 +2316,7 @@ const level = { // spawn.laserBombingBoss(1900, -500) // for (let i = 0; i < 5; i++) spawn.focuser(1900, -500) // spawn.slashBoss(1900, -500) - spawn.sucker(1900, -500) + // spawn.sucker(1900, -500) // spawn.shield(mob[mob.length - 1], 1900, -500, 1); // mob[mob.length - 1].isShielded = true // spawn.growBossCulture(1200, -500) @@ -2332,7 +2332,7 @@ const level = { // spawn.nodeGroup(1200, -500, "grenadier") // spawn.nodeGroup(1800, -500, "grenadier") // spawn.nodeGroup(1200, 0, "grenadier") - // spawn.snakeBoss(1200, -500) + spawn.blinkBoss(1200, -500) // spawn.suckerBoss(2900, -500) // spawn.randomMob(1600, -500) }, @@ -2931,12 +2931,7 @@ const level = { spawn.randomMob(-75, -1475, 0); spawn.randomGroup(600, -2600, 0); } - if (simulation.difficulty < 10) { - spawn.randomMob(700, -1650, 0); - spawn.randomMob(600, -3500, 0.2); - spawn.randomMob(-75, -1175, 0.2); - powerUps.spawnBossPowerUp(-125, -1760); - } else { + if (simulation.difficulty < 1) { if (Math.random() < 0.5) { spawn.randomLevelBoss(700, -1550); } else { diff --git a/js/mob.js b/js/mob.js index 2ccd008..20c8293 100644 --- a/js/mob.js +++ b/js/mob.js @@ -1140,7 +1140,7 @@ const mobs = { if (tech.isAddRemoveMaxHealth) { if (this.isBoss && this.isDropPowerUp) { powerUps.spawn(this.position.x, this.position.y, "tech", false) - if (0.5 < Math.random()) powerUps.spawn(this.position.x, this.position.y, "tech", false) + // if (0.5 < Math.random()) powerUps.spawn(this.position.x, this.position.y, "tech", false) } else { const amount = 0.01 if (tech.isEnergyHealth) { diff --git a/js/planetesimals.js b/js/planetesimals.js new file mode 100644 index 0000000..01eba97 --- /dev/null +++ b/js/planetesimals.js @@ -0,0 +1,807 @@ +/* http://brm.io/matter-js/docs/ +http://brm.io/matter-js/demo/#mixed +https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js + +git hub gist: +https://gist.github.com/lilgreenland/8f2a2c033fdf3d5546a0ca5d73a2ae11 + + todo: +can you render bullets a different color? // red? + +maybe add the ability to put up shields to survive collisions? +maybe add durability regeneration + +fix new mass spawn so that it will pick a new location if the first spawn location already has a mass + + */ +function planetesimals() { + + //pause n-gon + // if (!simulation.isChoosing && input.isPauseKeyReady && m.alive) { + // input.isPauseKeyReady = false + // setTimeout(function() { + // input.isPauseKeyReady = true + // }, 300); + // if (simulation.paused) { + // build.unPauseGrid() + // simulation.paused = false; + // // level.levelAnnounce(); + // document.body.style.cursor = "none"; + // requestAnimationFrame(cycle); + // } else { + // simulation.paused = true; + // build.pauseGrid() + // document.body.style.cursor = "auto"; + + // if (tech.isGunSwitchField || simulation.testing) { + // document.getElementById("pause-field").addEventListener("click", () => { + // const energy = m.energy + // m.setField((m.fieldMode === m.fieldUpgrades.length - 1) ? 1 : m.fieldMode + 1) //cycle to next field + // m.energy = energy + // document.getElementById("pause-field").innerHTML = `
  ${m.fieldUpgrades[m.fieldMode].name}
${m.fieldUpgrades[m.fieldMode].description}` + // }); + // } + // } + // } + + "use strict"; //strict mode to catch errors + //game objects values + var game = { + cycle: 0, + width: 0, + height: 0, + scale: 0.5, + gravity: 0.00011, + totalMass: 0, + massSize: 0, + level: 1, + startingMassValue: 0, + massSegment: 0, + clearThreshold: 0.2, // there must be less than this percent to move on to the next level + currentMass: 0, + explodeMin: 8000, + HUD: true, + }; + + function levelScaling() { + //game.gravity = 0.00011; // + 0.000012 * game.level; + game.width = 2000 * game.level; //shapes walls and spawn locations + game.height = 2000 * game.level; //shapes walls and spawn locations + game.scale = 1.2 / (Math.log(game.level + 1)); //0.6 + 1.0 / (game.level); //controls map zoom + game.totalMass = 3 + game.level * 1; //how many masses to spawn at start of level + game.massSize = 3 + game.level * 3; //adds the average length of a segment on a masses's vertices + game.massSegment = 0.1 + 0.1 / game.level; + } + + //looks for key presses and logs them + var keys = []; + document.body.addEventListener("keydown", function(e) { + keys[e.keyCode] = true; + }); + document.body.addEventListener("keyup", function(e) { + keys[e.keyCode] = false; + }); + + // module aliases + var Engine = Matter.Engine, + World = Matter.World, + Events = Matter.Events, + Composite = Matter.Composite, + Vertices = Matter.Vertices, + Body = Matter.Body, + Bodies = Matter.Bodies; + + // create an engine + var engine = Engine.create(); + //turn off gravity + engine.world.gravity.y = 0; + // run the engine + // Engine.run(engine); + Matter.Runner.run(engine) + + function addWalls() { + //add the walls + var wallSettings = { + size: 200, + isStatic: true, + render: { + restitution: 0, + fillStyle: 'rgba(0, 0, 0, 0.0)', + strokeStyle: '#00ffff' + } + }; + World.add(engine.world, [ + Bodies.rectangle(game.width * 0.5, -wallSettings.size * 0.5, game.width, wallSettings.size, wallSettings), //top + Bodies.rectangle(game.width * 0.5, game.height + wallSettings.size * 0.5, game.width, wallSettings.size, wallSettings), //bottom + Bodies.rectangle(-wallSettings.size * 0.5, game.height * 0.5, wallSettings.size, game.height + wallSettings.size * 2, wallSettings), //left + Bodies.rectangle(game.width + wallSettings.size * 0.5, game.height * 0.5, wallSettings.size, game.height + wallSettings.size * 2, wallSettings) //right + ]); + + } + //add the masses + var mass = []; + + function addPlayer() { + //add the player object as the first mass in the array + mass.push(); + //var arrow = Vertices.fromPath('100 0 75 50 100 100 25 100 0 50 25 0'); + var arrow = Vertices.fromPath('0 15 -10 -15 10 -15'); + mass[0] = Matter.Bodies.fromVertices(Math.random() * game.width, Math.random() * game.height, arrow, { + //density: 0.001, + alive: true, + friction: 0, + frictionStatic: 0, + frictionAir: 0, + restitution: 0, //bounce 1 = 100% elastic + density: 0.003333, + thrust: 0.0004, //forward acceleration, if mass goes up this needs to go up + yaw: 0.00133, //angular acceleration, needs to be higher with larger mass + rotationLimit: 0.05, //max acceleration for player in radians/cycle + angularFriction: 0.98, // 1 = no friction, 0.9 = high friction + durability: 1, + fireCD: 0, + lastPlayerVelocity: { //for keeping track of damamge from too much acceleration + x: 0, + y: 0 + }, + }); + World.add(engine.world, mass[0]); + } + + function randomConvexPolygon(size) { //returns a string of vectors that make a convex polygon + var polyVector = ''; + var x = 0; + var y = 0; + var r = 0; + var angle = 0; + for (var i = 1; i < 60; i++) { + angle += 0.1 + Math.random() * game.massSegment; //change in angle in radians + if (angle > 2 * Math.PI) { + break; //stop before it becomes convex + } + r = 2 + Math.random() * 2; + x = Math.round(x + r * Math.cos(angle)); + y = Math.round(y + r * Math.sin(angle)); + polyVector = polyVector.concat(x * size + ' ' + y * size + ' '); + } + return polyVector; + } + + function addMassVector(x, y, Vx, Vy, size) { + var verticies = []; + var vector = Vertices.fromPath(randomConvexPolygon(size)); + var i = mass.length; + mass.push(); + mass[i] = Matter.Bodies.fromVertices(x, y, vector, { // x,y,vectors,{options} + friction: 0, + frictionStatic: 0, + frictionAir: 0, + restitution: 1, + angle: Math.random() * 2 * Math.PI + }); + Matter.Body.setVelocity(mass[i], { + x: Vx, + y: Vy + }); + Matter.Body.setAngularVelocity(mass[i], (Math.random() - 0.5) * 0.03); + World.add(engine.world, mass[i]); + } + + function addMass(x, y, r, sides, Vx, Vy) { + var i = mass.length; + mass.push(); + mass[i] = Bodies.polygon(x, y, sides, r, { + friction: 0, + frictionStatic: 0, + frictionAir: 0, + restitution: 1, + }); + Matter.Body.setVelocity(mass[i], { + x: Vx, + y: Vy + }); + Matter.Body.setAngularVelocity(mass[i], (Math.random() - 0.5) * 0.03); + World.add(engine.world, mass[i]); + } + + function clearMasses() { + World.clear(engine.world, false); + console.log('clear') + mass = []; + } + + function spawnSetup() { + //make the level indicator more clear on a new level + // document.getElementById("level").innerHTML = 'system ' + game.level; + // document.getElementById("level").style.color = 'white'; + // document.getElementById("level").style.fontSize = '500%'; + // document.getElementById("level").style.left = '40%'; + // document.getElementById("level").style.position = 'absolute'; + // setTimeout(levelFontSize, 3000); + //after 3 seconds return to normal style + // function levelFontSize(size) { + // document.getElementById("level").style.color = 'grey'; + // document.getElementById("level").style.position = ''; + // document.getElementById("level").style.left = ''; + // document.getElementById("level").style.fontSize = '100%'; + // } + levelScaling(); + clearMasses(); + addWalls(); + addPlayer(); + //add other masses + for (var j = 0; j < game.totalMass; j++) { + // addMassVector(x,y,Vx,Vy,size) + addMassVector(game.width * 0.2 + Math.random() * game.width * 0.6, + game.height * 0.2 + Math.random() * game.height * 0.6, + 0, //(0.5 - Math.random()) * 4, + 0, + Math.random() * 3 + game.massSize + ); + } + //determine how much mass is in the game at the start + game.startingMassValue = 0; + for (var i = 0; i < mass.length; i++) { + game.startingMassValue += mass[i].mass; + } + game.currentMass = game.startingMassValue; + } + + spawnSetup(); + + function repopulateMasses() { + game.currentMass = 0; + for (var i = 0; i < mass.length; i++) { + game.currentMass += mass[i].mass; + } + if (game.currentMass < game.startingMassValue * game.clearThreshold) { + game.level++; + spawnSetup(); + mass[0].durability = 1; + } + } + + var bullet = []; + + function fireBullet() { //addMass(x, y, r, sides, Vx, Vy) + var i = bullet.length; + var angle = mass[0].angle + Math.PI * 0.5; + var speed = 9; + var playerDist = 25; + bullet.push(); + bullet[i] = Bodies.polygon( + mass[0].position.x + playerDist * Math.cos(angle), + mass[0].position.y + playerDist * Math.sin(angle), + 3, //sides + 2, { //radius + angle: Math.random() * 6.28, + friction: 0, + frictionStatic: 0, + frictionAir: 0, + restitution: 1, + endCycle: game.cycle + 90, // life span for a bullet (60 per second) + }); + Matter.Body.setVelocity(bullet[i], { + x: mass[0].velocity.x + speed * Math.cos(angle), + y: mass[0].velocity.y + speed * Math.sin(angle) + }); + Matter.Body.setAngularVelocity(bullet[i], (Math.random() - 0.5) * 1); + World.add(engine.world, bullet[i]); + } + + function bulletEndCycle() { + for (var i = 0; i < bullet.length; i++) { + if (bullet[i].endCycle < game.cycle) { + Matter.World.remove(engine.world, bullet[i]); + bullet.splice(i, 1); + } + } + } + + function controls() { + if (mass[0].alive) { + if (keys[32] && mass[0].fireCD < game.cycle) { + mass[0].fireCD = game.cycle + 10; // ?/60 seconds of cooldown before you can fire + fireBullet(); + } + + if (keys[38] || keys[87]) { //forward thrust + mass[0].force.x += mass[0].thrust * Math.cos(mass[0].angle + Math.PI * 0.5); + mass[0].force.y += mass[0].thrust * Math.sin(mass[0].angle + Math.PI * 0.5); + thrustGraphic(); + } else if (keys[40] || keys[83]) { //reverse thrust + mass[0].force = { + x: -mass[0].thrust * 0.5 * Math.cos(mass[0].angle + Math.PI * 0.5), + y: -mass[0].thrust * 0.5 * Math.sin(mass[0].angle + Math.PI * 0.5) + }; + torqueGraphic(-1); + torqueGraphic(1); + } + //rotate left and right + if ((keys[37] || keys[65])) { //&& mass[0].angularVelocity > -mass[0].rotationLimit) { + mass[0].torque = -mass[0].yaw; //counter clockwise + torqueGraphic(-1); + } else if ((keys[39] || keys[68])) { //&& mass[0].angularVelocity < mass[0].rotationLimit) { + mass[0].torque = mass[0].yaw; //clockwise + torqueGraphic(1); + } + //angular friction if spinning too fast + if (Math.abs(mass[0].angularVelocity) > mass[0].rotationLimit) { + Matter.Body.setAngularVelocity(mass[0], mass[0].angularVelocity * mass[0].angularFriction); + } + } + } + + function torqueGraphic(dir) { //thrust graphic when holding rotation keys + ctx.save(); + //ctx.translate(0.5 * canvas.width, 0.5 * canvas.height) + ctx.rotate(mass[0].angle - Math.PI * 0.6 * dir); + ctx.translate(0, -23); + var grd = ctx.createLinearGradient(0, 0, 0, 15); + grd.addColorStop(0.1, 'rgba(0, 0, 0, 0)'); + grd.addColorStop(1, 'rgba(160, 192, 255, 1)'); + ctx.fillStyle = grd; + ctx.beginPath(); + ctx.moveTo(dir * 6 * (Math.random() - 0.5) + 12 * dir, 6 * (Math.random() - 0.5)); + ctx.lineTo(dir * 8, 14); + ctx.lineTo(dir * 12, 14); + ctx.fill(); + ctx.restore(); + } + + function thrustGraphic() { + //ctx.fillStyle= "#90b0ff"; + ctx.save(); + //ctx.translate(0.5 * canvas.width, 0.5 * canvas.height) + ctx.rotate(mass[0].angle); + ctx.translate(0, -33); + var grd = ctx.createLinearGradient(0, 0, 0, 15); + grd.addColorStop(0, 'rgba(0, 0, 0, 0)'); + grd.addColorStop(1, 'rgba(160, 192, 255, 1)'); + ctx.fillStyle = grd; + ctx.beginPath(); + ctx.moveTo(10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)); + ctx.lineTo(7, 20); + ctx.lineTo(-7, 20); + ctx.fill(); + ctx.restore(); + } + + function gravity() { + var length = mass.length; + var Dx = 0; + var Dy = 0; + var force = 0; + var angle = 0; + var i = 0; + var j = 0; + //gravity for array masses, but not player: mass[0] + for (i = 0; i < length; i++) { + for (j = 0; j < length; j++) { + if (i != j) { + Dx = mass[j].position.x - mass[i].position.x; + Dy = mass[j].position.y - mass[i].position.y; + force = game.gravity * mass[j].mass * mass[i].mass / (Math.sqrt(Dx * Dx + Dy * Dy)); + angle = Math.atan2(Dy, Dx); + mass[i].force.x += force * Math.cos(angle); + mass[i].force.y += force * Math.sin(angle); + } + } + } + //gravity for bullets + var Blength = bullet.length; + for (i = 0; i < Blength; i++) { + for (j = 0; j < length; j++) { //bullets only feel gravity, they don't create it + Dx = mass[j].position.x - bullet[i].position.x; + Dy = mass[j].position.y - bullet[i].position.y; + force = game.gravity * mass[j].mass * bullet[i].mass / (Math.sqrt(Dx * Dx + Dy * Dy)); + angle = Math.atan2(Dy, Dx); + bullet[i].force.x += force * Math.cos(angle); + bullet[i].force.y += force * Math.sin(angle); + } + } + } + + function damage() { //changes player health if velocity changes too much + var limit2 = 9; //square of velocity damamge limit + var dX = Math.abs(mass[0].lastPlayerVelocity.x - mass[0].velocity.x); + var dY = Math.abs(mass[0].lastPlayerVelocity.y - mass[0].velocity.y); + var dV2 = dX * dX + dY * dY; //we are skipping the square root + if (dV2 > limit2) { //did velocity change enough to take damage + mass[0].durability -= Math.sqrt(dV2 - limit2) * 0.02; //player takes damage + if (mass[0].durability < 0 && mass[0].alive) { //player dead? + mass[0].alive = false; + //spawn player explosion debris + for (var j = 0; j < 10; j++) { //addMass(x, y, r, sides, Vx, Vy) + addMass(mass[0].position.x + 10 * (0.5 - Math.random()), + mass[0].position.y + 10 * (0.5 - Math.random()), + 5, + 3, + (0.5 - Math.random()) * 8 + mass[0].velocity.x, + (0.5 - Math.random()) * 8 + mass[0].velocity.y); + } + //shrink player to match debris size + Matter.Body.scale(mass[0], 0.4, 0.4); + //reset masses after a few seconds + window.setTimeout(spawnSetup, 6000); + } + } + //keep track of last player velocity to calculate changes in velocity + mass[0].lastPlayerVelocity.x = mass[0].velocity.x; + mass[0].lastPlayerVelocity.y = mass[0].velocity.y; + } + + //bullet collision event + Events.on(engine, 'collisionStart', function(event) { + + //slice the polygon up into sections + function slicePoly(m, start, end) { //cut a mass into two sectons + //build new string vector array that matches some of the reference mass + var polyVector = ''; + for (var i = start; i < end; i++) { + polyVector = polyVector.concat(mass[m].vertices[i].x + ' ' + mass[m].vertices[i].y + ' '); + } + //buggy: making polygons noncolide. not sure why + //if (end = mass[m].vertices.length) { //catch the first vertices if the polygon hits the last + // polyVector = polyVector.concat(mass[m].vertices[0].x + ' ' + mass[m].vertices[0].y + ' '); } + var verticies = []; //build the polygon in matter.js + var vector = Vertices.fromPath(polyVector); + + //add string vector array to game as a polygon + var len = mass.length; + mass.push(); + mass[len] = Matter.Bodies.fromVertices(0, 0, vector, { // x,y,vectors,{options} + friction: 0, + frictionStatic: 0, + frictionAir: 0, + restitution: 1 + }); + World.add(engine.world, mass[len]); + //scale down the polygon a bit to help with collisions + Matter.Body.scale(mass[len], 0.9, 0.9); + //move polygon into position + var vectorPos = Matter.Vertices.centre(vector); //find the center of new polygon + Matter.Body.translate(mass[len], { + x: vectorPos.x, + y: vectorPos.y + }); + //give a velocity pointed away from the old mass's center so it explodes + var angle = Math.atan2(mass[len].position.y - mass[m].position.y, mass[len].position.x - mass[m].position.x); + Matter.Body.setVelocity(mass[len], { + x: mass[m].velocity.x + 2 * Math.cos(angle), + y: mass[m].velocity.y + 2 * Math.sin(angle) + }); + //add some spin + Matter.Body.setAngularVelocity(mass[len], (Math.random() - 0.5) * 0.1); + } + + function hit(i, b, m) { + //match the collisions pair id to the mass + for (var j = 1; j < mass.length; j++) { //start at 1 to skip the player + if (mass[j].id === m.id) { + //remove bullet + Matter.World.remove(engine.world, bullet[i]); + bullet.splice(i, 1); + + // explosion graphics + var driftSpeed = 1; + var length = mass[j].vertices.length - 1; + var dx = (mass[j].vertices[length].x - mass[j].position.x); + var dy = (mass[j].vertices[length].y - mass[j].position.y); + var r = Math.sqrt(dx * dx + dy * dy) * 1.5; // *1.5 give the explosion outward spread + var angle = Math.atan2(dy, dx); + boom.push({ //the line form the 1st and last vertex + x1: mass[j].vertices[length].x, + y1: mass[j].vertices[length].y, + x2: mass[j].vertices[0].x, + y2: mass[j].vertices[0].y, + alpha: 1, + driftVx: mass[j].velocity.x + (Math.random() - 0.5) * driftSpeed + r * mass[j].angularSpeed * Math.cos(angle), + driftVy: mass[j].velocity.y + (Math.random() - 0.5) * driftSpeed + r * mass[j].angularSpeed * Math.sin(angle), + }); + + for (var n = 0; n < length; n++) { + dx = (mass[j].vertices[n].x - mass[j].position.x); + dy = (mass[j].vertices[n].y - mass[j].position.y); + r = Math.sqrt(dx * dx + dy * dy); + angle = Math.atan2(dy, dx); + boom.push({ + x1: mass[j].vertices[n].x, + y1: mass[j].vertices[n].y, + x2: mass[j].vertices[n + 1].x, + y2: mass[j].vertices[n + 1].y, + alpha: 1, + driftVx: mass[j].velocity.x + (Math.random() - 0.5) * driftSpeed + r * mass[j].angularSpeed * Math.cos(angle), + driftVy: mass[j].velocity.y + (Math.random() - 0.5) * driftSpeed + r * mass[j].angularSpeed * Math.sin(angle), + }); + } + + //choose to slice + if (mass[j].vertices.length > 13) { + var cut = 6 + Math.floor(Math.random() * 4); + slicePoly(j, 0, cut); + //sliceChoices(mass.length - 1); + slicePoly(j, cut - 1, mass[j].vertices.length); + //sliceChoices(mass.length - 1); + Matter.World.remove(engine.world, mass[j]); + mass.splice(j, 1); + } else { + Matter.World.remove(engine.world, mass[j]); + mass.splice(j, 1); + + } + + return; + } + } + } + //check to see if one of the collisison pairs is a bullet + var pairs = event.pairs; + for (var i = 0, j = pairs.length; i != j; ++i) { + var pair = pairs[i]; + for (var k = 0; k < bullet.length; k++) { + if (pair.bodyA === bullet[k]) { + hit(k, pair.bodyA, pair.bodyB); + repopulateMasses(); + break; + } else if (pair.bodyB === bullet[k]) { + hit(k, pair.bodyB, pair.bodyA); + repopulateMasses(); + break; + } + } + } + }); + + var boom = []; + + function explosions() { + var i = boom.length; + ctx.lineWidth = 1.5; + while (i--) { + ctx.strokeStyle = 'rgba(255, 255, 255, ' + boom[i].alpha + ')'; + //drift vector lines around + boom[i].x1 += boom[i].driftVx; + boom[i].y1 += boom[i].driftVy; + boom[i].x2 += boom[i].driftVx; + boom[i].y2 += boom[i].driftVy; + //draw vector lines + ctx.beginPath(); + ctx.moveTo(boom[i].x1, boom[i].y1); + ctx.lineTo(boom[i].x2, boom[i].y2); + ctx.stroke(); + //remove vector lines if they are too old + boom[i].alpha -= 0.03; + if (boom[i].alpha < 0.01) { + boom.splice(i, 1); + } + } + } + + //set up render + var canvas = document.createElement('canvas'), + ctx = canvas.getContext('2d'); + + //make canvas fill window + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + document.body.appendChild(canvas); + window.onresize = function(event) { + ctx.canvas.width = window.innerWidth; + ctx.canvas.height = window.innerHeight; + starsMoveRandom(); + }; + + (function cycle() { //render loop + game.cycle++; + //ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; //trails simulate a old arcade cathode look + //ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.clearRect(0, 0, canvas.width, canvas.height); //clear canvas for new cycle + bulletEndCycle(); + + damage(); + gravity(); + //background graphics + drawStars(); + + HUD(); + ctx.save(); //move camera + + ctx.translate(window.innerWidth * 0.5, window.innerHeight * 0.5); + ctx.scale(game.scale, game.scale); + controls(); + ctx.translate(-mass[0].position.x, -mass[0].position.y); + //primary graphics + render(); + drawVectors(); + explosions(); + ctx.restore(); //undo previous translation + //foreground graphics + miniMap(); + completionBar(); + durabilityBar(); + window.requestAnimationFrame(cycle); + })(); + + function render() { //draw all the objects from matter.js physics engine + var bodies = Composite.allBodies(engine.world); //draw every object + //fill and stroke each body + ctx.lineWidth = 1.5; + ctx.strokeStyle = '#ffffff'; + ctx.fillStyle = '#050505'; //'#111'; //'#0a0606'; + ctx.beginPath(); + for (var i = 0; i < bodies.length; i++) { + var vertices = bodies[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (var j = 1; j < vertices.length; j++) { + ctx.lineTo(vertices[j].x, vertices[j].y); + } + ctx.lineTo(vertices[0].x, vertices[0].y); + } + ctx.fill(); + ctx.stroke(); + } + + function drawVectors() { //render force and velocity vectors for each mass + if (game.HUD) { + var length = mass.length; + ctx.lineWidth = 1; + //velocity vector + //ctx.strokeStyle = 'rgba(0, 255, 255, 1)'; + ctx.strokeStyle = '#00ffff'; + ctx.beginPath(); + for (var i = 0; i < length; i++) { + ctx.moveTo(mass[i].position.x, mass[i].position.y); + ctx.lineTo(mass[i].position.x + 10 * mass[i].velocity.x, + mass[i].position.y + 10 * mass[i].velocity.y); + } + ctx.stroke(); + //force vector + //ctx.strokeStyle = 'rgba(255, 0, 255, 1)'; + ctx.strokeStyle = '#ff00ff'; + ctx.beginPath(); + for (i = 0; i < length; i++) { + ctx.moveTo(mass[i].position.x, mass[i].position.y); + ctx.lineTo(mass[i].position.x + 600000 * mass[i].force.x / mass[i].mass, + mass[i].position.y + 600000 * mass[i].force.y / mass[i].mass); + } + ctx.stroke(); + //angular motion vector + /* ctx.strokeStyle = 'rgba(255, 255, 00, 0.5)';//'#ff00ff'; + ctx.beginPath(); + if (mass[0].angularVelocity>0){ + ctx.arc(mass[0].position.x,mass[0].position.y,20,Math.PI*0.5,53*mass[0].angularVelocity+Math.PI*0.5); + }else{ + ctx.arc(mass[0].position.x,mass[0].position.y,20,53*mass[0].angularVelocity+Math.PI*0.5,Math.PI*0.5); + } + ctx.stroke(); */ + } + } + + function HUD() { //player data + // document.getElementById("level").innerHTML = 'system ' + game.level + if (game.HUD) { //testing and development data + document.getElementById("hud").innerHTML = ' ' + + //'' + + //'' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + '
#: ' + mass.length + '
level: ' + game.level + '
 t: ' + engine.timing.timestamp.toFixed(0) + '
 m: ' + mass[0].mass.toFixed(2) + '
 θ: ' + (Math.abs(mass[0].angle % (Math.PI * 2))).toFixed(2) + '
dθ: ' + mass[0].angularVelocity.toFixed(3) + '
 x: ' + mass[0].position.x.toFixed(0) + '
 y: ' + mass[0].position.y.toFixed(0) + '
Vx: ' + mass[0].velocity.x.toFixed(2) + '
Vy: ' + mass[0].velocity.y.toFixed(2) + '
Fx: ' + mass[0].force.x.toFixed(6) + '
Fy: ' + mass[0].force.y.toFixed(6) + '
'; + } else { + document.getElementById("hud").innerHTML = ""; + } + } + + function durabilityBar() { // player health bar + var size = 200; + var x = 6; + var y = 6; + //ctx.lineWidth = 2; + //ctx.strokeStyle = '#999'; + //ctx.strokeRect(x - 1, y - 1, size + 2, 12); //draw outline + //ctx.fillStyle = '#512'; + ctx.fillStyle = 'rgba(255, 85, 119, 0.3)' + ctx.fillRect(x, y, size, 10); //draw bar + if (mass[0].alive) { //only draw bar if player is alive + ctx.fillStyle = 'rgba(255, 85, 119, 0.9)' + ctx.fillRect(x, y, size * mass[0].durability, 10); //draw bar + } + } + + function completionBar() { + var size = 152; + var x = canvas.width - size - 4; + var y = 5; + + //ctx.fillStyle = '#000007'; + //ctx.fillRect(x, y, size, 5); //draw bar + ctx.fillStyle = 'rgba(85, 85, 170, 0.3)' + //ctx.fillStyle = '#55a'; + ctx.fillRect(x, y, size, 5); //draw bar + //ctx.fillStyle = '#55a'; + ctx.fillStyle = 'rgba(85, 85, 170, 0.9)' + ctx.fillRect(x, y, size * game.currentMass / game.startingMassValue, 5); //draw bar + //goal line + ctx.fillStyle = '#ffff00'; + ctx.fillRect(x + size * game.clearThreshold, y, 1, 5); //draw bar + //outline of bar + //ctx.lineWidth = 1; + //ctx.strokeStyle = '#88f'; + //ctx.strokeRect(x, y, size, 5); //draw outline + } + + function miniMap() { + ctx.lineWidth = 1; + ctx.strokeStyle = '#88f'; + ctx.beginPath(); + var xOff = canvas.width - 5; + var yOff = 10; + var size = 150; + var scale = size / game.width; + ctx.fillStyle = 'rgba(0, 0, 50, 0.5)' + ctx.rect(xOff, yOff, -size, size); + ctx.stroke(); + ctx.fill(); + //draw dot for masses + ctx.fillStyle = '#aaf'; + for (var i = 1; i < mass.length; i++) { + ctx.fillRect(mass[i].position.x * scale + xOff - size, mass[i].position.y * scale + yOff, 3, 3); + } + //draw player's dot + ctx.fillStyle = '#ffff00'; + ctx.fillRect(mass[0].position.x * scale + xOff - size, mass[0].position.y * scale + yOff, 5, 5); + } + + var star = []; + var totalStars = 100; + for (var i = 0; i < totalStars; i++) { + star.push({ + x: Math.random() * window.innerWidth, + y: Math.random() * window.innerHeight + }); + } + + function starsMoveRandom() { + for (var i = 0; i < totalStars; i++) { + star[i].x = Math.random() * window.innerWidth; + star[i].y = Math.random() * window.innerHeight; + } + } + + function drawStars() { + ctx.fillStyle = '#ffffff'; //'darkgrey'; //'rgba(255, 255, 255, 0.5)' + var parallax = 1; + var Vx = mass[0].velocity.x * game.scale; + var Vy = mass[0].velocity.y * game.scale; + var width = window.innerWidth; + var height = window.innerHeight; + for (var i = 0; i < totalStars; i++) { + star[i].x -= Vx; + star[i].y -= Vy; + ctx.fillRect(star[i].x, star[i].y, 1, 1); + if (star[i].x < 0) { + star[i].x = width; + star[i].y = Math.random() * height; + } + if (star[i].x > width) { + star[i].x = 0; + star[i].y = Math.random() * height; + } + if (star[i].y < 0) { + star[i].y = height; + star[i].x = Math.random() * width; + } + if (star[i].y > height) { + star[i].y = 0; + star[i].x = Math.random() * width; + } + } + } +} \ No newline at end of file diff --git a/js/player.js b/js/player.js index 47e52ec..1ec698f 100644 --- a/js/player.js +++ b/js/player.js @@ -949,12 +949,12 @@ const m = { }, setHoldDefaults() { if (tech.isFreeWormHole && m.fieldUpgrades[m.fieldMode].name !== "wormhole") { - tech.removeTech("charmed baryon") //neutronum can get player stuck so it has to be removed if player has wrong field - powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + const removed = tech.removeTech("charmed baryon") //neutronum can get player stuck so it has to be removed if player has wrong field + if (removed) powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); } if (tech.isNeutronium && m.fieldUpgrades[m.fieldMode].name !== "negative mass") { - tech.removeTech("neutronium") //neutronum can get player stuck so it has to be removed if player has wrong field - powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + const removed = tech.removeTech("neutronium") //neutronum can get player stuck so it has to be removed if player has wrong field + if (removed) powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); } if (m.energy < m.maxEnergy) m.energy = m.maxEnergy; m.fieldRegen = tech.energyRegen; //0.001 diff --git a/js/powerup.js b/js/powerup.js index ba7c022..ee114b3 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -357,7 +357,7 @@ const powerUps = { currentRerollCount: 0, use(type) { //runs when you actually research a list of selections, type can be field, gun, or tech if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { - tech.addJunkTechToPool(tech.junkResearchNumber) + tech.addJunkTechToPool(tech.junkResearchNumber * 0.01) } else { powerUps.research.changeRerolls(-1) } diff --git a/js/spawn.js b/js/spawn.js index 3ca43cc..50edca7 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -111,7 +111,7 @@ const spawn = { powerUps.research.changeRerolls(-4) simulation.makeTextLog(`m.research -= 4
${powerUps.research.count}`) } else { - tech.addJunkTechToPool(49) + tech.addJunkTechToPool(0.49) } spawn.randomLevelBoss(x, y); return true @@ -2323,11 +2323,15 @@ const spawn = { me.friction = 0; me.memory = 240 me.seePlayerFreq = 60 - me.delay = 25 + 30 * simulation.CDScale; - me.nextBlinkCycle = me.delay; me.blinkRange = 235 - me.grenadeDelay = 35 + 60 * simulation.CDScale - me.pulseRadius = 2 * Math.min(550, 220 + simulation.difficulty * 4) + if (false && 0.5 < Math.random()) { + me.grenadeDelay = 260 + } else { + me.grenadeDelay = 100 + } + me.pulseRadius = 1.4 * Math.min(550, 220 + simulation.difficulty * 4) + me.delay = 30 + 35 * simulation.CDScale; + me.nextBlinkCycle = me.delay; spawn.shield(me, x, y, 1); me.onDamage = function() { // this.cd = simulation.cycle + this.delay; diff --git a/js/tech.js b/js/tech.js index bc51e5e..65b724e 100644 --- a/js/tech.js +++ b/js/tech.js @@ -53,8 +53,8 @@ } const totalRemoved = tech.tech[index].count simulation.makeTextLog(`tech.removeTech("${tech.tech[index].name}")`) - tech.tech[index].count = 0; tech.tech[index].remove(); + tech.tech[index].count = 0; simulation.updateTechHUD(); tech.tech[index].isLost = true simulation.updateTechHUD(); @@ -73,15 +73,40 @@ // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1) // } // }, - addJunkTechToPool(num = 1) { + addJunkTechToPool(chance) { //chance is number between 0-1 + // { //count JUNK + // let count = 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].isJunk && tech.tech[i].frequency > 0) count += tech.tech[i].frequency + // } + // console.log(count) + // } + // { //count not JUNK + // let count = 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].isJunk && tech.tech[i].frequency > 0) count++ + // } + // console.log(count) + // } + // count total non junk tech + let count = 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].isJunk) count += tech.tech[i].frequency + } + //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); } + //add random array options to tech pool if (options.length) { + const num = chance * count //scale number added for (let i = 0; i < num; i++) tech.tech[options[Math.floor(Math.random() * options.length)]].frequency++ + simulation.makeTextLog(`tech.tech.push(${num} JUNK)`) + return num + } else { + return 0 } - simulation.makeTextLog(`tech.tech.push(${num} JUNK)`) }, removeJunkTechFromPool(num = 1) { for (let j = 0; j < num; j++) { @@ -506,7 +531,7 @@ { name: "desublimated ammunition", link: `desublimated ammunition`, - description: "every other crouched shot uses no ammo
+6 JUNK to the potential tech pool", + description: "every other crouched shot uses no ammo
+6% JUNK to the potential tech pool", maxCount: 1, count: 0, frequency: 1, @@ -517,32 +542,18 @@ requires: "", effect() { tech.isCrouchAmmo = true - tech.addJunkTechToPool(6) + this.refundAmount += tech.addJunkTechToPool(0.06) }, + refundAmount: 0, remove() { - tech.isCrouchAmmo = false; - if (this.count > 0) tech.removeJunkTechFromPool(6) + tech.isExtraChoice = false; + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } } + }, - // JUNK to the potential tech pool", - // maxCount: 9, - // count: 0, - // frequency: 1, - // frequencyDefault: 1, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // tech.bonusEnergy += 0.6 - // m.setMaxEnergy() - // tech.addJunkTechToPool(10) - // }, - // remove() { - // tech.bonusEnergy = 0; - // m.setMaxEnergy() - // if (this.count > 0) tech.removeJunkTechFromPool(10) - // } { name: "gun turret", description: "reduce harm by 55% when crouching", @@ -2470,7 +2481,7 @@ }, { name: "overcharge", - description: "increase your maximum energy by 60
+10 JUNK to the potential tech pool", + description: "increase your maximum energy by 60
+10% JUNK to the potential tech pool", maxCount: 9, count: 0, frequency: 1, @@ -2482,17 +2493,21 @@ effect() { tech.bonusEnergy += 0.6 m.setMaxEnergy() - tech.addJunkTechToPool(10) + this.refundAmount += tech.addJunkTechToPool(0.1) }, + refundAmount: 0, remove() { tech.bonusEnergy = 0; m.setMaxEnergy() - if (this.count > 0) tech.removeJunkTechFromPool(10) + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } } }, { name: "Maxwell's demon", - description: "energy above your max decays 92% slower
+18 JUNK to the potential tech pool", + description: "energy above your max decays 92% slower
+18% JUNK to the potential tech pool", maxCount: 1, count: 0, frequency: 2, @@ -2503,11 +2518,15 @@ requires: "energy above your max", effect() { tech.overfillDrain = 0.85 //70% = 1-(1-0.75)/(1-0.15) //92% = 1-(1-0.75)/(1-0.87) - tech.addJunkTechToPool(18) + this.refundAmount += tech.addJunkTechToPool(0.18) }, + refundAmount: 0, remove() { tech.overfillDrain = 0.7 - if (this.count > 0) tech.removeJunkTechFromPool(18) + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } } }, { @@ -3021,7 +3040,7 @@ }, { name: "pseudoscience", - description: "when selecting a power up, research 3 times
for free, but add 0-3 JUNK to the tech pool", + description: "when selecting a power up, research 3 times
for free, but add 0-3% JUNK to the tech pool", maxCount: 1, count: 0, frequency: 1, @@ -3072,7 +3091,7 @@ }, { name: "abiogenesis", - description: `at the start of a level spawn a 2nd boss
use ${powerUps.orb.research(4)}or add 49 JUNK to the tech pool`, + description: `at the start of a level spawn a 2nd boss
use ${powerUps.orb.research(4)}or add 49% JUNK to the tech pool`, maxCount: 1, count: 0, frequency: 2, @@ -3114,9 +3133,9 @@ frequency: 1, frequencyDefault: 1, allowed() { - return tech.duplicateChance + return true }, - requires: "replication", + requires: "", effect() { tech.isMetaAnalysis = true }, @@ -3126,7 +3145,7 @@ }, { name: "replication", - description: "10% chance to duplicate spawned power ups
+30 JUNK to the potential tech pool", + description: "10% chance to duplicate spawned power ups
+30% JUNK to the potential tech pool", maxCount: 9, count: 0, frequency: 1, @@ -3139,12 +3158,16 @@ tech.duplicateChance += 0.1 powerUps.setDupChance(); //needed after adjusting duplication chance if (!build.isExperimentSelection) simulation.circleFlare(0.1); - tech.addJunkTechToPool(30) + this.refundAmount += tech.addJunkTechToPool(0.3) }, + refundAmount: 0, remove() { tech.duplicateChance = 0 powerUps.setDupChance(); //needed after adjusting duplication chance - if (this.count > 1) tech.removeJunkTechFromPool(30) + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } } }, { @@ -3515,11 +3538,15 @@ requires: "not determinism", effect: () => { tech.isExtraChoice = true; - tech.addJunkTechToPool(5) + this.refundAmount += tech.addJunkTechToPool(0.05) }, + refundAmount: 0, remove() { tech.isExtraChoice = false; - if (this.count > 0) tech.removeJunkTechFromPool(5) + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } } }, { @@ -3584,7 +3611,7 @@ }, { name: "dark patterns", - description: "reduce combat difficulty by 1 level
+31 JUNK to the potential tech pool", + description: "reduce combat difficulty by 1 level
+31% JUNK to the potential tech pool", maxCount: 1, count: 0, frequency: 1, @@ -3597,12 +3624,13 @@ level.difficultyDecrease(simulation.difficultyMode) // simulation.difficulty-= simulation.makeTextLog(`level.difficultyDecrease(simulation.difficultyMode)`) - tech.addJunkTechToPool(31) + this.refundAmount += tech.addJunkTechToPool(0.31) // for (let i = 0; i < tech.junk.length; i++) tech.tech.push(tech.junk[i]) }, + refundAmount: 0, remove() { if (this.count > 0) { - tech.removeJunkTechFromPool(31) + if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) level.difficultyIncrease(simulation.difficultyMode) } } @@ -3734,9 +3762,9 @@ frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isIceCrystals && !tech.isRivets && !tech.nailRecoil + return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isIceCrystals && !tech.isRivets && !tech.nailRecoil && !tech.nailRecoil }, - requires: "nail gun, not ice crystal, rivets, or pneumatic actuator", + requires: "nail gun, not ice crystal, rivets, rotary cannon, or pneumatic actuator", effect() { tech.isNeedles = true for (i = 0, len = b.guns.length; i < len; i++) { //find which gun @@ -4582,7 +4610,7 @@ }, { name: "booby trap", - description: "drop a mine after picking up a power up
+53 JUNK to the potential tech pool", + description: "drop a mine after picking up a power up
+53% JUNK to the potential tech pool", isGunTech: true, maxCount: 1, count: 0, @@ -4595,11 +4623,15 @@ effect() { tech.isMineDrop = true; if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0) - tech.addJunkTechToPool(53) + this.refundAmount += tech.addJunkTechToPool(0.53) }, + refundAmount: 0, remove() { tech.isMineDrop = false; - if (this.count > 0) tech.removeJunkTechFromPool(53) + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } } }, { @@ -6316,7 +6348,7 @@ }, { name: "symbiosis", - description: "after a mob dies, lose 1 max health
bosses spawn 1-2 extra tech after they die", + description: "after a mob dies, lose 1 max health
bosses spawn 1 extra tech after they die", isFieldTech: true, maxCount: 1, count: 0, @@ -6890,6 +6922,43 @@ // }, // remove() {} // }, + { + name: "planetesimals", + description: `spawn a tech and play planetesimals,
an annoying asteroids game with Newtonian physics`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + window.open('../../planetesimals/index.html', '_blank') + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + }, + remove() {} + }, + { + name: "facsimile", + description: `inserts a copy of your current level into the level list`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + level.levels.splice(level.onLevel, 0, level.levels[level.onLevel]); + }, + remove() {} + }, { name: "negative friction", description: "when you touch walls you speed up instead of slowing down. It's kinda fun.", @@ -7872,7 +7941,7 @@ }, { name: "expert system", - description: "spawn a tech power up
+64 JUNK to the potential tech pool", + description: "spawn a tech power up
+64% JUNK to the potential tech pool", maxCount: 9, count: 0, frequency: 0, @@ -7885,7 +7954,7 @@ requires: "", effect() { powerUps.spawn(m.pos.x, m.pos.y, "tech"); - tech.addJunkTechToPool(64) + tech.addJunkTechToPool(0.64) }, remove() {} }, diff --git a/todo.txt b/todo.txt index 4a926b3..5ca4af8 100644 --- a/todo.txt +++ b/todo.txt @@ -1,14 +1,34 @@ ******************************************************** NEXT PATCH ************************************************** -tech: neutronium - 90% harm reduce while field is active, 33% slower move speed - (requires negative mass field) -tech: charmed baryon - 0 cost wormhole, 33% slower move speed -harpoon tech: reticulum now always fires extra harpoons even if there are no targets -tech.removeTech() method has been improved +JUNK tech: facsimile - copy current level and insert into level queue +JUNK tech: planetesimals - spawn a tech, and play planetesimals, an annoying asteroids like game with realistic physics + +tech that previously added 1 junk tech now adds 1% (of current non-JUNK tech options) + the percent values may seem like more but I actually lowered the JUNK chance about 10% + +meta-analysis no longer requires replication - (if you choose a JUNK tech get a random tech and 3 research) +symbiosis: only gives 1 bonus tech (was 1 or 2) +blink boss rebalanced (it's easier, and has two modes) + bug fixes ******************************************************** TODO ******************************************************** +fields weak in late game? + Pilot wave? + buff with cool tech options from other fields + buff with another damage scaling tech + stand wave and diamagnetism + buff end stage tech for these + + + +tech perfect diamagnetism - holding the field makes the field slowly travel forward + could be base effect + +make a field that just fires your first gun + if they have to share a cooldown give 50% shorter fire CD + bug - death while paused crashes game? tech: aerodynamic heating - railgun rods super heat the air around it doing AoE damage