diff --git a/js/bullet.js b/js/bullet.js index ff80835..294f84a 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -4925,7 +4925,7 @@ const b = { return tech.dynamoBotCount + tech.foamBotCount + tech.soundBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount }, hasBotUpgrade() { - return tech.isNailBotUpgrade + tech.isFoamBotUpgrade + tech.isBoomBotUpgrade + tech.isLaserBotUpgrade + tech.isOrbitBotUpgrade + tech.isDynamoBotUpgrade + return tech.isNailBotUpgrade + tech.isFoamBotUpgrade + tech.isBoomBotUpgrade + tech.isLaserBotUpgrade + tech.isOrbitBotUpgrade + tech.isDynamoBotUpgrade + tech.isSoundBotUpgrade }, convertBotsTo(type) { //type can be a string like "dynamoBotCount" const totalPermanentBots = b.totalBots() diff --git a/js/index.js b/js/index.js index 6d5d999..b852be9 100644 --- a/js/index.js +++ b/js/index.js @@ -1372,7 +1372,7 @@ window.addEventListener("keydown", function (event) { } break } - if (b.inventory.length > 1 && !simulation.testing) { + if (b.inventory.length > 1 && !simulation.testing && !tech.isGunCycle) { switch (event.code) { case "Digit1": simulation.switchToGunInInventory(0); diff --git a/js/level.js b/js/level.js index 83ee29c..1f9928b 100644 --- a/js/level.js +++ b/js/level.js @@ -10,7 +10,7 @@ const level = { // playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"], //see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , subway, final) added later playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"], - communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "dojo", "tlinat", "ruins", "ace", "crimsonTowers"], + communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "tlinat", "ruins", "ace", "crimsonTowers"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon", "diamagnetism"], levels: [], start() { @@ -27,7 +27,7 @@ const level = { // m.immuneCycle = Infinity //you can't take damage // tech.tech[297].frequency = 100 // m.couplingChange(10) - // m.setField("molecular assembler") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole + // m.setField("standing wave") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole // m.energy = 0 // simulation.molecularMode = 2 // m.damage(0.1); @@ -48,10 +48,10 @@ const level = { // for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling"); - // level.testing(); - // for (let i = 0; i < 10; ++i) spawn.sniper(1900, -500) - // for (let i = 0; i < 1; ++i) spawn.slasher2(1900, -500) + // level.testing(); + // for (let i = 0; i < 4; ++i) spawn.hopMother(1900, -500) + // for (let i = 0; i < 0; ++i) spawn.hopper(1900, -500) // for (let i = 0; i < 1; ++i) spawn.shooterBoss(1900, -2500) // spawn.suckerBoss(1900, -500, 25) // spawn.slasher2(2000, -1150) @@ -1915,7 +1915,7 @@ const level = { const mover = level.mover(2800, -300, 1000, 25); //x,y,width.height,VxGoal,force const train = level.transport(2900, -500, 500, 25, 8); //x,y,width.height,VxGoal,force - spawn.bodyRect(1900, -550, 50, 50); + // spawn.bodyRect(1900, -550, 50, 50); const button = level.button(2535, -200) // spawn.bodyRect(250, -450, 50, 50); //block on button @@ -5026,15 +5026,15 @@ const level = { powerUps.directSpawn(x + 50, y - 1525, "ammo"); powerUps.directSpawn(x + 1950, y - 1525, "ammo"); powerUps.directSpawn(x + 1900, y - 1525, "ammo"); - spawn.hopMomBoss(x + 800, y + -2200) + spawn.hopMotherBoss(x + 800, y + -2200) for (let i = 0; i < 6; ++i) spawn.hopBullet(x + 150 + 750 * Math.random(), y + -1600) for (let i = 0; i < 6; ++i) spawn.hopBullet(x + 1100 + 750 * Math.random(), y + -1600) spawn.hopper(x + 1550, y + -775); spawn.hopper(x + 500, y + -775); - spawn.hopper(x + 1400, y + -775); - spawn.hopper(x + 550, y + -775); - spawn.hopper(x + 525, y + -1475); - spawn.hopper(x + 1550, y + -1500); + spawn.hopMother(x + 1400, y + -775); + spawn.hopMother(x + 550, y + -775); + spawn.hopMother(x + 525, y + -1475); + spawn.hopMother(x + 1550, y + -1500); } } } @@ -25602,456 +25602,6 @@ const level = { }; powerUps.addResearchToLevel(); //needs to run after mobs are spawned }, - dojo() { // By - simulation.makeTextLog(`underpass by weird_pusheen`); - - const vanishes = []; - const smoofes = []; - const leftRotor = level.rotor(-550, 900, 950, 25); - leftRotor.frictionAir = 0.01; - var leftSchwoof = level.boost(-20, -60, -2000); - var rightSchwoof = level.button(2550, -50); - var rightSchwoofState = false; - var rightSchwoofLive = true; - spawn.mapRect(2513, -39, 200, 100); - var pathPoints = [ - [0, 0], // Index 0 is owned by M and is set to M's position during play - // this means that occasionally the boss will bonk M on the way to somewhere else, which gives it a chance to hurt M and gives the player a chance to hurt it - [250, -750], /* Left bases */ - [250, -2500], - [350, -1500], // Left doorway - [1150, -1500], // Home base - [1150, -2750], // Upper base - [1950, -1500], // Right doorway - [2050, -750], /* Right bases */ - [2050, -2500], - [-150, -250], // Left porthole - ]; - function isntIn(point, array) { - for (var x = 0; x < array.length; x++) { - if (point[0] == array[x][0] && point[1] == array[x][1]) { - return false; - } - } - return true; - } - function isObstructed(v1, v2) { - var ret = Matter.Query.ray(map, - { - x: v1[0], - y: v1[1], - }, - { - x: v2[0], - y: v2[1] - }).length != 0; - return ret; // Kinda-ish stolen from mob.js - } - function pythag(p1, p2) { - var dx = p1[0] - p2[0]; - var dy = p1[1] - p2[1]; - return Math.sqrt(dx * dx + dy * dy); - } - var path = undefined; // This is a stupid way to go about pathfinding code. I might even clean it up! - function pathFind(goalPoint, startPoint, curPath = []) { - var myPoint = startPoint; - if (curPath.length) { - myPoint = curPath[curPath.length - 1]; - } - if (path && (curPath.length >= path.length)) { // If we've already found a shorter or equal path, no reason to continue and waste CPU time - return; // Minimizes for HOP COUNT, not PATH LENGTH - path length was buggy - } - if (!isObstructed(myPoint, goalPoint)) { // If the line to the goal point ain't blocked by a map object, we've arrived! - path = [...curPath]; - path.push(goalPoint); - return; - } - pathPoints.forEach(testPoint => { - if (isntIn(testPoint, curPath)) { // If it's reusing points, there's clearly something wrong - if (!isObstructed(myPoint, testPoint)) { // If the line to the test point ain't blocked by a map object - var thing = [...curPath]; - thing.push(testPoint); - pathFind(goalPoint, startPoint, thing); // Branch to a valid test point - } - } - }); - } - level.setPosToSpawn(1200, 500); - level.exit.x = 51500; - level.exit.y = -1875; - spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); - level.defaultZoom = 1500; - simulation.zoomTransition(level.defaultZoom) - document.body.style.backgroundColor = "#d8dadf"; - - spawn.mapRect(-500, 0, 3300, 300); // Floor - spawn.mapRect(-100, -3000, 2500, 100); // Ceiling - spawn.mapRect(-200, -3000, 100, 2600); // Left wall - spawn.mapRect(2400, -3000, 100, 3000); // Right wall - - spawn.mapRect(500, -1000, 100, 500); /* obstruction blocks */ - smoofes.push(map[map.length - 1]); - spawn.mapRect(500, -2500, 100, 500); - smoofes.push(map[map.length - 1]); - spawn.mapRect(1700, -1000, 100, 500); - smoofes.push(map[map.length - 1]); - spawn.mapRect(1700, -2500, 100, 500); - smoofes.push(map[map.length - 1]); - - spawn.mapRect(-1000, 550, 200, 50); // Left chonky stepppp low - spawn.mapRect(-800, 300, 200, 50); // Left chonky stepppp high - spawn.mapVertex(-1000, 1200, "0 0 100 0 700 500 700 700 0 700"); // Left chonky - spawn.mapRect(3100, 550, 200, 50); // Right chonky stepppp low - spawn.mapRect(2900, 300, 200, 50); // Right chonky stepppp high - spawn.mapVertex(3300, 1200, "0 0 -100 0 -700 500 -700 700 0 700"); // Right chonky - const leftElevator = level.elevator(-1400 - 300, 1450, 300, 100, 500); - const rightElevator = level.elevator(-1400 + 5100, 1450, 300, 100, 500); - - spawn.mapRect(-150, -1700, 200, 50); - spawn.mapRect(400, -2050, 200, 50); - spawn.mapRect(1600, -1000, 200, 50); - - spawn.randomMob(1200, 700); - spawn.randomMob(600, 1000); - spawn.randomMob(1800, 1000); - spawn.randomMob(3200, 400); - spawn.randomMob(3000, 200); - spawn.randomMob(-900, 400); - spawn.randomMob(-700, 200); - spawn.randomMob(1200, 1000); - for (var i = 0; i < 4; i++) { - spawn.randomSmallMob(Math.random() * 600 - 600, Math.random() * 3000 - 400); - } - spawn.grenadier(-300, -1000); - spawn.grenadier(2600, -1000); - - spawn.mapRect(-1400, 1450, 5100, 100); // The True Floor - - const slime = level.hazard(-1250, 1400, 4800, 50); - slime.maxHeight = 600; - simulation.draw.body = function () { - ctx.beginPath(); - for (let i = 0, len = body.length; i < len; ++i) { - if (!body[i].hidden) { - let vertices = body[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1; j < vertices.length; j++) { - ctx.lineTo(vertices[j].x, vertices[j].y); - } - ctx.lineTo(vertices[0].x, vertices[0].y); - } - } - ctx.lineWidth = 2; - ctx.fillStyle = color.block; - ctx.fill(); - ctx.strokeStyle = color.blockS; - ctx.stroke(); - } // Override the old draw code to allow intelligent hiding of blocks - preferably this becomes official code because it's just a single added if statement and makes a lot of things cleaner and more intelligent - - const vanish = function (x, y, width, height) { // normal vanishes don't work well on my map for some reason, so I rewrote - x += width / 2; - y += height / 2; - const getVertices = function (bX, bY, bW, bH) { return [{ x: bX, y: bY, index: 0, isInternal: false }, { x: bX + bW, y: bY, index: 1, isInternal: false }, { x: bX + bW, y: bY + bH, index: 4, isInternal: false }, { x: bX, y: bY + bH, index: 3, isInternal: false }] }; - const cMask = cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; - const vertices = getVertices(x, y, width, height); - const block = body[body.length] = Bodies.fromVertices(x, y, vertices, { - collisionFilter: { - category: cat.map, - mask: cMask - }, - isNoSetCollision: true, - inertia: Infinity, //prevents rotation - isNotHoldable: true, - isNonStick: true, //this keep sporangium from sticking - isTouched: false, - cWidth: width, - hiddenCycle: 0, - isStatic: true, - query() { - if (this.cWidth <= 0) { - if (this.cWidth > -100) { - this.cWidth = -100; - Matter.Body.setVertices(this, vertices); - } - this.isTouched = false; - this.collisionFilter.mask = undefined; - this.hidden = true; - this.hiddenCycle++; - if (this.hiddenCycle > 100) { - if (Matter.Query.collides(this, [player]).length) { - this.hiddenCycle = 50; - } - else { - this.hiddenCycle = 0; - this.cWidth = width; - this.collisionFilter.mask = cMask; - this.hidden = false; - } - } - } - else if (this.isTouched) { - Matter.Body.setVertices(this, getVertices(x, y, this.cWidth, height * (this.cWidth / width))); - this.cWidth -= 3; - } - else if (Matter.Query.collides(this, [player]).length) { // Elseif short circuit avoids expensive collision detection - this.isTouched = true; - } - } - }); - return block; - }; - - vanishes.push(vanish(800, 800, 800, 50)); - vanishes.push(vanish(400, 1100, 400, 50)); - vanishes.push(vanish(1600, 1100, 400, 50)); - spawn.bodyRect(1700, 812, 300, 25, 1, { - collisionFilter: { - category: cat.body, - mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.map - }, - isNoSetCollision: true, - isNotHoldable: true, - isNonStick: true, //this keep sporangium from sticking - restitution: 1, - friction: 0, - frictionAir: 0, - frictionStatic: 0, - query() { - Matter.Body.setAngularVelocity(this, 0); - Matter.Body.applyForce(this, this.position, { - x: 0, - y: -(this.position.y - 812) * 0.002 - }); - } - }); - const zigzag = body[body.length - 1]; - Matter.Body.applyForce(zigzag, zigzag.position, { - x: 0.1, - y: 0 - }); - var buttonWasDown = false; - level.customTopLayer = () => { - - } - level.custom = () => { - rightSchwoof.isUp = false; - level.exit.drawAndCheck(); - leftSchwoof.query(); - level.enter.draw(); - pathPoints[0][0] = m.pos.x; - pathPoints[0][1] = m.pos.y; - leftElevator.move(); - rightElevator.move(); - slime.query(); - zigzag.query(); - slime.levelRise(0.2); - for (var i = 0; i < vanishes.length; i++) { - vanishes[i].query(); - } - if (!rightSchwoofState) { - var math = m.pos.y < leftRotor.position.y; - Matter.Body.setAngularVelocity(leftRotor, (math ? 1 : -1) * Math.PI / 45); - } - if (rightSchwoofLive) { - rightSchwoof.query(); - rightSchwoof.draw(); - if (rightSchwoofState) { - ctx.fillStyle = "lightgreen"; - } - else { - ctx.fillStyle = "red"; - } - ctx.beginPath(); - ctx.arc(2615, -220, 40, 0, Math.PI * 2); - ctx.fill(); - } - if (rightSchwoof.isUp) { - buttonWasDown = true; - } - else if (buttonWasDown) { - buttonWasDown = false; - rightSchwoofState = !rightSchwoofState; - } - if (Matter.Query.collides(player, smoofes).length) { - Matter.Body.applyForce(player, player.position, { - x: 0, - y: -0.015 - }); - } - }; - - mobs.spawn(500, -500, 10, 100, "yellow"); /* TacticalBoss - Modes: - Spawn: - Pathfinds to a point above M and starts dropping mobs. Learns which mobs to drop to cause the most damage, of course. - Occasionally strikes at M. - Hide: - Pathfinds to the point furthest from M - Strike: - Pathfind really, really fast to M - Recharge: - Stop moving for a bit to "recharge" (this is so the player has a chance to hit it) - - It must always Hide or Recharge after Spawning or Striking. Which one it does is based on some factor I'll figure out. - Pathfinding is a hypersimplified algorithm with hard-coded "points" that it can travel between. M is one of these. - */ - var boss = mob[mob.length - 1]; - boss.isBoss = true; - boss.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - boss.onDeath = function () { - powerUps.spawnBossPowerUp(this.position.x, this.position.y); - level.exit.x = 2560; - level.exit.y = -90; - rightSchwoofLive = false; - }; - var spawnables = {}; - ["hopper", "stabber", "springer", "striker", "sneaker", "grower"].forEach((m) => { /* Used to be spawn.fullPickList, but some of those mobs don't do collision-only damage and would thus never be properly selected for */ - if (spawn[m]) { - spawnables[m] = { - fun: spawn[m], - name: m, - weight: 1 - } - } - }); - boss.stabCycle = 0; - boss.spawnCycle = 0; - function spawny() { - var totalWeight = 0; - Object.keys(spawnables).forEach(key => { - totalWeight += spawnables[key].weight; - }); - var cursorWeight = 0; - var choice = Math.random(); - var mC = undefined; - Object.values(spawnables).forEach((thing) => { - var lower = cursorWeight / totalWeight; - cursorWeight += thing.weight; - var upper = cursorWeight / totalWeight; - if ((choice > lower && choice <= upper) || !mC) { - mC = thing; - } - }); - mC.fun(boss.position.x, boss.position.y); - var sp = mob[mob.length - 1]; - sp.typeName = mC.name; - sp.onHit = () => { - spawnables[sp.typeName].weight += 1; - }; - var oldFun = sp.onDeath; - sp.onDeath = () => { /* Mobs that die are worth less */ - oldFun.call(sp); - spawnables[sp.typeName].weight -= 0.3; /* But not too much less */ - }; - } - boss.spawnDelay = 40; - boss.mode = "hide"; - boss.modeSwitch = -1; // Randomize mode immediately - boss.damageReduction = 0.1; - var oldOnHit = boss.onHit; - boss.onHit = () => { - boss.modeSwitch = -1; // After striking the player, always switch modes - oldOnHit.call(boss); - }; - boss.do = () => { - path = undefined; - var pfGoal = [0, 0]; - boss.modeSwitch--; - if (boss.modeSwitch < 0) { - if (!boss.isShielded) { - spawn.shield(boss, boss.position.x, boss.position.y, 0.75); // Every time the mode switches, have a 75% chance to gain a new shield - } - if (boss.mode == "hide" || boss.mode == "recharge") { - if (Math.random() > 0.5) { - boss.mode = "spawn"; - } - else { - boss.mode = "strike"; - } - boss.modeSwitch = 600; - } - else { - if (boss.mode == "strike") { - boss.mode = "hide"; // Always hides after striking - } - else { - if (Math.random() > 0.5) { - boss.mode = "hide"; - } - else { - boss.mode = "recharge"; // same when it goes into recharge mode - spawn.shield(boss, boss.position.x, boss.position.y, 1); - } - } - boss.modeSwitch = 200; - } - } - if (boss.mode == "hide") { /* Find the furthest point from M and get to it */ - var longest = 0; - pathPoints.forEach(item => { - if (item[0] == 1150) { - return; - } - var iL = pythag(item, [m.pos.x, m.pos.y]); - if (iL > longest) { - longest = iL; - pfGoal = item; - } - }); - } - else if (boss.mode == "strike") { - pfGoal = pathPoints[0]; // Target M - } - else if (boss.mode == "spawn") { - pfGoal = pathPoints[4]; // Go to Home Base to spawn - } - if (boss.mode != "recharge") { - if (m.pos.x > 2350 || m.pos.x < -150 || m.pos.y > 50) { - boss.mode = "hide"; - } - pathFind(pfGoal, [boss.position.x, boss.position.y]); - if (!path) { - return; // If it couldn't pathfind, just drift - } - var goalX = path[0][0]; - var goalY = path[0][1]; - - var dX = goalX - boss.position.x; - var dY = goalY - boss.position.y; - var hyp = Math.sqrt(dX * dX + dY * dY); - Matter.Body.applyForce(boss, { - x: goalX, - y: goalY - }, { - x: dX / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1), - y: dY / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1)// - 0.005 - }); - } - if (boss.mode == "spawn") { - boss.stabCycle++; - if (boss.stabCycle > 25) { - if (Math.abs(dX) < 200 && dY > 0) { - Matter.Body.applyForce(boss, { - x: player.position.x, - y: player.position.y - }, { - x: 0, - y: 5 - }); - } - boss.stabCycle = 0; - } - boss.spawnCycle++; - if (boss.spawnCycle > boss.spawnDelay) { - spawny(); - boss.spawnDelay += 4; - boss.spawnCycle = 0; - } - } - }; - boss.showHealthBar = true; - powerUps.addResearchToLevel() //needs to run after mobs are spawned - }, tlinat() { // _Destined_ formerly Richard0820#2652 simulation.makeTextLog(`tlinat by Richard0820`); simulation.fallHeight = 1 / 0, level.setPosToSpawn(0, -1e3), level.exit.x = 5100, level.exit.y = 3770, spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20), spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20), level.defaultZoom = 3000, simulation.zoomTransition(level.defaultZoom), document.body.style.backgroundColor = "#d8dadf"; diff --git a/js/powerup.js b/js/powerup.js index da882e1..8fef7cb 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -733,6 +733,9 @@ const powerUps = { if (localSettings.isHideImages) { document.getElementById("choose-grid").style.gridTemplateColumns = width text += powerUps.researchAndCancelText(type) + } else if (totalChoices === 0) { + document.getElementById("choose-grid").style.gridTemplateColumns = width + text += powerUps.researchAndCancelText(type) } else if (totalChoices === 1 || canvas.width < 1200) { document.getElementById("choose-grid").style.gridTemplateColumns = width text += powerUps.researchAndCancelText(type) @@ -903,39 +906,39 @@ const powerUps = { if (b.guns[i].isRecentlyShown) removeOption(i) } for (let i = 0; i < b.guns.length; i++) b.guns[i].isRecentlyShown = false //reset recently shown back to zero - if (options.length > 0) { - let text = powerUps.buildColumns(totalChoices, "gun") - for (let i = 0; i < totalChoices; i++) { - const choose = options[Math.floor(Math.seededRandom(0, options.length))] //pick an element from the array of options - // text += `