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
This commit is contained in:
landgreen
2022-02-24 05:50:39 -08:00
parent e73abc63ba
commit 1ca00d3598
9 changed files with 608 additions and 222 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -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)

View File

@@ -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 ? "<br><br><em>lore disabled</em>": ""}
//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)` : "";
// <div class="circle-grid-small research" style="position:absolute; top:13px; left:30px;opacity:0.85;"></div>
if (tech.tech[i].isFieldTech) {
techID.classList.remove('experiment-grid-hide');
techID.innerHTML = `
<div class="grid-title">
<span style="position:relative;">
@@ -430,8 +423,7 @@ ${simulation.isCheating ? "<br><br><em>lore disabled</em>": ""}
</span>
&nbsp; &nbsp; &nbsp; &nbsp; ${tech.tech[i].link} ${techCountText}</div>${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
</div>`
} else
if (tech.tech[i].isJunk) {
} else if (tech.tech[i].isJunk) {
techID.innerHTML = `<div class="grid-title"><div class="circle-grid junk"></div> &nbsp; ${tech.tech[i].link} ${techCountText}</div>${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}</div>`
} else if (tech.tech[i].isExperimentalMode) {
techID.innerHTML = `<div class="grid-title">${tech.tech[i].name}</div>${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}</div>`
@@ -525,7 +517,7 @@ ${simulation.isCheating ? "<br><br><em>lore disabled</em>": ""}
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
});

View File

@@ -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 <span class='color-symbol'>=</span> 0; i <span class='color-symbol'><</span> localSettings.levelsClearedLastGame <span class='color-symbol'>/</span> 3; i<span class='color-symbol'>++</span>)`);
simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") <em>//simulation superposition</em>}`);
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)

View File

@@ -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(`<span class='color-var'>lore</span>.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);
}

View File

@@ -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 {

View File

@@ -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() {};

View File

@@ -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 <strong>mine</strong> after picking up a <strong>power up</strong><br><strong>+53%</strong> <strong class='color-j'>JUNK</strong> to the potential <strong class='color-m'>tech</strong> pool",
description: "<strong>50%</strong> chance to drop a <strong>mine</strong> from <strong>power ups</strong><br><strong>+50%</strong> <strong class='color-j'>JUNK</strong> to the potential <strong class='color-m'>tech</strong> 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 <strong>50% denser</strong>, but don't <strong>retract</strong><br>gain <strong>500%</strong> more harpoon <strong class='color-ammo'>ammo</strong> per ${powerUps.orb.ammo(1)}`,
description: `<strong>harpoons</strong> are <strong>50% denser</strong>, but don't <strong>retract</strong><br>gain <strong>500%</strong> more harpoon <strong class='color-ammo'>ammo</strong> 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: `<strong>harpoons</strong> attach to the <strong>map</strong> and pull you in<br><strong>rope</strong> extends much <strong>farther</strong> 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: "<strong>harpoon</strong> drains no <strong class='color-f'>energy</strong><br><strong>railgun</strong> generates <strong class='color-f'>energy</strong>", //as they <strong>retract</strong><br><strong>crouch</strong> firing <strong>harpoon</strong> generates <strong class='color-f'>energy</strong>",
description: "<strong>harpoon</strong> and <strong>grappling hook</strong> drain no <strong class='color-f'>energy</strong><br><strong>railgun</strong> generates <strong class='color-f'>energy</strong>", //as they <strong>retract</strong><br><strong>crouch</strong> firing <strong>harpoon</strong> generates <strong class='color-f'>energy</strong>",
isGunTech: true,
maxCount: 1,
count: 0,
@@ -5615,8 +5642,8 @@ const tech = {
{
name: "smelting",
// description: `spend ${powerUps.orb.ammo(2)}to upgrade the <strong>harpoon</strong><br>fire <strong>+1</strong> <strong>harpoon</strong> with each shot`,
// description: `forge ${Math.ceil(0.6*(tech.isRailGun? 5: 1))} ammo into a new <strong>harpoon</strong><br>fire <strong>+1</strong> <strong>harpoon</strong> with each shot`,
descriptionFunction() { return `forge <strong>${tech.isRailGun? 10: 2}</strong> <strong class='color-ammo'>ammo</strong> into a new harpoon<br>fire <strong>+1</strong> <strong>harpoon</strong> with each shot` },
description: `forge <strong>2</strong> <strong class='color-ammo'>ammo</strong> into a new harpoon<br>fire <strong>+1</strong> <strong>harpoon</strong> with each shot`,
// descriptionFunction() { return `forge <strong>${tech.isRailGun? 10: 2}</strong> <strong class='color-ammo'>ammo</strong> into a new harpoon<br>fire <strong>+1</strong> <strong>harpoon</strong> 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
}

View File

@@ -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