diff --git a/mod-list.html b/mod-list.html index 2298a079..0b5c089c 100644 --- a/mod-list.html +++ b/mod-list.html @@ -288,7 +288,7 @@ amogus.jsAdds a small amogus structureAlice citybuilding.jsAdds seeds that create miniature buildings and other city-related itemsSquareScreamYT collab_mod.jsCreated by multiple people, adds random thingsmrapple, ilikepizza, stefanblox -Doom Mod (Unreleased)As seen on TikTok - Not yet available!ggod +doom.jsAs seen on TikTok - Select the Doom element to start, WASDggod elem3.jsAdds all elements and combinations from Elemental 3 [Very Large]Sophie fools+.jsimproves and makes fools.js EXTREMELY annoying.SquareScreamYT funny elements 2022-11-15.jsAdds a few curated randomly-generated elementsAlice diff --git a/mods/Science_mod.js b/mods/Science_mod.js index 7f75597b..3b4b9a49 100644 --- a/mods/Science_mod.js +++ b/mods/Science_mod.js @@ -1,8 +1,14 @@ // Science mod for Sandboxels // (Inspired by survival.js) -// Build 26 (version alpha 0.0.26) -console.log("version alpha 0.0.26") -console.log("build 26") +// Build 37 (version alpha 0.0.37) +console.log("Version alpha 0.0.37") +console.log("Build 37") +console.log("If you see something that is alumium and not aluminium, just dm me on discord (this is a british/welsh mod aight)") +console.log("THIS IS NOT DONE") +// You'll see a lot more of my school account +// Read comment at line 1523 for information on what I mean by null/NULL +// This used to be a full time thing, but now it's basically just half-time now. +// It has been confirmed, I am getting a pc on 25/06/2024 :D (I'm also going to be animating for my friends movies that are coming out soon) // If there is anything you want to suggest or there's a bug then just dm me on discord (@a_british_proto) // I've decided to start doing this project on my schools computer's (because they don't crash all the time) so there will be long delays in updates, sorry! // Todo: @@ -1537,3 +1543,249 @@ substance.Aluminum_Chlorate_Nonahydrate= { state: "null", hidden:true } + +substance.Aluminum_Perchlorate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Potassium_Tetrachloroaluminate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +// Finally one I can actually do! +substance.Lithium_Tetrachloroaluminate = { + behavior: behaviors.WALL, + color: "FFFFFF", + category: "lands", + state: "solid", + hidden:true +} + +// Nvm, back to null +substance.Sodium_Tetrachloroaluminate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Titanium_Chloride_Aluminum_Chloride = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Cesium_Fluoroaluminate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Aluminum_Cesium_Sulfate_Dodecahydrate = { + behavior:behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +// Tf is this, I'm probs going to delete it later as i have no idea what it is +substance.Devarda’s_Alloy = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null",# + hidden:true +} + +substance.Lithium_Aluminum_Deuteride = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +// Finally another one! Thank the ptable.com gods! :skull: +substance.Aluminum_Floride = { + behavior: behaviors.WALL, + color: "FFFFFF", + category: "lands", + state: "solid", + hidden:true +} + +// AAAAAAAAAAAAAAAAAAAAAAAAAAA +substance.aluminum_Fluoride_Trihydrate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +// aight, i give up on the comments now but this is one that I can actually do +substance.Ammonium_Hexafluoroaluminate = { + behavior: behaviors.POWDER, + color: "FFFFFF", + category: "lands", + state: "powder", + hidden:true +} + +substance.Potassium_Hexafluoroaluminate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Sodium_Hexafluoroaluminate = { + behavior: behaviors.POWDER, + color: "FFFFFF", + category: "lands", + state: "powder", + hidden:true +} + +substance.Sodium_Phosphoaluminate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true, +} + +substance.Aluminum_Hypophosphite = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Aluminum_Hydroxide = { + behavior: behaviors.POWDER, + color: "FFFFFF", + category: "lands", + state: "powder", + hidden:true +} + +substance.Aluminum_Metasophate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Lithium_Aluminium_Hydride = { + behavior: behaviors.WALL, + color: "FFFFFF", + category: "lands", + state: "solid", + hidden:true +} + +substance.Sodium_Aluminium_Hydride = { + behavior: behaviors.WALL, + color: "FFFFFF", + category: "lands", + state: "solid", + hidden:true +} + +substance.Strontium_Lanthanum_Aluminate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Aluminum_Nitrate_Nonahydrate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Aluminium_Nitrate = { + behavior: behaviors.WALL, + color: "FFFFFF", + category: "lands", + state: "solid", + hidden:true +} + +substance.Potassium_Aluminum_Sulfate_Dodecahydrate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Alum = { + behavior: behaviors.WALL, + color: "FFFFFF", + category: "lands", + state: "solid", + hidden:true +} + +substance.Kalinite = { + behavior: behaviors.WALL, + color: "FFFFFF", + category: "lands", + state: "solid", + hidden:true +} + +substance.Potassium_Alum = { + behavior: behaviors.POWDER, + color: "FFFFFF", + category: "lands", + state: "powder", + hidden:true +} + +substance.Sodium_Alum = { + behavior: behaviors.POWDER, + color: "FFFFFF", + category: "lands", + state: "powder", + hidden:true +} + +substance.Aluminum_Ammonium_Sulfate_Dodecahydrate = { + behavior: behaviors.NULL, + color: "000000", + category: "null", + state: "null", + hidden:true +} + +substance.Ammonium_Aluminium_Sulfate = { + behavior: beahviors.WALL, + color: "FFFFFF", + category: "lands", + state: "solids", + hidden:true +} diff --git a/mods/a_mod_by_alice.js b/mods/a_mod_by_alice.js index 8e1235df..9149d9ff 100644 --- a/mods/a_mod_by_alice.js +++ b/mods/a_mod_by_alice.js @@ -171,6 +171,15 @@ try { throw new TypeError(`Unexpected type: ${typeof(stringOrArray)}`); }; }; + //Shuffle not-in-place + function arrayToShuffled(array) { + var _array = structuredClone ? structuredClone(array) : JSON.parse(JSON.stringify(array)); + for (let i = _array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [_array[i], _array[j]] = [_array[j], _array[i]]; + }; + return _array + }; //Checks //Element exists in the elements object function elementExists(elementName) { @@ -391,6 +400,10 @@ try { function addTwoNumbers(number1,number2) { //reducer return number1 + number2 } + //Linear interpolation + function lerp(a,b,t) { + return (b * t) + (a * (1 - t)) + }; //Logistic curve //x = real number //L = maximum value @@ -1747,6 +1760,16 @@ try { function getCirclePixels(x,y,radius) { return circleCoords(x,y,radius).map(coordinates => pixelMap[coordinates.x]?.[coordinates.y]).filter(function(pixelOrUndefined) { return typeof(pixelOrUndefined) == "object" }) }; + function getPixelsInRegion(x1,y1,x2,y2) { + var result = []; + for(var x = x1; x <= x2; x++) { + for(var y = y1; y <= y2; y++) { + var p = pixelMap[x]?.[y]; + if(p && !(p.del)) { result.push(p) } + } + }; + return result + }; function getPixelMooreNeighbors(pixel) { var coordsToCheck = mooreDonutCoords.map(function(offsets) { return {x: offsets[0]+pixel.x, y: offsets[1]+pixel.y} } ); var neighbors = []; @@ -2323,7 +2346,11 @@ td.inputCell { sweepingLaserRotationSpeed: -0.03, sweepingLaserBeamLength: 10, sweepingLaserBeamTemperature: 2000, - sweepingLaserBeamBrevity: 5 + sweepingLaserBeamBrevity: 5, + noteBlockFrequency: 440, + noteBlockLength: 1, + noteBlockVolume: 1, + noteBlockDelay: 0 }; hidePropertySetter(); @@ -4056,15 +4083,19 @@ color1 and color2 spread through striped paint like dye does with itself. col quickSlDetectorLastKeys = []; }; }); + /*if(urlParams.get("pause") !== null) { + paused = true; + document.getElementById("pauseButton").setAttribute("on","true") + };*/ gameLoaded = true; }; //MORE CONFIGURABLE EXPLOSIONS (explodeAtPlus) ## velocityBlacklist = []; function explodeAtPlus(x,y,radius,firee="fire",smokee="smoke",beforeFunction=null,afterFunction=null,changeTemp=true) { - var message = "Explosion "; + //var message = "Explosion "; var pixel = pixelMap[x]?.[y]; - if(pixel) { message += `of ${pixel.element} ` }; - message += `with radius ${radius} at (${x},${y})`; + //if(pixel) { message += `of ${pixel.element} ` }; + //message += `with radius ${radius} at (${x},${y})`; // if fire contains , split it into an array if(firee !== null) { if (firee.indexOf(",") !== -1) { @@ -4078,6 +4109,7 @@ color1 and color2 spread through striped paint like dye does with itself. col }; var coords = circleCoords(x,y,radius); var power = radius/10; + changePressure(x,y,radius ** 2,operationType="+",true); //for (var p = 0; p < Math.round(radius/10+1); p++) { for (var i = 0; i < coords.length; i++) { var fire = firee; @@ -5461,6 +5493,7 @@ color1 and color2 spread through striped paint like dye does with itself. col } //FIND MODE, PIXEL PROPERTIES LINKED TO SPECIAL CODE, CONFIGURABLE VISUAL DISTORTION AND VISUAL PIXEL SHAPE SETTINGS (acid_and_shapes.js) ## //two separate things i.e. not "pixel properties linked to special code, configurable visual distortion, and visual pixel shape settings" though there's basically no semantic difference + //And also all other changes to drawPixels var style = document.createElement('style'); style.type = 'text/css'; style.id = 'findStatusStylesheet'; @@ -5740,6 +5773,8 @@ color1 and color2 spread through striped paint like dye does with itself. col } }; + console.log("1/8 loaded") //True 1.8 is inside one viewColorFunctions[2] + hiding = false runAfterAutogen(function() { @@ -5806,6 +5841,35 @@ color1 and color2 spread through striped paint like dye does with itself. col ctx.fillRect(0, 0, canvas.width, canvas.height); } if(!hiding) { + if(settings.dopressure && settings.drawpressure) { + for(var x = 0; x < pressureMap.length; x++) { + for(var y = 0; y < pressureMap[x].length; y++) { + var pressureValue = pressureMap[x][y]; + if(typeof(pressureValue) == "number") { + var pressureSign = Math.sign(pressureValue); + pressureValue = bound(Math.abs(pressureValue),0,255) / 255; + switch(pressureSign) { + case -0: + case 0: + ctx.fillStyle = `rgb(0,0,0)`; + break + case 1: + ctx.fillStyle = `rgb(255,0,0)`; + break + case -1: + ctx.fillStyle = `rgb(0,0,255)`; + break + default: // covers NaN, since NaN != NaN + ctx.fillStyle = `rgb(255,255,0)`; + break + }; + ctx.globalAlpha = isNaN(pressureSign) ? 0.5 : pressureValue; + ctx.fillRect(x*pixelSize*pressureCellSize, y*pixelSize*pressureCellSize, pixelSize*pressureCellSize, pixelSize*pressureCellSize) + } + } + } + ctx.globalAlpha = 1; + }; var pixelDrawList = pixelsFirst.concat(pixelsLast); for (var i = 0; i < pixelDrawList.length; i++) { var pixel = pixelDrawList[i]; @@ -6135,6 +6199,48 @@ color1 and color2 spread through striped paint like dye does with itself. col rankineSettingSpan.appendChild(settingInput); rankineSettingSpan.appendChild(newHelpMark); acidSettingSpan.after(rankineSettingSpan); + var pressureSettingSpan = document.createElement("span"); + pressureSettingSpan.setAttribute("setting","dopressure"); + pressureSettingSpan.setAttribute("title","Default: OFF"); + pressureSettingSpan.classList.add("setting-span","multisetting"); + var settingInput = document.createElement("input"); + settingInput.setAttribute("type","button"); + settingInput.setAttribute("value",'Pressure simulation'); + settingInput.setAttribute("state","0"); + settingInput.classList.add("toggleInput"); + settingInput.setAttribute("onclick","toggleInput(this,'dopressure',false)"); + var options = { + "false": "Disabled", + "true": "Enabled" + }; + var newHelpMark = document.createElement("span"); + newHelpMark.setAttribute("title","Simplified pressure simulation (which may lag)."); + newHelpMark.classList.add("helpMark"); + newHelpMark.innerText = "?"; + pressureSettingSpan.appendChild(settingInput); + pressureSettingSpan.appendChild(newHelpMark); + rankineSettingSpan.after(pressureSettingSpan); + var showPressureSettingSpan = document.createElement("span"); + showPressureSettingSpan.setAttribute("setting","drawpressure"); + showPressureSettingSpan.setAttribute("title","Default: OFF"); + showPressureSettingSpan.classList.add("setting-span","multisetting"); + var settingInput = document.createElement("input"); + settingInput.setAttribute("type","button"); + settingInput.setAttribute("value",'Show pressure'); + settingInput.setAttribute("state","0"); + settingInput.classList.add("toggleInput"); + settingInput.setAttribute("onclick","toggleInput(this,'drawpressure',false)"); + var options = { + "false": "Disabled", + "true": "Enabled" + }; + var newHelpMark = document.createElement("span"); + newHelpMark.setAttribute("title","Draw pressure (only applies if pressure simulation is enabled)."); + newHelpMark.classList.add("helpMark"); + newHelpMark.innerText = "?"; + showPressureSettingSpan.appendChild(settingInput); + showPressureSettingSpan.appendChild(newHelpMark); + pressureSettingSpan.after(showPressureSettingSpan); var sizeSetting = document.querySelector('span[setting="pixelsize"]'); var sizeDropdown = sizeSetting.querySelector("select"); sizeDropdown.setAttribute("onchange","var size = (this.value === 'null' ? null : parseFloat(this.value)); console.log(size); if((size >= 0.05) && (size <= 194.73749999999999) && (size !== null) && (size !== false) && !(isNaN(size))) { console.log(size); setSetting('pixelsize',size);this.nextElementSibling.innerText='Reset Scene' }"); @@ -9207,6 +9313,7 @@ color1 and color2 spread through striped paint like dye does with itself. col //if(pixelTicks == pixel.start) { console.log("this range",pixel.range) }; if(pixel.range <= 0) { deletePixel(pixel.x,pixel.y); return }; var range = (pixel.range ?? (ambaPlaceProperties?.blackHoleRange ?? 15)) * 2; + if(settings.dopressure) { changePressure(pixel.x,pixel.y,10 + ((range / 2) ** 2),"-",true) }; var targets = mouseLikeRange(pixel.x,pixel.y,range,"circle",true); shuffleArray(targets); for (var i = 0; i < targets.length; i++) { @@ -9256,7 +9363,7 @@ color1 and color2 spread through striped paint like dye does with itself. col var taxicabDistance = Math.abs(newPixel.x - pixel.x) + Math.abs(newPixel.y - pixel.y); if((taxicabDistance <= 3) && (taxicabDistance > 0)) { pixel.temp += (newPixel.temp - (settings.abszero ?? 273.15)); - if(["amba_black_hole","amba_white_hole"].includes(newPixel.element) && (newPixel.range ?? 15) > 0) { + if(["amba_black_hole","amba_white_hole"].includes(newPixel.element) && ((newPixel.range ?? 15) > 0) && (newPixel !== pixel)) { //console.log("adding range on tick",pixelTicks); pixel.range ??= (ambaPlaceProperties?.blackHoleRange ?? 15); var rangeChange = (newPixel.range ?? (ambaPlaceProperties?.blackHoleRange ?? 15)); @@ -9378,6 +9485,29 @@ color1 and color2 spread through striped paint like dye does with itself. col maxSize: 1 }; + elements.pus = { + color: "#bfba71", + behavior: behaviors.LIQUID, + reactions: { + "water": { elem1:["pus","pus","pus","pus","pus","pus","dirty_water"], elem2:"dirty_water", chance:0.01 }, + "blood": { elem1:["pus","pus","pus","pus","pus","pus","infection"], elem2:"infection", chance:0.01 }, + "poison": { elem1:"bio_ooze", elem2:"bio_ooze", chance:0.2 }, + "bio_ooze": { elem1:"bio_ooze", chance:0.2 }, + "frog": { elem2:"rotten_meat", chance:0.005 }, + "fish": { elem2:"rotten_meat", chance:0.005 }, + "meat": { elem2:"rotten_meat", chance:0.005 }, + "alcohol": { elem1:"dirty_water", chance:0.2 } + }, + viscosity: 30, + tempHigh: 124.55, + stateHigh: ["plague","stench","steam","steam","steam","salt"], + tempLow: -2, + category:"liquids", + hidden: true, + state: "liquid", + density: 1100, + stain: 0.08 + }; //ASSORTED RAINBOW VARIANTS ## elements.concoction.reactions.diorite_gravel = { elem1: "static", elem2: null @@ -11155,6 +11285,7 @@ color1 and color2 spread through striped paint like dye does with itself. col density: 182, temp: -20, } + console.log("1/4 loaded") //Volatile Roseyiede elements.explosive_roseyiede = { color: "#986118", @@ -13138,6 +13269,9 @@ color1 and color2 spread through striped paint like dye does with itself. col version: 1, enabledMods: localStorage.enabledMods }; + if(settings.dopressure) { + simulationState.pressureMap = pressureMap + }; for(i = 0; i < simulationState.pixelMap.length; i++) { var column = simulationState.pixelMap[i]; for(j = 0; j < column.length; j++) { @@ -13371,6 +13505,7 @@ color1 and color2 spread through striped paint like dye does with itself. col } saveSettings }; + if((settings.dopressure) && json.pressureMap) { pressureMap = json.pressureMap }; //enabledMods handling { var enMods = "[]"; if(typeof(json.enabledMods) !== "undefined") { @@ -13498,6 +13633,9 @@ Pixel size (rendering only): (Use if the save looks cut o if(enabledMods.includes("mods/betterStats.js") && typeof(realTps) !== "undefined") { stats += "" + realTps + "tps" }; //i'm sorry but there's no other way to add compatibility //THAT CODE WAS MADE BY MOLLTHECODER FROM THEIR betterStats.js MOD stats += "" + pixelTicks+""; + if((settings.dopressure) && (typeof(width) == "number") && (!(outOfBounds(mousePos.x,mousePos.y)))) { + stats += "P:" + getPressureAtPixelCoords(mousePos.x,mousePos.y).toFixed(2).replace(/\.?0+$/,"")+""; + }; if ((typeof pixelMap).length === 9) { return; } if (pixelMap[mousePos.x] !== undefined) { var currentPixel = pixelMap[mousePos.x][mousePos.y]; @@ -16336,13 +16474,13 @@ Pixel size (rendering only): (Use if the save looks cut o return elements[e].category == "life"; }) }); - elements.bioooze = { + elements.bio_ooze = { //a.k.a. the water in the River Thames during July and August of 1858 + name: "Bio-Ooze", color: ["#53FF4F", "#53FF4F", "#06DE00", "#04A600", "#036E00"], behavior: behaviors.LIQUID, tempHigh: 100, stateHigh: ["plague","slime","steam","poison"], - //tempLow: -4, - //stateLow: "bioooze_ice", + tempLow: -4, category: "liquids", heatCapacity: 3.52, //unimplemented feature name: "bio-ooze", @@ -16353,34 +16491,34 @@ Pixel size (rendering only): (Use if the save looks cut o //"elder_fluid": { "elem1":"corrupt_slime" }, //acid should be sulfuric acid and product should be wastestone //"mercury": { "elem1":"liquid_protocite" }, //acid should be sulfuric acid and product should be wastestone //"blue_grav_liquid": { "elem1":"blue_grav_liquid" }, //bgl would set gravity to upwards gravity - "blood": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "infection" }, + "blood": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "infection" }, "soap": { "elem1": "slime", "chance": 0.02 }, - "plant": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "grass": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "algae": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "mushroom_spore": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "lichen": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "rat": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" }, - "frog": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" }, - "fish": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" }, - "bird": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" }, - "head": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" }, - "body": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" }, - "ant": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" }, - "worm": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" }, - "fly": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" }, - "firefly": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" }, - "bee": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" }, - "slug": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "dust" }, - "snail": { "elem1": ["bioooze","bioooze","bioooze","bioooze","poison","slime",null], "elem2": "calcium" }, - "sapling": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "root": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "flower_seed": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "pistil": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "petal": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "grass_seed": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "dead_plant" }, - "meat": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "rotten_meat" }, - "wood": { "elem1": ["bioooze","bioooze","poison","slime",null], "elem2": "sawdust", "chance": 0.25 } + "plant": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "grass": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "algae": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "mushroom_spore": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "lichen": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "rat": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "rotten_meat" }, + "frog": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "rotten_meat" }, + "fish": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "rotten_meat" }, + "bird": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "rotten_meat" }, + "head": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "rotten_meat" }, + "body": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "rotten_meat" }, + "ant": { "elem1": ["bio_ooze","bio_ooze","bio_ooze","bio_ooze","poison","slime",null], "elem2": "dust" }, + "worm": { "elem1": ["bio_ooze","bio_ooze","bio_ooze","bio_ooze","poison","slime",null], "elem2": "dust" }, + "fly": { "elem1": ["bio_ooze","bio_ooze","bio_ooze","bio_ooze","poison","slime",null], "elem2": "dust" }, + "firefly": { "elem1": ["bio_ooze","bio_ooze","bio_ooze","bio_ooze","poison","slime",null], "elem2": "dust" }, + "bee": { "elem1": ["bio_ooze","bio_ooze","bio_ooze","bio_ooze","poison","slime",null], "elem2": "dust" }, + "slug": { "elem1": ["bio_ooze","bio_ooze","bio_ooze","bio_ooze","poison","slime",null], "elem2": "dust" }, + "snail": { "elem1": ["bio_ooze","bio_ooze","bio_ooze","bio_ooze","poison","slime",null], "elem2": "calcium" }, + "sapling": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "root": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "flower_seed": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "pistil": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "petal": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "grass_seed": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "dead_plant" }, + "meat": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "rotten_meat" }, + "wood": { "elem1": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "sawdust", "chance": 0.25 } }, /*reactions: { "dirt": { // React with (water reacts with dirt to make mud) @@ -16799,6 +16937,7 @@ Pixel size (rendering only): (Use if the save looks cut o category: "gases", state: "gas" }, + console.log("3/8 loaded"); elements.cold_ash_cloud = { color: ["#af8f50","#ab9c50","#af6d50"], behavior: [ @@ -19227,7 +19366,7 @@ Pixel size (rendering only): (Use if the save looks cut o conduct: 0.23, }; elements.haseulite_gas = { - color: ["#ffff9d", _cc.w.h, "#e9ffe6", "#ffffe5"], + color: ["#ffff9d", _cc.w.h, "#e9ffe6", "#ffffe5"], fireColor: ["#08a953", "#2ea332", "#d1e0d3"], properties: { oldColor: null @@ -19376,7 +19515,7 @@ Pixel size (rendering only): (Use if the save looks cut o conduct: 0.22, }; elements.heejinite_gas = { - color: ["#fffab8", "#ffdab3", "#ffd1d1", "#ffc4df", "#ffb0eb"], + color: ["#fffab8", "#ffdab3", "#ffd1d1", "#ffc4df", "#ffb0eb"], fireColor: ["#a9085e", "#a32e61", "#fca7c6"], properties: { oldColor: null @@ -19652,7 +19791,7 @@ Pixel size (rendering only): (Use if the save looks cut o conduct: 0.34, }; elements.jinsoulite_gas = { - color: ["#c0f0ef", "#c2c1db", "#c0bff5", "#cdcce6"], + color: ["#c0f0ef", "#c2c1db", "#c0bff5", "#cdcce6"], behavior: [ "XX|CR:steam%0.5|XX", "CR:steam%0.5|XX|CR:steam%0.5", @@ -19832,7 +19971,7 @@ Pixel size (rendering only): (Use if the save looks cut o conduct: 0.22, }; elements.yvesite_gas = { - color: ["#e34070", "#d13060", "#c2234a", "#db4866"], + color: ["#e34070", "#d13060", "#c2234a", "#db4866"], fireColor: ["#b5103f", "#ab3254", "#cc2157", "#ba0936"], behavior: behaviors.GAS, state: "gas", @@ -20154,8 +20293,8 @@ Pixel size (rendering only): (Use if the save looks cut o stain: 0.04 }; elements.vivite_gas = { - color: ["#ffedfe", "#ffe0fd", "#ffd9f9", "#ffd1f0", "#ffccdf"], - colorOn: ["#eec7fc", "#f5b1fc", "#faa2f1", "#fa93c3", "#ff99b1"], + color: ["#ffedfe", "#ffe0fd", "#ffd9f9", "#ffd1f0", "#ffccdf"], + colorOn: ["#eec7fc", "#f5b1fc", "#faa2f1", "#fa93c3", "#ff99b1"], fireColor: ["#ff66ba", "#ff85ef", "#ff99f7"], tick: function(pixel) { if(Math.random() < 0.032 && exposedToAir(pixel)) { @@ -22914,6 +23053,7 @@ Pixel size (rendering only): (Use if the save looks cut o }; rockClouds.push(rockCloudName); }; + console.log("1/2 loaded") //true halfway point is inside newIgneousCompositionFamily. function standaloneBrokenFormMaker(elementName,suffixWithoutUnderscore,addBreakIntoToSourceElement=false,category=null,density=null,tempHigh=null,stateHigh=null,breakInto=null) { var newName = elementName + "_" + suffixWithoutUnderscore; elements[newName] = { @@ -26102,7 +26242,7 @@ Pixel size (rendering only): (Use if the save looks cut o //Metamorphism will be driven using solely temperature. //Pressure simulation, due to how the game is coded, will be limited to requiring the rock to be surrounded. elements.slate = { - color: ["#787B80", "#535557", "#695E58", "#696969", "#6B5D5B"], + color: ["#787B80", "#535557", "#695E58", "#696969", "#6B5D5B"], tempHigh: 200, stateHigh: "felsic_magma", category: "solid rock", @@ -28087,6 +28227,7 @@ Make sure to save your command in a file if you want to add this preset again.` desc: "Click here or press Shift+1 to open the command prompt.", category:"special", }; + console.log("5/8 loaded"); //REPLACER TOOL ## changeTo = "sand"; document.addEventListener("keydown", function(e) { //change prompt listener @@ -28593,7 +28734,7 @@ Make sure to save your command in a file if you want to add this preset again.` elements.bless.reactions.toxin = { elem2: "antidote" }; elements.bless.reactions.dead = { elem2: null }; elements.bless.reactions.brain = { elem2: null }; - elements.bless.reactions.bioooze = { elem2: null }; + elements.bless.reactions.bio_ooze = { elem2: null }; elements.bless.tool = function(pixel) { if (elements.bless.ignore.indexOf(pixel.element) !== -1) { return; } if (pixel.burning) { // stop burning @@ -33872,6 +34013,7 @@ Make sure to save your command in a file if you want to add this preset again.` }; }, }; + console.log("3/4 loaded"); //the real 3/4 point was inside Nothing There's tick function runAfterLoad(function() { if(typeof(badPixels) === "object") { badPixels.nothing_there_phase_1 = { panicIncrease: 1, panicIncreaseChance: 1 } //insta-panic for "aleph" thing and "level 1" humans @@ -36355,7 +36497,7 @@ Make sure to save your command in a file if you want to add this preset again.` amalgamatedBombFire += ",resonant_ender".repeat(5); amalgamatedBombFire += ",foof".repeat(8); amalgamatedBombFire += ",liquid_irradium".repeat(7); - amalgamatedBombFire += ",bioooze".repeat(8); + amalgamatedBombFire += ",bio_ooze".repeat(8); }); //Fairies runAfterLoad(function() { @@ -39390,6 +39532,7 @@ Make sure to save your command in a file if you want to add this preset again.` category: "machines", hardness: 0.6 }; + console.log("7/8 loaded"); //it was inside the weather controller code //KETCUP ## elements.ketcup = { color: "#ab2513", @@ -41622,10 +41765,14 @@ Make sure to save your command in a file if you want to add this preset again.` elements.molten_steel ??= {}; elements.molten_steel.tempHigh = 2727; elements.molten_steel.stateHigh = ["molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","molten_iron","carbon"]; //it may be FAR LESS than that irl; sus-304 steel has 0.08% - elements.carbon.reactions ??= {}; - elements.carbon.reactions.molten_iron = { - elem1: ["carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon",null], - elem2: "molten_steel" + if(elements.carbon) { + elements.carbon.reactions ??= {}; + elements.carbon.reactions.molten_iron = { + elem1: ["carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon",null], + elem2: "molten_steel" + } + } else { + logMessage("The mod that adds carbon failed to load in time. This is likely due to a race condition in the way Sandboxels applies mods, and not the fault of said mod's author, and will usually be fixed by reloading") }; elements.support_steel = { color: elements.steel.color, @@ -42176,7 +42323,7 @@ Make sure to save your command in a file if you want to add this preset again.` audioObject[oscillatorNodeName].connect(audioObject[gainNodeName]) audioObject[oscillatorNodeName].frequency.value = parameterObject.frequency audioObject[gainNodeName].connect(audioContext.destination) - audioObject[oscillatorNodeName].start(audioContext.currentTime + parameterObject.delay) + audioObject[oscillatorNodeName].start(audioContext.currentTime + (parameterObject.delay)) //stopping handler if(parameterObject.endType === "exponential") { //starts fading immediately audioObject[gainNodeName].gain.exponentialRampToValueAtTime( @@ -42200,16 +42347,71 @@ Make sure to save your command in a file if you want to add this preset again.` breakInto: ["plastic","metal_scrap","metal_scrap","metal_scrap"], conduct: 1, properties: { - frequency: 440, type: "sine", endType: "none", - length: 1, - volume: 1, - delay: 0, debounce: 0, debounceLength: tps }, + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","noteBlockFrequency"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.noteBlockFrequency; + }; + if(p0h) { + p0h.innerText = "Frequency"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","noteBlockLength"); + p1.setAttribute("min","0"); + p1.value = ambaPlaceProperties.noteBlockLength; + }; + if(p1h) { + p1h.innerText = "Length"; + }; + + showSetterColumn("numeric",2); + var p2 = document.getElementById("propertynumeric2input"); + var p2h = document.getElementById("propertynumeric2heading"); + if(p2) { + p2.setAttribute("set","noteBlockVolume"); + p2.setAttribute("min","0"); + p2.value = ambaPlaceProperties.noteBlockVolume; + }; + if(p2h) { + p2h.innerText = "Volume"; + }; + + showSetterColumn("numeric",3); + var p3 = document.getElementById("propertynumeric3input"); + var p3h = document.getElementById("propertynumeric3heading"); + if(p3) { + p3.setAttribute("set","noteBlockDelay"); + p3.setAttribute("min","0"); + p3.value = ambaPlaceProperties.noteBlockDelay; + }; + if(p3h) { + p3h.innerText = "Delay"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, tick: function(pixel) { + pixel.frequency ??= (ambaPlaceProperties?.noteBlockFrequency ?? 440); + pixel.length ??= (ambaPlaceProperties?.noteBlockLength ?? 1); + pixel.volume ??= (ambaPlaceProperties?.noteBlockVolume ?? 1); + pixel.delay ??= (ambaPlaceProperties?.noteBlockDelay ?? 0); var pixelSoundName = `x${pixel.x}y${pixel.y}`; //Generate unique-enough name var pixelPropertyObject = { //Load sound properties from pixel as object; frequency: pixel.frequency, @@ -44292,7 +44494,12 @@ maxPixels (default 1000): Maximum amount of pixels/changes (if xSpacing and ySpa } }); //SPECIFY CURRENT ELEMENT, MOUSE SIZE, AND TPS ON LOAD ## - window.addEventListener("load",function() { + /*if(urlParams.get("pause") !== null) { + paused = true; + document.getElementById("pauseButton").setAttribute("on","true") + };*/ + + window.addEventListener("load",function() { currentElement = urlParams.get("currentElement") ?? "sand"; if(!elementExists(currentElement)) { currentElement = "sand" @@ -44316,15 +44523,492 @@ maxPixels (default 1000): Maximum amount of pixels/changes (if xSpacing and ySpa if(shapeOrder.indexOf(shape) == -1) { shape = "square" }; - currentShape = shape + currentShape = shape; + + /*if(urlParams.get("pause") !== null) { + paused = true; + document.getElementById("pauseButton").setAttribute("on","true") + };*/ }); + //PRESSURE SYSTEM ## + loadSettings(); + settings.dopressure ??= false; + settings.drawpressure ??= false; + saveSettings(); + pressureCellSize = 4; + + function getPressureAtPixelCoords(pixelX,pixelY) { + var pressureCellX = Math.floor(pixelX / pressureCellSize); + var pressureCellY = Math.floor(pixelY / pressureCellSize); + return pressureMap?.[pressureCellX]?.[pressureCellY] ?? null + }; + + function regeneratePressureMap(_width,_height) { + pressureMap.forEach(function(x) { + x.forEach(y => y = null); + x.length = 0; + x = null + }); + pressureMap.length = 0; + pressureMap = null; + pressureMap = new Array(_width); + for(var i = 0; i < pressureMap.length; i++) { + pressureMap[i] = new Array(height).fill(0) + }; + }; + + pressureChangeDivisor = 2; + minimumPressure = -9999999; + + elements.sand.pressurePermeability = 0.44; + elements.snow.pressurePermeability = 0.7; + elements.ice.pressureHigh = 800; + elements.gravel.pressurePermeability = 0.53; + elements.rock.pressurePermeability = 0.56, + elements.wall.blockPressure = true; + elements.brick.pressurePermeability = 0.002; //given in L/s*m^2 //at 150Pa //per https://www.astm.org/stp157720130132.html //i don't know how to unit the pressure because it's not scaled to anything in reality + elements.brick.pressureHigh = 90; //arbitrary + elements.glass.pressureHigh = 70; //arbitrary + elements.wood.pressureHigh = 60; //arbitrary + runAfterLoad(function() { + var gravels = Object.keys(elements).filter(n => n.endsWith("gravel")); + for(var i = 0; i < gravels.length; i++) { + var gravelName = gravels[i]; + elements[gravelName].pressureHigh ??= 400; + }; + eLists.GRAVEL = gravels; + + var shards = Object.keys(elements).filter(n => n.endsWith("shards")); + for(var i = 0; i < shards.length; i++) { + var shardName = shards[i]; + var data = elements[shardName]; + if(data.breakInto) { + data.pressureHigh ??= 400; + } + }; + eLists.SHARD = shards; + + var elementsThatBreakIntoScrap = Object.keys(elements).filter(n => elements[n].breakInto?.endsWith?.("scrap")); + for(var i = 0; i < elementsThatBreakIntoScrap.length; i++) { + var etbisName = elementsThatBreakIntoScrap[i]; + var data = elements[etbisName] + var _hardness = data.hardness ?? 0.5; + data.pressureHigh ??= (_hardness * 625); + }; + + var rocks = Object.keys(elements).filter(n => elements[n]._data?.[2]?.endsWith?.("rock")); + for(var i = 0; i < rocks.length; i++) { + var rockName = rocks[i]; + elements[rockName].pressureHigh ??= 250; + }; + eLists.ROCK = rocks + }); + + + function pressureTick(forceTick=false) { + if(!(settings.dopressure)) { + return + }; + if(paused && !(forceTick)) { + return + } else if((!paused) || forceTick) { //shouldn't be necessary + var positions = []; + for(var x = 0; x < pressureMap.length; x++) { + for(var y = 0; y < pressureMap[x].length; y++) { + positions.push([x,y]) + }; + }; + shuffleArray(positions); + for(var i = 0; i < positions.length; i++) { + var [x,y] = positions[i]; + // yes i took this from doHeat lol + var v0 = pressureMap[x]?.[y]; + if(typeof(v0) !== "undefined") { + var _ac = arrayToShuffled(mooreDonutCoords); + if(isNaN(v0) || (v0 < minimumPressure)) { pressureMap[x][y] = 0; v0 = 0 }; + var cellLeftCoord = x * pressureCellSize; + var cellTopCoord = y * pressureCellSize; + var cellRightCoord = ((x + 1) * pressureCellSize) - 1; + var cellBottomCoord = ((y + 1) * pressureCellSize) - 1; + var pixels = getPixelsInRegion(cellLeftCoord,cellTopCoord,cellRightCoord,cellBottomCoord); + var howManyPixelsFitPerCell = (pressureCellSize ** 2); + var thisCellPermeability = 1; + if(pixels.length > 0) { + var pixelWasDeleted = false; + pixels.forEach(function(pixel) { + //var pressure = v0; + var data = elements[pixel.element]; + if(!data) { return }; + var highResult = data.highPressureTransition ?? data.breakInto + var lowResult = data.lowPressureTransition ?? data.breakInto + if((typeof(data.pressureHigh) == "number") && (typeof(highResult) !== "undefined")) { + //console.log(data.pressureHigh,highResult); + if(v0 >= data.pressureHigh) { + while(Array.isArray(highResult)) { + highResult = randomChoice(highResult) + }; + if(highResult === null) { + deletePixel(pixel.x,pixel.y) + return + } else { + changePixel(pixel,highResult) + }; + return + } + }; + if((typeof(data.pressureLow) == "number") && (typeof(lowResult) !== "undefined")) { + if(v0 <= data.pressureLow) { + while(Array.isArray(lowResult)) { + lowResult = randomChoice(lowResult) + }; + if(lowResult === null) { + deletePixel(pixel.x,pixel.y) + return + } else { + changePixel(pixel,lowResult) + }; + return + } + } + }); + if(pixelWasDeleted) { + pixels = getPixelsInRegion(cellLeftCoord,cellTopCoord,cellRightCoord,cellBottomCoord) + }; + var emptySpaces = howManyPixelsFitPerCell - pixels.length; + var preThisCellPermeability = pixels.map(x => getElementPressurePermeability(x.element)); + thisCellPermeability = (sumNumericArray(preThisCellPermeability) + emptySpaces) / howManyPixelsFitPerCell; + }; + for (var j = 0; j < _ac.length; j++) { + var offsets = _ac[j]; + var nx = x+(offsets[0]); + var ny = y+(offsets[1]); + var taxicabDistance = sumNumericArray(offsets.map(Math.abs)); + var adjustment = 1/Math.sqrt(taxicabDistance); + var v1 = pressureMap[nx]?.[ny]; + if(typeof(v1) === "number") { + if(isNaN(v1) || (v1 < minimumPressure)) { pressureMap[nx][ny] = 0; v1 = 0 }; + if(v0 == v1) { continue } + //coords of new cell + var newCellLeftCoord = nx * pressureCellSize; + var newCellTopCoord = ny * pressureCellSize; + var newCellRightCoord = ((nx + 1) * pressureCellSize) - 1; + var newCellBottomCoord = ((ny + 1) * pressureCellSize) - 1; + var pixels = getPixelsInRegion(newCellLeftCoord,newCellTopCoord,newCellRightCoord,newCellBottomCoord); + var permeability = 1; + if(pixels.length > 0) { + var pixelWasDeleted = false; + pixels.forEach(function(pixel) { + //var pressure = v0; + var data = elements[pixel.element]; + if(!data) { return }; + var highResult = data.highPressureTransition ?? data.breakInto + var lowResult = data.lowPressureTransition ?? data.breakInto + if((typeof(data.pressureHigh) == "number") && (typeof(highResult) !== "undefined")) { + //console.log(data.pressureHigh,highResult); + if(v0 >= data.pressureHigh) { + while(Array.isArray(highResult)) { + highResult = randomChoice(highResult) + }; + if(highResult === null) { + deletePixel(pixel.x,pixel.y); + pixelWasDeleted = true + } else { + changePixel(pixel,highResult) + }; + return + } + }; + if((typeof(data.pressureLow) == "number") && (typeof(lowResult) !== "undefined")) { + if(v0 <= data.pressureLow) { + while(Array.isArray(lowResult)) { + lowResult = randomChoice(lowResult) + }; + if(lowResult === null) { + deletePixel(pixel.x,pixel.y); + pixelWasDeleted = true + } else { + changePixel(pixel,lowResult) + }; + return + } + } + }); + if(pixelWasDeleted) { + pixels = getPixelsInRegion(newCellLeftCoord,newCellTopCoord,newCellRightCoord,newCellBottomCoord) + }; + var emptySpaces = howManyPixelsFitPerCell - pixels.length; + var prePermeability = pixels.map(x => getElementPressurePermeability(x.element)); + permeability = (sumNumericArray(prePermeability) + emptySpaces) / howManyPixelsFitPerCell; + //console.log(" with pixels",permeability); + }/* else { + console.log("without pixels",permeability) + }*/; + + if((thisCellPermeability == 0) || (permeability == 0)) { + continue + } else { + permeability = (thisCellPermeability + permeability) / 2; //average of source and destination permeabilities because it's simpler + var avg = (v0 + v1)/2; + // Set both cell araes to their average, permeability permitting + //console.log(pixelTicks,"c",change); + pressureMap[x][y] = lerp(pressureMap[x][y],avg,permeability); + pressureMap[nx][ny] = lerp(pressureMap[nx][ny],avg,permeability); + //set again the variables to the cell values + v0 = pressureMap[x][y]; + v1 = pressureMap[nx][ny]; + var change = avg - v1; + var direction = _ac[j]; + var intensity = ((Math.sqrt(Math.abs(change)) / pressureChangeDivisor) * adjustment) * Math.sign(change); + //console.log(intensity); + if(Math.abs(intensity) >= 0.01) { + var baseMovement = direction.map(x => x * intensity); //Will be turned into an int when it's added to the pixels' velocities + if(!((baseMovement[0] === 0) && (baseMovement[1] === 0))) { + pixels.forEach(function(pixel) { + var drag = Math.min(4,1000/(elements[pixel.element]?.density ?? 1000)); //no more than 4 times the effect + var trueMovementX = Math.trunc(baseMovement[0] * drag); + var trueMovementY = Math.trunc(baseMovement[1] * drag); + pixel.vx ??= 0; + pixel.vy ??= 0; + pixel.vx += trueMovementX; + pixel.vy += trueMovementY; + }) + } + } + } + } + } + } + } + } + }; + + function getElementPressurePermeability(elementName) { + if(typeof(elementName) == "object" && elementName.element) { + elementName = elementName.element + }; + if(!(elementExists(elementName))) { + return 0 + }; + var elementData = elements[elementName]; + if(elementData.blockPressure) { + return 0 + } else { + if(elementData.state == "gas") { + return 1 + } else { + return elementData.pressurePermeability ?? 0 + }; + }; + }; + + function changePressure(x,y,value,operationType="+",trueIfPixelCoordinates_FalseIfPressureGridCoordinates=false) { + if(trueIfPixelCoordinates_FalseIfPressureGridCoordinates) { + x = Math.floor(x / pressureCellSize); + y = Math.floor(y / pressureCellSize); + }; + if(typeof(pressureMap?.[x]?.[y]) === "number") { + switch(operationType.toLowerCase()) { + default: + case "+": + case "add": + case "addition": + case "plus": + case "increase": + case "increment": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + pressureMap[x][y] += value + }; + break; + case "-": + case "subtract": + case "subtraction": + case "minus": + case "take away": + case "takeaway": + case "decrease": + case "decrement": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + pressureMap[x][y] -= value + }; + break; + case "*": + case "x": + case "×": + case "multiply": + case "multiplication": + case "times": + case "by": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + pressureMap[x][y] *= value + }; + break; + case "/": + case "÷": + case "divide": + case "division": + case "divided by": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + pressureMap[x][y] /= value + }; + break; + case "%": + case "mod": + case "modulo": + case "modulus": + case "modulo by": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + pressureMap[x][y] %= value + }; + break; + case "=": + case "set": + case "equals": + case "assign": + case "assignment": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + pressureMap[x][y] = value + }; + break; + case ">": //lower-bounds the color + case ">=": + case "min": + case "minimum": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + pressureMap[x][y] = Math.max(value,pressureMap[x][y]) + }; + break; + case "<": + case "<=": + case "max": //upper-bounds the color + case "maximum": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + pressureMap[x][y] = Math.min(value,pressureMap[x][y]) + }; + break; + case "^": + case "**": + case "exp": + case "exponent": + case "exponentiate": + case "raise": + case "raise to": + case "raised to": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + var sign1 = Math.sign(pressureMap[x][y]); + var sign2 = Math.sign(value); + pressureMap[x][y] = (Math.abs(pressureMap[x][y]) ** Math.abs(value)) * sign1 * sign2; + }; + break; + case "√": + case "n√": + case "root": + case "nth root": + if(typeof(pressureMap?.[x]?.[y]) === "number") { + var sign1 = Math.sign(pressureMap[x][y]); + var sign2 = Math.sign(value); + pressureMap[x][y] = (Math.abs(pressureMap[x][y]) ** Math.abs(1 / value)) * sign1 * sign2; + }; + }; + if(isNaN(pressureMap[x][y])) { pressureMap[x][y] = 0 }; + return pressureMap[x][y]; + } else { + return false + } + }; + + function setGlobalPressure(value) { + for(var x = 0; x < pressureMap.length; x++) { + for(var y = 0; y < pressureMap[x].length; y++) { + pressureMap[x][y] = value + } + } + } + + function alertIfPressureDisabled() { + if(!(settings.dopressure)) { + logMessage("Pressure simulation is disabled") + } + }; + + elements.add_pressure = { + color: "#FF0000", + behavior: behaviors.WALL, + category: "special", + tool: function(pixel) { + changePressure(pixel.x,pixel.y,3 ** (shiftDown + 1),"+",true); + } + }; + + elements.subtract_pressure = { + color: "#0000FF", + behavior: behaviors.WALL, + category: "special", + tool: function(pixel) { + changePressure(pixel.x,pixel.y,3 ** (shiftDown + 1),"-",true); + } + }; + + elements.zero_pressure = { + color: "#000000", + behavior: behaviors.WALL, + category: "special", + tool: function(pixel) { + changePressure(pixel.x,pixel.y,0,"=",true); + } + }; + + elements.reset_pressure = { + color: "#000000", + maxSize: 1, + behavior: behaviors.WALL, + category: "special", + tool: function(pixel) { + setGlobalPressure(0) + } + }; + + function pressureTicker(forceTick=false) { + if(settings.dopressure && ((!paused) || forceTick)) { + pressureTick(forceTick) + } + }; + + oldDoFrame = doFrame; + doFrame = function() { + oldDoFrame(); + pressureTicker(true) + }; + + runAfterButtons(function() { + //WIDTH AND HEIGHT AREN'T DEFINED UNTIL THEN FOR SOME FUCKING REASON + oldClearAll = clearAll; + clearAll = function() { + oldClearAll(); + regeneratePressureMap(Math.ceil(width / pressureCellSize),Math.ceil(height / pressureCellSize)) // use the global width and height + }; + + pressureMap = new Array(Math.ceil(width / pressureCellSize)); + for(var i = 0; i < pressureMap.length; i++) { + pressureMap[i] = new Array(Math.ceil(height / pressureCellSize)).fill(0) + }; + + afterEveryTick(pressureTicker) + }) //FIX ## //fsr it's pausing silently on load now so this should fix that by silently unpausing it on load window.addEventListener("load",function() { - paused = false; + if(urlParams.get("paused") !== null) { + paused = true; + document.getElementById("pauseButton").setAttribute("on","true"); + } else { + paused = false; + document.getElementById("pauseButton").setAttribute("on","false"); + } crimsonObject.dirt = "crimsoil"; //something is changing it to sand }); + // + //MISCELLANEOUS CHANGES ## eLists.PIPE = ['pipe', 'destroyable_pipe', 'e_pipe', 'destroyable_e_pipe', 'channel_pipe', 'destroyable_channel_pipe', 'bridge_pipe']; elements.pipe_stage_shifter = { @@ -44442,7 +45126,7 @@ maxPixels (default 1000): Maximum amount of pixels/changes (if xSpacing and ySpa //aChefsDream fix: (re-)define juice reactions elements.juice.reactions ??= {}; - gigadebugMode = false; + gigadebugMode = false; //fights every-tick log spam by limiting each message to being logged 50 times if(gigadebugMode) { logLimit = 50; logLimitCache = {}; @@ -44455,7 +45139,9 @@ maxPixels (default 1000): Maximum amount of pixels/changes (if xSpacing and ySpa logLimitCache[argsKey]++ } } + //END ## + console.log("Mod loaded") } catch (error) { alert(`Load failed (try reloading).\nThis is likely a sporadic failure caused by inconsistencies in how mods are loaded, and will likely fix itself in a refresh or two. If it persists, then it's an issue.\nError: ${error.stack}`); console.error(error) diff --git a/mods/doom.js b/mods/doom.js new file mode 100644 index 00000000..1305e647 --- /dev/null +++ b/mods/doom.js @@ -0,0 +1,935 @@ +elements.screen = { + name: "Screen", + color: "#000000", + hidden: true +} +elements.doom = { + color: "#000000", + onSelect: function() { + startDoom() + } +} + +let running = false; + +const offsetX = 0; +const offsetY = 0; +const screenWidth = 166 - (2 * offsetX); +const screenHeight = 82 - (2 * offsetY); +const halfHeight = screenHeight / 2 + offsetY; + +const fov = 50; +const halfFov = fov / 2; + +const showDebugText = true; + +const map = [ + [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1], + [1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1], + [1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], + [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] +]; + +const colors = ["#ff0000", "#0000ff", "#ffffff", "#808080", "#ff5000"]; + +const defaults = { + x: 2, + y: 2, + angle: 90 +} + +const colorSettings = { + floor: "#cccccc", + ceiling: "#1e1e1e", +} + +const mapHeight = map.length; +const mapWidth = map[0].length; +const minimapOffset = 5; + +const speed = { + movementSpeed: 0.5, + rotationalSpeed: 5, + verticalRotationalSpeed: 5, +} + +const planeX = 0; +const planeY = 0.66; + +const inc = fov / screenWidth; +const precision_ = 64; +const maxDist = 25; +const accuracy = 1; + + +// const colorSettings = { +// floor: "#d1bd62", +// ceiling: "#f0c743" +// } + +const degToRad = (deg) => deg * (Math.PI / 180); +const radToDeg = (rad) => rad * (180 / Math.PI); +const splitHex = (hex) => hex.slice(1).match(/../g).map(a => Math.floor(parseInt(a, 16))); +const hexify = (rgb) => rgb.map(a => Math.floor(a).toString(16).padStart(2, "0")).join(""); +function colorLerp(color_, color2_, t) { + const color = splitHex(color_); + const color2 = splitHex(color2_); + const r = (1 - t) * color[0] + t * color2[0]; + const g = (1 - t) * color[1] + t * color2[1]; + const b = (1 - t) * color[2] + t * color2[2]; + return hexify([r, g, b]); +} + +function clamp(x, min, max) { + return Math.max(min, Math.min(x, max)); +} + +class Player { + constructor(x, y, angle) { + this.x = x; + this.y = y; + this.angle = angle; + this.verticalOffset = 0; + } + + update(key) { + // if (key.keyCode == 37) { // left + if (key.key == "a") { + this.angle -= speed.rotationalSpeed; + // } else if (key.keyCode == 39) { // right + } else if (key.key == "d") { + this.angle += speed.rotationalSpeed; + } else if (key.key == "b") { // up + this.verticalOffset = clamp(this.verticalOffset + 5, -45, 45); + } else if (key.key == "n") { // down + this.verticalOffset = clamp(this.verticalOffset - 5, -45, 45); + } + if (key.key == "w") { + console.log(this.angle); + const playerCos = Math.cos(degToRad(this.angle)) * speed.movementSpeed; + const playerSin = Math.sin(degToRad(this.angle)) * speed.movementSpeed; + const newX = this.x + playerCos; + const newY = this.y + playerSin; + const oldX = this.x; + const oldY = this.y; + if (map[Math.floor(newY)][Math.floor(oldX)] == 0) { + this.y = newY; + } + if (map[Math.floor(oldY)][Math.floor(newX)] == 0) { + this.x = newX; + } + } else if (key.key == "s") { + const playerCos = Math.cos(degToRad(this.angle)) * speed.movementSpeed; + const playerSin = Math.sin(degToRad(this.angle)) * speed.movementSpeed; + const newX = this.x - playerCos; + const newY = this.y - playerSin; + const oldX = this.x; + const oldY = this.y; + if (map[Math.floor(newY)][Math.floor(oldX)] == 0) { + this.y = newY; + } + if (map[Math.floor(oldY)][Math.floor(newX)] == 0) { + this.x = newX; + } + } else if (key.key == "a" && shiftDown) { + const playerCos = Math.cos(degToRad(this.angle + 90)) * speed.movementSpeed; + const playerSin = Math.sin(degToRad(this.angle + 90)) * speed.movementSpeed; + const newX = this.x - playerCos; + const newY = this.y - playerSin; + const oldX = this.x; + const oldY = this.y; + if (map[Math.floor(newY)][Math.floor(oldX)] == 0) { + this.y = newY; + } + if (map[Math.floor(oldY)][Math.floor(newX)] == 0) { + this.x = newX; + } + } else if (key.key == "d" && shiftDown) { + const playerCos = Math.cos(degToRad(this.angle - 90)) * speed.movementSpeed; + const playerSin = Math.sin(degToRad(this.angle - 90)) * speed.movementSpeed; + const newX = this.x - playerCos; + const newY = this.y - playerSin; + const oldX = this.x; + const oldY = this.y; + if (map[Math.floor(newY)][Math.floor(oldX)] == 0) { + this.y = newY; + } + if (map[Math.floor(oldY)][Math.floor(newX)] == 0) { + this.x = newX; + } + } + } +} + +const player = new Player(defaults.x, defaults.y, defaults.angle); + +// 5x5 +const font = { + a: [ + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, + 0, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 1 + ], + b: [ + 1, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 0 + ], + c: [ + 0, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 0, 1, 1, 1, 1 + ], + d: [ + 1, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 0 + ], + e: [ + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1 + ], + f: [ + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0 + ], + g: [ + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 1, 0, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ], + h: [ + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1 + ], + i: [ + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0 + ], + j: [ + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0 + ], + k: [ + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 1, 1, 0, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0 + ], + l: [ + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 0 + ], + m: [ + 1, 1, 1, 1, 0, + 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1 + ], + n: [ + 1, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1 + ], + o: [ + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0 + ], + p: [ + 1, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0 + ], + q: [ + 0, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1 + ], + r: [ + 1, 0, 1, 1, 1, + 1, 1, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0 + ], + s: [ + 0, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, + 1, 1, 1, 1, 0 + ], + t: [ + 1, 1, 1, 1, 1, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0 + ], + u: [ + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ], + v: [ + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0 + ], + w: [ + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0 + ], + x: [ + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1 + ], + y: [ + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0 + ], + z: [ + 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, + 0, 1, 1, 0, 0, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 0 + ], + "0": [ + 1, 1, 1, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 1, 1, 1, 0 + ], + "1": [ + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0 + ], + "2": [ + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1 + ], + "3": [ + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ], + "4": [ + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1 + ], + "5": [ + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ], + "6": [ + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ], + "7": [ + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1 + ], + "8": [ + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ], + "9": [ + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ], + ".": [ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0 + ], + ":": [ + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + ], + "-": [ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + ], + "+": [ + 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 1, 1, 1, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, + ], + ",": [ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + ], + "[": [ + 1, 1, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 1, 0, 0, 0 + ], + "]": [ + 1, 1, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 1, 0, 0, 0, + 1, 1, 0, 0, 0 + ], + "(": [ + 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0 + ], + ")": [ + 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0 + ], + ";": [ + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0 + ], + "!": [ + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0 + ], + "{": [ + 0, 1, 1, 0, 0, + 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 1, 1, 0, 0 + ], + "}": [ + 1, 1, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 1, 0, 0, 0, + 1, 1, 0, 0, 0 + ], + "_": [ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 0, 0 + ], + "°": [ + 1, 1, 1, 0, 0, + 1, 0, 1, 0, 0, + 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + ], + "|": [ + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0 + ] +} + +const customWidth = { + i: 1, + l: 4, + z: 4, + "0": 4, + "1": 1, + ".": 1, + ":": 1, + "-": 3, + "+": 3, + ",": 1, + "[": 2, + "]": 2, + "(": 2, + ")": 2, + ";": 1, + "!": 1, + "{": 3, + "}": 3, + "_": 3, + "°": 3, + " ": 3, + "|": 1, +} + +class TextRenderer { + static getCharWidth(char) { + return customWidth[char] ?? 5; + } + + static drawChar(char, x, y, color) { + if (!font[char]) return; + const width = this.getCharWidth(); + for (let i = 0; i < width; i++) { + for (let j = 0; j < 5; j++) { + if (font[char][j * 5 + i] == 1) { + pixelMap[x + offsetX + i][y + offsetY + j].color = color; + } + } + } + } + + static drawText(text, x, y, color) { + let offset = 0; + for (const char of text.toLowerCase().split("")) { + const width = this.getCharWidth(char); + this.drawChar(char, x + offset, y, color); + offset += width + 1; + } + } + + static getStringWidth(text) { + return text.split("").map(a => this.getCharWidth(a)).reduce((a, b) => a + b, 0) + text.length - 1; + } + + static drawCenteredText(text, x1, y, color) { + const x = Math.floor(x1 - (this.getStringWidth(text) / 2)); + this.drawText(text, x, y, color); + } +} + +class ButtonRegistry { + constructor (screen, buttons) { + this.screen = screen; + this.buttons = buttons; + this.currentButton = 0; + if (this.buttons.length > 0) { + this.buttons[this.currentButton].toggleSelection(); + } + } + + next() { + this.buttons[this.currentButton].toggleSelection(); + this.currentButton++; + if (this.currentButton >= this.buttons.length) { + this.currentButton %= this.buttons.length; + } + this.buttons[this.currentButton].toggleSelection(); + } + + prev() { + this.buttons[this.currentButton].toggleSelection(); + this.currentButton--; + if (this.currentButton < 0) { + this.currentButton = this.buttons.length - 1; + } + this.buttons[this.currentButton].toggleSelection(); + } + + current() { + return this.buttons[this.currentButton]; + } +} + +class GameMenu { + constructor (name, screen, width, height, offsets = null) { + this.name = name; + this.screen = screen; + this.width = width; + this.height = height; + this.offsetX = Math.floor((screenWidth - width - (offsets ? offsets.x : 0)) / 2) + (offsets ? offsets.x : 0); + this.offsetY = Math.floor((screenHeight - height - 10 - (offsets ? offsets.y : 0)) / 2) + (offsets ? offsets.y : 0) + 10; + this.buttons = []; + this.buttonRegistry = new ButtonRegistry(this, []); + } + + draw() { + for (const button of this.buttons) { + button.draw(); + } + } + + getOffsetX() { + return offsetX + this.offsetX; + } + + getOffsetY() { + return offsetY + this.offsetY; + } + + onKey(ev) { + if (ev.key == "b") { // up + this.buttonRegistry.prev(); + } else if (ev.key == "n") { // down + this.buttonRegistry.next(); + } else if (ev.key == "Enter") { + this.buttonRegistry.current().click(); + } + } + + addButtons(...buttons) { + this.buttons.push(...buttons); + this.updateButtonRegistry(); + } + + getButtons() { + return this.buttons; + } + + updateButtonRegistry() { + this.buttonRegistry = new ButtonRegistry(this, this.buttons); + } +} + +class GuiButton { + constructor (x, y, width, height, text, color, screen) { + this.x = x + screen.getOffsetX(); + this.y = y + screen.getOffsetY(); + this.width = width; + this.height = height; + this.text = text; + this.color = color; + this.onPressed = () => {}; + this.selected = false; + this.screen = screen; + } + + draw() { + for (let i = this.x; i < this.x + this.width; i++) { + for (let j = this.y; j < this.y + this.height; j++) { + if (this.selected && (i == this.x || j == this.y || i == this.x + this.width - 1 || j == this.y + this.height - 1)) { + pixelMap[i][j] = "#0080000"; + } else pixelMap[i][j] = this.color + } + } + TextRenderer.drawCenteredText(this.text, this.x + (this.width / 2), Math.floor(this.y + (this.height / 2) - 5/2), "#ffffff"); + } + + onClick(cb) { + this.onPressed = cb; + } + + click() { + this.onPressed(); + } + + toggleSelection() { + this.selected = !this.selected; + } +} + +class GuiUtils { + static drawRect(x1, y1, x2, y2, color) { + for (let i = Math.max(Math.min(x1, x2), 0); i < Math.min(Math.max(x1, x2), width); i++) { + for (let j = Math.max(Math.min(y1, y2), 0); j < Math.min(Math.max(y1, y2), height); j++) { + pixelMap[i][j].color = color; + } + } + } + + static drawOutline(x1, y1, x2, y2, color) { + const initI = Math.max(Math.min(x1, x2), 0); + const endI = Math.min(Math.max(x1, x2), width); + const initJ = Math.max(Math.min(y1, y2), 0); + const endJ = Math.min(Math.max(y1, y2), height); + for (let i = initI; i < endI; i++) { + for (let j = initJ; j < endJ; j++) { + if (i == initI || i == endI - 1 || j == initJ || j == endJ - 1) { + pixelMap[i][j].color = color; + } + } + } + } + + static drawVerticalLine(x, y1, y2, color) { + for (let i = Math.max(Math.min(y1, y2), 0); i <= Math.min(Math.max(y1, y2), height); i++) { + if (!pixelMap[x][i]) continue; + pixelMap[x][i].color = color; + } + } +} + +class TestGameMenu extends GameMenu { + constructor (screen, width, height, offsets = null) { + super("test menu screen", screen, width, height, offsets); + const button1 = new GuiButton(1, 1, width - 2, 20, "button", "#005555", this); + button1.onClick(() => { + button1.color = "#ff0000"; + button2.color = "#005555"; + }) + const button2 = new GuiButton(1, 22, width - 2, 20, "button 2", "#005555", this); + button2.onClick(() => { + button2.color = "#ff0000"; + button1.color = "#005555"; + }) + this.addButtons( + button1, + button2 + ) + } + draw() { + for (let i = this.getOffsetX(); i < this.width + this.getOffsetX(); i++) { + for (let j = this.getOffsetY(); j < this.height + this.getOffsetY(); j++) { + pixelMap[i][j].color = "#ffffff"; + } + } + super.draw(); + } +} + +class GameScreen { + clock() { + this.fps = this.currentFrames; + this.currentFrames = 0; + } + clear() { + for (let i = offsetX; i < width - offsetX; i++) { + for (let j = offsetY; j < height - offsetY; j++) { + pixelMap[i][j].color = "#ffffff"; + } + } + } + + draw() { + if (!this.currentFrames) this.currentFrames = 0; + this.currentFrames++; + this.clear(); + this.drawLevel(); + this.drawMinimap(); + if (showDebugText) { + TextRenderer.drawText(`angle: ${Math.floor(player.angle)}°`, 1, 1, "#ffffff"); + TextRenderer.drawText(`pos: ${player.x.toFixed(2)} | ${player.y.toFixed(2)}`, 1, 7, "#ffffff"); + TextRenderer.drawText(`fps: ${this.fps}`, 1, 13, "#ffffff"); + TextRenderer.drawText(`vertangle: ${player.verticalOffset}`, 1, 19, "#ffffff"); + } + if (this.menuScreen) { + this.drawMenu(); + } + } + + drawMenu() { + this.drawOverlay(); + GuiUtils.drawRect(this.menuScreen.getOffsetX(), Math.floor((screenHeight - this.menuScreen.height) / 2) - 2, this.menuScreen.getOffsetX() + this.menuScreen.width, this.menuScreen.getOffsetY(), "#1e1e1e"); + TextRenderer.drawCenteredText(this.menuScreen.name, (this.menuScreen.width / 2) + this.menuScreen.getOffsetX(), Math.floor((screenHeight - this.menuScreen.height) / 2) - 1, "#ffffff"); + this.menuScreen.draw(); + } + + drawOverlay() { + for (let i = offsetX; i < width - offsetX; i++) { + for (let j = offsetY; j < height - offsetY; j++) { + pixelMap[i][j].color = "#" + colorLerp(pixelMap[i][j].color, "#000000", 0.5); + } + } + } + + drawLevel() { + let rayAngle = player.angle - halfFov; + for (let i = 0; i < screenWidth; i += accuracy) { + let rayX = player.x; + let rayY = player.y; + const rayCos = Math.cos(degToRad(rayAngle)) / precision_; + const raySin = Math.sin(degToRad(rayAngle)) / precision_; + let wall = 0; + + while (wall == 0) { + rayX += rayCos; + rayY += raySin; + wall = map[Math.floor(rayY)][Math.floor(rayX)]; + } + + const angle = radToDeg(Math.atan2(rayY, rayX)); + const side = (angle >= 45 && angle <= 135) || (angle >= 275 && angle <= 315); + + const distance = Math.sqrt((player.x - rayX) ** 2 + (player.y - rayY) ** 2) * Math.cos(degToRad(rayAngle - player.angle)); + + const wallHeight = Math.floor(halfHeight / distance); + + let t = Math.min(1, (1 / maxDist) * distance); + + const color = "#" + colorLerp(colors[wall - 1], "#000000", t); + const ceilingColor = colorSettings.ceiling; + const floorColor = colorSettings.floor; + + GuiUtils.drawVerticalLine(offsetX + i, halfHeight - wallHeight - player.verticalOffset, halfHeight + wallHeight - player.verticalOffset, color); + GuiUtils.drawVerticalLine(offsetX + i, offsetY, halfHeight - wallHeight - 1 - player.verticalOffset, ceilingColor); + GuiUtils.drawVerticalLine(offsetX + i, halfHeight + wallHeight + 1 - player.verticalOffset, height - offsetY - 1, floorColor); + + rayAngle += inc; + } + } + + drawMinimap() { + for (let i = 0; i < mapWidth; i++) { + for (let j = 0; j < mapHeight; j++) { + const x = width - offsetX - minimapOffset - mapWidth + i; + const y = offsetY + minimapOffset + j; + if (map[j][i] == 0) { + pixelMap[x][y].color = colorSettings.floor; + } else { + pixelMap[x][y].color = colors[map[j][i] - 1]; + } + if (i == Math.floor(player.x) && j == Math.floor(player.y)) { + pixelMap[x][y].color = "#00ffff"; + } + } + } + } + + onKey(ev) { + // if (ev.key == "Escape") { + // this.menuScreen = this.menuScreen ? null : new TestGameMenu(this, Math.floor(0.75 * screenWidth), Math.floor(0.75 * screenHeight)); + // } + if (!this.menuScreen) { + player.update(ev); + } else { + this.menuScreen.onKey(ev); + } + } +} + +const game = new GameScreen(); + +setInterval(cellTick, (1000/(tps*4))); +setInterval(() => {game.clock()}, 1000); + +function cellTick() { + if (running && !paused) { + game.draw(); + } +} + +function startDoom() { + if (!running) { + videoFrame = 0; + + for (let i = offsetX; i < width - offsetX; i++) { + for (let j = offsetY; j < height - offsetY; j++) { + if (pixelMap[i][j]) deletePixel(i, j); + createPixel("screen", i, j); + } + } + } + running = !running; +} + +window.addEventListener("keydown", (ev) => { + if (ev.key == "u") { + startDoom(); + } else { + game.onKey(ev); + } +}) \ No newline at end of file diff --git a/mods/limewater.js b/mods/limewater.js new file mode 100644 index 00000000..9fb271c8 --- /dev/null +++ b/mods/limewater.js @@ -0,0 +1,63 @@ +// first time making a mod so bare with me + + +elements.limewater = { + color: "#3baaff", + behavior: behaviors.LIQUID, + category: "liquids", + state: "liquid", + density: 2211, + tempHigh: 100, + stateHigh: ["steam", "slaked_lime"], + tempLow: 0, + stateLow: "limewater_ice", + reactions: {"carbon_dioxide": {elem1: "calcium_carbonate_solution", elem2: null }, + }, +}; +elements.limewater_ice = { + color: "#def0ff", + behavior: behaviors.WALL, + category: "solids", + state: "solid", + density: 2211, + temp: -5, + tempHigh: 2, + stateHigh: "limewater", +}; +elements.calcium_carbonate_solution = { + color: "#ffffff", + behavior: behaviors.LIQUID, + category: "liquids", + state: "liquid", + density: 2710, + tempHigh: 100, + stateHigh: ["steam", "calcium_carbonate"], +}; + +elements.calcium_carbonate = { + color: ["#ffffff", "#e3e3e3"], + behavior: behaviors.POWDER, + category: "liquids", + state: "liquid", + density: 2710, + temp: 20, + tempHigh: 825, + stateHigh: "molten_calcium_carbonate", +}; + +elements.molten_calcium_carbonate = { + color: ["#f5190a", "#d4180b", "#f5190a", "#423f3e"], + behavior: behaviors.MOLTEN, + category: "states", + state: "solid", + density: 2710, + temp: 900, + tempLow: 820, + viscosity: 100, + stateLow: "calcium_carbonate", + reactions: {"water": {elem1: "steam", elem2:"calcium_carbonate"}, + }, +}; + +if (!elements.water.reactions) { elements.water.reactions = {} } +elements.water.reactions.slaked_lime = { "elem1":"limewater", "elem2": "limewater"};