From 1ca00d3598d5ce50fb3331e4b578d9d5155ba233 Mon Sep 17 00:00:00 2001 From: landgreen Date: Thu, 24 Feb 2022 05:50:39 -0800 Subject: [PATCH] grappling hook tech grappling hook - can attack to walls and pull you towards the walls harpoon extends farther as you hold down fire, but no longer has auto-steering mobs do 4% less harm per difficulty level railgun/harpoon auto-targeting is smarter at long distances with multiple small targets but it still has trouble with moving targets booby trap only has a 100 -> 50% chance to drop a mine when picking up power ups added fallback for browsers that don't allow local storage --- .DS_Store | Bin 6148 -> 6148 bytes js/bullet.js | 536 +++++++++++++++++++++++++++++++++++++++++--------- js/index.js | 72 ++++--- js/level.js | 105 ++++------ js/lore.js | 6 +- js/powerup.js | 2 +- js/spawn.js | 8 +- js/tech.js | 65 ++++-- todo.txt | 36 ++-- 9 files changed, 608 insertions(+), 222 deletions(-) diff --git a/.DS_Store b/.DS_Store index eeb07b126edeb6c11b937323062c73109d8b451d..fd8a40c7b68b09a620f2b0581eab72529aee0a5d 100644 GIT binary patch delta 22 dcmZoMXffEJ#mp3Qak37xFO%Ye&DG4EA^=lI2NeJS delta 22 dcmZoMXffEJ#mvN^K3Rv^mr0>ub2W3P2mnOp1?B(% diff --git a/js/bullet.js b/js/bullet.js index 85a5812..1ce44d4 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -1348,6 +1348,285 @@ const b = { Composite.add(engine.world, bullet[me]); //add bullet to world }, + grapple(where, angle = m.angle, isReturn = false, harpoonSize = 1) { + const me = bullet.length; + const returnRadius = 100 * Math.sqrt(harpoonSize) + bullet[me] = Bodies.fromVertices(where.x, where.y, [{ x: -40 * harpoonSize, y: 2 * harpoonSize, index: 0, isInternal: false }, { x: -40 * harpoonSize, y: -2 * harpoonSize, index: 1, isInternal: false }, { x: 50 * harpoonSize, y: -3 * harpoonSize, index: 3, isInternal: false }, { x: 30 * harpoonSize, y: 2 * harpoonSize, index: 4, isInternal: false }], { + cycle: 0, + angle: angle, + friction: 1, + frictionAir: 0.4, + thrustMag: 0.1, + dmg: 6, //damage done in addition to the damage from momentum + classType: "bullet", + endCycle: simulation.cycle + 45, + collisionFilter: { + category: cat.bullet, + mask: tech.isShieldPierce ? cat.map | cat.body | cat.mob | cat.mobBullet : cat.map | cat.body | cat.mob | cat.mobBullet | cat.mobShield, + }, + minDmgSpeed: 4, + ropeExtension: 0, + lookFrequency: Math.floor(7 + Math.random() * 3), + density: tech.harpoonDensity, //0.001 is normal for blocks, 0.006 is normal for harpoon, 0.006*6 when buffed + beforeDmg(who) { + if (tech.isShieldPierce && who.isShielded) { //disable shields + who.isShielded = false + requestAnimationFrame(() => { who.isShielded = true }); + } + if (tech.fragments) { + b.targetedNail(this.vertices[2], tech.fragments * 3) + if (!isReturn) this.endCycle = 0; + } + if (!who.isBadTarget) { + if (isReturn) { + this.do = this.returnToPlayer + } else { + this.frictionAir = 0.01 + this.do = () => { + this.force.y += this.mass * 0.003; //gravity + this.draw(); + } + } + } + }, + caughtPowerUp: null, + dropCaughtPowerUp() { + if (this.caughtPowerUp) { + this.caughtPowerUp.collisionFilter.category = cat.powerUp + this.caughtPowerUp.collisionFilter.mask = cat.map | cat.powerUp + this.caughtPowerUp = null + } + }, + onEnd() { + if (this.caughtPowerUp && !simulation.isChoosing && (this.caughtPowerUp.name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal)) { + let index = null //find index + for (let i = 0, len = powerUp.length; i < len; ++i) { + if (powerUp[i] === this.caughtPowerUp) index = i + } + if (index !== null) { + powerUps.onPickUp(this.caughtPowerUp); + this.caughtPowerUp.effect(); + Matter.Composite.remove(engine.world, this.caughtPowerUp); + powerUp.splice(index, 1); + if (tech.isHarpoonPowerUp) tech.harpoonDensity = 0.006 * 6 //0.006 is normal + } else { + this.dropCaughtPowerUp() + } + } else { + this.dropCaughtPowerUp() + } + }, + drawString() { + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const sub = Vector.sub(where, this.vertices[0]) + const controlPoint = Vector.add(where, Vector.mult(sub, -0.5)) + ctx.strokeStyle = "#000" // "#0ce" + ctx.lineWidth = 0.5 + ctx.beginPath(); + ctx.moveTo(where.x, where.y); + ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, this.vertices[0].x, this.vertices[0].y) + // ctx.lineTo(this.vertices[0].x, this.vertices[0].y); + ctx.stroke(); + }, + draw() {}, + returnToPlayer() { + if (Vector.magnitude(Vector.sub(this.position, m.pos)) < returnRadius) { //near player + this.endCycle = 0; + if (m.cycle + 25 * b.fireCDscale < m.fireCDcycle) m.fireCDcycle = m.cycle + 35 * b.fireCDscale //lower cd to 25 if it is above 25 + //recoil on catching + const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (input.down ? 0.00015 : 0.0003)) + player.force.x += momentum.x + player.force.y += momentum.y + // refund ammo + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") { + b.guns[i].ammo++; + simulation.updateGunHUD(); + break; + } + } + } else { + if (!tech.isRailEnergyGain && m.energy > 0.005) 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) + this.force.x -= returnForce.x + this.force.y -= returnForce.y + this.grabPowerUp() + } + this.draw(); + }, + grabPowerUp() { //grab power ups near the tip of the harpoon + if (this.caughtPowerUp) { + Matter.Body.setPosition(this.caughtPowerUp, Vector.add(this.vertices[2], this.velocity)) + Matter.Body.setVelocity(this.caughtPowerUp, { x: 0, y: 0 }) + } else { //&& simulation.cycle % 2 + for (let i = 0, len = powerUp.length; i < len; ++i) { + const radius = powerUp[i].circleRadius + 50 + if (Vector.magnitudeSquared(Vector.sub(this.vertices[2], powerUp[i].position)) < radius * radius) { + if (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) { + this.caughtPowerUp = powerUp[i] + Matter.Body.setVelocity(powerUp[i], { x: 0, y: 0 }) + Matter.Body.setPosition(powerUp[i], this.vertices[2]) + powerUp[i].collisionFilter.category = 0 + powerUp[i].collisionFilter.mask = 0 + this.thrustMag *= 0.6 + this.endCycle += 0.5 //it pulls back slower, so this prevents it from ending early + break //just pull 1 power up if possible + } + } + } + } + }, + do() { + this.cycle++ + if (isReturn) { + if (input.fire) { + this.grabPowerUp() + if (this.endCycle < simulation.cycle + 1) { //if at end of lifespan, but player is holding down fire, force retraction + this.endCycle = simulation.cycle + 60 + m.fireCDcycle = m.cycle + 20 // cool down + this.do = this.returnToPlayer + Matter.Body.setDensity(this, 0.0005); //reduce density on return + if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) + this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body + } + } else { + //snap rope if not enough energy + if (m.energy < 0.05) { + const returnForce = Vector.mult(Vector.normalise(Vector.sub(this.position, m.pos)), 3 * this.thrustMag * this.mass) + this.force.x -= returnForce.x + this.force.y -= returnForce.y + this.frictionAir = 0.002 + this.do = () => { + if (this.speed < 20) this.force.y += 0.0005 * this.mass; + } + this.dropCaughtPowerUp() + } else { + //return to player + this.do = this.returnToPlayer + this.endCycle = simulation.cycle + 60 + Matter.Body.setDensity(this, 0.0005); //reduce density on return + if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) + this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body + } + } + + //grappling hook + if (input.fire && Matter.Query.collides(this, map).length) { + const pull = Vector.mult(Vector.normalise(Vector.sub(this.position, m.pos)), 0.1) + player.force.x += pull.x + player.force.y += pull.y - player.mass * 0.02 + const move = { x: 50 * Math.cos(this.angle), y: 50 * Math.sin(this.angle), } + Matter.Body.setPosition(this, Vector.add(this.position, move)) + + if (Matter.Query.collides(this, map).length) { + Matter.Body.setStatic(this, true) + this.endCycle = simulation.cycle + 5 + this.dropCaughtPowerUp() + this.do = () => { + // const grappleBack = { + // x: this.position.x - 50 * Math.cos(this.angle), + // y: this.position.y - 50 * Math.sin(this.angle) + // } + + //between player nose and the grapple + const sub = Vector.sub(this.vertices[0], { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + }) + let dist = Vector.magnitude(sub) - this.ropeExtension + if (input.fire) { + m.fireCDcycle = m.cycle + 30; // cool down if out of energy + this.endCycle = simulation.cycle + 10 + + Matter.Body.setVelocity(player, { x: player.velocity.x * 0.8, y: player.velocity.y * 0.8 }); + if (input.down) { //down + dist *= 0.25 + player.force.y += 5 * player.mass * simulation.g; + } + //control position while hooked + // if (input.down) { //down + // player.force.y += 5 * player.mass * simulation.g; + // dist *= 0.25 + // this.ropeExtension += 10 + // } else if (input.up) { //up + // this.ropeExtension -= 10 + // if (this.ropeExtension < 0) this.ropeExtension = 0 + // player.force.y -= 5 * player.mass * simulation.g; + // dist *= 0.4 + // } else {} + + // if (input.right) { //down + // dist *= 0.4 + // player.force.x += 5 * player.mass * simulation.g; + // } else if (input.left) { //up + // dist *= 0.4 + // player.force.x -= 5 * player.mass * simulation.g; + // } + if (!tech.isRailEnergyGain && m.energy > 0.004 && dist > 400) m.energy -= 0.004 + const pull = Vector.mult(Vector.normalise(sub), 0.00035 * Math.min(Math.max(30, dist), 500)) + player.force.x += pull.x + player.force.y += pull.y + } else { //if (Vector.magnitude(Vector.sub(this.position, m.pos)) < returnRadius + 200) + // if (input.up) { //up + // player.force.y -= 20 * player.mass * simulation.g; + // } + //automatically get ammo back + this.endCycle = 0; + if (m.cycle + 15 * b.fireCDscale < m.fireCDcycle) m.fireCDcycle = m.cycle + 15 * b.fireCDscale //lower cd to 15 if it is above 15 + // refund ammo + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") { + b.guns[i].ammo++; + simulation.updateGunHUD(); + break; + } + } + } + // if (dist > returnRadius) + this.draw(); + } + } + } + this.force.x += this.thrustMag * this.mass * Math.cos(this.angle); + this.force.y += this.thrustMag * this.mass * Math.sin(this.angle); + this.draw() + } + }, + }); + if (!isReturn) { + Matter.Body.setVelocity(bullet[me], { + x: m.Vx / 2 + 60 * Math.cos(bullet[me].angle), + y: m.Vy / 2 + 60 * Math.sin(bullet[me].angle) + }); + bullet[me].frictionAir = 0.002 + bullet[me].do = function() { + if (this.speed < 20) this.force.y += 0.0005 * this.mass; + this.draw(); + } + } + if (tech.isHarpoonPowerUp && bullet[me].density > 0.01) { + if (isReturn) { + bullet[me].draw = function() { + this.drawToggleHarpoon() + this.drawString() + } + } else { + bullet[me].draw = function() { + this.drawToggleHarpoon() + } + } + } else if (isReturn) { + bullet[me].draw = function() { + this.drawString() + } + } + Composite.add(engine.world, bullet[me]); //add bullet to world + }, harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35) { const me = bullet.length; const returnRadius = 100 * Math.sqrt(harpoonSize) @@ -1505,42 +1784,77 @@ const b = { }, do() { this.cycle++ - if (isReturn) { - if (this.cycle > totalCycles) { - if (m.energy < 0.05) { //snap rope if not enough energy - const returnForce = Vector.mult(Vector.normalise(Vector.sub(this.position, m.pos)), 3 * this.thrustMag * this.mass) - this.force.x -= returnForce.x - this.force.y -= returnForce.y - this.frictionAir = 0.002 - this.do = () => { if (this.speed < 20) this.force.y += 0.0005 * this.mass; } - this.dropCaughtPowerUp() - } else { //return to player - this.do = this.returnToPlayer - Matter.Body.setDensity(this, 0.0005); //reduce density on return - if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) - this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body - } - } else { - this.grabPowerUp() - } - } else if (this.cycle > 30) { - this.frictionAir = 0.003 - this.do = () => { if (this.speed < 20) this.force.y += 0.0005 * this.mass; } - } - - if (target) { //rotate towards the target - const face = { - x: Math.cos(this.angle), - y: Math.sin(this.angle) - }; - const vectorGoal = Vector.normalise(Vector.sub(this.position, target.position)); - if (Vector.cross(vectorGoal, face) > 0) { - Matter.Body.rotate(this, this.turnRate); - } else { - Matter.Body.rotate(this, -this.turnRate); - } - } if (isReturn || target) { + if (isReturn) { + if (this.cycle > totalCycles) { + //snap rope if not enough energy + if (m.energy < 0.05) { + const returnForce = Vector.mult(Vector.normalise(Vector.sub(this.position, m.pos)), 3 * this.thrustMag * this.mass) + this.force.x -= returnForce.x + this.force.y -= returnForce.y + this.frictionAir = 0.002 + this.do = () => { + if (this.speed < 20) this.force.y += 0.0005 * this.mass; + } + this.dropCaughtPowerUp() + } else { + //return to player + this.do = this.returnToPlayer + Matter.Body.setDensity(this, 0.0005); //reduce density on return + if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) + this.collisionFilter.mask = cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body + } + } else { + this.grabPowerUp() + } + } + + if (target) { //rotate towards the target + const face = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + }; + const vectorGoal = Vector.normalise(Vector.sub(this.position, target.position)); + if (Vector.cross(vectorGoal, face) > 0) { + Matter.Body.rotate(this, this.turnRate); + } else { + Matter.Body.rotate(this, -this.turnRate); + } + } + //grappling hook + // if (input.fire && !input.down && Matter.Query.collides(this, map).length) { + // const pull = Vector.mult(Vector.normalise(Vector.sub(this.position, m.pos)), 0.1) + // player.force.x += pull.x + // player.force.y += pull.y - player.mass * 0.02 + // Matter.Body.setStatic(this, true) + // this.endCycle = simulation.cycle + 5 + // this.dropCaughtPowerUp() + // this.do = () => { + // const sub = Vector.sub(this.position, m.pos) + // const dist = Vector.magnitude(sub) + // if (input.fire) { + // m.fireCDcycle = m.cycle + 30; // cool down if out of energy + // this.endCycle = simulation.cycle + 10 + // const pull = Vector.mult(Vector.normalise(sub), 0.001 * Math.min(Math.max(1, dist), 100)) + // player.force.x += pull.x + // player.force.y += pull.y + // Matter.Body.setVelocity(player, { x: player.velocity.x * 0.8, y: player.velocity.y * 0.8 }); + // } else { //if (Vector.magnitude(Vector.sub(this.position, m.pos)) < returnRadius + 200) + // //automatically get ammo back + // this.endCycle = 0; + // if (m.cycle + 15 * b.fireCDscale < m.fireCDcycle) m.fireCDcycle = m.cycle + 15 * b.fireCDscale //lower cd to 15 if it is above 15 + // // refund ammo + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // if (b.guns[i].name === "harpoon") { + // b.guns[i].ammo++; + // simulation.updateGunHUD(); + // break; + // } + // } + // } + // if (dist > returnRadius) this.draw(); + // } + // } this.force.x += this.thrustMag * this.mass * Math.cos(this.angle); this.force.y += this.thrustMag * this.mass * Math.sin(this.angle); } @@ -2118,7 +2432,7 @@ const b = { dmg: 0, // 0.14 //damage done in addition to the damage from momentum minDmgSpeed: 2, lookFrequency: 67 + Math.floor(7 * Math.random()), - drain: 0.62 * tech.isLaserDiode * tech.laserFieldDrain, + drain: 0.7 * tech.isLaserDiode * tech.laserFieldDrain, isDetonated: false, torqueMagnitude: 0.000003 * (Math.round(Math.random()) ? 1 : -1), range: 1500, @@ -5615,6 +5929,9 @@ const b = { if (tech.isRailGun) { this.do = this.railDo this.fire = this.railFire + } else if (tech.isGrapple) { + this.do = () => {} + this.fire = this.grappleFire } else { this.do = () => {} this.fire = this.harpoonFire @@ -5643,57 +5960,6 @@ const b = { distance: 10000, target: null } - const harpoonSize = tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1 - - if (tech.extraHarpoons) { - let targetCount = 0 - const SPREAD = 0.06 + 0.05 * (!input.down) - let angle = m.angle - SPREAD * tech.extraHarpoons / 2; - const dir = { x: Math.cos(angle), y: Math.sin(angle) }; //make a vector for the player's direction of length 1; used in dot product - - for (let i = 0, len = mob.length; i < len; ++i) { - if (mob[i].alive && !mob[i].isBadTarget && !mob[i].shield && Matter.Query.ray(map, m.pos, mob[i].position).length === 0) { - const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors - if (dot > 0.7) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target - if (this.ammo > 0) { - this.ammo-- - b.harpoon(where, mob[i], angle, harpoonSize, false) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 }) - angle += SPREAD - targetCount++ - if (targetCount > tech.extraHarpoons) break - } - } - } - } - //if more harpoons and no targets left - if (targetCount < tech.extraHarpoons + 1) { - const num = tech.extraHarpoons + 1 - targetCount - for (let i = 0; i < num; i++) { - if (this.ammo > 0) { - this.ammo-- - b.harpoon(where, null, angle, harpoonSize, false) - angle += SPREAD - } - } - } - this.ammo++ //make up for the ammo used up in fire() - simulation.updateGunHUD(); - } else { - //look for closest mob in player's LoS - const dir = { x: Math.cos(m.angle), y: Math.sin(m.angle) }; //make a vector for the player's direction of length 1; used in dot product - for (let i = 0, len = mob.length; i < len; ++i) { - if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0) { - const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors - const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) - if (dist < closest.distance && dot > 0.88) { //target closest mob that player is looking at and isn't too close to target - closest.distance = dist - closest.target = mob[i] - } - } - } - b.harpoon(where, closest.target, m.angle, harpoonSize, false) - } - //push away blocks and mobs const range = 1200 * this.charge for (let i = 0, len = mob.length; i < len; ++i) { //push away mobs when firing @@ -5731,6 +5997,57 @@ const b = { player.force.x -= recoil.x player.force.y -= recoil.y tech.harpoonDensity = 0.006 //0.001 is normal for blocks, 0.006 is normal for harpoon, 0.006*6 when buffed + const harpoonSize = tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1 + if (tech.extraHarpoons) { + let targetCount = 0 + const SPREAD = 0.06 + 0.05 * (!input.down) + let angle = m.angle - SPREAD * tech.extraHarpoons / 2; + const dir = { x: Math.cos(angle), y: Math.sin(angle) }; //make a vector for the player's direction of length 1; used in dot product + + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && !mob[i].shield && Matter.Query.ray(map, m.pos, mob[i].position).length === 0) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + // console.log(dot, 0.95 - Math.min(dist * 0.00015, 0.3)) + if (dot > 0.95 - Math.min(dist * 0.00015, 0.3)) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target + if (this.ammo > -1) { + this.ammo-- + b.harpoon(where, mob[i], angle, harpoonSize, false) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 }) + angle += SPREAD + targetCount++ + if (targetCount > tech.extraHarpoons) break + } + } + } + } + //if more harpoons and no targets left + if (targetCount < tech.extraHarpoons + 1) { + const num = tech.extraHarpoons + 1 - targetCount + for (let i = 0; i < num; i++) { + if (this.ammo > -1) { + this.ammo-- + b.harpoon(where, null, angle, harpoonSize, false) + angle += SPREAD + } + } + } + this.ammo++ //make up for the ammo used up in fire() + simulation.updateGunHUD(); + } else { + //look for closest mob in player's LoS + const dir = { x: Math.cos(m.angle), y: Math.sin(m.angle) }; //make a vector for the player's direction of length 1; used in dot product + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0) { + const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors + const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) + if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target + closest.distance = dist + closest.target = mob[i] + } + } + } + b.harpoon(where, closest.target, m.angle, harpoonSize, false) + } this.charge = 0; } else { //charging @@ -5784,6 +6101,31 @@ const b = { m.fireCDcycle = m.cycle + 10 //can't fire until mouse is released this.charge += 0.00001 }, + grappleFire() { + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + const harpoonSize = (tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1) //* (input.down ? 0.7 : 1) + if (tech.extraHarpoons && !input.down) { //multiple harpoons + const SPREAD = 0.06 + const len = tech.extraHarpoons + 1 + let angle = m.angle - SPREAD * len / 2; + for (let i = 0; i < len; i++) { + if (this.ammo > 0) { + this.ammo-- + b.grapple(where, angle, true, harpoonSize) + angle += SPREAD + } + } + this.ammo++ //make up for the ammo used up in fire() + simulation.updateGunHUD(); + m.fireCDcycle = m.cycle + 90 // cool down + } else { + b.grapple(where, m.angle, !input.down, harpoonSize) + } + m.fireCDcycle = m.cycle + 45 // cool down + }, harpoonFire() { const where = { x: m.pos.x + 30 * Math.cos(m.angle), @@ -5796,18 +6138,18 @@ const b = { //look for closest mob in player's LoS const harpoonSize = (tech.isLargeHarpoon ? 1 + 0.1 * Math.sqrt(this.ammo) : 1) //* (input.down ? 0.7 : 1) const totalCycles = 7 * (tech.isFilament ? 1 + 0.01 * Math.min(110, this.ammo) : 1) * Math.sqrt(harpoonSize) - const SPREAD = 0.1 - let angle = m.angle - SPREAD * tech.extraHarpoons / 2; - const dir = { x: Math.cos(angle), y: Math.sin(angle) }; //make a vector for the player's direction of length 1; used in dot product - if (tech.extraHarpoons && !input.down) { + if (tech.extraHarpoons && !input.down) { //multiple harpoons + const SPREAD = 0.1 + let angle = m.angle - SPREAD * tech.extraHarpoons / 2; + const dir = { x: Math.cos(angle), y: Math.sin(angle) }; //make a vector for the player's direction of length 1; used in dot product 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) { if (mob[i].alive && !mob[i].isBadTarget && !mob[i].shield && Matter.Query.ray(map, m.pos, mob[i].position).length === 0) { const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) - if (dist < range && dot > 0.7) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target + if (dist < range && dot > 0.9) { //lower dot product threshold for targeting then if you only have one harpoon //target closest mob that player is looking at and isn't too close to target if (this.ammo > 0) { this.ammo-- b.harpoon(where, mob[i], angle, harpoonSize, true, totalCycles) //Vector.angle(Vector.sub(where, mob[i].position), { x: 0, y: 0 }) @@ -5833,17 +6175,23 @@ const b = { simulation.updateGunHUD(); m.fireCDcycle = m.cycle + 90 // cool down } else { + //single harpoon + const dir = { x: Math.cos(m.angle), y: Math.sin(m.angle) }; //make a vector for the player's direction of length 1; used in dot product for (let i = 0, len = mob.length; i < len; ++i) { if (mob[i].alive && !mob[i].isBadTarget && Matter.Query.ray(map, m.pos, mob[i].position).length === 0) { const dot = Vector.dot(dir, Vector.normalise(Vector.sub(mob[i].position, m.pos))) //the dot product of diff and dir will return how much over lap between the vectors const dist = Vector.magnitude(Vector.sub(where, mob[i].position)) - if (dist < closest.distance && dot > 0.88) { //target closest mob that player is looking at and isn't too close to target + if (dist < closest.distance && dot > 0.98 - Math.min(dist * 0.00014, 0.3)) { //target closest mob that player is looking at and isn't too close to target closest.distance = dist closest.target = mob[i] } } } - b.harpoon(where, closest.target, m.angle, harpoonSize, !input.down, totalCycles) + if (input.down) { + b.harpoon(where, closest.target, m.angle, harpoonSize, false, 70) + } else { + b.harpoon(where, closest.target, m.angle, harpoonSize, true, totalCycles) + } m.fireCDcycle = m.cycle + 45 // cool down } const recoil = Vector.mult(Vector.normalise(Vector.sub(where, m.pos)), input.down ? 0.015 : 0.035) diff --git a/js/index.js b/js/index.js index 99eb0a9..09f1b47 100644 --- a/js/index.js +++ b/js/index.js @@ -4,14 +4,10 @@ Math.hash = s => { for (var i = 0, h = 9; i < s.length;) h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9); return h ^ h >>> 9 } // const date1 = new Date() -// Math.seed = date1.getUTCDate() * date1.getUTCFullYear(); // daily seed, day + year -// Math.seed = Date.now() //random every time: just the time in seconds UTC -// Math.seed = Math.abs(Math.hash(String(Date.now()))) //update randomizer seed in case the player changed it +// console.log(date1.getUTCHours()) +// document.getElementById("seed").placeholder = Math.initialSeed = String(date1.getUTCDate() * date1.getUTCFullYear()) // daily seed, day + year -// document.getElementById("seed").placeholder = Math.seed = Math.initialSeed = Math.floor(Date.now() % 100000) //random every time: just the time in milliseconds UTC - - -document.getElementById("seed").placeholder = Math.initialSeed = String(Math.floor(Date.now() % 100000)) +document.getElementById("seed").placeholder = Math.initialSeed = Math.floor(Date.now() % 100000) //random every time: just the time in milliseconds UTC Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it Math.seededRandom = function(min = 0, max = 1) { // in order to work 'Math.seed' must NOT be undefined Math.seed = (Math.seed * 9301 + 49297) % 233280; @@ -400,15 +396,12 @@ ${simulation.isCheating ? "

