From 08e09b59c4e778a59d6606c0e14d098608ce46b8 Mon Sep 17 00:00:00 2001 From: landgreen Date: Sat, 18 Jul 2020 11:37:37 -0700 Subject: [PATCH] portal level work in progress --- js/bullet.js | 54 ++--- js/engine.js | 1 + js/game.js | 21 +- js/level.js | 548 ++++++++++++++++++++++++++++++++++++-------------- js/mods.js | 87 ++++++-- js/player.js | 11 +- js/powerup.js | 22 +- todo.txt | 25 ++- 8 files changed, 552 insertions(+), 217 deletions(-) diff --git a/js/bullet.js b/js/bullet.js index 6e85e41..2dbe576 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -740,6 +740,7 @@ const b = { const DIST = Vector.magnitude(TARGET_VECTOR); if (DIST < closeDist) { if (DIST < 60) { //eat the power up if close enough + powerUps.onPickUp(); powerUp[i].effect(); Matter.World.remove(engine.world, powerUp[i]); powerUp.splice(i, 1); @@ -988,6 +989,7 @@ const b = { const dir = mech.angle; const RADIUS = (12 + 4 * Math.random()) bullet[me] = Bodies.polygon(position.x, position.y, 4, RADIUS, { + isBot: true, angle: dir, friction: 0, frictionStatic: 0, @@ -1043,6 +1045,7 @@ const b = { const dir = mech.angle; const RADIUS = (10 + 5 * Math.random()) bullet[me] = Bodies.polygon(position.x, position.y, 6, RADIUS, { + isBot: true, angle: dir, friction: 0, frictionStatic: 0, @@ -1098,6 +1101,7 @@ const b = { const dir = mech.angle; const RADIUS = 21 bullet[me] = Bodies.polygon(position.x, position.y, 5, RADIUS, { + isBot: true, angle: dir, friction: 0, frictionStatic: 0, @@ -1288,6 +1292,7 @@ const b = { const dir = mech.angle; const RADIUS = (7 + 2 * Math.random()) bullet[me] = Bodies.polygon(position.x, position.y, 4, RADIUS, { + isBot: true, angle: dir, friction: 0, frictionStatic: 0, @@ -1364,6 +1369,7 @@ const b = { const dir = mech.angle; const RADIUS = (14 + 6 * Math.random()) bullet[me] = Bodies.polygon(position.x, position.y, 3, RADIUS, { + isBot: true, angle: dir, friction: 0, frictionStatic: 0, @@ -1391,11 +1397,6 @@ const b = { }, onEnd() {}, do() { - //move in a circle - // const radius = 1.5 - // this.offPlayer.x -= radius * Math.cos(game.cycle * 0.02) - // this.offPlayer.y -= radius * Math.sin(game.cycle * 0.02) - const playerPos = Vector.add(Vector.add(this.offPlayer, mech.pos), Vector.mult(player.velocity, 20)) //also include an offset unique to this bot to keep many bots spread out const farAway = Math.max(0, (Vector.magnitude(Vector.sub(this.position, playerPos))) / this.followRange) //linear bounding well const mag = Math.min(farAway, 4) * this.mass * this.acceleration @@ -1405,7 +1406,6 @@ const b = { x: this.velocity.x * 0.95, y: this.velocity.y * 0.95 }); - //find targets if (!(game.cycle % this.lookFrequency) && !mech.isStealth) { this.lockedOn = null; @@ -1420,7 +1420,6 @@ const b = { this.lockedOn = mob[i] } } - //randomize position relative to player if (Math.random() < 0.15) { this.offPlayer = { @@ -1429,7 +1428,6 @@ const b = { } } } - //hit target with laser if (this.lockedOn && this.lockedOn.alive && mech.energy > 0.15) { mech.energy -= 0.0014 * mod.isLaserDiode @@ -1526,12 +1524,11 @@ const b = { fire() { const me = bullet.length; const dir = mech.angle + (Math.random() - 0.5) * ((mech.crouch) ? 0.01 : 0.1); - bullet[me] = Bodies.rectangle(mech.pos.x + 23 * Math.cos(mech.angle), mech.pos.y + 23 * Math.sin(mech.angle), 20 * mod.bulletSize, 6 * mod.bulletSize, b.fireAttributes(dir)); - + bullet[me] = Bodies.rectangle(mech.pos.x + 23 * Math.cos(mech.angle), mech.pos.y + 23 * Math.sin(mech.angle), 20 * mod.bulletSize * mod.highCaliber, 6 * mod.bulletSize, b.fireAttributes(dir)); //fire delay decreases as you hold fire, down to 3 from 15 if (this.nextFireCycle + 1 < mech.cycle) this.startingHoldCycle = mech.cycle //reset if not constantly firing - const CD = Math.max(11 - 0.06 * (mech.cycle - this.startingHoldCycle), 2) //CD scales with cycles fire is held down + const CD = Math.max(11 - 0.06 * (mech.cycle - this.startingHoldCycle), 2) * mod.highCaliber //CD scales with cycles fire is held down this.nextFireCycle = mech.cycle + CD * b.fireCD //predict next fire cycle if the fire button is held down b.fireProps(CD, mech.crouch ? 38 : 34, dir, me); //cd , speed // b.fireProps(mech.crouch ? 7 : 4, mech.crouch ? 40 : 34, dir, me); //cd , speed @@ -1583,8 +1580,6 @@ const b = { player.force.y -= knock * Math.sin(mech.angle) * 0.3 //reduce knock back in vertical direction to stop super jumps } - - b.muzzleFlash(35); if (mod.isNailShot) { for (let i = 0; i < 14; i++) { @@ -1635,8 +1630,8 @@ const b = { num: 5, isEasyToAim: true, fire() { - const SPEED = mech.crouch ? 40 : 30 - mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 28 : 20) * b.fireCD); // cool down + const SPEED = mech.crouch ? 43 : 32 + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 18) * b.fireCD); // cool down if (mod.oneSuperBall) { let dir = mech.angle const me = bullet.length; @@ -1649,7 +1644,7 @@ const b = { // Matter.Body.setDensity(bullet[me], 0.0001); bullet[me].endCycle = game.cycle + Math.floor((300 + 60 * Math.random()) * mod.isBulletsLastLonger); bullet[me].minDmgSpeed = 0; - bullet[me].restitution = 0.999; + bullet[me].restitution = 1; bullet[me].friction = 0; bullet[me].do = function () { this.force.y += this.mass * 0.001; @@ -1663,7 +1658,7 @@ const b = { let dir = mech.angle - SPREAD * (mod.superBallNumber - 1) / 2; for (let i = 0; i < mod.superBallNumber; i++) { const me = bullet.length; - bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 12, 7 * mod.bulletSize, b.fireAttributes(dir, false)); + bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 12, 7.5 * mod.bulletSize, b.fireAttributes(dir, false)); World.add(engine.world, bullet[me]); //add bullet to world Matter.Body.setVelocity(bullet[me], { x: SPEED * Math.cos(dir), @@ -1927,7 +1922,7 @@ const b = { name: "missiles", description: "launch missiles that accelerate towards mobs
explodes when near target", ammo: 0, - ammoPack: 4, + ammoPack: 5, have: false, isEasyToAim: true, fireCycle: 0, @@ -2094,7 +2089,7 @@ const b = { bullet[me].restitution = 0.2; bullet[me].friction = 0.3; bullet[me].endCycle = Infinity - bullet[me].explodeRad = 440 + Math.floor(Math.random() * 30); + bullet[me].explodeRad = 450 + Math.floor(Math.random() * 30); bullet[me].onEnd = function () { b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end @@ -2218,7 +2213,7 @@ const b = { bullet[me].restitution = 0; bullet[me].minDmgSpeed = 0; bullet[me].damageRadius = 100; - bullet[me].maxDamageRadius = (425 + 125 * Math.random()) * (mod.isNeutronImmune ? 1.2 : 1) + bullet[me].maxDamageRadius = (435 + 150 * Math.random()) * (mod.isNeutronImmune ? 1.2 : 1) bullet[me].stuckTo = null; bullet[me].stuckToRelativePosition = null; bullet[me].onDmg = function () {}; @@ -2283,8 +2278,6 @@ const b = { } else { const bodyCollisions = Matter.Query.collides(this, body) if (bodyCollisions.length) { - - if (!bodyCollisions[0].bodyA.isNotSticky) { onCollide(this) this.stuckTo = bodyCollisions[0].bodyA @@ -2329,7 +2322,6 @@ const b = { this.endCycle = 0; } else { //aoe damage to player - if (!mod.isNeutronImmune && Vector.magnitude(Vector.sub(player.position, this.position)) < this.damageRadius) { const DRAIN = 0.0015 if (mech.energy > DRAIN) { @@ -2380,7 +2372,7 @@ const b = { x: speed * Math.cos(mech.angle), y: speed * Math.sin(mech.angle) }, 0, mod.isMineAmmoBack) - mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 60 : 20) * b.fireCD); // cool down + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 50 : 25) * b.fireCD); // cool down } }, { @@ -2508,7 +2500,7 @@ const b = { name: "drones", description: "deploy drones that crash into mobs
crashes reduce their lifespan by 1 second", ammo: 0, - ammoPack: 14, + ammoPack: 15, have: false, isEasyToAim: true, fire() { @@ -2621,7 +2613,10 @@ const b = { this.force.y += this.mass * 0.0003 / this.charge; // low gravity that scales with charge } } - + if (mod.isRailTimeSlow) { + game.fpsCap = game.fpsCapDefault + game.fpsInterval = 1000 / game.fpsCap; + } Matter.Body.scale(this, 8000, 8000) // show the bullet by scaling it up (don't judge me... I know this is a bad way to do it) this.endCycle = game.cycle + 140 @@ -2672,7 +2667,12 @@ const b = { let chargeRate = (mech.crouch) ? 0.975 : 0.987 chargeRate *= Math.pow(b.fireCD, 0.03) this.charge = this.charge * chargeRate + (1 - chargeRate) // this.charge converges to 1 - mech.energy -= (this.charge - lastCharge) * 0.28 //energy drain is proportional to charge gained, but doesn't stop normal mech.fieldRegen + if (mod.isRailTimeSlow) { + game.fpsCap = 30 //new fps + game.fpsInterval = 1000 / game.fpsCap; + } else { + mech.energy -= (this.charge - lastCharge) * 0.28 //energy drain is proportional to charge gained, but doesn't stop normal mech.fieldRegen + } //draw targeting let best; diff --git a/js/engine.js b/js/engine.js index 12ddd35..2109204 100644 --- a/js/engine.js +++ b/js/engine.js @@ -205,6 +205,7 @@ function collisionChecks(event) { if (obj.classType === "bullet" && obj.speed > obj.minDmgSpeed) { // const dmg = b.dmgScale * (obj.dmg + 0.15 * obj.mass * Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity))); let dmg = b.dmgScale * (obj.dmg + 0.15 * obj.mass * Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity))) + console.log(dmg) if (mod.isCrit && !mob[k].seePlayer.recall && !mob[k].shield) dmg *= 5 mob[k].foundPlayer(); mob[k].damage(dmg); diff --git a/js/game.js b/js/game.js index c2725da..dd9047c 100644 --- a/js/game.js +++ b/js/game.js @@ -39,6 +39,7 @@ const game = { b.bulletDraw(); b.bulletDo(); game.drawCircle(); + level.customTopLayer(); // game.clip(); ctx.restore(); game.drawCursor(); @@ -67,6 +68,7 @@ const game = { game.draw.testing(); game.drawCircle(); game.constructCycle() + level.customTopLayer(); ctx.restore(); game.testingOutput(); game.drawCursor(); @@ -691,6 +693,9 @@ const game = { mech.holdingTarget.collisionFilter.category = 0; mech.holdingTarget.collisionFilter.mask = 0; } + //set fps back to default + game.fpsCap = game.fpsCapDefault + game.fpsInterval = 1000 / game.fpsCap; }, getCoords: { //used when building maps, outputs a draw rect command to console, only works in testing mode @@ -735,12 +740,16 @@ const game = { x: level.enter.x + 50, y: level.enter.y - 20 }); - // Matter.Body.setPosition(player, { - // x: player.position.x, - // y: -7000 - // }); - // game.noCameraScroll() - + // move bots + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].isBot) { + Matter.Body.setPosition(bullet[i], player.position); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } if (game.difficultyMode === 2) mech.damage(0.3); if (game.difficultyMode === 1) mech.damage(0.1); mech.energy = 0; diff --git a/js/level.js b/js/level.js index 0ffa367..a58b4fe 100644 --- a/js/level.js +++ b/js/level.js @@ -12,18 +12,18 @@ const level = { if (build.isURLBuild && level.levelsCleared === 0) build.onLoadPowerUps(); if (level.levelsCleared === 0) { //this code only runs on the first level // level.difficultyIncrease(4) - // game.enableConstructMode() //used to build maps in testing mode + game.enableConstructMode() //used to build maps in testing mode + // game.zoomScale = 1000; + // game.setZoom(); // mech.isStealth = true; - // mod.giveMod("bot fabrication"); + // mod.giveMod("nail-bot"); // b.giveGuns("ice IX") // mech.setField("plasma torch") - level.intro(); //starting level - // level.sewers(); // level.testing(); - // level.template() - // level.bosses(); - // level.stronghold() + // level.intro(); //starting level + level.testChamber() + // level.sewers(); // level.satellite(); // level.skyscrapers(); // level.aerie(); @@ -31,7 +31,9 @@ const level = { // level.warehouse(); // level.highrise(); // level.office(); - + // level.bosses(); + // level.template() + // level.stronghold() //fan level } else { spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns // spawn.pickList = ["focuser", "focuser"] @@ -43,24 +45,12 @@ const level = { game.setZoom(); level.addToWorld(); //add bodies to game engine game.draw.setPaths(); - for (let i = 0; i < mod.laserBotCount; i++) { - b.laserBot() - } - for (let i = 0; i < mod.nailBotCount; i++) { - b.nailBot() - } - for (let i = 0; i < mod.foamBotCount; i++) { - b.foamBot() - } - for (let i = 0; i < mod.boomBotCount; i++) { - b.boomBot() - } - for (let i = 0; i < mod.plasmaBotCount; i++) { - b.plasmaBot() - } - + for (let i = 0; i < mod.laserBotCount; i++) b.laserBot() + for (let i = 0; i < mod.nailBotCount; i++) b.nailBot() + for (let i = 0; i < mod.foamBotCount; i++) b.foamBot() + for (let i = 0; i < mod.boomBotCount; i++) b.boomBot() + for (let i = 0; i < mod.plasmaBotCount; i++) b.plasmaBot() if (mod.isArmorFromPowerUps) { - // for (let i = 0; i < powerUps.totalPowerUps; i++) {} mech.maxHealth += 0.05 * powerUps.totalPowerUps if (powerUps.totalPowerUps) game.makeTextLog(" max health increased by " + (0.05 * powerUps.totalPowerUps * 100).toFixed(0) + "%", 300) } @@ -69,140 +59,249 @@ const level = { mech.displayHealth(); } }, + custom() {}, + customTopLayer() {}, //****************************************************************************************************************** //****************************************************************************************************************** //****************************************************************************************************************** //****************************************************************************************************************** - rotor(x, y, rotate = 0, radius = 900, width = 50, density = 0.0005) { - const rotor1 = Matter.Bodies.rectangle(x, y, width, radius, { - density: density, - isNotSticky: true, - isNotHoldable: true - }); - const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, { - angle: Math.PI / 2, - density: density, - isNotSticky: true, - isNotHoldable: true - }); - rotor = Body.create({ //combine rotor1 and rotor2 - parts: [rotor1, rotor2], - restitution: 0, - collisionFilter: { - category: cat.body, - mask: cat.body | cat.mob | cat.mobBullet | cat.mobShield | cat.powerUp | cat.player | cat.bullet - }, - }); - Matter.Body.setPosition(rotor, { - x: x, - y: y - }); - World.add(engine.world, [rotor]); - body[body.length] = rotor1 - body[body.length] = rotor2 + portal(centerA, angleA, centerB, angleB) { + const width = 30 + const height = 150 + const mapWidth = 200 + const unitA = Matter.Vector.rotate({ + x: 1, + y: 0 + }, angleA) + const unitB = Matter.Vector.rotate({ + x: 1, + y: 0 + }, angleB) - setTimeout(function () { - rotor.collisionFilter.category = cat.body; - rotor.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet //| cat.map - }, 1000); - - const constraint = Constraint.create({ //fix rotor in place, but allow rotation - pointA: { - x: x, - y: y - }, - bodyB: rotor - }); - World.add(engine.world, constraint); - - if (rotate) { - rotor.rotate = function () { - if (!mech.isBodiesAsleep) { - Matter.Body.applyForce(rotor, { - x: rotor.position.x + 100, - y: rotor.position.y + 100 - }, { - x: rotate * rotor.mass, - y: 0 - }) - } else { - Matter.Body.setAngularVelocity(rotor, 0); - } + draw = function () { + ctx.beginPath(); //portal + sensor = this.vertices; + ctx.moveTo(sensor[0].x, sensor[0].y); + for (let i = 1; i < sensor.length; ++i) { + ctx.lineTo(sensor[i].x, sensor[i].y); } + ctx.fillStyle = this.color + ctx.fill(); } - composite[composite.length] = rotor - return rotor - }, - button(x, y, width = 70) { - spawn.mapVertex(x + 35, y + 2, "70 10 -70 10 -40 -10 40 -10"); - return { - isUp: false, - min: { - x: x, - y: y - 15 - }, - max: { - x: x + width, - y: y - 5 - }, - width: width, - height: 20, - query() { - if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) { - this.isUp = true; + query = function () { + if (Matter.Query.collides(this, [player]).length === 0) { //not touching player + if (player.isInPortal === this) player.isInPortal = null + } else if (player.isInPortal !== this) { //touching player + //teleport + player.isInPortal = this.portalPair + Matter.Body.setPosition(player, this.portalPair.position); + //rotate velocity + // const unit = Vector.normalise(Vector.rotate(player.velocity, this.angle - this.portalPair.angle + Math.PI)) //rotate and flip velocity + // const mag = Math.max(20, Math.min(50, Vector.magnitude(player.velocity))) //20 is lowest speed, 50 is highest speed + // const v = Vector.mult(unit, mag) + + let mag + if (this.angle === -Math.PI / 2) { + //portal that fires the player up + mag = Math.max(10, Math.min(50, player.velocity.y * 0.8)) + 11 } else { - this.isUp = false; + mag = Math.max(3, Math.min(50, Vector.magnitude(player.velocity))) } - }, - draw() { - ctx.fillStyle = "hsl(0, 100%, 70%)" - if (this.isUp) { - ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20) - } else { - ctx.fillRect(this.min.x, this.min.y, this.width, 25) - } - } - } - }, - hazard(x, y, width, height, damage = 0.0005, color = "hsl(160, 100%, 35%)") { - return { - min: { - x: x, - y: y - }, - max: { - x: x + width, - y: y + height - }, - width: width, - height: height, - maxHeight: height, - query() { - if (this.height > 0 && Matter.Query.region([player], this).length && !mech.isStealth) { - mech.damage(damage) - const drain = 0.005 - if (mech.energy > drain) mech.energy -= drain - } - }, - draw() { - ctx.fillStyle = color - ctx.fillRect(this.min.x, this.min.y, this.width, this.height) - }, - level(isFill) { - const growSpeed = 1 - if (isFill) { - if (this.height < this.maxHeight) { - this.height += growSpeed - this.min.y -= growSpeed - this.max.y = this.min.y + this.height + console.log(mag) + const v = Vector.mult(this.portalPair.unit, mag) + Matter.Body.setVelocity(player, v); + mech.buttonCD_jump = 0 //disable short jumps when letting go of jump key + // move bots to follow player + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].isBot) { + Matter.Body.setPosition(bullet[i], this.portalPair.portal.position); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); } - } else if (this.height > 0) { - this.height -= growSpeed - this.min.y += growSpeed - this.max.y = this.min.y + this.height } } } + + const portalA = Bodies.rectangle(centerA.x, centerA.y, width, height, { + isSensor: true, + angle: angleA, + color: "hsla(197, 100%, 50%,0.7)", + draw: draw, + }); + const portalB = Bodies.rectangle(centerB.x, centerB.y, width, height, { + isSensor: true, + angle: angleB, + color: "hsla(29, 100%, 50%, 0.7)", + draw: draw + }); + const mapA = Bodies.rectangle(centerA.x - 0.5 * unitA.x * mapWidth, centerA.y - 0.5 * unitA.y * mapWidth, mapWidth, height + 100, { + collisionFilter: { + category: cat.map, + mask: cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + unit: unitA, + angle: angleA, + color: game.draw.mapFill, + draw: draw, + query: query, + lastPortalCycle: 0 + }); + Matter.Body.setStatic(mapA, true); //make static + World.add(engine.world, mapA); //add to world + + const mapB = Bodies.rectangle(centerB.x - 0.5 * unitB.x * mapWidth, centerB.y - 0.5 * unitB.y * mapWidth, mapWidth, height + 100, { + collisionFilter: { + category: cat.map, + mask: cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet + }, + unit: unitB, + angle: angleB, + color: game.draw.mapFill, + draw: draw, + query: query, + lastPortalCycle: 0 + }); + Matter.Body.setStatic(mapB, true); //make static + World.add(engine.world, mapB); //add to world + mapA.portal = portalA + mapB.portal = portalB + mapA.portalPair = mapB + mapB.portalPair = mapA + return [portalA, portalB, mapA, mapB] + }, + testChamber() { + const portal = level.portal({ + x: 2500, + y: -100 + }, Math.PI, { //left + x: 2500, + y: -3100 + }, Math.PI) //left + const portal2 = level.portal({ + x: 100, + y: -2150 + }, -Math.PI / 2, { //up + x: 1300, + y: -2150 + }, -Math.PI / 2) //up + level.custom = () => { + level.playerExitCheck(); + portal[2].query() + portal[3].query() + portal2[2].query() + portal2[3].query() + }; + level.customTopLayer = () => { + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + portal2[0].draw(); + portal2[1].draw(); + portal2[2].draw(); + portal2[3].draw(); + }; + + if (false) { + level.setPosToSpawn(0, -50); //lower spawn + level.exit.y = level.enter.y - 550; + level.fillBG.push({ + x: -300, + y: -1000, + width: 650, + height: 500, + color: "#d4f4f4" + }); + } else { + level.setPosToSpawn(0, -600); //upper spawn + level.exit.y = level.enter.y + 550; + level.fillBG.push({ + x: -300, + y: -500, + width: 650, + height: 500, + color: "#d4f4f4" + }); + } + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = level.enter.x; + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); + + level.defaultZoom = 1800 + game.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#dcdcde"; + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + + //outer wall + spawn.mapRect(-500, -3800, 200, 4000); //left map wall + spawn.mapRect(2500, -2975, 200, 2375); //right map middle wall + spawn.mapRect(-400, -3800, 3100, 200); //map ceiling + spawn.mapRect(-400, 0, 3100, 200); //floor + + //entrance + spawn.mapRect(300, -550, 50, 350); //right entrance wall + spawn.mapRect(-400, -550, 3000, 50); //ceiling + //entrance clutter + const xPos = shuffle([550, 1100, 1850]); + spawn.mapRect(xPos[0], -350, 400, 100); + spawn.mapRect(xPos[1], -350, 300, 400); + spawn.mapRect(xPos[2], -150, 300, 200); + + //exit + spawn.mapRect(-400, -1050, 750, 50); + spawn.mapRect(300, -1050, 50, 300); + + + //portal bottom + spawn.mapRect(2500, -700, 200, 540); //right portal wall + spawn.mapRect(2525, -200, 175, 250); //right portal back wall + + //portal top + spawn.mapRect(1400, -3000, 1300, 50); //floor + spawn.mapRect(2500, -3700, 200, 540); //right portal wall + spawn.mapRect(2525, -3200, 175, 250); //right portal back wall + // spawn.mapRect(700, -3250, 775, 350); + + spawn.mapRect(0, -3250, 1450, 50); + spawn.mapRect(1400, -3250, 50, 300); + + // spawn.mapRect(-350, -2875, 175, 325); + spawn.mapRect(-150, -3000, 150, 50); + spawn.mapRect(-350, -2750, 175, 200); + + + //portal 2 + spawn.mapRect(-300, -2600, 300, 700); + spawn.mapRect(-25, -1975, 250, 75); + + spawn.mapRect(1400, -2600, 300, 700); + spawn.mapRect(1175, -1975, 250, 75); + + // spawn.mapRect(200, -2150, 25, 250); + // spawn.mapRect(475, -2150, 25, 250); + //portal walls + // spawn.mapRect(575, -8, 100, 50); + // spawn.mapRect(925, -8, 100, 50); + // spawn.mapRect(675,t -625, 250, 675); + + // spawn.mapRect(1700, 75, 150, 25); + // spawn.mapRect(1700, -700, 150, 25); + // spawn.mapRect(1850, 0, 525, 100); + // spawn.mapRect(1850, -700, 525, 100); + + + + + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.boost(4150, 0, 1300); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // spawn.randomBoss(1700, -900, 0.4); + // if (game.difficulty > 3) spawn.randomLevelBoss(2200, -1300); + powerUps.addRerollToLevel() //needs to run after mobs are spawned }, sewers() { const rotor = level.rotor(5100, 2425, -0.001) @@ -2356,4 +2455,161 @@ const level = { World.add(engine.world, consBB[i]); } }, + rotor(x, y, rotate = 0, radius = 900, width = 50, density = 0.0005) { + const rotor1 = Matter.Bodies.rectangle(x, y, width, radius, { + density: density, + isNotSticky: true, + isNotHoldable: true + }); + const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, { + angle: Math.PI / 2, + density: density, + isNotSticky: true, + isNotHoldable: true + }); + rotor = Body.create({ //combine rotor1 and rotor2 + parts: [rotor1, rotor2], + restitution: 0, + collisionFilter: { + category: cat.body, + mask: cat.body | cat.mob | cat.mobBullet | cat.mobShield | cat.powerUp | cat.player | cat.bullet + }, + }); + Matter.Body.setPosition(rotor, { + x: x, + y: y + }); + World.add(engine.world, [rotor]); + body[body.length] = rotor1 + body[body.length] = rotor2 + + setTimeout(function () { + rotor.collisionFilter.category = cat.body; + rotor.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet //| cat.map + }, 1000); + + const constraint = Constraint.create({ //fix rotor in place, but allow rotation + pointA: { + x: x, + y: y + }, + bodyB: rotor + }); + World.add(engine.world, constraint); + + if (rotate) { + rotor.rotate = function () { + if (!mech.isBodiesAsleep) { + Matter.Body.applyForce(rotor, { + x: rotor.position.x + 100, + y: rotor.position.y + 100 + }, { + x: rotate * rotor.mass, + y: 0 + }) + } else { + Matter.Body.setAngularVelocity(rotor, 0); + } + } + } + composite[composite.length] = rotor + return rotor + }, + button(x, y, width = 66) { + spawn.mapVertex(x + 35, y + 2, "70 10 -70 10 -40 -10 40 -10"); + map[map.length - 1].restitution = 0; + map[map.length - 1].friction = 1; + map[map.length - 1].frictionStatic = 1; + + // const buttonSensor = Bodies.rectangle(x + 35, y - 1, 70, 20, { + // isSensor: true + // }); + + return { + isUp: false, + min: { + x: x + 2, + y: y - 11 + }, + max: { + x: x + width, + y: y - 10 + }, + width: width, + height: 20, + query() { + // if (Matter.Query.collides(buttonSensor, body).length === 0 && Matter.Query.collides(buttonSensor, [player]).length === 0) { + if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) { + this.isUp = true; + } else { + this.isUp = false; + // const list = Matter.Query.collides(buttonSensor, body) + // if (list.length > 0) { + // Matter.Body.setVelocity(list[0].bodyB, { + // x: 0, + // y: 0 + // }); + // } + } + }, + draw() { + ctx.fillStyle = "hsl(0, 100%, 70%)" + if (this.isUp) { + ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20) + } else { + ctx.fillRect(this.min.x, this.min.y, this.width, 25) + } + //draw sensor zone + // ctx.beginPath(); + // sensor = buttonSensor.vertices; + // ctx.moveTo(sensor[0].x, sensor[0].y); + // for (let i = 1; i < sensor.length; ++i) { + // ctx.lineTo(sensor[i].x, sensor[i].y); + // } + // ctx.lineTo(sensor[0].x, sensor[0].y); + // ctx.fillStyle = "rgba(255, 255, 0, 0.3)"; + // ctx.fill(); + } + } + }, + hazard(x, y, width, height, damage = 0.0005, color = "hsl(160, 100%, 35%)") { + return { + min: { + x: x, + y: y + }, + max: { + x: x + width, + y: y + height + }, + width: width, + height: height, + maxHeight: height, + query() { + if (this.height > 0 && Matter.Query.region([player], this).length && !mech.isStealth) { + mech.damage(damage) + const drain = 0.005 + if (mech.energy > drain) mech.energy -= drain + } + }, + draw() { + ctx.fillStyle = color + ctx.fillRect(this.min.x, this.min.y, this.width, this.height) + }, + level(isFill) { + const growSpeed = 1 + if (isFill) { + if (this.height < this.maxHeight) { + this.height += growSpeed + this.min.y -= growSpeed + this.max.y = this.min.y + this.height + } + } else if (this.height > 0) { + this.height -= growSpeed + this.min.y += growSpeed + this.max.y = this.min.y + this.height + } + } + } + }, }; \ No newline at end of file diff --git a/js/mods.js b/js/mods.js index ddb6a03..3e3f339 100644 --- a/js/mods.js +++ b/js/mods.js @@ -643,7 +643,7 @@ const mod = { maxCount: 1, count: 0, allowed() { - return game.fpsCapDefault > 45 + return game.fpsCapDefault > 45 && !mod.isRailTimeSlow }, requires: "FPS above 45", effect() { @@ -852,22 +852,6 @@ const mod = { mech.displayHealth(); } }, - { - name: "negentropy", - description: "at the start of each level
heal up to 66% of maximum health", - maxCount: 1, - count: 0, - allowed() { - return mech.maxHealth > 1 || mod.isArmorFromPowerUps - }, - requires: "increased max health", - effect() { - mod.isHealLowHealth = true; - }, - remove() { - mod.isHealLowHealth = false; - } - }, { name: "crystallized armor", description: "increase maximum health by 5% for each
unused power up at the end of a level", @@ -884,6 +868,38 @@ const mod = { mod.isArmorFromPowerUps = false; } }, + { + name: "negentropy", + description: "at the start of each level
heal up to 66% of maximum health", + maxCount: 1, + count: 0, + allowed() { + return mech.maxHealth > 1 || mod.isArmorFromPowerUps + }, + requires: "increased max health", + effect() { + mod.isHealLowHealth = true; + }, + remove() { + mod.isHealLowHealth = false; + } + }, + // { + // name: "prismatic cleavage", + // description: "harm that would be fatal no longer kills you
instead it is removed from your maximum health", + // maxCount: 1, + // count: 0, + // allowed() { + // return mech.maxHealth > 1 || mod.isArmorFromPowerUps + // }, + // requires: "increased max health", + // effect() { + // mod.isMaxHealthRemove = true; + // }, + // remove() { + // mod.isMaxHealthRemove = false; + // } + // }, { name: "recursive healing", description: "healing power ups trigger 1 more time", @@ -1245,6 +1261,22 @@ const mod = { mod.bulletSize = 1; } }, + { + name: "high caliber", + description: "100% increased minigun bullet size
100% increased delay after firing", + maxCount: 1, + count: 0, + allowed() { + return mod.haveGunCheck("minigun") + }, + requires: "minigun", + effect() { + mod.highCaliber = 2 + }, + remove() { + mod.highCaliber = 1 + } + }, { name: "ice crystal nucleation", description: "your minigun uses energy to condense
unlimited freezing bullets from water vapor", @@ -1813,7 +1845,22 @@ const mod = { mod.isFoamGrowOnDeath = false; } }, - + { + name: "frame-dragging", + description: "slow time while charging the rail gun
charging no longer drains energy", + maxCount: 1, + count: 0, + allowed() { + return game.fpsCapDefault > 45 && mod.haveGunCheck("rail gun") && !mod.isSlowFPS + }, + requires: "rail gun && FPS above 45", + effect() { + mod.isRailTimeSlow = true; + }, + remove() { + mod.isRailTimeSlow = false; + } + }, { name: "fragmenting projectiles", description: "rail gun fragments into nails
after hitting mobs at high speeds", @@ -2384,5 +2431,7 @@ const mod = { isRerollHaste: null, rerollHaste: null, isMineDrop: null, - isRerollBots: null + isRerollBots: null, + isRailTimeSlow: null + // isMaxHealthRemove: null } \ No newline at end of file diff --git a/js/player.js b/js/player.js index 9791101..c91127b 100644 --- a/js/player.js +++ b/js/player.js @@ -945,14 +945,7 @@ const mech = { y: powerUp[i].velocity.y * 0.11 }); if (dist2 < 5000 && !game.isChoosing) { //use power up if it is close enough - if (mod.isMassEnergy) mech.energy = mech.maxEnergy * 3; - if (mod.isMineDrop) b.mine({ - x: mech.pos.x, - y: mech.pos.y - }, { - x: 0, - y: 0 - }, 0, mod.isMineAmmoBack) + powerUps.onPickUp(); 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 @@ -1663,7 +1656,7 @@ const mech = { mech.grabPowerUp(); mech.lookForPickUp(180); - const DRAIN = 0.0017 + const DRAIN = 0.0014 if (mech.energy > DRAIN) { mech.energy -= DRAIN; if (mech.energy < DRAIN) { diff --git a/js/powerup.js b/js/powerup.js index 63a9b6d..d1db07a 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -195,7 +195,9 @@ const powerUps = { let choice2 = -1 let choice3 = -1 if (choice1 > -1) { - let text = `

choose a field

` + let text = "" + if (!mod.isDeterminism) text += `
` + text += `

choose a field

` text += `
  ${mech.fieldUpgrades[choice1].name}
${mech.fieldUpgrades[choice1].description}
` if (!mod.isDeterminism) { choice2 = pick(mech.fieldUpgrades, choice1) @@ -263,7 +265,9 @@ const powerUps = { } } - let text = `

choose a mod

` + let text = "" + if (!mod.isDeterminism) text += `
` + text += `

choose a mod

` let choice1 = pick() let choice2 = -1 let choice3 = -1 @@ -333,7 +337,9 @@ const powerUps = { let choice2 = -1 let choice3 = -1 if (choice1 > -1) { - let text = `

choose a gun

` + let text = "" + if (!mod.isDeterminism) text += `
` + text += `

choose a gun

` text += `
  ${b.guns[choice1].name}
${b.guns[choice1].description}
` if (!mod.isDeterminism) { choice2 = pick(b.guns, choice1) @@ -365,6 +371,16 @@ const powerUps = { } } }, + onPickUp() { + if (mod.isMassEnergy) mech.energy = mech.maxEnergy * 3; + if (mod.isMineDrop) b.mine({ + x: mech.pos.x, + y: mech.pos.y + }, { + x: 0, + y: 0 + }, 0, mod.isMineAmmoBack) + }, giveRandomAmmo() { const ammoTarget = Math.floor(Math.random() * (b.guns.length)); const ammo = Math.ceil(b.guns[ammoTarget].ammoPack * 6); diff --git a/todo.txt b/todo.txt index 51c1570..6a7f9f3 100644 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,18 @@ +determinism mod no longer lets you cancel power ups -rerolls now spawn at about 1 per level +mod: frame dragging - rail gun doesn't drain energy, and slows time while charging +mod: high caliber - minigun bullets are bigger, but fire slower ************** TODO - n-gon ************** +using a reroll gives you, a heal or ammo + +level boss: boss that dies and comes back to life but with one less side until it hits 3 sides + change color too (hsl) + boss shrinks and moves faster after each death + +mechanic: lose max health instead of taking fatal damage + map: laboratory rooms with switches that change physics gravity room @@ -10,10 +20,17 @@ map: laboratory laser room radiation room +map: observatory + button controls rotation of telescope + laser beam shoots out of telescope + button opens the dome + map: prison doors linked to buttons mobs inside the doors? +map: airport + button: blocks that are on the button at an angle will slowly slide off the button maybe just avoid long blocks near the button? maybe actively hold the button in place? @@ -42,12 +59,6 @@ field that pushes blocks and mobs away mod - AoE radiation might work when the effect ends or maybe just anytime another mob is near -medium caliber gun in between minigun and rail gun - mod: electricity damages mobs that get near the bullet - get ammo back if it hits mobs - ammo returns to you if it misses - bullets have a gentle arc? - mob that flashes the player (makes the graphics not update for a couple seconds) mod - do 50% more damage in close, but 50% less at a distance