From ce74f420e3e5e1383b9900e6180e36c2f32457a1 Mon Sep 17 00:00:00 2001 From: landgreen Date: Sat, 25 Nov 2023 15:26:09 -0800 Subject: [PATCH] grappling hook 2nd update grappling hook added coupling effect - 4% extra ammo per coupling doesn't destroy blocks, instead the player grabs blocks doesn't automatically retract after hitting power ups improved momentum conservation on yank and catching blocks, power ups removed accidental 55% defense for grapple field tech (no images yet) autonomous defense - fire harpoons at nearby mobs rupture - explosion on impact with map, block, mob negative mass field has horizontal block motion by default fixed tech sorting by "allowed tech" in experiment mode --- js/bullet.js | 189 +++++++++++++++++++++++++++++++------------------- js/engine.js | 18 +++++ js/index.js | 28 ++++---- js/level.js | 17 ++--- js/player.js | 76 ++++++++++++++++---- js/powerup.js | 49 +++++++------ js/tech.js | 89 ++++++++++++++++++++++-- todo.txt | 46 +++++------- 8 files changed, 345 insertions(+), 167 deletions(-) diff --git a/js/bullet.js b/js/bullet.js index b672669..57d523e 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -1484,54 +1484,17 @@ const b = { index: 3, isInternal: false }, { - x: 34, - y: 5, + x: 37, + y: 3, index: 4, isInternal: false }], - - // [{ - // x: -10, - // y: 2, - // index: 0, - // isInternal: false - // }, { - // x: -10, - // y: -2, - // index: 1, - // isInternal: false - // }, { - // x: 35, - // y: -3, - // index: 2, - // isInternal: false - // }, { - // x: 37, - // y: -2, - // index: 3, - // isInternal: false - // }, { - // x: 40, - // y: 0, - // index: 4, - // isInternal: false - // }, { - // x: 37, - // y: 2, - // index: 5, - // isInternal: false - // }, { - // x: 35, - // y: 3, - // index: 6, - // isInternal: false - // }], { angle: angle, friction: 1, frictionAir: 0.4, thrustMag: 0.13, - dmg: 6, //damage done in addition to the damage from momentum + dmg: 8, //damage done in addition to the damage from momentum classType: "bullet", endCycle: simulation.cycle + 70, isSlowPull: false, @@ -1543,6 +1506,70 @@ const b = { // lookFrequency: Math.floor(7 + Math.random() * 3), density: 0.004, //0.001 is normal for blocks, 0.004 is normal for harpoon, 0.004*6 when buffed drain: 0.001, + draw() { + 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)) + //draw rope + 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); + // if (tech.isHookWire) { + // //draw wire + // const hitMob = Matter.Query.ray(mob, this.position, m.pos, 10) + // if (hitMob.length && m.immuneCycle < m.cycle) { + // for (let i = 0; i < hitMob.length; i++) { + // console.log(hitMob[i].bodyA) + // // simulation.drawList.push({ //add dmg to draw queue + // // x: path[path.length - 1].x, + // // y: path[path.length - 1].y, + // // radius: Math.sqrt(2000 * damage * best.who.damageReduction) + 2, + // // color: tech.laserColorAlpha, + // // time: simulation.drawTime + // // }); + // hitMob[i].bodyA.damage(0.001) + // } + // } + // //draw glow around wire + // ctx.strokeStyle = "rgba(0,255,255,0.2)" // "#0ce" + // ctx.lineWidth = 20 + // ctx.stroke(); + // } + ctx.strokeStyle = "rgba(0,255,255,0.2)" // "#0ce" + ctx.lineWidth = 10 + ctx.stroke(); + ctx.strokeStyle = "#000" // "#0ce" + ctx.lineWidth = 0.5 + ctx.stroke(); + + + + //draw harpoon spikes + // ctx.beginPath(); + // ctx.lineTo(this.vertices[3].x, this.vertices[3].y); + // // const spike1 = Vector.add(this.vertices[1], Vector.mult(Vector.sub(this.vertices[1], this.vertices[2]), 3)) + // // ctx.lineTo(spike1.x, spike1.y); + // const controlPoint2 = Vector.add(this.vertices[3], Vector.mult(Vector.sub(this.vertices[3], this.vertices[2]), 20)) + // ctx.quadraticCurveTo(controlPoint2.x, controlPoint2.y, this.vertices[2].x, this.vertices[2].y) + // ctx.fillStyle = '#000' + // ctx.fill(); + + + ctx.beginPath(); + ctx.lineTo(this.vertices[0].x, this.vertices[0].y); + const spikeLength = 2 + // const spike1 = Vector.add(this.vertices[1], Vector.mult(Vector.sub(this.vertices[1], this.vertices[2]), spikeLength)) + // ctx.moveTo(this.vertices[2].x, this.vertices[2].y); + // ctx.lineTo(spike1.x, spike1.y); + // ctx.lineTo(this.vertices[3].x, this.vertices[3].y); + const spike2 = Vector.add(this.vertices[3], Vector.mult(Vector.sub(this.vertices[3], this.vertices[2]), spikeLength)) + ctx.moveTo(this.vertices[2].x, this.vertices[2].y); + ctx.lineTo(spike2.x, spike2.y); + ctx.lineTo(this.vertices[1].x, this.vertices[1].y); + ctx.fillStyle = '#000' + ctx.fill(); + }, beforeDmg(who) { if (tech.isShieldPierce && who.isShielded) { //disable shields who.isShielded = false @@ -1565,7 +1592,9 @@ const b = { // // this.endCycle = 0; // } if (m.fieldCDcycle < m.cycle + 40) m.fieldCDcycle = m.cycle + 40 //extra long cooldown on hitting mobs + if (tech.isHookExplosion) b.explosion(this.position, 250 + 150 * Math.random()); //makes bullet do explosive damage at end this.retract() + }, caughtPowerUp: null, dropCaughtPowerUp() { @@ -1594,35 +1623,6 @@ const b = { this.dropCaughtPowerUp() } }, - draw() { - 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 harpoon spikes - const spikeLength = 2 - ctx.beginPath(); - const spike1 = Vector.add(this.vertices[1], Vector.mult(Vector.sub(this.vertices[1], this.vertices[2]), spikeLength)) - ctx.moveTo(this.vertices[2].x, this.vertices[2].y); - ctx.lineTo(spike1.x, spike1.y); - ctx.lineTo(this.vertices[3].x, this.vertices[3].y); - - const spike2 = Vector.add(this.vertices[3], Vector.mult(Vector.sub(this.vertices[3], this.vertices[2]), spikeLength)) - ctx.moveTo(this.vertices[2].x, this.vertices[2].y); - ctx.lineTo(spike2.x, spike2.y); - ctx.lineTo(this.vertices[1].x, this.vertices[1].y); - ctx.fillStyle = '#000' - ctx.fill(); - }, retract() { this.do = this.returnToPlayer this.endCycle = simulation.cycle + 60 @@ -1630,7 +1630,8 @@ const b = { if (this.angularSpeed < 0.5) this.torque += this.inertia * 0.001 * (Math.random() - 0.5) //(Math.round(Math.random()) ? 1 : -1) this.collisionFilter.mask = 0//cat.map | cat.mob | cat.mobBullet | cat.mobShield // | cat.body //recoil on pulling grapple back - const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) + const mag = this.pickUpTarget ? Math.max(this.pickUpTarget.mass, 0.5) : 0.5 + const momentum = Vector.mult(Vector.sub(this.position, m.pos), mag * (m.crouch ? 0.0001 : 0.0002)) player.force.x += momentum.x player.force.y += momentum.y }, @@ -1642,7 +1643,20 @@ const b = { const momentum = Vector.mult(Vector.sub(this.velocity, player.velocity), (m.crouch ? 0.0001 : 0.0002)) player.force.x += momentum.x player.force.y += momentum.y + if (this.pickUpTarget) { + m.holdingTarget = this.pickUpTarget + // give block to player after it returns + m.isHolding = true; + //conserve momentum when player mass changes + totalMomentum = Vector.add(Vector.mult(player.velocity, player.mass), Vector.mult(Vector.normalise(this.velocity), 15 * Math.min(20, this.pickUpTarget.mass))) + Matter.Body.setVelocity(player, Vector.mult(totalMomentum, 1 / (m.defaultMass + this.pickUpTarget.mass))); + m.definePlayerMass(m.defaultMass + this.pickUpTarget.mass * m.holdingMassScale) + //make block collide with nothing + m.holdingTarget.collisionFilter.category = 0; + m.holdingTarget.collisionFilter.mask = 0; + this.pickUpTarget = null + } } else { if (m.energy > this.drain) m.energy -= this.drain const sub = Vector.sub(this.position, m.pos) @@ -1651,10 +1665,11 @@ const b = { this.force.x -= returnForce.x this.force.y -= returnForce.y this.grabPowerUp() + this.grabBlocks() } this.draw(); }, - destroyBlocks() { + destroyBlocks() {//not used? const blocks = Matter.Query.collides(this, body) if (blocks.length && !blocks[0].bodyA.isNotHoldable) { if (blocks[0].bodyA.mass > 2.5) this.retract() @@ -1679,6 +1694,32 @@ const b = { }) } }, + pickUpTarget: null, + grabBlocks() { + if (this.pickUpTarget) { + //position block on hook + Matter.Body.setPosition(this.pickUpTarget, Vector.add(this.vertices[2], this.velocity)) + Matter.Body.setVelocity(this.pickUpTarget, { x: 0, y: 0 }) + } else if (!input.down) { + const blocks = Matter.Query.collides(this, body) + if (blocks.length) { + // console.log(blocks) + for (let i = 0; i < blocks.length; i++) { + if (blocks[i].bodyA.classType === "body" && !blocks[i].bodyA.isNotHoldable && !blocks[0].bodyA.mass < 60) { + this.retract() + this.pickUpTarget = blocks[i].bodyA + if (tech.isHookExplosion) b.explosion(this.position, 250 + 150 * Math.random()); //makes bullet do explosive damage at end + } else if (blocks[i].bodyB.classType === "body" && !blocks[i].bodyB.isNotHoldable && !blocks[0].bodyB.mass < 60) { + this.retract() + this.pickUpTarget = blocks[i].bodyB + if (tech.isHookExplosion) b.explosion(this.position, 250 + 150 * Math.random()); //makes bullet do explosive damage at end + } + } + // if (blocks[0].bodyA.mass > 2.5 && blocks[0].bodyA.mass > 15) { + + } + } + }, 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)) @@ -1695,7 +1736,7 @@ const b = { powerUp[i].collisionFilter.mask = 0 this.thrustMag *= 0.6 this.endCycle += 0.5 //it pulls back slower, so this prevents it from ending early - this.retract() + // this.retract() break //just pull 1 power up if possible } } @@ -1706,7 +1747,8 @@ const b = { do() { if (m.fieldCDcycle < m.cycle + 5) m.fieldCDcycle = m.cycle + 5 if (input.field) { //&& !Matter.Query.collides(this, body).length - this.destroyBlocks() + // this.destroyBlocks() + this.grabBlocks() this.grabPowerUp() // if (this.endCycle < simulation.cycle + 1) { //if at end of lifespan, but player is holding down field, force retraction // this.endCycle = simulation.cycle + 30 @@ -1736,6 +1778,7 @@ const b = { if (input.field && Matter.Query.collides(this, map).length) { Matter.Body.setPosition(this, Vector.add(this.position, { x: -20 * Math.cos(this.angle), y: -20 * Math.sin(this.angle) })) if (Matter.Query.collides(this, map).length) { + if (tech.isHookExplosion) b.explosion(this.position, 150 + 50 * Math.random()); //makes bullet do explosive damage at end Matter.Body.setVelocity(this, { x: 0, y: 0 }); Matter.Sleeping.set(this, true) this.endCycle = simulation.cycle + 5 diff --git a/js/engine.js b/js/engine.js index 7a0b708..0374fcd 100644 --- a/js/engine.js +++ b/js/engine.js @@ -204,6 +204,24 @@ function collisionChecks(event) { // time: 25 // }); } + // if (true) { //fire harpoons at mobs after getting hit + // const countMax = 12 + // let count = countMax + // const range = 300 + // for (let i = 0; i < mob.length; i++) { + // if (count > 0 && Vector.magnitude(Vector.sub(m.pos, mob[i].position)) < range) { + // count-- + // if (m.fieldCDcycle < m.cycle + 30) m.fieldCDcycle = m.cycle + 30 + // const angle = Math.atan2(mob[i].position.y - player.position.y, mob[i].position.x - player.position.x); + // b.harpoon(m.pos, mob[i], angle, 0.75, true, 20) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { + // for (; count > 0; count--) { + // b.harpoon(m.pos, mob[i], count * Math.PI / countMax, 0.75, true, 9) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { + // bullet[bullet.length - 1].drain = 0 + // } + // break + // } + // } + // } if (tech.isStimulatedEmission) powerUps.ejectTech() if (mob[k].onHit) mob[k].onHit(); if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles diff --git a/js/index.js b/js/index.js index b852be9..54eff25 100644 --- a/js/index.js +++ b/js/index.js @@ -524,12 +524,10 @@ ${simulation.isCheating ? "

