diff --git a/img/parasitism.webp b/img/parasitism.webp deleted file mode 100644 index 3db9826..0000000 Binary files a/img/parasitism.webp and /dev/null differ diff --git a/js/bullet.js b/js/bullet.js index 08ffc80..9c00921 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -3160,7 +3160,7 @@ const b = { if ( Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && !( - (m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") || + (m.health > 0.94 * m.maxHealth && !tech.isOverHeal && !tech.isDroneGrab && powerUp[i].name === "heal") || (tech.isSuperDeterminism && powerUp[i].name === "field") || ((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo") ) @@ -3192,7 +3192,7 @@ const b = { let closeDist = Infinity; for (let i = 0, len = powerUp.length; i < len; ++i) { if (!( - (m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") || + (m.health > 0.94 * m.maxHealth && !tech.isOverHeal && !tech.isDroneGrab && powerUp[i].name === "heal") || (tech.isSuperDeterminism && powerUp[i].name === "field") || ((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo") )) { diff --git a/js/index.js b/js/index.js index e36a298..768bd67 100644 --- a/js/index.js +++ b/js/index.js @@ -28,11 +28,11 @@ Math.seededRandom = function (min = 0, max = 1) { // in order to work 'Math.seed // console.log(Math.seed) -function shuffle(array) { +function seededShuffle(array) { var currentIndex = array.length, temporaryValue, randomIndex; - // While there remain elements to shuffle... + // While there remain elements while (0 !== currentIndex) { // Pick a remaining element... // randomIndex = Math.floor(Math.random() * currentIndex); @@ -542,7 +542,7 @@ ${simulation.difficultyMode > 4 ? `
console log
-
${document.getElementById("text-log").innerHTML}
+
${document.getElementById("text-log").innerHTML}
` @@ -1205,6 +1205,7 @@ const input = { left: false, right: false, isPauseKeyReady: true, + // lastDown: null, key: { fire: "KeyF", field: "Space", @@ -1379,6 +1380,7 @@ window.addEventListener("keyup", function (event) { }); window.addEventListener("keydown", function (event) { + // input.lastDown = event.code // console.log(event.code) switch (event.code) { case input.key.right: diff --git a/js/level.js b/js/level.js index 85eb04b..7c70f51 100644 --- a/js/level.js +++ b/js/level.js @@ -22,7 +22,7 @@ const level = { // simulation.isHorizontalFlipped = true // spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns // spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns - // level.levelsCleared = 10 + // level.levelsCleared = 9 // level.updateDifficulty() // tech.giveTech("performance") // m.maxHealth = m.health = 100000000 @@ -33,7 +33,7 @@ const level = { // tech.tech[297].frequency = 100 // tech.addJunkTechToPool(0.5) // m.couplingChange(10) - // m.setField("negative mass") //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 + // m.setField("wormhole") //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 // m.energy = 0 // powerUps.research.count = 3 // tech.isHookWire = true @@ -58,7 +58,7 @@ const level = { // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("interest") }); // for (let i = 0; i < 1; i++) tech.giveTech("interest") // m.lastKillCycle = m.cycle - // for (let i = 0; i < 7; i++) powerUps.directSpawn(450, -50, "tech"); + // for (let i = 0; i < 7; i++) powerUps.directSpawn(450, -50, "field"); // for (let i = 0; i < 7; i++) powerUps.directSpawn(m.pos.x + 200, m.pos.y - 250, "research", false); // spawn.bodyRect(575, -700, 150, 150); //block mob line of site on testing // level.testing(); @@ -810,14 +810,14 @@ const level = { if (document.getElementById("seed").value) { //check for player entered seed in settings Math.initialSeed = String(document.getElementById("seed").value) - Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it } + Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed if (simulation.isTraining) { simulation.isHorizontalFlipped = false level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment if (simulation.isCommunityMaps) level.trainingLevels.push("diamagnetism") - } else { //add remove and shuffle levels for the normal game (not training levels) + } else { level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment if (simulation.isCommunityMaps) { level.levels = level.levels.concat(level.communityLevels) @@ -825,7 +825,7 @@ const level = { } else { simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally } - level.levels = shuffle(level.levels); //shuffles order of maps with seeded random + level.levels = seededShuffle(level.levels); //shuffles order of maps with seeded random level.levels.length = 9 //remove any extra levels past 9 pick = ["interferometer", "factory", "reservoir"] level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, pick[Math.floor(Math.random() * pick.length)]); //add level to the back half of the randomized levels list @@ -3533,7 +3533,7 @@ const level = { const stationList = [] //use to randomize station order for (let i = 1, totalNumberOfStations = 10; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station - shuffle(stationList); + stationList.sort(() => Math.random() - 0.5); stationList.splice(0, 3); //remove some stations to keep it to 4 stations stationList.unshift(0) //add index zero to the front of the array @@ -6690,14 +6690,14 @@ const level = { //3x2: 4 short rooms (3000x1500), 1 double tall room (3000x3000) //rooms let rooms = ["exit", "loot", "enter", "empty"] - rooms = shuffle(rooms); //shuffles array order + rooms.sort(() => Math.random() - 0.5); //look... you and I both know there is a better way to do this, but it works so I'm gonna focus on other things while ( //makes sure that the exit and entrance aren't both on the same floor (rooms[0] === "enter" && rooms[2] === "exit") || (rooms[2] === "enter" && rooms[0] === "exit") || (rooms[1] === "enter" && rooms[3] === "exit") || (rooms[3] === "enter" && rooms[1] === "exit") - ) rooms = shuffle(rooms); //shuffles array order + ) rooms.sort(() => Math.random() - 0.5); for (let i = 0; i < rooms.length; i++) { if (rooms[i] === "enter") rooms[i] = enter if (rooms[i] === "exit") rooms[i] = exit @@ -6764,7 +6764,7 @@ const level = { rooms[3]() }, ] - columns = shuffle(columns) //********************************* RUN THIS LINE IN THE FINAL VERSION *************************************** + columns.sort(() => Math.random() - 0.5); for (let i = 0; i < 3; i++) { if (i === 0) { isDoorLeft = false @@ -7054,7 +7054,7 @@ const level = { }; powerUps.spawnStartingPowerUps(1875, -3075); - const powerUpPos = shuffle([{ //no debris on this level but 2 random spawn instead + const powerUpPos = [{ //no debris on this level but 2 random spawn instead x: -150, y: -1775 }, { @@ -7066,7 +7066,8 @@ const level = { }, { x: 1325, y: -150 - }]); + }]; + powerUpPos.sort(() => Math.random() - 0.5); powerUps.chooseRandomPowerUp(powerUpPos[0].x, powerUpPos[0].y); powerUps.chooseRandomPowerUp(powerUpPos[1].x, powerUpPos[1].y); //outer wall @@ -8858,7 +8859,8 @@ const level = { // spawn.bodyRect(312, -100, 25, 100); spawn.bodyRect(1450, -300, 150, 50); - const xPos = shuffle([600, 1250, 2000]); + const xPos = [600, 1250, 2000]; + xPos.sort(() => Math.random() - 0.5); spawn.mapRect(xPos[0], -200, 300, 100); spawn.mapRect(xPos[1], -250, 300, 300); spawn.mapRect(xPos[2], -150, 300, 200); diff --git a/js/mob.js b/js/mob.js index df145f9..9027446 100644 --- a/js/mob.js +++ b/js/mob.js @@ -1069,7 +1069,11 @@ const mobs = { if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000 dmg *= this.damageReduction //energy and heal drain should be calculated after damage boosts - if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon * level.isReducedRegen + if (tech.energySiphon && this.isDropPowerUp && m.immuneCycle < m.cycle) { + //dmg !== Infinity && + const regen = Math.min(this.health, dmg) * tech.energySiphon * level.isReducedRegen + if (!isNaN(regen) && regen !== Infinity) m.energy += regen + } dmg /= Math.sqrt(this.mass) } @@ -1129,7 +1133,7 @@ const mobs = { for (let i = 0; i < mob.length; i++) { if (Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) < 500000 && mob[i].alive) { //700 if (mob[i].health < 1) { - mob[i].health += 0.33 + this.isBoss + mob[i].health += 0.33 if (mob[i].health > 1) mob[i].health = 1 simulation.drawList.push({ x: mob[i].position.x, diff --git a/js/player.js b/js/player.js index 39a41bc..1484d82 100644 --- a/js/player.js +++ b/js/player.js @@ -1226,11 +1226,7 @@ const m = { m.isAltSkin = true m.yOffWhen.stand = 52 m.yOffWhen.jump = 72 - m.coyoteCycles = 11 - m.hardLandCDScale = 0.5 - m.hardLanding = 160 - m.squirrelFx = 1.4; - m.squirrelJump = 1.16; + m.squirrelJump = 1.15; m.setMovement() m.draw = function () { @@ -3631,6 +3627,9 @@ const m = { // if ((m.fieldMode === 0 || m.fieldMode === 9) && !build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4); }, setField(index) { + // console.log("field mode: ", index) + window.removeEventListener("keydown", m.fieldEvent); + if (isNaN(index)) { //find index by name let found = false for (let i = 0; i < m.fieldUpgrades.length; i++) { @@ -3647,1520 +3646,684 @@ const m = { m.setHoldDefaults(); m.fieldUpgrades[index].effect(); simulation.inGameConsole(`
  m.setField("${m.fieldUpgrades[m.fieldMode].name}")
input.key.field: ["MouseRight"]`); + if (m.fieldMode === 4) simulation.inGameConsole(`simulation.molecularMode = ${m.fieldUpgrades[4].modeText()}     ↓↘→↓↙←↑↑↓`); }, - fieldUpgrades: [{ - name: "field emitter", - imageNumber: Math.floor(Math.random() * 26), //pick one of the 25 field emitter image files at random - description: `initial field
use energy to deflect mobs and throw blocks + fieldEvent: null, + fieldUpgrades: [ + { + name: "field emitter", + imageNumber: Math.floor(Math.random() * 26), //pick one of the 25 field emitter image files at random + description: `initial field
use energy to deflect mobs and throw blocks
4 energy per second`, //
100 max energy - effect: () => { - m.hold = function () { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - if (m.energy > m.minEnergyToDeflect) { - m.drawField(); - m.pushMobsFacing(); - } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } 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) - } - m.drawRegenEnergy() - } - } - }, - { - name: "standing wave", - //deflecting protects you in every direction - description: `3 oscillating shields are permanently active -
+150 max energy -
6 energy per second`, - drainCD: 0, - effect: () => { - m.fieldBlockCD = 0; - m.blockingRecoil = 1 //4 is normal - m.fieldRange = 185 - m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2)) - // m.fieldHarmReduction = 0.66; //33% reduction - - m.harmonic3Phase = () => { //normal standard 3 different 2-d circles - const fieldRange1 = (0.75 + 0.3 * Math.sin(m.cycle / 23)) * m.fieldRange * m.harmonicRadius - const fieldRange2 = (0.68 + 0.37 * Math.sin(m.cycle / 37)) * m.fieldRange * m.harmonicRadius - const fieldRange3 = (0.7 + 0.35 * Math.sin(m.cycle / 47)) * m.fieldRange * m.harmonicRadius - const netFieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) - ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, (0.04 + 0.7 * m.energy * (0.1 + 0.11 * Math.random()))) + ")"; - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, fieldRange1, 0, 2 * Math.PI); - ctx.fill(); - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, fieldRange2, 0, 2 * Math.PI); - ctx.fill(); - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, fieldRange3, 0, 2 * Math.PI); - ctx.fill(); - //360 block - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < netFieldRange && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 - mob[i].locatePlayer(); - if (this.drainCD > m.cycle) { - m.pushMass(mob[i], 0); - } else { - m.pushMass(mob[i]); - this.drainCD = m.cycle + 15 - } - } - } - } - m.harmonicRadius = 1 //for smoothing function when player holds mouse (for harmonicAtomic) - m.harmonicAtomic = () => { //several ellipses spinning about different axises - const rotation = simulation.cycle * 0.0031 - const phase = simulation.cycle * 0.023 - const radius = m.fieldRange * m.harmonicRadius - ctx.lineWidth = 1; - ctx.strokeStyle = "rgba(110,170,200,0.8)" - ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, 0.7 * m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics)) + ")"; - // ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; - for (let i = 0; i < tech.harmonics; i++) { - ctx.beginPath(); - ctx.ellipse(m.pos.x, m.pos.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); - ctx.fill(); - ctx.stroke(); - } - //360 block - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < radius && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 - mob[i].locatePlayer(); - if (this.drainCD > m.cycle) { - m.pushMass(mob[i], 0); - } else { - m.pushMass(mob[i]); - this.drainCD = m.cycle + 15 - } - } - } - } - if (tech.harmonics === 2) { - m.harmonicShield = m.harmonic3Phase - } else { - m.harmonicShield = m.harmonicAtomic - } - m.hold = function () { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if ((input.field) && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } 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 (m.energy > m.minEnergyToDeflect && m.fieldCDcycle < m.cycle) { - if (tech.isStandingWaveExpand) { - if (input.field) { - // const oldHarmonicRadius = m.harmonicRadius - m.harmonicRadius = 0.99 * m.harmonicRadius + 0.01 * 4 - // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius) - } else { - m.harmonicRadius = 0.994 * m.harmonicRadius + 0.006 - } - } - if (!simulation.isTimeSkipping) m.harmonicShield() - } - m.drawRegenEnergy() - } - } - }, - { - name: "perfect diamagnetism", - description: `deflecting does not drain energy
shield maintains functionality while inactive
5 energy per second`, - effect: () => { - m.fieldMeterColor = "#48f" //"#0c5" - m.eyeFillColor = m.fieldMeterColor - m.fieldShieldingScale = 0; - m.fieldBlockCD = 3; - m.grabPowerUpRange2 = 10000000 - m.fieldPosition = { x: m.pos.x, y: m.pos.y } - m.fieldAngle = m.angle - m.perfectPush = (isFree = false) => { - if (m.fieldCDcycle < m.cycle) { - for (let i = 0, len = mob.length; i < len; ++i) { - if ( - Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) - mob[i].radius < m.fieldRange && - !mob[i].isUnblockable && - Vector.dot({ x: Math.cos(m.fieldAngle), y: Math.sin(m.fieldAngle) }, Vector.normalise(Vector.sub(mob[i].position, m.fieldPosition))) > m.fieldThreshold && - Matter.Query.ray(map, mob[i].position, m.fieldPosition).length === 0 - ) { - mob[i].locatePlayer(); - const unit = Vector.normalise(Vector.sub(m.fieldPosition, mob[i].position)) - m.fieldCDcycle = m.cycle + m.fieldBlockCD + (mob[i].isShielded ? 10 : 0); - if (!mob[i].isInvulnerable && bullet.length < 250) { - for (let i = 0; i < m.coupling; i++) { - if (0.1 * m.coupling - i > Math.random()) { - const angle = m.fieldAngle + 4 * m.fieldArc * (Math.random() - 0.5) - const radius = m.fieldRange * (0.6 + 0.3 * Math.random()) - b.iceIX(6 + 6 * Math.random(), angle, Vector.add(m.fieldPosition, { - x: radius * Math.cos(angle), - y: radius * Math.sin(angle) - })) - } - } - } - if (tech.blockDmg) { //electricity - Matter.Body.setVelocity(mob[i], { x: 0.5 * mob[i].velocity.x, y: 0.5 * mob[i].velocity.y }); - if (mob[i].isShielded) { - for (let j = 0, len = mob.length; j < len; j++) { - if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 6 : 2), true) - } - } else if (tech.isBlockRadiation) { - if (mob[i].isMobBullet) { - mob[i].damage(tech.blockDmg * m.dmgScale * 3, true) - } else { - mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 0.42, 180) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 - } - } else { - mob[i].damage(tech.blockDmg * m.dmgScale, true) - } - // if (mob[i].isShielded) { - // for (let j = 0, len = mob.length; j < len; j++) { - // if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 3 : 1), true) - // } - // } else { - // if (tech.isBlockRadiation && !mob[i].isMobBullet) { - // mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 4 / 12, 360) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 - // } else { - // mob[i].damage(tech.blockDmg * m.dmgScale) - // } - // } - const step = 40 - ctx.beginPath(); - for (let i = 0, len = 0.5 * tech.blockDmg; i < len; i++) { - let x = m.fieldPosition.x - 20 * unit.x; - let y = m.fieldPosition.y - 20 * unit.y; - ctx.moveTo(x, y); - for (let i = 0; i < 8; i++) { - x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) - y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) - ctx.lineTo(x, y); - } - } - ctx.lineWidth = 3; - ctx.strokeStyle = "#f0f"; - ctx.stroke(); - } else if (isFree) { - ctx.lineWidth = 2; //when blocking draw this graphic - ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` - ctx.strokeStyle = "#000"; - const len = mob[i].vertices.length - 1; - const mag = mob[i].radius - ctx.beginPath(); - ctx.moveTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) - for (let j = 0; j < len; j++) { - ctx.lineTo(mob[i].vertices[j].x + mag * (Math.random() - 0.5), mob[i].vertices[j].y + mag * (Math.random() - 0.5)); - } - ctx.lineTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) - ctx.fill(); - ctx.stroke(); - } else { - - const eye = 15; //when blocking draw this graphic - const len = mob[i].vertices.length - 1; - ctx.lineWidth = 1; - ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` - ctx.strokeStyle = "#000"; - ctx.beginPath(); - ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); - ctx.lineTo(mob[i].vertices[len].x, mob[i].vertices[len].y); - ctx.lineTo(mob[i].vertices[0].x, mob[i].vertices[0].y); - ctx.fill(); - ctx.stroke(); - for (let j = 0; j < len; j++) { - ctx.beginPath(); - ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); - ctx.lineTo(mob[i].vertices[j].x, mob[i].vertices[j].y); - ctx.lineTo(mob[i].vertices[j + 1].x, mob[i].vertices[j + 1].y); - ctx.fill(); - ctx.stroke(); - } - } - m.bulletsToBlocks(mob[i]) - if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField) - //mob knock backs - const massRoot = Math.sqrt(Math.max(1, mob[i].mass)); - Matter.Body.setVelocity(mob[i], { - x: player.velocity.x - (30 * unit.x) / massRoot, - y: player.velocity.y - (30 * unit.y) / massRoot - }); - if (mob[i].isUnstable) { - if (m.fieldCDcycle < m.cycle + 10) m.fieldCDcycle = m.cycle + 6 - mob[i].death(); - } - if (!isFree) { //player knock backs - if (mob[i].isDropPowerUp && player.speed < 12) { - const massRootCap = Math.sqrt(Math.min(10, Math.max(0.2, mob[i].mass))); - Matter.Body.setVelocity(player, { - x: 0.9 * player.velocity.x + 0.6 * unit.x * massRootCap, - y: 0.9 * player.velocity.y + 0.6 * unit.y * massRootCap - }); - } - } - } - } - } - } - m.hold = function () { - const wave = Math.sin(m.cycle * 0.022); - m.fieldRange = 180 + 12 * wave + 100 * tech.isBigField - m.fieldArc = 0.35 + 0.045 * wave + 0.065 * tech.isBigField //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) - m.calculateFieldThreshold(); - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field) { //not hold but field button is pressed - //float while field is on - const angleReduction = 0.5 + 0.7 * (Math.PI / 2 - Math.min(Math.PI / 2, Math.abs(m.angle + Math.PI / 2))) - // console.log(angleReduction) - if (player.velocity.y > 1) { - player.force.y -= angleReduction * (tech.isBigField ? 0.95 : 0.5) * player.mass * simulation.g; - Matter.Body.setVelocity(player, { - x: player.velocity.x, - y: 0.98 * player.velocity.y - }); //set velocity to cap, but keep the direction - } - - // go invulnerable while field is active, but also drain energy - // if (true && m.energy > 2 * m.fieldRegen && m.immuneCycle < m.cycle + tech.cyclicImmunity) { - // m.immuneCycle = m.cycle + 1; //player is immune to damage for 60 cycles - // m.energy -= 2 * m.fieldRegen - // if (m.energy < m.fieldRegen) m.fieldCDcycle = m.cycle + 90; - // } - - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - m.fieldPosition = { x: m.pos.x, y: m.pos.y } - m.fieldAngle = m.angle - //draw field attached to player - if (m.holdingTarget) { - ctx.fillStyle = `rgba(110,150,220, ${0.06 + 0.03 * Math.random()})` - ctx.strokeStyle = `rgba(110,150,220, ${0.35 + 0.05 * Math.random()})` - } else { - ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` - ctx.strokeStyle = `rgba(110,150,220, ${0.4 + 0.5 * Math.random()})` - } - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false); - ctx.lineWidth = 2.5 - 1.5 * wave; - ctx.stroke(); - const curve = 0.57 + 0.04 * wave - const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc - let a = m.angle + aMag - let cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) - let cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) - ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle)) - a = m.angle - aMag - cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) - cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) - ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * m.fieldRange * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * m.fieldRange * Math.sin(m.angle - Math.PI * m.fieldArc)) - ctx.fill(); - m.perfectPush(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } 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 (!input.field) { //&& tech.isFieldFree - //draw field free of player - ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` - ctx.strokeStyle = `rgba(110,180,255, ${0.4 + 0.5 * Math.random()})` - ctx.beginPath(); - ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false); - ctx.lineWidth = 2.5 - 1.5 * wave; - ctx.stroke(); - const curve = 0.8 + 0.06 * wave - const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc - let a = m.fieldAngle + aMag - ctx.quadraticCurveTo(m.fieldPosition.x + curve * m.fieldRange * Math.cos(a), m.fieldPosition.y + curve * m.fieldRange * Math.sin(a), m.fieldPosition.x + 1 * m.fieldRange * Math.cos(m.fieldAngle - Math.PI * m.fieldArc), m.fieldPosition.y + 1 * m.fieldRange * Math.sin(m.fieldAngle - Math.PI * m.fieldArc)) - ctx.fill(); - m.perfectPush(true); - } - } - // m.drawRegenEnergy() - m.drawRegenEnergy("rgba(0,0,0,0.2)") - if (tech.isPerfectBrake) { //cap mob speed around player - const range = 200 + 140 * wave + 150 * m.energy - for (let i = 0; i < mob.length; i++) { - const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position)) - if (distance < range) { - const cap = mob[i].isShielded ? 8 : 4 - if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(m.pos, mob[i].position)) > 0) { // if velocity is directed towards player - Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction - } - } - } - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI); - ctx.fillStyle = "hsla(200,50%,61%,0.08)"; - ctx.fill(); - } - } - } - }, - { - name: "negative mass", - //
hold blocks as if they have a lower mass - description: `use energy to nullify  gravity
0.5x damage taken
6 energy per second`, - fieldDrawRadius: 0, - 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.5; - m.fieldDrawRadius = 0; - - m.hold = function () { - m.airSpeedLimit = 125 //5 * player.mass * player.mass - m.FxAir = 0.016 - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field) { //push away - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - if (m.energy > tech.negativeMassCost && m.fieldCDcycle < m.cycle) { - if (tech.isFlyFaster) { - //look for nearby objects to make zero-g - function moveThis(who, range, mag = 1.06) { - 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); //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 - } - //loose attraction to player - // const sub = Vector.sub(m.pos, body[i].position) - // const unit = Vector.mult(Vector.normalise(sub), who[i].mass * 0.0000002 * Vector.magnitude(sub)) - // body[i].force.x += unit.x - // body[i].force.y += unit.y - } - } - } - //control horizontal acceleration - m.airSpeedLimit = 1000 // 7* player.mass * player.mass - m.FxAir = 0.01 - //control vertical acceleration - if (input.down) { //down - player.force.y += 0.5 * player.mass * simulation.g; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 500 * 0.03; - moveThis(powerUp, this.fieldDrawRadius, 0); - moveThis(body, this.fieldDrawRadius, 0); - } else if (input.up) { //up - m.energy -= 5 * tech.negativeMassCost; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 1100 * 0.03; - player.force.y -= 2.25 * player.mass * simulation.g; - moveThis(powerUp, this.fieldDrawRadius, 1.8); - moveThis(body, this.fieldDrawRadius, 1.8); - } else { - m.energy -= tech.negativeMassCost; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 800 * 0.03; - player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift - moveThis(powerUp, this.fieldDrawRadius); - moveThis(body, this.fieldDrawRadius); - } - } else { - //look for nearby objects to make zero-g - function verticalForce(who, range, mag = 1.06) { - 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); //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 - m.airSpeedLimit = 400 // 7* player.mass * player.mass - m.FxAir = 0.005 - //control vertical acceleration - if (input.down) { //down - player.force.y -= 0.5 * player.mass * simulation.g; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 400 * 0.03; - verticalForce(powerUp, this.fieldDrawRadius, 0.7); - verticalForce(body, this.fieldDrawRadius, 0.7); - } else if (input.up) { //up - m.energy -= 5 * tech.negativeMassCost; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 850 * 0.03; - player.force.y -= 1.45 * player.mass * simulation.g; - verticalForce(powerUp, this.fieldDrawRadius, 1.38); - verticalForce(body, this.fieldDrawRadius, 1.38); - } else { - m.energy -= tech.negativeMassCost; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 650 * 0.03; - player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift - verticalForce(powerUp, this.fieldDrawRadius); - verticalForce(body, this.fieldDrawRadius); - } - } - - if (m.energy < 0.001) { - m.fieldCDcycle = m.cycle + 120; - m.energy = 0; - } - //add extra friction for horizontal motion - if (input.down || input.up || input.left || input.right) { - Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 }); - } else { //slow rise and fall - Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 }); - } - // if (tech.isFreezeMobs) { - // const ICE_DRAIN = 0.0005 - // for (let i = 0, len = mob.length; i < len; i++) { - // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && ((mob[i].distanceToPlayer() + mob[i].radius) < this.fieldDrawRadius)) { - // if (m.energy > ICE_DRAIN * 2) { - // m.energy -= ICE_DRAIN; - // this.fieldDrawRadius -= 2; - // mobs.statusSlow(mob[i], 60) - // } else { - // break; - // } - // } - // } - // } - //draw zero-G range - if (!simulation.isTimeSkipping) { - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, this.fieldDrawRadius, 0, 2 * Math.PI); - ctx.fillStyle = "#f5f5ff"; - ctx.globalCompositeOperation = "difference"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; - } - } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - this.fieldDrawRadius = 0 - } 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) - this.fieldDrawRadius = 0 - } - m.drawRegenEnergy("rgba(0,0,0,0.2)") - - - // if (tech.isHealAttract) { - // for (let i = 0; i < powerUp.length; i++) { - // if (powerUp[i].name === "heal") { - // //&& Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 500000 - // let attract = Vector.mult(Vector.normalise(Vector.sub(m.pos, powerUp[i].position)), 0.01 * powerUp[i].mass) - // powerUp[i].force.x += attract.x; - // powerUp[i].force.y += attract.y - powerUp[i].mass * simulation.g; //negate gravity - // Matter.Body.setVelocity(powerUp[i], Vector.mult(powerUp[i].velocity, 0.7)); - // } - // } - // } - - - // powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; - // powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity - // //extra friction - // Matter.Body.setVelocity(powerUp[i], { - // x: powerUp[i].velocity.x * 0.11, - // y: powerUp[i].velocity.y * 0.11 - // }); - - } - } - }, - { - name: "molecular assembler", - description: `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second`, - setDescription() { - return `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second` - }, - - effect: () => { - m.fieldMeterColor = "#ff0" - m.eyeFillColor = m.fieldMeterColor - m.hold = function () { - if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 300 && (m.cycle % 2)) { - if (simulation.molecularMode === 0) { - if (tech.isSporeFlea) { - const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 - if (m.energy > drain) { - m.energy -= drain - const speed = m.crouch ? 20 + 8 * Math.random() : 10 + 3 * Math.random() - b.flea({ - x: m.pos.x + 35 * Math.cos(m.angle), - y: m.pos.y + 35 * Math.sin(m.angle) - }, { - x: speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - }) - } - } else if (tech.isSporeWorm) { - const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 - if (m.energy > drain) { - m.energy -= drain - b.worm({ - x: m.pos.x + 35 * Math.cos(m.angle), - y: m.pos.y + 35 * Math.sin(m.angle) - }) - const SPEED = 2 + 1 * Math.random(); - Matter.Body.setVelocity(bullet[bullet.length - 1], { - x: SPEED * Math.cos(m.angle), - y: SPEED * Math.sin(m.angle) - }); - } - } else { - const drain = 0.095 + (Math.max(bullet.length, 130) - 130) * 0.01 - for (let i = 0, len = Math.random() * 20; i < len; i++) { - if (m.energy > 3 * drain) { - m.energy -= drain - b.spore(m.pos) - } else { - break - } - } - } - } else if (simulation.molecularMode === 1) { - m.energy -= 0.33; - const direction = { x: Math.cos(m.angle), y: Math.sin(m.angle) } - const push = Vector.mult(Vector.perp(direction), 0.08) - b.missile({ x: m.pos.x + 30 * direction.x, y: m.pos.y + 30 * direction.y }, m.angle, -15) - bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5) - bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) - // b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) - } else if (simulation.molecularMode === 2) { - m.energy -= 0.044; - b.iceIX(1) - } else if (simulation.molecularMode === 3) { - if (tech.isDroneRadioactive) { - const drain = 0.8 + (Math.max(bullet.length, 50) - 50) * 0.01 - if (m.energy > drain) { - m.energy -= drain - b.droneRadioactive({ - x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), - y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) - }, 25) - } - } else { - //every bullet above 100 adds 0.005 to the energy cost per drone - //at 200 bullets the energy cost is 0.45 + 100*0.006 = 1.05 - const drain = (0.45 + (Math.max(bullet.length, 100) - 100) * 0.006) * tech.droneEnergyReduction - if (m.energy > drain) { - m.energy -= drain - b.drone() - } - } - } - } - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - if (tech.isPrinter && m.holdingTarget.isPrinted && input.field) { - // if (Math.random() < 0.004 && m.holdingTarget.vertices.length < 12) m.holdingTarget.vertices.push({ x: 0, y: 0 }) //small chance to increase the number of vertices - m.holdingTarget.radius += Math.min(1.1, 1.3 / m.holdingTarget.mass) //grow up to a limit - const r1 = m.holdingTarget.radius * (1 + 0.12 * Math.sin(m.cycle * 0.11)) - const r2 = m.holdingTarget.radius * (1 + 0.12 * Math.cos(m.cycle * 0.11)) - let angle = (m.cycle * 0.01) % (2 * Math.PI) //rotate the object - let vertices = [] - for (let i = 0, len = m.holdingTarget.vertices.length; i < len; i++) { - angle += 2 * Math.PI / len - vertices.push({ x: m.holdingTarget.position.x + r1 * Math.cos(angle), y: m.holdingTarget.position.y + r2 * Math.sin(angle) }) - } - Matter.Body.setVertices(m.holdingTarget, vertices) - m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale) - } - m.throwBlock(); - } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - if (tech.isPrinter && input.down) { - m.printBlock(); - } else if (m.energy > m.minEnergyToDeflect) { - m.drawField(); - m.pushMobsFacing(); - } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } 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) - } - m.drawRegenEnergy() - } - } - }, - { - name: "plasma torch", - description: "use energy to emit short range plasma
1.5x damage
10 energy per second", - set() { - b.isExtruderOn = false - m.fieldDamage = 1.5 - // m.fieldCDcycleAlternate = 0 - - if (m.plasmaBall) { - m.plasmaBall.reset() - Matter.Composite.remove(engine.world, m.plasmaBall); - } - if (tech.isPlasmaBall) { - const circleRadiusScale = 2 - m.plasmaBall = Bodies.circle(m.pos.x + 10 * Math.cos(m.angle), m.pos.y + 10 * Math.sin(m.angle), 1, { - // collisionFilter: { - // group: 0, - // category: 0, - // mask: 0 //cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield - // }, - isSensor: true, - frictionAir: 0, - alpha: 0.7, - isPopping: false, - isAttached: false, - isOn: false, - drain: 0.0017, - radiusLimit: 10, - damage: 0.8, - setPositionToNose() { - const nose = { x: m.pos.x + 10 * Math.cos(m.angle), y: m.pos.y + 10 * Math.sin(m.angle) } - Matter.Body.setPosition(this, Vector.add(nose, Vector.mult(Vector.normalise(Vector.sub(nose, m.pos)), circleRadiusScale * this.circleRadius))); - }, - fire() { - this.isAttached = false; - const speed = 10 //scale with mass? - Matter.Body.setVelocity(this, { - x: player.velocity.x * 0.4 + speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - }); - m.plasmaBall.setPositionToNose() - if (this.circleRadius < 10) this.isPopping = true - }, - scale(scale) { - Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast - if (this.circleRadius < this.radiusLimit) this.reset() - }, - reset() { - // console.log(this.circleRadius) - const scale = 1 / m.plasmaBall.circleRadius - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - // console.log(this.circleRadius) - // this.circleRadius = 0 - this.alpha = 0.7 - this.isOn = false - this.isPopping = false - // this.isAttached = true; - }, - do() { - if (this.isOn) { - //collisions with map - if (Matter.Query.collides(this, map).length > 0) { - if (this.isAttached) { - this.scale(Math.max(0.9, 0.998 - 0.1 / m.plasmaBall.circleRadius)) - } else { - this.isPopping = true - } - } - if (this.isPopping) { - this.alpha -= 0.03 - if (this.alpha < 0.1) { - this.reset() - } else { - const scale = 1.04 + 4 / Math.max(1, m.plasmaBall.circleRadius) - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - } - // if (this.speed > 2.5) { - // const slow = 0.9 - // Matter.Body.setVelocity(this, { - // x: slow * this.velocity.x, - // y: slow * this.velocity.y - // }); - // } - } - //collisions with mobs - // const whom = Matter.Query.collides(this, mob) - // const dmg = this.damage * m.dmgScale - // for (let i = 0, len = whom.length; i < len; i++) { - // const mobHit = (who) => { - // if (who.alive) { - // if (!this.isAttached && !who.isMobBullet) this.isPopping = true - // who.damage(dmg); - // // if (who.shield) this.scale(Math.max(0.9, 0.99 - 0.5 / m.plasmaBall.circleRadius)) - // if (who.speed > 5) { - // Matter.Body.setVelocity(who, { //friction - // x: who.velocity.x * 0.6, - // y: who.velocity.y * 0.6 - // }); - // } else { - // Matter.Body.setVelocity(who, { //friction - // x: who.velocity.x * 0.93, - // y: who.velocity.y * 0.93 - // }); - // } - // } - // } - // mobHit(whom[i].bodyA) - // mobHit(whom[i].bodyB) - // } - - //damage nearby mobs - const dmg = this.damage * m.dmgScale - const arcList = [] - const damageRadius = circleRadiusScale * this.circleRadius - const dischargeRange = 150 + 1600 * tech.plasmaDischarge + 1.3 * damageRadius - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet) && !mob[i].isInvulnerable) { - const sub = Vector.magnitude(Vector.sub(this.position, mob[i].position)) - if (sub < damageRadius + mob[i].radius) { - // if (!this.isAttached && !mob[i].isMobBullet) this.isPopping = true - mob[i].damage(dmg); - if (mob[i].speed > 5) { - Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.6, y: mob[i].velocity.y * 0.6 }); - } else { - Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.93, y: mob[i].velocity.y * 0.93 }); - } - } else if (sub < dischargeRange + mob[i].radius && Matter.Query.ray(map, mob[i].position, this.position).length === 0) { - arcList.push(mob[i]) //populate electrical arc list - } - } - } - for (let i = 0; i < arcList.length; i++) { - if (tech.plasmaDischarge > Math.random()) { - const who = arcList[Math.floor(Math.random() * arcList.length)] - who.damage(dmg * 4); - //draw arcs - const sub = Vector.sub(who.position, this.position) - const unit = Vector.normalise(sub) - let len = 12 - const step = Vector.magnitude(sub) / (len + 2) - let x = this.position.x - let y = this.position.y - ctx.beginPath(); - ctx.moveTo(x, y); - for (let i = 0; i < len; i++) { - x += step * (unit.x + (Math.random() - 0.5)) - y += step * (unit.y + (Math.random() - 0.5)) - ctx.lineTo(x, y); - } - ctx.lineTo(who.position.x, who.position.y); - ctx.strokeStyle = "#88f"; - ctx.lineWidth = 4 + 3 * Math.random(); - ctx.stroke(); - if (who.damageReduction) { - simulation.drawList.push({ - x: who.position.x, - y: who.position.y, - radius: 15, - color: "rgba(150,150,255,0.4)", - time: 15 - }); - } - } - } - - - //slowly slow down if too fast - if (this.speed > 10) { - const scale = 0.998 - Matter.Body.setVelocity(this, { x: scale * this.velocity.x, y: scale * this.velocity.y }); - } - - //graphics - const radius = circleRadiusScale * this.circleRadius * (0.99 + 0.02 * Math.random()) + 3 * Math.random() - const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, radius); - const alpha = this.alpha + 0.1 * Math.random() - gradient.addColorStop(0, `rgba(255,255,255,${alpha})`); - gradient.addColorStop(0.35 + 0.1 * Math.random(), `rgba(255,150,255,${alpha})`); - gradient.addColorStop(1, `rgba(255,0,255,${alpha})`); - // gradient.addColorStop(1, `rgba(255,150,255,${alpha})`); - ctx.fillStyle = gradient - ctx.beginPath(); - ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); - ctx.fill(); - //draw arcs - const unit = Vector.rotate({ x: 1, y: 0 }, Math.random() * 6.28) - let len = 8 - const step = this.circleRadius / len - let x = this.position.x - let y = this.position.y - ctx.beginPath(); - if (Math.random() < 0.5) { - x += step * (unit.x + 6 * (Math.random() - 0.5)) - y += step * (unit.y + 6 * (Math.random() - 0.5)) - len -= 2 - } - if (Math.random() < 0.5) { - x += step * (unit.x + 6 * (Math.random() - 0.5)) - y += step * (unit.y + 6 * (Math.random() - 0.5)) - len -= 2 - } - ctx.moveTo(x, y); - - for (let i = 0; i < len; i++) { - x += step * (unit.x + 1.9 * (Math.random() - 0.5)) - y += step * (unit.y + 1.9 * (Math.random() - 0.5)) - ctx.lineTo(x, y); - } - ctx.strokeStyle = "#88f"; - ctx.lineWidth = 2 * Math.random(); - ctx.stroke(); - } - }, - }); - - Composite.add(engine.world, m.plasmaBall); - // m.plasmaBall.startingVertices = m.plasmaBall.vertices.slice(); + effect: () => { m.hold = function () { if (m.isHolding) { m.drawHold(m.holdingTarget); m.holding(); m.throwBlock(); - } else if (input.field) { //not hold but field button is pressed + } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen m.grabPowerUp(); m.lookForPickUp(); - if (m.fieldCDcycle < m.cycle) { - //field is active - if (!m.plasmaBall.isAttached) { //return ball to player - if (m.plasmaBall.isOn) { - m.plasmaBall.isPopping = true - } else { - m.plasmaBall.isAttached = true - m.plasmaBall.isOn = true - m.plasmaBall.isPopping = false - m.plasmaBall.alpha = 0.7 - m.plasmaBall.setPositionToNose() - // m.plasmaBall.reset() - - } - } else if (m.energy > m.plasmaBall.drain) { //charge up when attached - if (tech.isCapacitor) { - m.energy -= m.plasmaBall.drain * 2; - const scale = 1 + 48 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - } else { - m.energy -= m.plasmaBall.drain; - const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - } - if (m.energy > m.maxEnergy) { - m.energy -= m.plasmaBall.drain * 2; - const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - } - m.plasmaBall.setPositionToNose() - - //add friction for player when holding ball, more friction in vertical - // const floatScale = Math.sqrt(m.plasmaBall.circleRadius) - // const friction = 0.0002 * floatScale - // const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - friction * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - friction * Math.abs(player.velocity.y)) //down : up - // Matter.Body.setVelocity(player, { - // x: Math.max(0.95, 1 - friction * Math.abs(player.velocity.x)) * player.velocity.x, - // y: slowY * player.velocity.y - // }); - - // if (player.velocity.y > 7) player.force.y -= 0.95 * player.mass * simulation.g //less gravity when falling fast - // player.force.y -= Math.min(0.95, 0.05 * floatScale) * player.mass * simulation.g; //undo some gravity on up or down - - //float - const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up - Matter.Body.setVelocity(player, { - x: Math.max(0.95, 1 - 0.003 * Math.abs(player.velocity.x)) * player.velocity.x, - y: slowY * player.velocity.y - }); - if (player.velocity.y > 5) { - player.force.y -= 0.9 * player.mass * simulation.g //less gravity when falling fast - } else { - player.force.y -= 0.5 * player.mass * simulation.g; - } - } else { - m.fieldCDcycle = m.cycle + 90; - m.plasmaBall.fire() - } + if (m.energy > m.minEnergyToDeflect) { + m.drawField(); + m.pushMobsFacing(); } } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released m.pickUp(); - if (m.plasmaBall.isAttached) { - m.fieldCDcycle = m.cycle + 30; - m.plasmaBall.fire() - } } 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 (m.plasmaBall.isAttached) { - m.fieldCDcycle = m.cycle + 30; - m.plasmaBall.fire() - } - } - m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") - m.plasmaBall.do() - } - } else if (tech.isExtruder) { - m.hold = function () { - b.isExtruderOn = false - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - b.extruder(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } 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) - } - m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") - if (input.field) { - b.wasExtruderOn = true - } else { - b.wasExtruderOn = false - b.canExtruderFire = true - } - ctx.beginPath(); //draw all the wave bullets - for (let i = 1, len = bullet.length; i < len; i++) { //skip the first bullet (which is is oldest bullet) - if (bullet[i].isWave) { - if (bullet[i].isBranch || bullet[i - 1].isBranch) { - ctx.moveTo(bullet[i].position.x, bullet[i].position.y) - } else { - ctx.lineTo(bullet[i].position.x, bullet[i].position.y) - } - } - } - if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) - ctx.lineWidth = 4; - ctx.strokeStyle = "#f07" - ctx.stroke(); - ctx.lineWidth = tech.extruderRange; - ctx.strokeStyle = "rgba(255,0,110,0.06)" - ctx.stroke(); - } - // } else if (true) { //plasma sword slash - // const plasmaSweepCycles = 30 - // m.plasmaSweep = 0 - // m.plasmaSlashDirection = m.flipLegs//Math.random() > 0.5 ? 1 : -1; - // m.hold = function () { - // if (m.isHolding) { - // m.drawHold(m.holdingTarget); - // m.holding(); - // m.throwBlock(); - // m.plasmaSweep = 0 - // // } else if (true) { //not hold but field button is pressed - // } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - // m.grabPowerUp(); - // m.lookForPickUp(); - - // //graphics - // if (m.plasmaSweep === 0) m.plasmaSlashDirection = m.flipLegs //Math.random() > 0.5 ? 1 : -1; - // const angle = m.angle //+ 1 * (m.plasmaSweep - plasmaSweepCycles / 2) / plasmaSweepCycles * m.plasmaSlashDirection - // const plasmaSweepCapped = Math.min(m.plasmaSweep, plasmaSweepCycles - 8) / plasmaSweepCycles - // const range = 100 * plasmaSweepCapped - // const arc = 1.3 - // const A = { x: m.pos.x + range * Math.cos(angle - arc), y: m.pos.y + range * Math.sin(angle - arc) } - // const B = { x: m.pos.x + range * Math.cos(angle + arc), y: m.pos.y + range * Math.sin(angle + arc) } - // const controlRange = 500 * plasmaSweepCapped - // const AC = { x: m.pos.x + controlRange * Math.cos(angle - arc / 2), y: m.pos.y + controlRange * Math.sin(angle - arc / 2) } - // const BC = { x: m.pos.x + controlRange * Math.cos(angle + arc / 2), y: m.pos.y + controlRange * Math.sin(angle + arc / 2) } - // const innerControlRange = 300 * plasmaSweepCapped - // const ACinner = { x: m.pos.x + innerControlRange * Math.cos(angle - arc / 2), y: m.pos.y + innerControlRange * Math.sin(angle - arc / 2) } - // const BCinner = { x: m.pos.x + innerControlRange * Math.cos(angle + arc / 2), y: m.pos.y + innerControlRange * Math.sin(angle + arc / 2) } - // ctx.beginPath(); - // ctx.moveTo(A.x, A.y) - // ctx.bezierCurveTo(AC.x, AC.y, BC.x, BC.y, B.x, B.y); //outer curve - // ctx.bezierCurveTo(BCinner.x, BCinner.y, ACinner.x, ACinner.y, A.x, A.y); //inner curve - // // ctx.strokeStyle = "#000" - // // ctx.stroke(); - // ctx.fillStyle = "rgba(255,0,255,0.5)" - // ctx.fill(); - - // //draw control points for graphics reference - // ctx.lineWidth = '0.5' - // ctx.beginPath(); - // ctx.arc(A.x, A.y, 5, 0, 2 * Math.PI); - // ctx.stroke(); - // ctx.beginPath(); - // ctx.arc(B.x, B.y, 5, 0, 2 * Math.PI); - // ctx.stroke(); - // ctx.beginPath(); - // ctx.arc(AC.x, AC.y, 5, 0, 2 * Math.PI); - // ctx.stroke(); - // ctx.beginPath(); - // ctx.arc(BC.x, BC.y, 5, 0, 2 * Math.PI); - // ctx.stroke(); - // ctx.beginPath(); - // ctx.arc(ACinner.x, ACinner.y, 5, 0, 2 * Math.PI); - // ctx.stroke(); - // ctx.beginPath(); - // ctx.arc(BCinner.x, BCinner.y, 5, 0, 2 * Math.PI); - // ctx.stroke(); - - // //mob collision detection - // collideRange = 160 - // const collideCenter = { - // x: m.pos.x + collideRange * Math.cos(angle), - // y: m.pos.y + collideRange * Math.sin(angle) - // } - // ctx.beginPath(); - // ctx.arc(collideCenter.x, collideCenter.y, 140, 0, 2 * Math.PI); - // ctx.stroke(); - - // //push mob away and slow them? - - - // //sweeping motion and cooldown - // m.plasmaSweep++ - // if (m.plasmaSweep > plasmaSweepCycles) { - // m.plasmaSweep = 0 - // if (m.fireCDcycle < m.cycle + 30) m.fieldCDcycle = m.cycle + 30 - // } - // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - // m.pickUp(); - // m.plasmaSweep = 0 - // } else { - // m.plasmaSweep = 0 - // 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) - // } - // m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") - // } - } else { - m.hold = function () { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - b.plasma(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } 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) - } - m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") - } - } - }, - effect() { - m.fieldMeterColor = "#f0f" - m.eyeFillColor = m.fieldMeterColor - this.set(); - } - }, - { - name: "time dilation", - description: `use energy to stop time
1.2x movement and fire rate
12 energy per second`, - set() { - // m.fieldMeterColor = "#0fc" - // m.fieldMeterColor = "#ff0" - m.fieldMeterColor = "#3fe" - m.eyeFillColor = m.fieldMeterColor - m.fieldFx = 1.25 - // m.fieldJump = 1.09 - m.setMovement(); - b.setFireCD() - const timeStop = () => { - m.immuneCycle = m.cycle + 10; //invulnerable to harm while time is stopped, this also disables regen - //draw field everywhere - ctx.globalCompositeOperation = "saturation" - ctx.fillStyle = "#ccc"; - ctx.fillRect(-50000, -50000, 100000, 100000) - ctx.globalCompositeOperation = "source-over" - //stop time - m.isTimeDilated = true; - - function sleep(who) { - for (let i = 0, len = who.length; i < len; ++i) { - if (!who[i].isSleeping) { - who[i].storeVelocity = who[i].velocity - who[i].storeAngularVelocity = who[i].angularVelocity - } - Matter.Sleeping.set(who[i], true) - } - } - sleep(mob); - sleep(body); - sleep(bullet); - simulation.cycle--; //pause all functions that depend on game cycle increasing - } - if (tech.isRewindField) { - this.rewindCount = 0 - m.grabPowerUpRange2 = 300000// m.grabPowerUpRange2 = 200000; - - m.hold = function () { - // console.log(m.fieldCDcycle) - m.grabPowerUp(); - // //grab power ups - // for (let i = 0, len = powerUp.length; i < len; ++i) { - // if ( - // Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 && - // !simulation.isChoosing && - // (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) - // ) { - // powerUps.onPickUp(powerUp[i]); - // powerUp[i].effect(); - // Matter.Composite.remove(engine.world, powerUp[i]); - // powerUp.splice(i, 1); - // break; //because the array order is messed up after splice - // } - // } - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - m.wakeCheck(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - const drain = 0.0014 / (1 + 0.05 * m.coupling) - if (m.energy > drain) m.energy -= drain - m.grabPowerUp(); - if (this.rewindCount === 0) { - m.lookForPickUp(); - } - - if (!m.holdingTarget) { - if (this.rewindCount === 0) { //large upfront energy cost to enter rewind mode - if (m.energy > 0.3) { - m.energy -= 0.3 - } else { - this.rewindCount = 0; - m.resetHistory(); - if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 - m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity - } - } - this.rewindCount += 6; - const DRAIN = 0.003 - let history = m.history[(m.cycle - this.rewindCount) % 600] - if (this.rewindCount > 599 || m.energy < DRAIN) { - this.rewindCount = 0; - m.resetHistory(); - if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 - m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity - } else { - //draw field everywhere - ctx.globalCompositeOperation = "saturation" - ctx.fillStyle = "#ccc"; - ctx.fillRect(-100000, -100000, 200000, 200000) - ctx.globalCompositeOperation = "source-over" - // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme - m.energy -= DRAIN - if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 5 cycles - Matter.Body.setPosition(player, history.position); - Matter.Body.setVelocity(player, { - x: history.velocity.x, - y: history.velocity.y - }); - if (m.health < history.health) { - m.health = history.health - if (m.health > m.maxHealth) m.health = m.maxHealth - m.displayHealth(); - } - m.yOff = history.yOff - if (m.yOff < 48) { - m.doCrouch() - } else { - m.undoCrouch() - } - if (tech.isRewindBot && !(this.rewindCount % 60)) { - for (let i = 0; i < tech.isRewindBot; i++) { - b.randomBot(m.pos, false, false) - bullet[bullet.length - 1].endCycle = simulation.cycle + 300 + Math.floor(180 * Math.random()) //8-9 seconds - } - } - if (tech.isRewindGrenade && !(this.rewindCount % 30)) { - b.grenade(m.pos, this.rewindCount) //Math.PI / 2 - const who = bullet[bullet.length - 1] - who.endCycle = simulation.cycle + 120 - } - } - } - m.wakeCheck(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - this.rewindCount = 0; - m.wakeCheck(); - } else if (tech.isTimeStop && player.speed < 1 && m.onGround && !input.fire) { - timeStop(); - this.rewindCount = 0; - } 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) - this.rewindCount = 0; - m.wakeCheck(); - } - m.drawRegenEnergy() // this calls m.regenEnergy(); also - } - } else { - m.fieldFire = true; - m.isTimeDilated = false; - m.hold = function () { - if (m.isHolding) { - m.wakeCheck(); - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { - const drain = 0.0026 / (1 + 0.03 * m.coupling) - if (m.energy > drain) m.energy -= drain - m.grabPowerUp(); - m.lookForPickUp(); //this drains energy 0.001 - if (m.energy > drain) { - timeStop(); - } else { //holding, but field button is released - m.fieldCDcycle = m.cycle + 120; - m.energy = 0; - m.wakeCheck(); - m.wakeCheck(); - } - } else if (tech.isTimeStop && player.speed < 1 && m.onGround && m.fireCDcycle < m.cycle && !input.fire) { - timeStop(); - //makes things move at 1/5 time rate, but has an annoying flicker for mob graphics, and other minor bugs - // if (!(m.cycle % 4)) { - // // requestAnimationFrame(() => { - // m.wakeCheck(); - // // simulation.timePlayerSkip(1) - // // }); //wrapping in animation frame prevents errors, probably - // ctx.globalCompositeOperation = "saturation" - // ctx.fillStyle = "#ccc"; - // ctx.fillRect(-100000, -100000, 200000, 200000) - // ctx.globalCompositeOperation = "source-over" - // } else { - // timeStop(); - // } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.wakeCheck(); - m.pickUp(); - } else { - m.wakeCheck(); - 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) } m.drawRegenEnergy() } } }, - effect() { - if (tech.isTimeStop) { - m.fieldHarmReduction = 0.6; - } else { - m.fieldHarmReduction = 1; - } - this.set(); - } - }, - { - name: "metamaterial cloaking", - description: `0.3x damage taken while cloaked
after decloaking 4.5x damage for 2 s
6 energy per second`, - effect: () => { - m.fieldFire = true; - m.fieldMeterColor = "#333"; - m.eyeFillColor = m.fieldMeterColor - m.fieldPhase = 0; - m.isCloak = false - m.fieldDrawRadius = 0 - m.isSneakAttack = true; - m.sneakAttackCycle = 0; - m.enterCloakCycle = 0; - m.drawCloakedM = function () { - m.walk_cycle -= m.flipLegs * m.Vx; - m.pos.x += 4 - m.draw(); - } - m.drawCloak = function () { - m.fieldPhase += 0.007 - const wiggle = 0.15 * Math.sin(m.fieldPhase * 0.5) - ctx.beginPath(); - ctx.ellipse(m.pos.x, m.pos.y, m.fieldDrawRadius * (1 - wiggle), m.fieldDrawRadius * (1 + wiggle), m.fieldPhase, 0, 2 * Math.PI); - ctx.fillStyle = "#fff" - ctx.lineWidth = 2; - ctx.strokeStyle = "#000" - // ctx.stroke() - ctx.globalCompositeOperation = "destination-in"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; - ctx.clip(); - } - m.hold = function () { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold and field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding target exists, and field button is not pressed - m.pickUp(); - } 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) - } - //not shooting (or using field) enable cloak - if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle - if (m.fireCDcycle + 10 < m.cycle && !input.fire) { //automatically cloak if not firing - // const drain = 0.02 - if (!m.isCloak) { //&& m.energy > drain + 0.03 - // m.energy -= drain - m.isCloak = true //enter cloak - m.fieldHarmReduction = 0.3; - m.enterCloakCycle = m.cycle - if (tech.isCloakHealLastHit && m.lastHit > 0) { - const heal = Math.min(0.75 * m.lastHit, m.energy) - m.addHealth(heal); //heal from last hit - m.lastHit = 0 - simulation.drawList.push({ //add dmg to draw queue - x: m.pos.x, - y: m.pos.y, - radius: Math.sqrt(heal) * 200, - color: "rgba(0,255,200,0.6)", - time: 16 - }); - } - if (tech.isIntangible) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield - } - } - } - } else if (m.isCloak) { //exit cloak - m.sneakAttackCycle = m.cycle - m.isCloak = false - m.fieldHarmReduction = 1 - - if (tech.isIntangible) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield - } - } - if (tech.isCloakStun) { //stun nearby mobs after exiting cloak - // let isMobsAround = false - const stunRange = m.fieldDrawRadius * 1.25 - // const drain = 0.01 - // if (m.energy > drain) { - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) < stunRange && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 && !mob[i].isBadTarget) { - isMobsAround = true - mobs.statusStun(mob[i], 120) - } - } - // if (isMobsAround) { - // m.energy -= drain - // simulation.drawList.push({ - // x: m.pos.x, - // y: m.pos.y, - // radius: stunRange, - // color: "hsla(0,50%,100%,0.7)", - // time: 7 - // }); - // } - // } - } - } - - if (m.isCloak) { - m.fieldRange = m.fieldRange * 0.85 + 130 - m.fieldDrawRadius = m.fieldRange * 1.1 //* 0.88 //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); - m.drawCloak() - // ctx.globalCompositeOperation = "lighter"; - // m.drawCloakedM() - // ctx.globalCompositeOperation = "source-over"; + { + name: "standing wave", + //deflecting protects you in every direction + description: `3 oscillating shields are permanently active +
+150 max energy +
6 energy per second`, + drainCD: 0, + effect: () => { + m.fieldBlockCD = 0; + m.blockingRecoil = 1 //4 is normal + m.fieldRange = 185 + m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2)) + // m.fieldHarmReduction = 0.66; //33% reduction + m.harmonic3Phase = () => { //normal standard 3 different 2-d circles + const fieldRange1 = (0.75 + 0.3 * Math.sin(m.cycle / 23)) * m.fieldRange * m.harmonicRadius + const fieldRange2 = (0.68 + 0.37 * Math.sin(m.cycle / 37)) * m.fieldRange * m.harmonicRadius + const fieldRange3 = (0.7 + 0.35 * Math.sin(m.cycle / 47)) * m.fieldRange * m.harmonicRadius + const netFieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) + ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, (0.04 + 0.7 * m.energy * (0.1 + 0.11 * Math.random()))) + ")"; ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, 35, 0, 2 * Math.PI); - ctx.strokeStyle = "rgba(255,255,255,0.25)";//"rgba(0,0,0,0.7)";//"rgba(255,255,255,0.7)";//"rgba(255,0,100,0.7)"; - ctx.lineWidth = 10 - ctx.stroke(); - - } else if (m.fieldRange < 4000) { - m.fieldRange += 90 - m.fieldDrawRadius = m.fieldRange //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); - m.drawCloak() + ctx.arc(m.pos.x, m.pos.y, fieldRange1, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange2, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange3, 0, 2 * Math.PI); + ctx.fill(); + //360 block + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < netFieldRange && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 + mob[i].locatePlayer(); + if (this.drainCD > m.cycle) { + m.pushMass(mob[i], 0); + } else { + m.pushMass(mob[i]); + this.drainCD = m.cycle + 15 + } + } + } } - if (tech.isIntangible) { - if (m.isCloak) { - player.collisionFilter.mask = cat.map - let inPlayer = Matter.Query.region(mob, player.bounds) - if (inPlayer.length > 0) { - for (let i = 0; i < inPlayer.length; i++) { - if (m.energy > 0) { - if (!inPlayer[i].isUnblockable) m.energy -= 0.003; - if (inPlayer[i].shield) m.energy -= 0.011; + m.harmonicRadius = 1 //for smoothing function when player holds mouse (for harmonicAtomic) + m.harmonicAtomic = () => { //several ellipses spinning about different axises + const rotation = simulation.cycle * 0.0031 + const phase = simulation.cycle * 0.023 + const radius = m.fieldRange * m.harmonicRadius + ctx.lineWidth = 1; + ctx.strokeStyle = "rgba(110,170,200,0.8)" + ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, 0.7 * m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics)) + ")"; + // ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; + for (let i = 0; i < tech.harmonics; i++) { + ctx.beginPath(); + ctx.ellipse(m.pos.x, m.pos.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + } + //360 block + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < radius && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 + mob[i].locatePlayer(); + if (this.drainCD > m.cycle) { + m.pushMass(mob[i], 0); + } else { + m.pushMass(mob[i]); + this.drainCD = m.cycle + 15 + } + } + } + } + if (tech.harmonics === 2) { + m.harmonicShield = m.harmonic3Phase + } else { + m.harmonicShield = m.harmonicAtomic + } + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if ((input.field) && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } 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 (m.energy > m.minEnergyToDeflect && m.fieldCDcycle < m.cycle) { + if (tech.isStandingWaveExpand) { + if (input.field) { + // const oldHarmonicRadius = m.harmonicRadius + m.harmonicRadius = 0.99 * m.harmonicRadius + 0.01 * 4 + // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius) + } else { + m.harmonicRadius = 0.994 * m.harmonicRadius + 0.006 + } + } + if (!simulation.isTimeSkipping) m.harmonicShield() + } + m.drawRegenEnergy() + } + } + }, + { + name: "perfect diamagnetism", + description: `deflecting does not drain energy
shield maintains functionality while inactive
5 energy per second`, + effect: () => { + m.fieldMeterColor = "#48f" //"#0c5" + m.eyeFillColor = m.fieldMeterColor + m.fieldShieldingScale = 0; + m.fieldBlockCD = 3; + m.grabPowerUpRange2 = 10000000 + m.fieldPosition = { x: m.pos.x, y: m.pos.y } + m.fieldAngle = m.angle + m.perfectPush = (isFree = false) => { + if (m.fieldCDcycle < m.cycle) { + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) - mob[i].radius < m.fieldRange && + !mob[i].isUnblockable && + Vector.dot({ x: Math.cos(m.fieldAngle), y: Math.sin(m.fieldAngle) }, Vector.normalise(Vector.sub(mob[i].position, m.fieldPosition))) > m.fieldThreshold && + Matter.Query.ray(map, mob[i].position, m.fieldPosition).length === 0 + ) { + mob[i].locatePlayer(); + const unit = Vector.normalise(Vector.sub(m.fieldPosition, mob[i].position)) + m.fieldCDcycle = m.cycle + m.fieldBlockCD + (mob[i].isShielded ? 10 : 0); + if (!mob[i].isInvulnerable && bullet.length < 250) { + for (let i = 0; i < m.coupling; i++) { + if (0.1 * m.coupling - i > Math.random()) { + const angle = m.fieldAngle + 4 * m.fieldArc * (Math.random() - 0.5) + const radius = m.fieldRange * (0.6 + 0.3 * Math.random()) + b.iceIX(6 + 6 * Math.random(), angle, Vector.add(m.fieldPosition, { + x: radius * Math.cos(angle), + y: radius * Math.sin(angle) + })) + } + } + } + if (tech.blockDmg) { //electricity + Matter.Body.setVelocity(mob[i], { x: 0.5 * mob[i].velocity.x, y: 0.5 * mob[i].velocity.y }); + if (mob[i].isShielded) { + for (let j = 0, len = mob.length; j < len; j++) { + if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 6 : 2), true) + } + } else if (tech.isBlockRadiation) { + if (mob[i].isMobBullet) { + mob[i].damage(tech.blockDmg * m.dmgScale * 3, true) + } else { + mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 0.42, 180) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 + } + } else { + mob[i].damage(tech.blockDmg * m.dmgScale, true) + } + // if (mob[i].isShielded) { + // for (let j = 0, len = mob.length; j < len; j++) { + // if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 3 : 1), true) + // } + // } else { + // if (tech.isBlockRadiation && !mob[i].isMobBullet) { + // mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 4 / 12, 360) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 + // } else { + // mob[i].damage(tech.blockDmg * m.dmgScale) + // } + // } + const step = 40 + ctx.beginPath(); + for (let i = 0, len = 0.5 * tech.blockDmg; i < len; i++) { + let x = m.fieldPosition.x - 20 * unit.x; + let y = m.fieldPosition.y - 20 * unit.y; + ctx.moveTo(x, y); + for (let i = 0; i < 8; i++) { + x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) + y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + } + ctx.lineWidth = 3; + ctx.strokeStyle = "#f0f"; + ctx.stroke(); + } else if (isFree) { + ctx.lineWidth = 2; //when blocking draw this graphic + ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` + ctx.strokeStyle = "#000"; + const len = mob[i].vertices.length - 1; + const mag = mob[i].radius + ctx.beginPath(); + ctx.moveTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) + for (let j = 0; j < len; j++) { + ctx.lineTo(mob[i].vertices[j].x + mag * (Math.random() - 0.5), mob[i].vertices[j].y + mag * (Math.random() - 0.5)); + } + ctx.lineTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) + ctx.fill(); + ctx.stroke(); + } else { + + const eye = 15; //when blocking draw this graphic + const len = mob[i].vertices.length - 1; + ctx.lineWidth = 1; + ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` + ctx.strokeStyle = "#000"; + ctx.beginPath(); + ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); + ctx.lineTo(mob[i].vertices[len].x, mob[i].vertices[len].y); + ctx.lineTo(mob[i].vertices[0].x, mob[i].vertices[0].y); + ctx.fill(); + ctx.stroke(); + for (let j = 0; j < len; j++) { + ctx.beginPath(); + ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); + ctx.lineTo(mob[i].vertices[j].x, mob[i].vertices[j].y); + ctx.lineTo(mob[i].vertices[j + 1].x, mob[i].vertices[j + 1].y); + ctx.fill(); + ctx.stroke(); + } + } + m.bulletsToBlocks(mob[i]) + if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField) + //mob knock backs + const massRoot = Math.sqrt(Math.max(1, mob[i].mass)); + Matter.Body.setVelocity(mob[i], { + x: player.velocity.x - (30 * unit.x) / massRoot, + y: player.velocity.y - (30 * unit.y) / massRoot + }); + if (mob[i].isUnstable) { + if (m.fieldCDcycle < m.cycle + 10) m.fieldCDcycle = m.cycle + 6 + mob[i].death(); + } + if (!isFree) { //player knock backs + if (mob[i].isDropPowerUp && player.speed < 12) { + const massRootCap = Math.sqrt(Math.min(10, Math.max(0.2, mob[i].mass))); + Matter.Body.setVelocity(player, { + x: 0.9 * player.velocity.x + 0.6 * unit.x * massRootCap, + y: 0.9 * player.velocity.y + 0.6 * unit.y * massRootCap + }); + } } } } - } else { - player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions } } - this.drawRegenEnergyCloaking() - if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) { //show sneak attack status - m.fieldDamage = 4.5 * (1 + 0.05 * m.coupling) - const timeLeft = (m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) - m.cycle) * 0.5 - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, 32, 0, 2 * Math.PI); - ctx.strokeStyle = "rgba(180,30,70,0.5)";//"rgba(0,0,0,0.7)";//"rgba(255,255,255,0.7)";//"rgba(255,0,100,0.7)"; - ctx.lineWidth = Math.max(Math.min(10, timeLeft), 3); - ctx.stroke(); - // ctx.globalCompositeOperation = "multiply"; - // m.drawCloakedM() - // ctx.globalCompositeOperation = "source-over"; - } else { - m.fieldDamage = 1 - } - } - } - }, - { - name: "pilot wave", - description: `use energy to guide blocks
,
, and
have +3 choices
10 energy per second`, - effect: () => { - m.fieldMeterColor = "#333" - m.eyeFillColor = m.fieldMeterColor + m.hold = function () { + const wave = Math.sin(m.cycle * 0.022); + m.fieldRange = 180 + 12 * wave + 100 * tech.isBigField + m.fieldArc = 0.35 + 0.045 * wave + 0.065 * tech.isBigField //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) + m.calculateFieldThreshold(); + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field) { //not hold but field button is pressed + //float while field is on + const angleReduction = 0.5 + 0.7 * (Math.PI / 2 - Math.min(Math.PI / 2, Math.abs(m.angle + Math.PI / 2))) + // console.log(angleReduction) + if (player.velocity.y > 1) { + player.force.y -= angleReduction * (tech.isBigField ? 0.95 : 0.5) * player.mass * simulation.g; + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: 0.98 * player.velocity.y + }); //set velocity to cap, but keep the direction + } - m.fieldPhase = 0; - m.fieldPosition = { - x: simulation.mouseInGame.x, - y: simulation.mouseInGame.y - } - m.lastFieldPosition = { - x: simulation.mouseInGame.x, - y: simulation.mouseInGame.y - } - m.fieldOn = false; - m.fieldRadius = 0; - m.drop(); - m.hold = function () { - if (tech.isPrinter) { - //spawn blocks if field and crouch - if (input.field && m.fieldCDcycle < m.cycle && input.down && !m.isHolding) { - m.printBlock() + // go invulnerable while field is active, but also drain energy + // if (true && m.energy > 2 * m.fieldRegen && m.immuneCycle < m.cycle + tech.cyclicImmunity) { + // m.immuneCycle = m.cycle + 1; //player is immune to damage for 60 cycles + // m.energy -= 2 * m.fieldRegen + // if (m.energy < m.fieldRegen) m.fieldCDcycle = m.cycle + 90; + // } + + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + m.fieldPosition = { x: m.pos.x, y: m.pos.y } + m.fieldAngle = m.angle + //draw field attached to player + if (m.holdingTarget) { + ctx.fillStyle = `rgba(110,150,220, ${0.06 + 0.03 * Math.random()})` + ctx.strokeStyle = `rgba(110,150,220, ${0.35 + 0.05 * Math.random()})` + } else { + ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` + ctx.strokeStyle = `rgba(110,150,220, ${0.4 + 0.5 * Math.random()})` + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2.5 - 1.5 * wave; + ctx.stroke(); + const curve = 0.57 + 0.04 * wave + const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc + let a = m.angle + aMag + let cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) + let cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle)) + a = m.angle - aMag + cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) + cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * m.fieldRange * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * m.fieldRange * Math.sin(m.angle - Math.PI * m.fieldArc)) + ctx.fill(); + m.perfectPush(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } 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 (!input.field) { //&& tech.isFieldFree + //draw field free of player + ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` + ctx.strokeStyle = `rgba(110,180,255, ${0.4 + 0.5 * Math.random()})` + ctx.beginPath(); + ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2.5 - 1.5 * wave; + ctx.stroke(); + const curve = 0.8 + 0.06 * wave + const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc + let a = m.fieldAngle + aMag + ctx.quadraticCurveTo(m.fieldPosition.x + curve * m.fieldRange * Math.cos(a), m.fieldPosition.y + curve * m.fieldRange * Math.sin(a), m.fieldPosition.x + 1 * m.fieldRange * Math.cos(m.fieldAngle - Math.PI * m.fieldArc), m.fieldPosition.y + 1 * m.fieldRange * Math.sin(m.fieldAngle - Math.PI * m.fieldArc)) + ctx.fill(); + m.perfectPush(true); + } + } + // m.drawRegenEnergy() + m.drawRegenEnergy("rgba(0,0,0,0.2)") + if (tech.isPerfectBrake) { //cap mob speed around player + const range = 200 + 140 * wave + 150 * m.energy + for (let i = 0; i < mob.length; i++) { + const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position)) + if (distance < range) { + const cap = mob[i].isShielded ? 8 : 4 + if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(m.pos, mob[i].position)) > 0) { // if velocity is directed towards player + Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction + } + } + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI); + ctx.fillStyle = "hsla(200,50%,61%,0.08)"; + ctx.fill(); + } + } + } + }, + { + name: "negative mass", + //
hold blocks as if they have a lower mass + description: `use energy to nullify  gravity
0.5x damage taken
6 energy per second`, + fieldDrawRadius: 0, + 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.5; + m.fieldDrawRadius = 0; + + m.hold = function () { + m.airSpeedLimit = 125 //5 * player.mass * player.mass + m.FxAir = 0.016 + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field) { //push away + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + if (m.energy > tech.negativeMassCost && m.fieldCDcycle < m.cycle) { + if (tech.isFlyFaster) { + //look for nearby objects to make zero-g + function moveThis(who, range, mag = 1.06) { + 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); //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 + } + //loose attraction to player + // const sub = Vector.sub(m.pos, body[i].position) + // const unit = Vector.mult(Vector.normalise(sub), who[i].mass * 0.0000002 * Vector.magnitude(sub)) + // body[i].force.x += unit.x + // body[i].force.y += unit.y + } + } + } + //control horizontal acceleration + m.airSpeedLimit = 1000 // 7* player.mass * player.mass + m.FxAir = 0.01 + //control vertical acceleration + if (input.down) { //down + player.force.y += 0.5 * player.mass * simulation.g; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 500 * 0.03; + moveThis(powerUp, this.fieldDrawRadius, 0); + moveThis(body, this.fieldDrawRadius, 0); + } else if (input.up) { //up + m.energy -= 5 * tech.negativeMassCost; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 1100 * 0.03; + player.force.y -= 2.25 * player.mass * simulation.g; + moveThis(powerUp, this.fieldDrawRadius, 1.8); + moveThis(body, this.fieldDrawRadius, 1.8); + } else { + m.energy -= tech.negativeMassCost; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 800 * 0.03; + player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift + moveThis(powerUp, this.fieldDrawRadius); + moveThis(body, this.fieldDrawRadius); + } + } else { + //look for nearby objects to make zero-g + function verticalForce(who, range, mag = 1.06) { + 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); //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 + m.airSpeedLimit = 400 // 7* player.mass * player.mass + m.FxAir = 0.005 + //control vertical acceleration + if (input.down) { //down + player.force.y -= 0.5 * player.mass * simulation.g; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 400 * 0.03; + verticalForce(powerUp, this.fieldDrawRadius, 0.7); + verticalForce(body, this.fieldDrawRadius, 0.7); + } else if (input.up) { //up + m.energy -= 5 * tech.negativeMassCost; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 850 * 0.03; + player.force.y -= 1.45 * player.mass * simulation.g; + verticalForce(powerUp, this.fieldDrawRadius, 1.38); + verticalForce(body, this.fieldDrawRadius, 1.38); + } else { + m.energy -= tech.negativeMassCost; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 650 * 0.03; + player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift + verticalForce(powerUp, this.fieldDrawRadius); + verticalForce(body, this.fieldDrawRadius); + } + } + + if (m.energy < 0.001) { + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + } + //add extra friction for horizontal motion + if (input.down || input.up || input.left || input.right) { + Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 }); + } else { //slow rise and fall + Matter.Body.setVelocity(player, { x: player.velocity.x * 0.99, y: player.velocity.y * 0.98 }); + } + // if (tech.isFreezeMobs) { + // const ICE_DRAIN = 0.0005 + // for (let i = 0, len = mob.length; i < len; i++) { + // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && ((mob[i].distanceToPlayer() + mob[i].radius) < this.fieldDrawRadius)) { + // if (m.energy > ICE_DRAIN * 2) { + // m.energy -= ICE_DRAIN; + // this.fieldDrawRadius -= 2; + // mobs.statusSlow(mob[i], 60) + // } else { + // break; + // } + // } + // } + // } + //draw zero-G range + if (!simulation.isTimeSkipping) { + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, this.fieldDrawRadius, 0, 2 * Math.PI); + ctx.fillStyle = "#f5f5ff"; + ctx.globalCompositeOperation = "difference"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + } + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + this.fieldDrawRadius = 0 + } 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) + this.fieldDrawRadius = 0 + } + m.drawRegenEnergy("rgba(0,0,0,0.2)") + + + // if (tech.isHealAttract) { + // for (let i = 0; i < powerUp.length; i++) { + // if (powerUp[i].name === "heal") { + // //&& Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 500000 + // let attract = Vector.mult(Vector.normalise(Vector.sub(m.pos, powerUp[i].position)), 0.01 * powerUp[i].mass) + // powerUp[i].force.x += attract.x; + // powerUp[i].force.y += attract.y - powerUp[i].mass * simulation.g; //negate gravity + // Matter.Body.setVelocity(powerUp[i], Vector.mult(powerUp[i].velocity, 0.7)); + // } + // } + // } + + + // powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; + // powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + // //extra friction + // Matter.Body.setVelocity(powerUp[i], { + // x: powerUp[i].velocity.x * 0.11, + // y: powerUp[i].velocity.y * 0.11 + // }); + + } + } + }, + { + name: "molecular assembler", + modeText() { + return `${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}` + }, + description: `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second ↓↘→↓↙←↑↑↓`, + setDescription() { + return `use energy to deflect mobs
excess energy used to print ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
12 energy per second ↓↘→↓↙←↑↑↓` + }, + keyLog: [], + effect: () => { + //store event function so it can be found and removed in m.setField() + m.fieldEvent = function (event) { + m.fieldUpgrades[4].keyLog.push(event.code) + + + // Helper function to compare arrays + function arraysEqual(arr1, arr2) { + if (arr1.length !== arr2.length) return false; + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] !== arr2[i]) return false; + } + return true; + } + + const pattern = [input.key.down, input.key.right, input.key.down, input.key.left, input.key.up, input.key.up, input.key.down] + //check if the newest key press is correct + if (event.code !== pattern[m.fieldUpgrades[4].keyLog.length - 1]) { + m.fieldUpgrades[4].keyLog = [] //pattern is wrong, reset log + } else if (arraysEqual(m.fieldUpgrades[4].keyLog, pattern)) { //pattern is complete + //cycle to next molecular mode + m.fieldUpgrades[4].keyLog = [] + const energy = m.energy //save current energy + if (simulation.molecularMode < 3) { + simulation.molecularMode++ + } else { + simulation.molecularMode = 0 + } + // m.setField((m.fieldMode === m.fieldUpgrades.length - 1) ? 1 : m.fieldMode + 1) //cycle to next field, skip field emitter + m.fieldUpgrades[4].description = m.fieldUpgrades[4].setDescription() + m.energy = energy //return to current energy + + const name = `${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}` + simulation.inGameConsole(`simulation.molecularMode = ${simulation.molecularMode} // ${name}   ↓↘→↓↙←↑↑↓`); + } + // console.log(m.fieldUpgrades[4].keyLog) + } + window.addEventListener("keydown", m.fieldEvent); + + m.fieldMeterColor = "#ff0" + m.eyeFillColor = m.fieldMeterColor + m.hold = function () { + if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 300 && (m.cycle % 2)) { + if (simulation.molecularMode === 0) { + if (tech.isSporeFlea) { + const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 + if (m.energy > drain) { + m.energy -= drain + const speed = m.crouch ? 20 + 8 * Math.random() : 10 + 3 * Math.random() + b.flea({ + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + }, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }) + } + } else if (tech.isSporeWorm) { + const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 + if (m.energy > drain) { + m.energy -= drain + b.worm({ + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + }) + const SPEED = 2 + 1 * Math.random(); + Matter.Body.setVelocity(bullet[bullet.length - 1], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + } + } else { + const drain = 0.095 + (Math.max(bullet.length, 130) - 130) * 0.01 + for (let i = 0, len = 5; i < len; i++) { + if (m.energy > 3 * drain) { + m.energy -= drain + b.spore(m.pos) + } else { + break + } + } + } + } else if (simulation.molecularMode === 1) { + m.energy -= 0.33; + const direction = { x: Math.cos(m.angle), y: Math.sin(m.angle) } + const push = Vector.mult(Vector.perp(direction), 0.08) + b.missile({ x: m.pos.x + 30 * direction.x, y: m.pos.y + 30 * direction.y }, m.angle, -15) + bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5) + bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) + // b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) + } else if (simulation.molecularMode === 2) { + m.energy -= 0.044; + b.iceIX(1) + } else if (simulation.molecularMode === 3) { + if (tech.isDroneRadioactive) { + const drain = 0.8 + (Math.max(bullet.length, 50) - 50) * 0.01 + if (m.energy > drain) { + m.energy -= drain + b.droneRadioactive({ + x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) + }, 25) + } + } else { + //every bullet above 100 adds 0.005 to the energy cost per drone + //at 200 bullets the energy cost is 0.45 + 100*0.006 = 1.05 + const drain = (0.45 + (Math.max(bullet.length, 100) - 100) * 0.006) * tech.droneEnergyReduction + if (m.energy > drain) { + m.energy -= drain + b.drone() + } + } + } } - //if holding block grow it if (m.isHolding) { m.drawHold(m.holdingTarget); m.holding(); @@ -5178,365 +4341,1245 @@ const m = { Matter.Body.setVertices(m.holdingTarget, vertices) m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale) } - m.throwBlock() + m.throwBlock(); + } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + if (tech.isPrinter && input.down) { + m.printBlock(); + } else if (m.energy > m.minEnergyToDeflect) { + m.drawField(); + m.pushMobsFacing(); + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); } 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 releasing field throw it - + m.drawRegenEnergy() } - if (input.field) { - if (m.fieldCDcycle < m.cycle) { - const scale = 25 - const bounds = { - min: { - x: m.fieldPosition.x - scale, - y: m.fieldPosition.y - scale - }, - max: { - x: m.fieldPosition.x + scale, - y: m.fieldPosition.y + scale + } + }, + { + name: "plasma torch", + description: "use energy to emit short range plasma
1.5x damage
10 energy per second", + set() { + b.isExtruderOn = false + m.fieldDamage = 1.5 + // m.fieldCDcycleAlternate = 0 + + if (m.plasmaBall) { + m.plasmaBall.reset() + Matter.Composite.remove(engine.world, m.plasmaBall); + } + if (tech.isPlasmaBall) { + const circleRadiusScale = 2 + m.plasmaBall = Bodies.circle(m.pos.x + 10 * Math.cos(m.angle), m.pos.y + 10 * Math.sin(m.angle), 1, { + // collisionFilter: { + // group: 0, + // category: 0, + // mask: 0 //cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield + // }, + isSensor: true, + frictionAir: 0, + alpha: 0.7, + isPopping: false, + isAttached: false, + isOn: false, + drain: 0.0017, + radiusLimit: 10, + damage: 0.8, + setPositionToNose() { + const nose = { x: m.pos.x + 10 * Math.cos(m.angle), y: m.pos.y + 10 * Math.sin(m.angle) } + Matter.Body.setPosition(this, Vector.add(nose, Vector.mult(Vector.normalise(Vector.sub(nose, m.pos)), circleRadiusScale * this.circleRadius))); + }, + fire() { + this.isAttached = false; + const speed = 10 //scale with mass? + Matter.Body.setVelocity(this, { + x: player.velocity.x * 0.4 + speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }); + m.plasmaBall.setPositionToNose() + if (this.circleRadius < 10) this.isPopping = true + }, + scale(scale) { + Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast + if (this.circleRadius < this.radiusLimit) this.reset() + }, + reset() { + // console.log(this.circleRadius) + const scale = 1 / m.plasmaBall.circleRadius + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + // console.log(this.circleRadius) + // this.circleRadius = 0 + this.alpha = 0.7 + this.isOn = false + this.isPopping = false + // this.isAttached = true; + }, + do() { + if (this.isOn) { + //collisions with map + if (Matter.Query.collides(this, map).length > 0) { + if (this.isAttached) { + this.scale(Math.max(0.9, 0.998 - 0.1 / m.plasmaBall.circleRadius)) + } else { + this.isPopping = true + } + } + if (this.isPopping) { + this.alpha -= 0.03 + if (this.alpha < 0.1) { + this.reset() + } else { + const scale = 1.04 + 4 / Math.max(1, m.plasmaBall.circleRadius) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } + // if (this.speed > 2.5) { + // const slow = 0.9 + // Matter.Body.setVelocity(this, { + // x: slow * this.velocity.x, + // y: slow * this.velocity.y + // }); + // } + } + //collisions with mobs + // const whom = Matter.Query.collides(this, mob) + // const dmg = this.damage * m.dmgScale + // for (let i = 0, len = whom.length; i < len; i++) { + // const mobHit = (who) => { + // if (who.alive) { + // if (!this.isAttached && !who.isMobBullet) this.isPopping = true + // who.damage(dmg); + // // if (who.shield) this.scale(Math.max(0.9, 0.99 - 0.5 / m.plasmaBall.circleRadius)) + // if (who.speed > 5) { + // Matter.Body.setVelocity(who, { //friction + // x: who.velocity.x * 0.6, + // y: who.velocity.y * 0.6 + // }); + // } else { + // Matter.Body.setVelocity(who, { //friction + // x: who.velocity.x * 0.93, + // y: who.velocity.y * 0.93 + // }); + // } + // } + // } + // mobHit(whom[i].bodyA) + // mobHit(whom[i].bodyB) + // } + + //damage nearby mobs + const dmg = this.damage * m.dmgScale + const arcList = [] + const damageRadius = circleRadiusScale * this.circleRadius + const dischargeRange = 150 + 1600 * tech.plasmaDischarge + 1.3 * damageRadius + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet) && !mob[i].isInvulnerable) { + const sub = Vector.magnitude(Vector.sub(this.position, mob[i].position)) + if (sub < damageRadius + mob[i].radius) { + // if (!this.isAttached && !mob[i].isMobBullet) this.isPopping = true + mob[i].damage(dmg); + if (mob[i].speed > 5) { + Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.6, y: mob[i].velocity.y * 0.6 }); + } else { + Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.93, y: mob[i].velocity.y * 0.93 }); + } + } else if (sub < dischargeRange + mob[i].radius && Matter.Query.ray(map, mob[i].position, this.position).length === 0) { + arcList.push(mob[i]) //populate electrical arc list + } + } + } + for (let i = 0; i < arcList.length; i++) { + if (tech.plasmaDischarge > Math.random()) { + const who = arcList[Math.floor(Math.random() * arcList.length)] + who.damage(dmg * 4); + //draw arcs + const sub = Vector.sub(who.position, this.position) + const unit = Vector.normalise(sub) + let len = 12 + const step = Vector.magnitude(sub) / (len + 2) + let x = this.position.x + let y = this.position.y + ctx.beginPath(); + ctx.moveTo(x, y); + for (let i = 0; i < len; i++) { + x += step * (unit.x + (Math.random() - 0.5)) + y += step * (unit.y + (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + ctx.lineTo(who.position.x, who.position.y); + ctx.strokeStyle = "#88f"; + ctx.lineWidth = 4 + 3 * Math.random(); + ctx.stroke(); + if (who.damageReduction) { + simulation.drawList.push({ + x: who.position.x, + y: who.position.y, + radius: 15, + color: "rgba(150,150,255,0.4)", + time: 15 + }); + } + } + } + + + //slowly slow down if too fast + if (this.speed > 10) { + const scale = 0.998 + Matter.Body.setVelocity(this, { x: scale * this.velocity.x, y: scale * this.velocity.y }); + } + + //graphics + const radius = circleRadiusScale * this.circleRadius * (0.99 + 0.02 * Math.random()) + 3 * Math.random() + const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, radius); + const alpha = this.alpha + 0.1 * Math.random() + gradient.addColorStop(0, `rgba(255,255,255,${alpha})`); + gradient.addColorStop(0.35 + 0.1 * Math.random(), `rgba(255,150,255,${alpha})`); + gradient.addColorStop(1, `rgba(255,0,255,${alpha})`); + // gradient.addColorStop(1, `rgba(255,150,255,${alpha})`); + ctx.fillStyle = gradient + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); + ctx.fill(); + //draw arcs + const unit = Vector.rotate({ x: 1, y: 0 }, Math.random() * 6.28) + let len = 8 + const step = this.circleRadius / len + let x = this.position.x + let y = this.position.y + ctx.beginPath(); + if (Math.random() < 0.5) { + x += step * (unit.x + 6 * (Math.random() - 0.5)) + y += step * (unit.y + 6 * (Math.random() - 0.5)) + len -= 2 + } + if (Math.random() < 0.5) { + x += step * (unit.x + 6 * (Math.random() - 0.5)) + y += step * (unit.y + 6 * (Math.random() - 0.5)) + len -= 2 + } + ctx.moveTo(x, y); + + for (let i = 0; i < len; i++) { + x += step * (unit.x + 1.9 * (Math.random() - 0.5)) + y += step * (unit.y + 1.9 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + ctx.strokeStyle = "#88f"; + ctx.lineWidth = 2 * Math.random(); + ctx.stroke(); + } + }, + }); + + Composite.add(engine.world, m.plasmaBall); + // m.plasmaBall.startingVertices = m.plasmaBall.vertices.slice(); + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + if (m.fieldCDcycle < m.cycle) { + //field is active + if (!m.plasmaBall.isAttached) { //return ball to player + if (m.plasmaBall.isOn) { + m.plasmaBall.isPopping = true + } else { + m.plasmaBall.isAttached = true + m.plasmaBall.isOn = true + m.plasmaBall.isPopping = false + m.plasmaBall.alpha = 0.7 + m.plasmaBall.setPositionToNose() + // m.plasmaBall.reset() + + } + } else if (m.energy > m.plasmaBall.drain) { //charge up when attached + if (tech.isCapacitor) { + m.energy -= m.plasmaBall.drain * 2; + const scale = 1 + 48 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } else { + m.energy -= m.plasmaBall.drain; + const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } + if (m.energy > m.maxEnergy) { + m.energy -= m.plasmaBall.drain * 2; + const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } + m.plasmaBall.setPositionToNose() + + //add friction for player when holding ball, more friction in vertical + // const floatScale = Math.sqrt(m.plasmaBall.circleRadius) + // const friction = 0.0002 * floatScale + // const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - friction * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - friction * Math.abs(player.velocity.y)) //down : up + // Matter.Body.setVelocity(player, { + // x: Math.max(0.95, 1 - friction * Math.abs(player.velocity.x)) * player.velocity.x, + // y: slowY * player.velocity.y + // }); + + // if (player.velocity.y > 7) player.force.y -= 0.95 * player.mass * simulation.g //less gravity when falling fast + // player.force.y -= Math.min(0.95, 0.05 * floatScale) * player.mass * simulation.g; //undo some gravity on up or down + + //float + const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up + Matter.Body.setVelocity(player, { + x: Math.max(0.95, 1 - 0.003 * Math.abs(player.velocity.x)) * player.velocity.x, + y: slowY * player.velocity.y + }); + if (player.velocity.y > 5) { + player.force.y -= 0.9 * player.mass * simulation.g //less gravity when falling fast + } else { + player.force.y -= 0.5 * player.mass * simulation.g; + } + } else { + m.fieldCDcycle = m.cycle + 90; + m.plasmaBall.fire() + } + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + if (m.plasmaBall.isAttached) { + m.fieldCDcycle = m.cycle + 30; + m.plasmaBall.fire() + } + } 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 (m.plasmaBall.isAttached) { + m.fieldCDcycle = m.cycle + 30; + m.plasmaBall.fire() } } - const isInMap = Matter.Query.region(map, bounds).length - // const isInMap = Matter.Query.point(map, m.fieldPosition).length + m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + m.plasmaBall.do() + } + } else if (tech.isExtruder) { + m.hold = function () { + b.isExtruderOn = false + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + b.extruder(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } 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) + } + m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + if (input.field) { + b.wasExtruderOn = true + } else { + b.wasExtruderOn = false + b.canExtruderFire = true + } + ctx.beginPath(); //draw all the wave bullets + for (let i = 1, len = bullet.length; i < len; i++) { //skip the first bullet (which is is oldest bullet) + if (bullet[i].isWave) { + if (bullet[i].isBranch || bullet[i - 1].isBranch) { + ctx.moveTo(bullet[i].position.x, bullet[i].position.y) + } else { + ctx.lineTo(bullet[i].position.x, bullet[i].position.y) + } + } + } + if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) + ctx.lineWidth = 4; + ctx.strokeStyle = "#f07" + ctx.stroke(); + ctx.lineWidth = tech.extruderRange; + ctx.strokeStyle = "rgba(255,0,110,0.06)" + ctx.stroke(); + } + // } else if (true) { //plasma sword slash + // const plasmaSweepCycles = 30 + // m.plasmaSweep = 0 + // m.plasmaSlashDirection = m.flipLegs//Math.random() > 0.5 ? 1 : -1; + // m.hold = function () { + // if (m.isHolding) { + // m.drawHold(m.holdingTarget); + // m.holding(); + // m.throwBlock(); + // m.plasmaSweep = 0 + // // } else if (true) { //not hold but field button is pressed + // } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + // m.grabPowerUp(); + // m.lookForPickUp(); - if (!m.fieldOn) { // if field was off, and it starting up, teleport to new mouse location - m.fieldOn = true; - // m.fieldPosition = { //smooth the mouse position, set to starting at player - // x: m.pos.x, - // y: m.pos.y + // //graphics + // if (m.plasmaSweep === 0) m.plasmaSlashDirection = m.flipLegs //Math.random() > 0.5 ? 1 : -1; + // const angle = m.angle //+ 1 * (m.plasmaSweep - plasmaSweepCycles / 2) / plasmaSweepCycles * m.plasmaSlashDirection + // const plasmaSweepCapped = Math.min(m.plasmaSweep, plasmaSweepCycles - 8) / plasmaSweepCycles + // const range = 100 * plasmaSweepCapped + // const arc = 1.3 + // const A = { x: m.pos.x + range * Math.cos(angle - arc), y: m.pos.y + range * Math.sin(angle - arc) } + // const B = { x: m.pos.x + range * Math.cos(angle + arc), y: m.pos.y + range * Math.sin(angle + arc) } + // const controlRange = 500 * plasmaSweepCapped + // const AC = { x: m.pos.x + controlRange * Math.cos(angle - arc / 2), y: m.pos.y + controlRange * Math.sin(angle - arc / 2) } + // const BC = { x: m.pos.x + controlRange * Math.cos(angle + arc / 2), y: m.pos.y + controlRange * Math.sin(angle + arc / 2) } + // const innerControlRange = 300 * plasmaSweepCapped + // const ACinner = { x: m.pos.x + innerControlRange * Math.cos(angle - arc / 2), y: m.pos.y + innerControlRange * Math.sin(angle - arc / 2) } + // const BCinner = { x: m.pos.x + innerControlRange * Math.cos(angle + arc / 2), y: m.pos.y + innerControlRange * Math.sin(angle + arc / 2) } + // ctx.beginPath(); + // ctx.moveTo(A.x, A.y) + // ctx.bezierCurveTo(AC.x, AC.y, BC.x, BC.y, B.x, B.y); //outer curve + // ctx.bezierCurveTo(BCinner.x, BCinner.y, ACinner.x, ACinner.y, A.x, A.y); //inner curve + // // ctx.strokeStyle = "#000" + // // ctx.stroke(); + // ctx.fillStyle = "rgba(255,0,255,0.5)" + // ctx.fill(); + + // //draw control points for graphics reference + // ctx.lineWidth = '0.5' + // ctx.beginPath(); + // ctx.arc(A.x, A.y, 5, 0, 2 * Math.PI); + // ctx.stroke(); + // ctx.beginPath(); + // ctx.arc(B.x, B.y, 5, 0, 2 * Math.PI); + // ctx.stroke(); + // ctx.beginPath(); + // ctx.arc(AC.x, AC.y, 5, 0, 2 * Math.PI); + // ctx.stroke(); + // ctx.beginPath(); + // ctx.arc(BC.x, BC.y, 5, 0, 2 * Math.PI); + // ctx.stroke(); + // ctx.beginPath(); + // ctx.arc(ACinner.x, ACinner.y, 5, 0, 2 * Math.PI); + // ctx.stroke(); + // ctx.beginPath(); + // ctx.arc(BCinner.x, BCinner.y, 5, 0, 2 * Math.PI); + // ctx.stroke(); + + // //mob collision detection + // collideRange = 160 + // const collideCenter = { + // x: m.pos.x + collideRange * Math.cos(angle), + // y: m.pos.y + collideRange * Math.sin(angle) + // } + // ctx.beginPath(); + // ctx.arc(collideCenter.x, collideCenter.y, 140, 0, 2 * Math.PI); + // ctx.stroke(); + + // //push mob away and slow them? + + + // //sweeping motion and cooldown + // m.plasmaSweep++ + // if (m.plasmaSweep > plasmaSweepCycles) { + // m.plasmaSweep = 0 + // if (m.fireCDcycle < m.cycle + 30) m.fieldCDcycle = m.cycle + 30 + // } + // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + // m.pickUp(); + // m.plasmaSweep = 0 + // } else { + // m.plasmaSweep = 0 + // 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) + // } + // m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + // } + } else { + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + b.plasma(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } 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) + } + m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + } + } + }, + effect() { + m.fieldMeterColor = "#f0f" + m.eyeFillColor = m.fieldMeterColor + this.set(); + } + }, + { + name: "time dilation", + description: `use energy to stop time
1.2x movement and fire rate
12 energy per second`, + set() { + // m.fieldMeterColor = "#0fc" + // m.fieldMeterColor = "#ff0" + m.fieldMeterColor = "#3fe" + m.eyeFillColor = m.fieldMeterColor + m.fieldFx = 1.25 + // m.fieldJump = 1.09 + m.setMovement(); + b.setFireCD() + const timeStop = () => { + m.immuneCycle = m.cycle + 10; //invulnerable to harm while time is stopped, this also disables regen + //draw field everywhere + ctx.globalCompositeOperation = "saturation" + ctx.fillStyle = "#ccc"; + ctx.fillRect(-50000, -50000, 100000, 100000) + ctx.globalCompositeOperation = "source-over" + //stop time + m.isTimeDilated = true; + + function sleep(who) { + for (let i = 0, len = who.length; i < len; ++i) { + if (!who[i].isSleeping) { + who[i].storeVelocity = who[i].velocity + who[i].storeAngularVelocity = who[i].angularVelocity + } + Matter.Sleeping.set(who[i], true) + } + } + sleep(mob); + sleep(body); + sleep(bullet); + simulation.cycle--; //pause all functions that depend on game cycle increasing + } + if (tech.isRewindField) { + this.rewindCount = 0 + m.grabPowerUpRange2 = 300000// m.grabPowerUpRange2 = 200000; + + m.hold = function () { + // console.log(m.fieldCDcycle) + m.grabPowerUp(); + // //grab power ups + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // if ( + // Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 && + // !simulation.isChoosing && + // (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) + // ) { + // powerUps.onPickUp(powerUp[i]); + // powerUp[i].effect(); + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // break; //because the array order is messed up after splice + // } + // } + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + m.wakeCheck(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + const drain = 0.0014 / (1 + 0.05 * m.coupling) + if (m.energy > drain) m.energy -= drain + m.grabPowerUp(); + if (this.rewindCount === 0) { + m.lookForPickUp(); + } + + if (!m.holdingTarget) { + if (this.rewindCount === 0) { //large upfront energy cost to enter rewind mode + if (m.energy > 0.3) { + m.energy -= 0.3 + } else { + this.rewindCount = 0; + m.resetHistory(); + if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 + m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity + } + } + this.rewindCount += 6; + const DRAIN = 0.003 + let history = m.history[(m.cycle - this.rewindCount) % 600] + if (this.rewindCount > 599 || m.energy < DRAIN) { + this.rewindCount = 0; + m.resetHistory(); + if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 + m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity + } else { + //draw field everywhere + ctx.globalCompositeOperation = "saturation" + ctx.fillStyle = "#ccc"; + ctx.fillRect(-100000, -100000, 200000, 200000) + ctx.globalCompositeOperation = "source-over" + // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme + m.energy -= DRAIN + if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 5 cycles + Matter.Body.setPosition(player, history.position); + Matter.Body.setVelocity(player, { + x: history.velocity.x, + y: history.velocity.y + }); + if (m.health < history.health) { + m.health = history.health + if (m.health > m.maxHealth) m.health = m.maxHealth + m.displayHealth(); + } + m.yOff = history.yOff + if (m.yOff < 48) { + m.doCrouch() + } else { + m.undoCrouch() + } + if (tech.isRewindBot && !(this.rewindCount % 60)) { + for (let i = 0; i < tech.isRewindBot; i++) { + b.randomBot(m.pos, false, false) + bullet[bullet.length - 1].endCycle = simulation.cycle + 300 + Math.floor(180 * Math.random()) //8-9 seconds + } + } + if (tech.isRewindGrenade && !(this.rewindCount % 30)) { + b.grenade(m.pos, this.rewindCount) //Math.PI / 2 + const who = bullet[bullet.length - 1] + who.endCycle = simulation.cycle + 120 + } + } + } + m.wakeCheck(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + this.rewindCount = 0; + m.wakeCheck(); + } else if (tech.isTimeStop && player.speed < 1 && m.onGround && !input.fire) { + timeStop(); + this.rewindCount = 0; + } 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) + this.rewindCount = 0; + m.wakeCheck(); + } + m.drawRegenEnergy() // this calls m.regenEnergy(); also + } + } else { + m.fieldFire = true; + m.isTimeDilated = false; + m.hold = function () { + if (m.isHolding) { + m.wakeCheck(); + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { + const drain = 0.0026 / (1 + 0.03 * m.coupling) + if (m.energy > drain) m.energy -= drain + m.grabPowerUp(); + m.lookForPickUp(); //this drains energy 0.001 + if (m.energy > drain) { + timeStop(); + } else { //holding, but field button is released + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + m.wakeCheck(); + m.wakeCheck(); + } + } else if (tech.isTimeStop && player.speed < 1 && m.onGround && m.fireCDcycle < m.cycle && !input.fire) { + timeStop(); + //makes things move at 1/5 time rate, but has an annoying flicker for mob graphics, and other minor bugs + // if (!(m.cycle % 4)) { + // // requestAnimationFrame(() => { + // m.wakeCheck(); + // // simulation.timePlayerSkip(1) + // // }); //wrapping in animation frame prevents errors, probably + // ctx.globalCompositeOperation = "saturation" + // ctx.fillStyle = "#ccc"; + // ctx.fillRect(-100000, -100000, 200000, 200000) + // ctx.globalCompositeOperation = "source-over" + // } else { + // timeStop(); // } - m.fieldPosition = { //smooth the mouse position, set to mouse's current location - x: simulation.mouseInGame.x, - y: simulation.mouseInGame.y + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.wakeCheck(); + m.pickUp(); + } else { + m.wakeCheck(); + 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) + } + m.drawRegenEnergy() + } + } + }, + effect() { + if (tech.isTimeStop) { + m.fieldHarmReduction = 0.6; + } else { + m.fieldHarmReduction = 1; + } + this.set(); + } + }, + { + name: "metamaterial cloaking", + description: `0.3x damage taken while cloaked
after decloaking 4.5x damage for 2 s
6 energy per second`, + effect: () => { + m.fieldFire = true; + m.fieldMeterColor = "#333"; + m.eyeFillColor = m.fieldMeterColor + m.fieldPhase = 0; + m.isCloak = false + m.fieldDrawRadius = 0 + m.isSneakAttack = true; + m.sneakAttackCycle = 0; + m.enterCloakCycle = 0; + m.drawCloakedM = function () { + m.walk_cycle -= m.flipLegs * m.Vx; + m.pos.x += 4 + m.draw(); + } + m.drawCloak = function () { + m.fieldPhase += 0.007 + const wiggle = 0.15 * Math.sin(m.fieldPhase * 0.5) + ctx.beginPath(); + ctx.ellipse(m.pos.x, m.pos.y, m.fieldDrawRadius * (1 - wiggle), m.fieldDrawRadius * (1 + wiggle), m.fieldPhase, 0, 2 * Math.PI); + ctx.fillStyle = "#fff" + ctx.lineWidth = 2; + ctx.strokeStyle = "#000" + // ctx.stroke() + ctx.globalCompositeOperation = "destination-in"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.clip(); + } + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold and field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding target exists, and field button is not pressed + m.pickUp(); + } 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) + } + //not shooting (or using field) enable cloak + if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle + if (m.fireCDcycle + 10 < m.cycle && !input.fire) { //automatically cloak if not firing + // const drain = 0.02 + if (!m.isCloak) { //&& m.energy > drain + 0.03 + // m.energy -= drain + m.isCloak = true //enter cloak + m.fieldHarmReduction = 0.3; + m.enterCloakCycle = m.cycle + if (tech.isCloakHealLastHit && m.lastHit > 0) { + const heal = Math.min(0.75 * m.lastHit, m.energy) + m.addHealth(heal); //heal from last hit + m.lastHit = 0 + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x, + y: m.pos.y, + radius: Math.sqrt(heal) * 200, + color: "rgba(0,255,200,0.6)", + time: 16 + }); } - m.lastFieldPosition = { //used to find velocity of field changes - x: m.fieldPosition.x, - y: m.fieldPosition.y - } - } else { //when field is on it smoothly moves towards the mouse - m.lastFieldPosition = { //used to find velocity of field changes - x: m.fieldPosition.x, - y: m.fieldPosition.y - } - const smooth = isInMap ? 0.985 : 0.96; - m.fieldPosition = { //smooth the mouse position - x: m.fieldPosition.x * smooth + simulation.mouseInGame.x * (1 - smooth), - y: m.fieldPosition.y * smooth + simulation.mouseInGame.y * (1 - smooth), + if (tech.isIntangible) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield + } } } + } else if (m.isCloak) { //exit cloak + m.sneakAttackCycle = m.cycle + m.isCloak = false + m.fieldHarmReduction = 1 - //grab power ups into the field + if (tech.isIntangible) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield + } + } + if (tech.isCloakStun) { //stun nearby mobs after exiting cloak + // let isMobsAround = false + const stunRange = m.fieldDrawRadius * 1.25 + // const drain = 0.01 + // if (m.energy > drain) { + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) < stunRange && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 && !mob[i].isBadTarget) { + isMobsAround = true + mobs.statusStun(mob[i], 120) + } + } + // if (isMobsAround) { + // m.energy -= drain + // simulation.drawList.push({ + // x: m.pos.x, + // y: m.pos.y, + // radius: stunRange, + // color: "hsla(0,50%,100%,0.7)", + // time: 7 + // }); + // } + // } + } + } + + if (m.isCloak) { + m.fieldRange = m.fieldRange * 0.85 + 130 + m.fieldDrawRadius = m.fieldRange * 1.1 //* 0.88 //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); + m.drawCloak() + // ctx.globalCompositeOperation = "lighter"; + // m.drawCloakedM() + // ctx.globalCompositeOperation = "source-over"; + + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 35, 0, 2 * Math.PI); + ctx.strokeStyle = "rgba(255,255,255,0.25)";//"rgba(0,0,0,0.7)";//"rgba(255,255,255,0.7)";//"rgba(255,0,100,0.7)"; + ctx.lineWidth = 10 + ctx.stroke(); + + } else if (m.fieldRange < 4000) { + m.fieldRange += 90 + m.fieldDrawRadius = m.fieldRange //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); + m.drawCloak() + } + if (tech.isIntangible) { + if (m.isCloak) { + player.collisionFilter.mask = cat.map + let inPlayer = Matter.Query.region(mob, player.bounds) + if (inPlayer.length > 0) { + for (let i = 0; i < inPlayer.length; i++) { + if (m.energy > 0) { + if (!inPlayer[i].isUnblockable) m.energy -= 0.003; + if (inPlayer[i].shield) m.energy -= 0.011; + } + } + } + } else { + player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + } + } + this.drawRegenEnergyCloaking() + if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) { //show sneak attack status + m.fieldDamage = 4.5 * (1 + 0.05 * m.coupling) + const timeLeft = (m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) - m.cycle) * 0.5 + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 32, 0, 2 * Math.PI); + ctx.strokeStyle = "rgba(180,30,70,0.5)";//"rgba(0,0,0,0.7)";//"rgba(255,255,255,0.7)";//"rgba(255,0,100,0.7)"; + ctx.lineWidth = Math.max(Math.min(10, timeLeft), 3); + ctx.stroke(); + // ctx.globalCompositeOperation = "multiply"; + // m.drawCloakedM() + // ctx.globalCompositeOperation = "source-over"; + } else { + m.fieldDamage = 1 + } + } + } + }, + { + name: "pilot wave", + description: `use energy to guide blocks
,
, and
have +3 choices
10 energy per second`, + effect: () => { + m.fieldMeterColor = "#333" + m.eyeFillColor = m.fieldMeterColor + + m.fieldPhase = 0; + m.fieldPosition = { + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.lastFieldPosition = { + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.fieldOn = false; + m.fieldRadius = 0; + m.drop(); + m.hold = function () { + if (tech.isPrinter) { + //spawn blocks if field and crouch + if (input.field && m.fieldCDcycle < m.cycle && input.down && !m.isHolding) { + m.printBlock() + } + //if holding block grow it + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + if (tech.isPrinter && m.holdingTarget.isPrinted && input.field) { + // if (Math.random() < 0.004 && m.holdingTarget.vertices.length < 12) m.holdingTarget.vertices.push({ x: 0, y: 0 }) //small chance to increase the number of vertices + m.holdingTarget.radius += Math.min(1.1, 1.3 / m.holdingTarget.mass) //grow up to a limit + const r1 = m.holdingTarget.radius * (1 + 0.12 * Math.sin(m.cycle * 0.11)) + const r2 = m.holdingTarget.radius * (1 + 0.12 * Math.cos(m.cycle * 0.11)) + let angle = (m.cycle * 0.01) % (2 * Math.PI) //rotate the object + let vertices = [] + for (let i = 0, len = m.holdingTarget.vertices.length; i < len; i++) { + angle += 2 * Math.PI / len + vertices.push({ x: m.holdingTarget.position.x + r1 * Math.cos(angle), y: m.holdingTarget.position.y + r2 * Math.sin(angle) }) + } + Matter.Body.setVertices(m.holdingTarget, vertices) + m.definePlayerMass(m.defaultMass + m.holdingTarget.mass * m.holdingMassScale) + } + m.throwBlock() + } 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 releasing field throw it + + } + if (input.field) { + if (m.fieldCDcycle < m.cycle) { + const scale = 25 + const bounds = { + min: { + x: m.fieldPosition.x - scale, + y: m.fieldPosition.y - scale + }, + max: { + x: m.fieldPosition.x + scale, + y: m.fieldPosition.y + scale + } + } + const isInMap = Matter.Query.region(map, bounds).length + // const isInMap = Matter.Query.point(map, m.fieldPosition).length + + if (!m.fieldOn) { // if field was off, and it starting up, teleport to new mouse location + m.fieldOn = true; + // m.fieldPosition = { //smooth the mouse position, set to starting at player + // x: m.pos.x, + // y: m.pos.y + // } + m.fieldPosition = { //smooth the mouse position, set to mouse's current location + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.lastFieldPosition = { //used to find velocity of field changes + x: m.fieldPosition.x, + y: m.fieldPosition.y + } + } else { //when field is on it smoothly moves towards the mouse + m.lastFieldPosition = { //used to find velocity of field changes + x: m.fieldPosition.x, + y: m.fieldPosition.y + } + const smooth = isInMap ? 0.985 : 0.96; + m.fieldPosition = { //smooth the mouse position + x: m.fieldPosition.x * smooth + simulation.mouseInGame.x * (1 - smooth), + y: m.fieldPosition.y * smooth + simulation.mouseInGame.y * (1 - smooth), + } + } + + //grab power ups into the field + for (let i = 0, len = powerUp.length; i < len; ++i) { + if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue + + const dxP = m.fieldPosition.x - powerUp[i].position.x; + const dyP = m.fieldPosition.y - powerUp[i].position.y; + const dist2 = dxP * dxP + dyP * dyP + 200; + // float towards field if looking at and in range or if very close to player + if ( + dist2 < m.fieldRadius * m.fieldRadius && + (m.lookingAt(powerUp[i]) || dist2 < 16000) + ) { + powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; + powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + //extra friction + Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.11, y: powerUp[i].velocity.y * 0.11 }); + if ( + dist2 < 5000 && + !simulation.isChoosing && + (tech.isOverHeal || powerUp[i].name !== "heal" || m.maxHealth - m.health > 0.01) + // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth) + // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity) + ) { //use power up if it is close enough + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + // m.fieldRadius += 50 + break; //because the array order is messed up after splice + } + } + } + //grab power ups normally too + m.grabPowerUp(); + + if (m.energy > 0.01) { + //find mouse velocity + const diff = Vector.sub(m.fieldPosition, m.lastFieldPosition) + const speed = Vector.magnitude(diff) + const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity + let radius, radiusSmooth + if (Matter.Query.ray(map, m.fieldPosition, player.position).length) { //is there something block the player's view of the field + radius = 0 + radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99 + } else { + radius = Math.max(50, 250 - 2 * speed) + radiusSmooth = 0.97 + } + m.fieldRadius = m.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth) + + for (let i = 0, len = body.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(body[i].position, m.fieldPosition)) < m.fieldRadius && !body[i].isNotHoldable) { + const DRAIN = speed * body[i].mass * 0.0000035 // * (1 + m.energy * m.energy) //drain more energy when you have more energy + if (m.energy > DRAIN) { + m.energy -= DRAIN; + Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity + Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8) + // body[i].force.y -= body[i].mass * simulation.g; //remove gravity effects + //blocks drift towards center of pilot wave + const sub = Vector.sub(m.fieldPosition, body[i].position) + const push = Vector.mult(Vector.normalise(sub), 0.0001 * body[i].mass * Vector.magnitude(sub)) + body[i].force.x += push.x + body[i].force.y += push.y - body[i].mass * simulation.g //remove gravity effects + // if (body[i].collisionFilter.category !== cat.bullet) { + // body[i].collisionFilter.category = cat.bullet; + // } + } else { + m.fieldCDcycle = m.cycle + 120; + m.fieldOn = false + m.fieldRadius = 0 + break + } + } + } + + + // m.holdingTarget.collisionFilter.category = cat.bullet; + // m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; + // //check every second to see if player is away from thrown body, and make solid + // const solid = function(that) { + // const dx = that.position.x - player.position.x; + // const dy = that.position.y - player.position.y; + // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { + // that.collisionFilter.category = cat.body; //make solid + // that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now + // } else { + // setTimeout(solid, 40, that); + // } + // }; + // setTimeout(solid, 200, m.holdingTarget); + + + + // if (tech.isFreezeMobs) { + // for (let i = 0, len = mob.length; i < len; ++i) { + // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) < m.fieldRadius + mob[i].radius) { + // const ICE_DRAIN = 0.0005 + // if (m.energy > ICE_DRAIN) m.energy -= ICE_DRAIN; + // mobs.statusSlow(mob[i], 180) + // } + // } + // } + + ctx.beginPath(); + const rotate = m.cycle * 0.008; + m.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(m.energy, 1)); + const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); + const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); + ctx.beginPath(); + ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI); + ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; + ctx.fillStyle = "#fff"; //"#eef"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.beginPath(); + ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 4; + ctx.stroke(); + } else { + m.fieldCDcycle = m.cycle + 120; + m.fieldOn = false + m.fieldRadius = 0 + } + } else { + m.grabPowerUp(); + } + } else { + m.fieldOn = false + m.fieldRadius = 0 + } + m.drawRegenEnergy("rgba(0,0,0,0.2)") + } + } + }, + { + name: "wormhole", + //wormholes attract blocks and power ups
+ description: "use energy to tunnel through a wormhole
+8% chance to duplicate spawned power ups
8 energy per second", //
bullets may also traverse wormholes + drain: 0, + effect: function () { + m.fieldMeterColor = "#bbf" //"#0c5" + m.eyeFillColor = m.fieldMeterColor + + m.duplicateChance = 0.08 + m.fieldRange = 0 + powerUps.setPowerUpMode(); //needed after adjusting duplication chance + + m.hold = function () { + // m.hole = { //this is reset with each new field, but I'm leaving it here for reference + // isOn: false, + // isReady: true, + // pos1: {x: 0,y: 0}, + // pos2: {x: 0,y: 0}, + // angle: 0, + // unit:{x:0,y:0}, + // } + if (m.hole.isOn) { + // draw holes + m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + const semiMajorAxis = m.fieldRange + 30 + const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1) + const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1) + const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2) + const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2) + ctx.beginPath(); + ctx.moveTo(edge1a.x, edge1a.y) + ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y); + ctx.lineTo(edge2b.x, edge2b.y) + ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y); + ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)" + ctx.fill(); + ctx.beginPath(); + ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) + ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) + ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})` + ctx.fill(); + + //suck power ups for (let i = 0, len = powerUp.length; i < len; ++i) { if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue - - const dxP = m.fieldPosition.x - powerUp[i].position.x; - const dyP = m.fieldPosition.y - powerUp[i].position.y; - const dist2 = dxP * dxP + dyP * dyP + 200; - // float towards field if looking at and in range or if very close to player - if ( - dist2 < m.fieldRadius * m.fieldRadius && - (m.lookingAt(powerUp[i]) || dist2 < 16000) - ) { - powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; - powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity - //extra friction - Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.11, y: powerUp[i].velocity.y * 0.11 }); - if ( - dist2 < 5000 && - !simulation.isChoosing && - (tech.isOverHeal || powerUp[i].name !== "heal" || m.maxHealth - m.health > 0.01) - // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth) - // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity) - ) { //use power up if it is close enough + //which hole is closer + const dxP1 = m.hole.pos1.x - powerUp[i].position.x; + const dyP1 = m.hole.pos1.y - powerUp[i].position.y; + const dxP2 = m.hole.pos2.x - powerUp[i].position.x; + const dyP2 = m.hole.pos2.y - powerUp[i].position.y; + let dxP, dyP, dist2 + if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) { + dxP = dxP1 + dyP = dyP1 + } else { + dxP = dxP2 + dyP = dyP2 + } + dist2 = dxP * dxP + dyP * dyP; + if (dist2 < 600000) { //&& !(m.health === m.maxHealth && powerUp[i].name === "heal") + powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole + powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.05, y: powerUp[i].velocity.y * 0.05 }); + if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough + m.fieldRange *= 0.8 powerUps.onPickUp(powerUp[i]); powerUp[i].effect(); Matter.Composite.remove(engine.world, powerUp[i]); powerUp.splice(i, 1); - // m.fieldRadius += 50 break; //because the array order is messed up after splice } } } - //grab power ups normally too - m.grabPowerUp(); - - if (m.energy > 0.01) { - //find mouse velocity - const diff = Vector.sub(m.fieldPosition, m.lastFieldPosition) - const speed = Vector.magnitude(diff) - const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity - let radius, radiusSmooth - if (Matter.Query.ray(map, m.fieldPosition, player.position).length) { //is there something block the player's view of the field - radius = 0 - radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99 - } else { - radius = Math.max(50, 250 - 2 * speed) - radiusSmooth = 0.97 - } - m.fieldRadius = m.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth) - - for (let i = 0, len = body.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(body[i].position, m.fieldPosition)) < m.fieldRadius && !body[i].isNotHoldable) { - const DRAIN = speed * body[i].mass * 0.0000035 // * (1 + m.energy * m.energy) //drain more energy when you have more energy - if (m.energy > DRAIN) { - m.energy -= DRAIN; - Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity - Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8) - // body[i].force.y -= body[i].mass * simulation.g; //remove gravity effects - //blocks drift towards center of pilot wave - const sub = Vector.sub(m.fieldPosition, body[i].position) - const push = Vector.mult(Vector.normalise(sub), 0.0001 * body[i].mass * Vector.magnitude(sub)) - body[i].force.x += push.x - body[i].force.y += push.y - body[i].mass * simulation.g //remove gravity effects - // if (body[i].collisionFilter.category !== cat.bullet) { - // body[i].collisionFilter.category = cat.bullet; - // } - } else { - m.fieldCDcycle = m.cycle + 120; - m.fieldOn = false - m.fieldRadius = 0 - break + //suck and shrink blocks + const suckRange = 500 + const shrinkRange = 100 + const shrinkScale = 0.97; + const slowScale = 0.9 + for (let i = 0, len = body.length; i < len; i++) { + if (!body[i].isNotHoldable) { + const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) + const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) + if (dist1 < dist2) { + if (dist1 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1) + const slow = Vector.mult(body[i].velocity, slowScale) + Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); + //shrink + if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) { + Matter.Body.scale(body[i], shrinkScale, shrinkScale); + if (body[i].mass < 0.05) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + m.fieldRange *= 0.8 + if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling * level.isReducedRegen + if (tech.isWormholeWorms) { //pandimensional spermia + b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10)); + if (Math.random() < 0.5) { //chance for a second worm + b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10)); + } + } + break + } + } } - } - } - - - // m.holdingTarget.collisionFilter.category = cat.bullet; - // m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; - // //check every second to see if player is away from thrown body, and make solid - // const solid = function(that) { - // const dx = that.position.x - player.position.x; - // const dy = that.position.y - player.position.y; - // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { - // that.collisionFilter.category = cat.body; //make solid - // that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now - // } else { - // setTimeout(solid, 40, that); - // } - // }; - // setTimeout(solid, 200, m.holdingTarget); - - - - // if (tech.isFreezeMobs) { - // for (let i = 0, len = mob.length; i < len; ++i) { - // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) < m.fieldRadius + mob[i].radius) { - // const ICE_DRAIN = 0.0005 - // if (m.energy > ICE_DRAIN) m.energy -= ICE_DRAIN; - // mobs.statusSlow(mob[i], 180) - // } - // } - // } - - ctx.beginPath(); - const rotate = m.cycle * 0.008; - m.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(m.energy, 1)); - const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); - const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); - ctx.beginPath(); - ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI); - ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; - ctx.fillStyle = "#fff"; //"#eef"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; - ctx.beginPath(); - ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy); - ctx.strokeStyle = "#000"; - ctx.lineWidth = 4; - ctx.stroke(); - } else { - m.fieldCDcycle = m.cycle + 120; - m.fieldOn = false - m.fieldRadius = 0 - } - } else { - m.grabPowerUp(); - } - } else { - m.fieldOn = false - m.fieldRadius = 0 - } - m.drawRegenEnergy("rgba(0,0,0,0.2)") - } - } - }, - { - name: "wormhole", - //wormholes attract blocks and power ups
- description: "use energy to tunnel through a wormhole
+8% chance to duplicate spawned power ups
8 energy per second", //
bullets may also traverse wormholes - drain: 0, - effect: function () { - m.fieldMeterColor = "#bbf" //"#0c5" - m.eyeFillColor = m.fieldMeterColor - - m.duplicateChance = 0.08 - m.fieldRange = 0 - powerUps.setPowerUpMode(); //needed after adjusting duplication chance - - m.hold = function () { - // m.hole = { //this is reset with each new field, but I'm leaving it here for reference - // isOn: false, - // isReady: true, - // pos1: {x: 0,y: 0}, - // pos2: {x: 0,y: 0}, - // angle: 0, - // unit:{x:0,y:0}, - // } - if (m.hole.isOn) { - // draw holes - m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) - const semiMajorAxis = m.fieldRange + 30 - const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1) - const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1) - const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2) - const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2) - ctx.beginPath(); - ctx.moveTo(edge1a.x, edge1a.y) - ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y); - ctx.lineTo(edge2b.x, edge2b.y) - ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y); - ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)" - ctx.fill(); - ctx.beginPath(); - ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) - ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) - ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})` - ctx.fill(); - - //suck power ups - for (let i = 0, len = powerUp.length; i < len; ++i) { - if (tech.isEnergyNoAmmo && powerUp[i].name === "ammo") continue - //which hole is closer - const dxP1 = m.hole.pos1.x - powerUp[i].position.x; - const dyP1 = m.hole.pos1.y - powerUp[i].position.y; - const dxP2 = m.hole.pos2.x - powerUp[i].position.x; - const dyP2 = m.hole.pos2.y - powerUp[i].position.y; - let dxP, dyP, dist2 - if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) { - dxP = dxP1 - dyP = dyP1 - } else { - dxP = dxP2 - dyP = dyP2 - } - dist2 = dxP * dxP + dyP * dyP; - if (dist2 < 600000) { //&& !(m.health === m.maxHealth && powerUp[i].name === "heal") - powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole - powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity - Matter.Body.setVelocity(powerUp[i], { x: powerUp[i].velocity.x * 0.05, y: powerUp[i].velocity.y * 0.05 }); - if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough - m.fieldRange *= 0.8 - powerUps.onPickUp(powerUp[i]); - powerUp[i].effect(); - Matter.Composite.remove(engine.world, powerUp[i]); - powerUp.splice(i, 1); - break; //because the array order is messed up after splice - } - } - } - //suck and shrink blocks - const suckRange = 500 - const shrinkRange = 100 - const shrinkScale = 0.97; - const slowScale = 0.9 - for (let i = 0, len = body.length; i < len; i++) { - if (!body[i].isNotHoldable) { - const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) - const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) - if (dist1 < dist2) { - if (dist1 < suckRange) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1) + } else if (dist2 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1) const slow = Vector.mult(body[i].velocity, slowScale) Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); //shrink - if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) { + if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) { Matter.Body.scale(body[i], shrinkScale, shrinkScale); if (body[i].mass < 0.05) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); m.fieldRange *= 0.8 if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling * level.isReducedRegen + if (m.fieldMode === 0 || m.fieldMode === 9) m.energy += 0.02 * m.coupling * level.isReducedRegen if (tech.isWormholeWorms) { //pandimensional spermia - b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10)); + b.worm(Vector.add(m.hole.pos1, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5)); if (Math.random() < 0.5) { //chance for a second worm - b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10)); + b.worm(Vector.add(m.hole.pos1, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5)); } } break } } } - } else if (dist2 < suckRange) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1) - const slow = Vector.mult(body[i].velocity, slowScale) - Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); - //shrink - if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) { - Matter.Body.scale(body[i], shrinkScale, shrinkScale); - if (body[i].mass < 0.05) { - Matter.Composite.remove(engine.world, body[i]); - body.splice(i, 1); - m.fieldRange *= 0.8 - if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.02 * m.coupling * level.isReducedRegen - if (m.fieldMode === 0 || m.fieldMode === 9) m.energy += 0.02 * m.coupling * level.isReducedRegen - if (tech.isWormholeWorms) { //pandimensional spermia - b.worm(Vector.add(m.hole.pos1, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5)); - if (Math.random() < 0.5) { //chance for a second worm - b.worm(Vector.add(m.hole.pos1, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5)); - } - } - break + } + } + if (tech.isWormHoleBullets) { + //teleport bullets + for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2 + if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots + if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position))); + m.fieldRange += 5 + bullet[i].isInHole = true + } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position))); + m.fieldRange += 5 + bullet[i].isInHole = true } } } - } - } - if (tech.isWormHoleBullets) { - //teleport bullets - for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2 - if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots - if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 - Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position))); - m.fieldRange += 5 - bullet[i].isInHole = true - } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 - Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position))); - m.fieldRange += 5 - bullet[i].isInHole = true + // mobs get pushed away + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); + } + if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); } } } - // mobs get pushed away - for (let i = 0, len = mob.length; i < len; i++) { - if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07) - Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); - } - if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07) - Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); - } - } } - } - if (m.fieldCDcycle < m.cycle) { - const scale = 60 - const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) - const sub = Vector.sub(simulation.mouseInGame, m.pos) - const mag = Vector.magnitude(sub) + if (m.fieldCDcycle < m.cycle) { + const scale = 60 + const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) + const sub = Vector.sub(simulation.mouseInGame, m.pos) + const mag = Vector.magnitude(sub) - if (input.field) { - if (tech.isWormHolePause) { - const drain = m.fieldRegen + 0.000035 - if (m.energy > drain) { - m.energy -= drain + if (input.field) { + if (tech.isWormHolePause) { + // const drain = m.fieldRegen + 0.000035 + // if (m.energy > drain) { + // m.energy -= drain if (m.immuneCycle < m.cycle + 1) m.immuneCycle = m.cycle + 1; //player is immune to damage for 1 cycle m.isTimeDilated = true; @@ -5559,399 +5602,395 @@ const m = { }); player.force.x = 0 player.force.y = 0 + // } else { + // m.wakeCheck(); + // m.energy = 0; + // } + } + + m.grabPowerUp(); + //draw possible wormhole + if (tech.isWormholeMapIgnore && Matter.Query.ray(map, m.pos, justPastMouse).length !== 0) { + this.drain = (0.05 + 0.005 * Math.sqrt(mag)) * 2 } else { - m.wakeCheck(); - m.energy = 0; + this.drain = tech.isFreeWormHole ? 0 : 0.05 + 0.005 * Math.sqrt(mag) } - } - - m.grabPowerUp(); - //draw possible wormhole - if (tech.isWormholeMapIgnore && Matter.Query.ray(map, m.pos, justPastMouse).length !== 0) { - this.drain = (0.05 + 0.005 * Math.sqrt(mag)) * 2 - } else { - this.drain = tech.isFreeWormHole ? 0 : 0.05 + 0.005 * Math.sqrt(mag) - } - const unit = Vector.perp(Vector.normalise(sub)) - const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) } - m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) - const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) - const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) - ctx.beginPath(); - ctx.moveTo(where.x, where.y) - ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); - ctx.moveTo(where.x, where.y) - ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2b.x, edge2b.y); - if ( - mag > 250 && m.energy > this.drain && - (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && - Matter.Query.region(map, { - min: { - x: simulation.mouseInGame.x - scale, - y: simulation.mouseInGame.y - scale - }, - max: { - x: simulation.mouseInGame.x + scale, - y: simulation.mouseInGame.y + scale - } - }).length === 0 - ) { - m.hole.isReady = true; - // ctx.fillStyle = "rgba(255,255,255,0.5)" - // ctx.fill(); - ctx.lineWidth = 1 - ctx.strokeStyle = "#000" - ctx.stroke(); - } else { - m.hole.isReady = false; - ctx.lineWidth = 1 - ctx.strokeStyle = "#000" - ctx.lineDashOffset = 30 * Math.random() - ctx.setLineDash([20, 40]); - ctx.stroke(); - ctx.setLineDash([]); - } - } else { - if (tech.isWormHolePause && m.isTimeDilated) m.wakeCheck(); - - //make new wormhole - if ( - m.hole.isReady && mag > 250 && m.energy > this.drain && - (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && - Matter.Query.region(map, { - min: { - x: simulation.mouseInGame.x - scale, - y: simulation.mouseInGame.y - scale - }, - max: { - x: simulation.mouseInGame.x + scale, - y: simulation.mouseInGame.y + scale - } - }).length === 0 - ) { - m.energy -= this.drain - m.hole.isReady = false; - m.fieldRange = 0 - Matter.Body.setPosition(player, simulation.mouseInGame); - // simulation.translatePlayerAndCamera(simulation.mouseInGame) //too jerky - m.buttonCD_jump = 0 //this might fix a bug with jumping - const velocity = Vector.mult(Vector.normalise(sub), 20) - Matter.Body.setVelocity(player, { - x: velocity.x, - y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer - }); - if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 1/4 seconds - // move bots to player - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType) { - Matter.Body.setPosition(bullet[i], Vector.add(player.position, { - x: 250 * (Math.random() - 0.5), - y: 250 * (Math.random() - 0.5) - })); - Matter.Body.setVelocity(bullet[i], { - x: 0, - y: 0 - }); - } + const unit = Vector.perp(Vector.normalise(sub)) + const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) } + m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) + const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) + ctx.beginPath(); + ctx.moveTo(where.x, where.y) + ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); + ctx.moveTo(where.x, where.y) + ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2b.x, edge2b.y); + if ( + mag > 250 && m.energy > this.drain && + (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && + Matter.Query.region(map, { + min: { + x: simulation.mouseInGame.x - scale, + y: simulation.mouseInGame.y - scale + }, + max: { + x: simulation.mouseInGame.x + scale, + y: simulation.mouseInGame.y + scale + } + }).length === 0 + ) { + m.hole.isReady = true; + // ctx.fillStyle = "rgba(255,255,255,0.5)" + // ctx.fill(); + ctx.lineWidth = 1 + ctx.strokeStyle = "#000" + ctx.stroke(); + } else { + m.hole.isReady = false; + ctx.lineWidth = 1 + ctx.strokeStyle = "#000" + ctx.lineDashOffset = 30 * Math.random() + ctx.setLineDash([20, 40]); + ctx.stroke(); + ctx.setLineDash([]); } + } else { + if (tech.isWormHolePause && m.isTimeDilated) m.wakeCheck(); - //set holes - m.hole.isOn = true; - m.hole.pos1.x = m.pos.x - m.hole.pos1.y = m.pos.y - m.hole.pos2.x = player.position.x - m.hole.pos2.y = player.position.y - m.hole.angle = Math.atan2(sub.y, sub.x) - m.hole.unit = Vector.perp(Vector.normalise(sub)) + //make new wormhole + if ( + m.hole.isReady && mag > 250 && m.energy > this.drain && + (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && + Matter.Query.region(map, { + min: { x: simulation.mouseInGame.x - scale, y: simulation.mouseInGame.y - scale }, + max: { x: simulation.mouseInGame.x + scale, y: simulation.mouseInGame.y + scale } + }).length === 0 + ) { + //tech.isWormHolePause + // console.log(this.drain) + m.energy -= this.drain + m.hole.isReady = false; + m.fieldRange = 0 + Matter.Body.setPosition(player, simulation.mouseInGame); + // simulation.translatePlayerAndCamera(simulation.mouseInGame) //too jerky + m.buttonCD_jump = 0 //this might fix a bug with jumping + const velocity = Vector.mult(Vector.normalise(sub), 20) + Matter.Body.setVelocity(player, { + x: velocity.x, + y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + }); + if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 1/4 seconds + // move bots to player + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } - if (tech.isWormholeDamage) { - who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) - for (let i = 0; i < who.length; i++) { - if (who[i].body.alive) { - mobs.statusDoT(who[i].body, 1, 420) - mobs.statusStun(who[i].body, 360) + //set holes + m.hole.isOn = true; + m.hole.pos1.x = m.pos.x + m.hole.pos1.y = m.pos.y + m.hole.pos2.x = player.position.x + m.hole.pos2.y = player.position.y + m.hole.angle = Math.atan2(sub.y, sub.x) + m.hole.unit = Vector.perp(Vector.normalise(sub)) + + if (tech.isWormholeDamage) { + who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) + for (let i = 0; i < who.length; i++) { + if (who[i].body.alive) { + mobs.statusDoT(who[i].body, 1, 420) + mobs.statusStun(who[i].body, 360) + } } } } } + + // if (true && m.energy > 0.5) { //teleport away low mass mobs + // // && !(m.cycle % 1) + // const hit = Matter.Query.region(mob, { + // min: { + // x: m.pos.x - 80, + // y: m.pos.y - 80 + // }, + // max: { + // x: m.pos.x + 80, + // y: m.pos.y + 160 + // } + // }) + + // // find incoming mob with low mass + // for (let i = 0; i < hit.length; i++) { + // if (hit[i].mass < 4 && m.energy > hit[i].mass * 0.06) { + // //is the mob moving towards the player? + + // // console.log('found one', hit[i].mass) + // const unit = Vector.normalise(hit[i].velocity) + // const jump = Vector.mult(unit, 200) + // const where = Vector.add(hit[i].position, jump) + // if (Matter.Query.ray(map, hit[i].position, where).length === 0) { // check if space 180 from mob is clear of body and map + // // m.energy -= hit[i].mass * 0.06 + // // m.fieldCDcycle = m.cycle + 30; + // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 }); + // Matter.Body.setPosition(hit[i], where); + // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 }); + // } + // // break + // } + // } + // } } - - // if (true && m.energy > 0.5) { //teleport away low mass mobs - // // && !(m.cycle % 1) - // const hit = Matter.Query.region(mob, { - // min: { - // x: m.pos.x - 80, - // y: m.pos.y - 80 - // }, - // max: { - // x: m.pos.x + 80, - // y: m.pos.y + 160 - // } - // }) - - // // find incoming mob with low mass - // for (let i = 0; i < hit.length; i++) { - // if (hit[i].mass < 4 && m.energy > hit[i].mass * 0.06) { - // //is the mob moving towards the player? - - // // console.log('found one', hit[i].mass) - // const unit = Vector.normalise(hit[i].velocity) - // const jump = Vector.mult(unit, 200) - // const where = Vector.add(hit[i].position, jump) - // if (Matter.Query.ray(map, hit[i].position, where).length === 0) { // check if space 180 from mob is clear of body and map - // // m.energy -= hit[i].mass * 0.06 - // // m.fieldCDcycle = m.cycle + 30; - // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 }); - // Matter.Body.setPosition(hit[i], where); - // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 }); + // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) + // const scale = 60 + // const sub = Vector.sub(simulation.mouseInGame, m.pos) + // const mag = Vector.magnitude(sub) + // const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) + // if (m.hole.isReady && mag > 250 && m.energy > drain) { + // if ( + // Matter.Query.region(map, { + // min: { + // x: simulation.mouseInGame.x - scale, + // y: simulation.mouseInGame.y - scale + // }, + // max: { + // x: simulation.mouseInGame.x + scale, + // y: simulation.mouseInGame.y + scale + // } + // }).length === 0 && + // Matter.Query.ray(map, m.pos, justPastMouse).length === 0 + // // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 && + // // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 && + // // Matter.Query.ray(map, player.position, justPastMouse).length === 0 + // ) { + // m.energy -= drain + // m.hole.isReady = false; + // m.fieldRange = 0 + // Matter.Body.setPosition(player, simulation.mouseInGame); + // m.buttonCD_jump = 0 //this might fix a bug with jumping + // const velocity = Vector.mult(Vector.normalise(sub), 20) + // Matter.Body.setVelocity(player, { + // x: velocity.x, + // y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + // }); + // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds + // // move bots to player + // for (let i = 0; i < bullet.length; i++) { + // if (bullet[i].botType) { + // Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + // x: 250 * (Math.random() - 0.5), + // y: 250 * (Math.random() - 0.5) + // })); + // Matter.Body.setVelocity(bullet[i], { + // x: 0, + // y: 0 + // }); + // } // } - // // break + + // //set holes + // m.hole.isOn = true; + // m.hole.pos1.x = m.pos.x + // m.hole.pos1.y = m.pos.y + // m.hole.pos2.x = player.position.x + // m.hole.pos2.y = player.position.y + // m.hole.angle = Math.atan2(sub.y, sub.x) + // m.hole.unit = Vector.perp(Vector.normalise(sub)) + + // if (tech.isWormholeDamage) { + // who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) + // for (let i = 0; i < who.length; i++) { + // if (who[i].body.alive) { + // mobs.statusDoT(who[i].body, 1, 420) + // mobs.statusStun(who[i].body, 360) + // } + // } + // } + // } else { + // //draw failed wormhole + // const unit = Vector.perp(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos))) + // const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle), } + // m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + // const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) + // const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) + // ctx.beginPath(); + // ctx.moveTo(where.x, where.y) + // ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); + // ctx.lineTo(edge2b.x, edge2b.y) + // ctx.bezierCurveTo(simulation.mouseInGame.x, simulation.mouseInGame.y, where.x, where.y, where.x, where.y); + // // ctx.fillStyle = "rgba(255,255,255,0.5)" + // // ctx.fill(); + // ctx.lineWidth = 1 + // ctx.strokeStyle = "#000" + // ctx.lineDashOffset = 30 * Math.random() + // ctx.setLineDash([20, 40]); + // ctx.stroke(); + // ctx.setLineDash([]); // } // } + // m.grabPowerUp(); + // } else { + // m.hole.isReady = true; // } + m.drawRegenEnergy() } - // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) - // const scale = 60 - // const sub = Vector.sub(simulation.mouseInGame, m.pos) - // const mag = Vector.magnitude(sub) - // const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) - // if (m.hole.isReady && mag > 250 && m.energy > drain) { - // if ( - // Matter.Query.region(map, { - // min: { - // x: simulation.mouseInGame.x - scale, - // y: simulation.mouseInGame.y - scale - // }, - // max: { - // x: simulation.mouseInGame.x + scale, - // y: simulation.mouseInGame.y + scale - // } - // }).length === 0 && - // Matter.Query.ray(map, m.pos, justPastMouse).length === 0 - // // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 && - // // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 && - // // Matter.Query.ray(map, player.position, justPastMouse).length === 0 - // ) { - // m.energy -= drain - // m.hole.isReady = false; - // m.fieldRange = 0 - // Matter.Body.setPosition(player, simulation.mouseInGame); - // m.buttonCD_jump = 0 //this might fix a bug with jumping - // const velocity = Vector.mult(Vector.normalise(sub), 20) - // Matter.Body.setVelocity(player, { - // x: velocity.x, - // y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer - // }); - // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds - // // move bots to player - // for (let i = 0; i < bullet.length; i++) { - // if (bullet[i].botType) { - // Matter.Body.setPosition(bullet[i], Vector.add(player.position, { - // x: 250 * (Math.random() - 0.5), - // y: 250 * (Math.random() - 0.5) - // })); - // Matter.Body.setVelocity(bullet[i], { - // x: 0, - // y: 0 - // }); - // } - // } + }, - // //set holes - // m.hole.isOn = true; - // m.hole.pos1.x = m.pos.x - // m.hole.pos1.y = m.pos.y - // m.hole.pos2.x = player.position.x - // m.hole.pos2.y = player.position.y - // m.hole.angle = Math.atan2(sub.y, sub.x) - // m.hole.unit = Vector.perp(Vector.normalise(sub)) + // rewind: function() { + // if (input.down) { + // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // const DRAIN = 0.01 + // if (this.rewindCount < 289 && m.energy > DRAIN) { + // m.energy -= DRAIN - // if (tech.isWormholeDamage) { - // who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) - // for (let i = 0; i < who.length; i++) { - // if (who[i].body.alive) { - // mobs.statusDoT(who[i].body, 1, 420) - // mobs.statusStun(who[i].body, 360) - // } - // } - // } - // } else { - // //draw failed wormhole - // const unit = Vector.perp(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos))) - // const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle), } - // m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) - // const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) - // const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) - // ctx.beginPath(); - // ctx.moveTo(where.x, where.y) - // ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); - // ctx.lineTo(edge2b.x, edge2b.y) - // ctx.bezierCurveTo(simulation.mouseInGame.x, simulation.mouseInGame.y, where.x, where.y, where.x, where.y); - // // ctx.fillStyle = "rgba(255,255,255,0.5)" - // // ctx.fill(); - // ctx.lineWidth = 1 - // ctx.strokeStyle = "#000" - // ctx.lineDashOffset = 30 * Math.random() - // ctx.setLineDash([20, 40]); - // ctx.stroke(); - // ctx.setLineDash([]); - // } - // } - // m.grabPowerUp(); - // } else { - // m.hole.isReady = true; - // } - m.drawRegenEnergy() + + // if (this.rewindCount === 0) { + // const shortPause = function() { + // if (m.defaultFPSCycle < m.cycle) { //back to default values + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); + // simulation.fpsCap = 4 //1 is longest pause, 4 is standard + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // m.defaultFPSCycle = m.cycle + // } + + + // this.rewindCount += 10; + // simulation.wipe = function() { //set wipe to have trails + // // ctx.fillStyle = "rgba(255,255,255,0)"; + // ctx.fillStyle = `rgba(221,221,221,${0.004})`; + // ctx.fillRect(0, 0, canvas.width, canvas.height); + // } + // let history = m.history[(m.cycle - this.rewindCount) % 300] + // Matter.Body.setPosition(player, history.position); + // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); + // if (history.health > m.health) { + // m.health = history.health + // m.displayHealth(); + // } + // //grab power ups + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // const dxP = player.position.x - powerUp[i].position.x; + // const dyP = player.position.y - powerUp[i].position.y; + // if (dxP * dxP + dyP * dyP < 50000 && !simulation.isChoosing && !(m.health === m.maxHealth && powerUp[i].name === "heal")) { + // powerUps.onPickUp(player.position); + // powerUp[i].effect(); + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // const shortPause = function() { + // if (m.defaultFPSCycle < m.cycle) { //back to default values + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); + // simulation.fpsCap = 3 //1 is longest pause, 4 is standard + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // m.defaultFPSCycle = m.cycle + // break; //because the array order is messed up after splice + // } + // } + // m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles + // } else { + // m.fieldCDcycle = m.cycle + 30; + // // m.resetHistory(); + // } + // } else { + // if (this.rewindCount !== 0) { + // m.fieldCDcycle = m.cycle + 30; + // m.resetHistory(); + // this.rewindCount = 0; + // simulation.wipe = function() { //set wipe to normal + // ctx.clearRect(0, 0, canvas.width, canvas.height); + // } + // } + // 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) + // } + // } + // m.drawRegenEnergy() + // }, + }, + { + name: "grappling hook", + description: `use energy to fire a hook that pulls you
0.5x damage taken
9 energy per second`, + effect: () => { + m.fieldFire = true; + // m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping + // m.fieldMeterColor = "#789"//"#456" + m.eyeFillColor = m.fieldMeterColor + m.fieldHarmReduction = 0.5; //40% reduction + m.grabPowerUpRange2 = 300000 //m.grabPowerUpRange2 = 200000; + + m.hold = function () { + 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 + 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.15 && m.fieldCDcycle < m.cycle) { + 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 + ) { + m.energy -= 0.1 + 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 + const maxCount = 6 + for (let j = maxCount - 1; j > 0; j--) { + b.harpoon(m.pos, mob[i], angle + j * 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 + } } }, - - // rewind: function() { - // if (input.down) { - // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // const DRAIN = 0.01 - // if (this.rewindCount < 289 && m.energy > DRAIN) { - // m.energy -= DRAIN - - - // if (this.rewindCount === 0) { - // const shortPause = function() { - // if (m.defaultFPSCycle < m.cycle) { //back to default values - // simulation.fpsCap = simulation.fpsCapDefault - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // // document.getElementById("dmg").style.transition = "opacity 1s"; - // // document.getElementById("dmg").style.opacity = "0"; - // } else { - // requestAnimationFrame(shortPause); - // } - // }; - // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); - // simulation.fpsCap = 4 //1 is longest pause, 4 is standard - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // m.defaultFPSCycle = m.cycle - // } - - - // this.rewindCount += 10; - // simulation.wipe = function() { //set wipe to have trails - // // ctx.fillStyle = "rgba(255,255,255,0)"; - // ctx.fillStyle = `rgba(221,221,221,${0.004})`; - // ctx.fillRect(0, 0, canvas.width, canvas.height); - // } - // let history = m.history[(m.cycle - this.rewindCount) % 300] - // Matter.Body.setPosition(player, history.position); - // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); - // if (history.health > m.health) { - // m.health = history.health - // m.displayHealth(); - // } - // //grab power ups - // for (let i = 0, len = powerUp.length; i < len; ++i) { - // const dxP = player.position.x - powerUp[i].position.x; - // const dyP = player.position.y - powerUp[i].position.y; - // if (dxP * dxP + dyP * dyP < 50000 && !simulation.isChoosing && !(m.health === m.maxHealth && powerUp[i].name === "heal")) { - // powerUps.onPickUp(player.position); - // powerUp[i].effect(); - // Matter.Composite.remove(engine.world, powerUp[i]); - // powerUp.splice(i, 1); - // const shortPause = function() { - // if (m.defaultFPSCycle < m.cycle) { //back to default values - // simulation.fpsCap = simulation.fpsCapDefault - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // // document.getElementById("dmg").style.transition = "opacity 1s"; - // // document.getElementById("dmg").style.opacity = "0"; - // } else { - // requestAnimationFrame(shortPause); - // } - // }; - // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); - // simulation.fpsCap = 3 //1 is longest pause, 4 is standard - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // m.defaultFPSCycle = m.cycle - // break; //because the array order is messed up after splice - // } - // } - // m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles - // } else { - // m.fieldCDcycle = m.cycle + 30; - // // m.resetHistory(); - // } - // } else { - // if (this.rewindCount !== 0) { - // m.fieldCDcycle = m.cycle + 30; - // m.resetHistory(); - // this.rewindCount = 0; - // simulation.wipe = function() { //set wipe to normal - // ctx.clearRect(0, 0, canvas.width, canvas.height); - // } - // } - // 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) - // } - // } - // m.drawRegenEnergy() - // }, - }, - { - name: "grappling hook", - description: `use energy to fire a hook that pulls you
0.5x damage taken
9 energy per second`, - effect: () => { - m.fieldFire = true; - // m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping - // m.fieldMeterColor = "#789"//"#456" - m.eyeFillColor = m.fieldMeterColor - m.fieldHarmReduction = 0.5; //40% reduction - m.grabPowerUpRange2 = 300000 //m.grabPowerUpRange2 = 200000; - - m.hold = function () { - 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 - 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.15 && m.fieldCDcycle < m.cycle) { - 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 - ) { - m.energy -= 0.1 - 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 - const maxCount = 6 - for (let j = maxCount - 1; j > 0; j--) { - b.harpoon(m.pos, mob[i], angle + j * 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 b7722bc..559f5a7 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -1653,6 +1653,7 @@ const powerUps = { if (b.inventory.length === 0) { powerUps.spawn(x, y, "gun", false); //first gun } else if (tech.totalCount === 0) { //first tech + powerUps.spawn(x - 22, y - 50, "ammo", false); //some ammo powerUps.spawn(x, y, "tech", false); } else if (b.inventory.length === 1) { //second gun or extra ammo if (Math.random() < 0.4) { diff --git a/js/simulation.js b/js/simulation.js index df22584..ae76c3c 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -987,7 +987,7 @@ const simulation = { } else { Composite.add(engine.world, [player]) } - shuffle(level.constraint) + seededShuffle(level.constraint) level.populateLevels() input.endKeySensing(); simulation.ephemera = [] diff --git a/js/spawn.js b/js/spawn.js index 4f743bc..0aa972a 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -745,7 +745,7 @@ const spawn = { // exit() { }, // }, ] - shuffle(me.mode); //THIS SHOULD NOT BE COMMENTED OUT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + me.mode.sort(() => Math.random() - 0.5); me.do = function () { this.fill = `hsl(${360 * Math.sin(this.cycle * 0.011)},${80 + 20 * Math.sin(this.cycle * 0.004)}%,${60 + 20 * Math.sin(this.cycle * 0.009)}%)` if (this.health < 1) { @@ -1771,6 +1771,7 @@ const spawn = { powerUps.spawn(me.position.x, me.position.y, "heal"); powerUps.spawn(me.position.x, me.position.y, "ammo"); powerUps.spawn(me.position.x, me.position.y, "ammo"); + powerUps.spawn(me.position.x, me.position.y, "ammo"); } else if (!m.isCloak) { me.foundPlayer(); } @@ -2683,22 +2684,24 @@ const spawn = { this.seePlayerByHistory() this.invulnerabilityCountDown-- if (this.isInvulnerable) { - ctx.beginPath(); - let vertices = this.vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); - ctx.lineTo(vertices[0].x, vertices[0].y); - for (let i = 0; i < this.babyList.length; i++) { - if (this.babyList[i].alive) { - let vertices = this.babyList[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); - ctx.lineTo(vertices[0].x, vertices[0].y); + if (this.invulnerabilityCountDown > 90 || this.invulnerabilityCountDown % 20 > 10) { + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) { + let vertices = this.babyList[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + } } + ctx.lineWidth = 3 + 0.2 * Math.min(this.invulnerabilityCountDown, 90) + 5 * Math.random() + ctx.strokeStyle = `rgba(255,255,255,${0.4 + 0.4 * Math.random()})`; + ctx.stroke(); } - ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; - ctx.stroke(); if (this.invulnerabilityCountDown < 0) { this.invulnerabilityCountDown = 110 this.isInvulnerable = false @@ -2711,7 +2714,7 @@ const spawn = { } } } else if (this.invulnerabilityCountDown < 0) { - this.invulnerabilityCountDown = 120 + 9 * simulation.difficulty + this.invulnerabilityCountDown = 240 + 5 * simulation.difficulty this.isInvulnerable = true if (this.damageReduction) this.startingDamageReduction = this.damageReduction this.damageReduction = 0 diff --git a/js/tech.js b/js/tech.js index 954987c..59d5ea4 100644 --- a/js/tech.js +++ b/js/tech.js @@ -271,7 +271,6 @@ const tech = { if (tech.isImmunityDamage && m.immuneCycle > m.cycle) dmg *= 3 if (tech.isPowerUpDamage) dmg *= 1 + 0.07 * powerUp.length if (tech.isDamageCooldown) dmg *= m.lastKillCycle + tech.isDamageCooldownTime > m.cycle ? 0.4 : 4 - if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 2 if (tech.isDivisor && b.activeGun !== undefined && b.activeGun !== null && b.guns[b.activeGun].ammo % 3 === 0) dmg *= 1.9 if (tech.offGroundDamage && !m.onGround) dmg *= tech.offGroundDamage if (tech.isDilate) dmg *= 1.9 + 1.1 * Math.sin(m.cycle * 0.01) @@ -1018,7 +1017,7 @@ const tech = { { name: "Pareto efficiency", descriptionFunction() { - return `all you ${powerUps.orb.gun()} randomly get
5x or 0.2x ammo per ${powerUps.orb.ammo(1)}` + return `all your ${powerUps.orb.gun()} randomly get
5x or 0.2x ammo per ${powerUps.orb.ammo(1)}` }, maxCount: 1, count: 0, @@ -1033,7 +1032,7 @@ const tech = { let options = [] for (let i = 0; i < b.inventory.length; i++) options.push(b.inventory[i]) - options = shuffle(options) + options.sort(() => Math.random() - 0.5); for (let i = 0; i < options.length; i++) { const index = options[i] const scale = (i < options.length / 2) ? 4 : 0.25 @@ -2758,7 +2757,7 @@ const tech = { }, { name: "Pauli exclusion", - description: `for 7 seconds after mob collisions
become invulnerable and inhibit energy regen`, + description: `for 7 seconds after mob collisions
gain invulnerbility and blocked energy regen`, maxCount: 9, count: 0, frequency: 1, @@ -2777,7 +2776,7 @@ const tech = { }, { name: "spin-statistics theorem", - description: `for 1.9 seconds out of every 7 seconds
become invulnerable and inhibit energy regen`, + description: `for 1.9 seconds out of every 7 seconds
gain invulnerbility and blocked energy regen`, maxCount: 3, count: 0, frequency: 1, @@ -2795,7 +2794,7 @@ const tech = { }, { name: "fermion", - description: `for 5 seconds after mobs die
become invulnerable and inhibit energy regen`, + description: `if a mob has died in the last 5 seconds
gain invulnerbility and blocked energy regen`, maxCount: 1, count: 0, frequency: 1, @@ -3009,9 +3008,9 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return !tech.isDamageAfterKillNoRegen + return true }, - requires: "not parasitism", + requires: "", effect() { tech.isCrouchRegen = true; //only used to check for requirements m.regenEnergy = function () { @@ -3042,29 +3041,6 @@ const tech = { tech.energySiphon = 0; } }, - { - name: "parasitism", - description: "if a mob has died in the last 5 seconds
2x damage, no passive energy generation", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isCrouchRegen - }, - requires: "not inductive charging", - effect() { - tech.isDamageAfterKillNoRegen = true; - m.regenEnergy = function () { - if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle) && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen * level.isReducedRegen; - if (m.energy < 0) m.energy = 0 - } - }, - remove() { - if (this.count) m.regenEnergy = m.regenEnergyDefault - tech.isDamageAfterKillNoRegen = false; - } - }, { name: "waste heat recovery", description: "if a mob has died in the last 5 seconds
generate 0.05x maximum energy every second", @@ -4769,7 +4745,7 @@ const tech = { for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups if (tech.tech[i].count && !tech.tech[i].isInstant) pool.push(i) } - pool = shuffle(pool); //shuffles order of maps + pool.sort(() => Math.random() - 0.5); let removeCount = 0 for (let i = 0, len = pool.length * 0.5; i < len; i++) removeCount += tech.removeTech(pool[i]) this.damage = this.damagePerRemoved * removeCount @@ -8965,22 +8941,31 @@ const tech = { }, { name: "invariant", - description: "while placing your wormhole
use energy to pause time", + cost: 1, + descriptionFunction() { + return `use ${powerUps.orb.research(this.cost)}
pause time while placing your wormhole` + }, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { - return m.fieldMode === 9 && !tech.isNoDraftPause + return m.fieldMode === 9 && !tech.isNoDraftPause && (build.isExperimentSelection || powerUps.research.count > this.cost - 1) }, requires: "wormhole, not eternalism", effect() { tech.isWormHolePause = true + for (let i = 0; i < this.cost; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } }, remove() { if (tech.isWormHolePause && m.isTimeDilated) m.wakeCheck(); tech.isWormHolePause = false + if (this.count) { + powerUps.research.changeRerolls(this.cost) + } } }, { @@ -9031,7 +9016,7 @@ const tech = { allowed() { return m.fieldMode === 9 && !tech.isFreeWormHole }, - requires: "wormhole, not charmed baryons", + requires: "wormhole, not holographic principle", effect() { tech.isWormholeMapIgnore = true }, @@ -10044,8 +10029,8 @@ const tech = { }, requires: "", effect() { - // const colors = shuffle(["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"]) - const colors = shuffle([powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color]) + const colors = [powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color] + colors.sort(() => Math.random() - 0.5); powerUps.research.color = colors[0] powerUps.heal.color = colors[1] powerUps.ammo.color = colors[2] @@ -10075,37 +10060,7 @@ const tech = { } } }, - remove() { - // const colors = ["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"] //no shuffle - // powerUps.research.color = colors[0] - // powerUps.heal.color = colors[1] - // powerUps.ammo.color = colors[2] - // powerUps.field.color = colors[3] - // powerUps.tech.color = colors[4] - // powerUps.gun.color = colors[5] - // for (let i = 0; i < powerUp.length; i++) { - // switch (powerUp[i].name) { - // case "research": - // powerUp[i].color = colors[0] - // break; - // case "heal": - // powerUp[i].color = colors[1] - // break; - // case "ammo": - // powerUp[i].color = colors[2] - // break; - // case "field": - // powerUp[i].color = colors[3] - // break; - // case "tech": - // powerUp[i].color = colors[4] - // break; - // case "gun": - // powerUp[i].color = colors[5] - // break; - // } - // } - } + remove() { } }, { name: "emergency broadcasting", @@ -10519,7 +10474,7 @@ const tech = { }, requires: "", effect() { - String.prototype.shuffle = function () { + String.prototype.shuf = function () { var a = this.split(""), n = a.length; @@ -10532,7 +10487,7 @@ const tech = { return a.join(""); } - for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuffle() + for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuf() }, remove() { } }, @@ -12190,7 +12145,6 @@ const tech = { isDuplicateMobs: null, isDynamoBotUpgrade: null, isBlockPowerUps: null, - isDamageAfterKillNoRegen: null, isHarmReduceNoKill: null, isSwitchReality: null, isResearchReality: null, diff --git a/todo.txt b/todo.txt index 2802010..4dc0196 100644 --- a/todo.txt +++ b/todo.txt @@ -1,98 +1,77 @@ ******************************************************** NEXT PATCH ************************************************** -tech: demineralization - after mobs die gain 0.85x damage taken, effect stacks, but fades 10% every second -tech: remineralization - after mobs die gain 1.08x damage, effect stacks, but fades 10% every second -tech: equivalence principle - negative mass field doesn't cost energy -new JUNK tech: aerodynamics - -interferometer - slower elevator and lasers - wider side ledges - large laser blocking blocks -flocculation - fewer mobs - it's easier to get out of the slime -pavilion - move vanish elements - easier traversal - secret tunnel - removed debris, but added power ups and blocks -corridor - limited to bosses that don't interact with the movers poorly -gravitron, substructure, corridor, interferometer - added more heal and ammo power ups to match other levels -because some newer levels are zoomed out more - laser max range is 3000->5000 - nails last 1/3 of a second longer -bosses spawn an extra ammo power up - and 2 extra ammo on the hardest 2 difficulties -slasher mob's laserSwords will now damage a cloaked player -constraint announcement text looks more like computer code style to match game theme - -foam recoil is back: 1->0.7x horizontal force and 2->4.3x vertical up force - this makes it less annoying to horizontally and easier to kinda fly/float -negative mass field damage reduction 0.4->0.5x -holographic principle no longer slows player movement - added 2 research cost -fermion gives 6->5 seconds of invulnerability after mobs die -stability 0.2->0.1x damage taken at max health -non-Newtonian armor 0.3->0.4x damage taken after collisions -Zeno's paradox 0.15->0.2x damage taken -annihilation energy cost 10->8 to destroy mobs after collisions -radiative equilibrium damage is 3->4x for 8->4 seconds -aerostat can be taken 1->3 times -dynamic equilibrium damage increased by 6->8x damage per last damage taken -aerostat no longer has 0.9x damage for being on the ground -launch system 1.2->1.3x ammo for missiles -research says that repeatedly entering alternate realities builds up some positive effects - Hilbert space 4x->3x damage - Ψ(t) collapse 6->4 research on boss death -transdimensional worms: 50% chance for a second worm per block in wormhole -wormhole 7->8 energy regen per second -hidden-variable theory 1.15->1.2 damage after choosing a field tech -ghoster mobs are less likely to get knocked far away from the player for long periods of time +mantisBoss flashes for a second before it drops invulnerability +removed parasitism - it's too similar to invulnerability tech +invariant no longer drains energy while wormhole time is paused + added 1 research cost +added secret combo to change molecular assembler mode bug fixes - dynamic equilibrium was set to 100 times higher frequency then normal - when constraints hide health bar's it's now hidden in the pause menu - mobs aiming at cloaked player - snakeBoss more intelligently chases player for a few seconds - pulsarBoss aims at player's history 3 seconds in past - pulsar will not stop firing - but it will still not fire at cloaked player + issue with constraint: "mob death heals mobs" + mob health was becoming NaN, this was infecting other values like player energy + entering a seed in settings wasn't giving the same results as a randomly generated seeds + also removed some random code that was using seeded shuffle, but didn't need to + converted it to non seeded random shuffle with .sort(() => Math.random() - 0.5); ******************************************************** BUGS ******************************************************** -figure out why seeded random isn't making runs the same: - shuffle is being used for a wide variety of things that don't need a seeded random - make two shuffle functions? - check which of these is working - level order - is flipped - constraint order - mob type order - boss order - gun, field, tech options - make field options offered precalculated so it doesn't depend on player choices? - generate all constraints at the start of the game - doesn't seem to be determined by the seed... - display future constraints in pause menu? - player can become crouched while not touching the ground if they exit the ground while crouched *********************************************************** TODO ***************************************************** + +use ←↑→↓↖↗↘↙ combos to allow fields to have special actions + !!should this be wasd, arrows, or both? + how to limit spam? + on cooldown + timer or once per level + energy cost + research cost + toggle modes + combos are listed in white text in field description, and console message when getting field + shows up on highlight, and mouse over in experiment mode + field ideas: + standing wave + push mobs away are any distance + pull mobs towards you + perfect diamagnetism + negative mass + molecular assembler - done + plasma torch + time dilation + metamaterial cloaking + pilot wave + spawn blocks + wormhole + shoot out all the blocks that were sucked in this level (maybe cap at like 10?, cap with energy spent to fire) + are block sizes stored properly? because they shrink before they get eaten... + store vertices on the body object if one does already exists + after eating store the saved vertices + where to fire blocks from and in what direction? + target mobs? + fire from player (and draw a wormhole looking graphic) + grappling hook + +new level idea: escort mission + player has to stay near something that moves slowly through the level + maybe only a zone around the escort is safe + safe from? + lasers + slime + radiation + use line of site vision? + is it going to feel too slow? + where to put in level order? + random option instead of reactor? + normal level? + 4th hard level? + already too many hard options + you could put in 2 hard levels in the level list + make the style look like substructure, that level looks great + use the motion sense lasers + tech synergy ideas a tech that spawns mobs that the player can use to trigger mob death tech - -wormhole field needs a buff - it's good on wide open maps, but it needs a defensive effect on more closed maps - fix? - increase energy regen? - new tech? - a way to make more blocks - the block can feed into worms/coupling energy - tech: - remove the research costs of all tech there's about 15 tech with a research cost