diff --git a/index.html b/index.html index df44b6c..516b61f 100644 --- a/index.html +++ b/index.html @@ -49,12 +49,6 @@
settings
- - - -
- -
+ + +
+ + +
+
+ + + + + + + + +
-
- -
diff --git a/js/index.js b/js/index.js index 651329c..3eae6ad 100644 --- a/js/index.js +++ b/js/index.js @@ -247,6 +247,8 @@ const build = {
defense: ${(1-m.harmReduction()).toPrecision(3)}     difficulty: ${(1/simulation.dmgScale).toPrecision(3)}
fire rate: ${((1-b.fireCDscale)*100).toFixed(b.fireCDscale < 0.1 ? 2 : 0)}%
duplication: ${(tech.duplicationChance()*100).toFixed(0)}% +
coupling: ${(m.coupling).toFixed(2)} +${m.coupling> 0 ? '
'+m.couplingDescription(): ""} ${botText}

health: (${(m.health*100).toFixed(0)} / ${(m.maxHealth*100).toFixed(0)}) @@ -920,7 +922,7 @@ window.addEventListener("keydown", function(event) { input.fire = true break case input.key.field: - event.preventDefault(); + // event.preventDefault(); input.field = true break case input.key.nextGun: @@ -1248,7 +1250,6 @@ if (localstorageCheck()) { localSettings = { isAllowed: false } } - if (localSettings.isAllowed && !localSettings.isEmpty) { console.log('restoring previous settings') @@ -1279,10 +1280,18 @@ if (localSettings.isAllowed && !localSettings.isEmpty) { simulation.fpsCapDefault = Number(localSettings.fpsCapDefault) } document.getElementById("fps-select").value = localSettings.fpsCapDefault + + if (localSettings.banList.length === 0 || localSettings.banList === "undefined") { + localSettings.banList = "" + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } + document.getElementById("banned").value = localSettings.banList + } else { console.log('setting default localSettings') const isAllowed = localSettings.isAllowed //don't overwrite isAllowed value localSettings = { + banList: "", isAllowed: isAllowed, personalSeeds: [], isJunkExperiment: false, @@ -1302,6 +1311,7 @@ if (localSettings.isAllowed && !localSettings.isEmpty) { simulation.isCommunityMaps = localSettings.isCommunityMaps document.getElementById("difficulty-select").value = localSettings.difficultyMode document.getElementById("fps-select").value = localSettings.fpsCapDefault + document.getElementById("banned").value = localSettings.banList } document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" // document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" @@ -1322,6 +1332,11 @@ document.getElementById("fps-select").addEventListener("input", () => { if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); +document.getElementById("banned").addEventListener("input", () => { + localSettings.banList = document.getElementById("banned").value + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage +}); + document.getElementById("community-maps").addEventListener("input", () => { simulation.isCommunityMaps = document.getElementById("community-maps").checked localSettings.isCommunityMaps = simulation.isCommunityMaps diff --git a/js/level.js b/js/level.js index 2634cbf..c2f9a30 100644 --- a/js/level.js +++ b/js/level.js @@ -16,16 +16,16 @@ const level = { start() { if (level.levelsCleared === 0) { //this code only runs on the first level // simulation.enableConstructMode() //used to build maps in testing mode - // level.difficultyIncrease(1 * 4) //30 is near max on hard //60 is near max on why + // level.difficultyIncrease(5 * 4) //30 is near max on hard //60 is near max on why // simulation.isHorizontalFlipped = true // m.maxHealth = m.health = 100 // tech.isRerollDamage = true // powerUps.research.changeRerolls(100000) // m.immuneCycle = Infinity //you can't take damage // tech.tech[297].frequency = 100 - // b.guns[0].ammo = 10000 // m.setField("time dilation") //molecular assembler time dilation perfect diamagnetism metamaterial cloaking wormhole negative mass // b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 matter wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser + // b.guns[0].ammo = 1000000 // tech.giveTech("sentry"); // tech.giveTech("MACHO"); // tech.giveTech("elephant's toothpaste") @@ -36,10 +36,8 @@ const level = { // for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "tech"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(450, -50, "research"); - // spawn.flutter(1900, -500, 10) // spawn.starter(1900, -500, 200) - // spawn.historyBoss(1900, -400) - // spawn.beetleBoss(1900, -400) + // spawn.snakeSpitBoss(1900, -400) // for (let i = 0; i < 15; ++i) spawn.starter(1900 + 300 * Math.random(), -500 + 300 * Math.random()) // level.testing(); // for (let i = 0; i < 7; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research"); @@ -239,30 +237,44 @@ const level = { } }, populateLevels() { //run a second time if URL is loaded - if (document.getElementById("seed").value) { + if (document.getElementById("banned").value) { //remove levels from ban list in settings + const banList = document.getElementById("banned").value.replace(/,/g, ' ').replace(/\s\s+/g, ' ').replace(/[^\w\s]/g, '') //replace commas with spaces, replace double spaces with single, remove strange symbols + const remove = banList.split(" "); + console.log('remove these', remove) + console.log('community levels before', level.communityLevels) + for (let i = 0; i < remove.length; i++) { + const index = level.communityLevels.indexOf(remove[i]) + if (index !== -1) level.communityLevels.splice(index, 1); + } + console.log('community levels after', level.communityLevels) + console.log('Landgreen levels before', level.playableLevels) + for (let i = 0; i < remove.length; i++) { + if (level.playableLevels.length + level.communityLevels.length * simulation.isCommunityMaps < 10) break //can't remove too many levels + const index = level.playableLevels.indexOf(remove[i]) + if (index !== -1) level.playableLevels.splice(index, 1); + } + console.log('Landgreen levels after', level.playableLevels) + } + + if (document.getElementById("seed").value) { //check for player entered seed in settings Math.initialSeed = String(document.getElementById("seed").value) Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it } + if (simulation.isTraining) { level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment - } else { - simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally + } else { //add remove and shuffle levels for the normal game (not training levels) level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment if (simulation.isCommunityMaps) { - // level.levels.push(level.communityLevels) level.levels = level.levels.concat(level.communityLevels) - level.levels = shuffle(level.levels); //shuffles order of maps - level.levels.splice(0, level.communityLevels.length); //remove some random levels to make up for adding the community levels simulation.isHorizontalFlipped = false; } else { - level.levels = shuffle(level.levels); //shuffles order of maps + simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally } - // level.levels.splice(Math.floor(level.levels.length * (0.4 + 0.6 * Math.random())), 0, "reservoir"); //add level to the back half of the randomized levels list + level.levels = shuffle(level.levels); //shuffles order of maps with seeded random + level.levels.length = 9 //remove any extra levels past 9 level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, "reservoir"); //add level to the back half of the randomized levels list level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, "reactor"); //add level to the back half of the randomized levels list - // level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.2, level.levels.length * 0.5)), 0, "reactor"); //add level to the back half of the randomized levels list - // level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.7, level.levels.length)), 0, "reactor"); //add level to the back half of the randomized levels list - level.levels.splice(0, 2); //remove 2 levels from the start of the array if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech level.levels.unshift("intro"); //add level to the start of the randomized levels list level.levels.push("gauntlet"); //add level to the end of the randomized levels list @@ -9923,7 +9935,6 @@ const level = { powerUps.spawn(3000, -230, "heal"); // level.difficultyIncrease(60) }, - temple() { simulation.makeTextLog(`temple by Scar1337`); @@ -9977,6 +9988,26 @@ const level = { Rect.fromBounds = function(min, max) { return new Rect(min.x, min.y, max.x - min.x, max.y - min.y); } + Rect.prototype.isCollidingWith = function(other) { + const tc = { + p1: [this.pos.x, this.pos.y], + p2: [this.pos.x + this.width, this.pos.y + this.height] + }; + const oc = { + p1: [other.pos.x, other.pos.y], + p2: [other.pos.x + other.width, other.pos.y + other.height] + }; + + // If one rectangle is on left side of other + if (tc.p1[0] >= oc.p2[0] || oc.p1[0] >= tc.p2[0]) + return false; + + // If one rectangle is above other + if (tc.p1[1] >= oc.p2[1] || oc.p1[1] >= tc.p2[1]) + return false; + + return true; + } return Rect; })(); @@ -9998,7 +10029,7 @@ const level = { } } - function secondRoomBoss(x, y, radius = 25, isDark = false) { + function secondRoomSuckerBoss(x, y, isDark = false, radius = 25) { mobs.spawn(x, y, 12, radius, isDark ? "#000" : "#fff"); let me = mob[mob.length - 1]; me.isBoss = true; @@ -10100,6 +10131,119 @@ const level = { } }; + function secondRoomPlacerBoss(x, y, isDark = false, size = 70) { + mobs.spawn(x, y, isDark ? 3 : 4, size, isDark ? "#0008" : "#fff8"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.isDark = isDark; + + me.stroke = isDark ? "#000" : "#fff"; + me.seeAtDistance2 = 5e6; // Basically just see at all times, in the context it's given + me.accelMag = 0.0001 * simulation.accelScale; + me.collisionFilter.mask = cat.player | cat.bullet; + me.memory = 1600; + me.randomPRNGMult = Math.random() * 500; + + me.attackCycle = 0; + me.maxAttackCycle = isDark ? 90 : 240; + Matter.Body.setDensity(me, 0.006); // extra dense, normal is 0.001 // makes effective life much larger + me.onDeath = function() { + powerUps.spawn(this.position.x + 20, this.position.y, "ammo"); + if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo"); + if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); + }; + me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); + me.do = function() { + // keep it slow, to stop issues from explosion knock backs + if (this.speed > 2) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.95, + y: this.velocity.y * 0.95 + }); + } + if (!(simulation.cycle % this.seePlayerFreq)) { + if (this.distanceToPlayer2() < this.seeAtDistance2) { // ignore cloak + this.locatePlayer(); + if (!this.seePlayer.yes) this.seePlayer.yes = true; + } else if (this.seePlayer.recall) { + this.lostPlayer(); + } + } + this.checkStatus(); + if (this.seePlayer.recall) { + // accelerate towards the player + const forceMag = this.accelMag * this.mass; + const dx = this.seePlayer.position.x - this.position.x + const dy = this.seePlayer.position.y - this.position.y + const mag = Math.sqrt(dx * dx + dy * dy) + this.force.x += forceMag * dx / mag; + this.force.y += forceMag * dy / mag; + this.attackCycle++; + if (this.attackCycle > this.maxAttackCycle) { + this.attackCycle = 0; + secondRoomObstacle(this.position.x, this.position.y, this.isDark, size); + } + } + } + }; + + function secondRoomObstacle(x, y, isDark = false, size = 70) { + mobs.spawn(x, y, isDark ? 3 : 4, size, isDark ? "#0004" : "#fff4"); + let me = mob[mob.length - 1]; + + me.stroke = isDark ? "#000b" : "#fffb"; + me.collisionFilter.mask = isDark ? cat.player | cat.bullet : 0; + me.isDropPowerUp = false; + me.showHealthBar = false; + me.leaveBody = false; + me.timeLeft = 1200; + me.isObstacle = true; + me.damageReduction = isDark ? 0.5 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) : 0; + if (!isDark) { + me.isBadTarget = true; + me.attackCycle = 0; + me.maxAttackCycle = 10; + me.inertia = Infinity; + } + me.do = isDark ? function() { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.95, + y: this.velocity.y * 0.95 + }); + } : function() { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.95, + y: this.velocity.y * 0.95 + }); + if (Rect.fromBounds(this.bounds.min, this.bounds.max).isCollidingWith(Rect.fromBounds(player.bounds.min, player.bounds.max))) { + this.attackCycle++; + this.attackCycle = Math.min(this.attackCycle, 10); + } else { + this.attackCycle--; + this.attackCycle = Math.max(this.attackCycle, 0); + } + if (this.attackCycle > 0) { + ctx.beginPath(); + const vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + if (this.attackCycle >= 10) { + ctx.shadowBlur = 10; + ctx.shadowColor = "rgb(255, 210, 64)"; + } + ctx.fillStyle = `rgba(255, 210, 64, ${this.attackCycle / 15})`; + ctx.fill(); + ctx.shadowBlur = 0; + if (this.attackCycle >= 10) { + DrawTools.lightning(this.position, m.pos, simulation.cycle); + m.damage(0.003 * simulation.dmgScale); + } + } + this.timeLimit(); + } + } + function mobGrenade(...args) { spawn.grenade(...args); const pulseRadius = args[3] || Math.min(550, 250 + simulation.difficulty * 3) @@ -10209,12 +10353,12 @@ const level = { } } me.horizon = function() { - if (this.isInvulnerable) return; + if (this.isInvulnerable) return this.fill = "#f00"; // eventHorizon waves in and out const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)); const charge = this.attackCycle / 90; - this.fill = this.isInvulnerable ? "#f00" : `rgb(${charge * 255},${charge * 255},${charge * 255})`; + this.fill = `rgb(${charge * 255},${charge * 255},${charge * 255})`; // draw darkness ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.6)`; DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI); @@ -11048,7 +11192,7 @@ const level = { if (simulation.cycle % 4 === 0) { let newMobPositions = []; for (const i of mob) { - if (!(i.isMACHO || i.isWIMP)) newMobPositions.push({ x: i.position.x, y: i.position.y }); + if (!(i.isMACHO || i.isWIMP || i.isObstacle)) newMobPositions.push({ x: i.position.x, y: i.position.y }); } mobPositionsQueue.shift(); mobPositionsQueue.push(newMobPositions); @@ -11135,11 +11279,12 @@ const level = { room2() { if (!isInBound(secondRoomBounds)) return; if (templePlayer.room2.spawnInitiatorCycles > Objects.room2Initiator.cap) { + const randomSecondRoomBoss = [secondRoomSuckerBoss, secondRoomPlacerBoss]; if (templePlayer.room2.cycles % 720 === 0 && templePlayer.room2.cycles <= 2160) { const isOdd = Math.floor(templePlayer.room2.cycles / 720) & 1; - secondRoomBoss(-600, -9800, 25, isOdd); - secondRoomBoss(600, -9800, 25, isOdd); - secondRoomBoss(0, -9800, 25, !isOdd); + randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](-600, -9800, isOdd); + randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](600, -9800, isOdd); + randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](0, -9800, !isOdd); } templePlayer.room2.cycles++; if (templePlayer.room2.cycles === 2400) { @@ -11310,6 +11455,7 @@ const level = { DrawHandler.room2Top(); }; }, + // temple() { // simulation.makeTextLog(`temple by Scar1337`); diff --git a/js/player.js b/js/player.js index fd05fc8..cd8ce46 100644 --- a/js/player.js +++ b/js/player.js @@ -898,6 +898,7 @@ const m = { maxEnergy: 1, //can be increased by a tech holdingTarget: null, timeSkipLastCycle: 0, + coupling: 0, // these values are set on reset by setHoldDefaults() fieldFx: 1, fieldJump: 1, @@ -1517,6 +1518,30 @@ const m = { } }, hold() {}, + couplingDescription() { + switch (m.fieldMode) { + case 0: //field emitter + return `gain the effects of all fields` + case 1: //standing wave + return `+20 max energy per coupling` + case 2: //perfect diamagnetism + return `+10° arc per coupling` + case 3: //negative mass + return `+25% defense per coupling` + case 4: //assembler + return `generate 4 energy per second per coupling` + case 5: //plasma + return `+13% damage per coupling` + case 6: //time dilation + return `+20% fire rate per coupling` //movement, jumping, and + case 7: //cloaking + return `remove +10% mob durability per coupling` + case 8: //pilot wave + return `________ per coupling` + case 9: //wormhole + return `+5% duplication per coupling` + } + }, setField(index) { if (isNaN(index)) { //find index by name let found = false @@ -3240,13 +3265,13 @@ const m = { { name: "wormhole", //wormholes attract blocks and power ups
- description: "use energy to tunnel through a wormhole
+4% chance to duplicate spawned power ups
generate 6 energy per second", //
bullets may also traverse wormholes + description: "use energy to tunnel through a wormhole
+3% chance to duplicate spawned power ups
generate 6 energy per second", //
bullets may also traverse wormholes drain: 0, effect: function() { m.fieldMeterColor = "#bbf" //"#0c5" m.eyeFillColor = m.fieldMeterColor - m.duplicateChance = 0.04 + m.duplicateChance = 0.03 m.fieldRange = 0 powerUps.setDupChance(); //needed after adjusting duplication chance diff --git a/js/powerup.js b/js/powerup.js index 8bf17d4..3e53405 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -921,7 +921,7 @@ const powerUps = { if (tech.isExtraBotOption) { const botTech = [] //make an array of bot options for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed()) botTech.push(i) + if (tech.tech[i].isBotTech && tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isRecentlyShown) botTech.push(i) } if (botTech.length > 0) { //pick random bot tech const choose = botTech[Math.floor(Math.random() * botTech.length)]; diff --git a/js/spawn.js b/js/spawn.js index 7cdca1d..d9c8bc9 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -1577,8 +1577,8 @@ const spawn = { hopper(x, y, radius = 30 + Math.ceil(Math.random() * 30)) { mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); let me = mob[mob.length - 1]; - me.accelMag = 0.04; - me.g = 0.0017; //required if using this.gravity + me.accelMag = 0.05; + me.g = 0.0032; //required if using this.gravity me.frictionAir = 0.01; me.friction = 1 me.frictionStatic = 1 @@ -1598,7 +1598,7 @@ const spawn = { const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); this.force.x += forceMag * Math.cos(angle); - this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.07 + 0.06) * this.mass; //antigravity + this.force.y += forceMag * Math.sin(angle) - (Math.random() * 0.06 + 0.1) * this.mass; //antigravity } } else { //randomly hob if not aware of player @@ -1609,7 +1609,7 @@ const spawn = { const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.1 + Math.random() * 0.3); const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI; this.force.x += forceMag * Math.cos(angle); - this.force.y += forceMag * Math.sin(angle) - 0.05 * this.mass; //antigravity + this.force.y += forceMag * Math.sin(angle) - 0.07 * this.mass; //antigravity } } }; @@ -4782,6 +4782,7 @@ const spawn = { me.alpha = 1; //used in drawSneaker // me.leaveBody = false; me.canTouchPlayer = false; //used in drawSneaker + me.isBadTarget = true; me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player me.showHealthBar = false; me.memory = 240; @@ -4801,6 +4802,7 @@ const spawn = { this.healthBar(); if (!this.canTouchPlayer) { this.canTouchPlayer = true; + this.isBadTarget = false; this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player } } @@ -4816,6 +4818,7 @@ const spawn = { ctx.fill(); } else if (this.canTouchPlayer) { //stealth this.canTouchPlayer = false; + this.isBadTarget = true; this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player } }; @@ -4830,6 +4833,7 @@ const spawn = { me.stroke = "transparent"; //used for drawGhost me.alpha = 1; //used in drawGhost me.canTouchPlayer = false; //used in drawGhost + me.isBadTarget = true; // me.leaveBody = false; me.collisionFilter.mask = cat.bullet //| cat.body me.showHealthBar = false; @@ -4857,6 +4861,7 @@ const spawn = { this.healthBar(); if (!this.canTouchPlayer) { this.canTouchPlayer = true; + this.isBadTarget = false; this.collisionFilter.mask = cat.player | cat.bullet } } @@ -4873,6 +4878,7 @@ const spawn = { ctx.fill(); } else if (this.canTouchPlayer) { this.canTouchPlayer = false; + this.isBadTarget = true; this.collisionFilter.mask = cat.bullet; //can't touch player or walls } }; @@ -5055,7 +5061,7 @@ const spawn = { }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 me.timeLeft = 200; - me.g = 0.001; //required if using this.gravity + // me.g = 0.001; //required if using this.gravity me.frictionAir = 0; me.restitution = 0.8; me.leaveBody = false; @@ -5066,7 +5072,7 @@ const spawn = { me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; me.do = function() { - this.gravity(); + // this.gravity(); this.timeLimit(); }; }, @@ -5134,10 +5140,11 @@ const spawn = { me.frictionStatic = 0; me.friction = 0; me.canTouchPlayer = false; //used in drawSneaker + me.isBadTarget = true; me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player - me.memory = 60 //140; - me.fireFreq = 0.006 + Math.random() * 0.002; + me.memory = 30 //140; + me.fireFreq = 0.005 + Math.random() * 0.002 + 0.0005 * simulation.difficulty; //larger = fire more often me.noseLength = 0; me.fireAngle = 0; me.accelMag = 0.0005 * simulation.accelScale; @@ -5183,8 +5190,8 @@ const spawn = { this.torque -= 0.000004 * this.inertia; } else if (this.noseLength > 1.5 && dot > -0.2 && dot < 0.2) { //fire - spawn.sniperBullet(this.vertices[1].x, this.vertices[1].y, 7 + Math.ceil(this.radius / 15), 4); - const v = 10 + 15 * simulation.accelScale; + spawn.sniperBullet(this.vertices[1].x, this.vertices[1].y, 7 + Math.ceil(this.radius / 15), 5); + const v = 10 + 8 * simulation.accelScale; Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v + Math.random(), y: this.velocity.y + this.fireDir.y * v + Math.random() @@ -5216,6 +5223,7 @@ const spawn = { this.healthBar(); if (!this.canTouchPlayer) { this.canTouchPlayer = true; + this.isBadTarget = false; this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player } } @@ -5231,11 +5239,12 @@ const spawn = { ctx.fill(); } else if (this.canTouchPlayer) { this.canTouchPlayer = false; + this.isBadTarget = true this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player } }; }, - sniperBullet(x, y, radius = 9, sides = 4) { //bullets + sniperBullet(x, y, radius = 9, sides = 5) { //bullets mobs.spawn(x, y, sides, radius, "rgb(255,0,155)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; @@ -5924,11 +5933,12 @@ const spawn = { mobs.spawn(x + mag * Math.cos(angle), y + mag * Math.sin(angle), 8, radius, color1); //"rgb(55,170,170)" let me = mob[mob.length - 1]; me.isBoss = true; - me.accelMag = 0.0004 + 0.0002 * Math.sqrt(simulation.accelScale) + me.accelMag = 0.0001 + 0.0004 * Math.sqrt(simulation.accelScale) + // me.accelMag = 0.0004 + 0.0002 * Math.sqrt(simulation.accelScale) me.memory = 250; me.laserRange = 500; Matter.Body.setDensity(me, 0.0022 + 0.00022 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.startingDamageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.startingDamageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.damageReduction = 0 me.isInvulnerable = true @@ -5952,7 +5962,7 @@ const spawn = { this.cycle++ if (this.seePlayer.recall && ((this.cycle % 10) === 0)) { if (this.canFire) { - if (this.cycle > 100) { + if (this.cycle > 120) { this.cycle = 0 this.canFire = false // Matter.Body.setAngularVelocity(this, 0.1) @@ -5963,7 +5973,7 @@ const spawn = { } spawn.seeker(this.vertices[this.closestVertex1].x, this.vertices[this.closestVertex1].y, 6) Matter.Body.setDensity(mob[mob.length - 1], 0.000001); //normal is 0.001 - const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, this.vertices[this.closestVertex1])), -13) + const velocity = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.closestVertex1], this.position)), 15) Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + velocity.x, y: this.velocity.y + velocity.y @@ -5975,7 +5985,7 @@ const spawn = { // x: this.velocity.x + velocity2.x, // y: this.velocity.y + velocity2.y // }); - } else if (this.cycle > 210) { + } else if (this.cycle > 200) { this.cycle = 0 this.canFire = true @@ -6011,6 +6021,8 @@ const spawn = { mag -= 10 let previousTailID = 0 + const damping = 1 + const stiffness = 1 for (let i = 0; i < nodes; ++i) { angle -= 0.15 + i * 0.008 mag -= (i < 2) ? -15 : 5 @@ -6021,7 +6033,7 @@ const spawn = { mob[mob.length - 1].previousTailID = previousTailID previousTailID = mob[mob.length - 1].id } - this.constrain2AdjacentMobs(nodes, Math.random() * 0.06 + 0.01); + this.constrain2AdjacentMobs(nodes, stiffness, false, damping); for (let i = mob.length - 1, len = i - nodes; i > len; i--) { //set alternating colors if (i % 2) { @@ -6034,40 +6046,45 @@ const spawn = { consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - nodes], bodyB: mob[mob.length - 1 - nodes], - stiffness: 0.05 + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - nodes + 1], bodyB: mob[mob.length - 1 - nodes], - stiffness: 0.05 + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - nodes + 2], bodyB: mob[mob.length - 1 - nodes], - stiffness: 0.05 + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); // spawn.shield(me, x, y, 1); }, dragonFlyBoss(x, y, radius = 42) { //snake boss with a laser head - const nodes = Math.min(8 + Math.ceil(0.5 * simulation.difficulty), 40) let angle = Math.PI let mag = 300 - - const color1 = "#00bfd9" //"#f27" mobs.spawn(x + mag * Math.cos(angle), y + mag * Math.sin(angle), 8, radius, "#000"); //"rgb(55,170,170)" let me = mob[mob.length - 1]; me.isBoss = true; - me.accelMag = 0.0009 + 0.0002 * Math.sqrt(simulation.accelScale) - me.memory = 250; - me.laserRange = 400; Matter.Body.setDensity(me, 0.00165 + 0.00011 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger me.startingDamageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.damageReduction = 0 me.isInvulnerable = true + me.accelMag = 0.0001 + 0.0004 * Math.sqrt(simulation.accelScale) + me.memory = 250; + me.flapRate = 0.4 + me.flapArc = 0.2 //don't go past 1.57 for normal flaps + me.wingLength = 150 + me.ellipticity = 0.3 + me.angleOff = 0.4 + me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) for (let i = 0, len = mob.length; i < len; i++) { @@ -6101,15 +6118,10 @@ const spawn = { this.wing(a - Math.PI / 2 + this.angleOff + this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity) this.wing(a + Math.PI / 2 - this.angleOff - this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity) }; - me.flapRate = 0.4 - me.flapArc = 0.2 //don't go past 1.57 for normal flaps - me.wingLength = 150 - me.ellipticity = 0.3 - me.angleOff = 0.4 - //extra space to give head room angle -= 0.1 mag -= 10 let previousTailID = 0 + const nodes = Math.min(8 + Math.ceil(0.5 * simulation.difficulty), 40) for (let i = 0; i < nodes; ++i) { angle -= 0.15 + i * 0.008 mag -= (i < 2) ? -15 : 5 @@ -6119,7 +6131,9 @@ const spawn = { mob[mob.length - 1].previousTailID = previousTailID previousTailID = mob[mob.length - 1].id } - this.constrain2AdjacentMobs(nodes, Math.random() * 0.06 + 0.01); + const damping = 1 + const stiffness = 1 + this.constrain2AdjacentMobs(nodes, stiffness, false, damping); for (let i = mob.length - 1, len = i - nodes; i > len; i--) { //set alternating colors mob[i].fill = `hsla(${160+40*Math.random()}, 100%, ${5 + 25*Math.random()*Math.random()}%, 0.9)` } @@ -6127,19 +6141,22 @@ const spawn = { consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - nodes], bodyB: mob[mob.length - 1 - nodes], - stiffness: 0.05 + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - nodes + 1], bodyB: mob[mob.length - 1 - nodes], - stiffness: 0.05 + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - nodes + 2], bodyB: mob[mob.length - 1 - nodes], - stiffness: 0.05 + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); // spawn.shield(me, x, y, 1); @@ -6147,15 +6164,15 @@ const spawn = { snakeBody(x, y, radius = 10) { mobs.spawn(x, y, 8, radius, "rgba(0,180,180,0.4)"); let me = mob[mob.length - 1]; - me.collisionFilter.mask = cat.bullet | cat.player | cat.mob //| cat.body - me.damageReduction = 0.23 - Matter.Body.setDensity(me, 0.005); //normal is 0.001 + me.collisionFilter.mask = cat.bullet | cat.player //| cat.mob //| cat.body + me.damageReduction = 0.015 + Matter.Body.setDensity(me, 0.0001); //normal is 0.001 // me.accelMag = 0.0007 * simulation.accelScale; me.leaveBody = Math.random() < 0.33 ? true : false; me.showHealthBar = false; me.isDropPowerUp = false; - me.frictionAir = 0.015; + me.frictionAir = 0; me.isSnakeTail = true; me.stroke = "transparent" me.onDeath = function() { @@ -6476,13 +6493,14 @@ const spawn = { } } }, - constrain2AdjacentMobs(nodes, stiffness, loop = false) { + constrain2AdjacentMobs(nodes, stiffness, loop = false, damping = 0) { //runs through every combination of last 'num' bodies and constrains them for (let i = 0; i < nodes - 1; ++i) { consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - i - 1], bodyB: mob[mob.length - i - 2], - stiffness: stiffness + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); } @@ -6491,7 +6509,8 @@ const spawn = { consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - i - 1], bodyB: mob[mob.length - i - 3], - stiffness: stiffness + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); } @@ -6501,19 +6520,22 @@ const spawn = { consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - 1], bodyB: mob[mob.length - nodes], - stiffness: stiffness + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - 2], bodyB: mob[mob.length - nodes], - stiffness: stiffness + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); consBB[consBB.length] = Constraint.create({ bodyA: mob[mob.length - 1], bodyB: mob[mob.length - nodes + 1], - stiffness: stiffness + stiffness: stiffness, + damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); } diff --git a/js/tech.js b/js/tech.js index 7d02ff7..efcef70 100644 --- a/js/tech.js +++ b/js/tech.js @@ -306,7 +306,41 @@ const tech = { } } }, - tech: [{ + tech: [ + // { + // name: "field coupling", + // descriptionFunction() { + // return `+1 field coupling (${m.fieldUpgrades[m.fieldMode].name})
${ m.couplingDescription()}` + // }, + // // isFieldTech: true, + // maxCount: 9, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return (build.isExperimentSelection || powerUps.research.count > 1) + // }, + // requires: "", + // // researchUsed: 0, + // // couplingToResearch: 0.1, + // effect() { + // m.coupling++ + // // while (powerUps.research.count > 0) { + // // powerUps.research.changeRerolls(-1) + // // this.researchUsed++ + // // m.coupling += this.couplingToResearch + // // } + // }, + // remove() { + // m.coupling = 0 + // // if (this.count) { + // // m.coupling -= this.researchUsed * this.couplingToResearch + // // powerUps.research.changeRerolls(this.researchUsed) + // // this.researchUsed = 0 + // // } + // } + // }, + { name: "ordnance", description: "double the frequency of finding guntech
spawn a gun", maxCount: 1, @@ -644,7 +678,7 @@ const tech = { allowed() { return !tech.isEnergyHealth //(tech.crouchAmmoCount || tech.isCrouchRegen) && }, - requires: "not mass-energy", //inductive coupling, desublimated ammunition, + requires: "not mass-energy", effect() { tech.isTurret = true }, @@ -2428,7 +2462,7 @@ const tech = { } }, { - name: "inductive coupling", + name: "inductive charging", description: "if crouched +600% passive energy generation
if not crouched energy generation is disabled", maxCount: 1, count: 0, @@ -2511,7 +2545,7 @@ const tech = { allowed() { return !tech.isCrouchRegen }, - requires: "not inductive coupling", + requires: "not inductive charging", effect() { tech.isDamageAfterKillNoRegen = true; m.regenEnergy = function() { @@ -5788,21 +5822,11 @@ const tech = { requires: "foam", effect() { tech.isFoamPressure = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "foam") { - b.guns[i].chooseFireMethod() - break - } - } + b.guns[8].chooseFireMethod() }, remove() { tech.isFoamPressure = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "foam") { - b.guns[i].chooseFireMethod() - break - } - } + b.guns[8].chooseFireMethod() } }, { @@ -6436,7 +6460,7 @@ const tech = { //************************************************** //************************************************** field //************************************************** tech - //************************************************** + //************************************************** { name: "zero point energy", description: `use ${powerUps.orb.research(2)}
+100 maximum energy`, @@ -7032,7 +7056,7 @@ const tech = { { name: "plasma-bot", link: `plasma-bot`, - description: "remove your field to build a bot
that uses energy to emit plasma", + description: "remove your field to build a bot
that uses energy to emit plasma", isFieldTech: true, maxCount: 1, count: 0, diff --git a/style.css b/style.css index 18a785a..612736f 100644 --- a/style.css +++ b/style.css @@ -38,7 +38,7 @@ input { border: 1px #333 solid; border-radius: 4px; /* margin: 0.2em; */ - width: 38px; + /* width: 38px; */ } a { @@ -924,6 +924,34 @@ summary { } } +.color-coupling { + /* animation: coupling 1000ms linear infinite alternate; */ + /* animation: vibrate 500ms linear infinite alternate; + display: inline-block; */ + /* text-shadow: 0px 0px 2px #0cf; */ + text-shadow: 0px 0px 1.5px #0cf; + /* color: hsl(255, 82%, 59%); */ + letter-spacing: 1px; + font-weight: 100; +} + +/* @keyframes coupling{ + 0%{ + letter-spacing: 0.5px; + } + 100%{ + letter-spacing: 1px; + } + } + +@keyframes vibrate{ + 0% { + transform: translateY(-0.25px) + } + 100% { + transform: translateY(0.25px) + } +} */ .box { padding: 3px 8px 3px 8px; @@ -956,7 +984,6 @@ summary { animation: textColor 3s linear infinite; } - @keyframes fieldColorCycle { 0% { background-color: rgb(255, 255, 255) diff --git a/todo.txt b/todo.txt index 8fc7cad..76e01cd 100644 --- a/todo.txt +++ b/todo.txt @@ -1,19 +1,37 @@ ******************************************************** NEXT PATCH ************************************************** -finalBoss black hole phase: - makes 100% more bullets - grows and shrinks smoothly near start and end of phase - does 20% more damage to player - -historyBoss has a "fun" graphical effect -acetone peroxide has 50 -> 40% less explosion defense - -bug fix: timeSkip graphic glitch - there is a chance to cause other bugs with timeSkip effects +new setting: level ban list +bug fix: removed a command to preventDefault on space key, this might break something else +new boss added to level - Temple +snake tails have a lower mass and whip around a bit +auto targeting no longer works for stealth mobs + snipers, sneakers, ghosters +snipers fire more often at high difficulty, but bullets move slower at all difficulties +hoppers have move gravity, so it feels like they are hopping a bit faster + *********************************************************** TODO ***************************************************** +tech: field coupling - +1 field coupling, coupling is a new stat that provides different buffs for each field + you can see your coupling in the pause menu +make field coupling a stat that shows in pause menu and is effected by other tech? + names: fine-structure constant, strongly coupled, Vibronic coupling + tech: convert all research into "coupling" + tech: +x% field coupling, your field changes randomly every y seconds + tech: coupling starts at 200%, but decays when the field is in use, coupling recharges when the field is not in use + some fields aren't used much (that's ok?) -screen shake effect - add random numbers to ctx.translate +buffing your deflecting for 1 second after pressing the field button sounds cool + 2 second cooldown on the effect to prevent spamming it + buff: giving energy or doing damage makes sense + maybe this could be a rework for bremstralung + +greatly increase walking speed + not mid air control? + for time dilation field + make jumping normal + +after taking damage explode while invulnerable + scale explosion radius with damage quantum immortality: send you to a new tab after you die with a random load out basically everything is the same as it is now, but you switch tabs @@ -25,17 +43,29 @@ tech: get sent to a new tab that closes in 3 minutes count guns, field, tech and give random stuff on new tab i-frame instead of tab? -field efficiency - Give each field an extra numerical effect, and add a 'field efficiency' stat that increases it - field numbers - standing wave, molecular assembler: energy efficiency - plasma, metamaterial: damage - time dilation: movement, jump, fire rate - negative mass: defense - diamagnetism: arc length - wormhole: (i dislike it but idk of other things) dupe chance - tech: +x% field efficiency, your field changes randomly every y seconds - tech: starts at 200%, but decays when the field is in use, efficiency recharges when the field is not in use +reduce the amount of research and nerf anti randomization tech + increase possible synergies that go nuts +tech expansion: field coupling also expands each fields in different ways + how to make the description work + change description based on your current field? + perfect diamagnetism moves forward when you hold down the shield + it's great, but maybe annoying? + maybe only with crouch? + perfect diamagnetism just replace or increase Messier effect + time dilation drains 1/2 as much energy when paused + grow plasma torch as you hold it down + negative mass effects much more space + needs more benefit? + reduces the cloaking vision effect? + needs more benefit? + +tech: missiles explode a 2nd time after 1/2 a second (with a slightly different position determined by original velocity) + +1st ionisation energy should scale with heath powerup efficiency +The tech that makes blocks that fall into a wormhole give energy should scale with block size, with the same formula as tokomak + +junk suggestion: useless machine - ejects itself and removes itself from the item pool bug blocks and power ups falling through map always foam gun (4-5 times) @@ -221,20 +251,6 @@ pause time like invariant for other things... charging railgun charging anything? -tech expansion: should also make other fields do things - how to make the description work - change description based on your current field? - perfect diamagnetism moves forward when you hold down the shield - it's great, but maybe annoying? - maybe only with crouch? - perfect diamagnetism just replace Messier effect - time dilation drains 1/2 as much energy when paused - grow plasma torch as you hold it down - negative mass effects much more space - needs more benefit? - reduces the cloaking vision effect? - needs more benefit? - guntech fire a bullet that fires nail fragments after 1s in the same direction as the original bullet like overwatch roadhog @@ -480,8 +496,6 @@ auto-gon - auto battler with n-gon mob AI and tech similar research and tech system to n-gon some mobs can fire player weapons -harpoon grappling hook - basic effect is working, but needs work before it becomes fun - tech: relativistic jets: small particles that shot out from front and back poles and end up in a wide variety of spirals slow trickle when charging and several more when firing @@ -493,16 +507,11 @@ adapt the cloaking graphics to make a flashlight cone visual effect be nice if block throwing had a projected path -JUNK tech: planetesimals game inside n-gon -https://codepen.io/lilgreenland/pen/jrMvaB?editors=0010 - Pilot wave tech Energy use is increased, but you can now shape blocks using pressure Grouping blocks will merge them into a massive ball Size, density is determined by total mass -look into 360 matter wave lag - aoe effect pushes mobs away, then rapidly pulls them in for mines?