lore disabled" : ""} if (!aHasKeyword && bHasKeyword) return 1; return 0; } - if (find === 'guntech') { tech.tech.sort((a, b) => { if (a.isGunTech && b.isGunTech) { - if (a.allowed() > b.allowed()) return -1; //sort to the top - if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + return (a.allowed() === b.allowed()) ? 0 : a.allowed() ? -1 : 1; } if (a.isGunTech && !b.isGunTech) return -1; //sort to the top if (!a.isGunTech && b.isGunTech) return 1; //sort to the bottom @@ -538,30 +536,30 @@ ${simulation.isCheating ? "

lore disabled" : ""} } else if (find === 'fieldtech') { tech.tech.sort((a, b) => { if (a.isFieldTech && b.isFieldTech) { - if (a.allowed() > b.allowed()) return -1; //sort to the top - if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + return (a.allowed() === b.allowed()) ? 0 : a.allowed() ? -1 : 1; } if (a.isFieldTech && !b.isFieldTech) return -1; //sort to the top if (!a.isFieldTech && b.isFieldTech) return 1; //sort to the bottom return 0; }); } else if (find === 'allowed') { + // tech.tech.sort((a, b) => { + // if (a.allowed() > !b.allowed()) return -1; //sort to the top + // if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + // return 0; + // }); tech.tech.sort((a, b) => { - if (a.allowed() > b.allowed()) return -1; //sort to the top - if (!a.allowed() < b.allowed()) return 1; //sort to the bottom - return 0; + return (a.allowed() === b.allowed()) ? 0 : a.allowed() ? -1 : 1; }); } else if (find === 'have') { tech.tech.sort((a, b) => { - if (a.count > b.count) return -1; //sort to the top - if (!a.count < b.count) return 1; //sort to the bottom + return (a.allowed() === b.allowed()) ? 0 : a.allowed() ? -1 : 1; return 0; }); } else if (find === 'heal') { tech.tech.sort((a, b) => { if (a.isHealTech && b.isHealTech) { - if (a.allowed() > b.allowed()) return -1; //sort to the top - if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + return (a.allowed() === b.allowed()) ? 0 : a.allowed() ? -1 : 1; } if (a.isHealTech && !b.isHealTech) return -1; //sort to the top if (!a.isHealTech && b.isHealTech) return 1; //sort to the bottom @@ -570,8 +568,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} } else if (find === 'bot') { tech.tech.sort((a, b) => { if (a.isBotTech && b.isBotTech) { - if (a.allowed() > b.allowed()) return -1; //sort to the top - if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + return (a.allowed() === b.allowed()) ? 0 : a.allowed() ? -1 : 1; } if (a.isBotTech && !b.isBotTech) return -1; //sort to the top if (!a.isBotTech && b.isBotTech) return 1; //sort to the bottom @@ -580,8 +577,7 @@ ${simulation.isCheating ? "

