diff --git a/js/bullets.js b/js/bullets.js index 7ae9c60..9a038d6 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -79,6 +79,7 @@ const b = { isModNailPoison: null, isModEnergyHealth: null, isModPulseStun: null, + isModPilotFreeze: null, modOnHealthChange() { //used with acid mod if (b.isModAcidDmg && mech.health > 0.8) { b.modAcidDmg = 0.5 @@ -211,22 +212,6 @@ const b = { b.isModHarmDamage = false; } }, - { - name: "thermal runaway", - description: "mobs explode when they die
be careful", - maxCount: 1, - count: 0, - allowed() { - return true - }, - requires: "", - effect: () => { - b.isModExplodeMob = true; - }, - remove() { - b.isModExplodeMob = false; - } - }, { name: "auto-loading heuristics", description: "your delay after firing is +14% shorter", @@ -386,6 +371,22 @@ const b = { b.modSporesOnDeath = 0; } }, + { + name: "thermal runaway", + description: "mobs explode when they die
be careful", + maxCount: 1, + count: 0, + allowed() { + return true + }, + requires: "", + effect: () => { + b.isModExplodeMob = true; + }, + remove() { + b.isModExplodeMob = false; + } + }, { name: "reaction inhibitor", description: "mobs die if their life goes below 12%", @@ -504,7 +505,7 @@ const b = { } }, { - name: "weak anthropic principle", + name: "anthropic principle", description: "fatal harm can't happen
saves you up to once every 3 seconds", maxCount: 1, count: 0, @@ -749,7 +750,7 @@ const b = { }, { name: "leveraged investment", - description: "remove all future ammo power ups
spawn 6 mods and 3 healing power ups", + description: "remove all future ammo power ups
spawn 5 mods and 3 healing power ups", maxCount: 1, count: 0, allowed() { @@ -758,7 +759,7 @@ const b = { requires: "", effect: () => { b.isModNoAmmo = true; - for (let i = 0; i < 6; i++) { //if you change the six also change it in Born rule + for (let i = 0; i < 5; i++) { //if you change the six also change it in Born rule powerUps.spawn(mech.pos.x, mech.pos.y, "mod"); if (Math.random() < b.isModBayesian) powerUps.spawn(mech.pos.x, mech.pos.y, "mod"); } @@ -814,7 +815,7 @@ const b = { bullet = []; let count = b.modCount - if (b.isModNoAmmo) count - 6 //remove the 6 bonus mods when getting rid of leveraged investment + if (b.isModNoAmmo) count - 5 //remove the 5 bonus mods when getting rid of leveraged investment for (let i = 0; i < count; i++) { // spawn new mods powerUps.spawn(mech.pos.x, mech.pos.y, "mod"); } @@ -1496,6 +1497,22 @@ const b = { b.superposition = false; } }, + { + name: "Bose Einstein condensate", + description: "mobs in superposition with the pilot wave
are frozen for 2 second", + maxCount: 1, + count: 0, + allowed() { + return mech.fieldUpgrades[mech.fieldMode].name === "pilot wave" + }, + requires: "pilot wave", + effect() { + b.isModPilotFreeze = true + }, + remove() { + b.isModPilotFreeze = false + } + }, ], removeMod(index) { b.mods[index].remove(); @@ -1533,9 +1550,15 @@ const b = { } } else { if (isNaN(index)) { //find index by name + let found = false; for (let i = 0; i < b.mods.length; i++) { - if (index === b.mods[i].name) index = i + if (index === b.mods[i].name) { + index = i; + found = true; + break; + } } + if (!found) return //if name not found don't give any mod } b.mods[index].effect(); //give specific mod @@ -1818,7 +1841,7 @@ const b = { bullet[me].onEnd = function () { b.explosion(this.position, this.explodeRad * size); //makes bullet do explosive damage at end for (let i = 0; i < spawn; i++) { - b.missile(this.position, 2 * Math.PI * Math.random(), 0, 0.75) + b.missile(this.position, 2 * Math.PI * Math.random(), 0, 0.65) } } bullet[me].onDmg = function () { @@ -2124,7 +2147,7 @@ const b = { friction: 0, frictionAir: 0.10, restitution: 0.3, - dmg: 0.17, //damage done in addition to the damage from momentum + dmg: 0.16, //damage done in addition to the damage from momentum lookFrequency: 10 + Math.floor(7 * Math.random()), endCycle: game.cycle + 120 * b.isModBulletsLastLonger, //Math.floor((1200 + 420 * Math.random()) * b.isModBulletsLastLonger), classType: "bullet", @@ -2195,7 +2218,7 @@ const b = { frictionAir: 0.0005, restitution: 1, dmg: 0.17, //damage done in addition to the damage from momentum - lookFrequency: 83 + Math.floor(41 * Math.random()), + lookFrequency: 107 + Math.floor(47 * Math.random()), endCycle: game.cycle + Math.floor((1200 + 420 * Math.random()) * b.isModBulletsLastLonger), classType: "bullet", collisionFilter: { @@ -2205,18 +2228,21 @@ const b = { minDmgSpeed: 0, lockedOn: null, isFollowMouse: true, + deathCycles: 110 + RADIUS * 5, onDmg() { this.lockedOn = null - if (this.endCycle > game.cycle + 180 && b.isModDroneCollide) { + if (this.endCycle > game.cycle + this.deathCycles && b.isModDroneCollide) { this.endCycle -= 60 - if (game.cycle + 180 > this.endCycle) this.endCycle = game.cycle + 180 + if (game.cycle + this.deathCycles > this.endCycle) this.endCycle = game.cycle + this.deathCycles } }, onEnd() {}, do() { - if (game.cycle + 180 > this.endCycle) { //fall and die + if (game.cycle + this.deathCycles > this.endCycle) { //fall shrink and die this.force.y += this.mass * 0.0012; this.restitution = 0.2; + const scale = 0.99; + Matter.Body.scale(this, scale, scale); } else { this.force.y += this.mass * 0.0002; //find mob targets @@ -2496,9 +2522,15 @@ const b = { } } else { if (isNaN(gun)) { //find gun by name + let found = false; for (let i = 0; i < b.guns.length; i++) { - if (gun === b.guns[i].name) gun = i + if (gun === b.guns[i].name) { + gun = i + found = true; + break + } } + if (!found) return //if no gun found don't give a gun } if (!b.guns[gun].have) b.inventory.push(gun); b.guns[gun].have = true; @@ -3151,7 +3183,7 @@ const b = { name: "ice IX", //11 description: "synthesize short-lived ice crystals
crystals seek out and freeze mobs", ammo: 0, - ammoPack: 80, + ammoPack: 75, have: false, isStarterGun: true, isEasyToAim: true, @@ -3898,70 +3930,5 @@ const b = { // player.force.x -= KNOCK * Math.cos(dir) // player.force.y -= KNOCK * Math.sin(dir) * 0.3 //reduce knock back in vertical direction to stop super jumps // }, - // { - // name: "triboelectricty", //14 - // description: "release particles that quickly seek out targets", - // ammo: 0, - // ammoPack: 40, - // have: false, - // isStarterGun: true, - // fire() { - // const dir = mech.angle + 0.2 * (Math.random() - 0.5); - // const me = bullet.length; - // const RADIUS = 6 - // bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), RADIUS, { - // angle: dir, - // inertia: Infinity, - // // friction: 0.05, - // // frictionAir: 0.05, - // restitution: 0.8, - // dmg: 0.14, //damage done in addition to the damage from momentum - // lookFrequency: 3, - // endCycle: game.cycle + Math.floor(120 * b.isModBulletsLastLonger), - // classType: "bullet", - // collisionFilter: { - // category: 0x000100, - // mask: 0x010111 //self collide - // }, - // minDmgSpeed: 0, - // lockedOn: null, - // isFollowMouse: true, - // onDmg() { - // this.endCycle = 0; - // }, - // onEnd() {}, - // do() { - // if (this.lockedOn) { //accelerate towards mobs - // this.force = Vector.mult(Vector.normalise(Vector.sub(this.position, this.lockedOn.position)), -this.mass * 0.01) - // Matter.Body.setVelocity(this, { - // x: this.velocity.x * 0.93, - // y: this.velocity.y * 0.93 - // }); - // } else { - // this.force.y += this.mass * 0.0004; - // } - // } - // }) - - // b.fireProps(mech.crouch ? 19 : 15, mech.crouch ? 45 : 30, dir, me); //cd , speed - - // //find mob targets - // let closeDist = Infinity; - // for (let i = 0, len = mob.length; i < len; ++i) { - // if ( - // Matter.Query.ray(map, bullet[me].position, mob[i].position).length === 0 && - // Matter.Query.ray(body, bullet[me].position, mob[i].position).length === 0 - // ) { - // const TARGET_VECTOR = Vector.sub(bullet[me].position, mob[i].position) - // const DIST = Vector.magnitude(TARGET_VECTOR); - // if (DIST < closeDist) { - // closeDist = DIST; - // bullet[me].lockedOn = mob[i] - // } - // } - // } - // } - // }, - // { ] }; \ No newline at end of file diff --git a/js/level.js b/js/level.js index 81b4f5b..e2a1caa 100644 --- a/js/level.js +++ b/js/level.js @@ -17,8 +17,7 @@ const level = { // level.difficultyIncrease(9) // b.giveGuns("vacuum bomb") // mech.setField("pilot wave") - // b.giveMod("energy"); - // b.giveMod("Born rule"); + // b.giveMod("nonlocality"); level.intro(); //starting level // level.testing(); @@ -103,6 +102,10 @@ const level = { level.defaultZoom = 1500 game.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#ddd"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" + level.fill.push({ x: 6400, y: -550, @@ -164,6 +167,9 @@ const level = { // } document.body.style.backgroundColor = "#ddd"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" // level.fillBG.push({ // x: -150, @@ -242,6 +248,10 @@ const level = { spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump document.body.style.backgroundColor = "#eee"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" + level.fillBG.push({ x: 2600, y: -600, @@ -395,6 +405,9 @@ const level = { spawn.debris(3035, -3900, 1500, 3); //16 debris per level document.body.style.backgroundColor = "#dbdcde"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" //spawn start building spawn.mapRect(-300, -800, 50, 800); @@ -575,6 +588,9 @@ const level = { level.defaultZoom = 1700 game.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#dcdcde"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" if (Math.random() < 0.75) { //normal direction start in top left @@ -804,6 +820,9 @@ const level = { spawn.debris(3450, 0, 2000, 16); //16 debris per level spawn.debris(3500, -2350, 1500, 2); //16 debris per level document.body.style.backgroundColor = "#dcdcde"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" //foreground level.fill.push({ @@ -984,6 +1003,9 @@ const level = { powerUps.spawnStartingPowerUps(1475, -1175); spawn.debris(750, -2200, 3700, 16); //16 debris per level document.body.style.backgroundColor = "#dcdcde"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" //foreground level.fill.push({ @@ -1124,7 +1146,6 @@ const level = { level.defaultZoom = 1500 game.zoomTransition(level.defaultZoom) - document.body.style.backgroundColor = "#dcdcde" //"#fafcff"; mech.setPosToSpawn(0, -700); //normal spawn //mech.setPosToSpawn(-2000, -1700); // left ledge spawn level.enter.x = mech.spawnPos.x - 50; @@ -1135,6 +1156,10 @@ const level = { level.exit.y = -2805; level.addZone(level.exit.x, level.exit.y, 100, 30, "nextLevel"); powerUps.spawnStartingPowerUps(-2550, -700); + document.body.style.backgroundColor = "#dcdcde" //"#fafcff"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" // spawn.laserZone(-550, -350, 10, 400, 0.3) // spawn.deathQuery(-550, -350, 50, 400) @@ -1306,7 +1331,6 @@ const level = { level.defaultZoom = 1300 game.zoomTransition(level.defaultZoom) - document.body.style.backgroundColor = "#dcdcde" //"#f2f5f3"; mech.setPosToSpawn(25, -55); //normal spawn //mech.setPosToSpawn(-2000, -1700); // left ledge spawn level.enter.x = mech.spawnPos.x - 50; @@ -1320,6 +1344,10 @@ const level = { spawn.debris(-3000, -800, 3280, 6); //16 debris per level spawn.debris(-1400, 410, 2300, 5); //16 debris per level powerUps.spawnStartingPowerUps(25, 500); + document.body.style.backgroundColor = "#dcdcde" //"#f2f5f3"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" //foreground // level.fill.push({ x: -3025, y: 50, width: 4125, height: 1350, color: "rgba(0,0,0,0.05)"}); @@ -1517,7 +1545,11 @@ const level = { level.addZone(level.exit.x, level.exit.y, 100, 30, "nextLevel"); document.body.style.backgroundColor = "#e0e5e0"; - //foreground + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" + + // foreground level.fill.push({ x: -550, y: -1700, @@ -1673,9 +1705,12 @@ const level = { level.exit.y = -1250; level.addZone(level.exit.x, level.exit.y, 100, 30, "nextLevel"); spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 20); //exit bump - document.body.style.backgroundColor = "#dbdcde"; spawn.debris(3800, -1480, 300, 12); spawn.debris(3600, -1130, 200, 2); + document.body.style.backgroundColor = "#dbdcde"; + // game.draw.mapFill = "#444" + // game.draw.bodyFill = "rgba(140,140,140,0.85)" + // game.draw.bodyStroke = "#222" level.fillBG.push({ x: -500, diff --git a/js/player.js b/js/player.js index 6d81d1e..e97ab6d 100644 --- a/js/player.js +++ b/js/player.js @@ -1225,9 +1225,15 @@ const mech = { hold() {}, setField(index) { if (isNaN(index)) { //find index by name + let found = false for (let i = 0; i < mech.fieldUpgrades.length; i++) { - if (index === mech.fieldUpgrades[i].name) index = i + if (index === mech.fieldUpgrades[i].name) { + index = i; + found = true; + break; + } } + if (!found) return //if you can't find the field don't give a field to avoid game crash } mech.fieldMode = index; document.getElementById("field").innerHTML = mech.fieldUpgrades[index].name @@ -1376,7 +1382,7 @@ const mech = { } } else if (b.isModMissileField) { // mech.fieldCDcycle = mech.cycle + 10; // set cool down to prevent +energy from making huge numbers of drones - mech.energy -= 0.5; + mech.energy -= 0.6; b.missile({ x: mech.pos.x + 40 * Math.cos(mech.angle), y: mech.pos.y + 40 * Math.sin(mech.angle) - 3 @@ -1927,7 +1933,7 @@ const mech = { }, { name: "pilot wave", - description: "use energy to push blocks with your mouse
energy drain is increased out of line of sight", + description: "use energy to push blocks with your mouse
field radius decreases out of line of sight", isEasyToAim: false, effect: () => { game.replaceTextLog = true; //allow text over write @@ -1944,16 +1950,22 @@ const mech = { mech.fieldRadius = 0; mech.drop(); mech.hold = function () { - if ((keys[32] || game.mouseDownRight && mech.fieldCDcycle < mech.cycle)) { //not hold but field button is pressed + if ((keys[32] || game.mouseDownRight && mech.fieldCDcycle < mech.cycle)) { + const scale = 25 + const bounds = { + min: { + x: mech.fieldPosition.x - scale, + y: mech.fieldPosition.y - scale + }, + max: { + x: mech.fieldPosition.x + scale, + y: mech.fieldPosition.y + scale + } + } + const isInMap = Matter.Query.region(map, bounds).length + // const isInMap = Matter.Query.point(map, mech.fieldPosition).length - - // if (Matter.Query.ray(map, game.mouseInGame, player.position).length === 0){ - - // } else { - // mech.fieldOn = false; - // } - - if (!mech.fieldOn) { + if (!mech.fieldOn) { // if field was off, and it starting up, teleport to new mouse location mech.fieldOn = true; mech.fieldPosition = { //smooth the mouse position x: game.mouseInGame.x, @@ -1963,62 +1975,58 @@ const mech = { x: mech.fieldPosition.x, y: mech.fieldPosition.y } - } else { + } else { //when field is on it smoothly moves towards the mouse mech.lastFieldPosition = { //used to find velocity of field changes x: mech.fieldPosition.x, y: mech.fieldPosition.y } - const smooth = 0.97 + const smooth = isInMap ? 0.985 : 0.96; mech.fieldPosition = { //smooth the mouse position x: mech.fieldPosition.x * smooth + game.mouseInGame.x * (1 - smooth), y: mech.fieldPosition.y * smooth + game.mouseInGame.y * (1 - smooth), } } - // Matter.Query.ray(map, game.mouseInGame, player.position).length === 0 - //make it so the field only works in line of sight - // make the field not get stuck on map when there in no line of site - // maybe track the last mouse position, and revert to smooting towards it when mouse leaves line of site - // if (Matter.Query.ray(map, mech.fieldPosition, player.position).length === 0) - - - mech.grabPowerUp(); - //disable if player is inside field - if (mech.energy > 0.01) { - // && Vector.magnitude(Vector.sub(game.mouseInGame, player.position)) > radius * 1.5 //disable effect when near player //find mouse velocity const diff = Vector.sub(mech.fieldPosition, mech.lastFieldPosition) const speed = Vector.magnitude(diff) - const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity - let radius = Math.max(50, 250 - 1.5 * speed) //change radius proportional to mouse speed //run a smoothing function? - let isVisible = true - if (Matter.Query.ray(map, mech.fieldPosition, player.position).length !== 0) { - isVisible = false - radius *= 0.2 + const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 45)) //limit velocity + let radius, radiusSmooth + if (Matter.Query.ray(map, mech.fieldPosition, player.position).length) { //is there something block the player's view of the field + radius = 0 + radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99 + } else { + radius = Math.max(50, 250 - 2 * speed) + radiusSmooth = 0.97 } - smooth = 0.9 - mech.fieldRadius = mech.fieldRadius * smooth + radius * (1 - smooth) + mech.fieldRadius = mech.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth) - //find nearby blocks for (let i = 0, len = body.length; i < len; ++i) { if (Vector.magnitude(Vector.sub(body[i].position, mech.fieldPosition)) < mech.fieldRadius) { - // Matter.Query.collides(player, [body[i]]).length === 0) { //block is not touching player, for no flying - // (Matter.Query.ray(map, game.mouseInGame, player.position).length === 0 ? 1 : 4) - const DRAIN = speed * body[i].mass * 0.00002 * (isVisible ? 1 : 4) + const DRAIN = speed * body[i].mass * 0.00002 if (mech.energy > DRAIN) { mech.energy -= DRAIN; Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8) body[i].force.y -= body[i].mass * game.g; //remove gravity effects } else { - mech.fieldOn = false mech.fieldCDcycle = mech.cycle + 120; + mech.fieldOn = false mech.fieldRadius = 0 break } } } + + if (b.isModPilotFreeze) { + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, mech.fieldPosition)) < mech.fieldRadius) { + mobs.statusSlow(mob[i], 120) + } + } + } + ctx.beginPath(); const rotate = mech.cycle * 0.008; mech.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(mech.energy, 1)); @@ -2026,21 +2034,18 @@ const mech = { const off2 = 1 - 0.06 * Math.sin(mech.fieldPhase); ctx.beginPath(); ctx.ellipse(mech.fieldPosition.x, mech.fieldPosition.y, 1.2 * mech.fieldRadius * off1, 1.2 * mech.fieldRadius * off2, rotate, 0, 2 * Math.PI); - // ctx.ellipse(game.mouseInGame.x, game.mouseInGame.y, radius * off1, radius * off2, -rotate, 0, 2 * Math.PI); - // ctx.arc(game.mouseInGame.x, game.mouseInGame.y, this.fieldRange, 0, 2 * Math.PI); - ctx.fillStyle = "#fff"; //"#eef"; ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; + ctx.fillStyle = "#fff"; //"#eef"; ctx.fill(); ctx.globalCompositeOperation = "source-over"; - ctx.beginPath(); ctx.ellipse(mech.fieldPosition.x, mech.fieldPosition.y, 1.2 * mech.fieldRadius * off1, 1.2 * mech.fieldRadius * off2, rotate, 0, mech.energy * 2 * Math.PI); ctx.strokeStyle = "#000"; ctx.lineWidth = 4; ctx.stroke(); } else { - mech.fieldOn = false mech.fieldCDcycle = mech.cycle + 120; + mech.fieldOn = false mech.fieldRadius = 0 } } else { diff --git a/todo.txt b/todo.txt index 192bcad..0db43dc 100644 --- a/todo.txt +++ b/todo.txt @@ -3,6 +3,8 @@ ************** TODO - n-gon ************** +mod pilot wave - nonlocality: works out of LOS + an effect when canceling a power up ammo? heals? 50% chance for a mod, 25% heal, 25% ammo @@ -13,19 +15,9 @@ liquid nitrogen cooling uranium reactor core Mobs get irradiated on contact with the player -add player Scent Trails for mod navigation +add player Scent Trails for mob navigation https://abitawake.com/news/articles/enemy-ai-chasing-a-player-without-navigation2d-or-a-star-pathfinding -try adding a subtile shift of color towards red, green or blue - block, map, background, text? color - could be random as each level loads - -player health becomes NaN -occurred a few times at higher levels - game.dmgScale, game.healScale not corrupted - - didn't occur: shotgun / laser with all mods that have no requirements - mod - killing a stunned mob gives something ammo or heal power up? @@ -42,16 +34,12 @@ boss mob - just a faster and larger version of a springer mob always shielded consider combining with time skipper field? -pulse and time dilation only upgrades left with no dedicated mod - mob - time skipper: sends a pulse wave out that will cause time to jump forward 1 second. mob stabber - extends one vector like the shooter, but quickly in order to stab mob sniper - targeting laser, then a high speed, no gravity bullet -mod - nails do poison damage - mod - increase laser bot range, and reduce energy drain mod - mines become a turret that fires nails