From 60e59a858a3553d155fc45f7a623a0e65c9d61c6 Mon Sep 17 00:00:00 2001 From: landgreen Date: Mon, 21 Dec 2020 12:47:07 -0800 Subject: [PATCH] CPT gun new level boss that fires 2 streams of small bullets that chase you mod: add a CPT gun to your inventory that rewinds your history, reverts your health, position, velocity for 10 seconds I expect that spamming rewind has some overpowered combos. Let me know what you find, and your ideas on balance. --- js/bullet.js | 307 +++++++++++++++++-------------- js/game.js | 48 +++-- js/index.js | 24 ++- js/level.js | 20 +- js/mob.js | 2 +- js/mods.js | 134 ++++++++++---- js/player.js | 507 ++++++++++++++++++++++++++++++--------------------- js/spawn.js | 145 +++++++++++++-- style.css | 1 + todo.txt | 85 ++++++--- 10 files changed, 815 insertions(+), 458 deletions(-) diff --git a/js/bullet.js b/js/bullet.js index 7bd9a00..21c5c80 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -274,6 +274,132 @@ const b = { } } }, + pulse(energy, angle = mech.angle) { + let best; + let explosionRange = 1560 * energy + let range = 3000 + const path = [{ + x: mech.pos.x + 20 * Math.cos(angle), + y: mech.pos.y + 20 * Math.sin(angle) + }, + { + x: mech.pos.x + range * Math.cos(angle), + y: mech.pos.y + range * Math.sin(angle) + } + ]; + const vertexCollision = function(v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + //check for collisions + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + if (mod.isPulseAim) { //find mobs in line of sight + let dist = 2200 + for (let i = 0, len = mob.length; i < len; i++) { + const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position)) + if (explosionRange < newDist && + newDist < dist && + Matter.Query.ray(map, path[0], mob[i].position).length === 0 && + Matter.Query.ray(body, path[0], mob[i].position).length === 0) { + dist = newDist + best.who = mob[i] + path[path.length - 1] = mob[i].position + } + } + } + if (!best.who) { + vertexCollision(path[0], path[1], mob); + vertexCollision(path[0], path[1], map); + vertexCollision(path[0], path[1], body); + if (best.dist2 != Infinity) { //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + } + } + if (best.who) b.explosion(path[1], explosionRange, true) + + if (mod.isPulseStun) { + const range = 100 + 2000 * energy + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isShielded) { + dist = Vector.magnitude(Vector.sub(path[1], mob[i].position)) - mob[i].radius; + if (dist < range) mobs.statusStun(mob[i], 30 + Math.floor(energy * 60)) + } + } + } + //draw laser beam + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + ctx.strokeStyle = "rgba(255,0,0,0.13)" + ctx.lineWidth = 60 * energy / 0.2 + ctx.stroke(); + ctx.strokeStyle = "rgba(255,0,0,0.2)" + ctx.lineWidth = 18 + ctx.stroke(); + ctx.strokeStyle = "#f00"; + ctx.lineWidth = 4 + ctx.stroke(); + + //draw little dots along the laser path + const sub = Vector.sub(path[1], path[0]) + const mag = Vector.magnitude(sub) + for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) { + const dist = Math.random() + game.drawList.push({ + x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5), + y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5), + radius: 1 + 4 * Math.random(), + color: "rgba(255,0,0,0.5)", + time: Math.floor(2 + 33 * Math.random() * Math.random()) + }); + } + }, grenade() { }, @@ -550,7 +676,7 @@ const b = { } } slow(body, this.damageRadius) - slow([player], this.damageRadius) + if (!mod.isNeutronImmune) slow([player], this.damageRadius) } } } @@ -591,6 +717,7 @@ const b = { }, onEnd() { b.explosion(this.position, this.explodeRad * size); //makes bullet do explosive damage at end + if (mod.fragments) b.targetedNail(this.position, mod.fragments * 5) if (spawn) { for (let i = 0; i < mod.recursiveMissiles; i++) { if (0.2 - 0.02 * i > Math.random()) { @@ -724,8 +851,8 @@ const b = { const q = Matter.Query.point(mob, this.position) for (let i = 0; i < q.length; i++) { Matter.Body.setVelocity(q[i], { - x: q[i].velocity.x * 0.7, - y: q[i].velocity.y * 0.7 + x: q[i].velocity.x * 0.6, + y: q[i].velocity.y * 0.6 }); Matter.Body.setPosition(this, Vector.add(this.position, q[i].velocity)) //move with the medium let dmg = this.dmg / Math.min(10, q[i].mass) @@ -743,9 +870,9 @@ const b = { this.cycle++ const wiggleMag = (mech.crouch ? 6 : 12) * Math.cos(game.cycle * 0.09) const wiggle = Vector.mult(transverse, wiggleMag * Math.cos(this.cycle * 0.36)) //+ wiggleMag * Math.cos(game.cycle * 0.3)) - // const velocity = Vector.mult(player.velocity, 0.25) //move with player - // Matter.Body.setPosition(this, Vector.add(velocity, Vector.add(this.position, wiggle))) - Matter.Body.setPosition(this, Vector.add(this.position, wiggle)) + const velocity = Vector.mult(player.velocity, 0.3) //move with player + Matter.Body.setPosition(this, Vector.add(velocity, Vector.add(this.position, wiggle))) + // Matter.Body.setPosition(this, Vector.add(this.position, wiggle)) } } }); @@ -2089,11 +2216,11 @@ const b = { const DIST = Vector.magnitude(sub); const unit = Vector.normalise(sub) if (DIST < mod.isPlasmaRange * 450 && mech.energy > this.drainThreshold) { - mech.energy -= 0.0012; - if (mech.energy < 0) { - mech.fieldCDcycle = mech.cycle + 120; - mech.energy = 0; - } + mech.energy -= 0.005; + // if (mech.energy < 0) { + // mech.fieldCDcycle = mech.cycle + 120; + // mech.energy = 0; + // } //calculate laser collision let best; let range = mod.isPlasmaRange * (120 + 300 * Math.sqrt(Math.random())) @@ -2164,7 +2291,7 @@ const b = { y: best.y }; if (best.who.alive) { - const dmg = 0.8 * b.dmgScale; //********** SCALE DAMAGE HERE ********************* + const dmg = 0.6 * b.dmgScale; //********** SCALE DAMAGE HERE ********************* best.who.damage(dmg); best.who.locatePlayer(); //push mobs away @@ -3612,13 +3739,13 @@ const b = { y: mech.pos.y + 3000 * Math.sin(mech.angle) }, dmg, 0, true); for (let i = 1; i < len; i++) { - const history = mech.history[(mech.cycle - i * spacing) % 300] + const history = mech.history[(mech.cycle - i * spacing) % 600] b.laser({ x: history.position.x + 20 * Math.cos(history.angle), - y: history.position.y + 20 * Math.sin(history.angle) + y: history.position.y + 20 * Math.sin(history.angle) - mech.yPosDifference }, { x: history.position.x + 3000 * Math.cos(history.angle), - y: history.position.y + 3000 * Math.sin(history.angle) + y: history.position.y + 3000 * Math.sin(history.angle) - mech.yPosDifference }, dmg, 0, true); } ctx.stroke(); @@ -3642,130 +3769,38 @@ const b = { }, }, ], - pulse(energy, angle = mech.angle) { - let best; - let explosionRange = 1560 * energy - let range = 3000 - const path = [{ - x: mech.pos.x + 20 * Math.cos(angle), - y: mech.pos.y + 20 * Math.sin(angle) - }, - { - x: mech.pos.x + range * Math.cos(angle), - y: mech.pos.y + range * Math.sin(angle) - } - ]; - const vertexCollision = function(v1, v1End, domain) { - for (let i = 0; i < domain.length; ++i) { - let vertices = domain[i].vertices; - const len = vertices.length - 1; - for (let j = 0; j < len; j++) { - results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); - if (results.onLine1 && results.onLine2) { - const dx = v1.x - results.x; - const dy = v1.y - results.y; - const dist2 = dx * dx + dy * dy; - if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - best = { - x: results.x, - y: results.y, - dist2: dist2, - who: domain[i], - v1: vertices[j], - v2: vertices[j + 1] - }; - } - } - } - results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); - if (results.onLine1 && results.onLine2) { - const dx = v1.x - results.x; - const dy = v1.y - results.y; - const dist2 = dx * dx + dy * dy; - if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - best = { - x: results.x, - y: results.y, - dist2: dist2, - who: domain[i], - v1: vertices[0], - v2: vertices[len] - }; + gunRewind: { //this gun is added with a mod + name: "CPT gun", + description: "use energy to rewind your health, velocity,
and position up to 10 seconds", + ammo: 0, + ammoPack: Infinity, + have: false, + isRewinding: false, + lastFireCycle: 0, + holdCount: 0, + fire() { + if (this.lastFireCycle === mech.cycle - 1) { //button has been held down + this.rewindCount += 7; + const DRAIN = 0.01 + if (this.rewindCount > 599 || mech.energy < DRAIN) { + this.rewindCount = 0; + mech.resetHistory(); + mech.fireCDcycle = mech.cycle + Math.floor(60 * b.fireCD); // cool down + } else { + mech.energy -= DRAIN + mech.immuneCycle = mech.cycle + 5; //player is immune to collision damage for 5 cycles + let history = mech.history[(mech.cycle - this.rewindCount) % 600] + Matter.Body.setPosition(player, history.position); + Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); + if (mech.health !== history.health) { + mech.health = history.health + mech.displayHealth(); } } + } else { //button is held the first time + this.rewindCount = 0; } - }; - //check for collisions - best = { - x: null, - y: null, - dist2: Infinity, - who: null, - v1: null, - v2: null - }; - if (mod.isPulseAim) { //find mobs in line of sight - let dist = 2200 - for (let i = 0, len = mob.length; i < len; i++) { - const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position)) - if (explosionRange < newDist && - newDist < dist && - Matter.Query.ray(map, path[0], mob[i].position).length === 0 && - Matter.Query.ray(body, path[0], mob[i].position).length === 0) { - dist = newDist - best.who = mob[i] - path[path.length - 1] = mob[i].position - } - } - } - if (!best.who) { - vertexCollision(path[0], path[1], mob); - vertexCollision(path[0], path[1], map); - vertexCollision(path[0], path[1], body); - if (best.dist2 != Infinity) { //if hitting something - path[path.length - 1] = { - x: best.x, - y: best.y - }; - } - } - if (best.who) b.explosion(path[1], explosionRange, true) - - if (mod.isPulseStun) { - const range = 100 + 2000 * energy - for (let i = 0, len = mob.length; i < len; ++i) { - if (mob[i].alive && !mob[i].isShielded) { - dist = Vector.magnitude(Vector.sub(path[1], mob[i].position)) - mob[i].radius; - if (dist < range) mobs.statusStun(mob[i], 30 + Math.floor(energy * 60)) - } - } - } - //draw laser beam - ctx.beginPath(); - ctx.moveTo(path[0].x, path[0].y); - ctx.lineTo(path[1].x, path[1].y); - ctx.strokeStyle = "rgba(255,0,0,0.13)" - ctx.lineWidth = 60 * energy / 0.2 - ctx.stroke(); - ctx.strokeStyle = "rgba(255,0,0,0.2)" - ctx.lineWidth = 18 - ctx.stroke(); - ctx.strokeStyle = "#f00"; - ctx.lineWidth = 4 - ctx.stroke(); - - //draw little dots along the laser path - const sub = Vector.sub(path[1], path[0]) - const mag = Vector.magnitude(sub) - for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) { - const dist = Math.random() - game.drawList.push({ - x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5), - y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5), - radius: 1 + 4 * Math.random(), - color: "rgba(255,0,0,0.5)", - time: Math.floor(2 + 33 * Math.random() * Math.random()) - }); + this.lastFireCycle = mech.cycle; } } }; \ No newline at end of file diff --git a/js/game.js b/js/game.js index d9b7938..2b2abf7 100644 --- a/js/game.js +++ b/js/game.js @@ -596,7 +596,17 @@ const game = { } if (mod.isEndLevelPowerUp) { - for (let i = 0; i < powerUp.length; i++) powerUp[i].effect(); + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "mod") { + mod.giveMod() + } else if (powerUp[i].name === "gun") { + if (!mod.isOneGun) b.giveGuns("random") + } else if (powerUp[i].name === "field") { + if (mech.fieldMode === 0) mech.setField(Math.ceil(Math.random() * (mech.fieldUpgrades.length - 1))) //pick a random field, but not field 0 + } else { + powerUp[i].effect(); + } + } } powerUps.totalPowerUps = powerUp.length @@ -761,24 +771,24 @@ const game = { x: 0, y: 40 }); - if ((playerHead.position.y - player.position.y) > 0) { - Matter.Body.translate(playerHead, { - x: 0, - y: 40 - }); - if ((playerHead.position.y - player.position.y) > 0) { - Matter.Body.translate(playerHead, { - x: 0, - y: 40 - }); - if ((playerHead.position.y - player.position.y) > 0) { - Matter.Body.translate(playerHead, { - x: 0, - y: 40 - }); - } - } - } + // if ((playerHead.position.y - player.position.y) > 0) { + // Matter.Body.translate(playerHead, { + // x: 0, + // y: 40 + // }); + // if ((playerHead.position.y - player.position.y) > 0) { + // Matter.Body.translate(playerHead, { + // x: 0, + // y: 40 + // }); + // if ((playerHead.position.y - player.position.y) > 0) { + // Matter.Body.translate(playerHead, { + // x: 0, + // y: 40 + // }); + // } + // } + // } } else if (mech.crouch && ((playerHead.position.y - player.position.y) > 10)) { Matter.Body.translate(playerHead, { x: 0, diff --git a/js/index.js b/js/index.js index aefdc73..0695e53 100644 --- a/js/index.js +++ b/js/index.js @@ -787,7 +787,7 @@ window.addEventListener("keydown", function(event) { break } if (game.testing) { - switch (event.key) { + switch (event.key.toLowerCase()) { case "o": game.isAutoZoom = false; game.zoomScale /= 0.9; @@ -1081,4 +1081,24 @@ function cycle() { // loop[i]() // } } -} \ No newline at end of file +} + + + +//display console logs in game + +// function proxy(context, method, message) { +// return function() { +// // method.apply(context, [message].concat(Array.prototype.slice.apply(arguments))) +// game.makeTextLog(arguments[0], 300) +// } +// } + +// console.log = proxy(console, console.log, 'n-gon: ') +// console.error = proxy(console, console.error, 'Error:') +// console.warn = proxy(console, console.warn, 'Warning:') + +// let's test +// console.log('im from console.log', level, 2, 3); +// console.error('im from console.error', 1, 2, 3); +// console.warn('im from console.warn', 1, 2, 3); \ No newline at end of file diff --git a/js/level.js b/js/level.js index caf4e5f..1b9af04 100644 --- a/js/level.js +++ b/js/level.js @@ -13,17 +13,15 @@ const level = { start() { if (level.levelsCleared === 0) { //this code only runs on the first level // game.enableConstructMode() //used to build maps in testing mode - // level.difficultyIncrease(19) + // level.difficultyIncrease(1) // game.zoomScale = 1000; // game.setZoom(); // mech.setField("plasma torch") // b.giveGuns("wave beam") - // mod.giveMod("micro-extruder") - // mod.giveMod("piezoelectricity") + // mod.giveMod("CPT reversal") + // mod.giveMod("CPT gun") // for (let i = 0; i < 15; i++) mod.giveMod("plasma jet") - - level.intro(); //starting level // level.testing(); //not in rotation // level.finalBoss() //final boss level @@ -163,12 +161,12 @@ const level = { // spawn.launcherBoss(1200, -500) // spawn.laserTargetingBoss(1600, -400) // spawn.striker(1600, -500) - spawn.shooter(1700, -120) + // spawn.shooter(1700, -120) // spawn.bomberBoss(1400, -500) // spawn.sniper(1800, -120) // spawn.cellBossCulture(1600, -500) - // spawn.spiderBoss(1600, -500) - // spawn.laser(1200, -500) + spawn.streamBoss(1600, -500) + // spawn.beamer(1200, -500) // spawn.shield(mob[mob.length - 1], 1800, -120, 1); // spawn.nodeBoss(1200, -500, "launcher") @@ -793,9 +791,9 @@ const level = { powerUps.spawnBossPowerUp(-125, -1760); } else { if (Math.random() < 0.5) { - spawn.randomLevelBoss(700, -1550, ["shooterBoss", "launcherBoss", "laserTargetingBoss"]); + spawn.randomLevelBoss(700, -1550, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]); } else { - spawn.randomLevelBoss(675, -2775, ["shooterBoss", "launcherBoss", "laserTargetingBoss"]); + spawn.randomLevelBoss(675, -2775, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]); } } powerUps.addRerollToLevel() //needs to run after mobs are spawned @@ -942,7 +940,7 @@ const level = { spawn.randomMob(3600, 1725, 0.9); spawn.randomMob(4100, 1225, 0.9); spawn.randomMob(2825, 400, 0.9); - if (game.difficulty > 3) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss"]); + if (game.difficulty > 3) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]); powerUps.addRerollToLevel() //needs to run after mobs are spawned }, satellite() { diff --git a/js/mob.js b/js/mob.js index d0680ae..57eed7f 100644 --- a/js/mob.js +++ b/js/mob.js @@ -481,7 +481,7 @@ const mobs = { } } }; - if (this.seePlayer.recall) { + if (this.seePlayer.recall && !this.isSlowed) { this.torque = this.lookTorque * this.inertia * 2; const seeRange = 2500; diff --git a/js/mods.js b/js/mods.js index 7892503..144c3ca 100644 --- a/js/mods.js +++ b/js/mods.js @@ -386,22 +386,6 @@ const mod = { mod.throwChargeRate = 1 } }, - { - name: "fragmentation", - description: "detonation or collisions with mobs eject nails
blocks, rail gun, grenades, shotgun slugs", - maxCount: 9, - count: 0, - allowed() { - return (mod.haveGunCheck("grenades") && !mod.isNeutronBomb) || mod.haveGunCheck("rail gun") || (mod.haveGunCheck("shotgun") && mod.isSlugShot) || mod.throwChargeRate > 1 - }, - requires: "grenades, rail gun, shotgun slugs, or mass driver", - effect() { - mod.fragments++ - }, - remove() { - mod.fragments = 0 - } - }, { name: "ammonium nitrate", description: "increase explosive damage by 20%
increase explosive radius by 20%", @@ -569,7 +553,7 @@ const mod = { b.nailBot(); }, remove() { - mod.nailBotCount = 0; + mod.nailBotCount -= this.count; } }, { @@ -608,7 +592,7 @@ const mod = { b.foamBot(); }, remove() { - mod.foamBotCount = 0; + mod.foamBotCount -= this.count; } }, { @@ -647,7 +631,7 @@ const mod = { b.boomBot(); }, remove() { - mod.boomBotCount = 0; + mod.boomBotCount -= this.count; } }, { @@ -686,7 +670,7 @@ const mod = { b.laserBot(); }, remove() { - mod.laserBotCount = 0; + mod.laserBotCount -= this.count; } }, { @@ -725,7 +709,7 @@ const mod = { mod.orbitBotCount++; }, remove() { - mod.orbitBotCount = 0; + mod.orbitBotCount -= this.count; } }, { @@ -1074,9 +1058,9 @@ const mod = { maxCount: 1, count: 0, allowed() { //&& (mech.fieldUpgrades[mech.fieldMode].name !== "nano-scale manufacturing" || mech.maxEnergy > 1) - return mech.maxEnergy > 0.99 && mech.fieldUpgrades[mech.fieldMode].name !== "standing wave harmonics" && !mod.isEnergyHealth + return mech.maxEnergy > 0.99 && mech.fieldUpgrades[mech.fieldMode].name !== "standing wave harmonics" && !mod.isEnergyHealth && !mod.isRewindGun }, - requires: "standing wave, mass-energy, piezoelectricity, max energy reduction", + requires: "not standing wave, mass-energy, piezo, max energy reduction, CPT gun", effect() { mod.isRewindAvoidDeath = true; }, @@ -1135,13 +1119,13 @@ const mod = { }, { name: "ground state", - description: "reduce harm by 66%
you no longer passively regenerate energy", + description: "reduce harm by 60%
you no longer passively regenerate energy", maxCount: 1, count: 0, allowed() { - return mod.isPiezo && mod.energyRegen !== 0.004 + return (mod.iceEnergy || mod.isWormholeEnergy || mod.isPiezo || mod.isRailEnergyGain) && mod.energyRegen !== 0.004 }, - requires: "piezoelectricity, not time crystals", + requires: "piezoelectricity, Penrose, half-wave, or thermoelectric, but not time crystals", effect: () => { mod.energyRegen = 0; mech.fieldRegen = mod.energyRegen; @@ -1157,7 +1141,7 @@ const mod = { maxCount: 1, count: 0, allowed() { - return !mod.isEnergyLoss && !mod.isPiezo && !mod.isRewindAvoidDeath && !mod.isSpeedHarm && mech.fieldUpgrades[mech.fieldMode].name !== "negative mass field" + return !mod.isEnergyLoss && !mod.isPiezo && !mod.isRewindAvoidDeath && !mod.isRewindGun && !mod.isSpeedHarm && mech.fieldUpgrades[mech.fieldMode].name !== "negative mass field" }, requires: "not exothermic process, piezoelectricity, CPT, 1st law, negative mass", effect: () => { @@ -1328,13 +1312,13 @@ const mod = { }, { name: "transceiver chip", - description: "at the end of each level
gain the full effect of unused power ups", + description: "unused power ups at the end of each level
are still activated (selections are random)", maxCount: 1, count: 0, allowed() { return mod.isArmorFromPowerUps }, - requires: "crystallized armor", + requires: "inductive coupling", effect() { mod.isEndLevelPowerUp = true; }, @@ -1535,7 +1519,7 @@ const mod = { if (mod.mods[i].count > 0) have.push(i) } const choose = have[Math.floor(Math.random() * have.length)] - game.makeTextLog(`
  ${mod.mods[choose].name} removed by reallocation`, 300) + game.makeTextLog(`
  ${mod.mods[choose].name} removed by monte carlo experiment`, 300) for (let i = 0; i < mod.mods[choose].count; i++) { powerUps.spawn(mech.pos.x, mech.pos.y, "mod"); } @@ -1935,10 +1919,50 @@ const mod = { //************************************************** //************************************************** gun //************************************************** mods - //************************************************** + //************************************************** + { + name: "CPT gun", + description: "adds the CPT gun to your inventory
it rewinds your health, velocity, and position", + isGunMod: true, + maxCount: 1, + count: 0, + allowed() { + return (mod.totalBots() > 5 || mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" || mech.fieldUpgrades[mech.fieldMode].name === "plasma torch" || mech.fieldUpgrades[mech.fieldMode].name === "pilot wave") && !mod.isEnergyHealth && !mod.isRewindAvoidDeath + }, + requires: "bots > 5, plasma torch, nano-scale, pilot wave, not mass-energy equivalence, CPT", + effect() { + mod.isRewindGun = true + b.guns.push(b.gunRewind) + b.giveGuns("CPT gun"); + }, + remove() { + if (mod.isRewindGun) { + for (let i = 0; i < b.guns.length; i++) { + if (b.guns[i].name === "CPT gun") { + for (let j = 0; j < b.inventory.length; j++) { + if (b.inventory[j] === i) { + b.inventory.splice(j, 1) + break + } + } + if (b.inventory.length) { + b.activeGun = b.inventory[0]; + } else { + b.activeGun = null; + } + game.makeGunHUD(); + + b.guns.splice(i, 1) //also remove CPT gun from gun pool array + break + } + } + mod.isRewindGun = false + } + } + }, { name: "incendiary ammunition", - description: "bullets are loaded with explosives
nail gun, shotgun, super balls, drones", + description: "some bullets are loaded with explosives
nail gun, shotgun, super balls, drones", isGunMod: true, maxCount: 1, count: 0, @@ -1953,9 +1977,26 @@ const mod = { mod.isIncendiary = false; } }, + { + name: "fragmentation", + description: "some detonations and collisions eject nails
blocks, rail gun, grenades, missiles, shotgun slugs", + isGunMod: true, + maxCount: 9, + count: 0, + allowed() { + return (mod.haveGunCheck("grenades") && !mod.isNeutronBomb) || mod.haveGunCheck("missiles") || mod.haveGunCheck("rail gun") || (mod.haveGunCheck("shotgun") && mod.isSlugShot) || mod.throwChargeRate > 1 + }, + requires: "grenades, missiles, rail gun, shotgun slugs, or mass driver", + effect() { + mod.fragments++ + }, + remove() { + mod.fragments = 0 + } + }, { name: "Lorentzian topology", - description: "bullets last 30% longer
drones, spores, missiles, foam, wave, ice IX, neutron", + description: "some bullets last 30% longer
drones, spores, missiles, foam, wave, ice IX, neutron", isGunMod: true, maxCount: 3, count: 0, @@ -2783,7 +2824,7 @@ const mod = { }, { name: "colloidal foam", - description: "increase foam damage by 200%
foam dissipates 40% faster", + description: "increase foam damage by 366%
foam dissipates 40% faster", isGunMod: true, maxCount: 1, count: 0, @@ -3259,8 +3300,8 @@ const mod = { mod.giveMod("orbital-bot upgrade") mod.setModToNonRefundable("orbital-bot upgrade") for (let i = 0; i < 2; i++) { - b.orbitalBot() - mod.orbitalBotCount++; + b.orbitBot() + mod.orbitBotCount++; } }) //choose random function from the array and run it @@ -3370,6 +3411,23 @@ const mod = { mod.isFreezeMobs = false } }, + // { + // name: "thermal reservoir", + // description: "increase your plasma damage by 100%
plasma temporarily lowers health not energy", + // isFieldMod: true, + // maxCount: 1, + // count: 0, + // allowed() { + // return mech.fieldUpgrades[mech.fieldMode].name === "plasma torch" && !mod.isEnergyHealth + // }, + // requires: "plasma torch, not mass-energy equivalence", + // effect() { + // mod.isPlasmaRange += 0.27; + // }, + // remove() { + // mod.isPlasmaRange = 1; + // } + // }, { name: "plasma jet", description: "increase plasma torch's range by 27%", @@ -3874,5 +3932,7 @@ const mod = { isRewindBot: null, isRewindGrenade: null, isExtruder: null, - isEndLevelPowerUp: null + isEndLevelPowerUp: null, + isRewindGun: null + } \ No newline at end of file diff --git a/js/player.js b/js/player.js index b9be3b9..e7b4f9c 100644 --- a/js/player.js +++ b/js/player.js @@ -103,6 +103,7 @@ const mech = { x: 0, y: 0 }, + yPosDifference: 24.285923217549026, //player.position.y - mech.pos.y Sy: 0, //adds a smoothing effect to vertical only Vx: 0, Vy: 0, @@ -135,11 +136,11 @@ const mech = { transY: 0, history: [], //tracks the last second of player position resetHistory() { - for (let i = 0; i < 300; i++) { //reset history + for (let i = 0; i < 600; i++) { //reset history mech.history[i] = { position: { - x: mech.pos.x, - y: mech.pos.y, + x: player.position.x, + y: player.position.y, }, velocity: { x: player.velocity.x, @@ -159,10 +160,10 @@ const mech = { //tracks the last second of player information // console.log(mech.history) - mech.history.splice(mech.cycle % 300, 1, { + mech.history.splice(mech.cycle % 600, 1, { position: { - x: mech.pos.x, - y: mech.pos.y, + x: player.position.x, + y: player.position.y, }, velocity: { x: player.velocity.x, @@ -461,14 +462,14 @@ const mech = { if (mod.isBotArmor) dmg *= 0.97 ** mod.totalBots() if (mod.isHarmArmor && mech.lastHarmCycle + 600 > mech.cycle) dmg *= 0.33; if (mod.isNoFireDefense && mech.cycle > mech.fireCDcycle + 120) dmg *= 0.6 - if (mod.energyRegen === 0) dmg *= 0.33 //0.22 + 0.78 * mech.energy //77% damage reduction at zero energy + if (mod.energyRegen === 0) dmg *= 0.4 if (mod.isTurret && mech.crouch) dmg *= 0.5; if (mod.isEntanglement && b.inventory[0] === b.activeGun) { for (let i = 0, len = b.inventory.length; i < len; i++) dmg *= 0.87 // 1 - 0.15 } return dmg }, - rewind(steps) { + rewind(steps) { // mech.rewind(Math.floor(Math.min(599, 137 * mech.energy))) if (mod.isRewindGrenade) { for (let i = 1, len = Math.floor(2 + steps / 40); i < len; i++) { b.grenade(Vector.add(mech.pos, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }), -i * Math.PI / len) //fire different angles for each grenade @@ -494,7 +495,7 @@ const mech = { } } } - let history = mech.history[(mech.cycle - steps) % 300] + let history = mech.history[(mech.cycle - steps) % 600] Matter.Body.setPosition(player, history.position); Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); // move bots to follow player @@ -529,7 +530,7 @@ const mech = { ctx.scale(game.zoom / game.edgeZoomOutSmooth, game.zoom / game.edgeZoomOutSmooth); //zoom in once centered ctx.translate(-canvas.width2 + mech.transX, -canvas.height2 + mech.transY); //translate for (let i = 1; i < steps; i++) { - history = mech.history[(mech.cycle - i) % 300] + history = mech.history[(mech.cycle - i) % 600] mech.pos.x = history.position.x mech.pos.y = history.position.y mech.draw(); @@ -547,7 +548,7 @@ const mech = { if (mod.isRewindBot) { const len = steps * 0.042 * mod.isRewindBot for (let i = 0; i < len; i++) { - const where = mech.history[Math.abs(mech.cycle - i * 40) % 300].position //spread out spawn locations along past history + const where = mech.history[Math.abs(mech.cycle - i * 40) % 600].position //spread out spawn locations along past history b.randomBot({ x: where.x + 100 * (Math.random() - 0.5), y: where.y + 100 * (Math.random() - 0.5) @@ -558,8 +559,7 @@ const mech = { }, damage(dmg) { if (mod.isRewindAvoidDeath && mech.energy > 0.66) { - const steps = Math.floor(Math.min(299, 137 * mech.energy)) - mech.rewind(steps) + mech.rewind(Math.floor(Math.min(299, 137 * mech.energy))) return } mech.lastHarmCycle = mech.cycle @@ -764,7 +764,7 @@ const mech = { ctx.stroke(); // ctx.beginPath(); // ctx.arc(15, 0, 3, 0, 2 * Math.PI); - // ctx.fillStyle = '#9cf' //'#0cf'; + // ctx.fillStyle = '#0cf'; // ctx.fill() ctx.restore(); mech.yOff = mech.yOff * 0.85 + mech.yOffGoal * 0.15; //smoothly move leg height towards height goal @@ -2194,11 +2194,104 @@ const mech = { { name: "wormhole", description: "use energy to tunnel through a wormhole
wormholes attract blocks and power ups
10% chance to duplicate spawned power ups", //
bullets may also traverse wormholes - effect: () => { + effect: function() { game.replaceTextLog = true; //allow text over write mech.drop(); mech.duplicateChance = 0.1 game.draw.powerUp = game.draw.powerUpBonus //change power up draw + + // if (mod.isRewindGun) { + // mech.hold = this.rewind + // } else { + mech.hold = this.teleport + // } + }, + rewindCount: 0, + // rewind: function() { + // if (input.down) { + // if (input.field && mech.fieldCDcycle < mech.cycle) { //not hold but field button is pressed + // const DRAIN = 0.01 + // if (this.rewindCount < 289 && mech.energy > DRAIN) { + // mech.energy -= DRAIN + + + // if (this.rewindCount === 0) { + // const shortPause = function() { + // if (mech.defaultFPSCycle < mech.cycle) { //back to default values + // game.fpsCap = game.fpsCapDefault + // game.fpsInterval = 1000 / game.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (mech.defaultFPSCycle < mech.cycle) requestAnimationFrame(shortPause); + // game.fpsCap = 4 //1 is longest pause, 4 is standard + // game.fpsInterval = 1000 / game.fpsCap; + // mech.defaultFPSCycle = mech.cycle + // } + + + // this.rewindCount += 10; + // game.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 = mech.history[(mech.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 > mech.health) { + // mech.health = history.health + // mech.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 && !game.isChoosing && !(mech.health === mech.maxHealth && powerUp[i].name === "heal")) { + // powerUps.onPickUp(player.position); + // powerUp[i].effect(); + // Matter.World.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // const shortPause = function() { + // if (mech.defaultFPSCycle < mech.cycle) { //back to default values + // game.fpsCap = game.fpsCapDefault + // game.fpsInterval = 1000 / game.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (mech.defaultFPSCycle < mech.cycle) requestAnimationFrame(shortPause); + // game.fpsCap = 3 //1 is longest pause, 4 is standard + // game.fpsInterval = 1000 / game.fpsCap; + // mech.defaultFPSCycle = mech.cycle + // break; //because the array order is messed up after splice + // } + // } + // mech.immuneCycle = mech.cycle + 5; //player is immune to collision damage for 30 cycles + // } else { + // mech.fieldCDcycle = mech.cycle + 30; + // // mech.resetHistory(); + // } + // } else { + // if (this.rewindCount !== 0) { + // mech.fieldCDcycle = mech.cycle + 30; + // mech.resetHistory(); + // this.rewindCount = 0; + // game.wipe = function() { //set wipe to normal + // ctx.clearRect(0, 0, canvas.width, canvas.height); + // } + // } + // mech.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // } + // } + // mech.drawFieldMeter() + // }, + teleport: function() { // mech.hole = { //this is reset with each new field, but I'm leaving it here for reference // isOn: false, // isReady: true, @@ -2207,237 +2300,235 @@ const mech = { // angle: 0, // unit:{x:0,y:0}, // } - mech.hold = function() { - if (mech.hole.isOn) { - // draw holes - mech.fieldRange = 0.97 * mech.fieldRange + 0.03 * (50 + 10 * Math.sin(game.cycle * 0.025)) - const semiMajorAxis = mech.fieldRange + 30 - const edge1a = Vector.add(Vector.mult(mech.hole.unit, semiMajorAxis), mech.hole.pos1) - const edge1b = Vector.add(Vector.mult(mech.hole.unit, -semiMajorAxis), mech.hole.pos1) - const edge2a = Vector.add(Vector.mult(mech.hole.unit, semiMajorAxis), mech.hole.pos2) - const edge2b = Vector.add(Vector.mult(mech.hole.unit, -semiMajorAxis), mech.hole.pos2) - ctx.beginPath(); - ctx.moveTo(edge1a.x, edge1a.y) - ctx.bezierCurveTo(mech.hole.pos1.x, mech.hole.pos1.y, mech.hole.pos2.x, mech.hole.pos2.y, edge2a.x, edge2a.y); - ctx.lineTo(edge2b.x, edge2b.y) - ctx.bezierCurveTo(mech.hole.pos2.x, mech.hole.pos2.y, mech.hole.pos1.x, mech.hole.pos1.y, edge1b.x, edge1b.y); - ctx.fillStyle = `rgba(255,255,255,${200 / mech.fieldRange / mech.fieldRange})` //"rgba(0,0,0,0.1)" - ctx.fill(); - ctx.beginPath(); - ctx.ellipse(mech.hole.pos1.x, mech.hole.pos1.y, mech.fieldRange, semiMajorAxis, mech.hole.angle, 0, 2 * Math.PI) - ctx.ellipse(mech.hole.pos2.x, mech.hole.pos2.y, mech.fieldRange, semiMajorAxis, mech.hole.angle, 0, 2 * Math.PI) - ctx.fillStyle = `rgba(255,255,255,${32 / mech.fieldRange})` - ctx.fill(); + if (mech.hole.isOn) { + // draw holes + mech.fieldRange = 0.97 * mech.fieldRange + 0.03 * (50 + 10 * Math.sin(game.cycle * 0.025)) + const semiMajorAxis = mech.fieldRange + 30 + const edge1a = Vector.add(Vector.mult(mech.hole.unit, semiMajorAxis), mech.hole.pos1) + const edge1b = Vector.add(Vector.mult(mech.hole.unit, -semiMajorAxis), mech.hole.pos1) + const edge2a = Vector.add(Vector.mult(mech.hole.unit, semiMajorAxis), mech.hole.pos2) + const edge2b = Vector.add(Vector.mult(mech.hole.unit, -semiMajorAxis), mech.hole.pos2) + ctx.beginPath(); + ctx.moveTo(edge1a.x, edge1a.y) + ctx.bezierCurveTo(mech.hole.pos1.x, mech.hole.pos1.y, mech.hole.pos2.x, mech.hole.pos2.y, edge2a.x, edge2a.y); + ctx.lineTo(edge2b.x, edge2b.y) + ctx.bezierCurveTo(mech.hole.pos2.x, mech.hole.pos2.y, mech.hole.pos1.x, mech.hole.pos1.y, edge1b.x, edge1b.y); + ctx.fillStyle = `rgba(255,255,255,${200 / mech.fieldRange / mech.fieldRange})` //"rgba(0,0,0,0.1)" + ctx.fill(); + ctx.beginPath(); + ctx.ellipse(mech.hole.pos1.x, mech.hole.pos1.y, mech.fieldRange, semiMajorAxis, mech.hole.angle, 0, 2 * Math.PI) + ctx.ellipse(mech.hole.pos2.x, mech.hole.pos2.y, mech.fieldRange, semiMajorAxis, mech.hole.angle, 0, 2 * Math.PI) + ctx.fillStyle = `rgba(255,255,255,${32 / mech.fieldRange})` + ctx.fill(); - //suck power ups - for (let i = 0, len = powerUp.length; i < len; ++i) { - //which hole is closer - const dxP1 = mech.hole.pos1.x - powerUp[i].position.x; - const dyP1 = mech.hole.pos1.y - powerUp[i].position.y; - const dxP2 = mech.hole.pos2.x - powerUp[i].position.x; - const dyP2 = mech.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 && !(mech.health === mech.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 * game.g; //negate gravity - Matter.Body.setVelocity(powerUp[i], { //extra friction - x: powerUp[i].velocity.x * 0.05, - y: powerUp[i].velocity.y * 0.05 - }); - if (dist2 < 1000 && !game.isChoosing) { //use power up if it is close enough - mech.fieldRange *= 0.8 - powerUps.onPickUp(powerUp[i].position); - powerUp[i].effect(); - Matter.World.remove(engine.world, powerUp[i]); - powerUp.splice(i, 1); - break; //because the array order is messed up after splice - } + //suck power ups + for (let i = 0, len = powerUp.length; i < len; ++i) { + //which hole is closer + const dxP1 = mech.hole.pos1.x - powerUp[i].position.x; + const dyP1 = mech.hole.pos1.y - powerUp[i].position.y; + const dxP2 = mech.hole.pos2.x - powerUp[i].position.x; + const dyP2 = mech.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 && !(mech.health === mech.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 * game.g; //negate gravity + Matter.Body.setVelocity(powerUp[i], { //extra friction + x: powerUp[i].velocity.x * 0.05, + y: powerUp[i].velocity.y * 0.05 + }); + if (dist2 < 1000 && !game.isChoosing) { //use power up if it is close enough + mech.fieldRange *= 0.8 + powerUps.onPickUp(powerUp[i].position); + powerUp[i].effect(); + Matter.World.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(mech.hole.pos1, body[i].position)) - const dist2 = Vector.magnitude(Vector.sub(mech.hole.pos2, body[i].position)) - if (dist1 < dist2) { - if (dist1 < suckRange) { - const pull = Vector.mult(Vector.normalise(Vector.sub(mech.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(mech.hole.pos1, body[i].position)) < shrinkRange) { - Matter.Body.scale(body[i], shrinkScale, shrinkScale); - if (body[i].mass < 0.05) { - Matter.World.remove(engine.world, body[i]); - body.splice(i, 1); - mech.fieldRange *= 0.8 - if (mod.isWormholeEnergy) mech.energy += 0.5 - if (mod.isWormSpores) { //pandimensionalspermia - for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) { - b.spore(Vector.add(mech.hole.pos2, Vector.rotate({ - x: mech.fieldRange * 0.4, - y: 0 - }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(mech.hole.unit, Math.PI / 2), -15)); - } - } - break - } - } - } - } else if (dist2 < suckRange) { - const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos2, body[i].position)), 1) + } + //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(mech.hole.pos1, body[i].position)) + const dist2 = Vector.magnitude(Vector.sub(mech.hole.pos2, body[i].position)) + if (dist1 < dist2) { + if (dist1 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(mech.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(mech.hole.pos2, body[i].position)) < shrinkRange) { + if (Vector.magnitude(Vector.sub(mech.hole.pos1, body[i].position)) < shrinkRange) { Matter.Body.scale(body[i], shrinkScale, shrinkScale); if (body[i].mass < 0.05) { Matter.World.remove(engine.world, body[i]); body.splice(i, 1); mech.fieldRange *= 0.8 - // if (mod.isWormholeEnergy && mech.energy < mech.maxEnergy * 2) mech.energy = mech.maxEnergy * 2 if (mod.isWormholeEnergy) mech.energy += 0.5 if (mod.isWormSpores) { //pandimensionalspermia for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) { - b.spore(Vector.add(mech.hole.pos1, Vector.rotate({ + b.spore(Vector.add(mech.hole.pos2, Vector.rotate({ x: mech.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(mech.hole.unit, Math.PI / 2), 15)); + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(mech.hole.unit, Math.PI / 2), -15)); } } break } } } - } - } - if (mod.isWormBullets) { - //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(mech.hole.pos1, bullet[i].position)) < mech.fieldRange) { //find if bullet is touching hole1 - Matter.Body.setPosition(bullet[i], Vector.add(mech.hole.pos2, Vector.sub(mech.hole.pos1, bullet[i].position))); - mech.fieldRange += 5 - bullet[i].isInHole = true - } else if (Vector.magnitude(Vector.sub(mech.hole.pos2, bullet[i].position)) < mech.fieldRange) { //find if bullet is touching hole1 - Matter.Body.setPosition(bullet[i], Vector.add(mech.hole.pos1, Vector.sub(mech.hole.pos2, bullet[i].position))); - mech.fieldRange += 5 - bullet[i].isInHole = true + } else if (dist2 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(mech.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(mech.hole.pos2, body[i].position)) < shrinkRange) { + Matter.Body.scale(body[i], shrinkScale, shrinkScale); + if (body[i].mass < 0.05) { + Matter.World.remove(engine.world, body[i]); + body.splice(i, 1); + mech.fieldRange *= 0.8 + // if (mod.isWormholeEnergy && mech.energy < mech.maxEnergy * 2) mech.energy = mech.maxEnergy * 2 + if (mod.isWormholeEnergy) mech.energy += 0.5 + if (mod.isWormSpores) { //pandimensionalspermia + for (let i = 0, len = Math.ceil(3 * Math.random()); i < len; i++) { + b.spore(Vector.add(mech.hole.pos1, Vector.rotate({ + x: mech.fieldRange * 0.4, + y: 0 + }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(mech.hole.unit, Math.PI / 2), 15)); + } + } + break } } } - // mobs get pushed away - for (let i = 0, len = mob.length; i < len; i++) { - if (Vector.magnitude(Vector.sub(mech.hole.pos1, mob[i].position)) < 200) { - const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos1, mob[i].position)), -0.07) - Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); - } - if (Vector.magnitude(Vector.sub(mech.hole.pos2, mob[i].position)) < 200) { - const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos2, mob[i].position)), -0.07) - Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); - } - } } } + if (mod.isWormBullets) { + //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(mech.hole.pos1, bullet[i].position)) < mech.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(mech.hole.pos2, Vector.sub(mech.hole.pos1, bullet[i].position))); + mech.fieldRange += 5 + bullet[i].isInHole = true + } else if (Vector.magnitude(Vector.sub(mech.hole.pos2, bullet[i].position)) < mech.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(mech.hole.pos1, Vector.sub(mech.hole.pos2, bullet[i].position))); + mech.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(mech.hole.pos1, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos1, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); + } + if (Vector.magnitude(Vector.sub(mech.hole.pos2, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos2, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); + } + } + } + } - if (input.field && mech.fieldCDcycle < mech.cycle) { //not hold but field button is pressed - const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(game.mouseInGame, mech.pos)), 50), game.mouseInGame) - const scale = 60 - // console.log(Matter.Query.region(map, bounds)) - if (mech.hole.isReady && - ( - Matter.Query.region(map, { - min: { - x: game.mouseInGame.x - scale, - y: game.mouseInGame.y - scale - }, - max: { - x: game.mouseInGame.x + scale, - y: game.mouseInGame.y + scale - } - }).length === 0 && - Matter.Query.ray(map, mech.pos, justPastMouse).length === 0 - // Matter.Query.ray(map, mech.pos, game.mouseInGame).length === 0 && - // Matter.Query.ray(map, player.position, game.mouseInGame).length === 0 && - // Matter.Query.ray(map, player.position, justPastMouse).length === 0 - ) - ) { - const sub = Vector.sub(game.mouseInGame, mech.pos) - const mag = Vector.magnitude(sub) - const drain = 0.03 + 0.005 * Math.sqrt(mag) - if (mech.energy > drain && mag > 300) { - mech.energy -= drain - mech.hole.isReady = false; - mech.fieldRange = 0 - Matter.Body.setPosition(player, game.mouseInGame); - mech.buttonCD_jump = 0 //this might fix a bug with jumping - const velocity = Vector.mult(Vector.normalise(sub), 18) - Matter.Body.setVelocity(player, { - x: velocity.x, - y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer - }); - mech.immuneCycle = mech.cycle + 15; //player is immune to collision damage - // move bots to follow 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 (input.field && mech.fieldCDcycle < mech.cycle) { //not hold but field button is pressed + const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(game.mouseInGame, mech.pos)), 50), game.mouseInGame) + const scale = 60 + // console.log(Matter.Query.region(map, bounds)) + if (mech.hole.isReady && + ( + Matter.Query.region(map, { + min: { + x: game.mouseInGame.x - scale, + y: game.mouseInGame.y - scale + }, + max: { + x: game.mouseInGame.x + scale, + y: game.mouseInGame.y + scale + } + }).length === 0 && + Matter.Query.ray(map, mech.pos, justPastMouse).length === 0 + // Matter.Query.ray(map, mech.pos, game.mouseInGame).length === 0 && + // Matter.Query.ray(map, player.position, game.mouseInGame).length === 0 && + // Matter.Query.ray(map, player.position, justPastMouse).length === 0 + ) + ) { + const sub = Vector.sub(game.mouseInGame, mech.pos) + const mag = Vector.magnitude(sub) + const drain = 0.03 + 0.005 * Math.sqrt(mag) + if (mech.energy > drain && mag > 300) { + mech.energy -= drain + mech.hole.isReady = false; + mech.fieldRange = 0 + Matter.Body.setPosition(player, game.mouseInGame); + mech.buttonCD_jump = 0 //this might fix a bug with jumping + const velocity = Vector.mult(Vector.normalise(sub), 18) + Matter.Body.setVelocity(player, { + x: velocity.x, + y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + }); + mech.immuneCycle = mech.cycle + 15; //player is immune to collision damage + // move bots to follow 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 + mech.hole.isOn = true; + mech.hole.pos1.x = mech.pos.x + mech.hole.pos1.y = mech.pos.y + mech.hole.pos2.x = player.position.x + mech.hole.pos2.y = player.position.y + mech.hole.angle = Math.atan2(sub.y, sub.x) + mech.hole.unit = Vector.perp(Vector.normalise(sub)) + + if (mod.isWormholeDamage) { + who = Matter.Query.ray(mob, mech.pos, game.mouseInGame, 80) + for (let i = 0; i < who.length; i++) { + if (who[i].body.alive) { + mobs.statusDoT(who[i].body, 0.6, 420) + mobs.statusStun(who[i].body, 240) } } - - //set holes - mech.hole.isOn = true; - mech.hole.pos1.x = mech.pos.x - mech.hole.pos1.y = mech.pos.y - mech.hole.pos2.x = player.position.x - mech.hole.pos2.y = player.position.y - mech.hole.angle = Math.atan2(sub.y, sub.x) - mech.hole.unit = Vector.perp(Vector.normalise(sub)) - - if (mod.isWormholeDamage) { - who = Matter.Query.ray(mob, mech.pos, game.mouseInGame, 80) - for (let i = 0; i < who.length; i++) { - if (who[i].body.alive) { - mobs.statusDoT(who[i].body, 0.6, 420) - mobs.statusStun(who[i].body, 240) - } - } - } - } else { - mech.grabPowerUp(); } } else { mech.grabPowerUp(); } - } else if (mech.holdingTarget && mech.fieldCDcycle < mech.cycle) { //holding, but field button is released - mech.pickUp(); } else { - mech.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - mech.hole.isReady = true; + mech.grabPowerUp(); } - mech.drawFieldMeter() + } else if (mech.holdingTarget && mech.fieldCDcycle < mech.cycle) { //holding, but field button is released + mech.pickUp(); + } else { + mech.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + mech.hole.isReady = true; } - } + mech.drawFieldMeter() + }, }, ], }; \ No newline at end of file diff --git a/js/spawn.js b/js/spawn.js index 801962f..349fe10 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -81,7 +81,7 @@ const spawn = { } } }, - randomLevelBoss(x, y, options = ["shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "snakeBoss"]) { + randomLevelBoss(x, y, options = ["shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "snakeBoss", "streamBoss"]) { // other bosses: suckerBoss, laserBoss, tetherBoss, //all need a particular level to work so they are not included spawn[options[Math.floor(Math.random() * options.length)]](x, y) }, @@ -101,15 +101,34 @@ const spawn = { level.bossKilled = true; level.exit.x = 5500; level.exit.y = -330; + //ramp up damage + for (let i = 0; i < 4; i++) level.difficultyIncrease(game.difficultyMode) + //pull in particles - for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally + for (let i = 0, len = body.length; i < len; ++i) { const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, body[i].position)), 65) - const pushUp = Vector.add(velocity, { x: 0, y: -0.3 }) + const pushUp = Vector.add(velocity, { x: 0, y: -0.5 }) Matter.Body.setVelocity(body[i], Vector.add(body[i].velocity, pushUp)); } - //ramp up damage - for (let i = 0; i < 5; i++) level.difficultyIncrease(game.difficultyMode) + //push away mobs + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i] !== this) { + const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, mob[i].position)), -65) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, velocity)); + } + } + + //draw stuff + for (let i = 0, len = 22; i < len; i++) { + game.drawList.push({ //add dmg to draw queue + x: this.position.x, + y: this.position.y, + radius: (i + 1) * 150, + color: `rgba(255,255,255,0.17)`, + time: 5 * (len - i + 1) + }); + } }; me.onDamage = function() {}; @@ -761,7 +780,7 @@ const spawn = { let me = mob[mob.length - 1]; me.stroke = "transparent"; //used for drawSneaker me.eventHorizon = radius * 23; //required for blackhole - me.seeAtDistance2 = (me.eventHorizon + 300) * (me.eventHorizon + 300); //vision limit is event horizon + me.seeAtDistance2 = (me.eventHorizon + 400) * (me.eventHorizon + 400); //vision limit is event horizon me.accelMag = 0.00009 * game.accelScale; me.frictionAir = 0.025; me.collisionFilter.mask = cat.player | cat.bullet @@ -775,8 +794,15 @@ const spawn = { y: this.velocity.y * 0.99 }); } - // this.seePlayerByDistOrLOS(); - this.seePlayerCheckByDistance() + // this.seePlayerCheckByDistance() + if (!(game.cycle % this.seePlayerFreq)) { + if (this.distanceToPlayer2() < this.seeAtDistance2) { //&& !mech.isCloak ignore cloak for black holes + this.locatePlayer(); + if (!this.seePlayer.yes) this.seePlayer.yes = true; + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } this.checkStatus(); if (this.seePlayer.recall) { //eventHorizon waves in and out @@ -832,7 +858,7 @@ const spawn = { me.isBoss = true; me.stroke = "transparent"; //used for drawSneaker me.eventHorizon = 1100; //required for black hole - me.seeAtDistance2 = (me.eventHorizon + 1000) * (me.eventHorizon + 1000); //vision limit is event horizon + me.seeAtDistance2 = (me.eventHorizon + 1200) * (me.eventHorizon + 1200); //vision limit is event horizon me.accelMag = 0.00003 * game.accelScale; me.collisionFilter.mask = cat.player | cat.bullet // me.frictionAir = 0.005; @@ -865,7 +891,14 @@ const spawn = { y: this.velocity.y * 0.95 }); } - this.seePlayerByDistOrLOS(); + if (!(game.cycle % this.seePlayerFreq)) { + if (this.distanceToPlayer2() < this.seeAtDistance2) { //&& !mech.isCloak ignore cloak for black holes + this.locatePlayer(); + if (!this.seePlayer.yes) this.seePlayer.yes = true; + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } this.checkStatus(); if (this.seePlayer.recall) { //accelerate towards the player @@ -1865,7 +1898,7 @@ const spawn = { me.onHit = function() { this.explode(this.mass * 20); }; - Matter.Body.setDensity(me, 0.00005); //normal is 0.001 + Matter.Body.setDensity(me, 0.00004); //normal is 0.001 me.timeLeft = 200; me.g = 0.001; //required if using 'gravity' me.frictionAir = 0; @@ -2106,7 +2139,7 @@ const spawn = { me.frictionStatic = 0; me.friction = 0; me.frictionAir = 0.02; - me.memory = 420 * game.CDScale; + me.memory = 420; me.repulsionRange = 1200000; //squared spawn.shield(me, x, y, 1); Matter.Body.setDensity(me, 0.004 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger @@ -2124,7 +2157,7 @@ const spawn = { Matter.Body.setAngularVelocity(this, 0.11) //fire a bullet from each vertex for (let i = 0, len = this.vertices.length; i < len; i++) { - spawn.seeker(this.vertices[i].x, this.vertices[i].y, 7) + spawn.seeker(this.vertices[i].x, this.vertices[i].y, 6) //give the bullet a rotational velocity as if they were attached to a vertex const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[i]))), -10) Matter.Body.setVelocity(mob[mob.length - 1], { @@ -2135,7 +2168,84 @@ const spawn = { } }; }, - seeker(x, y, radius = 5, sides = 0) { + streamBoss(x, y, radius = 110) { + mobs.spawn(x, y, 5, radius, "rgb(245,180,255)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + // me.accelMag = 0.00023 * game.accelScale; + me.accelMag = 0.00008 * game.accelScale; + // me.fireFreq = Math.floor(30 * game.CDScale) + me.canFire = false; + me.closestVertex1 = 0; + me.closestVertex2 = 1; + me.cycle = 0 + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.022; + me.memory = 240; + me.repulsionRange = 1200000; //squared + spawn.shield(me, x, y, 1); + Matter.Body.setDensity(me, 0.025); //extra dense //normal is 0.001 //makes effective life much larger + me.onDeath = function() { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed + }; + me.onDamage = function() {}; + me.do = function() { + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + this.repulsion(); + + this.cycle++ + if (this.seePlayer.recall && ((this.cycle % 15) === 0) && !mech.isBodiesAsleep) { + if (this.canFire) { + if (this.cycle > 120) { + this.cycle = 0 + this.canFire = false + // Matter.Body.setAngularVelocity(this, 0.1) + // const forceMag = 0.01 * this.mass; + // const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); + // this.force.x -= 2 * forceMag * Math.cos(angle); + // this.force.y -= 2 * forceMag * Math.sin(angle); // - 0.0007 * this.mass; //antigravity + } + spawn.seeker(this.vertices[this.closestVertex1].x, this.vertices[this.closestVertex1].y, 4) + Matter.Body.setDensity(mob[mob.length - 1], 0.000001); //normal is 0.001 + const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, this.vertices[this.closestVertex1])), -10) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + spawn.seeker(this.vertices[this.closestVertex2].x, this.vertices[this.closestVertex2].y, 4) + Matter.Body.setDensity(mob[mob.length - 1], 0.000001); //normal is 0.001 + const velocity2 = Vector.mult(Vector.normalise(Vector.sub(this.position, this.vertices[this.closestVertex2])), -10) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + velocity2.x, + y: this.velocity.y + velocity2.y + }); + } else if (this.cycle > 210) { + this.cycle = 0 + this.canFire = true + + //find closest 2 vertexes + let distance2 = Infinity + for (let i = 0; i < this.vertices.length; i++) { + const d = Vector.magnitudeSquared(Vector.sub(this.vertices[i], player.position)) + if (d < distance2) { + distance2 = d + this.closestVertex2 = this.closestVertex1 + this.closestVertex1 = i + } + } + if (this.closestVertex2 === this.closestVertex1) { + this.closestVertex2++ + if (this.closestVertex2 === this.vertices.length) this.closestVertex2 = 0 + } + } + } + }; + }, + seeker(x, y, radius = 5, sides = 6) { //bullets mobs.spawn(x, y, sides, radius, "rgb(255,0,255)"); let me = mob[mob.length - 1]; @@ -2143,10 +2253,10 @@ const spawn = { me.onHit = function() { this.explode(this.mass * 20); }; - Matter.Body.setDensity(me, 0.00002); //normal is 0.001 + Matter.Body.setDensity(me, 0.000015); //normal is 0.001 me.timeLeft = 420 * (0.8 + 0.4 * Math.random()); - me.accelMag = 0.00017 * (0.8 + 0.4 * Math.random()) * game.accelScale; - me.frictionAir = 0.01 * (0.8 + 0.4 * Math.random()); + me.accelMag = 0.00017 * game.accelScale; //* (0.8 + 0.4 * Math.random()) + me.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); me.restitution = 0.5; me.leaveBody = false; me.dropPowerUp = false; @@ -2190,6 +2300,7 @@ const spawn = { //run this function on hitting player this.explode(); }; + Matter.Body.setDensity(me, 0.0005); //normal is 0.001 me.g = 0.0001; //required if using 'gravity' me.accelMag = 0.0003 * game.accelScale; me.memory = 30; diff --git a/style.css b/style.css index 754418a..4b84895 100644 --- a/style.css +++ b/style.css @@ -587,6 +587,7 @@ em { /* background: rgb(116, 102, 238); */ /* background: hsl(253, 57%, 52%); */ background: hsl(255, 100%, 71%); + /* background: hsl(282, 100%, 64%); */ } /* .grey { diff --git a/todo.txt b/todo.txt index 8db3d91..5c119d9 100644 --- a/todo.txt +++ b/todo.txt @@ -1,22 +1,16 @@ ******************************************************** NEXT PATCH ******************************************************** -your build url can now be copied in the pause screen +new level boss that fires 2 streams of small bullets that chase you -mod: inductive coupling - 4 max health per power up, but limited to 44 max health per level (replaces crystalized armor) -mod: transceiver chip - use all the power ups left over at the end of a level -mod: catabolism - does a flat 5 damage to your health for 3 ammo (was 2% of max health for 1 ammo) +mod: add a CPT gun to your inventory that rewinds your history, reverts your health, position, velocity for 10 seconds + I expect that spamming rewind has some overpowered combos. + Let me know what you find, and your ideas on balance. + ******************************************************** BUGS ******************************************************** -give worm hole pair production? - -2nd mod for crystalized armor - no cap for health power ups? - -(not able to reproduce, might be fixed) possible bug with neutron rewind - status doesn't apply correctly for spawned neutron bombs that are stuck to a shield - also saw neutron bombs bounce off shield, for normal bullets - test this more +check for crouch after rewind + CPT, tesseract mod and mob are too similar @@ -26,13 +20,15 @@ mod and mob are too similar trigger a short term non-collide if that occurs (12+ reports) bug - crouch and worm hole? -> crouch locked in - doesn't occur on my computer? + ***try checking the date of the first bug, and then look at what patches came out right before that*** + doesn't occur on my computer? but it does occur on fast computers you can spoof it with mech.crouch = true in console players have extra gravity might be from the short jump code add in a check every 7 seconds to try and fix it this fix was added and it is working for some cases maybe move the fix to once a second? + bug fix - rewrite crouch to not translate the player height, but instead switch between 2 sensors (intermittent, but almost every time) bug - capping the fps causes random slow downs, that can be fixed with pause @@ -44,7 +40,39 @@ mod and mob are too similar ******************************************************** TODO ******************************************************** -make a reset hit box function as a way to fix crouch bug +mod: laser beams push like plasma torch pushes with directional force + +mod: worm hole - you move through time instead of space. (field click to rewind) + add support for eating blocks, and damaging mobs that are nearby when you rewind + allow those mods + mod might not work as is because player needs to be able to pick up and move blocks sometimes + what about as a gun that uses energy not bullets? + +mechanic: technological dead end - add mods to the mod pool with a dumb effect + don't show up in custom? + negative effect (one time effects are better to avoid code clutter) + make the player rainbow colors + mech.color = { + hue: 0, + sat: 100, + light: 50 + } + setInterval(function(){ + mech.color.hue++ + mech.setFillColors() + }, 10); + remove all your energy + eject all your rerolls (not bad with dup) + teleport to the start of the level + remove your bots (requires you to have some bots) + your bots are changed to random bots + +Mod: "High Risk": Spawn two bosses per level. + maybe limit to just the power up boss and spawn it at the exit every time to keep it simple + also weaken the player + remove a mod up? + lower harm reduction? + increase game difficulty by one level mod that requires integrated armament @@ -75,15 +103,6 @@ make different move methods mod: when mobs are at full health you do 40% to them -mechanic: failed technology - add mods to the mod pool with a dumb effect - don't show up in custom? - negative effect (one time effects are better to avoid code clutter) - remove all your energy - eject all your rerolls (not bad with dup) - teleport to the start of the level - remove your bots (requires you to have some bots) - your bots are changed to random bots - mod - move super fast, go intangible, drain energy very fast this is like a dodge roll mod for standing wave?, cloaking? @@ -371,12 +390,16 @@ robot AI communication output to bottom left message tab title? - style + style + output console.log? make it look like a computer terminal + track multiple lines, like your vocoder program + messages about heal, ammo, mods, that just list internal computer code + example: a heal would be mech.health += 12 mono space font square edges - in baby talk? - with random ASCII gibberish letters + black text on bottom right with no background? + or white text, or yellow end each message with a hexadecimal encryption code/hash message after selecting each new (mod / gun / field) put messages in (mod / gun / field) method @@ -385,6 +408,14 @@ robot AI communication you'd have to store an array of guns/fields/mod used last game n-gon escape simulation ${random seed} say something about what mobs types are queued up, and level order + **all communication should be from scientists watching the simulation; the robot shouldn't talk** + talking about the robot, watching + trying to communicate with the robot? but how + lines: + I think it's planing to escape + Why is it attacking those shapes? + Are those shapes supposed to be us? + add an ending to the game maybe the game ending should ask you to open the console and type in some commands like in the end of doki doki