diff --git a/js/bullet.js b/js/bullet.js index 1656e40..a726528 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -165,12 +165,13 @@ const b = { } }, explosion(where, radius) { // typically explode is used for some bullets with .onEnd + radius *= mod.explosiveRadius let dist, sub, knock; let dmg = radius * 0.013; - if (mod.isExplosionHarm) radius *= 1.43 // sqrt(2)radius for 2x more area + if (mod.isExplosionHarm) radius *= 1.8 // 1/sqrt(2) radius -> area if (mod.isSmallExplosion) { - radius *= 0.5 - dmg *= 1.5 + radius *= 0.8 + dmg *= 1.6 } game.drawList.push({ //add dmg to draw queue @@ -197,7 +198,7 @@ const b = { if (dist < radius) { if (mod.isImmuneExplosion) { - const mitigate = Math.min(1, Math.max(1 - mech.energy * 0.6, 0)) + const mitigate = Math.min(1, Math.max(1 - mech.energy * 0.7, 0)) mech.damage(mitigate * radius * (mod.isExplosionHarm ? 0.0004 : 0.0001)); } else { mech.damage(radius * (mod.isExplosionHarm ? 0.0004 : 0.0001)); @@ -800,7 +801,6 @@ const b = { lockedOn: null, isFollowMouse: true, onDmg(who) { - console.log(who.alive) mobs.statusSlow(who, 60) this.endCycle = game.cycle if (mod.isHeavyWater) mobs.statusDoT(who, 0.15, 300) diff --git a/js/engine.js b/js/engine.js index 7e32677..444929e 100644 --- a/js/engine.js +++ b/js/engine.js @@ -85,16 +85,6 @@ function collisionChecks(event) { // if (obj.onWallHit) obj.onWallHit(); // } - - //body + player collision - // if (game.isBodyDamage) { - // if (pairs[i].bodyA === playerBody || pairs[i].bodyA === playerHead) { - // collidePlayer(pairs[i].bodyB) - // } else if (pairs[i].bodyB === playerBody || pairs[i].bodyB === playerHead) { - // collidePlayer(pairs[i].bodyA) - // } - // } - // function collidePlayer(obj) { // //player dmg from hitting a body // if (obj.classType === "body" && obj.speed > 10 && mech.immuneCycle < mech.cycle) { @@ -145,22 +135,8 @@ function collisionChecks(event) { let dmg = Math.min(Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05), 0.3) * game.dmgScale; //player damage is capped at 0.3*dmgScale of 1.0 if (mod.isPiezo) mech.energy = mech.maxEnergy; mech.damage(dmg); - if (mod.isBayesian) { - const have = [] //find which mods you have - for (let i = 0; i < mod.mods.length; i++) { - if (mod.mods[i].count > 0) have.push(i) - } - const choose = have[Math.floor(Math.random() * have.length)] - game.makeTextLog(`
  ${mod.mods[choose].name} ejected by Bayesian statistics`, 600) //message about what mod was lost - for (let i = 0; i < mod.mods[choose].count; i++) powerUps.spawn(mech.pos.x, mech.pos.y, "mod"); - mod.mods[choose].remove(); // remove a random mod form the list of mods you have - mod.mods[choose].count = 0; - mod.mods[choose].isLost = true; - game.updateModHUD(); - mech.fieldCDcycle = mech.cycle + 30; //disable field so you can't pick up the ejected mod - } + if (mod.isBayesian) powerUps.ejectMod() if (mob[k].onHit) mob[k].onHit(k); - //extra kick between player and mob //this section would be better with forces but they don't work... let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x); Matter.Body.setVelocity(player, { diff --git a/js/game.js b/js/game.js index 4096d25..e8d8e6f 100644 --- a/js/game.js +++ b/js/game.js @@ -126,7 +126,6 @@ const game = { lastTimeStamp: 0, //tracks time stamps for measuring delta delta: 1000 / 60, //speed of game engine //looks like it has to be 16 to match player input buttonCD: 0, - isBodyDamage: true, levelsCleared: 0, difficultyMode: 1, isEasyMode: false, @@ -472,6 +471,7 @@ const game = { mech.maxHealth = 1 mech.maxEnergy = 1 mech.energy = 1 + mech.hole.isOn = false game.paused = false; engine.timing.timeScale = 1; game.fpsCap = game.fpsCapDefault; @@ -559,7 +559,7 @@ const game = { if (game.isCommunityMaps) { level.levels.push("stronghold"); level.levels.push("basement"); - level.levels.push("newLevel"); + level.levels.push("detours"); level.levels.push("house"); } level.levels = shuffle(level.levels); //shuffles order of maps @@ -607,6 +607,7 @@ const game = { mech.fireCDcycle = 0 mech.drop(); + mech.hole.isOn = false; level.fill = []; level.fillBG = []; level.zones = []; diff --git a/js/index.js b/js/index.js index e900573..a80d1c6 100644 --- a/js/index.js +++ b/js/index.js @@ -871,7 +871,13 @@ document.body.addEventListener("wheel", (e) => { //********************************************************************** let localSettings = JSON.parse(localStorage.getItem("localSettings")); if (localSettings) { - input.key = localSettings.key + if (localSettings.key) { + input.key = localSettings.key + } else { + input.setDefault() + } + + game.isCommunityMaps = localSettings.isCommunityMaps document.getElementById("community-maps").checked = localSettings.isCommunityMaps game.difficultyMode = localSettings.difficultyMode @@ -883,15 +889,15 @@ if (localSettings) { } document.getElementById("fps-select").value = localSettings.fpsCapDefault } else { - input.setDefault() localSettings = { isCommunityMaps: false, difficultyMode: '1', fpsCapDefault: 'max', runCount: 0, levelsClearedLastGame: 0, - key: input.key + key: undefined }; + input.setDefault() localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage document.getElementById("community-maps").checked = localSettings.isCommunityMaps game.isCommunityMaps = localSettings.isCommunityMaps diff --git a/js/level.js b/js/level.js index 8ea014a..c4b3116 100644 --- a/js/level.js +++ b/js/level.js @@ -15,7 +15,7 @@ const level = { // game.zoomScale = 1000; // game.setZoom(); // mech.isCloak = true; - // mech.setField("perfect diamagnetism") + mech.setField("wormhole") // b.giveGuns("nail gun") // for (let i = 0; i < 10; i++) { // mod.giveMod("laser-bot"); @@ -37,7 +37,7 @@ const level = { // level.office(); // level.bosses(); //only fighting, very simple map // level.house() //fan level - // level.newLevel() //fan level + // level.detours() //fan level // level.basement(); //fan level // level.stronghold() //fan level } else { @@ -141,7 +141,7 @@ const level = { spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump // spawn.boost(1500, 0, 900); - spawn.starter(1600, -500) + // spawn.starter(1600, -500) // spawn.bomberBoss(2900, -500) // spawn.launcherBoss(1200, -500) // spawn.laserTargetingBoss(1600, -400) @@ -151,7 +151,7 @@ const level = { // spawn.sniper(1800, -120) // spawn.sniper(2200, -120) // spawn.cellBossCulture(1600, -500) - // spawn.powerUpBoss(1600, -500) + spawn.powerUpBoss(1600, -500) // spawn.shield(mob[mob.length - 1], 1200, -500, 1); // spawn.nodeBoss(1200, -500, "launcher") @@ -2821,7 +2821,7 @@ const level = { powerUps.spawn(3010, 1630, "mod"); powerUps.spawn(3100, 1630, "heal"); }, - newLevel() { + detours() { level.setPosToSpawn(0, 0); //lower start level.exit.y = 150; spawn.mapRect(level.enter.x, 45, 100, 20); @@ -4365,33 +4365,33 @@ const level = { } } } - if (body.length) { - for (let i = 0, len = body.length; i < len; i++) { - if (body[i] !== mech.holdingTarget) { - // body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100 - if (Matter.Query.collides(this, [body[i]]).length === 0) { - if (body[i].isInPortal === this) body[i].isInPortal = null - } else if (body[i].isInPortal !== this) { - body[i].isInPortal = this.portalPair - //teleport - if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down - Matter.Body.setPosition(body[i], this.portalPair.portal.position); - } else { //if at some odd angle - Matter.Body.setPosition(body[i], this.portalPair.position); - } - //rotate velocity - let mag - if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up - mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11 - } else { - mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity))) - } - let v = Vector.mult(this.portalPair.unit, mag) - Matter.Body.setVelocity(body[i], v); + // if (body.length) { + for (let i = 0, len = body.length; i < len; i++) { + if (body[i] !== mech.holdingTarget) { + // body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100 + if (Matter.Query.collides(this, [body[i]]).length === 0) { + if (body[i].isInPortal === this) body[i].isInPortal = null + } else if (body[i].isInPortal !== this) { + body[i].isInPortal = this.portalPair + //teleport + if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down + Matter.Body.setPosition(body[i], this.portalPair.portal.position); + } else { //if at some odd angle + Matter.Body.setPosition(body[i], this.portalPair.position); } + //rotate velocity + let mag + if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up + mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11 + } else { + mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity))) + } + let v = Vector.mult(this.portalPair.unit, mag) + Matter.Body.setVelocity(body[i], v); } } } + // } //remove block if touching // if (body.length) { diff --git a/js/mods.js b/js/mods.js index 352f4d9..58edd67 100644 --- a/js/mods.js +++ b/js/mods.js @@ -403,8 +403,24 @@ const mod = { } }, { - name: "trinitrotoluene", - description: "increase explosive damage by 50%
decrease explosive area by 71%", + name: "ammonium nitrate", + description: "increase explosive damage by 20%
increase explosive radius by 20%", + maxCount: 9, + count: 0, + allowed() { + return mod.haveGunCheck("missiles") || mod.haveGunCheck("flak") || mod.haveGunCheck("grenades") || mod.haveGunCheck("vacuum bomb") || mod.haveGunCheck("pulse") || mod.isMissileField || mod.boomBotCount > 1; + }, + requires: "an explosive damage source", + effect: () => { + mod.explosiveRadius += 0.2; + }, + remove() { + mod.explosiveRadius = 1; + } + }, + { + name: "nitroglycerin", + description: "increase explosive damage by 60%
decrease explosive radius by 20%", maxCount: 1, count: 0, allowed() { @@ -419,8 +435,8 @@ const mod = { } }, { - name: "ammonium nitrate", - description: "increase explosive area by 100%, but
you take 400% more harm from explosions", + name: "acetone peroxide", + description: "increase explosive radius by 80%, but
you take 400% more harm from explosions", maxCount: 1, count: 0, allowed() { @@ -437,7 +453,7 @@ const mod = { { name: "electric reactive armor", // description: "explosions do no harm
while your energy is above 98%", - description: "harm from explosions is passively reduced
by 6% for every 10 stored energy", + description: "harm from explosions is passively reduced
by 7% for every 10 stored energy", maxCount: 1, count: 0, allowed() { @@ -1192,7 +1208,7 @@ const mod = { }, { name: "Bayesian statistics", - description: "17% chance to duplicate spawned power ups
after a collision, eject one of your mods", + description: "20% chance to duplicate spawned power ups
after a collision, eject one of your mods", maxCount: 1, count: 0, allowed() { @@ -1201,13 +1217,13 @@ const mod = { requires: "", effect: () => { mod.isBayesian = true - mod.duplicateChance += 0.17 + mod.duplicateChance += 0.2 game.draw.powerUp = game.draw.powerUpBonus //change power up draw }, remove() { if (mod.isBayesian) { - mod.duplicateChance -= 0.17 + mod.duplicateChance -= 0.2 if (mod.duplicateChance < 0) mod.duplicateChance = 0 } mod.isBayesian = false @@ -1226,12 +1242,12 @@ const mod = { effect() { mod.duplicateChance += 0.08 game.draw.powerUp = game.draw.powerUpBonus //change power up draw - this.description = `8% chance to duplicate spawned power ups
chance to duplicate = ${mod.duplicateChance}` + // this.description = `8% chance to duplicate spawned power ups
chance to duplicate = ${mod.duplicateChance}` }, remove() { mod.duplicateChance -= 0.08 * this.count if (mod.duplicateChance === 0) game.draw.powerUp = game.draw.powerUpNormal - this.description = `8% chance to duplicate spawned power ups
chance to duplicate = ${mod.duplicateChance}` + // this.description = `8% chance to duplicate spawned power ups
chance to duplicate = ${mod.duplicateChance}` } }, { @@ -1604,7 +1620,7 @@ const mod = { allowed() { return mod.haveGunCheck("nail gun") && mod.nailFireRate && !mod.isIceCrystals }, - requires: "nail gun", + requires: "nail gun and pneumatic actuator", effect() { mod.nailInstantFireRate = true }, @@ -3023,5 +3039,6 @@ const mod = { duplicateChance: null, beamSplitter: null, iceEnergy: null, - isPerfectBrake: null + isPerfectBrake: null, + explosiveRadius: null } \ No newline at end of file diff --git a/js/player.js b/js/player.js index cc13bee..38f9ec4 100644 --- a/js/player.js +++ b/js/player.js @@ -107,7 +107,6 @@ const mech = { Sy: 0, //adds a smoothing effect to vertical only Vx: 0, Vy: 0, - friction: { ground: 0.01, air: 0.0025 @@ -222,19 +221,18 @@ const mech = { }, buttonCD_jump: 0, //cool down for player buttons groundControl() { + //check for crouch or jump if (mech.crouch) { if (!(input.down) && mech.checkHeadClear() && mech.hardLandCD < mech.cycle) mech.undoCrouch(); } else if (input.down || mech.hardLandCD > mech.cycle) { mech.doCrouch(); //on ground && not crouched and pressing s or down } else if ((input.up) && mech.buttonCD_jump + 20 < mech.cycle && mech.yOffWhen.stand > 23) { mech.buttonCD_jump = mech.cycle; //can't jump again until 20 cycles pass - //apply a fraction of the jump force to the body the player is jumping off of Matter.Body.applyForce(mech.standingOn, mech.pos, { x: 0, y: mech.jumpForce * 0.12 * Math.min(mech.standingOn.mass, 5) }); - player.force.y = -mech.jumpForce; //player jump force Matter.Body.setVelocity(player, { //zero player y-velocity for consistent jumps x: player.velocity.x, @@ -243,9 +241,6 @@ const mech = { } if (input.left) { - // if (game.mouseDownRight && mech.fieldCDcycle < mech.cycle && !mech.crouch) { - // blink(-1) - // } else { if (player.velocity.x > -2) { player.force.x -= mech.Fx * 1.5 } else { @@ -253,15 +248,11 @@ const mech = { } // } } else if (input.right) { - // if (game.mouseDownRight && mech.fieldCDcycle < mech.cycle && !mech.crouch) { - // blink(1) - // } else { if (player.velocity.x < 2) { player.force.x += mech.Fx * 1.5 } else { player.force.x += mech.Fx } - // } } else { const stoppingFriction = 0.92; Matter.Body.setVelocity(player, { @@ -728,6 +719,18 @@ const mech = { fieldFire: false, fieldHarmReduction: 1, holdingMassScale: 0, + hole: { + isOn: false, + isReady: true, + pos1: { + x: 0, + y: 0 + }, + pos2: { + x: 0, + y: 0 + }, + }, fieldArc: 0, fieldThreshold: 0, calculateFieldThreshold() { @@ -739,7 +742,6 @@ const mech = { mech.fieldMeterColor = "#0cf" mech.fieldShieldingScale = 1; mech.fieldBlockCD = 10; - game.isBodyDamage = true; mech.fieldHarmReduction = 1; mech.fieldDamage = 1 mech.grabPowerUpRange2 = 156000; @@ -757,6 +759,18 @@ const mech = { mech.isBodiesAsleep = true; mech.wakeCheck(); mech.setMaxEnergy(); + mech.hole = { + isOn: false, + isReady: true, + pos1: { + x: 0, + y: 0 + }, + pos2: { + x: 0, + y: 0 + }, + } }, setMaxEnergy() { mech.maxEnergy = 1 + mod.bonusEnergy + mod.healMaxEnergyBonus @@ -996,8 +1010,8 @@ const mech = { if (dist2 < 5000 && !game.isChoosing) { //use power up if it is close enough powerUps.onPickUp(mech.pos); Matter.Body.setVelocity(player, { //player knock back, after grabbing power up - x: player.velocity.x + ((powerUp[i].velocity.x * powerUp[i].mass) / player.mass) * 0.3, - y: player.velocity.y + ((powerUp[i].velocity.y * powerUp[i].mass) / player.mass) * 0.3 + x: player.velocity.x + powerUp[i].velocity.x / player.mass * 5, + y: player.velocity.y + powerUp[i].velocity.y / player.mass * 5 }); powerUp[i].effect(); Matter.World.remove(engine.world, powerUp[i]); @@ -1544,7 +1558,7 @@ const mech = { }, { name: "plasma torch", - description: "use energy to emit short range plasma
damages mobs
pushes mobs and blocks away", + description: "use energy to emit short range plasma
damages and pushes mobs away", effect() { mech.fieldMeterColor = "#f0f" mech.hold = function () { @@ -2069,7 +2083,6 @@ const mech = { description: "use energy to push blocks with your mouse
field radius decreases out of line of sight", effect: () => { game.replaceTextLog = true; //allow text over write - game.isBodyDamage = false; mech.fieldPhase = 0; mech.fieldPosition = { x: game.mouseInGame.x, @@ -2225,5 +2238,220 @@ const mech = { } } }, + { + name: "wormhole", + description: "use energy to tunnel through a wormhole
bullets may also traverse the wormholes
blocks and power ups can't exit it", + effect: () => { + game.replaceTextLog = true; //allow text over write + mech.drop(); + // mech.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 + // }, + // } + 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)) + + //draw bezier curves between the portals + const semiMajorAxis = mech.fieldRange + 30 + const sub = Vector.sub(mech.hole.pos1, mech.hole.pos2) + const unit = Vector.perp(Vector.normalise(sub)) + const edge1a = Vector.add(Vector.mult(unit, semiMajorAxis), mech.hole.pos1) + const edge1b = Vector.add(Vector.mult(unit, -semiMajorAxis), mech.hole.pos1) + const edge2a = Vector.add(Vector.mult(unit, semiMajorAxis), mech.hole.pos2) + const edge2b = Vector.add(Vector.mult(unit, -semiMajorAxis), mech.hole.pos2) + const opacity = 200 / mech.fieldRange / mech.fieldRange + 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(); + const angle = Math.atan2(sub.y, sub.x) + ctx.beginPath(); + ctx.ellipse(mech.hole.pos1.x, mech.hole.pos1.y, mech.fieldRange, semiMajorAxis, angle, 0, 2 * Math.PI) + ctx.ellipse(mech.hole.pos2.x, mech.hole.pos2.y, mech.fieldRange, semiMajorAxis, 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 + } + } + } + //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 + } + } + } + //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 + break + } + } + } + } 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 + break + } + } + } + } + } + //mobs get sucked in + for (let i = 0, len = mob.length; i < len; i++) { + if (!mob[i].shield && !mob[i].isShielded) { + if (Vector.magnitude(Vector.sub(mech.hole.pos1, mob[i].position)) < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos1, mob[i].position)), 0.05) + const slow = Vector.mult(mob[i].velocity, 0.99) + Matter.Body.setVelocity(mob[i], Vector.add(slow, pull)); + } + if (Vector.magnitude(Vector.sub(mech.hole.pos2, mob[i].position)) < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(mech.hole.pos2, mob[i].position)), 0.05) + const slow = Vector.mult(mob[i].velocity, 0.99) + Matter.Body.setVelocity(mob[i], Vector.add(slow, pull)); + } + } + } + } + + if (mech.isHolding) { + mech.drawHold(mech.holdingTarget); + mech.holding(); + mech.throwBlock(); + } else if ((input.field && mech.fieldCDcycle < mech.cycle)) { //not hold but field button is pressed + // Matter.Query.ray(map, jumpSensor.position, game.mouseInGame).length === 0 || + if ( + mech.hole.isReady && !mech.holdingTarget && + (Matter.Query.ray(map, player.position, game.mouseInGame).length === 0 && Matter.Query.ray(map, mech.pos, game.mouseInGame).length === 0) + ) { + const sub = Vector.sub(game.mouseInGame, mech.pos) + const mag = Vector.magnitude(sub) + const drain = 0.005 * Math.sqrt(mag) + if (mech.energy > drain && mag > 150) { + mech.energy -= drain + mech.hole.isReady = false; + mech.fieldRange = 0 + Matter.Body.setPosition(player, game.mouseInGame); + 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 for 30 cycles + // 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.grabPowerUp(); + // mech.lookForPickUp(); can't pick things up with this field + // if (mech.energy > 0.05) { //can't use shield + // mech.drawField(); + // mech.pushMobsFacing(); + // } + } 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/powerup.js b/js/powerup.js index e809722..c0afd8f 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -541,6 +541,27 @@ const powerUps = { powerUps.spawnRandomPowerUp(x, y); } }, + ejectMod() { + //find which mods you have + const have = [] + for (let i = 0; i < mod.mods.length; i++) { + if (mod.mods[i].count > 0) have.push(i) + } + if (have.length) { + const choose = have[Math.floor(Math.random() * have.length)] + game.makeTextLog(`
  ${mod.mods[choose].name} ejected by Bayesian statistics`, 600) //message about what mod was lost + for (let i = 0; i < mod.mods[choose].count; i++) { + powerUps.directSpawn(mech.pos.x, mech.pos.y, "mod"); + powerUp[powerUp.length - 1].isBonus = true + } + // remove a random mod from the list of mods you have + mod.mods[choose].remove(); + mod.mods[choose].count = 0; + mod.mods[choose].isLost = true; + game.updateModHUD(); + mech.fieldCDcycle = mech.cycle + 30; //disable field so you can't pick up the ejected mod + } + }, directSpawn(x, y, target, moving = true, mode = null, size = powerUps[target].size()) { let index = powerUp.length; target = powerUps[target]; diff --git a/js/spawn.js b/js/spawn.js index c384444..ce3804c 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -231,13 +231,13 @@ const spawn = { mobs.spawn(x, y, vertices, radius, "transparent"); let me = mob[mob.length - 1]; me.isBoss = true; - me.frictionAir = 0.025 + me.frictionAir = 0.01 me.seeAtDistance2 = 9000000; - me.accelMag = 0.0005 * game.accelScale; - Matter.Body.setDensity(me, 0.002); //normal is 0.001 + me.accelMag = 0.00062 * game.accelScale; + Matter.Body.setDensity(me, 0.001); //normal is 0.001 me.collisionFilter.mask = cat.bullet | cat.player me.memory = Infinity; - me.seePlayerFreq = 85 + Math.floor(10 * Math.random()) + me.seePlayerFreq = 60 me.lockedOn = null; if (vertices === 9) { @@ -248,7 +248,11 @@ const spawn = { } else if (!mech.isCloak) { me.foundPlayer(); } - + me.onHit = function () { //run this function on hitting player + powerUps.ejectMod() + powerUps.spawn(mech.pos.x, mech.pos.y, "heal"); + powerUps.spawn(mech.pos.x, mech.pos.y, "heal"); + }; me.onDeath = function () { this.leaveBody = false; this.dropPowerUp = false; @@ -276,6 +280,55 @@ const spawn = { this.checkStatus(); }; }, + // powerUpBoss(x, y, vertices = 9, radius = 130) { + // mobs.spawn(x, y, vertices, radius, "transparent"); + // let me = mob[mob.length - 1]; + // me.isBoss = true; + // me.frictionAir = 0.025 + // me.seeAtDistance2 = 9000000; + // me.accelMag = 0.0005 * game.accelScale; + // Matter.Body.setDensity(me, 0.002); //normal is 0.001 + // me.collisionFilter.mask = cat.bullet | cat.player + // me.memory = Infinity; + // me.seePlayerFreq = 85 + Math.floor(10 * Math.random()) + + // me.lockedOn = null; + // if (vertices === 9) { + // //on primary spawn + // powerUps.spawnBossPowerUp(me.position.x, me.position.y) + // powerUps.spawn(me.position.x, me.position.y, "heal"); + // powerUps.spawn(me.position.x, me.position.y, "ammo"); + // } else if (!mech.isCloak) { + // me.foundPlayer(); + // } + + // me.onDeath = function () { + // this.leaveBody = false; + // this.dropPowerUp = false; + + // if (vertices > 3) spawn.powerUpBoss(this.position.x, this.position.y, vertices - 1) + // for (let i = 0; i < powerUp.length; i++) { + // powerUp[i].collisionFilter.mask = cat.map | cat.powerUp + // } + // }; + // me.do = function () { + // this.stroke = `hsl(0,0%,${80+25*Math.sin(game.cycle*0.01)}%)` + + // //steal all power ups + // for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { + // powerUp[i].collisionFilter.mask = 0 + // Matter.Body.setPosition(powerUp[i], this.vertices[i]) + // Matter.Body.setVelocity(powerUp[i], { + // x: 0, + // y: 0 + // }) + // } + + // this.seePlayerCheckByDistance(); + // this.attraction(); + // this.checkStatus(); + // }; + // }, // healer(x, y, radius = 20) { // mobs.spawn(x, y, 3, radius, "rgba(50,255,200,0.4)"); // let me = mob[mob.length - 1]; @@ -1654,7 +1707,7 @@ const spawn = { this.explode(this.mass * 10); }; me.onDeath = function () { - if (game.difficulty > 7) { + if (game.difficulty > 4) { spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); diff --git a/style.css b/style.css index 4ff1674..86bf1b2 100644 --- a/style.css +++ b/style.css @@ -488,9 +488,7 @@ em { } .color-plasma { - /* opacity: 0.5; */ color: #c0e; - /* color: #000; */ letter-spacing: 1px; background-color: rgba(132, 0, 255, 0.04); padding: 2px; @@ -498,6 +496,12 @@ em { letter-spacing: 1px; } +.color-worm { + color: #fff; + text-shadow: 0px 0px 3px #357; + letter-spacing: 1px; +} + .color-harm { /* color: */ /* text-shadow: #FC0 1px 0 10px; */ diff --git a/todo.txt b/todo.txt index 3cb94ef..665eecc 100644 --- a/todo.txt +++ b/todo.txt @@ -1,32 +1,48 @@ +mods ejected from Bayesian statistics can't duplicate -ice IX thermoelectric energy 66% -> 100%, heal 3%->4% +the power up boss moves faster, has less health, + it will eject one of your mods after a collision + and two health power ups -new key press detection system - I rewrote some fundamental systems so there may be some bugs - testing mode death is now shift X (was x+z) +mod ammonium nitrate: increase explosion damage and area by 25% + also other explosion mods have been rebalanced (damage buffed, but self damage is also higher) -you can now change your keys with the controls settings - this is probably buggy too +field: wormhole - teleport around, bullets teleport too, blocks and power ups get sucked in + mobs don't do much in worm hole yet, but that is coming with future mods ************** TODO - n-gon ************** -laser portal field - teleport to blocks and map elements in line of sight (like the how the laser works) - do damage to mobs in the path of your teleportation - go immune to damage for 1 second after teleportation - after jumping into the portal you can send things back to your old location, like bullets and blocks, and mobs +add an ending to the game + revamp the boss level, or add a new final level + final level requires you to kill something, not skip content + around level 15 + game never ends if you have used cheats -add testing key for spawn boss mob +field wormhole + mobs are protected from bullets when stuck in a hole + mobs should destabilize holes, or take damage + store constant info about the holes: unit, angle to save processing + maybe bullets should be able to enter and exit multiple times + or bullets shouldn't get stuck at all? + maybe give bullets an attraction to holes + player: drain energy when near a hole, does damage if no energy + mod: Hawking radiation: do damage, to mobs that get near the end points + this is good because it explains why mobs don't teleport + mod: cosmic string: do damage, like a laser for any mob that passes between the two portals, near the bezier curves + mod: extend immunity cycle after a teleport //mech.immuneCycle = mech.cycle + 15; + mod: reduce energy cost of teleportation look for mods that could update description text with count and mod.is information + can only use variables that change in effect() and remove() this.description = `8% chance to duplicate spawned power ups
chance to duplicate = ${mod.duplicateChance}` -give the power up boss the ability to eject your mobs if it hits you +mod (drones or spores) explode after 10 seconds mouse event e.which is deprecated -time dilation mod rework brain storm +time dilation mod rework (time dilation is cool, but it can feel like a chore) + redistribute effects take no damage can fire 2x move jump fire while field is active @@ -37,12 +53,6 @@ vacuum bomb applies status effect to mobs that makes blocks attracted to them mod: take less harm if you are moving fast require squirrel cage rotor -getting stuck above a mob can immobilize player - just allow player to jump on mobs again? - occurs with Pauli exclusion, and time dilation field immunity - mod time-like world line - add a knock to player mob collisions even while player is immune to damage - keep the knock very small - bug - mine spawned one new mine every second after sticking to the top right corner of a wall notes: had only gun mine, mod mine reclamation, field plasma,