lore disabled": ""} //update tech text //disable not allowed tech for (let i = 0, len = tech.tech.length; i < len; i++) { const techID = document.getElementById("tech-" + i) - if (!tech.tech[i].isExperimentHide && (!tech.tech[i].isNonRefundable || tech.tech[i].isExperimentalMode || (localSettings.isJunkExperiment && tech.tech[i].isJunk))) { + if (!tech.tech[i].isExperimentHide && !tech.tech[i].isNonRefundable && (!tech.tech[i].isJunk || tech.tech[i].isExperimentalMode || localSettings.isJunkExperiment)) { if (tech.tech[i].allowed() || isAllowed || tech.tech[i].count > 0) { - // console.log(tech.tech[i].name, isAllowed, tech.tech[i].count, tech.haveGunCheck("nail gun")) const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; - //
if (tech.tech[i].isFieldTech) { techID.classList.remove('experiment-grid-hide'); - techID.innerHTML = `
@@ -430,8 +423,7 @@ ${simulation.isCheating ? "

lore disabled": ""}
        ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description} ` - } else - if (tech.tech[i].isJunk) { + } else if (tech.tech[i].isJunk) { techID.innerHTML = `
  ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` } else if (tech.tech[i].isExperimentalMode) { techID.innerHTML = `
${tech.tech[i].name}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}` @@ -525,7 +517,7 @@ ${simulation.isCheating ? "

lore disabled": ""} lore.setTechGoal() localSettings.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-experiment").value - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); //add tooltips for (let i = 0, len = tech.tech.length; i < len; i++) { @@ -756,7 +748,7 @@ const input = { document.getElementById("splash-previous-gun").innerHTML = cleanText(input.key.previousGun)[0] localSettings.key = input.key - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }, focus: null, setTextFocus() { @@ -1061,7 +1053,12 @@ window.addEventListener("keydown", function(event) { break case "h": // m.health = Infinity - m.immuneCycle = Infinity //you can't take damage + if (m.immuneCycle === Infinity) { + m.immuneCycle = 0 //you can't take damage + } else { + m.immuneCycle = Infinity //you can't take damage + } + // m.energy = Infinity // document.getElementById("health").style.display = "none" // document.getElementById("health-bg").style.display = "none" @@ -1175,8 +1172,38 @@ document.body.addEventListener("wheel", (e) => { //********************************************************************** // local storage //********************************************************************** -let localSettings = JSON.parse(localStorage.getItem("localSettings")); -if (localSettings) { +let localSettings + +function localstorageCheck() { + try { + return 'localStorage' in window && window['localStorage'] !== null; + } catch (e) { + return false; + } + +} +if (localstorageCheck()) { + localSettings = JSON.parse(localStorage.getItem("localSettings")) + if (localSettings) { + localSettings.isAllowed = true + localSettings.isEmpty = false + console.log('localStorage is enabled') + } else { + console.log('localStorage is enabled, local settings empty') + localSettings = { + isAllowed: true, + isEmpty: true + } + } +} else { + localSettings = { isAllowed: false } + console.log("localStorage is disabled") +} + + +if (localSettings.isAllowed && !localSettings.isEmpty) { + console.log('restoring previous settings') + if (localSettings.key) { input.key = localSettings.key } else { @@ -1200,6 +1227,7 @@ if (localSettings) { } document.getElementById("fps-select").value = localSettings.fpsCapDefault } else { + console.log('setting default localSettings') localSettings = { isJunkExperiment: false, isCommunityMaps: false, @@ -1213,7 +1241,7 @@ if (localSettings) { key: undefined }; input.setDefault() - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage document.getElementById("community-maps").checked = localSettings.isCommunityMaps simulation.isCommunityMaps = localSettings.isCommunityMaps document.getElementById("difficulty-select").value = localSettings.difficultyMode @@ -1235,13 +1263,13 @@ document.getElementById("fps-select").addEventListener("input", () => { simulation.fpsCapDefault = Number(value) } localSettings.fpsCapDefault = value - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); document.getElementById("community-maps").addEventListener("input", () => { simulation.isCommunityMaps = document.getElementById("community-maps").checked localSettings.isCommunityMaps = simulation.isCommunityMaps - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); // difficulty-select-experiment event listener is set in build.makeGrid @@ -1250,7 +1278,7 @@ document.getElementById("difficulty-select").addEventListener("input", () => { lore.setTechGoal() localSettings.difficultyMode = simulation.difficultyMode localSettings.levelsClearedLastGame = 0 //after changing difficulty, reset run history - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); diff --git a/js/level.js b/js/level.js index 2fd3608..9a1ea23 100644 --- a/js/level.js +++ b/js/level.js @@ -17,11 +17,9 @@ const level = { // simulation.isHorizontalFlipped = true // m.setField("time dilation") // b.giveGuns("harpoon") - // tech.giveTech("unaaq") - // tech.giveTech("railgun") - // tech.giveTech("capacitor bank") - // tech.giveTech("half-wave rectifier") - // for (let i = 0; i < 3; i++) tech.giveTech("reticulum") + // for (let i = 0; i < 9; i++) tech.giveTech("smelting") + // tech.giveTech("UHMWPE") + // tech.giveTech("grappling hook") // for (let i = 0; i < 2; i++) powerUps.directSpawn(0, 0, "tech"); // for (let i = 0; i < 3; i++) tech.giveTech("undefined") // for (let i = 10; i < tech.tech.length; i++) { tech.tech[i].isBanished = true } @@ -34,7 +32,7 @@ const level = { // simulation.enableConstructMode() //used to build maps in testing mode // level.reactor(); // level.testing(); //not in rotation, used for testing - + // level.run() if (simulation.isTraining) { level.walk(); } else { level.intro(); } // powerUps.research.changeRerolls(3000) @@ -43,9 +41,11 @@ const level = { // lore.techCount = 3 // simulation.isCheating = false //true; // localSettings.loreCount = 2; //this sets what conversation is heard - // localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + // if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage // level.onLevel = -1 //this sets level.levels[level.onLevel] = undefined which is required to run the conversation // level.null() + // lore.unlockTesting(); + // tech.giveTech("tinker"); //show junk tech in experiment mode } else { spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns // spawn.pickList = ["focuser", "focuser"] @@ -53,7 +53,7 @@ const level = { if (!simulation.isCheating && !build.isExperimentRun && !simulation.isTraining) { localSettings.runCount += level.levelsCleared //track the number of total runs locally localSettings.levelsClearedLastGame = level.levelsCleared - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } } if (!simulation.isTraining) level.levelAnnounce(); @@ -128,7 +128,7 @@ const level = { m.dmgScale = 1; //damage done by player decreases each level simulation.accelScale = 1 //mob acceleration increases each level simulation.CDScale = 1 //mob CD time decreases each level - simulation.dmgScale = Math.max(0.1, 0.35 * simulation.difficulty) //damage done by mobs scales with total levels + simulation.dmgScale = Math.max(0.1, 0.34 * simulation.difficulty) //damage done by mobs scales with total levels simulation.healScale = 1 / (1 + simulation.difficulty * 0.055) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; }, difficultyIncrease(num = 1) { @@ -138,7 +138,7 @@ const level = { if (simulation.accelScale < 6) simulation.accelScale *= 1.025 //mob acceleration increases each level if (simulation.CDScale > 0.15) simulation.CDScale *= 0.965 //mob CD time decreases each level } - simulation.dmgScale = Math.max(0.1, 0.35 * simulation.difficulty) //damage done by mobs scales with total levels + simulation.dmgScale = Math.max(0.1, 0.34 * simulation.difficulty) //damage done by mobs scales with total levels simulation.healScale = 1 / (1 + simulation.difficulty * 0.055) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; // console.log(`CD = ${simulation.CDScale}`) }, @@ -2385,7 +2385,7 @@ const level = { lore.sentence = 0 //what part of the conversation to start on lore.conversation[lore.chapter][lore.sentence]() localSettings.loreCount++ //hear the next conversation next time you win - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } // const hazardSlime = level.hazard(-1800, 150, 3600, 650, 0.004, "hsla(160, 100%, 35%,0.75)") @@ -2488,63 +2488,39 @@ const level = { testing() { const button = level.button(1000, 0) spawn.bodyRect(1000, -50, 50, 50); - - // const toggle = level.toggle(200, -700) level.custom = () => { - // button.draw(); ctx.fillStyle = "rgba(0,255,255,0.1)"; ctx.fillRect(6400, -550, 300, 350); level.exit.drawAndCheck(); - level.enter.draw(); }; level.customTopLayer = () => { button.query(); button.draw(); - vanish1.query(); - // vanish2.query(); - // vanish3.query(); - // vanish4.query(); - // vanish5.query(); }; - const vanish1 = level.vanish(1400, -200, 200, 50) //x, y, width, height, hide = { x: 0, y: 0 } //hide should just be somewhere behind the map so the player can't see it - // const vanish2 = level.vanish(1825, -150, 150, 150) //x, y, width, height, hide = { x: 0, y: 0 } //hide should just be somewhere behind the map so the player can't see it - // const vanish3 = level.vanish(1975, -150, 150, 150) //x, y, width, height, hide = { x: 0, y: 0 } //hide should just be somewhere behind the map so the player can't see it - // const vanish4 = level.vanish(1825, -300, 150, 150) //x, y, width, height, hide = { x: 0, y: 0 } //hide should just be somewhere behind the map so the player can't see it - // const vanish5 = level.vanish(1975, -300, 150, 150) //x, y, width, height, hide = { x: 0, y: 0 } //hide should just be somewhere behind the map so the player can't see it level.setPosToSpawn(0, -450); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); - level.exit.x = 200 //6500; - level.exit.y = -430; + level.exit.x = 6500; + level.exit.y = -230; // level.difficultyIncrease(14); //hard mode level 7 level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = color.background //"#ddd"; - // simulation.draw.mapFill = "#444" - // simulation.draw.bodyFill = "rgba(140,140,140,0.85)" - // simulation.draw.bodyStroke = "#222" - // level.addZone(level.exit.x, level.exit.y, 100, 30, "nextLevel"); - spawn.mapRect(-950, 0, 8200, 800); //ground spawn.mapRect(-950, -1200, 800, 1400); //left wall spawn.mapRect(-950, -1800, 8200, 800); //roof spawn.mapRect(-250, -400, 1000, 600); // shelf spawn.mapRect(-250, -1200, 1000, 550); // shelf roof - // powerUps.spawnStartingPowerUps(600, -800); - // for (let i = 0; i < 50; ++i) powerUps.spawn(550, -800, "research", false); - // powerUps.spawn(350, -800, "gun", false); + // for (let i = 0; i < 10; ++i) powerUps.spawn(550, -800, "ammo", false); function blockDoor(x, y, blockSize = 58) { spawn.mapRect(x, y - 290, 40, 60); // door lip spawn.mapRect(x, y, 40, 50); // door lip for (let i = 0; i < 4; ++i) spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize); } - // blockDoor(710, -710); - // for (let i = 0; i < 200; i++) powerUps.directSpawn(710 + 1000 * Math.random(), -710 + 1000 * Math.random(), "tech"); spawn.mapRect(2500, -1200, 200, 750); //right wall - // blockDoor(2585, -210) spawn.mapRect(2500, -200, 200, 300); //right wall spawn.mapRect(4500, -1200, 200, 650); //right wall blockDoor(4585, -310) @@ -2566,10 +2542,11 @@ const level = { m.addHealth(Infinity) // spawn.starter(1900, -500, 200) //big boy + // for (let i = 0; i < 10; ++i) spawn.hopper(1900, -500) // spawn.slashBoss(1900, -500) // spawn.launcherBoss(3200, -500) // spawn.laserTargetingBoss(1700, -500) - spawn.powerUpBoss(1900, -500) + // spawn.powerUpBoss(1900, -500) // spawn.powerUpBossBaby(3200, -500) // spawn.snakeBoss(1700, -500) // spawn.streamBoss(3200, -500) @@ -2928,7 +2905,7 @@ const level = { simulation.makeTextLog(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`); simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") //simulation superposition}`); localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } } spawn.mapRect(2025, 0, 150, 50); //lid to floor hole @@ -7086,8 +7063,8 @@ const level = { //Boss Spawning if (simulation.difficulty > 3) { spawn.randomLevelBoss(6000, 700, ["pulsarBoss", "laserTargetingBoss", "powerUpBoss", "bomberBoss", "historyBoss", "orbitalBoss"]); - if (simulation.difficulty > 10) spawn.shieldingBoss(7200, 500); - if (simulation.difficulty > 20) spawn.randomLevelBoss(2000, 300, ["historyBoss", "shooterBoss"]); + // if (simulation.difficulty > 10) spawn.shieldingBoss(7200, 500); + // if (simulation.difficulty > 20) spawn.randomLevelBoss(2000, 300, ["historyBoss", "shooterBoss"]); } //Blocks @@ -7101,15 +7078,15 @@ const level = { //Powerups powerUps.spawnStartingPowerUps(1250, 1500); - powerUps.spawnStartingPowerUps(1500, 1500); - powerUps.spawn(8650, -200, "ammo"); - powerUps.spawn(8650, -200, "ammo"); - powerUps.spawn(8650, -200, "ammo"); + // powerUps.spawnStartingPowerUps(1500, 1500); powerUps.spawn(8650, -200, "ammo"); + // powerUps.spawn(8650, -200, "ammo"); + // powerUps.spawn(8650, -200, "ammo"); + // powerUps.spawn(8650, -200, "ammo"); powerUps.spawn(200, 50, "heal"); - powerUps.spawn(200, 50, "ammo"); - powerUps.spawn(200, 50, "ammo"); - powerUps.spawn(200, 50, "ammo"); + // powerUps.spawn(200, 50, "ammo"); + // powerUps.spawn(200, 50, "ammo"); + // powerUps.spawn(200, 50, "ammo"); powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(6600, 600) @@ -8431,7 +8408,7 @@ const level = { const hazard = level.hazard(2300, -3090, 1700, 110, 0.005); let isButtonTapped = false; - if (b.inventory.length < 5) powerUps.spawn(3800, -3200, "gun"); + // if (b.inventory.length < 5) powerUps.spawn(3800, -3200, "gun"); powerUps.spawn(3900, -3100, "heal", true, null, 30); powerUps.spawn(3900, -3100, "heal", true, null, 30); @@ -8519,7 +8496,7 @@ const level = { spawn.shieldingBoss(3900, -3200, 70); let randomBoss = Math.floor(Math.random() * 2); - if (simulation.difficulty > 5) spawn[["shooterBoss", "launcherBoss"][randomBoss]](7500, -150); + if (simulation.difficulty > 5) spawn[["shooterBoss", "launcherBoss"][randomBoss]](7500, -150, 100, false); else spawn[["shooter", "launcher"][randomBoss]](7500, -150, 150); spawn[["shooter", "launcher"][randomBoss]](8500, -150, 150); @@ -8913,14 +8890,14 @@ const level = { } powerUps.spawnRandomPowerUp(1700, -700); - if (simulation.difficulty > 20) { - powerUps.spawn(4600, -700, "tech"); - } + // if (simulation.difficulty > 20) { + // powerUps.spawn(4600, -700, "tech"); + // } powerUps.spawnRandomPowerUp(4700, -700); - if (simulation.difficulty > 30) { - powerUps.spawn(6800, -1000, "tech"); - } + // if (simulation.difficulty > 30) { + // powerUps.spawn(6800, -1000, "tech"); + // } powerUps.spawnRandomPowerUp(6900, -1000); powerUps.spawn(9200, -5400, "tech"); @@ -8928,12 +8905,12 @@ const level = { if (simulation.difficulty > 10) { powerUps.spawn(9200, -5500, "tech"); } - if (simulation.difficulty > 20) { - powerUps.spawn(9200, -5600, "tech"); - } - if (simulation.difficulty > 30) { - powerUps.spawn(9200, -5700, "tech"); - } + // if (simulation.difficulty > 20) { + // powerUps.spawn(9200, -5600, "tech"); + // } + // if (simulation.difficulty > 30) { + // powerUps.spawn(9200, -5700, "tech"); + // } powerUps.addResearchToLevel() //needs to run after mobs are spawned initialSpawn == true; } @@ -8996,7 +8973,7 @@ const level = { crouch() { //learn to crouch if (localSettings.isTrainingNotAttempted) { //after making it to the second training level localSettings.isTrainingNotAttempted = false // this makes the training button less obvious at the start screen - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } m.addHealth(Infinity) diff --git a/js/lore.js b/js/lore.js index 5256903..11b765f 100644 --- a/js/lore.js +++ b/js/lore.js @@ -43,7 +43,7 @@ const lore = { }, unlockTesting() { if (localSettings.loreCount < 1) localSettings.loreCount = 1 - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" // document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" simulation.makeTextLog(`lore.unlockTesting()`, Infinity); @@ -406,13 +406,13 @@ const lore = { lore.sentence-- lore.conversation[lore.chapter].splice(lore.sentence + 1, 1, () => { lore.anand.text("Not a human, maybe it's an artificial intelligence?") }) localSettings.isHuman = false - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } else if (input.up) { lore.anand.text("It jumped: so YES") lore.sentence-- lore.conversation[lore.chapter].splice(lore.sentence + 1, 1, () => { lore.anand.text("So you're just a regular human playing a video game!") }) localSettings.isHuman = true - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } else if (m.alive) { requestAnimationFrame(cycle); } diff --git a/js/powerup.js b/js/powerup.js index c4679eb..54d4fdc 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -872,7 +872,7 @@ const powerUps = { powerUps.research.currentRerollCount = 0 if (tech.isTechDamage && who.name === "tech") m.damage(0.11) if (tech.isMassEnergy) m.energy += 2; - if (tech.isMineDrop && bullet.length < 150) { + if (tech.isMineDrop && bullet.length < 150 && Math.random() < 0.66) { if (tech.isLaserMine && input.down) { b.laserMine(who.position) } else { diff --git a/js/spawn.js b/js/spawn.js index a4c3098..1c0e616 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -4399,7 +4399,7 @@ const spawn = { this.fire(); }; }, - shooterBoss(x, y, radius = 110) { + shooterBoss(x, y, radius = 110, isSpawnBossPowerUp = true) { mobs.spawn(x, y, 3, radius, "rgb(255,70,180)"); let me = mob[mob.length - 1]; setTimeout(() => { //fix mob in place, but allow rotation @@ -4440,7 +4440,7 @@ const spawn = { }, 100); //have to wait a sec so the tether constraint doesn't attach to an orbital Matter.Body.setDensity(me, 0.008 + 0.0003 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger me.onDeath = function() { - powerUps.spawnBossPowerUp(this.position.x, this.position.y) + if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed }; @@ -4746,7 +4746,7 @@ const spawn = { } }; }, - launcherBoss(x, y, radius = 90) { + launcherBoss(x, y, radius = 90, isSpawnBossPowerUp = true) { mobs.spawn(x, y, 6, radius, "rgb(150,150,255)"); let me = mob[mob.length - 1]; me.isBoss = true; @@ -4763,7 +4763,7 @@ const spawn = { Matter.Body.setDensity(me, 0.0022 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger me.onDeath = function() { - powerUps.spawnBossPowerUp(this.position.x, this.position.y) + if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed }; me.onDamage = function() {}; diff --git a/js/tech.js b/js/tech.js index 769bc5b..0f5b619 100644 --- a/js/tech.js +++ b/js/tech.js @@ -657,9 +657,9 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return !m.isShipMode && !tech.isAlwaysFire + return !m.isShipMode && !tech.isAlwaysFire, !tech.isGrapple }, - requires: "not ship mode, not automatic", + requires: "not ship mode, not automatic, grappling hook", effect: () => { tech.isFireMoveLock = true; b.setFireCD(); @@ -4831,7 +4831,7 @@ const tech = { }, { name: "booby trap", - description: "drop a mine after picking up a power up
+53% JUNK to the potential tech pool", + description: "50% chance to drop a mine from power ups
+50% JUNK to the potential tech pool", isGunTech: true, maxCount: 1, count: 0, @@ -4844,7 +4844,7 @@ const tech = { effect() { tech.isMineDrop = true; if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0) - this.refundAmount += tech.addJunkTechToPool(0.53) + this.refundAmount += tech.addJunkTechToPool(0.5) }, refundAmount: 0, remove() { @@ -5479,16 +5479,16 @@ const tech = { }, { name: "railgun", - description: `harpoons are 50% denser, but don't retract
gain 500% more harpoon ammo per ${powerUps.orb.ammo(1)}`, + description: `harpoons are 50% denser, but don't retract
gain 500% more harpoon ammo per ${powerUps.orb.ammo(1)}`, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp + return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isGrapple }, - requires: "harpoon, not filament, toggling harpoon", + requires: "harpoon, not filament, toggling harpoon, grappling hook", ammoBonus: 5, effect() { tech.isRailGun = true; @@ -5555,9 +5555,36 @@ const tech = { // tech.isRodAreaDamage = false; // } // }, + { + name: "grappling hook", + description: `harpoons attach to the map and pull you in
rope extends much farther while you hold fire`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isRailGun && !tech.isFireMoveLock + }, + requires: "harpoon, not railgun, filament, toggling harpoon, Higgs mechanism", + effect() { + tech.isGrapple = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.isGrapple) { + tech.isGrapple = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() + } + } + } + }, { name: "alternator", - description: "harpoon drains no energy
railgun generates energy", //as they retract
crouch firing harpoon generates energy", + description: "harpoon and grappling hook drain no energy
railgun generates energy", //as they retract
crouch firing harpoon generates energy", isGunTech: true, maxCount: 1, count: 0, @@ -5615,8 +5642,8 @@ const tech = { { name: "smelting", // description: `spend ${powerUps.orb.ammo(2)}to upgrade the harpoon
fire +1 harpoon with each shot`, - // description: `forge ${Math.ceil(0.6*(tech.isRailGun? 5: 1))} ammo into a new harpoon
fire +1 harpoon with each shot`, - descriptionFunction() { return `forge ${tech.isRailGun? 10: 2} ammo into a new harpoon
fire +1 harpoon with each shot` }, + description: `forge 2 ammo into a new harpoon
fire +1 harpoon with each shot`, + // descriptionFunction() { return `forge ${tech.isRailGun? 10: 2} ammo into a new harpoon
fire +1 harpoon with each shot` }, isGunTech: true, maxCount: 9, count: 0, @@ -5629,7 +5656,7 @@ const tech = { effect() { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "harpoon") { - b.guns[i].ammo -= tech.isRailGun ? 10 : 2 + b.guns[i].ammo -= 2 if (b.guns[i].ammo < 0) b.guns[i].ammo = 0 simulation.updateGunHUD(); tech.extraHarpoons++; @@ -5641,7 +5668,7 @@ const tech = { if (tech.extraHarpoons) { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "harpoon") { - b.guns[i].ammo += Math.ceil(b.guns[i].ammoPack) * 2 * tech.extraHarpoons + b.guns[i].ammo += 2 simulation.updateGunHUD(); break } @@ -5659,9 +5686,9 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("harpoon") && !tech.isRailGun + return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple }, - requires: "harpoon", + requires: "harpoon, not grappling hook, railgun", effect() { tech.isFilament = true; }, @@ -5678,9 +5705,9 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return tech.haveGunCheck("harpoon") && !tech.isRailGun + return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple }, - requires: "harpoon", + requires: "harpoon, not grappling hook, railgun", effect() { tech.isHarpoonPowerUp = true }, @@ -7409,8 +7436,9 @@ const tech = { }, requires: "", effect() { + console.log('hi') localSettings.isJunkExperiment = true - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }, remove() {} }, @@ -9382,5 +9410,6 @@ const tech = { extraSuperBalls: null, isTimeCrystals: null, isGroundState: null, - isRailGun: null + isRailGun: null, + isGrapple: null } \ No newline at end of file diff --git a/todo.txt b/todo.txt index e85327c..40464d2 100644 --- a/todo.txt +++ b/todo.txt @@ -1,19 +1,28 @@ ******************************************************** NEXT PATCH ************************************************** -filament renamed UHMWPE -unaaq renamed Bessemer process -toggling harpoon renamed induction furnace -half-wave rectifier renamed alternator -reticulum renamed smelting - smelting costs 2 ammo packs per upgrade - railgun works with smelting - -JUNK tech: Higgs phase transition - spawn 3 tech, there is a chance to remove everything with a 5 minute halflife +tech grappling hook - can attack to walls and pull you towards the walls + harpoon extends farther as you hold down fire, but no longer has auto-steering + +mobs do 4% less harm per difficulty level +railgun/harpoon auto-targeting is smarter at long distances with multiple small targets + but it still has trouble with moving targets +booby trap only has a 100 -> 50% chance to drop a mine when picking up power ups +added fallback for browsers that don't allow local storage ******************************************************** TODO ******************************************************** -make a variety of harpoon shapes? - just a different railgun shape? +grappling hook + new shape, different from harpoon + how to draw convex? + composite + draw the hook part directly + give player more control over motion while hanging and retracting + reduce friction effects so player swing around? + up down left right push player around? + lengthen and shrink the rope length? + scale velocity dampening with distance to grapple + get induction furnace working? + I'm not a fan of this tech, I'd be happy if it was basic harpoon only tech - Plasma railgun like foam, or phonon? @@ -23,11 +32,6 @@ pause time like invariant for other things... charging railgun charging anything? -tech: when this tech is ejected spawn one of every power up type - JUNK tech? - -try to get grappling hook working again - bug - url sharing still broken sometimes setting to remove UI, except health bar