lore disabled" : ""} } else if (document.getElementById("sort-input").value === 'skin') { tech.tech.sort((a, b) => { if (a.isSkin && b.isSkin) { - if (a.allowed() > b.allowed()) return -1; //sort to the top - if (!a.allowed() < b.allowed()) return 1; //sort to the bottom + return (a.allowed() === b.allowed()) ? 0 : a.allowed() ? -1 : 1; } if (a.isSkin && !b.isSkin) return -1; //sort to the top if (!a.isSkin && b.isSkin) return 1; //sort to the bottom diff --git a/js/level.js b/js/level.js index 3e00aba..3f1401f 100644 --- a/js/level.js +++ b/js/level.js @@ -19,16 +19,18 @@ const level = { // simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode // simulation.isHorizontalFlipped = true // tech.giveTech("performance") - // level.difficultyIncrease(3 * 4) //30 is near max on hard //60 is near max on why + // level.difficultyIncrease(1 * 4) //30 is near max on hard //60 is near max on why // spawn.setSpawnList(); // spawn.setSpawnList(); // m.maxHealth = m.health = 100 + // m.maxEnergy = m.energy = 10000000 // tech.isRerollDamage = true // powerUps.research.changeRerolls(99999) // m.immuneCycle = Infinity //you can't take damage // tech.tech[297].frequency = 100 // m.couplingChange(10) // m.setField("grappling hook") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole 10 grappling hook + // tech.isHookWire = true // m.energy = 0 // simulation.molecularMode = 2 // m.damage(0.1); @@ -36,10 +38,8 @@ const level = { // b.giveGuns("harpoon") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.guns[8].ammo = 100000000 // requestAnimationFrame(() => { tech.giveTech("MACHO") }); - // for (let i = 0; i < 1; ++i) tech.giveTech("electrostatic induction") - // for (let i = 0; i < 1; ++i) tech.giveTech("grappling hook") - // for (let i = 0; i < 1; ++i) tech.giveTech("superdeterminism") - // for (let i = 0; i < 1; ++i) tech.giveTech("fine-structure constant") + // for (let i = 0; i < 1; ++i) tech.giveTech("autonomous defense") + // for (let i = 0; i < 1; ++i) tech.giveTech("rupture") // for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot upgrade") // requestAnimationFrame(() => { for (let i = 0; i < 30; i++) tech.giveTech("laser-bot") }); // for (let i = 0; i < 1; i++) tech.giveTech("laser-bot upgrade") @@ -47,11 +47,11 @@ const level = { // for (let i = 0; i < 1; ++i) tech.giveTech("mechanical resonance") // for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research"); - // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling"); + // for (let i = 0; i < 100; i++) powerUps.directSpawn(1750, -500, "coupling"); // level.testing(); // for (let i = 0; i < 4; ++i) spawn.hopMother(1900, -500) - // for (let i = 0; i < 0; ++i) spawn.hopper(1900, -500) + // for (let i = 0; i < 5; ++i) spawn.starter(1900, -500) // for (let i = 0; i < 1; ++i) spawn.shooterBoss(1900, -2500) // spawn.beetleBoss(1900, -500, 25) // spawn.slasher2(2000, -1150) @@ -63,7 +63,8 @@ const level = { // for (let i = 0; i < 40; ++i) tech.giveTech() level[simulation.isTraining ? "walk" : "intro"]() //normal starting level ************************************************** - + // spawn.bodyRect(2425, -120, 200, 200); + // console.log(body[body.length - 1].mass) // simulation.isAutoZoom = false; //look in close // simulation.zoomScale *= 0.5; // simulation.setZoom(); diff --git a/js/player.js b/js/player.js index f269256..459438c 100644 --- a/js/player.js +++ b/js/player.js @@ -2496,7 +2496,6 @@ const m = { // set pick up target for when mouse is released if (body[grabbing.targetIndex]) { m.holdingTarget = body[grabbing.targetIndex]; - // ctx.beginPath(); //draw on each valid body let vertices = m.holdingTarget.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); @@ -2588,6 +2587,8 @@ const m = { return `+${(4 * couple).toFixed(0)}% block collision damage` case 9: //wormhole return `after eating blocks +${(2 * couple).toFixed(0)} energy` + case 10: //grappling hook + return `${powerUps.orb.ammo(1)} give ${(4 * couple).toFixed(0)}% more ammo` } }, couplingChange(change = 0) { @@ -2659,12 +2660,6 @@ const m = { } } }, - //
- //
1
- //
2
- //
3
- //
4
- //
{ name: "standing wave", //deflecting protects you in every direction @@ -3083,7 +3078,20 @@ const m = { for (let i = 0, len = who.length; i < len; ++i) { sub = Vector.sub(who[i].position, m.pos); dist = Vector.magnitude(sub); - if (dist < range) who[i].force.y -= who[i].mass * (simulation.g * mag); + if (dist < range) { + who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity + if (input.left) { //blocks move horizontally with the same force as the player + who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a + } else if (input.right) { + who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d + } + } + + + + // sub = Vector.sub(who[i].position, m.pos); + // dist = Vector.magnitude(sub); + // if (dist < range) who[i].force.y -= who[i].mass * (simulation.g * mag); } } //control horizontal acceleration @@ -3825,7 +3833,8 @@ const m = { } if (tech.isRewindField) { this.rewindCount = 0 - m.grabPowerUpRange2 = 300000 + m.grabPowerUpRange2 = 300000// m.grabPowerUpRange2 = 200000; + m.hold = function () { // console.log(m.fieldCDcycle) m.grabPowerUp(); @@ -4944,25 +4953,64 @@ const m = { { name: "grappling hook", // description: `use energy to pull yourself towards the map
generate 6 energy per second`, - description: `use energy to fire a hook that attaches to map,
pulls player, damages mobs, and destroys blocks
generate 6 energy per second`, + description: `use energy to fire a hook that pulls player
damages mobs and destroys blocks
generate 6 energy per second`, effect: () => { m.fieldFire = true; // m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping m.fieldMeterColor = "#333" m.eyeFillColor = m.fieldMeterColor - m.fieldHarmReduction = 0.45; //55% reduction + m.grabPowerUpRange2 = 300000 //m.grabPowerUpRange2 = 200000; + // m.fieldHarmReduction = 0.45; //55% reduction m.hold = function () { - if (input.field) { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field) { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) if (m.fieldCDcycle < m.cycle) { if (m.energy > 0.02) m.energy -= 0.02 - const where = { x: m.pos.x + 40 * Math.cos(m.angle), y: m.pos.y + 40 * Math.sin(m.angle) } - b.grapple(where, m.angle) + b.grapple({ x: m.pos.x + 40 * Math.cos(m.angle), y: m.pos.y + 40 * Math.sin(m.angle) }, m.angle) if (m.fieldCDcycle < m.cycle + 20) m.fieldCDcycle = m.cycle + 20 } m.grabPowerUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + if (tech.isHookDefense && m.energy > 0.33 && m.fieldCDcycle < m.cycle) { + const maxCount = 6 //scale the number of hooks fired + let count = maxCount + const range = 300 + for (let i = 0; i < mob.length; i++) { + if (!mob[i].isBadTarget && + !mob[i].isInvulnerable && + Vector.magnitude(Vector.sub(m.pos, mob[i].position)) < range && + Matter.Query.ray(map, m.pos, mob[i].position).length === 0 + ) { + count-- + m.energy -= 0.2 + if (m.fieldCDcycle < m.cycle + 30) m.fieldCDcycle = m.cycle + 30 + const angle = Math.atan2(mob[i].position.y - player.position.y, mob[i].position.x - player.position.x); + b.harpoon(m.pos, mob[i], angle, 0.75, true, 20) // harpoon(where, target, angle = m.angle, harpoonSize = 1, isReturn = false, totalCycles = 35, isReturnAmmo = true, thrust = 0.1) { + bullet[bullet.length - 1].drain = 0 + for (; count > 0; count--) { + b.harpoon(m.pos, mob[i], angle + count * 2 * Math.PI / maxCount, 0.75, true, 10) + bullet[bullet.length - 1].drain = 0 + } + break + } + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 0.25; + ctx.setLineDash([10, 30]); + ctx.stroke(); + ctx.setLineDash([]); + } } m.drawRegenEnergy() + //look for nearby mobs and fire harpoons at them } } }, diff --git a/js/powerup.js b/js/powerup.js index 1b27ff1..6ffd71b 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -621,17 +621,18 @@ const powerUps = { return 17; }, effect() { + const couplingExtraAmmo = m.fieldMode === 10 ? 1 + 0.04 * m.coupling : 1 if (b.inventory.length > 0) { powerUps.animatePowerUpGrab('rgba(68, 102, 119,0.25)') if (tech.isAmmoForGun && b.activeGun !== null) { //give extra ammo to one gun only with tech logistics const target = b.guns[b.activeGun] if (target.ammo !== Infinity) { if (tech.ammoCap) { - const ammoAdded = Math.ceil(target.ammoPack * 0.7 * tech.ammoCap * 0.8) //0.7 is average + const ammoAdded = Math.ceil(target.ammoPack * 0.7 * tech.ammoCap * 0.8 * couplingExtraAmmo) //0.7 is average target.ammo = ammoAdded // simulation.makeTextLog(`${target.name}.ammo = ${ammoAdded}`) } else { - const ammoAdded = Math.ceil((0.7 * Math.random() + 0.7 * Math.random()) * target.ammoPack * 0.8) + const ammoAdded = Math.ceil((0.7 * Math.random() + 0.7 * Math.random()) * target.ammoPack * 0.8 * couplingExtraAmmo) target.ammo += ammoAdded // simulation.makeTextLog(`${target.name}.ammo += ${ammoAdded}`) } @@ -642,11 +643,12 @@ const powerUps = { const target = b.guns[b.inventory[i]] if (target.ammo !== Infinity) { if (tech.ammoCap) { - const ammoAdded = Math.ceil(target.ammoPack * 0.45 * tech.ammoCap) //0.45 is average + const ammoAdded = Math.ceil(target.ammoPack * 0.45 * tech.ammoCap * couplingExtraAmmo) //0.45 is average target.ammo = ammoAdded // textLog += `${target.name}.ammo = ${ammoAdded}
` - } else { - const ammoAdded = Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack) //Math.ceil(Math.random() * target.ammoPack) + } else { //default ammo behavior + const ammoAdded = Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack * couplingExtraAmmo) //Math.ceil(Math.random() * target.ammoPack) + // console.log(ammoAdded, Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack)) target.ammo += ammoAdded // textLog += `${target.name}.ammo += ${ammoAdded}
` } @@ -1270,24 +1272,25 @@ const powerUps = { } for (let i = 0; i < localSettings.entanglement.techIndexes.length; i++) { //add tech let choose = localSettings.entanglement.techIndexes[i] - const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; - - if (choose === null || tech.tech[choose].count + 1 > tech.tech[choose].maxCount || !tech.tech[choose].allowed()) { - // text += `
${tech.tech[choose].name} - incoherent
` - text += powerUps.incoherentTechText(choose) - } else { - if (tech.tech[choose].isFieldTech) { - text += powerUps.fieldTechText(choose, `powerUps.choose('tech',${choose})`) - } else if (tech.tech[choose].isGunTech) { - text += powerUps.gunTechText(choose, `powerUps.choose('tech',${choose})`) - } else if (tech.tech[choose].isLore) { - text += `
  ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` - } else if (tech.tech[choose].isJunk) { - text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`) - } else if (tech.tech[choose].isSkin) { - text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`) - } else { //normal tech - text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) + if (tech.tech[choose]) { + const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; + if (choose === null || tech.tech[choose].count + 1 > tech.tech[choose].maxCount || !tech.tech[choose].allowed()) { + // text += `
${tech.tech[choose].name} - incoherent
` + text += powerUps.incoherentTechText(choose) + } else { + if (tech.tech[choose].isFieldTech) { + text += powerUps.fieldTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isGunTech) { + text += powerUps.gunTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isLore) { + text += `
  ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` + } else if (tech.tech[choose].isJunk) { + text += powerUps.junkTechText(choose, `powerUps.choose('tech',${choose})`) + } else if (tech.tech[choose].isSkin) { + text += powerUps.skinTechText(choose, `powerUps.choose('tech',${choose})`) + } else { //normal tech + text += powerUps.techText(choose, `powerUps.choose('tech',${choose})`) + } } } } diff --git a/js/tech.js b/js/tech.js index 9c4a5cb..2c835f9 100644 --- a/js/tech.js +++ b/js/tech.js @@ -4545,7 +4545,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return (!tech.isLargeHarpoon && tech.haveGunCheck("harpoon")) || tech.isNeedles + return (!tech.isLargeHarpoon && tech.haveGunCheck("harpoon")) || tech.isNeedles || tech.isHookDefense }, requires: "needle gun, harpoon, not Bessemer process", effect() { @@ -5149,7 +5149,7 @@ const tech = { frequency: 2, frequencyDefault: 2, allowed() { - return ((tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isSuperBounce) || (tech.haveGunCheck("harpoon") && !tech.fragments) + return ((tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isSuperBounce) || (tech.haveGunCheck("harpoon") && !tech.fragments) || tech.isHookDefense }, requires: "super balls, harpoon, not fragmentation", effect() { @@ -5574,7 +5574,7 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isExplodeRadio && ((tech.haveGunCheck("harpoon") && !tech.isFoamBall) || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount || tech.isRivets || tech.blockDamage > 0.075) + return !tech.isExplodeRadio && ((tech.haveGunCheck("harpoon") && !tech.isFoamBall) || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount || tech.isRivets || tech.blockDamage > 0.075 || tech.isHookDefense) }, requires: "grenades, missiles, rivets, harpoon, or mass driver, not iridium-192, not polyurethane foam", effect() { @@ -7784,7 +7784,7 @@ const tech = { }, { name: "inertial mass", - description: "negative mass is larger and faster
blocks also move horizontally with the field", + description: "negative mass is larger and faster", //
blocks also move horizontally with the field isFieldTech: true, maxCount: 1, count: 0, @@ -8052,7 +8052,7 @@ const tech = { }, { name: "electric generator", - description: "after deflecting mobs
molecular assembler generates +50 energy", + description: "after deflecting mobs
molecular assembler generates +50 energy", isFieldTech: true, maxCount: 9, count: 0, @@ -8754,6 +8754,82 @@ const tech = { tech.isWormholeMapIgnore = false } }, + { + name: "autonomous defense", + description: "grappling hook uses 20 energy
to fire harpoons at nearby mobs", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 10 + }, + requires: "grappling hook", + effect() { + tech.isHookDefense = true + }, + remove() { + tech.isHookDefense = false + } + }, + { + name: "rupture", + description: "after grappling hook impacts solid objects
generate an explosion", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldMode === 10 + }, + requires: "grappling hook", + effect() { + tech.isHookExplosion = true + }, + remove() { + tech.isHookExplosion = false + } + }, + // { + // name: "autonomous defense", + // description: "if you collide with a mob
fire harpoons at nearby mobs", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return m.fieldMode === 10 && !tech.isHookDefense + // }, + // requires: "grappling hook, not automatic offense", + // effect() { + // tech.isHookDefense = true + // }, + // remove() { + // tech.isHookDefense = false + // } + // }, + // { + // name: "wire", + // description: "", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return m.fieldMode === 10 + // }, + // requires: "grappling hook", + // effect() { + // tech.isHookWire = true + // }, + // remove() { + // tech.isHookWire = false + // } + // }, //************************************************** //************************************************** experimental //************************************************** modes @@ -11800,4 +11876,7 @@ const tech = { isHealBrake: null, isMassProduction: null, isPrinter: null, + // isHookWire: null, + isHookDefense: null, + isHookExplosion: null, } \ No newline at end of file diff --git a/todo.txt b/todo.txt index 76a7a9c..bd298b9 100644 --- a/todo.txt +++ b/todo.txt @@ -1,39 +1,29 @@ ******************************************************** NEXT PATCH ************************************************** -grappling hook is now a field (work in progress) - reworked physics to allow faster speeds, but more control - improved rate of power up grabbing - more player control to hook retraction rate - changed hook shape and field image graphics - grappling hook field coupling, more tech, bug fixes, and general polish to be added soon +grappling hook + added coupling effect - 4% extra ammo per coupling + doesn't destroy blocks, instead the player grabs blocks + doesn't automatically retract after hitting power ups + improved momentum conservation on yank and catching blocks, power ups + removed accidental 55% defense for grapple field + tech (no images yet) + autonomous defense - fire harpoons at nearby mobs + rupture - explosion on impact with map, block, mob -aerostat - 88->100% damage in air 22-> 25% damage on ground -foam damage reduced 10%, ammo increased about 10% -after hitting an invulnerable mob (drones,spores,worms,iceIX,fleas) don't die or lose cycles -added JUNK tech: mobs! - summon 20 random mobs -added announcement of mob names in console at start of new level - -bug fixes +negative mass field has horizontal block motion by default +fixed tech sorting by "allowed tech" in experiment mode *********************************************************** TODO ***************************************************** grappling hook is a field check for places that the player could get into but not out of - maybe grapple could grab more then 1 power up? - grapple slices blocks - cut large blocks into 2,3 - use dead mob code for mobs > 5 sides - write code to cut large blocks in half and remove one half - need several field tech - new field tech ideas - increase hook damage - hook damage aura - hook's line does damage - make several auto targeting harpoons after taking damage - how to make them not drain energy - generate ___ after destroying blocks - energy, drones, iceIX, explosion, nails, junk bots? - coupling effect: defense?, bonus from ammo power ups, fire rate + field tech ideas + hook and line stuns? + increase hook damage + hook damage aura + hook's line does damage + generate ___ after destroying blocks + energy, drones, iceIX, explosion, nails, junk bots? tech - killing a mob heals for the last damage you took disable cloaking heal? maybe you don't need to disable, just don't heal twice