diff --git a/mods/a_mod_by_alice.js b/mods/a_mod_by_alice.js index c5a03299..98966752 100644 --- a/mods/a_mod_by_alice.js +++ b/mods/a_mod_by_alice.js @@ -1 +1,45293 @@ -logMessage("a_mod_by_alice.js is deprecated. Please remove this mod from your mod list.") +var modName = "mods/../a_mod_by_alice.js" //can't do "alice's mod" because the apostrophe will fuck up code, be too confusing, or both +//Version ω0.0 [Preliminary Revival] +var dependencies = ["mods/libhooktick.js", "mods/chem.js", "mods/minecraft.js", "mods/Neutronium Mod.js", "mods/CrashTestDummy.js", "mods/fey_and_more.js", "mods/velocity.js", "mods/ketchup_mod.js", "mods/moretools.js", "mods/aChefsDream.js", "mods/nousersthings.js"]; //thanks to mollthecoder, PlanetN9ne, StellarX20 (3), MelecieDiancie, R74n, Nubo318, Sightnado, SquareScreamYT, and NoUsernameFound +var dependencyExistence = dependencies.map(x => enabledMods.includes(x)); +var allDependenciesExist = dependencyExistence.reduce(function(a,b) { return a && b }); +//console.log(allDependenciesExist); +if(allDependenciesExist) { +try { + //COMMON VARIABLES ## + _cc = { + w: { + j: {r: 255, g: 255, b: 255}, + r: "rgb(255,255,255)", + h: "#FFFFFF" + }, + b: { + j: {r: 0, g: 0, b: 0}, + r: "rgb(0,0,0)", + h: "#000000" + } + }; + canvas = document.getElementsByTagName("canvas")?.[0]; + ctx = canvas?.getContext?.("2d") ?? null; + //ESSENTIAL COMMON FUNCTIONS (CODE LIBRARY) ## + //DEBUGGING + function logAndReturn(thing) { + console.log(thing); + return thing + }; + //URL + urlParams = new URLSearchParams(window.location.search); + //Objects + //getKeyByValue code by SO UncleLaz: https://stackoverflow.com/questions/9907419/how-to-get-a-key-in-a-javascript-object-by-its-value + //CC-BY-SA-4.0 + function getKeyByValue(object, value) { + return Object.keys(object).find(key => object[key] === value); + }; + //RNG + //Random integer from 0 to n + function randomIntegerFromZeroToValue(value) { + var absoluteValuePlusOne = Math.abs(value) + 1; + if(value >= 0) { //Positive case + return Math.floor(Math.random() * absoluteValuePlusOne) + } else { //Negative case: flip sign + return 0 - Math.floor(Math.random() * absoluteValuePlusOne) + }; + }; + //Random thing from array + function randomChoice(array) { + if(!array) { throw new Error("randomChoice: Array is undefined or missing!") } + if(array.length === 0) { throw new Error(`The array ${array} is empty`) }; + var length = array.length; + var randomIndex = randomIntegerFromZeroToValue(length - 1); + return array[randomIndex]; + }; + //Random 1 or -1 + function randomSign() { + return Math.random() < 0.5 ? 1 : -1 + }; + //Random integer from m to n + function randomIntegerBetweenTwoValues(min,max) { + if(min > max) { + var temp = max; //the need of a temporary space has always annoyed me + max = min; + min = temp; + }; + return Math.floor(Math.random() * (max - min + 1)) + min + }; + //cyrb128 idk where this comes from but it was in the same thread + function cyrb128(str) { + let h1 = 1779033703, h2 = 3144134277, + h3 = 1013904242, h4 = 2773480762; + for (let i = 0, k; i < str.length; i++) { + k = str.charCodeAt(i); + h1 = h2 ^ Math.imul(h1 ^ k, 597399067); + h2 = h3 ^ Math.imul(h2 ^ k, 2869860233); + h3 = h4 ^ Math.imul(h3 ^ k, 951274213); + h4 = h1 ^ Math.imul(h4 ^ k, 2716044179); + } + h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067); + h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233); + h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213); + h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179); + return [(h1^h2^h3^h4)>>>0, (h2^h1)>>>0, (h3^h1)>>>0, (h4^h1)>>>0]; + } + function mulberry32(a) { //Mulberry32 implemented in JS from StackOverflow, https://gist.github.com/tommyettinger/46a874533244883189143505d203312c + return function() { + var t = a += 0x6D2B79F5; + t = Math.imul(t ^ t >>> 15, t | 1); + t ^= t + Math.imul(t ^ t >>> 7, t | 61); + return ((t ^ t >>> 14) >>> 0) / 4294967296; + } + } //returns random function seeded with a + //Seeded randbetween + function seededRandBetween(min,max,randomFunction) { + if(min > max) { + var temp = max; + max = min; + min = temp; + }; + return Math.floor(randomFunction() * (max - min + 1)) + min + }; + //Arrays + //Shallow array comparer by SO Tim Down: https://stackoverflow.com/a/10260204 + //CC-BY-SA-3.0 + function arraysIdentical(arr1, arr2) { + var i = arr1.length; + if (i !== arr2.length) { + return false; + }; + while (i--) { + if (arr1[i] !== arr2[i]) { + return false; + }; + }; + return true; + }; + function indexOf(arr, val, comparer) { + for (var i = 0, len = arr.length; i < len; ++i) { + if ( i in arr && comparer(arr[i], val) ) { + return i; + }; + }; + return -1; + }; + function averageNumericArray(array) { + var total = array.reduce(addTwoNumbers,0) + return total / array.length + }; + function sumNumericArray(array) { //Sum of array numbers + return array.reduce((partialSum, a) => partialSum + a, 0); + }; + function pad_array(arr,len,fill) { //https://stackoverflow.com/a/38851957 + //console.log("Padding array"); + return arr.concat(Array(len).fill(fill)).slice(0,len); + } + //Function to check if an array includes a given array by SO Johnny Tisdale: https://stackoverflow.com/a/60922255 + //CC-BY-SA-4.0 + function includesArray(parentArray, testArray) { + for (let i = 0; i < parentArray.length; i++) { + if (parentArray[i].every(function(value, index) { return value === testArray[index]})) { + return true; + }; + }; + return false; + }; + function addArraysInPairs(array1,array2,fill=0) { //e.g. [1,2,3] + [10,0,-1] = [11,2,2] + //console.log("Adding in pairs: " + array1 + " and " + array2 + "."); + if(array1.length > array2.length) { //zero-padding + array2 = pad_array(array2,array1.length,fill); //if a1 is longer, pad a2 to a1's length + } else if(array2.length > array1.length) { + array1 = pad_array(array1,array2.length,fill); //if a2 is longer, pad a1 to a2's length + }; + var tempArray = []; + for(z = 0; z < array1.length; z++) { + //console.log("Forming output values (" + array1[z] + " + " + array2[z] + ")"); + tempArray[z] = array1[z] + array2[z]; + //console.log("Sum" + tempArray[z]); + }; + //console.log("Added into " + tempArray + "."); + return tempArray; + }; + function tryJoin(stringOrArray,joiner) { + //console.log(`tryJoin: ${stringOrArray}`); + if(typeof(stringOrArray) === "string") { + //console.log("tryJoin: String"); + return stringOrArray; + } else if(Array.isArray(stringOrArray)) { + //console.log("tryJoin: Array"); + return stringOrArray.join(joiner); + } else { + 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) { + return typeof(elements[elementName]) === "object"; + }; + //Has a given state + function isState(elementName,inputState) { + if(!elementExists(elementName)) { + throw new Error(`Element ${elementName} doesn't exist`); + }; + var infoState = elements[elementName].state; + if(infoState == undefined) { infoState = "undefined" }; + if(inputState == undefined) { inputState = "undefined" }; + if(inputState instanceof Array) { + var limit = 0; + while(inputState.includes(undefined) && limit < 3) { + inputState[inputState.indexOf(undefined)] = "undefined" + limit++; + }; + }; + if(inputState instanceof Array) { + return inputState.includes(infoState); + }; + return infoState == inputState; + }; + //Check if pixel of given element exists at given location + function hasPixel(x,y,elementInput) { + if(isEmpty(x,y,true)) { //if empty, it can't have a pixel + return false; + } else { + if(elementInput.includes(",")) { //CSTA + elementInput = elementInput.split(","); + }; + if(Array.isArray(elementInput)) { //if element list + for(i = 0; i < elementInput.length; i++) { if(!elementExists(elementInput[i])) { console.log(`hasPixel: Element "${elementInput[i]}" doesn't exist`) } }; + return elementInput.includes(pixelMap[x][y].element); + } else { //if single element + if(!elementExists(elementInput)) { console.log(`hasPixel: Element "${elementInput}" doesn't exist`) }; + return pixelMap[x][y].element === elementInput; + }; + }; + }; + //Is movable + var backupCategoryWhitelist = ["land","powders","weapons","food","life","corruption","states","fey","Fantastic Creatures","dyes","energy liquids","random liquids","random gases","random rocks"]; + var backupElementWhitelist = ["mercury", "chalcopyrite_ore", "chalcopyrite_dust", "copper_concentrate", "fluxed_copper_concentrate", "unignited_pyrestone", "ignited_pyrestone", "everfire_dust", "extinguished_everfire_dust", "mistake", "polusium_oxide", "vaporized_polusium_oxide", "glowstone_dust", "redstone_dust", "soul_mud", "wet_soul_sand", "nitrogen_snow", "fusion_catalyst", "coal", "coal_coke", "blast_furnace_fuel", "molten_mythril"]; + function commonMovableCriteria(name,shallowBlacklist=null) { + if(typeof(elements[name]) !== "object") { + throw new Error(`Nonexistent element ${name}`); + }; + var info = elements[name]; + //console.log(`${name} (${JSON.stringify(elements[name])})`); + if(typeof(info.state) === "undefined") { + var state = null; + } else { + var state = info.state; + }; + if(typeof(info.category) === "undefined") { + var category = "other"; + } else { + var category = info.category; + }; + if(shallowBlacklist !== null && shallowBlacklist.includes(name)) { + return false; + }; + if(elements[name].tool) { + return false; + }; + if(elements[name].behavior && elements[name].behavior.toString() == elements.wall.behavior.toString() && !elements[name].tick) { + return false; + }; + if(["liquid","gas"].includes(state)) { + return true; + }; + if(info.movable) { + return true; + }; + if(elements[name].behavior instanceof Array) { + var behaviorString = elements[name].behavior.toString(); + return behaviorString.includes("M1") || behaviorString.includes("M2"); + }; + if(backupCategoryWhitelist.includes(category)) { + return true; + }; + if(backupElementWhitelist.includes(name)) { + return true; + }; + if(category.includes("mudstone")) { + return true; + }; + return false; + }; + //Element name search + window.searchQuery = {}; + function searchElements(query) { + if(typeof(window.searchQuery) == "undefined") { window.searchQuery = "" }; //necessary because of filter's idiotic no-argument policy + window.searchQuery = query; + var elemNames = Object.keys(elements); + var matches = elemNames.filter(function(name) { + return !!(name.match(window.searchQuery)) + }); + return matches + }; + function elementsWith(keyQuery) { + if(typeof(window.keyQuery) == "undefined") { window.keyQuery = "" }; //necessary because of filter's idiotic no-argument policy + window.keyQuery = keyQuery; + var elemNames = Object.keys(elements); + var matches = elemNames.filter(function(name) { + return typeof(elements[name]?.[window.keyQuery]) !== "undefined" + }); + return matches + }; + function elementsWithout(keyInverseQuery) { + if(typeof(window.keyInverseQuery) == "undefined") { window.keyInverseQuery = "" }; //necessary because of filter's idiotic no-argument policy + window.keyInverseQuery = keyInverseQuery; + var elemNames = Object.keys(elements); + var matches = elemNames.filter(function(name) { + return typeof(elements[name]?.[window.keyInverseQuery]) === "undefined" + }); + return matches + }; + function getElementsInCategory(categoryName) { + if(["",null,undefined].includes(categoryName)) { categoryName = "other" }; + window.categoryQuery = categoryName; + var elemNames = Object.keys(elements); + var matches = elemNames.filter(function(name) { + return (elements[name].category ?? "other") == window.categoryQuery + }); + return matches + }; + function getStateHigh(element,forceArray=false) { + if(!(element instanceof Array)) { element = [element] }; + var existantElements = element.filter(function(name) { return elementExists(name) }); + if(existantElements.length == 0) { return undefined } else { element = existantElements }; + var results = element.map(name => elements[name].stateHigh).filter(function(nameOrUndefined) { return typeof(nameOrUndefined) !== "undefined" }); + switch(results.length) { + case 0: + return null; + case 1: + if(!forceArray) { return results[0] }; + default: + return results + } + }; + function getState(element,forceArray=false) { + if(!(element instanceof Array)) { element = [element] }; + var existantElements = element.filter(function(name) { return elementExists(name) }); + if(existantElements.length == 0) { return undefined } else { element = existantElements }; + var results = element.map(name => elements[name].state).filter(function(nameOrUndefined) { return typeof(nameOrUndefined) !== "undefined" }); + switch(results.length) { + case 0: + return null; + case 1: + if(!forceArray) { return results[0] }; + default: + return results + } + }; + function getStateLow(element,forceArray=false) { + if(!(element instanceof Array)) { element = [element] }; + var existantElements = element.filter(function(name) { return elementExists(name) }); + if(existantElements.length == 0) { return undefined } else { element = existantElements }; + var results = element.map(name => elements[name].stateLow).filter(function(nameOrUndefined) { return typeof(nameOrUndefined) !== "undefined" }); + switch(results.length) { + case 0: + return null; + case 1: + if(!forceArray) { return results[0] }; + default: + return results + } + }; + function getStateAtTemp(element,temp) { + var data = elements[element]; + var tl = data.tempLow; + var th = data.tempHigh; + if(typeof(tl) == "number" && temp <= tl) { + return data.stateLow + } else if(typeof(th) == "number" && temp >= th) { + return data.stateHigh + } else { + return element + } + }; + function getBreakInto(element,forceArray=false) { + if(!(element instanceof Array)) { element = [element] }; + var existantElements = element.filter(function(name) { return elementExists(name) }); + if(existantElements.length == 0) { return undefined } else { element = existantElements }; + var results = element.map(name => elements[name].breakInto).filter(function(nameOrUndefined) { return typeof(nameOrUndefined) !== "undefined" }); + switch(results.length) { + case 0: + return null; + case 1: + if(!forceArray) { return results[0] }; + default: + return results + } + }; + //Math(s) + //Base n logarithm from https://stackoverflow.com/a/3019290 + function logN(number,base) { //Vulnerable to float issues + return Math.log(number) / Math.log(base); + }; + //Distance between points + function pyth(xA,yA,xB,yB) { + var a = Math.abs(xB - xA); + var b = Math.abs(yB - yA); + var c = Math.sqrt(a**2 + b**2); + return c; + }; + //Limit number to [min, max] + function bound(number,lowerBound,upperBound) { + return Math.min(upperBound,Math.max(lowerBound,number)); + }; + //Emergency color wrapper + rgbColorBound = function(color) { + return bound(color,0,255); + }; + 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 + //x_0 = "the x value of the sigmoid midpoint" i.e. the x center of the bendy part + //k = steepness + function logisticCurve(x,L,k,x0) { + return L/( 1 + ( Math.E ** ( -k * (x - x0) ) ) ); + }; + // https://stackoverflow.com/questions/10756313/javascript-jquery-map-a-range-of-numbers-to-another-range-of-numbers + // Function from August Miller + //Map a range of numbers to another range of numbers + function scale (number, inMin, inMax, outMin, outMax) { + return (number - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; + } + //Color + function rgbStringToUnvalidatedObject(string,stripAlpha) { + var numbers = string.match(/[\d\.]+/g); + var red = numbers[0]; + var green = numbers[1]; + var blue = numbers[2]; + var alpha = null; + if((numbers.length > 3) && !stripAlpha) {alpha = numbers[3]}; + var result = {r: red, g: green, b: blue}; + if(alpha !== null) { result.a = alpha }; + numbers = numbers.map(x => parseFloat(x)); + return result + }; + function hslStringToUnvalidatedObject(string,stripAlpha) { + var numbers = string.match(/[\d\.]+/g); + var hue = numbers[0]; + var saturation = numbers[1]; + var whateverL_StandsFor = numbers[2]; + var alpha = null; + if((numbers.length > 3) && !stripAlpha) {alpha = numbers[3]}; + var result = {h: hue, s: saturation, l: whateverL_StandsFor}; + if(alpha !== null) { result.a = alpha }; + numbers = numbers.map(x => parseFloat(x)); + return result + }; + function rgbStringToObject(string,doRounding=true,doBounding=true) { //turns rgb() to {r,g,b} + //console.log(`rgbStringToObject: ${string}`); + //console.log("Splitting string into object"); + if( !(string.startsWith("rgb(")) || !(string.endsWith(")")) ) { + throw new Error('Color must start with "rgb(" and end with ")"'); + }; + var red,green,blue; + [red,green,blue] = string.match(/[0-9\-.]+/g).slice(0,3).map(x => parseFloat(x)); + //console.log(`Colors loaded (${red}, ${green}, ${blue})`); + //NaN checking + var redNaN = isNaN(red); + var greenNaN = isNaN(green); + var blueNaN = isNaN(blue); + var NanErrorString = "One or more colors are NaN:" + if(redNaN) { NanErrorString += " red" }; + if(greenNaN) { NanErrorString += " green" }; + if(blueNaN) { NanErrorString += " blue" }; + if(redNaN || greenNaN || blueNaN) { throw new Error(NanErrorString) }; + if(doRounding) { + red = Math.round(red); + green = Math.round(green); + blue = Math.round(blue); + //console.log(`Colors rounded to (${red}, ${green}, ${blue})`); + }; + if(doBounding) { + red = bound(red,0,255) + green = bound(green,0,255) + blue = bound(blue,0,255) + //console.log(`Colors bounded to (${red}, ${green}, ${blue})`); + }; + //console.log("String split: outputs " + red + ", " + green + ", " + blue + "."); + return {r: red, g: green, b: blue}; + }; + function hslColorStringToObject(color,stripAlpha=false) { + if(!color.startsWith("hsl(") || !color.endsWith(")")) { + throw new Error(`The color ${color} is not a valid hsl() color`) + }; + var colorTempArray = color.split(",").map(x => x.trim()) + if(colorTempArray.length < 3) { + throw new Error(`The color ${color} is not a valid hsl() color`) + }; + if(!colorTempArray[1].endsWith("%")) { console.log(`hslColorStringToObject: Saturation in color ${color} was missing a %`); colorTempArray[1] += "%"; } + if(!colorTempArray[2].endsWith("%)")) { console.log(`hslColorStringToObject: Lightness in color ${color} was missing a %`); colorTempArray[2] = [colorTempArray[2].slice(0, colorTempArray[2].length - 1), "%", colorTempArray[2].slice(colorTempArray[2].length - 1)].join(''); } + var hue = parseFloat(colorTempArray[0].substring(4)); + var saturation = parseFloat(colorTempArray[1].slice(0,-1)) + var lightness = parseFloat(colorTempArray[2].match(/\d+(\.\d+|)/)?.[0] ?? NaN); + var alpha = (colorTempArray.length < 3 || stripAlpha) ? null : parseFloat(colorTempArray[3].match(/\d+(\.\d+|)/)?.[0] ?? NaN); + //NaN checking + var hueNaN,saturationNaN,lightnessNaN,alphaNaN; + var nanColors = []; + hueNaN = isNaN(hue); + saturationNaN = isNaN(saturation); + lightnessNaN = isNaN(lightness); + alphaNaN = (isNaN(alpha) && !stripAlpha); + if(hueNaN) { nanColors.push("hue") }; + if(saturationNaN) { nanColors.push("saturation") }; + if(lightnessNaN) { nanColors.push("lightness") }; + if(alphaNaN) { nanColors.push("alpha") }; + if(nanColors.length > 0) { + var NanErrorString = ["hslColorStringToObject:",capitalizeFirstLetter(englishFormatList(nanColors)),nanColors.length == 1 ? "is" : "are","NaN"].join(" "); + throw new Error(NanErrorString) + }; + var result = {h: hue, s: saturation, l: lightness}; + if(alpha !== null) { result.a = alpha }; + return result + }; + function rgbToHex(color,stripAlpha=false) { + //console.log(`rgbToHex called on ${typeof(color) === "object" ? JSON.stringify(color) : color}`); + if(typeof(color) == "object") { //Expects object like "{r: 172, g: 11, b: 34}" + var red = color.r; + var green = color.g; + var blue = color.b; + var alpha = stripAlpha ? null : (color.a ?? null); + //console.log(`Colors loaded (${red}, ${green}, ${blue})`); + red = Math.round(red); + green = Math.round(green); + blue = Math.round(blue); + //console.log(`Colors rounded to (${red}, ${green}, ${blue})`); + red = bound(red,0,255) + green = bound(green,0,255) + blue = bound(blue,0,255) + if(alpha !== null) { alpha = bound(alpha,0,1) } + //console.log(`Colors bounded to (${red}, ${green}, ${blue})`); + red = red.toString(16); + green = green.toString(16); + blue = blue.toString(16); + if(alpha !== null) { alpha = Math.round(alpha * 255).toString(16) } + //console.log(`Colors converted to (0x${red}, 0x${green}, 0x${blue})`); + //console.log("Padding R"); + while(red.length < 2) { + red = "0" + red; + }; + //console.log("Padding G"); + while(green.length < 2) { + green = "0" + green; + }; + //console.log("Padding B"); + while(blue.length < 2) { + blue = "0" + blue; + }; + //console.log("Padding A"); + if(alpha !== null) { + while(alpha.length < 2) { + alpha = "0" + alpha; + } + }; + //console.log(`Colors padded to (0x${red}, 0x${green}, 0x${blue}), concatenating...`); + return "#" + red + green + blue + (stripAlpha ? "" : (alpha ?? "")); + } else if(typeof(color) == "string") { //Expects string like "rgb(20,137,4)". Also doesn't round properly for some reason... + //console.log("Splitting string") + color = rgbStringToUnvalidatedObject(color,stripAlpha); + red = color.r; + green = color.g; + blue = color.b; + alpha = stripAlpha ? null : (color.a ?? null); + //console.log(`Colors loaded (${red}, ${green}, ${blue})`); + red = Math.round(red); + green = Math.round(green); + blue = Math.round(blue); + if(alpha !== null) { alpha = Math.round(alpha * 255) }; + //console.log(`Colors rounded to (${red}, ${green}, ${blue})`); + red = bound(red,0,255) + green = bound(green,0,255) + blue = bound(blue,0,255) + if(alpha !== null) { alpha = bound(alpha,0,255) }; + //console.log(`Colors bounded to (${red}, ${green}, ${blue})`); + red = red.toString(16); + green = green.toString(16); + blue = blue.toString(16); + if(alpha !== null) { alpha = alpha.toString(16) }; + //console.log(`Colors converted to (0x${red}, 0x${green}, 0x${blue})`); + //console.log("Padding R"); + while(red.length < 2) { + red = "0" + red; + }; + //console.log("Padding G"); + while(green.length < 2) { + green = "0" + green; + }; + //console.log("Padding B"); + while(blue.length < 2) { + blue = "0" + blue; + }; + //console.log("Padding A"); + if(alpha !== null) { + while(alpha.length < 2) { + alpha = "0" + alpha; + } + }; + //console.log(`Colors padded to (0x${red}, 0x${green}, 0x${blue}), concatenating...`); + return "#" + red + green + blue + (stripAlpha ? "" : (alpha ?? "")); + } else { + throw new Error(`Received invalid color: ${color}`); + }; + }; + function linearBlendTwoColorObjects(color1,color2,weight1=0.5) { /*third argument is for color1 and expects a float from 0 + to 1, where 0 means "all color2" and 1 means "all color1"*/ + var w1 = Math.min(Math.max(weight1,0),1); + var red1 = color1.r; + var green1 = color1.g; + var blue1 = color1.b; + var red2 = color2.r; + var green2 = color2.g; + var blue2 = color2.b; + var red3 = (red1 * w1) + (red2 * (1 - w1)); + var green3 = (green1 * w1) + (green2 * (1 - w1)); + var blue3 = (blue1 * w1) + (blue2 * (1 - w1)); + return {r: red3, g: green3, b: blue3}; + }; + function lightenColor(color,offset,outputType="rgb") { + if(typeof(color) === "string") { + if(color.length < 10) { + //console.log(`detected as hex: ${color}`); + //catch missing octothorpes + if(!color.startsWith("#")) { + color = "#" + color; + }; + //console.log(`octothorpe checked: ${color}`); + offset = parseFloat(offset); + if(isNaN(offset)) { + throw new Error("Offset is NaN"); + }; + var oldColor = color; //for error display + color = hexToRGB(color); + if(color === null) { + throw new Error(`hexToRGB(color) was null (${oldColor}, maybe it's an invalid hex triplet?)`); + }; + //console.log("converted color: " + JSON.stringify(color)); + var red = color.r + offset; + var green = color.g + offset; + var blue = color.b + offset; + //console.log(`altered color: rgb(${red},${green},${blue})`); + //rounding and bounding + red = Math.round(red); + green = Math.round(green); + blue = Math.round(blue); + //console.log(`rounded color: rgb(${red},${green},${blue})`); + red = bound(red,0,255) + green = bound(green,0,255) + blue = bound(blue,0,255) + //console.log(`bounded color: rgb(${red},${green},${blue})`); + color = {r: red, g: green, b: blue}; + switch(outputType.toLowerCase()) { + case "rgb": + return `rgb(${red},${green},${blue})`; + break; + case "hex": + return rgbToHex(color); + break; + case "json": + return color; + break; + default: + throw new Error("outputType must be \"rgb\", \"hex\", \"json\""); + }; + } else { + if(color.startsWith("rgb(")) { + color = convertColorFormats(color,"json"); //object conversion + //console.log(`color converted to object: ${JSON.stringify(color)}`); + offset = parseFloat(offset); + if(isNaN(offset)) { + throw new Error("Offset is NaN"); + }; + var red = color.r + offset; + var green = color.g + offset; + var blue = color.b + offset; + //console.log(`altered color: rgb(${red},${green},${blue})`); + //rounding and bounding + red = Math.round(red); + green = Math.round(green); + blue = Math.round(blue); + //console.log(`rounded color: rgb(${red},${green},${blue})`); + red = bound(red,0,255) + green = bound(green,0,255) + blue = bound(blue,0,255) + //console.log(`bounded color: rgb(${red},${green},${blue})`); + color = {r: red, g: green, b: blue}; + switch(outputType.toLowerCase()) { + case "rgb": + return `rgb(${red},${green},${blue})`; + break; + case "hex": + return rgbToHex(color); + break; + case "json": + return color; + break; + default: + throw new Error("outputType must be \"rgb\", \"hex\", \"json\""); + }; + } /*else if(color.startsWith("hsl")) { + throw new Error("HSL is not implemented yet"); + }*/ else { + throw new Error('Color must be of the type "rgb(red,green,blue)"'/* or "hsl(hue,saturation%,lightness%)"*/); + }; + }; + } else if(typeof(color) === "object") { + if(typeof(color.r) === "undefined" || typeof(color.g) === "undefined" || typeof(color.b) === "undefined") { + throw new Error("Color must be of the form {r: red, g: green, b: blue}"); + }; + //console.log("received color: " + JSON.stringify(color)); + var red = color.r + offset; + var green = color.g + offset; + var blue = color.b + offset; + //console.log(`altered color: rgb(${red},${green},${blue})`); + //rounding and bounding + red = Math.round(red); + green = Math.round(green); + blue = Math.round(blue); + //console.log(`rounded color: rgb(${red},${green},${blue})`); + red = bound(red,0,255) + green = bound(green,0,255) + blue = bound(blue,0,255) + //console.log(`bounded color: rgb(${red},${green},${blue})`); + color = {r: red, g: green, b: blue}; + switch(outputType.toLowerCase()) { + case "rgb": + return `rgb(${red},${green},${blue})`; + break; + case "hex": + return rgbToHex(color); + break; + case "json": + return color; + break; + default: + throw new Error("outputType must be \"rgb\", \"hex\", \"json\""); + }; + }; + }; + function rgbObjectToString(color,stripAlpha=false) { + if(typeof(color) !== "object") { + throw new Error("Input color is not an object"); + }; + var red = color.r; + var green = color.g; + var blue = color.b; + //console.log(`Colors loaded (${red}, ${green}, ${blue})`); + red = Math.round(red); + green = Math.round(green); + blue = Math.round(blue); + //console.log(`Colors rounded to (${red}, ${green}, ${blue})`); + red = bound(red,0,255) + green = bound(green,0,255) + blue = bound(blue,0,255) + //console.log(`Colors bounded to (${red}, ${green}, ${blue})`); + return `rgb(${red},${green},${blue})` + }; + function convertColorFormats(color,outputType="rgb",stripAlpha=false) { + if(typeof(color) === "undefined") { + //console.log("Warning: An element has an undefined color. Unfortunately, due to how the code is structured, I can't say which one."); + //color = "#FF00FF"; + throw new Error("Color is undefined!"); + }; + //console.log("Logged color for convertColorFormats: " + color); + var oldColor = color; + var bytes,r,g,b,a; + if(typeof(color) === "string") { + //Hex input case + if(/^#?[A-Za-z0-9]{1,8}$/.test(oldColor)) { + if(color.startsWith("#")) { + oldColor = oldColor.match(/[A-Za-z0-9]{1,8}/)[0]; + }; + switch(oldColor.length) { + case 0: + throw new Error("convertColorFormats: hexadecimal input but color bytes are somehow missing?"); + case 1: //A => AAAAAA (bs) + oldColor = oldColor.repeat(6); + break + case 2: //AB => AAAAAABB (bs) + oldColor = (oldColor[0].repeat(6)) + (oldColor[1].repeat(2)); + break + case 3: //ABC => AABBCC, ABCD => AABBCCDD (real) + case 4: + oldColor = oldColor.match(/[A-Za-z0-9]/g).map(x => x.repeat(2)).join("") + break + case 5: //ABCDE => AABBCCDE (bs) + var _rgb = oldColor.slice(0,3); + var _alpha = oldColor.slice(3,5); + oldColor = (_rgb.match(/[A-Za-z0-9]/g).map(x => x.repeat(2)).join("")) + alpha + case 7: //9ABCDEF => 9ABCDEFF (bs) + var _rgb = oldColor.slice(0,6); + var _alpha = oldColor.slice(6,7); + oldColor = _rgb + (_alpha.repeat(2)) + case 6: //no change + case 8: //no change + break + }; + bytes = oldColor.toLowerCase().match(/[a-z0-9]{2}/g).map(x => parseInt(x,16)); + r = bytes[0]; + g = bytes[1]; + b = bytes[2]; + a = stripAlpha ? null : (bytes[3] ?? null); + if(a !== null) { a = bound(a,0,1) }; + //to JSON for ease of use + color = {"r": r, "g": g, "b": b}; + if(typeof(a) == "number") { color["a"] = a }; + } else { + //otherwise assume rgb() input + bytes = color.match(/[\d\.]+/g); + if(typeof(bytes?.map) == "undefined") { + console.error(`convertColorFormats: Invalid color ${JSON.stringify(color)} producing null bytes ${bytes}`); + bytes = [255,0,255] + } else { + bytes = bytes.map(x => Number(x)); + }; + r = bytes[0]; + g = bytes[1]; + b = bytes[2]; + a = stripAlpha ? null : (bytes[3] ?? null); + if(a !== null) { a = bound(a,0,1) }; + //to JSON for ease of use + color = {"r": r, "g": g, "b": b} + if(typeof(a) == "number") { color["a"] = a }; + }; + } else if(Array.isArray(color)) { + bytes = color; + r = bytes[0]; + g = bytes[1]; + b = bytes[2]; + a = stripAlpha ? null : (bytes[3] ?? null); + //to JSON for ease of use + color = {"r": r, "g": g, "b": b} + if(typeof(a) == "number") { color["a"] = a }; + } else if(typeof(color) == "object") { + //variable mappings only + r = color.r; + g = color.g; + b = color.b; + a = stripAlpha ? null : (color.a ?? null); + }; + //Colors are now objects + if(a !== null) { a /= 255 }; + switch(outputType.toLowerCase()) { + case "rgb": + case "rgba": + var _r,_g,_b,_a; + _r = r; + _g = g; + _b = b; + if(typeof(a) == "number") { _a = a } else { _a = null }; + var values; + if(stripAlpha || _a == null) { + values = [_r,_g,_b]; + } else { + values = [_r,_g,_b,_a]; + }; + for(var i = 0; i <= 2; i++) { + values[i] = Math.round(values[i]) + }; + return (typeof(a) == "number" ? "rgba" : "rgb") + `(${values.join(",")})` + case "hex": + var _r,_g,_b,_a; + _r = r; + _g = g; + _b = b; + if(typeof(a) == "number") { _a = Math.round(a * 255) } else { _a = null }; + var bytesToBe; + if(stripAlpha || _a == null) { + bytesToBe = [_r,_g,_b]; + } else { + bytesToBe = [_r,_g,_b,_a]; + }; + return "#" + bytesToBe.map(x => Math.round(x).toString(16).padStart(2,"0")).join(""); + case "json": + return color; + case "array": + return Object.values(color); + break; + default: + throw new Error("outputType must be \"rgb\", \"hex\", \"json\", or \"array\""); + } + }; + function rgbHexCatcher(color) { + return convertColorFormats(color,"rgb"); + }; + function _rgbHexCatcher(color) { + return convertColorFormats(color,"rgb"); + }; + function averageColorObjects(color1,color2,weight1=0.5) { //misnomer, actually a linear interpolation but it's too late to rename that + //third argument is for color1 and expects a float from 0 to 1, where 0 means "all color2" and 1 means "all color1" + //(backwards from how it should work) + var w1 = Math.min(Math.max(weight1,0),1) + var red1 = color1.r + var green1 = color1.g + var blue1 = color1.b + var red2 = color2.r + var green2 = color2.g + var blue2 = color2.b + var red3 = (red1 * w1) + (red2 * (1 - w1)) + var green3 = (green1 * w1) + (green2 * (1 - w1)) + var blue3 = (blue1 * w1) + (blue2 * (1 - w1)) + return {r: red3, g: green3, b: blue3} + }; + function lerpColors(color1,color2,outputType="rgb",weight1=0.5) { + color1 = convertColorFormats(color1,"json"); + color2 = convertColorFormats(color2,"json"); + theColor = averageColorObjects(color1,color2,weight1); + return convertColorFormats(theColor,outputType); + }; + function multiplyColors(color1,color2,outputType="rgb") { + //normalize rgb()/hex by turning any hex into rgb() and then rgb()s to {r,g,b} + if(typeof(color1) !== "object") { + color1 = convertColorFormats(color1,"json"); + }; + if(typeof(color2) !== "object") { + color2 = convertColorFormats(color2,"json"); + }; + var finalR = Math.round(color1.r * (color2.r/255)); + var finalG = Math.round(color1.g * (color2.g/255)); + var finalB = Math.round(color1.b * (color2.b/255)); + var finalColor = {r: finalR, g: finalG, b: finalB}; + switch(outputType.toLowerCase()) { + case "rgb": + return `rgb(${finalColor.r},${finalColor.g},${finalColor.b})`; + break; + case "hex": + return rgbToHex(finalColor); + break; + case "json": + return finalColor; + break; + default: + throw new Error("outputType must be \"rgb\", \"hex\", \"json\""); + }; + }; + function divideColors(color1,color2,outputType="rgb") { //color2 is the divisor and color1 the dividend (base/original color) + //normalize rgb()/hex by turning any hex into rgb() and then rgb()s to {r,g,b} + if(typeof(color1) !== "object") { + color1 = convertColorFormats(color1,"json"); + }; + if(typeof(color2) !== "object") { + color2 = convertColorFormats(color2,"json"); + }; + var finalR = bound(Math.round(255 / (color2.r / color1.r)),0,255); + var finalG = bound(Math.round(255 / (color2.g / color1.g)),0,255); + var finalB = bound(Math.round(255 / (color2.b / color1.b)),0,255); + if(isNaN(finalR)) { finalR = 255 }; + if(isNaN(finalG)) { finalG = 255 }; + if(isNaN(finalB)) { finalB = 255 }; + var finalColor = {r: finalR, g: finalG, b: finalB}; + switch(outputType.toLowerCase()) { + case "rgb": + return `rgb(${finalColor.r},${finalColor.g},${finalColor.b})`; + break; + case "hex": + return rgbToHex(finalColor); + break; + case "json": + return finalColor; + break; + default: + throw new Error("outputType must be \"rgb\", \"hex\", \"json\""); + }; + }; + function addColors(color1,color2,outputType="rgb") { + //console.log("colors",color1,color2); + //normalize rgb()/hex by turning any hex into rgb() and then rgb()s to {r,g,b} + if(typeof(color1) !== "object") { + color1 = convertColorFormats(color1,"json"); + }; + if(typeof(color2) !== "object") { + color2 = convertColorFormats(color2,"json"); + }; + //if(JSON.stringify([color1,color2]).indexOf("NaN") >= 0) { console.log(color1,color2) } + var finalR = bound(Math.round(color1.r + color2.r),0,255) + var finalG = bound(Math.round(color1.g + color2.g),0,255) + var finalB = bound(Math.round(color1.b + color2.b),0,255) + var finalColor = {r: finalR, g: finalG, b: finalB}; + switch(outputType.toLowerCase()) { + case "rgb": + return `rgb(${finalColor.r},${finalColor.g},${finalColor.b})`; + break; + case "hex": + return rgbToHex(finalColor); + break; + case "json": + return finalColor; + break; + default: + throw new Error("outputType must be \"rgb\", \"hex\", \"json\""); + }; + }; + function subtractColors(color1,color2,outputType="rgb") { + //normalize rgb()/hex by turning any hex into rgb() and then rgb()s to {r,g,b} + if(typeof(color1) !== "object") { + color1 = convertColorFormats(color1,"json"); + }; + if(typeof(color2) !== "object") { + color2 = convertColorFormats(color2,"json"); + }; + var finalR = bound(Math.round(color1.r - color2.r),0,255) + var finalG = bound(Math.round(color1.g - color2.g),0,255) + var finalB = bound(Math.round(color1.b - color2.b),0,255) + var finalColor = {r: finalR, g: finalG, b: finalB}; + switch(outputType.toLowerCase()) { + case "rgb": + return `rgb(${finalColor.r},${finalColor.g},${finalColor.b})`; + break; + case "hex": + return rgbToHex(finalColor); + break; + case "json": + return finalColor; + break; + default: + throw new Error("outputType must be \"rgb\", \"hex\", \"json\""); + }; + }; + function averageRgbPrefixedColorArray(colorArray,returnObject=false) { //array of rgb()s to single rgb() of average color + //averageRgbPrefixedColorArray(["rgb(255,0,0)", _cc.b.r, "rgb(0,0,255)"]); + //console.log("Averaging started"); + var reds = []; + var greens = []; + var blues = []; + for(k = 0; k < colorArray.length; k++) { + //console.log("Average function: Executing catcher on " + colorArray); + var color = convertColorFormats(colorArray[k]); + //console.log("Logged color for aRPCA: " + color); + color = color.split(","); + var red = parseFloat(color[0].substring(4)); + reds.push(red) + var green = parseFloat(color[1]); + greens.push(green) + var blue = parseFloat(color[2].slice(0,-1)); + blues.push(blue) + }; + redAverage = Math.round(averageNumericArray(reds)); + greenAverage = Math.round(averageNumericArray(greens)); + blueAverage = Math.round(averageNumericArray(blues)); + var output; + returnObject ? output = {r: redAverage, g: greenAverage, b: blueAverage} : output = `rgb(${redAverage},${greenAverage},${blueAverage})`; + //console.log("Averaging finished, product: " + output); + return output; + }; + //https://stackoverflow.com/questions/46432335/hex-to-hsl-convert-javascript + function rgbStringToHSL(rgb,outputType="array",stripAlpha=false) { //Originally a hex-to-HSL function, edited to take RGB and spit out an array + //console.log("HSLing some RGBs"); + var result = rgbStringToUnvalidatedObject(rgb); + var r = result.r; + var g = result.g; + var b = result.b; + var a = stripAlpha ? null : (result.a ?? null); + r /= 255, g /= 255, b /= 255; if(a) { a /= 255 }; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, l = (max + min) / 2; + if(max == min){ + h = s = 0; // achromatic + } else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + }; + s = s*100; + s = Math.round(s); + l = l*100; + l = Math.round(l); + h = Math.round(360*h); + if(a !== null && a > 1) { a /= 255 }; + //var colorInHSL = 'hsl(' + h + ', ' + s + '%, ' + l + '%)'; + //Edit to return an array + switch(outputType.toLowerCase()) { + case "array": + return a == null ? [h,s,l] : [h,s,l,a]; + break; + case "hsl": + return a == null ? `hsl(${h},${s}%,${l}%)` : `hsla(${h},${s}%,${l}%,${a})`; + break; + case "json": + return a == null ? {h: h, s: s, l: l} : {h: h, s: s, l: l, a: a}; + default: + throw new Error("outputType must be \"array\", \"hsl\", or \"json\""); + break; + }; + //console.log("HSL output "+ colorInHSL + "."); + }; + function normalizeColorToHslObject(color,arrayType=null,stripAlpha=false) { + var ambiguousArrayError = "changeSaturation can't tell if the array input is supposed to be RGB or HSL. Please use an \"arrayType\" argument of \"rgb\" or \"hsl\"."; + var isHsl = false; + if(Array.isArray(color)) { + if(arrayType === null) { + throw new Error(ambiguousArrayError); + } else if(arrayType === "rgb") { + color = ((color.length > 3) && !stripAlpha) ? `rgba(${color[0]},${color[1]},${color[2]},${color[3]})` : `rgb(${color[0]},${color[1]},${color[2]})`; + color = rgbStringToHSL(color,"json"); //rgb arr to hsl obj + } else if(arrayType === "hsl") { + color = ((color.length > 3) && !stripAlpha) > 3 ? {h: color[0], s: color[1], l: color[2], a: color[3]} : {h: color[0], s: color[1], l: color[2]}; //hsl arr to hsl obj + } else { + throw new Error(ambiguousArrayError); + }; + } else { + //by this point, any array cases would have been handled, leaving just hex (rgb), json rgb, json hsl, string rgb, and string hsl + if(typeof(color) === "string") { + if(/^#?[A-Za-z0-9]{1,8}$/.test(color)) { //detect hex + if(!color.startsWith("#")) { + color = "#" + color; //catch missing # + }; + isHsl = false; + }; + if(color.startsWith("rgb")) { //detect rgba?(): self-explanatory + isHsl = false; + }; + if(color.startsWith("hsl")) { //detect hsla?(): self-explanatory + isHsl = true; + }; + } else if(typeof(color) === "object") { + if(typeof(color.r) !== "undefined") { //detect {r,g,b}: check for r key + isHsl = false; + }; + if(typeof(color.h) !== "undefined") { //detect {h,s,l}: check for h key + isHsl = true; + }; + }; + if(!isHsl) { + color = convertColorFormats(color,"rgb",stripAlpha); //make any RGBs rgb() + color = rgbStringToHSL(color,"json",stripAlpha); //make that rgb() an {h,s,l} + } else { //by this point, it would have to either be a string or an object + if(typeof(color) === "string") { //if it's a string + color = hslColorStringToObject(color,stripAlpha) //now it's an object + }; + }; + }; + return color; + }; + function convertHslObjects(color,outputType="rgb") { + if(color == null) { console.error("convertHslObjects: Color is null"); color = {h: 300, s: 100, l: 50} }; + switch(outputType.toLowerCase()) { + //RGB cases + case "rgb": + color = convertColorFormats(hslToHex(...Object.values(color)),"json"); //hsl to hex, hex to rgb_json, and rgb_json to rgb() + return `rgb(${color.r},${color.g},${color.b})`; + break; + case "hex": + color = hslToHex(...Object.values(color)); //hsl to hex + return color; + break; + case "rgbjson": + case "rgb-json": + case "rgb_json": + color = hexToRGB(hslToHex(...Object.values(color))); //hsl to hex and hex to rgb_json + return color; + break; + case "rgbarray": + case "rgb-array": + case "rgb_array": + color = hexToRGB(hslToHex(...Object.values(color))); //hsl to hex, hex to rgb_json, and rgb_json to rgb_array + return [color.r, color.g, color.b]; + break; + //HSL cases + case "hsl": + //note: color was previously converted to {h, s, l} + return `hsl(${color.h},${color.s}%,${color.l}%)`; + break; + case "hsljson": + case "hsl-json": + case "hsl_json": + return color; + break; + case "hslarray": + case "hsl-array": + case "hsl_array": + return [color.h, color.s, color.l]; + break; + default: + throw new Error("outputType must be \"rgb\", \"hex\", \"rgb_json\", \"rgb_array\", \"hsl\", \"hsl_json\", or \"hsl_array\""); + } + }; + function changeSaturation(color,saturationChange,operationType="add",outputType="rgb",arrayType=null,doRounding=true) { + color = normalizeColorToHslObject(color,arrayType); + //only {h,s,l} should exist now + //Math + switch(operationType.toLowerCase()) { + case "+": + case "add": + color.s += saturationChange; + break; + case "-": + case "subtract": + color.s -= saturationChange; + break; + case "*": + case "x": + case "×": + case "multiply": + color.s *= saturationChange; + break; + case "/": + case "÷": + case "divide": + color.s /= saturationChange; + break; + case "=": + case "set": + color.s = saturationChange; + break; + case ">": + case ">=": + case "min": //lower-bounds the color + color.s = Math.max(color.s,saturationChange); //math.max to bound it to the higher of the input number or the existing color + break; + case "<": + case "<=": + case "max": //upper-bounds the color + color.s = Math.min(color.s,saturationChange); //math.min to bound it to the lower of the input number or the existing color + break; + default: + throw new Error("Operation must be \"add\", \"subtract\", \"multiply\", \"divide\", \"set\", \"min\", or \"max\""); + }; + color.h = doRounding ? Math.round(color.h % 360) : color.h % 360; + color.s = doRounding ? Math.round(bound(color.s,0,100)) : color.s,0,100; + color.l = doRounding ? Math.round(bound(color.l,0,100)) : color.l,0,100; + return convertHslObjects(color,outputType) + }; + function changeLightness(color,lightnessChange,operationType="add",outputType="rgb",arrayType=null,doRounding=true) { + color = normalizeColorToHslObject(color,arrayType); + //only {h,s,l} should exist now + //Math + switch(operationType.toLowerCase()) { + case "+": + case "add": + color.l += lightnessChange; + break; + case "-": + case "subtract": + color.l -= lightnessChange; + break; + case "*": + case "x": + case "×": + case "multiply": + color.l *= lightnessChange; + break; + case "/": + case "÷": + case "divide": + color.l /= lightnessChange; + break; + case "=": + case "set": + color.l = lightnessChange; + break; + case ">": + case ">=": + case "min": + color.l = Math.max(color.l,lightnessChange); + break; + case "<": + case "<=": + case "max": + color.l = Math.min(color.l,lightnessChange); + break; + default: + throw new Error("Operation must be \"add\", \"subtract\", \"multiply\", \"divide\", \"set\", \"min\", or \"max\""); + }; + color.h = doRounding ? Math.round(color.h % 360) : color.h % 360; + color.s = doRounding ? Math.round(bound(color.s,0,100)) : color.s,0,100; + color.l = doRounding ? Math.round(bound(color.l,0,100)) : color.l,0,100; + return convertHslObjects(color,outputType); + }; + function changeHue(color,hueChange,operationType="add",outputType="rgb",arrayType=null,doRounding=true) { + color = normalizeColorToHslObject(color,arrayType); + //only {h,s,l} should exist now + //Math + switch(operationType.toLowerCase()) { + case "+": + case "add": + color.h += hueChange; + break; + case "-": + case "subtract": + color.h -= hueChange; + break; + case "*": + case "x": + case "×": + case "multiply": + color.h *= hueChange; + break; + case "/": + case "÷": + case "divide": + color.h /= hueChange; + break; + case "=": + case "set": + color.h = hueChange; + break; + case ">": + case ">=": + case "min": + color.h = Math.max(color.h,hueChange); + break; + case "<": + case "<=": + case "max": + color.h = Math.min(color.h,hueChange); + break; + default: + throw new Error("Operation must be \"add\", \"subtract\", \"multiply\", \"divide\", \"set\", \"min\", or \"max\""); + }; + color.h = doRounding ? Math.round(color.h % 360) : color.h % 360; + color.s = doRounding ? Math.round(bound(color.s,0,100)) : color.s,0,100; + color.l = doRounding ? Math.round(bound(color.l,0,100)) : color.l,0,100; + return convertHslObjects(color,outputType); + }; + function colorToHsl(color,outputType="json") { + if(typeof(color.h) == "number" && typeof(color.s) == "number" && typeof(color.l) == "number") { + return color + }; + color = convertColorFormats(color,"rgb"); + color = rgbStringToHSL(color,outputType); + return color; + }; + //https://stackoverflow.com/questions/36721830/convert-hsl-to-rgb-and-hex + function hslToHex(h, s, l) { //h, s, l params to hex triplet + //console.log(`Hexing some HSLs (the HSLs are ${h},${s},${l})`) + s = bound(s,0,100); //limit to 0-100 + l = bound(l,0,100); + l /= 100; + var a = s * Math.min(l, 1 - l) / 100; + var f = n => { + var k = (n + h / 30) % 12; + var color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); + return Math.round(255 * color).toString(16).padStart(2, '0'); // convert to Hex and prefix "0" if needed + }; + //console.log(`Hexed to #${f(0)}${f(8)}${f(4)}`) + return `#${f(0)}${f(8)}${f(4)}`; + }; + //Pixels + function tryMoveAndReturnBlockingPixel(pixel,nx,ny,leaveBehind,force) { + if(outOfBounds(nx,ny)) { return false }; + if(isEmpty(nx,ny,false)) { return tryMove(pixel,nx,ny,leaveBehind,force) }; + return pixelMap[nx][ny] + }; + + function exposedToAir(pixel) { + return (isEmpty(pixel.x+1,pixel.y) || isEmpty(pixel.x-1,pixel.y) || isEmpty(pixel.x,pixel.y+1) || isEmpty(pixel.x,pixel.y-1)); + }; + + function tryTarnish(pixel,element,chance) { + if(exposedToAir(pixel)) { + if(Array.isArray(element)) { + if(Math.random() < chance) { + changePixel(pixel,randomChoice(element)); + }; + } else { + if(Math.random() < chance) { + changePixel(pixel,element); + }; + }; + }; + }; + + //Try to create a pixel, return true if it could be created and false if it couldn't + function tryCreatePixel(elementInput,x,y) { + //array handling + if(elementInput.includes(",")) { //CSTA + elementInput = elementInput.split(","); + }; + if(Array.isArray(elementInput)) { //if element list + elementInput = elementInput.filter(function(e) { + return elementExists(e); + }); + if(elementInput.length === 0) { throw new Error("elementInput has no existing elements") }; + elementInput = randomChoice(elementInput); + }; + //existence check + if(!elementExists(elementInput)) { + throw new Error("Element " + elementInput + " doesn't exist!"); + }; + //actual creation check + if(isEmpty(x,y)) { + createPixel(elementInput,x,y); + return true; + } else { + return false; + }; + }; + + function createOrChangePixel(elementInput,x,y) { + //array handling + if(elementInput.includes(",")) { //CSTA + elementInput = elementInput.split(","); + }; + if(Array.isArray(elementInput)) { //if element list + elementInput = elementInput.filter(function(e) { + return elementExists(e); + }); + if(elementInput.length === 0) { throw new Error("elementInput has no existing elements") }; + elementInput = randomChoice(elementInput); + }; + //existence check + if(!elementExists(elementInput)) { + throw new Error("Element " + elementInput + " doesn't exist!"); + }; + //actual creation check + if(outOfBounds(x,y)) { return false }; + if(isEmpty(x,y)) { + createPixel(elementInput,x,y); + } else { + changePixel(pixelMap[x][y],elementInput) + }; + return true + }; + + function createOrChangePixelAndReturn(elementInput,x,y) { + //array handling + if(elementInput.includes(",")) { //CSTA + elementInput = elementInput.split(","); + }; + if(Array.isArray(elementInput)) { //if element list + elementInput = elementInput.filter(function(e) { + return elementExists(e); + }); + if(elementInput.length === 0) { throw new Error("elementInput has no existing elements") }; + elementInput = randomChoice(elementInput); + }; + //existence check + if(!elementExists(elementInput)) { + throw new Error("Element " + elementInput + " doesn't exist!"); + }; + //actual creation check + if(outOfBounds(x,y)) { return false }; + if(isEmpty(x,y)) { + return createPixelReturn(elementInput,x,y); + } else { + return changePixelReturn(pixelMap[x][y],elementInput) + } + return true + }; + + function tryCreatePixelReturn(elementInput,x,y) { + //array handling + if(elementInput.includes(",")) { //CSTA + elementInput = elementInput.split(","); + }; + if(Array.isArray(elementInput)) { //if element list + elementInput = elementInput.filter(function(e) { + return elementExists(e); + }); + if(elementInput.length === 0) { throw new Error("elementInput has no existing elements") }; + elementInput = randomChoice(elementInput); + }; + //existence check + if(!elementExists(elementInput)) { + throw new Error("Element " + elementInput + " doesn't exist!"); + }; + //actual creation check + if(isEmpty(x,y)) { + return createPixelReturn(elementInput,x,y); + } else { + return false; + }; + }; + + function createPixelReturn(elementIn,x,y) { //sugar + var element = elementIn; while(element instanceof Array) { element = randomChoice(element) }; + var newPixel = new Pixel(x, y, element); + currentPixels.push(newPixel); + checkUnlock(element); + return newPixel; + }; + + function changePixelReturn(pixel,element,changetemp=true) { + if(typeof(elements[element]) == "undefined") { + if(doLog) { console.error(`Something tried to change a pixel of ${pixel.element} at (${pixel.x},${pixel.y}) to nonexistent element "${element}"`) }; + return false; + }; + pixel.element = element; + pixel.color = pixelColorPick(pixel); + pixel.start = pixelTicks; + var elementInfo = elements[element]; + if (elementInfo.burning == true) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } + else if (pixel.burning && !elementInfo.burn) { + delete pixel.burning; + delete pixel.burnStart; + } + delete pixel.origColor; // remove stain + if (pixel.r && !elementInfo.rotatable) { + delete pixel.r; + } + if (pixel.flipX && !elementInfo.flippableX) { + delete pixel.flipX; + } + if (pixel.flipY && !elementInfo.flippableY) { + delete pixel.flipY; + } + // If elementInfo.flippableX, set it to true or false randomly + if (elementInfo.flipX !== undefined) { pixel.flipX = elementInfo.flipX } + else if (elementInfo.flippableX) { + pixel.flipX = Math.random() >= 0.5; + } + // If elementInfo.flippableY, set it to true or false randomly + if (elementInfo.flipY !== undefined) { pixel.flipY = elementInfo.flipY } + else if (elementInfo.flippableY) { + pixel.flipY = Math.random() >= 0.5; + } + if (elementInfo.temp != undefined && changetemp) { + pixel.temp = elementInfo.temp; + pixelTempCheck(pixel) + } + // If elementInfo.properties, set each key to its value + if (elementInfo.properties !== undefined) { + for (var key in elementInfo.properties) { + // If it is an array or object, make a copy of it + if (typeof elementInfo.properties[key] == "object") { + pixel[key] = JSON.parse(JSON.stringify(elementInfo.properties[key])); + } + else { + pixel[key] = elementInfo.properties[key]; + } + } + } + checkUnlock(element); + return pixel; + }; + function storeFirstTouchingElement(pixel,propertyName,copyTemp=true,spread=true) { + var info = elements[pixel.element]; + if(pixel[propertyName]) { + return false; + }; + for(i = 0; i < adjacentCoords.length; i++) { + var newCoords = {x: pixel.x+adjacentCoords[i][0], y: pixel.y+adjacentCoords[i][1]}; + if (!isEmpty(newCoords.x,newCoords.y,true)) { + newPixel = pixelMap[newCoords.x][newCoords.y]; + if (info.ignore && info.ignore.indexOf(newPixel.element) !== -1) { + continue; + }; + if (newPixel.element != pixel.element && newPixel.element != "wire") { + pixel[propertyName] = newPixel.element; + if(copyTemp) { pixel.temp = newPixel.temp }; + return newPixel.element; + } + else if (newPixel[propertyName] && spread) { + pixel[propertyName] = newPixel[propertyName]; + pixel.temp = newPixel.temp; + return newPixel[propertyName]; + } + } + }; + }; + function breakPixel(pixel,changeTemp=false,defaultBreakIntoDust=false) { + var result = elements[pixel.element].breakInto; + if (result === undefined) {if(defaultBreakIntoDust) { result = "dust" } else { return }}; + // if it is an array, choose a random item, else just use the value + while (Array.isArray(result)) { + result = randomChoice(result); + }; + // change the pixel to the result + if (result === null) { + deletePixel(pixel.x,pixel.y); + return + }; + if (elements[pixel.element].breakIntoColor) { + var oldelement = pixel.element; + changePixel(pixel,result,changeTemp); + pixel.color = pixelColorPick(pixel, elements[oldelement].breakIntoColor); + } + else { + changePixel(pixel,result,changeTemp); + } + } + defaultHardness = 0.3; + function tryBreak(pixel,changetemp=false,defaultBreakIntoDust=false) { + var info = elements[pixel.element]; + var hardness = defaultHardness; + if(typeof(info.hardness) === "number") { + hardness = info.hardness; + }; + hardness = 1 - hardness; //invert hardness, so a hardness of 0 becomes a 100% chance and a hardness of 1 becomes a 0% chance + if(Math.random() < hardness) { + return breakPixel(pixel,changetemp=false,defaultBreakIntoDust=false); + } else { + return false; + }; + }; + function reactionStealer(pixel,newPixel,reactionTarget) { + if(!elements[reactionTarget]) { + throw new Error(`No such element ${reactionTarget}!`); + }; + if(typeof(newPixel) === "undefined") { //timing issue? + return false; + }; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(typeof(newInfo.reactions) === "undefined") { + return false; + }; + if(typeof(newInfo.reactions[reactionTarget]) === "undefined") { + return false; + }; + var pixel2 = pixel; + var pixel1 = newPixel; + var r = newInfo.reactions[reactionTarget]; + if (r.chance !== undefined && Math.random() > r.chance) { + return false; + } + if (r.setting && settings[r.setting]===0) { + return false; + } + if (r.tempMin !== undefined && pixel1.temp < r.tempMin) { + return false; + } + if (r.tempMax !== undefined && pixel1.temp > r.tempMax) { + return false; + } + if (r.burning1 !== undefined && Boolean(pixel1.burning) !== r.burning1) { + return false; + } + if (r.burning2 !== undefined && Boolean(pixel2.burning) !== r.burning2) { + return false; + } + if (r.charged && !(pixel1.charge || pixel2.charge)) { + return false; + } + if (r.y !== undefined && (pixel1.y < r.y[0] || pixel1.y > r.y[1])) { + return false; + } + if (r.elem1 !== undefined) { + // if r.elem1 is an array, set elem1 to a random element from the array, otherwise set it to r.elem1 + if (Array.isArray(r.elem1)) { + var elem1 = r.elem1[Math.floor(Math.random() * r.elem1.length)]; + } else { var elem1 = r.elem1; } + if (elem1 == null) { + deletePixel(pixel1.x,pixel1.y); + } + else { + changePixel(pixel1,elem1); + } + } + if(pixel1) { + if (r.charge1) { pixel1.charge = r.charge1; } + if (r.temp1) { pixel1.temp += r.temp1; pixelTempCheck(pixel1); } + if (r.color1) { // if it's a list, use a random color from the list, else use the color1 attribute + pixel1.color = pixelColorPick(pixel1, Array.isArray(r.color1) ? r.color1[Math.floor(Math.random() * r.color1.length)] : r.color1); + } + if (r.attr1) { // add each attribute to pixel1 + for (var key in r.attr1) { + pixel1[key] = r.attr1[key]; + } + } + if (r.stain1) { stainPixel(pixel1,r.stain1,0.05); } + }; + if (r.elem2 !== undefined) { + // if r.elem2 is an array, set elem2 to a random element from the array, otherwise set it to r.elem2 + if (Array.isArray(r.elem2)) { + var elem2 = r.elem2[Math.floor(Math.random() * r.elem2.length)]; + } else { var elem2 = r.elem2; } + if (elem2 == null) { + deletePixel(pixel2.x,pixel2.y); + } + else { + changePixel(pixel2,elem2); + } + } + if(pixel2) { + if (r.charge2) { pixel2.charge = r.charge2; } + if (r.temp2) { pixel2.temp += r.temp2; pixelTempCheck(pixel2); } + if (r.color2) { // if it's a list, use a random color from the list, else use the color2 attribute + pixel2.color = pixelColorPick(pixel2, Array.isArray(r.color2) ? r.color2[Math.floor(Math.random() * r.color2.length)] : r.color2); + } + if (r.attr2) { // add each attribute to pixel2 + for (var key in r.attr2) { + pixel2[key] = r.attr2[key]; + } + } + if (r.stain2) { stainPixel(pixel2,r.stain2,0.05); } + }; + if(pixel1 && pixel2) { + if (r.func) { r.func(pixel1,pixel2); } + } + return r.elem1!==undefined || r.elem2!==undefined; + }; + function spreadingProperty(pixel,propertyName,whitelist=null) { + if(isNaN(pixel[propertyName])) { + pixel[propertyName] = 0; + }; + var randomNeighborOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var rX = randomNeighborOffset[0]; + var rY = randomNeighborOffset[1]; + var rfX = pixel.x+rX; + var rfY = pixel.y+rY; + if(!isEmpty(rfX,rfY,true)) { + if(!pixelMap[rfX]) { + return false; + }; + var rOtherPixel = pixelMap[rfX][rfY]; + var rOtherElement = rOtherPixel.element; + if(whitelist === null || (whitelist !== null && whitelist.includes(rOtherElement))) { + if(typeof(rOtherPixel) === "undefined" || isEmpty(rfX,rfY,true)) { + return false; + }; + if(isNaN(pixel[propertyName])) { //should include undefined + pixel[propertyName] = 0; + }; + var averageValue = (pixel[propertyName] + rOtherPixel[propertyName]) / 2; + pixel[propertyName] = averageValue; + rOtherPixel[propertyName] = averageValue; + }; + }; + return true; + }; + function spreadingPropertyReturn(pixel,propertyName,whitelist=null) { + if(isNaN(pixel[propertyName])) { + pixel[propertyName] = 0; + }; + var recipients = []; //will never be more than one but done with [] for forEach + var randomNeighborOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var rX = randomNeighborOffset[0]; + var rY = randomNeighborOffset[1]; + var rfX = pixel.x+rX; + var rfY = pixel.y+rY; + if(!isEmpty(rfX,rfY,true)) { + if(!pixelMap[rfX]) { + return []; + }; + var rOtherPixel = pixelMap[rfX][rfY]; + var rOtherElement = rOtherPixel.element; + if(whitelist === null || (whitelist !== null && whitelist.includes(rOtherElement))) { + if(typeof(rOtherPixel) === "undefined" || isEmpty(rfX,rfY,true)) { + return []; + }; + if(isNaN(pixel[propertyName])) { //should include undefined + pixel[propertyName] = 0; + }; + var averageValue = (pixel[propertyName] + rOtherPixel[propertyName]) / 2; + pixel[propertyName] = averageValue; + rOtherPixel[propertyName] = averageValue; + recipients.push(rOtherPixel); + }; + }; + return recipients; + }; + function swapNumericPropertyValues(pixel1,pixel2,propertyName,whitelist=null) { + if(!pixel1 || !pixel2) { + return false; + }; + if(isNaN(pixel1[propertyName])) { + pixel1[propertyName] = 0; + }; + if(isNaN(pixel2[propertyName])) { + pixel2[propertyName] = 0; + }; + if(whitelist === null || (whitelist !== null && whitelist.includes(pixel1.element) && whitelist.includes(pixel2.element))) { + var temp1 = pixel1[propertyName]; + pixel1[propertyName] = pixel2[propertyName]; + pixel2[propertyName] = temp1; + }; + return true; + }; + //World + 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 = []; + for(var i = 0; i < coordsToCheck.length; i++) { + var coords = coordsToCheck[i]; + if(outOfBounds(coords.x,coords.y)) { + continue + }; + if(isEmpty(coords.x,coords.y,true)) { + continue + }; + if(!pixelMap[coords.x]?.[coords.y]) { + continue + }; + neighbors.push(pixelMap[coords.x][coords.y]) + }; + return neighbors + }; + function clonePixel(pixel,newX,newY,replaceExistingPixel=false,returnPixel=false) { + if(!pixel) { return false }; + if(outOfBounds(newX,newY)) { return false }; + if(isEmpty(newX,newY)) { + //Do nothing + } else { + if(replaceExistingPixel) { + deletePixel(newX,newY) + } else { + return false + } + }; + var newPixel = structuredClone ? structuredClone(pixel) : JSON.parse(JSON.stringify(pixel)); + newPixel.x = newX; newPixel.y = newY; + pixelMap[newX][newY] = newPixel; + currentPixels.push(newPixel); + return returnPixel ? newPixel : true + }; + clonedPixel = null; + elements.clone_painter_picker = { + color: _cc.w.h, + tool: function(pixel) { + var newPixel = structuredClone ? structuredClone(pixel) : JSON.parse(JSON.stringify(pixel)); + delete newPixel.x; + delete newPixel.y; + clonedPixel = newPixel; + logMessage(`Selected the pixel of ${pixel.element} from (${pixel.x},${pixel.y})`); + selectElement("clone_painter") + }, + tick: function(pixel) { + if(clonedPixel) { + adjacentCoords.forEach(function(offsetPair) { + var finalCoords = [offsetPair[0] + pixel.x,offsetPair[1] + pixel.y]; + clonePixel(clonedPixel,...finalCoords) + }); + } + }, + onSelect: function() { + if(!clonedPixel) { + logMessage("Select the pixel you want to duplicate"); + } + }, + maxSize: 1, + category: "special", + state: "solid", + density: 150000, + desc: "This selects the pixel that the clone_painter element will duplicate." + }; + elements.clone_painter = { + color: "rgb(255,255,0)", + tick: function(pixel) { + var x = pixel.x; //they need to be used after the pixel is removed + var y = pixel.y; + deletePixel(x,y); + if(clonedPixel) { + clonePixel(clonedPixel,x,y) + }; + return + }, + category: "tools", + density: 150000, + onSelect: function() { + if(!clonedPixel) { + logMessage("Please select a pixel to clone using the clone painter picker"); + selectElement("clone_painter_picker") + } + }, + desc: `This places (or due to how elements work, changes itself into) duplicates of a previously selected pixel.

Click here to select the clone painter picker to choose which pixel to clone` + }; + function cloneArea(topLeftX,topLeftY,bottomRightX,bottomRightY,newTopLeftX,newTopLeftY,oldPixelHandling_PreClear1_OnlyReplace2_Ignore3=1,errorOnOutOfBounds=false) { + var results = {"created": 0, "replaced": 0, "deleted": 0, "skipped": 0, "skipped_OOB": 0}; + for(var x = topLeftX; x <= bottomRightX; x++) { + for(var y = topLeftY; y <= bottomRightY; y++) { + var relativeOffsetX = x - topLeftX; + var relativeOffsetY = y - topLeftY; + var newCoords = {"x": newTopLeftX+relativeOffsetX, "y": newTopLeftY+relativeOffsetY}; + var oldCoords = {"x": x, "y": y}; + var oldCoordsOOB = outOfBounds(oldCoords.x,oldCoords.y); + var newCoordsOOB = outOfBounds(newCoords.x,newCoords.y); + if(oldCoordsOOB || newCoordsOOB) { + if(errorOnOutOfBounds) { + var message; + if(oldCoordsOOB && !newCoordsOOB) { + message = "cloneArea: Source is or extends outside of the canvas." + } else if(!oldCoordsOOB && newCoordsOOB) { + message = "cloneArea: Destination is or extends outside of the canvas." + } else if(oldCoordsOOB && newCoordsOOB) { + message = "cloneArea: Source and destination are or extend outside of the canvas." + } else { + message = "cloneArea: ??? (Something has gone wrong with the OOB handling code.)" + }; + throw new Error(message) + } else { + results.skipped_OOB++; + continue + } + }; + if(isEmpty(newCoords.x,newCoords.y)) { + //Empty destination, full source + if(!(isEmpty(oldCoords.x,oldCoords.y))) { + clonePixel(pixelMap[oldCoords.x][oldCoords.y],newCoords.x,newCoords.y); + results.created++; + } + } else { + //Full destination, empty source + if(isEmpty(oldCoords.x,oldCoords.y)) { + //Delete the blocking pixel only if pre-clearing + if(oldPixelHandling_PreClear1_OnlyReplace2_Ignore3 == 1) { + deletePixel(newCoords.x,newCoords.y); + results.deleted++ + } else { + results.skipped++ + } + } + //Full destination, full source + else { + //Delete the blocking pixel if not ignoring (as for the above case, pre-clearing is not ignoring) + if(oldPixelHandling_PreClear1_OnlyReplace2_Ignore3 !== 3) { + deletePixel(newCoords.x,newCoords.y); + //Place the cloned pixel + clonePixel(pixelMap[oldCoords.x][oldCoords.y],newCoords.x,newCoords.y) + results.replaced++; + } else { + results.skipped++ + }; + } + } + } + }; + return results + }; + function getEmptyVonNeumannNeighbors(pixel) { + var neighbors = []; + var x = pixel.x; + var y = pixel.y; + for(var i = 0; i < adjacentCoords.length; i++) { + var finalX = pixel.x + adjacentCoords[i][0]; + var finalY = pixel.y + adjacentCoords[i][1]; + if(isEmpty(finalX,finalY,false)) { + neighbors.push([finalX,finalY]) + }; + }; + return neighbors + }; + function getEmptyMooreNeighbors(pixel) { + var neighbors = []; + var x = pixel.x; + var y = pixel.y; + for(var i = 0; i < mooreDonutCoords.length; i++) { + var finalX = pixel.x + mooreDonutCoords[i][0]; + var finalY = pixel.y + mooreDonutCoords[i][1]; + if(isEmpty(finalX,finalY,false)) { + neighbors.push([finalX,finalY]) + }; + }; + return neighbors + }; + function getVonNeumannNeighbors(pixel) { + var neighbors = []; + var x = pixel.x; + var y = pixel.y; + for(var i = 0; i < adjacentCoords.length; i++) { + var finalX = pixel.x + adjacentCoords[i][0]; + var finalY = pixel.y + adjacentCoords[i][1]; + if(!isEmpty(finalX,finalY,true)) { + neighbors.push(pixelMap[finalX][finalY]) + }; + }; + return neighbors + }; + function getMooreNeighbors(pixel) { + var neighbors = []; + var x = pixel.x; + var y = pixel.y; + for(var i = 0; i < mooreDonutCoords.length; i++) { + var finalX = pixel.x + mooreDonutCoords[i][0]; + var finalY = pixel.y + mooreDonutCoords[i][1]; + if(!isEmpty(finalX,finalY,true)) { + neighbors.push(pixelMap[finalX][finalY]) + }; + }; + return neighbors + }; + function breakCircle(x,y,radius,respectHardness=false,changeTemp=false,defaultBreakIntoDust=false) { + var coords = getCirclePixels(x,y,radius); + coords.forEach(pixel => respectHardness ? tryBreak(pixel,changeTemp,defaultBreakIntoDust) : breakPixel(pixel,changeTemp,defaultBreakIntoDust)) + }; + function fillCircle(element,x,y,radius,overwrite=false) { + var coords = circleCoords(x,y,radius); + var newElement = element; + if(Array.isArray(newElement)) { + newElement = newElement[Math.floor(Math.random() * newElement.length)]; + }; + for(i = 0; i < coords.length; i++) { + coordX = Math.round(coords[i].x); + coordY = Math.round(coords[i].y); + if(overwrite && !isEmpty(coordX,coordY,true)) { + changePixel(pixelMap[coordX][coordY],element); + }; + if(isEmpty(coordX,coordY,false)) { + createPixel(element,coordX,coordY); + }; + }; + }; + function fillCircleReturn(element,x,y,radius,overwrite=false) { + //console.log("fcr"); + var pixels = []; + //console.log("pixels initted"); + var coords = circleCoords(x,y,radius); + //console.log("coords gotten"); + var newElement = element; + //console.log("element processing"); + if(Array.isArray(newElement)) { + newElement = newElement[Math.floor(Math.random() * newElement.length)]; + }; + //console.log("element processed"); + for(i = 0; i < coords.length; i++) { + //console.log("iterator through spots: " + i); + coordX = Math.round(coords[i].x); + coordY = Math.round(coords[i].y); + //console.log(`coord: (${coords[i].x},${coords[i].y})`); + if(overwrite && !isEmpty(coordX,coordY,true)) { + //console.log("replaced pixel"); + pixels.push(changePixelReturn(pixelMap[coordX][coordY],newElement)); + }; + if(isEmpty(coordX,coordY,false)) { + //console.log("created pixel"); + pixels.push(createPixelReturn(newElement,coordX,coordY)); + }; + }; + //console.log("fcr finished"); + //console.log(pixels.map(x => x.element)); + return pixels; + }; + function isOpenAndOnSurface(x,y,includeBottomBound=true) { + if(!isEmpty(x,y,false)) { + return false; + }; + if(y + 1 == height) { + return includeBottomBound; + }; + return !isEmpty(x,y+1,true); + }; + //Freeze pixel + function freezePixel(pixel,changetemp=true) { + var info = elements[pixel.element]; + var result = info.stateLow; + if (!result) { + return false + }; + if(result instanceof Array) { + result = result.filter(elementExists); + if(result.length == 0) { + return false; + }; + } else { + if(!(elementExists(result))) { + return false; + }; + }; + while(result instanceof Array) { + result = randomChoice(result); + }; + changePixel(pixel,result,changetemp); + return true; + }; + //Melt pixel + function meltPixel(pixel,changetemp=true) { + var info = elements[pixel.element]; + var result = info.stateHigh; + if (!result) { + return false + }; + if(result instanceof Array) { + result = result.filter(elementExists); + if(result.length == 0) { + return false; + }; + } else { + if(!(elementExists(result))) { + return false; + }; + }; + while(result instanceof Array) { + result = randomChoice(result); + }; + changePixel(pixel,result,changetemp); + return true; + }; + //Logic + function xor(c1,c2) { + if(!!c1 && !c2) { + return true; + } else if(!c1 && !!c2) { + return true; + } else { + return false; + }; + }; + //currentPixels operations + function findInCurrentPixels(x,y) { + var pixel = currentPixels.filter(function(pixelObject) { + return pixelObject.x == x && pixelObject.y == y; + }); + if(pixel.length <= 0) { + return undefined; + }; + if(pixel.length > 1) { + pixel.length = 1; + }; + pixel = pixel[0]; + return pixel; + }; + function filterCurrentPixels(filterFunction) { + return currentPixels.filter(filterFunction); + }; + //Filter test functions + function _filterTest_xIsTwenty(pixel) { + return pixel.x == 20; + }; + function _filterTest_tempIsOdd(pixel) { + return Math.trunc(pixel.temp) % 2 == 1; + }; + function _filterTest_redRock(pixel) { + var color = rgbStringToHSL(convertColorFormats(pixel.color,"rgb"),"json"); + var isRed = ((color.h % 360) >= 350) || ((color.h % 360) <= 10); + var isVivid = (color.s > 30); + var isBright = (color.l > 20); + return isRed && isVivid && isBright; + }; + //Ghost pixel repair functions + function rebuildCurrentPixels() { + var currPix = []; //rebuild currentPixels from pixelMap to try to fix bug + for(pmi = 0; pmi < pixelMap.length; pmi++) { + var pixelMapPart = pixelMap[pmi]; + for(pmj = 0; pmj < pixelMapPart.length; pmj++) { + var pixelMapUnit = pixelMapPart[pmj]; + if(typeof(pixelMapUnit) === "object") { + if(pixelMapUnit !== null) { + currPix.push(pixelMapUnit); + }; + }; + }; + }; + currentPixels = currPix; + }; //Take each item from pixelMap into currentPixels + afterEveryTick(function() { + if(typeof(rebuildCurrentPixels) !== "undefined") { + rebuildCurrentPixels() + } + }); //nuclear option: rebuild current pixels every tick (this is kind of cursed but desyncs are bad) + function removePixelsFromPixelmapThatAreNotInCurrentpixels() { + pixelMap = pixelMap.map(col => col.map(function(pixel) { + if(pixel == undefined) { + return undefined + } else if(currentPixels.includes(pixel)) { + return pixel + } else { + return undefined + } + })) + }; + function removePixelsFromCurrentpixelsThatAreNotInPixelmap() { + var flatPixelMap = pixelMap.flat(); + currentPixels = currentPixels.filter(pixel => flatPixelMap.includes(pixel)); + flatPixelMap = null + }; + //Sugar functions + function newElement(name="element_name",color="#FF00FF",otherProps={}) { + elements[name] = { + color: color, + }; + for(property in otherProps) { + elements[name][property] = otherProps[property]; + }; + return elements[name]; + }; + //Fixes + //fix -1-caused ghost pixels + function deletePixel(x,y) { + if(isEmpty(x,y,true)) { return false }; + // remove pixelMap[x][y] from currentPixels + var pixelIndex = currentPixels.indexOf(pixelMap[x][y]); + if(pixelIndex !== -1) { + currentPixels.splice(pixelIndex,1) + if (pixelMap[x][y]) { delete pixelMap[x][y] }; + } else { + return false + }; + //if (pixelMap[x][y]) {pixelMap[x][y].del = true} + //if (pixelMap[x][y]) { delete pixelMap[x][y] }; + /*for (var i = 0; i < currentPixels.length; i++) { + if (currentPixels[i].x == x && currentPixels[i].y == y) { + currentPixels.splice(i, 1); + break; + } + }*/ + /*if (id != null) { + for (var i = 0; i < currentPixels.length; i++) { + if (currentPixels[i].id == id) { + currentPixels.splice(i, 1); + return; + } + } + }*/ + }; + //Language + function englishFormatList(thingsArrayIn) { + var thingsArray = thingsArrayIn; + var amount = thingsArray.length; + if(amount == 1) { + return thingsArray[0] + } else if(amount == 2) { + return thingsArray.join(" and ") + } else { + var lastItem = thingsArray[thingsArray.length - 1]; + thingsArray[thingsArray.length - 1] = "and " + lastItem; + return thingsArray.join(", ") + }; + }; + function capitalizeFirstLetter(string,locale=null) { + return string[0][locale ? "toLocaleUpperCase" : "toUpperCase"](locale) + string.slice(1) + }; + + //INTERFACE TO SET OTHER PIXEL PROPERTIES WHEN PLACING SPECIFIC ELEMENTS ## + var css = +`.ps-number { + width: 35px; + line-height: 35px; + height: 35px; + display: table-cell; + text-align: center; + font-size: 80%; + vertical-align: baseline; +} + +.ps-heading { + font-size: 90%; +} + +td.inputCell { + text-align: center; +} +`; + head = document.head || document.getElementsByTagName('head')[0], + style = document.createElement('style'); + + head.appendChild(style); + + style.type = 'text/css'; + if (style.styleSheet){ + // This is required for IE8 and below. + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); + } + + function showPropertySetter() { + var ps = document.getElementById("propertySetter"); + if(ps) { + ps.style.display = "block" + } + }; + + function hidePropertySetter() { + var ps = document.getElementById("propertySetter"); + if(ps) { + ps.style.display = "none" + } + }; + + function hideSetterColumn(type,index) { + //currently the only type is "numeric" + var heading = document.getElementById(`property${type}${index.toString()}headingcell`); + var input = document.getElementById(`property${type}${index.toString()}inputcell`); + heading.style.display = "none"; + input.style.display = "none"; + }; + + function showSetterColumn(type,index) { + var heading = document.getElementById(`property${type}${index.toString()}headingcell`); + var input = document.getElementById(`property${type}${index.toString()}inputcell`); + heading.style.display = "table-cell"; + input.style.display = "table-cell"; + }; + + function hideAllSetterColumnsOfType(type) { + var setter = document.getElementById("setterTable"); + var headingsAndInputs = setter.querySelectorAll(`[id^="property${type}][id$="cell"]`); + headingsAndInputs.forEach(n => n.style.display = "none"); + }; + + function hideAllSetterColumns() { + var setter = document.getElementById("setterTable"); + var headingsAndInputs = setter.querySelectorAll(`[id^="property"][id$="cell"]`); + headingsAndInputs.forEach(n => n.style.display = "none"); + }; + + howManySetters = 5; + var propertySetter = document.createElement("div"); + propertySetter.setAttribute("id","propertySetter"); + propertySetter.style.display = "none"; + var newTable = document.createElement("table"); + newTable.setAttribute("id","setterTable"); + propertySetter.appendChild(newTable); + var labelRow = document.createElement("tr"); + var inputRow = document.createElement("tr"); + newTable.appendChild(labelRow); + newTable.appendChild(inputRow); + //NUMERIC SETTERS + for(var i = 0; i < howManySetters; i++) { + var newHeading = document.createElement("th"); + newHeading.setAttribute("id",`propertynumeric${i.toString()}headingcell`); + var newHeadingText = document.createElement("span"); + newHeadingText.classList.add("ps-heading"); + newHeadingText.setAttribute("id",`propertynumeric${i.toString()}heading`); + newHeadingText.innerText = "None"; + newHeading.appendChild(newHeadingText) + labelRow.appendChild(newHeading); + var newInputCell = document.createElement("td"); + newInputCell.setAttribute("id",`propertynumeric${i.toString()}inputcell`); + newInputCell.classList.add("inputCell"); + var newNumberField = document.createElement("input"); + newNumberField.setAttribute("type","number"); + newNumberField.value = "1"; + newNumberField.setAttribute("set","none"); + newNumberField.addEventListener("change", function() { + var property = this.getAttribute("set"); + var parsedValue = parseFloat(this.value); + if(isNaN(parsedValue)) { + var newValue = (Math.max(parseFloat(this.getAttribute("min")),0)); + this.value = newValue; + parsedValue = newValue + }; + var value = parsedValue; + ambaPlaceProperties[property] = value + }); + newNumberField.setAttribute("id",`propertynumeric${i.toString()}input`); + newNumberField.classList.add("ps-number"); + newInputCell.appendChild(newNumberField) + inputRow.appendChild(newInputCell); + }; + document.getElementById("colorSelector").after(propertySetter); + + ambaPlaceProperties = { + blackHoleRange: 15, + whiteHoleRange: 15, + pusherRange: 10, + pusherStrength: 1, + ePusherLength: 5, + starBombRadius: 50, + ophbRadius: 15, + stripePaintScale: 1, + stripePaintPhase: 0, + stripePaintAngle: 0, + sweepingLaserRotationSpeed: -0.03, + sweepingLaserBeamLength: 10, + sweepingLaserBeamTemperature: 2000, + sweepingLaserBeamBrevity: 5, + noteBlockFrequency: 440, + noteBlockLength: 1, + noteBlockVolume: 1, + noteBlockDelay: 0 + }; + + hidePropertySetter(); + hideAllSetterColumns(); + + //COLOR MANIPULATION TOOLS ## + var colorToolCounter = 0; + saturationAmount = 1; + saturationOp = "add"; + lightnessAmount = 1; + lightnessOp = "add"; + hueAmount = 1; + hueOp = "add"; + colorToolElementFilter = "none"; + var ops = ["add","subtract","multiply","divide","set","min","max","+","-","*","x","×","/","÷","=",">",">=","<","<="]; + function colorToolFilterPrompt() { + var preElement = prompt("Enter the elements you want to limit it to\nSeparate multiple elements with commas\nType \"none\" for no filter"); + if(preElement === null) { + return false; + }; + if(preElement.includes(",")) { + preElement = preElement.split(","); + colorToolElementFilter = preElement; + return colorToolElementFilter; + }; + colorToolElementFilter = preElement; + return colorToolElementFilter; + }; + function saturationPrompt() { + var preSaturation = prompt("Enter the value you want to use"); + var preSatOp = prompt(`Enter the operation ("add", "subtract", "multiply", or "divide", or "set")`); + //value check + if(isNaN(parseFloat(preSaturation))) { + if(preSaturation === "" || preSaturation === null) { + alert("No value was specified! Defaulting to 1"); + preSaturation = 1; + } else { + alert("Invalid value! The value must be a number (defaulting to 1)"); + preSaturation = 1; + }; + }; + preSaturation = parseFloat(preSaturation); + //operation check + if(!ops.includes(preSatOp.toLowerCase())) { + if(preSatOp === "" || preSatOp === null) { + alert(`No operation was specified! Defaulting to "add".`); + preSatOp = "add"; + } else { + alert(`Invalid operation! Only "add", "subract", "multiply", "divide", "set", "min", and "max" are accepted (defaulting to "add").`); + preSatOp = "add"; + }; + }; + saturationAmount = preSaturation; + saturationOp = preSatOp; + return [preSaturation, preSatOp]; + }; + function lightnessPrompt() { + var preLightness = prompt("Enter the value you want to use"); + var preLumOp = prompt(`Enter the operation ("add", "subtract", "multiply", or "divide", or "set")`); + //value check + if(isNaN(parseFloat(preLightness))) { + if(preLightness === "" || preLightness === null) { + alert("No value was specified! Defaulting to 1"); + preLightness = 1; + } else { + alert("Invalid value! The value must be a number (defaulting to 1)"); + preLightness = 1; + }; + }; + //operation check + if(!ops.includes(preLumOp.toLowerCase())) { + if(preLumOp === "" || preLumOp === null) { + alert(`No operation was specified! Defaulting to "add".`); + preLumOp = "add"; + } else { + alert(`Invalid operation! Only "add", "subract", "multiply", "divide", "set", "min", and "max" are accepted (defaulting to "add").`); + preLumOp = "add"; + }; + }; + preLightness = parseFloat(preLightness) + lightnessAmount = preLightness; + lightnessOp = preLumOp; + return [preLightness, preLumOp]; + }; + function huePrompt() { + var preHue = prompt("Enter the value you want to use"); + var preHueOp = prompt(`Enter the operation ("add", "subtract", "multiply", or "divide", or "set")`); + //value check + if(isNaN(parseFloat(preHue))) { + if(preHue === "" || preHue === null) { + alert("No value was specified! Defaulting to 1"); + preHue = 1; + } else { + alert("Invalid value! The value must be a number (defaulting to 1)"); + preHue = 1; + }; + }; + preHue = parseFloat(preHue); + //operation check + if(!ops.includes(preHueOp.toLowerCase())) { + if(preHueOp === "" || preHueOp === null) { + alert(`No operation was specified! Defaulting to "add".`); + preHueOp = "add"; + } else { + alert(`Invalid operation! Only "add", "subract", "multiply", "divide", "set", "min", and "max" are accepted (defaulting to "add").`); + preHueOp = "add"; + }; + }; + hueAmount = preHue; + hueOp = preHueOp; + return [preHue, preHueOp]; + }; + elements.multiply_color = { + color: ["#c27070","#c29c70","#c2c270","#70c270","#70c2c2","#7070c2","#c270c2"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + pixel.color = multiplyColors(pixel.color,currentColor,"rgb"); + }; + }, + customColor: true, + cooldown: 3, + category: "color tools", //the toolbar is getting cluttered + excludeRandom: true, //the toolbar is getting cluttered + desc: "Click here to configure the element filter (applies to all color tools).", + } + elements.divide_color = { + color: ["#c27070","#c29c70","#c2c270","#70c270","#70c2c2","#7070c2","#c270c2"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + pixel.color = divideColors(pixel.color,currentColor,"rgb"); + }; + }, + customColor: true, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the element filter (applies to all color tools).", + } + elements.add_color = { + color: ["#c27070","#c29c70","#c2c270","#70c270","#70c2c2","#7070c2","#c270c2"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + pixel.color = addColors(pixel.color,currentColor,"rgb"); + }; + }, + customColor: true, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the element filter (applies to all color tools).", + } + elements.subtract_color = { + color: ["#c27070","#c29c70","#c2c270","#70c270","#70c2c2","#7070c2","#c270c2"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + pixel.color = subtractColors(pixel.color,currentColor,"rgb"); + }; + }, + customColor: true, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the element filter (applies to all color tools).", + } + elements.hue = { + color: ["#ff0000","#ccff00","#00ff66","#0066ff","#cc00ff","#ff0000"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + pixel.color = changeHue(pixel.color,hueAmount,hueOp,"rgb"); + }; + }, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the tool.
Click here to configure the element filter (applies to all color tools).", + }; + elements.saturation = { + color: ["#808080","#996666","#b34d4d","#cc3333","#e61919","#ff0000"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + pixel.color = changeSaturation(pixel.color,saturationAmount,saturationOp,"rgb"); + }; + }, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the tool.
Click here to configure the element filter (applies to all color tools)." + } + elements.lightness = { + color: [_cc.b.h,"#333333","#666666","#999999","#cccccc",_cc.w.h], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + pixel.color = changeLightness(pixel.color,lightnessAmount,lightnessOp,"rgb"); + }; + }, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the tool.
Click here to configure the element filter (applies to all color tools)." + } + elements.grayscale = { + color: ["#7f7f7f"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + var oldColor = convertColorFormats(pixel.color,"json"); + var lightness = Math.round((oldColor.r * 0.299) + (oldColor.g * 0.587) + (oldColor.b * 0.114)) + var finalColor = [lightness, lightness, lightness] + pixel.color = "rgb(" + finalColor.join(",") + ")" + }; + }, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the element filter (applies to all color tools).", + } + elements.invert = { + color: ["#ff0000", "#00ffff"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + var oldColor = convertColorFormats(pixel.color,"json"); + var finalColor = [(255 - oldColor.r), (255 - oldColor.g), (255 - oldColor.b)] + pixel.color = "rgb(" + finalColor.join(",") + ")" + }; + }, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the element filter (applies to all color tools).", + } + elements.reverse_R_G_B = { + color: ["#7f7f7f"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + var oldColor = convertColorFormats(pixel.color,"json"); + var finalColor = [oldColor.b, oldColor.g, oldColor.r] + pixel.color = "rgb(" + finalColor.join(",") + ")" + }; + }, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the element filter (applies to all color tools).", + } + elements.shift_R_G_B = { + color: ["#7f7f7f"], + tool: function(pixel) { + var element = pixel.element; + if( colorToolElementFilter === "none" || ( (typeof(colorToolElementFilter) === "string" && element === colorToolElementFilter) || (Array.isArray(colorToolElementFilter) && colorToolElementFilter.includes(element)) ) ) { + var oldColor = convertColorFormats(pixel.color,"json"); + var finalColor = [oldColor.g, oldColor.b, oldColor.r] + pixel.color = "rgb(" + finalColor.join(",") + ")" + }; + }, + cooldown: 3, + category: "color tools", + excludeRandom: true, + desc: "Click here to configure the element filter (applies to all color tools).", + } + //STRIPED PAINT ## + stripeFixedDefaultProperties = { + color2: _cc.b.r, + phase: 0, + scale: 1, + angle: 0 + }; + stripeSpreadingProperties = { + color1: "It doesn't matter what I put here; I'm just sick of writing for loops.", + color2: "stan loona", + }; + /*stripeSpreadingProperties2 = { + phase: 0, + scale: 1, :eggTF: + angle: 0 + };*/ + function stripeFunction(pixel) { + if(pixel.color1 == undefined || pixel.color1 == null) { + pixel.color1 = pixel.color; + }; + for(prop in stripeFixedDefaultProperties) { + if(pixel[prop] == undefined || pixel[prop] == null) { + pixel[prop] = stripeFixedDefaultProperties[prop]; + }; + }; + //color1 and color2 self staining + for (var i = 0; i < adjacentCoords.length; i++) { + var x = pixel.x+adjacentCoords[i][0]; + var y = pixel.y+adjacentCoords[i][1]; + if(isEmpty(x,y,true)) { + continue; + }; + var otherPixel = pixelMap[x][y]; + for(prop in stripeSpreadingProperties) { + if(otherPixel.element == pixel.element && pixel[prop] && otherPixel[prop]) { + if((otherPixel.color1 == pixel.color1) && (otherPixel.color2 == pixel.color2)) { + } else { + otherPixel[prop] = lerpColors(pixel[prop],otherPixel[prop]); + } + } + } + /*for(prop in stripeSpreadingProperties2) { + if(otherPixel.element == pixel.element && pixel[prop] !== undefined && otherPixel[prop] !== undefined) { + otherPixel[prop] = pixel[prop]/2 + otherPixel[prop]/2; + }; :eggTF: + };*/ + }; + var radians = pixel.angle * (Math.PI / 180); + var colorNumber = (pixel.x*Math.cos(radians))+(pixel.y*Math.sin(radians)); + var sineWeight = (1+Math.sin(pixel.phase + colorNumber / pixel.scale))/2; + var preColor = lerpColors(pixel.color1,pixel.color2,"json",sineWeight); + for(colorlet in preColor) { + preColor[colorlet] = Math.round(preColor[colorlet]); + }; + pixel.color = convertColorFormats(preColor,"rgb"); + }; + stripePaintDesc = `Exactly what it says on the button. +
+Properties:
    +
  1. color1: The first color of the stripe
  2. +
  3. color2: The second color of the stripe (defaults to black)
  4. +
  5. scale: Relative width of the stripes, compared to the default
  6. +
  7. phase: Offset in the position of the stripes (π/2 = 1 stripe width)
  8. +
  9. angle: Angle in degrees
+color1 and color2 spread through striped paint like dye does with itself. color1 can be set on initial placement through the color picker, but otherwise, properties must be changed through the console or with prop.js's tools. + This does not work with HSL color and the game will black screen if one is somehow used. The color conversion functions are a clusterf***.` + elements.stripe_paint = { + behavior: behaviors.LIQUID, + state: "liquid", + density: 998, + tempHigh: 100, + stateHigh: "smoke", + color: elements.paint.color, + customColor: true, + category: "special", + properties: { + color1: null, + color2: null + }, + stain: elements.dye.stain, + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","stripePaintScale"); + p0.setAttribute("min","0.00000001"); + p0.value = ambaPlaceProperties.stripePaintScale; + }; + if(p0h) { + p0h.innerText = "Scale"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","stripePaintPhase"); + p1.setAttribute("min","-99999999"); + p1.value = ambaPlaceProperties.stripePaintPhase; + }; + if(p1h) { + p1h.innerText = "Phase"; + }; + + showSetterColumn("numeric",2); + var p2 = document.getElementById("propertynumeric2input"); + var p2h = document.getElementById("propertynumeric2heading"); + if(p2) { + p2.setAttribute("set","stripePaintAngle"); + p2.setAttribute("min","-99999999"); + p2.value = ambaPlaceProperties.stripePaintAngle; + }; + if(p2h) { + p2h.innerText = "Angle"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + tick: function(pixel) { + pixel.scale ??= (ambaPlaceProperties?.stripePaintScale ?? 1); + pixel.phase ??= (ambaPlaceProperties?.stripePaintPhase ?? 0); + pixel.angle ??= (ambaPlaceProperties?.stripePaintAngle ?? 0); + stripeFunction(pixel) + }, + desc: stripePaintDesc + }; + //CHANGEPIXEL LOG UNDEFINED-ELEMENT FAILURES ## + doLog = true; + oldChangePixel = changePixel; + changePixel = function(pixel,element,changetemp=true) { + if(typeof(elements[element]) == "undefined") { + if(typeof(element) == "undefined" || element == "undefined") { return false }; + if(doLog) { console.error(`Something tried to change a pixel of ${pixel.element} at (${pixel.x},${pixel.y}) to nonexistent element "${element}"`) }; + return false + }; + oldChangePixel(pixel,element,changetemp); + }; + //VARIABLES FOR IN-GAME CONSOLE ## + colorInvalidError = "Color must be in the form \"rgb(red,green,blue)\" or \"hsl(hue,saturation%,lightness%)\" (without quotes)!"; + stringSynonyms = [ "string", "str", "st", "s" ]; + numberSynonyms = [ "number", "num", "nm", "nu", "nb", "integer", "int", "i", "it", "float", + "flt", "ft", "fl", "f", "wholenumber", "decimalnumber", "wn", "dn", "w", + "d", "deeznuts" ]; /*The purpose of these blatant lies is, through a + reference to the Alice series of software, have an excuse to include deez + nuts. (p.s. this was before i picked that name)*/ + objectSynonyms = [ "object", "oj", "obj", "ob", "o", "json" ]; + booleanSynonyms = [ "boolean", "bool", "boole", "boo", "bo", "bl", "b" ]; + arraySynonyms = [ "arr", "a", "ar", "list" ]; + defaultStringTypeValues = ["element","color","clone","changeTo","void","type","spawn"]; + defaultNumberTypeValues = ["x","y","charge","temp","start","vx","vy","chargeCD","start","burnStart","dir","panic","r","frequency","length","delay","volume","debounce","debounceLength","speed","fall","penetrateCounter","chargeCounter","spawnCounter","spawnTime","squadiusX","squadiusY","spawnTries","counter","attachDirection","value","range","xSpacing","ySpacing","maxPixels","explosionRadius","circleRadius","breakAroundRadius"]; + defaultBooleanTypeValues = ["burning","dead","hissing","following","dirLocked","del","didChargeBlueTinted","shooting","del","spawnAtPixelTemp","overwrite"]; + defaultArrayTypeValues = ["attachOffsets"]; + synonymsOfTrue = ["true", "t", "1", "yes"]; + synonymsOfFalse = ["false", "f", "0", "no"]; + //ENABLE RUNNING CODE AFTER STATE ELEMENT AUTOGENERATION (runAfterAutogen) ## + resizeCanvas = function(newHeight,newWidth,newPixelSize,clear) { + var gameCanvas = document.getElementById("game"); + var ctx = gameCanvas.getContext("2d"); + ctx.canvas.width = newWidth; + ctx.canvas.height = newHeight; + document.getElementById("gameDiv").style.width = newWidth + "px"; + ctx.webkitImageSmoothingEnabled = false; + ctx.mozImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + pixelSize = newPixelSize; + pixelSizeHalf = newPixelSize/2; + pixelSize3 = newPixelSize*3; + height = Math.round(newHeight/newPixelSize)-1; + width = Math.round(newWidth/newPixelSize)-1; + mousePos = {x:Math.round(width/2),y:Math.round(height/2)}; + if (clear!==false) { clearAll(); } + var canvasLayerKeys = Object.keys(canvasLayers); + for (let i = 0; i < canvasLayerKeys.length; i++) { + const layerCanvas = canvasLayers[canvasLayerKeys[i]]; + var layerCtx = layerCanvas.getContext("2d"); + layerCtx.canvas.width = ctx.canvas.width; + layerCtx.canvas.height = ctx.canvas.height; + layerCtx.webkitImageSmoothingEnabled = false; + layerCtx.mozImageSmoothingEnabled = false; + layerCtx.imageSmoothingEnabled = false; + } + } + + autoResizeCanvas = function(clear) { + pixelSize = parseInt(settings.pixelsize) || 6; + if (window.innerWidth < 700) { + pixelSize--; + } + if (window.innerWidth < 700) { + var newWidth = Math.ceil(window.innerWidth / pixelSize) * pixelSize; + var newHeight = Math.ceil(window.innerHeight*0.6 / pixelSize) * pixelSize; + } + else { + var newWidth = Math.ceil(window.innerWidth*0.9 / pixelSize) * pixelSize; + var newHeight = Math.ceil(window.innerHeight*0.675 / pixelSize) * pixelSize; + } + // If the new width is greater than 1000, set it to 1000 + if (newWidth > 1000) { newWidth = 1000; } + // If we are on a desktop and the new height is greater than 500, set it to 500 + if (window.innerWidth > 1000 && newHeight > 500) { newHeight = 500; } + if (settings.pixelsize && settings.pixelsize.toString().slice(-1) === "w") { + // resizeCanvas(window.innerHeight/1.5,window.innerWidth-10,pixelSize); + newHeight = window.innerHeight/1.5; + newWidth = window.innerWidth-10; + } + resizeCanvas(newHeight,newWidth,pixelSize,clear); + } + + function runAfterAutogen(func) { + runAfterAutogenList.push(func); + }; + function runAfterButtons(func) { + runAfterButtonsList.push(func); + }; + runAfterAutogenList = []; + runAfterButtonsList = []; + + function behaviorStringsToArrays() { + for (var behavior in behaviors) { + if (typeof behaviors[behavior][0] === "string") { + var newbehavior = []; + for (var i = 0; i < behaviors[behavior].length; i++) { + newbehavior.push(behaviors[behavior][i].split("|")); + } + behaviors[behavior] = newbehavior; + } + } + } + + function tripletsToRgbAndGenerateColorObjects() { + for (var key in elements) { + if (elements.hasOwnProperty(key)) { + // if the element has no color, skip it + if (elements[key].color === undefined) { + continue; + } + // if the color is an array, loop over each one + if (elements[key].color instanceof Array) { + var rgbs = []; + var rgbos = []; + for (var i = 0; i < elements[key].color.length; i++) { + var c = elements[key].color[i]; + if(!c.startsWith) { + console.error(`element ${key} array color ${i} (${c}) isn't a string`); + rgbs.push(_cc.w.r) + rgbos.push(_cc.w.j) + } else if (c.startsWith("#") || c.length <= 8 ) { + var rgb = null; + var rgbo = null; + try { rgb = convertColorFormats(c,"rgb") } catch(error) { console.error(key,error) }; + try { rgbo = convertColorFormats(c,"json") } catch(error) { console.error(key,error) }; + if(rgb == null) { console.log(key,c); rgb = _cc.w.r }; + if(rgbo == null) { console.log(key,c); rgbo = _cc.w.j }; + rgbs.push(rgb); + rgbos.push(rgbo); + } else { + rgbs.push(c); + rgbos.push(convertColorFormats(c,"json")); + console.log(key,rgbs,rgbos) + } + } + elements[key].color = rgbs; + elements[key].colorObject = rgbos; + } else { + var c = elements[key].color; + if(!c.startsWith) { + console.error(`element ${key} array color ${i} (${c}) isn't a string`); + rgbs.push(_cc.w.r) + rgbos.push(_cc.w.j) + } else if (c.startsWith("#") || c.length <= 8 ) { + var rgb = null; + var rgbo = null; + try { rgb = convertColorFormats(c,"rgb") } catch(error) { console.error(key,error) }; + try { rgbo = convertColorFormats(c,"json") } catch(error) { console.error(key,error) }; + if(rgb == null) { console.log(key,c); rgb = _cc.w.r }; + if(rgbo == null) { console.log(key,c); rgbo = _cc.w.j }; + elements[key].color = rgb; + elements[key].colorObject = rgbo; + } else { + elements[key].color = convertColorFormats(c,"rgb"); + elements[key].colorObject = convertColorFormats(c,"json"); + } + } + } + } + } + + // Automatic molten element generation + // Moved above the exposed autoGenAllElements for ReferenceError purposes + function autoGen(newname,element,autoType) { + var autoInfo = autoElements[autoType]; + var newcolor = elements[element].colorObject; + if (!newcolor) { newcolor = {r:255,g:255,b:255} }; + var colorList = []; + var colorObjectList = []; + // if newcolor is not an array, put it in an array + if (!(newcolor instanceof Array)) { newcolor = [newcolor]; } + // for every color in the newcolor array, add a new color with the same value, but with the r and g values increased + for (var i = 0; i < newcolor.length; i++) { + var c = newcolor[i] ?? "#ff00ff"; + for (var j = 0; j < autoInfo.rgb.length; j++) { + var newc = autoInfo.rgb[j]; + r = Math.floor(c.r * newc[0]); + g = Math.floor(c.g * newc[1]); + b = Math.floor(c.b * newc[2]); + if (r > 255) {r = 255;} if (g > 255) {g = 255;} + colorList.push("rgb("+r+","+g+","+b+")"); + colorObjectList.push({r:r,g:g,b:b}); + } + } + var newelem = { + //"name": newname.replaceAll("_"," "), + behavior: autoInfo.behavior, + hidden: autoInfo.hidden || false, + state: autoInfo.state || "solid", + category: autoInfo.category || "states" + } + if (colorList.length <= 1) { colorList = colorList[0]; } + if (colorObjectList.length <= 1) { colorObjectList = colorObjectList[0]; } + newelem.color = colorList; + newelem.colorObject = colorObjectList; + var multiplier = 1.1; + if (autoInfo.type === "high") { + if (!elements[element].stateHigh) {elements[element].stateHigh = newname;} + newelem.temp = elements[element].tempHigh; + newelem.tempLow = elements[element].tempHigh+(autoInfo.tempDiff || 0); + newelem.stateLow = element; + // Change density by *0.9 + if (elements[element].density) { newelem.density = Math.round(elements[element].density * 0.9 * 10) / 10; } + } + else if (autoInfo.type === "low") { + if (!elements[element].stateLow) {elements[element].stateLow = newname;} + newelem.temp = elements[element].tempLow; + newelem.tempHigh = elements[element].tempLow+(autoInfo.tempDiff || 0); + newelem.stateHigh = element; + multiplier = 0.5; + // Change density by *1.1 + if (elements[element].density) { newelem.density = Math.round(elements[element].density * 1.1 * 10) / 10; } + } + if (!elements[element].ignore) { elements[element].ignore = [] } + elements[element].ignore.push(newname); + if (elements[element].viscosity || autoInfo.viscosity) { + newelem.viscosity = elements[element].viscosity || autoInfo.viscosity; + } + // Change by *multiplier + if (elements[element].conduct) { newelem.conduct = Math.round(elements[element].conduct * multiplier * 10) / 10; } + if (elements[element].burn) { newelem.burn = Math.min(100,Math.round(elements[element].burn * multiplier * 10) / 10); } + if (elements[element].burnTime) { newelem.burnTime = Math.round(elements[element].burnTime * multiplier * 10) / 10; } + if (elements[element].burnInto) { newelem.burnInto = elements[element].burnInto; } + if (elements[element].fireColor) { newelem.fireColor = elements[element].fireColor; } + // If the new element doesn't exist, add it + if (!elements[newname]) { elements[newname] = newelem; } + else { + // Loop through newelem's keys and values, copy them to the new element if they are not already defined + for (var key in newelem) { + if (elements[newname][key] == undefined) { elements[newname][key] = newelem[key]; } + } + } + if (autoType === "molten" && (elements.molten_slag && elements.molten_slag.ignore && elements.molten_slag.ignore.indexOf(element) === -1)) { // Slag reactions + if (newname !== "molten_slag") { + if (!elements[newname].reactions) { elements[newname].reactions = {}; } + elements[newname].reactions.ash ??= { elem1:null, elem2:"molten_slag" }; + elements[newname].reactions.dust ??= { elem1:null, elem2:"molten_slag" }; + elements[newname].reactions.magma ??= { elem1:null, elem2:"molten_slag" } + elements[newname].reactions.smog ??= { elem1:null, elem2:"molten_slag" } + elements[newname].reactions.pyrocumulus ??= { elem1:null, elem2:"molten_slag" } + elements[newname].reactions.dioxin ??= { elem1:null, elem2:"molten_slag" } + elements[newname].reactions.poison_gas ??= { elem1:null, elem2:"molten_slag" } + elements[newname].reactions.dirt ??= { elem1:null, elem2:"molten_slag" } + elements[newname].reactions.molten_dirt ??= { elem1:null, elem2:"molten_slag" } + }; + } + } + + // Loop through each element. If it has a tempHigh, but not a stateHigh, create a new molten element + function autoGenAllElements() { + for (element in elements) { + if (elements[element].tempHigh!==undefined && (elements[element].stateHigh===undefined||elements[element].forceAutoGen)) { + var newname = elements[element].stateHighName; + if ((elements[element].state==="solid" || !elements[element].state)) { // Melting + if (!newname) { newname = "molten_"+element } + autoGen(newname,element,"molten"); + } + else if (elements[element].state==="liquid") { // Evaporating + if (!newname) { + newname = element; + if (newname.startsWith("liquid_")) { newname = newname.substring(7); } + if (newname.startsWith("molten_")) { newname = newname.substring(7); } + newname += "_gas"; + } + autoGen(newname,element,"vaporize"); + } + } + if (elements[element].tempLow!==undefined && (elements[element].stateLow===undefined||elements[element].forceAutoGen)) { + var newname = elements[element].stateLowName; + if (elements[element].state==="liquid") { // Freezing + if (!newname) { + newname = element; + if (newname.startsWith("liquid_")) { newname = newname.substring(7); } + if (newname.endsWith("_water")) { newname = newname.substring(0,newname.length-6); } + newname += "_ice"; + } + autoGen(newname,element,"frozen"); + } + else if (elements[element].state==="gas") { // Condensing + if (!newname) { + newname = element; + if (newname.endsWith("_gas")) { newname = newname.substring(0,newname.length-4); } + newname = "liquid_"+newname; + } + autoGen(newname,element,"condense"); + } + } + if (elements[element].behavior && typeof elements[element].behavior[0] === "string") { + var newbehavior = []; + for (var i = 0; i < elements[element].behavior.length; i++) { + newbehavior.push(elements[element].behavior[i].split("|")); + } + elements[element].behavior = newbehavior; + } + if (elements[element].behaviorOn && typeof elements[element].behaviorOn[0] === "string") { + var newbehavior = []; + for (var i = 0; i < elements[element].behaviorOn.length; i++) { + newbehavior.push(elements[element].behaviorOn[i].split("|")); + } + elements[element].behaviorOn = newbehavior; + } + } + } + + function doFinalChecks() { + nextid = 1; + for (key in elements) { + if(!elements[key]) { + console.error(`Nonexistent element ${key}`); + continue + }; + elements[key].id = nextid; + nextid++; + + // Language Loader Part 2 + if (lang[key] !== undefined) { + elements[key].name = lang[key] + } + + // If the element has no behavior, set it to behaviors.WALL + if (!elements[key].behavior && !elements[key].tick) { + elements[key].tick = function(pixel) {}; + } + // If the behavior is a function, delete it and set tick to it instead + if (typeof elements[key].behavior === "function") { + if (elements[key].tick) { + elements[key].tick1 = elements[key].tick; + elements[key].tick2 = elements[key].behavior; + elements[key].tick = function(pixel) { + if (pixel.start === pixelTicks) {return} + var id = elements[pixel.element].id; + elements[pixel.element].tick1(pixel); + if (!pixel.del && id === elements[pixel.element].id) { + elements[pixel.element].tick2(pixel); + } + } + } + else { + elements[key].tick = elements[key].behavior; + } + delete elements[key].behavior; + } + // If the element has no color, set it to white + if (elements[key].color === undefined) { + elements[key].color = _cc.w.r; + elements[key].colorObject = _cc.w.j; + } + if (elements[key].movable === false) { delete elements[key].movable } + else if (elements[key].movable === undefined) { + // If the element's behavior is an array and contains M1 or M2, set its movable to true + if (elements[key].behavior && typeof elements[key].behavior[0] === "object") { + var bstring = JSON.stringify(elements[key].behavior); + if (bstring.indexOf("M1")!==-1 || bstring.indexOf("M2")!==-1) { elements[key].movable = true; } + } + if (elements[key].tick) { elements[key].movable = true; } + } + if (elements[key].behavior) { + var behavior = elements[key].behavior; + var behaviorCenterY = behavior[Math.floor(behavior.length/2)] + var behaviorCenter = behaviorCenterY[Math.floor(behaviorCenterY.length/2)] + // If the element's behavior[1][1] includes "FX", set it's flippableX to true + if (behaviorCenter.indexOf("FX") !== -1) { + elements[key].flippableX = true; + } + // If the element's behavior[1][1] includes "FY", set it's flippableY to true + if (behaviorCenter.indexOf("FY") !== -1) { + elements[key].flippableY = true; + } + // If the element's behavior[1][1] includes "RT", set it's rotatable to "true" + if (behaviorCenter.indexOf("RT") !== -1) { + elements[key].rotatable = true; + } + // If the element's behavior stringified includes "BO", loop through the behavior + if (elements[key].behavior.toString().indexOf("BO") !== -1 && !elements[key].rotatable) { + for (var i = 0; i < elements[key].behavior.length; i++) { + // Loop through each array in the behavior + for (var j = 0; j < elements[key].behavior[i].length; j++) { + // If the behavior includes "BO", set the behaviorOn to the behavior + if (elements[key].behavior[i][j].indexOf("BO") !== -1) { + if ((i==0 && j==0) || (i==0 && j==2) || (i==2 && j==0) && (i==2 && j==2)) { + elements[key].flippableX = true; + elements[key].flippableY = true; + } + else if (i==0 || i==2) { + elements[key].flippableY = true; + } + else if (j==0 || j==2) { + elements[key].flippableX = true; + } + } + } + } + } + } + // If the element's state is "gas", isGas = true + if (elements[key].state === "gas") { + elements[key].isGas = true; + } + // Else if the state is not "solid" or "liquid", delete it + else if (elements[key].state !== "liquid") { + if (elements[key].state !== "solid" && elements[key].state !== undefined) { + delete elements[key].state; + } + // add heat glow to solid if applicable + else if (elements[key].tempHigh > 400 && elements[key].renderer === undefined && elements[elements[key].stateHigh] && elements[elements[key].stateHigh].state === "liquid") { + elements[key].renderer = renderPresets.HEATGLOW; + } + else if (elements[key].state !== "solid" && elements[key].state !== "liquid") { + delete elements[key].state; + } + } + + // If the element has reactions, loop through each one (it is an object), if the value for elem1 or elem2 is not an element and is not null, remove that key + if (elements[key].reactions) { + for (var reaction in elements[key].reactions) { + if(!(elements[key].reactions[reaction])) { + console.error(`Nonexistent reaction ${reaction} in element ${key}`); + continue + } + // If elem1 exists + if (elements[key].reactions[reaction].elem1) { + // If elem1 is an array, loop through each element, else check once. Don't delete if it === null + if (Array.isArray(elements[key].reactions[reaction].elem1)) { + for (var i = 0; i < elements[key].reactions[reaction].elem1.length; i++) { + if (elements[key].reactions[reaction].elem1[i] && !elements[elements[key].reactions[reaction].elem1[i]]) { + elements[key].reactions[reaction].elem1.splice(i,1); + } + } + } + else if (elements[key].reactions[reaction].elem1 && !elements[elements[key].reactions[reaction].elem1]) { + delete elements[key].reactions[reaction].elem1; + } + } + // If elem2 exists + if (elements[key].reactions[reaction].elem2) { + // If elem2 is an array, loop through each element, else check once. Don't delete if it === null + if (Array.isArray(elements[key].reactions[reaction].elem2)) { + for (var i = 0; i < elements[key].reactions[reaction].elem2.length; i++) { + if (elements[key].reactions[reaction].elem2[i] && !elements[elements[key].reactions[reaction].elem2[i]]) { + elements[key].reactions[reaction].elem2.splice(i,1); + } + } + } + else if (elements[key].reactions[reaction].elem2 && !elements[elements[key].reactions[reaction].elem2]) { + delete elements[key].reactions[reaction].elem2; + } + } + } + } + // If the element's stateHigh or stateLow is not an element, remove it and tempHigh/Low + if (elements[key].stateHigh) { + // If it's an array, do it for each item, otherwise, just do it once + if (Array.isArray(elements[key].stateHigh)) { + for (var i = 0; i < elements[key].stateHigh.length; i++) { + if (!elements[elements[key].stateHigh[i]] && elements[key].stateHigh[i] !== null) { + elements[key].stateHigh.splice(i,1); + } + } + if (elements[key].stateHigh.length == 0) { + delete elements[key].stateHigh; + delete elements[key].tempHigh; + } + } + else { + if (!elements[elements[key].stateHigh] && elements[key].stateHigh !== null) { + delete elements[key].stateHigh; + delete elements[key].tempHigh; + } + } + } + if (elements[key].stateLow) { + if (Array.isArray(elements[key].stateLow)) { + for (var i = 0; i < elements[key].stateLow.length; i++) { + if (!elements[elements[key].stateLow[i]] && elements[key].stateLow[i] !== null) { + elements[key].stateLow.splice(i,1); + } + } + if (elements[key].stateLow.length == 0) { + delete elements[key].stateLow; + delete elements[key].tempLow; + } + } + else { + if (!elements[elements[key].stateLow] && elements[key].stateLow !== null) { + delete elements[key].stateLow; + delete elements[key].tempLow; + } + } + } + // same for burnInto + if (elements[key].burnInto) { + if (Array.isArray(elements[key].burnInto)) { + for (var i = 0; i < elements[key].burnInto.length; i++) { + if (!elements[elements[key].burnInto[i]]) { + delete elements[key].burnInto[i]; + } + } + if (elements[key].burnInto.length == 0) { + delete elements[key].burnInto; + } + } + else { + if (!elements[elements[key].burnInto]) { + delete elements[key].burnInto; + } + } + } + // same for breakInto + if (elements[key].breakInto) { + if (Array.isArray(elements[key].breakInto)) { + for (var i = 0; i < elements[key].breakInto.length; i++) { + if (elements[key].breakInto[i]!==null && !elements[elements[key].breakInto[i]]) { delete elements[key].breakInto[i]; } + } + if (elements[key].breakInto.length == 0) { delete elements[key].breakInto; } + } + else { + if (elements[key].breakInto[i]!==null && !elements[elements[key].breakInto]) { delete elements[key].breakInto; } + } + } + if (elements[key].colorPattern) { + if (!elements[key].colorKey) { + delete elements[key].colorPattern; + } + else { + var newPattern = []; + for (var i = 0; i < elements[key].colorPattern.length; i++) { + newPattern.push([]); + var line = elements[key].colorPattern[i]; + // loop through each character in the line + for (var j = 0; j < line.length; j++) { + var char = line[j]; + var colorValue = elements[key].colorKey[char]; + if (colorValue) { + if (colorValue.startsWith("#") || colorValue.length == 6) { + elements[key].colorKey[char] = convertColorFormats(elements[key].colorKey[char],"rgb"); + } + newPattern[i].push(elements[key].colorKey[char]); + } + else { + newPattern[i].push(_cc.w.r); + } + } + } + elements[key].colorPattern = newPattern; + delete elements[key].colorKey; + } + } + } + }; + + function createWorldGenOptions() { + for (var key in worldgentypes) { + document.getElementById("worldgenselect").innerHTML += ""; + } + }; + + function validateWorldGenSelection() { + if (settings["worldgen"] && !worldgentypes[settings["worldgen"]]) { + settings["worldgen"] = "off"; + } + }; + + function validateRandomEventChoices() { + for (var key in randomEventChoices) { + for (var i = 0; i < randomEventChoices[key].length; i++) { + if (!elements[randomEventChoices[key][i]]) { + randomEventChoices[key].splice(i,1); + } + } + } + }; + + function setEqualReactions(fromElement,toElement) { + if (elements[fromElement] && elements[toElement]) { + if (elements[fromElement].reactions) { + elements[toElement].reactions = elements[fromElement].reactions; + return true; + }; + }; + return false; + }; + + function loadSettingsIntoHTML() { + loadSettings(); + var settingSpans = document.getElementsByClassName("setting-span"); + for (var i = 0; i < settingSpans.length; i++) { + var setting = settingSpans[i].getAttribute("setting"); + if (setting in settings) { + var settingValue = settings[setting]; + var toggleButtons = settingSpans[i].getElementsByClassName("toggleInput"); + if (toggleButtons.length > 0) { + if (settingValue == 1) { + toggleButtons[0].setAttribute("state","1"); + // toggleButtons[0].value = "ON"; + } + else { + toggleButtons[0].setAttribute("state","0"); + // toggleButtons[0].value = "OFF"; + } + } + else { + var settingElements = settingSpans[i].getElementsByTagName("select") + if (settingElements.length === 0) { + settingElements = settingSpans[i].getElementsByTagName("input"); + } + if (settingElements.length > 0) { + settingElements[0].value = settingValue; + } + } + } + } + }; + + gameCanvas = document.getElementById("game"); + canvas = document.getElementById("game"); + ctx = gameCanvas.getContext("2d"); + + function setCanvasWidthAndHeight(ctx) { + if (window.innerWidth < 700) { + var newWidth = Math.ceil(window.innerWidth / pixelSize) * pixelSize; + var newHeight = Math.ceil(window.innerHeight*0.6 / pixelSize) * pixelSize; + } + else { + var newWidth = Math.ceil(window.innerWidth*0.9 / pixelSize) * pixelSize; + var newHeight = Math.ceil(window.innerHeight*0.675 / pixelSize) * pixelSize; + } + // If the new width is greater than 1000, set it to 1000 + if (newWidth > 1000) { newWidth = 1000; } + // If we are on a desktop and the new height is greater than 500, set it to 500 + if (window.innerWidth > 1000 && newHeight > 500) { newHeight = 500; } + canvas.style.backgroundColor = settings.bg||"#000000"; + + // ctx.canvas.width = newWidth; + // ctx.canvas.height = newHeight; + // document.getElementById("gameDiv").style.width = newWidth + "px"; + // document.getElementById("loadingP").style.display = "none"; + // document.getElementById("canvasDiv").style.display = "block"; + // width = Math.round(newWidth/pixelSize)-1; + // height = Math.round(newHeight/pixelSize)-1; + }; + + function definePixelMap() { + if (settings["worldgen"]) { + clearAll(); + } + else { + // Object with width arrays of pixels starting at 0 + pixelMap = []; + for (var i = 0; i < width; i++) { + pixelMap[i] = []; + } + } + }; + + function setRandomChoices() { + randomChoices = Object.keys(elements).filter(function(e) { + return elements[e].excludeRandom != true && elements[e].category != "tools" && !elements[e].tool; + }); + }; + + function addCanvasAndWindowListeners(gameCanvas) { + gameCanvas.addEventListener("mousedown", mouseClick); + gameCanvas.addEventListener("mousedown", function(e){ + if (elements[currentElement] && elements[currentElement].onMouseDown) { + elements[currentElement].onMouseDown(e); + } + }); + gameCanvas.addEventListener("touchstart", function(e) { + if (e.touches) e = e.touches[0]; + mousePos = getMousePos(canvas, e); + return false; + }); + gameCanvas.addEventListener("touchstart", mouseClick, { passive: false }); + window.addEventListener("mouseup", mouseUp); + window.addEventListener("touchend", mouseUp, { passive: false }); + window.addEventListener("mousemove", mouseMove); + gameCanvas.addEventListener("touchmove", mouseMove, { passive: false }); + gameCanvas.addEventListener("wheel", wheelHandle); + gameCanvas.addEventListener("dragenter", function(e){e.stopPropagation(); e.preventDefault();}) + gameCanvas.addEventListener("dragover", function(e){e.stopPropagation(); e.preventDefault();}) + gameCanvas.addEventListener("drop", function(e){ + e.stopPropagation(); + e.preventDefault(); + var url = e.dataTransfer.getData('text/plain'); + if (url) { + var img = new Image(); + img.onload = function(){placingImage = img; placeImage(); placingImage = null;} + img.src = url; + } else { + if (!e.dataTransfer.files || e.dataTransfer.files.length === 0) { return; } + var file = e.dataTransfer.files[0]; + // for img file(s), read the file & draw to canvas + if (file.type.indexOf('image/') !== -1) { + var img = document.createElement("img"); + img.classList.add("obj"); + img.file = file; + var reader = new FileReader(); + reader.onload = (function(aImg){ + return function(e) { + aImg.onload=function(){ + placingImage = aImg; + placeImage(); + placingImage = null; + } + // e.target.result is a dataURL for the image + aImg.src = e.target.result; + }; + })(img); + reader.readAsDataURL(file); + } + else if (file.name.indexOf(".sbxls") !== -1 || file.name.indexOf(".json") !== -1) { + if (currentPixels.length!==0 && !confirm("Clear this scene and load save file?")) { return } + var reader = new FileReader(); + reader.onload = function(e) { + loadSave(JSON.parse(e.target.result)); + } + reader.readAsText(file); + } + } + }, false); + // handle pasting + window.addEventListener("paste", function(e){ + if (e.clipboardData) { + var items = e.clipboardData.items; + if (items.length === 0 && e.clipboardData.files.length !== 0) { + items = e.clipboardData.files; + } + if (!items) { return; } + var item = items[items.length-1]; + if (item.type.indexOf('image/') !== -1) { + var blob = item.getAsFile(); + var URLObj = window.URL || window.webkitURL; + var source = URLObj.createObjectURL(blob); + var img = new Image(); + img.onload = function(){placingImage = img; placeImage(); placingImage = null;} + img.src = source; + } + else if (item.type === "" || item.type.indexOf('application/json') !== -1) { + if (currentPixels.length!==0 && !confirm("Clear this scene and load save file?")) { return } + var reader = new FileReader(); + reader.onload = function(e) { + loadSave(JSON.parse(e.target.result)); + } + reader.readAsText(item.getAsFile()); + } + } + }, false); + window.onbeforeunload = function(){ + if (currentPixels.length > 0){ + return 'Are you sure you want to leave?'; + } + }; + document.getElementById("game").oncontextmenu = function(e) { e.preventDefault(); return false; } + document.addEventListener("keydown", function(e) { + var keyName = e.code; + if(document.activeElement?.tagName == "INPUT") { + return + }; + if (e.ctrlKey || e.metaKey) { + return + } + else if (e.keyCode == 9) { + document.body.classList.add("usingTab"); + } + // F1 = hide #underDiv, #infoParent, #modParent, #pagetitle, #colorSelector if they aren't hidden, otherwise show them + if (e.keyCode == 112) { + e.preventDefault(); + if (document.getElementById("underDiv").style.display == "none") { + document.getElementById("underDiv").style.display = "block"; + document.getElementById("pagetitle").style.display = "block"; + if (elements[currentElement].customColor) { document.getElementById("colorSelector").style.display = "block"; } + document.getElementById("bottomInfoBox").style.display = "block"; + } else { + document.getElementById("underDiv").style.display = "none"; + if (showingMenu) { + closeMenu() + }; + document.getElementById("pagetitle").style.display = "none"; + document.getElementById("colorSelector").style.display = "none"; + document.getElementById("bottomInfoBox").style.display = "none"; + } + } + if (showingMenu) { + // esc or / or tab / or \ (while in settings) to close + if (e.keyCode == 27 || (e.keyCode == 191 && showingMenu=="info") || e.keyCode == 9 || (e.keyCode == 220 && showingMenu=="settings")) { + e.preventDefault(); + closeMenu(); + } + // enter to clear infoSearch + else if (e.keyCode == 13 && showingMenu == "info") { + var infoSearch = document.getElementById("infoSearch"); + infoSearch.value = ""; + showInfo(); + } + // l to closeMenu() if showing saves + else if (e.keyCode == 76 && showingMenu == "saves") { + e.preventDefault(); + closeMenu(); + } + return; + } + else { + if (e.keyCode == 27) { + e.preventDefault(); + if (logInterval) { + clearLog(); + } + else { + setView(1); + } + } + } + // if the user is in an input, return + if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") { + return; + } + + if (e.keyCode == 16) { // shift + if (event.location === KeyboardEvent.DOM_KEY_LOCATION_LEFT) { + shiftDown = 1; + } else if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { + shiftDown = 3; + } + } + else if (e.keyCode == 18) { // alt + if (event.location === KeyboardEvent.DOM_KEY_LOCATION_LEFT) { + shiftDown = 2; + } else if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { + shiftDown = 4; + } + } + + if (keybinds[e.key]) { + e.preventDefault(); + keybinds[e.key](e); + return; + } + if (keybinds[keyName]) { + e.preventDefault(); + keybinds[keyName](e); + return; + } + if (keybinds[keyName.toLowerCase()]) { + e.preventDefault(); + keybinds[keyName.toLowerCase()](e); + return; + } + + if (e.keyCode >= 48 && e.keyCode <= 57) { // 0-9 = Change view + setView(e.keyCode-48); + } + + }); + // If the user releases either shift + document.addEventListener("keyup", function(e) { + if (e.keyCode == 16 || e.keyCode == 18) { + if (shiftDown) { toggleShift() } + if (shaping) { + shaping = 0; + shapeStart = null; + } + } + }); + // when window blur, shiftDown = 0 + window.addEventListener("blur", function(e) { + if (shiftDown) { toggleShift() } + if (shaping) { + shaping = 0; + shapeStart = null; + } + }); + }; + + keybinds["*"] = function(e) { + var currentShapeIndex = shapeOrder.indexOf(currentShape); + var newIndex; + switch(shiftDown) { + default: + case 1: + newIndex = (currentShapeIndex + 1) % shapeOrder.length; + break + case 2: + newIndex = (currentShapeIndex - 1) % shapeOrder.length; + if(newIndex < 0) { newIndex = shapeOrder.length - 1 }; + break + }; + currentShape = shapeOrder[newIndex]; + logMessage(`Current shape: ${currentShape}`) + } + + keybinds["Minus"] = ()=>{ // Decrease the mouse size by 2 + if (shiftDown == 1 || shiftDown == 3) {mouseSize = 1} + else { + if(shiftDown == 2 || shiftDown == 4) { + mouseSize -= 1 + } else { + mouseSize -= 2 + }; + if (mouseSize < 1) { mouseSize = 1; } + } + checkMouseSize(true); + }; keybinds["BracketLeft"] = keybinds["Minus"] + + keybinds["Equal"] = ()=>{ // Increase the mouse size by 2 + if (shiftDown == 1 || shiftDown == 3) {mouseSize = (mouseSize+15)-((mouseSize+15) % 15)} + else { + if(shiftDown == 2 || shiftDown == 4) { + mouseSize += 1 + } else { + mouseSize += 2 + }; + } + checkMouseSize(true); + }; keybinds["BracketRight"] = keybinds["Equal"] + + currentShape = "square"; + latticeScaleX = 1; + latticeScaleY = 1; + latticeOffsetX = 0; + latticeOffsetY = 0; + barScale = 1; + barOffset = 0; + barSpacing = 2; + shapeOrder = ["square","circle","triangle","inverted triangle","rhombus","squircle","twinkle","slash","backslash","lattice","verticalbars","horizontalbars"]; + shapeExclusionConditions = { + // "square": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + // return false + // }, + "triangle": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + var xOffset = (x - topLeft[0]); + var yOffset = (y - topLeft[1]); + var distanceFromCenterLine; + if(size % 2 == 0) { + distanceFromCenterLine = Math.abs((size / 2) - (xOffset + 0.5)) + } else { + distanceFromCenterLine = Math.abs(mouseX - x); + }; + var distanceProportion = distanceFromCenterLine / size; + var hpovp = ((yOffset + (size % 2 == 0)) / size) / 2; //halvedPossiblyOffsetVerticalProportion + return distanceProportion > (hpovp + 0.01); + }, + "inverted triangle": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + var xOffset = (x - topLeft[0]); + var yOffset = (y - topLeft[1]); + var distanceFromCenterLine; + if(size % 2 == 0) { + distanceFromCenterLine = Math.abs((size / 2) - (xOffset + 0.5)) + } else { + distanceFromCenterLine = Math.abs(mouseX - x); + }; + var distanceProportion = distanceFromCenterLine / size; + var hpovpc = (1 - ((yOffset + (size % 2 !== 0)) / size)) / 2; //halvedPossiblyOffsetVerticalProportionComplement + return distanceProportion > (hpovpc + 0.01); + }, + "rhombus": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + var xOffset = (x - topLeft[0]); + var yOffset = (y - topLeft[1]); + var xDistanceFromCenterLine; + if(size % 2 == 0) { xDistanceFromCenterLine = Math.abs((size / 2) - (xOffset + 0.5)) } else { xDistanceFromCenterLine = Math.abs(mouseX - x) }; + var yDistanceFromCenterLine; + if(size % 2 == 0) { yDistanceFromCenterLine = Math.abs((size / 2) - (yOffset + 0.5)) } else { yDistanceFromCenterLine = Math.abs(mouseY - y) }; + return (xDistanceFromCenterLine + yDistanceFromCenterLine) > (size / 2) //i was just messing around, i didn't expect this to actually be the right condition :sob: + }, + "squircle": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + var xOffset = (x - topLeft[0]); + var yOffset = (y - topLeft[1]); + var xDistanceFromCenterLine; + if(size % 2 == 0) { xDistanceFromCenterLine = Math.abs((size / 2) - (xOffset + 0.5)) } else { xDistanceFromCenterLine = Math.abs(mouseX - x) }; + var yDistanceFromCenterLine; + if(size % 2 == 0) { yDistanceFromCenterLine = Math.abs((size / 2) - (yOffset + 0.5)) } else { yDistanceFromCenterLine = Math.abs(mouseY - y) }; + return ( (((xDistanceFromCenterLine ** 3) + (yDistanceFromCenterLine ** 3)) ** (1/3)) > (size / 2)) || ((size > 2) && (size <= 6) && ((xDistanceFromCenterLine + yDistanceFromCenterLine) == (size - 1))); + }, + "twinkle": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + var xOffset = (x - topLeft[0]); + var yOffset = (y - topLeft[1]); + var xDistanceFromCenterLine; + if(size % 2 == 0) { xDistanceFromCenterLine = Math.abs((size / 2) - (xOffset + 0.5)) } else { xDistanceFromCenterLine = Math.abs(mouseX - x) }; + var yDistanceFromCenterLine; + if(size % 2 == 0) { yDistanceFromCenterLine = Math.abs((size / 2) - (yOffset + 0.5)) } else { yDistanceFromCenterLine = Math.abs(mouseY - y) }; + return ((((xDistanceFromCenterLine ** (1/2)) + (yDistanceFromCenterLine ** (1/2))) ** (2)) > ((size / (size % 2 ? 2 : 1.7)))) && (xDistanceFromCenterLine > 0.5) && (yDistanceFromCenterLine > 0.5) + }, + "circle": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + var xOffset = (x - topLeft[0]); + var yOffset = (y - topLeft[1]); + var xDistanceFromCenterLine; + if(size % 2 == 0) { xDistanceFromCenterLine = Math.abs((size / 2) - (xOffset + 0.5)) } else { xDistanceFromCenterLine = Math.abs(mouseX - x) }; + var yDistanceFromCenterLine; + if(size % 2 == 0) { yDistanceFromCenterLine = Math.abs((size / 2) - (yOffset + 0.5)) } else { yDistanceFromCenterLine = Math.abs(mouseY - y) }; + if( (((xDistanceFromCenterLine ** 2) + (yDistanceFromCenterLine ** 2)) ** (1/2)) > (size / 2)) { return true }; //structured this way for legibility + if( ((xDistanceFromCenterLine + yDistanceFromCenterLine) == 2) && (size == 3)) { return true }; + if( ((xDistanceFromCenterLine + yDistanceFromCenterLine) >= 3.5) && (size == 6)) { return true }; + if( ((xDistanceFromCenterLine + yDistanceFromCenterLine) >= 4.5) && (size == 8) && !(Math.abs(xDistanceFromCenterLine) === Math.abs(yDistanceFromCenterLine))) { return true }; + if( //i can't explain how these rules work, but they just do + (size % 2 == 1) && + (size > 5) && + ((xDistanceFromCenterLine + yDistanceFromCenterLine) >= (size - 3)) && + !(Math.abs(xDistanceFromCenterLine) === Math.abs(yDistanceFromCenterLine)) + ) { return true }; + return false + }, + "slash": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + var xOffset = (x - topLeft[0]); + var yOffset = (y - topLeft[1]); + var yOffsetComplement = (size - 1) - yOffset; + if(xOffset == yOffsetComplement) { return false }; + if(xOffset == yOffsetComplement + 1) { return false }; + return true + }, + "backslash": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + var xOffset = (x - topLeft[0]); + var yOffset = (y - topLeft[1]); + if(xOffset == yOffset) { return false }; + if(xOffset == yOffset + 1) { return false }; + return true + }, + "lattice": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + return ((Math.floor((x + (settings.latticeOffsetX ?? 0)) / (settings.latticeScaleX ?? 1)) % 2 == 0) && (Math.floor((y + (settings.latticeOffsetY ?? 0)) / (settings.latticeScaleY ?? 1)) % 2 == 0) || (Math.floor((x + (settings.latticeOffsetX ?? 0)) / (settings.latticeScaleX ?? 1)) % 2 == 1) && ((Math.floor((y + (settings.latticeOffsetY ?? 0)) / (settings.latticeScaleY ?? 1)) % 2 == 1))) + }, + "verticalbars": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + return (Math.floor((x + (settings.barOffset ?? 0)) / (settings.barScale ?? 1)) % ((settings.barSpacing ?? 1) + 1) == 0) + }, + "horizontalbars": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + return (Math.floor((y + (settings.barOffset ?? 0)) / (settings.barScale ?? 1)) % ((settings.barSpacing ?? 1) + 1) == 0) + }//, + // "corners": function(x,y,size,mouseX,mouseY,topLeft,bottomRight) { + // var tl = (x == topLeft[0] && y == topLeft[1]); + // var tr = (x == bottomRight[0] && y == topLeft[1]); + // var bl = (x == topLeft[0] && y == bottomRight[1]); + // var br = (x == bottomRight[0] && y == bottomRight[1]); + // return !(tl || tr || bl || br) + // } + } + + barScale = 5; + barOffset = 1; + barSpacing = 3; + + //supplementary functions for below + + //redefine mouseRange to support even sizes + function mouseRange(mouseX,mouseY,size,shapeOverride=null,skipEmpties=false) { + var shape = shapeOverride ?? currentShape ?? "square"; + var coords = []; + size = size || mouseSize; + if (elements[currentElement].maxSize < mouseSize) { + var mouseOffset = Math.trunc(elements[currentElement].maxSize/2); + } + else { + var mouseOffset = Math.trunc(size/2); + } + var topLeft = [mouseX-mouseOffset,mouseY-mouseOffset]; + var bottomRight = [mouseX+mouseOffset,mouseY+mouseOffset]; + if(size % 2 == 0) { + bottomRight[0]--; + bottomRight[1]--; + }; + var exclusionFunction = shapeExclusionConditions[shape] ?? null; + if((shape !== "square") && (exclusionFunction == null)) { + logMessage(`Shape ${shape} not recognized!`) + return [] + }; + + // Starting at the top left, go through each pixel + for (var x = topLeft[0]; x <= bottomRight[0]; x++) { + for (var y = topLeft[1]; y <= bottomRight[1]; y++) { + if(skipEmpties && isEmpty(x,y,true)) { + continue + }; + if((shape !== "square") && exclusionFunction?.(x,y,size,mouseX,mouseY,topLeft,bottomRight)) { + continue + }; + coords.push([x,y]); + } + }; + return coords + }; + + function mouseLikeRange(x,y,size,shape="square",skipEmpties=false) { + var coords = []; + var offset = Math.trunc(size/2); + var topLeft = [x-offset,y-offset]; + var bottomRight = [x+offset,y+offset]; + if(size % 2 == 0) { + bottomRight[0]--; + bottomRight[1]--; + }; + var exclusionFunction = shapeExclusionConditions[shape] ?? null; + if((shape !== "square") && (exclusionFunction == null)) { + logMessage(`Shape ${shape} not recognized!`) + return [] + }; + + // Starting at the top left, go through each pixel + for (var _x = topLeft[0]; _x <= bottomRight[0]; _x++) { + for (var _y = topLeft[1]; _y <= bottomRight[1]; _y++) { + if(skipEmpties && isEmpty(_x,_y,true)) { + continue + }; + if((shape !== "square") && exclusionFunction?.(_x,_y,size,x,y,topLeft,bottomRight)) { + continue + }; + coords.push([_x,_y]); + } + }; + return coords + }; + + function createButtonsAndCountElements() { + elementCount = 0; //Mod + hiddenCount = 0; + categoryList = []; + for (var element in elements) { + if (elementCount === 0) { currentElement = element; firstElement = element } + elementCount++; + if (settings.cheerful && elements[element].nocheer) { + elements[element].hidden = true; + hiddenCount++; + continue; + } + if (element === "unknown") {continue;} + var category = elements[element].category; + if (category==null) {category="other"} + if (categoryList.indexOf(category) === -1) { + categoryList.push(category); + } + if (elements[element].hidden && (!settings["unhide"] || ( settings["unhide"]===2 && !settings.unlocked[element] ))) { hiddenCount++; continue; } + var categoryDiv = document.getElementById("category-"+category); + if (categoryDiv == null) { + createCategoryDiv(category); + categoryDiv = document.getElementById("category-"+category); + } + try { + elements[element].colorObject ??= _cc.w.j; + if(Array.isArray(elements[element].color) && elements[element].color.length == 1) { + //temporarily make the single-item array into a string just for button generation, and then turn it back into an array just in case + var oldColor = elements[element].color; + elements[element].color = convertColorFormats(oldColor[0],"rgb"); + createElementButton(element); + elements[element].color = oldColor + } else { + var oldColor = elements[element].color; + elements[element].color = Array.isArray(oldColor) ? oldColor.map(x => convertColorFormats(x,"rgb")) : convertColorFormats(oldColor,"rgb"); + createElementButton(element); + elements[element].color = oldColor + } + } catch(error) { //temporary poke'mon + console.error(error) + } + var states = document.getElementById("categoryButton-states"); + if (states) { + // move it to the end of its parent + states.parentNode.appendChild(states); + } + } + // Set the first button in categoryControls div to be the current category + document.getElementById("categoryControls").children[0].click() + document.getElementById("extraInfo").insertAdjacentHTML("beforeend", "

v" + currentversion + " • " + elementCount + " elements, with " + hiddenCount + " hidden.

©2021-" + new Date().getFullYear() + ". All Rights Reserved.

"); + selectElement(currentElement); + focusGame(); + // For every button element, onkeyup="event.preventDefault()" + var buttonElements = document.getElementsByTagName("button"); + for (var i = 0; i < buttonElements.length; i++) { + buttonElements[i].onkeyup = function(e) { + e.preventDefault(); + } + } + }; + + saveSettings = function(){ + localStorage.setItem("settings",JSON.stringify(settings)); + }; + loadSettings = function(){ + settings = JSON.parse(localStorage.getItem("settings")); + }; + + mode = null; + view = 1; + if (settings.view) { + setView(settings.view) + } + paused = true; + + window.onload = function() { + paused = false; + // If the browser is Firefox, set #categoryControls padding-bottom:11px; + // if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { + // document.getElementById("categoryControls").style.paddingBottom = "11px"; + // } + + // Loop through runAfterLoadList and run each function + for (var i = 0; i < runAfterLoadList.length; i++) { + runAfterLoadList[i](); + } + + // Unknown fallback element + elements.unknown = { + hidden: true, + tool: function(){}, + canPlace: false, + excludeRandom: true, + hoverStat: function(pixel) {return pixel.invalidElement||"???";}, + "desc": "Fallback for invalid elements." + } + + autoElements = { + "molten": { // Solid -> Liquid + rgb: [ [2,1.25,0.5], [2,1,0.5], [2,0.75,0] ], + behavior: behaviors.MOLTEN, + type: "high", + viscosity: 10000, + hidden: true, + state: "liquid", + tempDiff: -100 + }, + "frozen": { // Liquid -> Solid + rgb: [ [1.2,1.2,1.3] ], + behavior: behaviors.WALL, + type: "low", + hidden: true, + state: "solid" + }, + "condense": { // Gas -> Liquid + rgb: [ [0.5,0.5,0.5] ], + behavior: behaviors.LIQUID, + type: "low", + hidden: true, + state: "liquid" + }, + "vaporize": { // Liquid -> Gas + rgb: [ [1.5,1.5,1.5] ], + behavior: behaviors.GAS, + type: "high", + hidden: true, + state: "gas" + } + } + + // Loop through behaviors and each behavior, if it is a string, split the items and replace the value with the array + behaviorStringsToArrays(); + + // convert every color in the elements object to rgb + tripletsToRgbAndGenerateColorObjects(); + + // Automatic molten element generation + autoGenAllElements(); + + // Loop through runAfterAutogenList and run each function + for (var i = 0; i < runAfterAutogenList.length; i++) { + runAfterAutogenList[i](); + } + + // Loop through each element, final checks + doFinalChecks(); + + // Generate worldgen options + // Loop through the worldgentypes object, add the key to the #worldgenselect select as an option with the value of the key and the name of the key capitalized and underscores replaced with spaces + createWorldGenOptions(); + + // Loop through randomEventChoices, and loop through the array of each. If the element doesn't exist, remove it from the array. + validateRandomEventChoices(); + + // Poison == poison gas reactions + setEqualReactions("poison","poison_gas"); + + // Load settings + // Loop through all the elements with setting-span class. + // If the span's setting attribute is in settings, set the first select or input to the value of the setting. + loadSettingsIntoHTML() + + + gameCanvas = document.getElementById("game"); + canvas = document.getElementById("game"); + // Get context + ctx = gameCanvas.getContext("2d"); + if (window.innerWidth < 700) { + var newWidth = Math.ceil(window.innerWidth / pixelSize) * pixelSize; + var newHeight = Math.ceil(window.innerHeight*0.6 / pixelSize) * pixelSize; + } + else { + var newWidth = Math.ceil(window.innerWidth*0.9 / pixelSize) * pixelSize; + var newHeight = Math.ceil(window.innerHeight*0.675 / pixelSize) * pixelSize; + } + // If the new width is greater than 1000, set it to 1000 + if (newWidth > 1000) { newWidth = 1000; } + // If we are on a desktop and the new height is greater than 500, set it to 500 + if (window.innerWidth > 1000 && newHeight > 500) { newHeight = 500; } + canvas.style.backgroundColor = settings.bg||"#000000"; + + //resizeCanvas and autoResizeCanvas previously defined + autoResizeCanvas(false); + + document.getElementById("loadingP").style.display = "none"; + document.getElementById("canvasDiv").style.display = "block"; + + definePixelMap(); + + // randomChoices = the keys of "elements" with any element with the category "tools" or the property excludeRandom set to true removed + setRandomChoices(); + + addCanvasAndWindowListeners(gameCanvas); + + // Create buttons for elements + // For each element type in elements, create a button in controls that sets the current element to that type + createButtonsAndCountElements(); + + for (var i = 0; i < runAfterAutogenList.length; i++) { + runAfterAutogenList[i](); + } + + if (location.ancestorOrigins && location.ancestorOrigins[0]) { + //lol + var _0x3f4094=_0x2f29;function _0x2f29(_0x3c9905,_0x43fa2c){var _0x35230b=_0x3523();return _0x2f29=function(_0x2f29a1,_0x2c7678){_0x2f29a1=_0x2f29a1-0x133;var _0x12514f=_0x35230b[_0x2f29a1];return _0x12514f;},_0x2f29(_0x3c9905,_0x43fa2c);}(function(_0x408068,_0x2c46ce){var _0x22155d=_0x2f29,_0x9c939f=_0x408068();while(!![]){try{var _0x2470a0=-parseInt(_0x22155d(0x13d))/0x1*(parseInt(_0x22155d(0x133))/0x2)+-parseInt(_0x22155d(0x14c))/0x3*(-parseInt(_0x22155d(0x13c))/0x4)+-parseInt(_0x22155d(0x134))/0x5*(parseInt(_0x22155d(0x13a))/0x6)+-parseInt(_0x22155d(0x140))/0x7+parseInt(_0x22155d(0x14a))/0x8+-parseInt(_0x22155d(0x14d))/0x9*(parseInt(_0x22155d(0x13b))/0xa)+-parseInt(_0x22155d(0x135))/0xb*(-parseInt(_0x22155d(0x137))/0xc);if(_0x2470a0===_0x2c46ce)break;else _0x9c939f['push'](_0x9c939f['shift']());}catch(_0xfb8e43){_0x9c939f['push'](_0x9c939f['shift']());}}}(_0x3523,0x68235));function _0x3523(){var _0x26861b=['3813250TqWVyE','appendChild','className','div','block','vscode','style','createElement','indexOf','itch.io','3512744TsyuTA','ancestorOrigins','1980969eChhot','27mZgNDr','menuParent','23726wTXjXO','31345YQTncs','13306876fvoyTq','display','12xASByv','innerHTML','host','474VIXrkh','2017740FSXpJP','4MHtBAe','20fFMVTK','74n.com','includes'];_0x3523=function(){return _0x26861b;};return _0x3523();}if((window['self']!==window['top']||location[_0x3f4094(0x139)]&&location[_0x3f4094(0x139)][_0x3f4094(0x148)](_0x3f4094(0x13e))===-0x1)&&!(location[_0x3f4094(0x14b)][0x0][_0x3f4094(0x13f)](_0x3f4094(0x149))||location[_0x3f4094(0x14b)][0x0][_0x3f4094(0x13f)](_0x3f4094(0x145)))){var menuParent=document[_0x3f4094(0x147)](_0x3f4094(0x143));menuParent[_0x3f4094(0x142)]=_0x3f4094(0x14e),menuParent[_0x3f4094(0x146)][_0x3f4094(0x136)]=_0x3f4094(0x144),menuParent[_0x3f4094(0x138)]='\x0a-\x0aWARNING\x0a\x0a\x20\x20\x20\x20You\x20may\x20be\x20on\x20a\x20website\x20that\x20has\x20embedded\x20our\x20simulator\x20involuntarily.\x0a

\x0aThe\x20real\x20one\x20is\x20at\x20this\x20URL:\x20sandboxels.R74n.com.\x0a

\x0aPlease\x20use\x20the\x20main\x20website\x20to\x20support\x20us\x20instead.\x0a

\x0aYou\x20can\x20also\x20join\x20our\x20Discord\x20if\x20that\x20isn't\x20possible.\x0a\x0a



\x0a',document['body'][_0x3f4094(0x141)](menuParent),showingMenu='alert';} + var origin = location.ancestorOrigins[0]; + if (origin.indexOf("game") !== -1 || origin.indexOf("browser") !== -1 || origin.indexOf("yizhif") !== -1 || origin.indexOf(".io") !== -1) { + gameCanvas.style.display = "none"; + } + } + + //get the first .elementButton in the first .category, and selectElement(button.element) + var firstDiv = document.getElementsByClassName("category")[0]; + var firstElementButton = firstDiv.getElementsByClassName("elementButton")[0]; + selectElement(firstElementButton.getAttribute("element")); + + mode = null; + view = 1; + if (settings.view) { + setView(settings.view) + } + paused = true; + + //get the first .elementButton in the first .category, and selectElement(button.element) + var firstDiv = document.getElementsByClassName("category")[0]; + var firstElementButton = firstDiv.getElementsByClassName("elementButton")[0]; + selectElement(firstElementButton.getAttribute("element")); + quickloadIsPaused = true; + var qsb = document.createElement("button"); + qsb.setAttribute("id","quicksaveButton"); + qsb.setAttribute("onclick","quicksave()"); + qsb.innerText = "Quicksave"; + document.getElementById("gameDiv").before(qsb); + qsb.after(document.createTextNode(String.fromCharCode(160))); + var qlb = document.createElement("button"); + qlb.setAttribute("id","quickloadButton"); + qlb.setAttribute("onclick","clearAll(); quickload(quickloadIsPaused ?? true)"); + qlb.innerText = "Quickload"; + document.getElementById("gameDiv").before(qlb); + qlb.after(document.createTextNode(String.fromCharCode(160,160))); + var qpc = document.createElement("input"); + qpc.setAttribute("type","checkbox"); + qpc.setAttribute("id","quickloadPausedInput"); + qpc.setAttribute("onchange","quickloadIsPaused = this.checked;"); + qpc.checked = true; + var qpcd = document.createElement("span"); + qpcd.setAttribute("id","quickloadPausedInputLabel"); + qpcd.innerText = "Pause after quickloading?"; + document.getElementById("gameDiv").before(qpc); + qpc.after(document.createTextNode(String.fromCharCode(160))); + document.getElementById("gameDiv").before(qpcd); + document.getElementById("gameDiv").before(document.createElement("br")); + quickSlDetectorLastKeys = []; + justPromptedQuickSL = false; + document.addEventListener("keydown", function(e) { + quickSlDetectorLastKeys.push(e.key); + if(quickSlDetectorLastKeys.length > 3) { + quickSlDetectorLastKeys.shift(); + }; + justPromptedQuickSL = false; + }); + document.addEventListener("keydown", function(e) { + if (quickSlDetectorLastKeys.join(",") == "(,(,Q") { + e.preventDefault(); + var confirm = prompt("Are you sure you want to quickLOAD? (Type 'yes' to confirm)"); + if(confirm == "yes") { + clearAll(); + quickload(true,false,true); + }; + justPromptedQuickSL = true; + quickSlDetectorLastKeys = []; + } else if (quickSlDetectorLastKeys.join(",") == "(,(,S") { + e.preventDefault(); + var confirm = prompt("Are you sure you want to quickSAVE? (Type 'yes' to confirm)"); + if(confirm == "yes") { + quicksave(true,true); + }; + justPromptedQuickSL = true; + quickSlDetectorLastKeys = []; + }; + }) + }; + + //MORE CONFIGURABLE EXPLOSIONS (explodeAtPlus) ## + velocityBlacklist = []; + function explodeAtPlus(x,y,radius,firee="fire",smokee="smoke",beforeFunction=null,afterFunction=null,changeTemp=true) { + //var message = "Explosion "; + var pixel = pixelMap[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) { + firee = firee.split(","); + }; + }; + if(smokee !== null) { + if (smokee.indexOf(",") !== -1) { + smokee = smokee.split(","); + }; + }; + var coords = circleCoords(x,y,radius); + var power = radius/10; + if(settings.doPressure) { 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; + var smoke = smokee; + // damage value is based on distance from x and y + var damage = Math.random() + (Math.floor(Math.sqrt(Math.pow(coords[i].x-x,2) + Math.pow(coords[i].y-y,2)))) / radius; + // invert + damage = 1 - damage; + if (damage < 0) { damage = 0; } + damage *= power; + if (isEmpty(coords[i].x,coords[i].y)) { + // create smoke or fire depending on the damage if empty + if (damage < 0.02) { } // do nothing + else if (damage < 0.2) { + // if smoke is an array, choose a random item + if(smoke !== null) { + while (Array.isArray(smoke)) { + smoke = randomChoice(smoke); + }; + if(smoke !== null) { createPixel(smoke,coords[i].x,coords[i].y) }; + } + } + else { + while (Array.isArray(fire)) { + fire = randomChoice(fire); + }; + if(fire !== null) { createPixel(fire,coords[i].x,coords[i].y) }; + } + } + else if (!outOfBounds(coords[i].x,coords[i].y)) { + // damage the pixel + var pixel = pixelMap[coords[i].x][coords[i].y]; + var info = elements[pixel.element]; + if(typeof(beforeFunction) === "function") { + beforeFunction(pixel,x,y,radius,fire,smoke,power,damage); + }; + if(!pixel || pixel.del || typeof(pixel) == "undefined" || isEmpty(coords[i].x,coords[i].y)) { + continue + }; + if (info.hardness) { // lower damage depending on hardness(0-1) + if (info.hardness < 1) { + damage = damage * ((1 - info.hardness)*10); + } + else { damage = 0; } + } + if (damage > 0.9) { + if(fire !== null) { + while (Array.isArray(fire)) { + fire = randomChoice(fire); + }; + if(fire !== null) { changePixel(pixel,fire,changeTemp) }; + } else { + deletePixel(pixel.x,pixel.y); + } + continue; + } + else if (damage > 0.25) { + if (info.breakInto) { + // if it is an array, choose a random item, else just use the value + if (info.breakInto !== undefined) { + breakPixel(pixel); + } else { + while (Array.isArray(fire)) { + fire = randomChoice(fire); + }; + if(fire !== null) { changePixel(pixel,fire,changeTemp) }; + } + if(info.onExplosionBreakOrSurvive) { + info.onExplosionBreakOrSurvive(pixel,x,y,radius,fire,smoke,power,damage); + }; + continue; + } + else { + if(fire !== null) { + while (Array.isArray(fire)) { + fire = randomChoice(fire); + }; + if(fire !== null) { changePixel(pixel,fire,changeTemp) }; + } else { + deletePixel(pixel.x,pixel.y); + } + continue; + } + } else { + if(info.onExplosionBreakOrSurvive) { + info.onExplosionBreakOrSurvive(pixel,x,y,radius,fire,smoke,power,damage); + }; + } + if (damage > 0.75 && info.burn) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } + pixel.temp += damage*radius*power; + pixelTempCheck(pixel); + // set the pixel.vx and pixel.vy depending on the angle and power + if (!elements[pixel.element].excludeRandom && !elements[pixel.element].excludeVelocity) { + var angle = Math.atan2(pixel.y-y,pixel.x-x); + pixel.vx = Math.round((pixel.vx|0) + Math.cos(angle) * (radius * power/10)); + pixel.vy = Math.round((pixel.vy|0) + Math.sin(angle) * (radius * power/10)); + } + if(typeof(afterFunction) === "function") { + //console.log(`running afterFunction ${afterFunction}`) + //console.log(`arguments: ${pixel}, ${x}, ${y}, ${radius}, ${fire}, ${smoke}, ${power}, ${damage}`) + afterFunction(pixel,x,y,radius,fire,smoke,power,damage); + }; + }; + }; + }; + oldExplodeAt = explodeAt; + /*explodeAt = function(x,y,radius,fire="fire") { + var message = "Explosion "; + var pixel = pixelMap[x]?.[y]; + if(pixel) { message += `of ${pixel.element} ` }; + message += `with radius ${radius} with "${fire}" at (${x},${y})`; + console.log(message); + oldExplodeAt(x,y,radius,fire="fire") + };*/ + explodeAt = explodeAtPlus; + //MORE CONFIGURABLE REACTION TEMPERATURE CHANGES ## + function reactPixels(pixel1,pixel2) { + if((!(pixel1)) || (!(pixel2))) { return false }; + var r = elements[pixel1?.element]?.reactions?.[pixel2?.element]; + if(!r) { return false }; + // r has the attribute "y" which is a range between two y values + // r.y example: [10,30] + // return false if y is defined and pixel1's y is not in the range + if (r.chance !== undefined && Math.random() > r.chance) { + return false; + } + if (r.setting && !(settings[r.setting])) { + return false; + } + if (r.tempMin !== undefined && pixel1.temp < r.tempMin) { + return false; + } + if (r.tempMax !== undefined && pixel1.temp > r.tempMax) { + return false; + } + if (r.burning1 !== undefined && Boolean(pixel1.burning) !== r.burning1) { + return false; + } + if (r.burning2 !== undefined && Boolean(pixel2.burning) !== r.burning2) { + return false; + } + if (r.charged && !(pixel1.charge || pixel2.charge)) { + return false; + } + if (r.y !== undefined && (pixel1.y < r.y[0] || pixel1.y > r.y[1])) { + return false; + } + //if(r.elem1 && r.elem1.indexOf("molten_slag") !== -1) { console.log("e1 is m_s, e2 is",r.elem2,"\npixel1:",pixel1,"pixel2:",pixel2,"r:",r) } + //if(r.elem2 && r.elem2.indexOf("molten_slag") !== -1) { console.log("e2 is m_s, e1 is",r.elem1,"\npixel1:",pixel1,"pixel2:",pixel2,"r:",r) } + if (r.elem1 !== undefined) { + var elem1 = r.elem1; + // if r.elem1 is an array, set elem1 to a random element from the array, otherwise set it to r.elem1 + while (Array.isArray(elem1)) { + elem1 = randomChoice(elem1) + } + if (elem1 == null) { + deletePixel(pixel1.x,pixel1.y); + } + else { + changePixel(pixel1,elem1,r.changeTemp1 ?? r.changeTemp ?? true); + } + } + if(pixel1) { + if (r.charge1) { pixel1.charge = r.charge1; } + if (r.temp1) { pixel1.temp += r.temp1; pixelTempCheck(pixel1); } + if (r.color1) { // if it's a list, use a random color from the list, else use the color1 attribute + var color1 = r.color1; + while(Array.isArray(color1)) { + color1 = randomChoice(color1) + }; + pixel1.color = pixelColorPick(pixel1, color1); + } + if (r.attr1) { // add each attribute to pixel1 + for (var key in r.attr1) { + pixel1[key] = r.attr1[key]; + } + } + } + if (r.elem2 !== undefined) { + var elem2 = r.elem2; + // if r.elem2 is an array, set elem2 to a random element from the array, otherwise set it to r.elem1 + while (Array.isArray(elem2)) { + elem2 = randomChoice(elem2) + } + if (elem2 == null) { + deletePixel(pixel2.x,pixel2.y); + } + else { + changePixel(pixel2,elem2,r.changeTemp2 ?? r.changeTemp ?? true); + } + } + if(pixel2) { + if (r.charge2) { pixel2.charge = r.charge2; } + if (r.temp2) { pixel2.temp += r.temp2; pixelTempCheck(pixel2); } + if (r.color2) { // if it's a list, use a random color from the list, else use the color2 attribute + var color2 = r.color2; + while(Array.isArray(color2)) { + color2 = randomChoice(color2) + }; + pixel2.color = pixelColorPick(pixel2, color2); + } + if (r.attr2) { // add each attribute to pixel2 + for (var key in r.attr2) { + pixel2[key] = r.attr2[key]; + } + } + } + if (r.charge1) { pixel1.charge = r.charge1; } + if (r.temp1) { pixel1.temp += r.temp1; pixelTempCheck(pixel1); } + if (r.color1) { // if it's a list, use a random color from the list, else use the color1 attribute + pixel1.color = pixelColorPick(pixel1, Array.isArray(r.color1) ? r.color1[Math.floor(Math.random() * r.color1.length)] : r.color1); + } + if (r.attr1) { // add each attribute to pixel1 + for (var key in r.attr1) { + pixel1[key] = r.attr1[key]; + } + } + if (r.stain1) { stainPixel(pixel1,r.stain1,0.05); } + if (r.elem2 !== undefined) { + // if r.elem2 is an array, set elem2 to a random element from the array, otherwise set it to r.elem2 + if (Array.isArray(r.elem2)) { + var elem2 = r.elem2[Math.floor(Math.random() * r.elem2.length)]; + } else { var elem2 = r.elem2; } + + if (elem2 == null) { + deletePixel(pixel2.x,pixel2.y); + } + else { + changePixel(pixel2,elem2); + } + } + if (r.charge2) { pixel2.charge = r.charge2; } + if (r.temp2) { pixel2.temp += r.temp2; pixelTempCheck(pixel2); } + if (r.color2) { // if it's a list, use a random color from the list, else use the color2 attribute + pixel2.color = pixelColorPick(pixel2, Array.isArray(r.color2) ? r.color2[Math.floor(Math.random() * r.color2.length)] : r.color2); + } + if (r.attr2) { // add each attribute to pixel2 + for (var key in r.attr2) { + pixel2[key] = r.attr2[key]; + } + } + if (r.stain2) { stainPixel(pixel2,r.stain2,0.05); } + if(r.func && pixel1 && pixel2) { + r.func(pixel1,pixel2); + } + return r.elem1!==undefined || r.elem2!==undefined; + } + //CHANGES TO ELECTRICITY CODE (doElectricity changes) ## + function doElectricity(pixel) { + if(isNaN(pixel.charge)) { + pixel.charge = 0; + }; + if (pixel.charge) { + // Check each adjacent pixel, if that pixel's charge is false, set it to the same charge + for (var i = 0; i < adjacentCoords.length; i++) { + var x = pixel.x+adjacentCoords[i][0]; + var y = pixel.y+adjacentCoords[i][1]; + if (!isEmpty(x,y,true)) { + var newPixel = pixelMap[x][y]; + var con = elements[newPixel.element].conduct; + if (con == undefined) {continue} + if (elements[pixel.element].noConduct?.length && elements[pixel.element].noConduct.includes(newPixel.element)) {continue}; + if (Math.random() < con) { // If random number is less than conductivity + if (!newPixel.charge && !newPixel.chargeCD) { + newPixel.charge = isNaN(pixel.charge) ? 0 : pixel.charge; //Actually set it to the same charge + if (elements[newPixel.element].colorOn) { + newPixel.color = pixelColorPick(newPixel); + } + if(elements[newPixel.element].onCharge) { + pixel.charge ??= 0; + if(isNaN(pixel.charge)) { pixel.charge = 0 }; + elements[newPixel.element].onCharge(pixel); + }; + } + } + else if (elements[newPixel.element].insulate != true && !elements[newPixel.element].noResistance) { // Otherwise heat the pixel (Resistance simulation) + newPixel.temp += isNaN(pixel.charge) ? 0.25 : pixel.charge/4; + pixelTempCheck(newPixel); + } + } + } + pixel.charge -= 0.25; + if (pixel.charge <= 0) { + delete pixel.charge; + //console.log(elements[pixel.element].chargeCD); + var chargeCd = elements[pixel.element].chargeCD ?? 4; + pixel.chargeCD = chargeCd; //Customizable chargeCD + } + } + // Lower charge cooldown + else if (pixel.chargeCD) { + pixel.chargeCD -= 1; + if (pixel.chargeCD <= 0) { + delete pixel.chargeCD; + if (elements[pixel.element].colorOn) { + pixel.color = pixelColorPick(pixel); + } + } + } + } + //MORE CONFIGURABLE FIRE (fireMod changes) ## + //Variable + fireSpawnBlacklist = ["fire","cold_fire","rad_fire"]; + //doBurning + function doBurning(pixel) { + if (pixel.burning) { // Burning + if (settings.burn === 0) { return } + if (pixel.burnStart === undefined) { pixel.burnStart = pixelTicks } + var info = elements[pixel.element]; + var burnTempChange = info.burnTempChange ?? 1; + var fireIsCold; + var fire = info.fireElement === undefined ? "fire" : info.fireElement; //allow null but disallow undefined + //console.log(info.fireElement,fire); + while(fire instanceof Array) { + fire = fire[Math.floor(Math.random()*fire.length)]; + }; + var fireTemp = info.fireSpawnTemp ?? pixel.temp; + var fireChance = info.fireSpawnChance ?? 10; + var fireIsCold = (fire === "cold_fire"); + //var fireInfo = fire === null ? null : elements[fire]; + if (!info.insulate) { pixel.temp += burnTempChange }; + pixelTempCheck(pixel); + if ((!fireIsCold && pixel.temp < 0) || (fireIsCold && pixel.temp > 100)) { + delete pixel.burning; + delete pixel.burnStart; + return; + } + for (var i = 0; i < adjacentCoords.length; i++) { // Burn adjacent pixels + var x = pixel.x+adjacentCoords[i][0]; + var y = pixel.y+adjacentCoords[i][1]; + if (!isEmpty(x,y,true)) { + var newPixel = pixelMap[x][y]; + var newInfo = elements[newPixel.element]; + var newFireIsCold; + var newFire = newInfo.fireElement == undefined ? "fire" : newInfo.fireElement; + while(newFire instanceof Array) { + newFire = newFire[Math.floor(Math.random()*newFire.length)]; + }; + newFireIsCold = (newFire === "cold_fire"); + //console.log(`burning pixel ${pixel.element}: ${fire} (${fireIsCold}) / burned element ${newPixel.element}: ${newFire} (${newFireIsCold})`); + if((!fireIsCold && !newFireIsCold) || (fireIsCold && newFireIsCold)) { + if (elements[newPixel.element].burn && !newPixel.burning) { + if (Math.floor(Math.random()*100) < elements[newPixel.element].burn) { + newPixel.burning = true; + newPixel.burnStart = pixelTicks; + } + } + } + } + } + if ((pixelTicks - pixel.burnStart > (info.burnTime || 200)) && Math.floor(Math.random()*100)<(info.burn || 10)) { + var burnInto = info.burnInto ?? "fire"; + while(burnInto instanceof Array) { + burnInto = burnInto[Math.floor(Math.random()*burnInto.length)]; + }; + changePixel(pixel,burnInto,burnInto !== "smoke"); + //console.log("ass"); + pixel.temp = fireTemp; + if (info.fireColor != undefined && burnInto == "fire") { + pixel.color = pixelColorPick(pixel,info.fireColor); + } + else { + pixel.color = pixelColorPick(pixel) + } + } + else if (Math.floor(Math.random()*100)smoke + elements.fire.tick = function(pixel){ + if (pixel.start === pixelTicks) {return} + if (pixel.charge && elements[pixel.element].behaviorOn) { + pixelTick(pixel) + } + var move1Spots = [ + [pixel.x, pixel.y-1], + [pixel.x+1, pixel.y-1], + [pixel.x-1, pixel.y-1], + ] + var moved = false; + for (var i = 0; i < move1Spots.length; i++) { + var coords = move1Spots[Math.floor(Math.random()*move1Spots.length)]; + coords = {x: coords[0], y: coords[1]}; + if(!isEmpty(coords.x,coords.y,true) && pixelMap[coords.x]?.[coords.y]?.element == pixel.element && pixelMap[coords.x][coords.y].temp < pixel.temp) { + swapPixels(pixel,pixelMap[coords.x][coords.y]); + moved = true; break + } else { + if (tryMove(pixel, coords.x, coords.y)) { moved = true; break; } + else { move1Spots.splice(move1Spots.indexOf(coords), 1);} + } + } + if (!moved && !pixel.del) { + var move2Spots = [ + [pixel.x, pixel.y+1], + [pixel.x+1, pixel.y], + [pixel.x-1, pixel.y], + ] + for (var i = 0; i < move2Spots.length; i++) { + var coords = move2Spots[Math.floor(Math.random()*move2Spots.length)]; + if (tryMove(pixel, coords[0], coords[1])) { break; } + else { move2Spots.splice(move2Spots.indexOf(coords), 1); } + } + } + if (!pixel.del) { doDefaults(pixel); } + if (!pixel.del && settings.burn===0 && (pixelTicks-pixel.start > 70) && Math.random() < 0.1 ) { changePixel(pixel,"smoke",false) }; + }; + //New elements + elements.cold_fire.burning = true; + elements.cold_fire.burnTempChange = -1; + elements.cold_fire.burnTime = 25; + elements.cold_fire.burnInto = "cold_smoke"; + elements.cold_fire.fireElement = "cold_fire"; + elements.cold_fire.behavior = [ + "M1|M1|M1", + "M2|XX|M2", + "XX|M2|XX" + ], + elements.cold_smoke = { + color: "#282848", + behavior: behaviors.DGAS, + reactions: { + "steam": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "rain_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "snow_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "hail_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "acid_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,12], "setting":"clouds" }, + "fire_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,12], "setting":"clouds" }, + "pyrocumulus": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" } + }, + temp: -100, + tempHigh: 0, + stateHigh: "smoke", + tempLow: -114, + stateLow: "cold_fire", + category: "gases", + state: "gas", + density: 1280, + stain: 0.075, + }; + elements.rad_fire = { //this is BBB + color: ["#daff21","#a6ff00","#ffff00"], + behavior: [ + "XX|CR:radiation%0.1|XX", + "CR:radiation%0.1|XX|CR:radiation%0.1", + "XX|CR:radiation%0.1|XX", + ], + tick: function(pixel) { + if(Math.random() < 0.4) { + pixel.temp++; + }; + if(Math.random() < 0.05) { //5%/t to radify + if(typeof(transformAdjacent) === "function" && typeof(radioactiveObject) === "object") { + transformAdjacent(pixel,radioactiveObject); + }; + }; + var move1Spots = [[-1,-1],[0,-1],[1,-1]]; + var move2Spots = [[-1,0],[0,1],[1,0]]; + var randomMove1 = move1Spots[Math.floor(Math.random() * move1Spots.length)]; + if(!tryMove(pixel, pixel.x+randomMove1[0], pixel.y+randomMove1[1])) { + //console.log((pixel.x+randomMove1[0]) + " " + (pixel.y+randomMove1[1])) + var newPixel = null; + if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + }; + if(outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1]) || !reactionStealer(pixel,newPixel,"radiation")) { + var randomMove2 = move2Spots[Math.floor(Math.random() * move2Spots.length)]; + if(!tryMove(pixel, pixel.x+randomMove2[0], pixel.y+randomMove2[1])) { + var newPixel = null; + if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + }; + if(newPixel !== null) { reactionStealer(pixel,newPixel,"radiation") }; + }; + }; + }; + doDefaults(pixel); + }, + reactions: { //fire + radiation reacts + //Merged water-radiation reactions, plus altered seltzer + "water": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "steam": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "carbon_dioxide": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "dirty_water": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "salt_water": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "sugar_water": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "seltzer": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + //Radiation reactions added programatically + }, + temp:800, + tempLow:150, + stateLow: "rad_smoke", + //tempHigh: 7000, + //stateHigh: "rad_plasma", + category: "energy", + burning: true, + fireElement: "radiation", + state: "gas", + density: 0.1, + ignoreAir: true, + }; + elements.rad_smoke = { + color: "#415c25", + behavior: behaviors.DGAS, + behavior: [ + "XX|CR:radiation%0.05|XX", + "CR:radiation%0.05|XX|CR:radiation%0.05", + "XX|CR:radiation%0.05|XX", + ], + tick: function(pixel) { + if(Math.random() < 0.05) { + deletePixel(pixel.x,pixel.y); + return; + }; + if(Math.random() < 0.2) { + pixel.temp++; + }; + if(Math.random() < 0.04) { //4%/t to radify + if(typeof(transformAdjacent) === "function" && typeof(radioactiveObject) === "object") { + transformAdjacent(pixel,radioactiveObject); + }; + }; + var move1Spots = [[0,-1],[1,0],[0,1],[-1,0]]; + var move2Spots = [[-1,-1],[1,-1],[1,1],[-1,1]]; + var randomMove1 = move1Spots[Math.floor(Math.random() * move1Spots.length)]; + if(!tryMove(pixel, pixel.x+randomMove1[0], pixel.y+randomMove1[1])) { + //console.log((pixel.x+randomMove1[0]) + " " + (pixel.y+randomMove1[1])) + var newPixel = null; + if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + }; + if(outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1]) || !reactionStealer(pixel,newPixel,"radiation")) { + var randomMove2 = move2Spots[Math.floor(Math.random() * move2Spots.length)]; + if(!tryMove(pixel, pixel.x+randomMove2[0], pixel.y+randomMove2[1])) { + var newPixel = null; + if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + }; + if(newPixel !== null) { reactionStealer(pixel,newPixel,"radiation") }; + }; + }; + }; + doDefaults(pixel); + }, + reactions: { + //Spreading + "liquid_fire": { "elem2":"liquid_rad_fire", "chance":0.2 }, + "fire": { "elem2":"rad_fire", "chance":0.2 }, + "smoke": { "elem2":"rad_smoke", "chance":0.2 }, + /*"steam": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "rain_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "snow_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "hail_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "acid_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,12], "setting":"clouds" }, + "fire_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,12], "setting":"clouds" }, + "pyrocumulus": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" },*/ + //Radiation reactions added programatically + }, + temp: 134, + tempHigh: 595, + stateHigh: "rad_fire", + category: "gases", + state: "gas", + density: 1340, + stain: 0.075, + }; + elements.holy_fire = { + color: ["#FFFF96","#FFBF49","#CE743B"], //placeholder + tick: function(pixel) { + var moveResult = tryMoveAndReturnBlockingPixel(pixel,pixel.x + randomSign(),pixel.y - 1); + var blockingPixels = []; + var secondMoveResult = null; + if(typeof(moveResult) == "object" && !(elements[pixel.element].ignore.concat(pixel.element).includes(moveResult.element))) { + blockingPixels.push(moveResult) + }; + //if move1Result = true then nothing else happens + if(moveResult !== true) { + var coords = randomChoice([[-1,0],[0,1],[1,0]]).map(offsetPair => addArraysInPairs(offsetPair,[pixel.x,pixel.y])); + secondMoveResult = tryMoveAndReturnBlockingPixel(pixel,...coords); + if(typeof(secondMoveResult) == "object" && !(elements[pixel.element].ignore.concat(pixel.element).includes(secondMoveResult.element))) { + blockingPixels.push(secondMoveResult) + }; + }; + if(blockingPixels.length > 0) { + blockingPixels.forEach(function(pixel) { + var blessRxn = elements.bless.reactions[pixel.element]; + if(typeof(blessRxn) == "object") { + var elem2 = blessRxn.elem2; + if(elem2 !== null) { + while(Array.isArray(elem2)) { elem2 = randomChoice(elem2) }; + changePixel(pixel,elem2) + } + }; + var value = Math.random(); + if(value < 0.01) { + if(pixel.burnInto) { + finishBurn(pixel) + } else { + changePixel(pixel,randomChoice(["ash","ash","light"])) + } + } else if(value < 0.20) { + pixel.burning = true; + pixel.burnStart ??= pixelTicks; + } else if(value < 0.205) { + changePixel(pixel,randomChoice(["ash","ash","light"])) + } else { + pixel.temp += 10; pixelTempCheck(pixel) + }; + return + }) + }; + doDefaults(pixel); + }, + temp:6000, + tempLow:1000, + stateLow: ["bless","fire"], + burnInto: ["bless","fire"], + category: "energy", + burning: true, + burnTime: 500, + burnTempChange: 8, + fireElement: ["bless","plasma"], + ignore: ["ash","light","bless","plasma","fire","smoke","liquid_holy_fire"].concat(searchElements("haseulite")).concat("haseulite_singularity"), + state: "gas", + density: 0.08, + ignoreAir: true + }; + elements.holy_bomb = { + color: ["#dbb260", "#94591e"], + tick: function(pixel) { + if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds) + var newPixel = pixelMap[pixel.x][pixel.y-1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,13,"holy_fire","plasma",null,firebombFire); + }; + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case) + var newPixel = pixelMap[pixel.x][pixel.y+1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,13,"holy_fire","plasma",null,firebombFire); + }; + }; + if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case) + explodeAtPlus(pixel.x,pixel.y,13,"holy_fire","plasma",null,firebombFire); + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER + Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1); + }; + }, + category: "weapons", + state: "solid", + density: 4000, + excludeRandom: true, + desc: "A bomb that burns the world to pure ash.
To enable automatic bomb generation, set the generateBombs query parameter.", + }; + elements.bless.ignore ??= []; + elements.bless.ignore.push("holy_fire"); + elements.plasma_explosion = { + color: ["#c78fff","#ea8fff","#be8fff"], + tick: function(pixel) { + explodeAtPlus(pixel.x,pixel.y,10,"plasma","fire"); + return + }, + temp: 6500, + category: "energy", + state: "gas", + density: 1000, + excludeRandom: true, + noMix: true + }; + elements.god_slayer_fire = { + color: ["#FFBACE","#FC6DCA","#9954B0"], + tick: function(pixel) { + var moveResult = tryMoveAndReturnBlockingPixel(pixel,pixel.x + randomSign(),pixel.y - 1); + var blockingPixels = []; + var secondMoveResult = null; + if(typeof(moveResult) == "object" && !(elements[pixel.element].ignore.concat(pixel.element).includes(moveResult.element))) { + blockingPixels.push(moveResult) + }; + //if move1Result = true then nothing else happens + if(moveResult !== true) { + var coords = randomChoice([[-1,0],[0,1],[1,0]]).map(offsetPair => addArraysInPairs(offsetPair,[pixel.x,pixel.y])); + secondMoveResult = tryMoveAndReturnBlockingPixel(pixel,...coords); + if(typeof(secondMoveResult) == "object" && !(elements[pixel.element].ignore.concat(pixel.element).includes(secondMoveResult.element))) { + blockingPixels.push(secondMoveResult) + }; + }; + if(blockingPixels.length > 0) { + blockingPixels.forEach(function(pixel) { + var value = Math.random(); + var randomDefaultResult = randomChoice(["ash","slag","plasma"]); + var oldTemp = pixel.temp; + if(value < 0.03) { + if(pixel.stateHigh) { + meltPixel(pixel); + } + } else if(value < 0.06) { + if(pixel.burnInto) { + finishBurn(pixel); + } else { + changePixel(pixel,randomDefaultResult) + } + } else if(value < 0.09) { + if(pixel.breakInto) { + breakPixel(pixel); + } else { + changePixel(pixel,randomDefaultResult) + } + } else if(value < 0.24) { + pixel.burning = true; + pixel.burnStart ??= pixelTicks; + } else if(value < 0.245) { + changePixel(pixel,randomDefaultResult) + } else { + pixel.temp += 25; pixelTempCheck(pixel) + }; + if(pixel) { + pixel.temp = Math.max(oldTemp,pixel.temp) + }; + return + }) + }; + doDefaults(pixel); + }, + temp:10000, + tempLow:1500, + stateLow: ["plasma_explosion","plasma","plasma","plasma","plasma","plasma","plasma","plasma"], + burnInto: ["plasma_explosion","plasma","plasma","plasma","plasma","plasma","plasma","plasma"], + category: "energy", + ignore: ["ash","slag","wall","plasma","fire","smoke","liquid_god_slayer_fire"].concat(searchElements("haseulite")).concat("haseulite_singularity"), + burning: true, + burnTime: 500, + burnTempChange: 10, + fireElement: ["plasma"], + state: "gas", + density: 0.07, + ignoreAir: true + }; + elements.liquid_holy_fire = { + color: ["#FFFF96","#FFBF49","#CE743B"], //placeholder + tick: function(pixel) { + var moveResult = tryMoveAndReturnBlockingPixel(pixel,pixel.x + randomIntegerBetweenTwoValues(-1,1),pixel.y + 1); + var blockingPixels = []; + var secondMoveResult = null; + if(typeof(moveResult) == "object" && !(elements[pixel.element].ignore.concat(pixel.element).includes(moveResult.element))) { + blockingPixels.push(moveResult) + }; + //if move1Result = true then nothing else happens + if(moveResult !== true) { + var coords = [randomSign(),0].map(offsetPair => addArraysInPairs(offsetPair,[pixel.x,pixel.y])); + secondMoveResult = tryMoveAndReturnBlockingPixel(pixel,...coords); + if(typeof(secondMoveResult) == "object" && !(elements[pixel.element].ignore.concat(pixel.element).includes(secondMoveResult.element))) { + blockingPixels.push(secondMoveResult) + }; + }; + if(blockingPixels.length > 0) { + blockingPixels.forEach(function(pixel) { + var blessRxn = elements.bless.reactions[pixel.element]; + if(typeof(blessRxn) == "object") { + var elem2 = blessRxn.elem2; + if(elem2 !== null) { + while(Array.isArray(elem2)) { elem2 = randomChoice(elem2) }; + changePixel(pixel,elem2) + } + }; + var value = Math.random(); + if(value < 0.01) { + if(pixel.burnInto) { + finishBurn(pixel) + } else { + changePixel(pixel,randomChoice(["ash","ash","light"])) + } + } else if(value < 0.20) { + pixel.burning = true; + pixel.burnStart ??= pixelTicks; + } else if(value < 0.205) { + changePixel(pixel,randomChoice(["ash","ash","light"])) + } else { + pixel.temp += 10; pixelTempCheck(pixel) + }; + return + }) + }; + doDefaults(pixel); + }, + temp:8000, + tempLow:2000, + stateLow: "holy_fire", + burnInto: ["bless","fire"], + category: "energy", + burning: true, + burnTime: 1000, + burnTempChange: 10, + fireElement: "holy_fire", + ignore: ["light","bless","wall","plasma","fire","smoke","holy_fire"].concat(searchElements("haseulite")).concat("haseulite_singularity"), + state: "liquid", + density: 270, + ignoreAir: true + }; + elements.liquid_god_slayer_fire = { + color: ["#FFBACE","#FC6DCA","#9954B0"], + tick: function(pixel) { + var moveResult = tryMoveAndReturnBlockingPixel(pixel,pixel.x + randomIntegerBetweenTwoValues(-1,1),pixel.y + 1); + var blockingPixels = []; + var secondMoveResult = null; + if(typeof(moveResult) == "object" && !(elements[pixel.element].ignore.concat(pixel.element).includes(moveResult.element))) { + blockingPixels.push(moveResult) + }; + //if move1Result = true then nothing else happens + if(moveResult !== true) { + var coords = [randomSign(),0].map(offsetPair => addArraysInPairs(offsetPair,[pixel.x,pixel.y])); + secondMoveResult = tryMoveAndReturnBlockingPixel(pixel,...coords); + if(typeof(secondMoveResult) == "object" && !(elements[pixel.element].ignore.concat(pixel.element).includes(secondMoveResult.element))) { + blockingPixels.push(secondMoveResult) + }; + }; + if(blockingPixels.length > 0) { + blockingPixels.forEach(function(pixel) { + var value = Math.random(); + var randomDefaultResult = randomChoice(["ash","slag","plasma"]); + var oldTemp = pixel.temp; + if(value < 0.03) { + if(pixel.stateHigh) { + meltPixel(pixel); + } + } else if(value < 0.06) { + if(pixel.burnInto) { + finishBurn(pixel); + } else { + changePixel(pixel,randomDefaultResult) + } + } else if(value < 0.09) { + if(pixel.breakInto) { + breakPixel(pixel); + } else { + changePixel(pixel,randomDefaultResult) + } + } else if(value < 0.24) { + pixel.burning = true; + pixel.burnStart ??= pixelTicks; + } else if(value < 0.245) { + changePixel(pixel,randomDefaultResult) + } else { + pixel.temp += 25; pixelTempCheck(pixel) + }; + if(pixel) { + pixel.temp = Math.max(oldTemp,pixel.temp) + }; + return + }) + }; + doDefaults(pixel); + }, + temp:15000, + tempLow:2000, + stateLow: ["plasma_explosion","liquid_plasma","liquid_plasma","liquid_plasma","liquid_plasma","liquid_plasma","liquid_plasma","liquid_plasma"], + burnInto: "god_slayer_fire", + category: "energy", + ignore: ["ash","slag","wall","plasma","fire","smoke","god_slayer_fire"].concat(searchElements("haseulite")).concat("haseulite_singularity"), + burning: true, + burnTime: 1000, + burnTempChange: 14, + fireElement: "god_slayer_fire", + state: "liquid", + density: 380, + ignoreAir: true + }; + elements.god_slayer_bomb = { + color: ["#a43dcc", "#49b6d1"], + tick: function(pixel) { + if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds) + var newPixel = pixelMap[pixel.x][pixel.y-1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,40,"god_slayer_fire","plasma"); + }; + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case) + var newPixel = pixelMap[pixel.x][pixel.y+1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,40,"god_slayer_fire","plasma"); + }; + }; + if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case) + explodeAtPlus(pixel.x,pixel.y,40,"god_slayer_fire","plasma"); + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER + Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1); + }; + }, + category: "weapons", + state: "solid", + density: 4500, + excludeRandom: true, + desc: "A bomb that makes gods tremble.
To enable automatic bomb generation, set the generateBombs query parameter.", + }; + elements.cloner.burnTime = Infinity; + elements.cloner.burnInto = "cloner"; + elements.cold_torch = { + "color": "#4394d6", + "behavior": [ + "XX|CR:cold_fire|XX", + "XX|XX|XX", + "XX|XX|XX" + ], + "reactions": { + "water": { "elem1": "wood" }, + "sugar_water": { "elem1": "wood" }, + "salt_water": { "elem1": "wood" }, + "seltzer": { "elem1": "wood" }, + "dirty_water": { "elem1": "wood" }, + "pool_water": { "elem1": "wood" }, + "steam": { "elem1": "wood" }, + "smog": { "elem1": "wood" }, + "rain_cloud": { "elem1": "wood" }, + "cloud": { "elem1": "wood" }, + "snow_cloud": { "elem1": "wood" }, + "hail_cloud": { "elem1": "wood" }, + "black_damp": { "elem1": "wood" } + }, + "temp": -200, + "category": "special", + "breakInto": "sawdust", + "tempHigh": 600, + "stateHigh": "wood", + }; + var GTDR = { "elem1": "torch", chance: 0.01 }; //grand torch degradation reaction + elements.grand_torch = { + "color": "#FFBF2F", + "tick": function(pixel) { + var coords = circleCoords(pixel.x,pixel.y,2).concat([[-1,-2],[1,-2],[-1,2],[1,2],[-2,-1],[2,-1],[-2,1],[2,1],[-1,-3],[0,-3],[1,-3],[0,-4],[0,-5]].map( + function(offsets) { + return {x: offsets[0]+pixel.x, y: offsets[1]+pixel.y} + } + )); + for(var i = 0; i < coords.length; i++) { + var coordPair = coords[i]; + if(outOfBounds(coordPair.x,coordPair.y)) { continue }; + if(coordPair.x == pixel.x && coordPair.y == pixel.y) { continue }; + if(!(isEmpty(coordPair.x,coordPair.y))) { continue }; + var newPixel = tryCreatePixelReturn("fire",coordPair.x,coordPair.y); + if(typeof(newPixel) == "object") { + newPixel.temp = pixel.temp + } + }; + }, + "reactions": { + "water": GTDR, + "sugar_water": GTDR, + "salt_water": GTDR, + "seltzer": GTDR, + "dirty_water": GTDR, + "pool_water": GTDR, + "steam": GTDR, + "smog": GTDR, + "rain_cloud": GTDR, + "cloud": GTDR, + "snow_cloud": GTDR, + "hail_cloud": GTDR, + "black_damp": { "elem1": "wood", chance: 0.02 }, + "magic": { "elem1": ["grand_torch","grand_torch","grand_plasma_torch"], "elem2": null, changeTemp: true } + }, + "temp": 1500, + "category": "special", + "breakInto": "charcoal", + "tempHigh": 6000, + "stateHigh": "grand_plasma_torch", + "tempLow": 1000, + "stateLow": "torch", + }; + elements.torch.reactions ??= {}; + elements.torch.reactions.magic = { elem1: ["torch","torch","grand_torch"], elem2: null, changeTemp: true }; + elements.torch.tempHigh = 1500; + elements.torch.stateHigh = "grand_torch"; + var PTDR = { "elem1": "wood", chance: 0.003 }; + elements.plasma_torch = { + "color": "#86579c", + "tick": function(pixel) { + var rotation = -(((pixel.r ?? 0) % 4) + 1); //preserving the original behavior of 0 = up, 1 = left + var rotationInRadians = scale(rotation,0,4,0,Math.PI * 2); + var vector = [Math.cos(rotationInRadians), Math.sin(rotationInRadians)]; + //base strength (distance) 2, plus 1 for each 500*C above 7K*C; when charged, increase by 10% for each full or partial unit of charge (0.25 yields 10%, 1 yields 10%, 1.55 yields 20%, 2 yields 20%...) + var strength = Math.max(2,Math.min(300, ((2 + Math.floor((pixel.temp - 7000) / 500)) * (1 + (Math.ceil(pixel.charge ?? 0) * 0.1))))); //bound to 2-300, in part for performance reasons + //console.log(strength); + for(var i = 1; i <= strength; i++) { + var newOffsets = vector.map(coord => Math.round(coord * i)); + var finalPos = {x: pixel.x + newOffsets[0], y: pixel.y + newOffsets[1]}; + //console.log({x:pixel.x,y:pixel.y},finalPos); + if(!(isEmpty(finalPos.x,finalPos.y))) { + if(pixelMap[finalPos.x]?.[finalPos.y] && pixelMap[finalPos.x][finalPos.y].element == "plasma") { + continue + } else { + break + } + }; + var newPlasma = tryCreatePixelReturn("plasma",finalPos.x,finalPos.y); + if(!newPlasma) { + break + } else { + newPlasma.temp = pixel.temp + } + } + }, + "reactions": { + "water": PTDR, + "sugar_water": PTDR, + "salt_water": PTDR, + "seltzer": PTDR, + "dirty_water": PTDR, + "pool_water": PTDR, + "steam": PTDR, + "smog": PTDR, + "rain_cloud": PTDR, + "cloud": PTDR, + "snow_cloud": PTDR, + "hail_cloud": PTDR, + "black_damp": { "elem1": "wood", change: 0.02 } + }, + "temp": 7000, + "category": "special", + "breakInto": "charcoal", + "tempLow": 4999, + "stateLow": "grand_torch", + }; + var GrPTDR = { "elem1": "plasma_torch", chance: 0.001 }; //grand plasma torch degradation reaction + elements.grand_plasma_torch = { + "color": "#b92eff", + "tick": function(pixel) { + var coords = circleCoords(pixel.x,pixel.y,4).concat([[4,4],[4,4],[3,3],[3,3],[1,1],[-1,-1],[-3,-3],[-4,-4],[-4,-4],[-3,-3],[-1,-1],[1,1],[0,0],[0,0],[0,0],[0,0],[1,1],[1,1],[2,2],[-1,-1],[-1,-1],[-2,-2]].map( //ae = filterCurrentPixels(function(pixel) { return pixel.element == "ivory_growth_crystal" }).map(px => [130-px.x,67-px.y]) + function(offsets) { + return {x: offsets[0]+pixel.x, y: offsets[1]+pixel.y} + } + )); + for(var i = 0; i < coords.length; i++) { + var coordPair = coords[i]; + if(outOfBounds(coordPair.x,coordPair.y)) { continue }; + if(coordPair.x == pixel.x && coordPair.y == pixel.y) { continue }; + if(!(isEmpty(coordPair.x,coordPair.y))) { continue }; + var newPixel = tryCreatePixelReturn("plasma",coordPair.x,coordPair.y); + if(typeof(newPixel) == "object") { + newPixel.temp = pixel.temp + } + }; + }, + "reactions": { + "water": GrPTDR, + "sugar_water": GrPTDR, + "salt_water": GrPTDR, + "seltzer": GrPTDR, + "dirty_water": GrPTDR, + "pool_water": GrPTDR, + "steam": GrPTDR, + "smog": GrPTDR, + "rain_cloud": GrPTDR, + "cloud": GrPTDR, + "snow_cloud": GrPTDR, + "hail_cloud": GrPTDR, + "black_damp": { "elem1": "wood", chance: 0.02 }, + }, + "temp": 8500, + "category": "special", + "breakInto": "charcoal", + "tempLow": 6000, + "stateLow": "plasma_torch", + }; + elements.rad_torch = { + "color": "#85d643", + "behavior": [ + "XX|CR:rad_fire|XX", + "XX|XX|XX", + "XX|XX|XX" + ], + "reactions": { + "water": { "elem1": "wood" }, + "sugar_water": { "elem1": "wood" }, + "salt_water": { "elem1": "wood" }, + "seltzer": { "elem1": "wood" }, + "dirty_water": { "elem1": "wood" }, + "pool_water": { "elem1": "wood" }, + "steam": { "elem1": "wood" }, + "smog": { "elem1": "wood" }, + "rain_cloud": { "elem1": "wood" }, + "cloud": { "elem1": "wood" }, + "snow_cloud": { "elem1": "wood" }, + "hail_cloud": { "elem1": "wood" }, + "black_damp": { "elem1": "wood" } + }, + "temp": 800, + "category": "special", + "breakInto": "sawdust", + "tempLow": -273, + "stateHigh": "wood", + }; + elements.mystic_torch = { + "color": ["#8e27ba", "#b3297e"], + "behavior": [ + "XX|CR:mystic_fire|XX", + "XX|XX|XX", + "XX|XX|XX" + ], + "reactions": { + "water": { "elem2": "steam" }, + "sugar_water": { "elem2": "steam" }, + "salt_water": { "elem1": "salt" }, + "seltzer": { "elem1": "steam" }, + "dirty_water": { "elem1": "steam" }, + "pool_water": { "elem1": "steam" }, + "smog": { "elem1": "smoke" }, + "rain_cloud": { "elem1": "cloud" }, + "snow_cloud": { "elem1": "cloud" }, + "hail_cloud": { "elem1": "cloud" }, + "black_damp": { "elem1": "wood" } + }, + "temp": 20000, + "category": "special", + "breakInto": ["molten_ash","carbon_dioxide","charcoal","electric","magic"], + "tempLow": 8000, + "stateLow": new Array(99).fill("mystic_torch").concat("plasma_torch"), //1 in 100 chance to cool to a plasma torch, to mitigate inexplicable explosive cooling's tendency to cause the torch to spontaneously "downgrade" + "hardness": 0.999 + }; + elements.mystic_fire.state = "gas"; + elements.napalm = { + color: "#e0873e", + behavior: [ + "XX|SA%40 AND ST|XX", + "M2%10 AND SA%40 AND ST|XX|M2%10 AND SA%40 AND ST", + "M2%50 AND M1%10|M1 AND SA%40 AND ST|M2%50 AND M1%10" + ], + category: "weapons", + state: "liquid", + viscosity: 1000, + density: 1200, //google was f***ing useless and i'm not searching that again, so arbitrary 1.2 it is + burnTempChange: 3, + burn: 300, + burnTime: 500, + temp: airTemp, + }; + elements.hypernapalm = { + name: "h y p e r n a p a l m", //HYPERNAPALM + color: "#bd34eb", + behavior: [ + "XX|SA%40 AND ST|XX", + "M2%10 AND SA%40 AND ST|XX|M2%10 AND SA%40 AND ST", + "M2%50 AND M1%10|M1 AND SA%40 AND ST|M2%50 AND M1%10" + ], + category: "weapons", + state: "liquid", + viscosity: 1000, + density: 1200, + fireElement: "plasma", + fireSpawnChance: 33, + fireSpawnTemp: 7200, + burnTempChange: 30, + burn: 300, + burnTime: 500, + }; + elements.cold_napalm = { + color: "#3e87e0", + behavior: [ + "XX|SA%40 AND ST|XX", + "M2%10 AND SA%40 AND ST|XX|M2%10 AND SA%40 AND ST", + "M2%50 AND M1%10|M1 AND SA%40 AND ST|M2%50 AND M1%10" + ], + category: "weapons", + state: "liquid", + viscosity: 1000, + density: 1200, + burn: 300, + burnTime: 500, + fireElement: "cold_fire", + burnTempChange: -1, + burnInto: "cold_fire", + }; + elements.rad_napalm = { + color: "#cdf760", + behavior: [ + "XX|SA%40 AND ST AND CR:radiation%1|XX", + "M2%10 AND SA%40 AND ST AND CR:radiation%1|HT%2.5|M2%10 AND SA%40 AND ST AND CR:radiation%1", + "M2%50 AND M1%10|M1 AND SA%40 AND ST AND CR:radiation%1|M2%50 AND M1%10" + ], + category: "weapons", + state: "liquid", + viscosity: 1000, + density: 1300, + burnTempChange: 2, + burn: 300, + burnTime: 500, + fireElement: "rad_fire", + temp: airTemp, + burnInto: "rad_fire", + }; + runAfterLoad(function() { + if(eLists.spout) { + eLists.spout.push("cold_torch"); + eLists.spout.push("rad_torch"); + }; + elements.liquid_fire = { + color: ["#ff6b21","#ffa600","#ff4000"], + behavior: [ + "XX|M2|XX", + "M2|XX|M2", + "M1|M1|M1", + ], + reactions: { + "water": { "elem1": "liquid_smoke" }, + }, + temp:600, + tempLow:100, + stateLow: "liquid_smoke", + tempHigh: 7000, + stateHigh: "liquid_plasma", + category: "energy liquids", + burning: true, + burnTime: Infinity, + burnTempChange: 2, + fireSpawnChance: 5, + state: "liquid", + density: 200, + }; + elements.liquid_cold_fire = { + color: ["#21cbff","#006aff","#00ffff"], + behavior: [ + "XX|M2|XX", + "M2|XX|M2", + "M1|M1|M1", + ], + reactions: { + "fire": { "elem1": "liquid_smoke", "elem2": "liquid_smoke" }, + "plasma": { "elem1": "le_liquid_light", "elem2": "le_liquid_light" }, //prefixed to avoid conflict with F&M liquid_light + }, + temp:-200, + tempHigh:0, + stateHigh: "liquid_smoke", + burning: true, + burnTempChange: -2, + fireSpawnChance: 5, + burnTime: Infinity, + fireElement: "cold_fire", + category: "energy liquids", + state: "liquid", + density: 420, + }; + elements.liquid_rad_fire = { + color: ["#daff21","#a6ff00","#ffff00"], + behavior: [ + "XX|CR:radiation%0.1|XX", + "CR:radiation%0.1|XX|CR:radiation%0.1", + "XX|CR:radiation%0.1|XX", + ], + tick: function(pixel) { + if(Math.random() < 0.4) { + pixel.temp++; + }; + if(Math.random() < 0.06) { //6%/t to radify + if(typeof(transformAdjacent) === "function" && typeof(radioactiveObject) === "object") { + transformAdjacent(pixel,radioactiveObject); + }; + }; + var move1Spots = [[-1,1],[0,1],[1,1]]; + var move2Spots = [[-1,0],[0,-1],[1,0]]; + var randomMove1 = move1Spots[Math.floor(Math.random() * move1Spots.length)]; + if(!tryMove(pixel, pixel.x+randomMove1[0], pixel.y+randomMove1[1])) { + //console.log((pixel.x+randomMove1[0]) + " " + (pixel.y+randomMove1[1])) + var newPixel = null; + if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + }; + if(outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1]) || !reactionStealer(pixel,newPixel,"radiation")) { + var randomMove2 = move2Spots[Math.floor(Math.random() * move2Spots.length)]; + if(!tryMove(pixel, pixel.x+randomMove2[0], pixel.y+randomMove2[1])) { + var newPixel = null; + if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + }; + if(newPixel !== null) { reactionStealer(pixel,newPixel,"radiation") }; + }; + }; + }; + doDefaults(pixel); + }, + reactions: { //fire + radiation reacts + //Merged water-radiation reactions, plus altered seltzer + "water": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "steam": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "carbon_dioxide": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "dirty_water": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "salt_water": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "sugar_water": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + "seltzer": { "elem1": "rad_smoke", "elem2":"rad_steam", "chance":0.4 }, + //Radiation reactions added programatically + }, + temp:800, + //tempLow:100, + //stateLow: "liquid_smoke", + //tempHigh: 7000, + //stateHigh: "liquid_plasma", + category: "energy liquids", + burning: true, + burnTime: Infinity, + burnTempChange: 3, + fireSpawnChance: 5, + fireElement: "rad_fire", + state: "liquid", + density: 210, + }; + elements.radiation.reactions.liquid_fire = { "elem2":"liquid_rad_fire", "chance":0.4 }; + elements.radiation.reactions.fire = { "elem2":"rad_fire", "chance":0.4 }; + elements.radiation.reactions.smoke = { "elem2":"rad_smoke", "chance":0.4 }; + runAfterLoad(function() { + for(key in elements.radiation.reactions) { + var value = elements.radiation.reactions[key]; + if(typeof(elements.rad_fire.reactions[key]) === "undefined") { + elements.rad_fire.reactions[key] = value; + }; + }; + }); + elements.unrealistically_flammable_gas.burnTempChange = 10; + elements.unrealistically_flammable_gas.fireElement = "plasma"; + elements.unrealistically_flammable_powder.burnTempChange = 20; + elements.unrealistically_flammable_powder.fireElement = "plasma"; + elements.burning_unrealistically_flammable_gas.burnTempChange = 15; + elements.burning_unrealistically_flammable_gas.fireElement = "plasma"; + elements.burning_unrealistically_flammable_powder.burnTempChange = 30; + elements.burning_unrealistically_flammable_powder.fireElement = "plasma"; + }); + //CONFIGURABLE MAXIMUM COLOR OFFSET (maxColorOffset) ## + defaultColorOffset = 15; + pixelColorPick = function(pixel,customColor=null,maxOffset=null,dontForceColorsToNulls=false) { + var element = pixel.element; + var elementInfo = elements[element]; + //if (elementInfo.behavior instanceof Array) { + if (pixel.charge && elementInfo.colorOn) { + customColor = elementInfo.colorOn; + } + if (customColor !== null) { + if (Array.isArray(customColor)) { + customColor = customColor[Math.floor(Math.random() * customColor.length)]; + } else if (customColor.startsWith?.("#")) { + customColor = hexToRGB(customColor); + } + var rgb = customColor; + } + else { + var rgb = elements[element].colorObject; // {r, g, b} + // If rgb is an array, choose a random item + while(Array.isArray(rgb)) { + rgb = rgb[Math.floor(Math.random() * rgb.length)]; + } + } + // Randomly darken or lighten the RGB color + //try maxOffset parameter, then info maxColorOffset, then default 15 + var offsetAmount; + if(maxOffset !== null) { + offsetAmount = maxOffset; + } else { + offsetAmount = elementInfo?.maxColorOffset ?? defaultColorOffset; + }; + if(typeof(rgb) !== "object") { rgb = convertColorFormats(rgb,"json") }; //somehow rgb can be a hex triplet even though it's pulled from the f*cking JSON color object and there's no logical way that that should be able to happen + var maxColorOffset = Math.floor(Math.random() * (Math.random() > 0.5 ? -1 : 1) * Math.random() * offsetAmount); + var maxRepickTries = 10; + var repickTries = 0; + var rgbWasNull = (rgb === null); + if(rgb == null || (typeof(rgb?.r) !== "number") || (typeof(rgb?.g) !== "number") || (typeof(rgb?.b) !== "number")) { + //console.log(pixel.element,pixel.color,"\n",rgb,customColor,Array.isArray(elementInfo.colorObject) ? elementInfo.colorObject.indexOf(rgb) : elementInfo.colorObject); + //this SHOULDN'T be necessary but SOMEHOW IT IS + while(rgb == null && repickTries < maxRepickTries) { + var color_also_fxck_you = elementInfo.colorObject; + while(Array.isArray(color_also_fxck_you)) { color_also_fxck_you = randomChoice(color_also_fxck_you) }; + rgb = color_also_fxck_you + }; + //console.log(pixel.element,pixel.color,rgb,customColor); + }; + var r = rgb.r + maxColorOffset; + var g = rgb.g + maxColorOffset; + var b = rgb.b + maxColorOffset; + // Make sure the color is within the RGB range + r = Math.max(0, Math.min(255, r)); + g = Math.max(0, Math.min(255, g)); + b = Math.max(0, Math.min(255, b)); + var color = "rgb("+r+","+g+","+b+")"; + /*} + else { + var color = elementInfo.color; + if (Array.isArray(color)) { + color = color[Math.floor(Math.random() * color.length)]; + } + }*/ + if((!dontForceColorsToNulls) && rgbWasNull && rgb !== null) { + pixel.color = convertColorFormats(rgb,"rgb") + }; + return color; + } + //FIND MODE AND PIXEL PROPERTIES LINKED TO SPECIAL CODE (acid_and_shapes.js functionality removed) ## + var style = document.createElement('style'); + style.type = 'text/css'; + style.id = 'findStatusStylesheet'; + style.innerHTML = '.findStatus { color: #E11; text-decoration: none; }'; + document.getElementsByTagName('head')[0].appendChild(style); + find = false; + findElement = "sand"; + findColorPulseTimer = 0; + findColorPulseTimerSubTimer = 0; + function marasi(number) { + return Math.min(255,Math.round(Math.abs(Math.sin(number) * 255))); + }; + function updateFindDescription() { + var elems = findElement; + if(elems instanceof Array) { + elems = elems.join(", "); + }; + elements.find_toggle.desc = `I'm running out of keybinds +If this text is green or underlined, find mode is on. Currently finding: ${elems} (this display does not update automatically). +Click here to toggle find mode. This highlights the currently selected element.
+Click here to configure the find filter.`; + }; + function toggleFind() { + if(find != true) { + find = true; + document.getElementById("findStatusStylesheet").innerHTML = '.findStatus { color: #1E1; text-decoration: underline; }'; //Displayed info doen't update until it's pulled up again, so I'm using CSS to dynamically change the color of an element. + } else { + find = false; + document.getElementById("findStatusStylesheet").innerHTML = '.findStatus { color: #E11; text-decoration: none; }'; + }; + updateFindDescription(); + }; + elements.find_toggle = { + color: [_cc.b.h, _cc.b.h, _cc.b.h, _cc.b.h, "#ff0000", "#ff0000", "#ff0000", "#ff0000"], + name: "find toggle (look at info)", + behavior: behaviors.SELFDELETE, + category: "special", + excludeRandom: true, + desc: ` +I'm running out of keybinds +If this text is green or underlined, find mode is on. Currently finding: sand (this display does not update automatically). +Click here to toggle find mode. This highlights the currently selected element.
+Click here to configure the find filter.`, + }; + function findFilterPrompt() { + var preElement = prompt("Enter the elements you want to highlight\nSeparate multiple elements with commas"); + if(preElement === null || preElement === "") { + return false; + }; + if(preElement.includes(",")) { + preElement = preElement.split(","); + findElement = preElement; + updateFindDescription(); + return findElement; + }; + findElement = preElement; + updateFindDescription(); + return findElement; + }; + var incrementt = 0; + var interval = setInterval( increment, 500/30); + function increment(){ + incrementt = incrementt % (Math.PI*8.8) + (Math.PI/30); + } + shapeModes = ["normal","circles","triangles"]; + function getShapeMode() { + return shapeModes[settings.shapeMode] ?? "normal"; + }; + specialProperties = { + /*red: { + specialFunction: function(pixel) { pixel.color = "rgb(255,0,0)" } + },*/ + }; + acidFunctions = { + sin: Math.sin, + tan: Math.tan, + none: function(number) { return number } + }; + var tickBehaviorStringCache = { + POWDER: behaviors.POWDER.toString(), + LIQUID: behaviors.LIQUID.toString(), + UL_UR_OPTIMIZED: behaviors.UL_UR_OPTIMIZED.toString() + }; + settings ??= {}; + settings.shockoverlay ??= true; + viewInfo[1] = { // Normal View (Default) + name: "", + effects: true, + colorEffects: true, + pixel: function(pixel,ctx) { + var a = (settings.textures !== 0) ? pixel.alpha : undefined; + if (((elements[pixel.element].isGas && elements[pixel.element].glow !== false) || elements[pixel.element].glow || pixel.glow) && pixel.glow !== false) { + drawPlus(ctx,pixel.color,pixel.x,pixel.y,undefined,a) + // if (isEmpty(pixel.x+1,pixel.y) || isEmpty(pixel.x-1,pixel.y) || isEmpty(pixel.x,pixel.y+1) || isEmpty(pixel.x,pixel.y-1)) {} + } + else { + drawSquare(ctx,pixel.color,pixel.x,pixel.y,undefined,a) + } + if (settings.shockoverlay && pixel.charge && view !== 2) { // Yellow glow on charge + if (!elements[pixel.element].colorOn) { + drawSquare(ctx,"rgba(255,255,0,0.5)",pixel.x,pixel.y); + } + } + if (settings.burnoverlay && pixel.burning && view !== 2) { // Red glow on burn + if (!elements[pixel.element].burningColor) { + drawSquare(ctx,"rgba(255,0,0,0.5)",pixel.x,pixel.y); + } + } + } + }; + function drawCursor() { + var layerCtx = canvasLayers.gui.getContext('2d'); + var mouseOffset = Math.trunc(mouseSize/2); + var topLeft = [mousePos.x-mouseOffset,mousePos.y-mouseOffset]; + var bottomRight = [mousePos.x+mouseOffset,mousePos.y+mouseOffset]; + if(mouseSize % 2 == 0) { + bottomRight[0]--; + bottomRight[1]--; + }; + // Draw a square around the mouse + layerCtx.strokeStyle = "white"; + layerCtx.strokeRect(topLeft[0]*pixelSize,topLeft[1]*pixelSize,(bottomRight[0]-topLeft[0]+1)*pixelSize,(bottomRight[1]-topLeft[1]+1)*pixelSize); + // draw one transparent pixel in the center + if (settings.precision) { + layerCtx.fillStyle = "rgba(255,255,255,0.5)"; + layerCtx.fillRect(mousePos.x*pixelSize,mousePos.y*pixelSize,pixelSize,pixelSize); + } + if (shaping) { + if (shaping === 1) { // Draw a white line from shapeStart.x to shapeStart.y + let coords = lineCoords(shapeStart.x,shapeStart.y,mousePos.x,mousePos.y); + coords.forEach((coord) => { + if (outOfBounds(coord[0],coord[1])) return; + drawSquare(layerCtx,"rgba(255,255,255,0.5)",coord[0],coord[1],undefined) + }) + } + } + } + + //I hate overwriting vanilla rendering functions + function drawSquare(ctx,color,x,y,scale=1,opacity=1) { + if (color) { ctx.fillStyle = color; } + if (ctx.globalAlpha !== opacity) { ctx.globalAlpha = opacity; } + ctx.fillRect(canvasCoord(x), canvasCoord(y), pixelSize*scale, pixelSize*scale); + } + function drawLayers(includeBackground) { + if (ctx === null) return + clearLayers(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + findColorPulseTimerSubTimer++; + if(findColorPulseTimerSubTimer >= 2) { + findColorPulseTimer++; + findColorPulseTimerSubTimer = 0; + }; + if (settings.bg) { // Set background color + if(settings["bg"] instanceof Array) { + settings.bgAngle ??= 90; + var colors = settings.bg; + var angleDeg = settings.bgAngle; + var angleRad = (angleDeg) * Math.PI / 180; + if (canvas.style.backgroundColor !== colors) { + canvas.style.backgroundColor = `linear-gradient(${angleDeg}deg, ${colors.join(", ")})`; + } + if (includeBackground) { + ctx.fillStyle = ctx.createLinearGradient( + 0, + 0, + canvas.width * Math.cos(angleRad) + 0, + canvas.height * Math.sin(angleRad) + ); + for(i = 0; i < colors.length; i++) { + var color = colors[i]; + var position = i / (colors.length - 1); + ctx.fillStyle.addColorStop(position,color); + }; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + } else { + if (canvas.style.backgroundColor !== settings.bg) { + canvas.style.backgroundColor = settings.bg; + } + if (includeBackground) { + ctx.fillStyle = settings.bg; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + }; + } + for (let i = 0; i < canvasLayersPre.length; i++) { + const layerCanvas = canvasLayersPre[i]; + ctx.drawImage(layerCanvas, 0, 0); + } + if (paused || pixelTicks !== lastPixelDraw) { + if (!drawPixels() && !paused) { + logMessage("One or more of your mods are outdated and cannot render properly."); + togglePause(); + } + lastPixelDraw = pixelTicks; + } + drawCursor(); + for (let i = 0; i < canvasLayersPost.length; i++) { + const layerCanvas = canvasLayersPost[i]; + ctx.drawImage(layerCanvas, 0, 0); + } + } + canvasLayers.pressure = document.createElement("canvas"); + canvasLayersPre.push(canvasLayers.pressure); + function drawPressure() { + var layerCtx = canvasLayers.pressure.getContext('2d'); + if(!(settings.dopressure && settings.drawpressure)) { + return + } else { + 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: + layerCtx.fillStyle = `rgb(0,0,0)`; + break + case 1: + layerCtx.fillStyle = `rgb(255,0,0)`; + break + case -1: + layerCtx.fillStyle = `rgb(0,0,255)`; + break + default: // covers NaN, since NaN != NaN + layerCtx.fillStyle = `rgb(127,127,127)`; + break + }; + layerCtx.globalAlpha = isNaN(pressureSign) ? 0.5 : pressureValue; + layerCtx.fillRect(x*pixelSize*pressureCellSize, y*pixelSize*pressureCellSize, pixelSize*pressureCellSize, pixelSize*pressureCellSize) + } else { + layerCtx.fillStyle = `rgb(127,127,127)`; + layerCtx.globalAlpha = isNaN(pressureSign) ? 0.5 : pressureValue; + layerCtx.fillRect(x*pixelSize*pressureCellSize, y*pixelSize*pressureCellSize, pixelSize*pressureCellSize, pixelSize*pressureCellSize) + } + } + layerCtx.globalAlpha = 1; + } + } + }; + + //I hate overwriting drawPixels + tempScale = 1; + tempScaleOffset = 20; + + normalColorFunction = function(pixel) { + var colorOut = pixel.color; + for(var sry4thelag in specialProperties) { + if(pixel[sry4thelag] !== undefined && specialProperties[sry4thelag].specialColorFunction) { + colorOut = specialProperties[sry4thelag].specialColorFunction(pixel,oldColor=colorOut) + } + } + return colorOut; + } + + /*5: function(pixel) { // velocity view + var data = elements[pixel.element]; + var vx = pixel.vx ?? 0; + var vy = pixel.vy ?? 0; + / * + var pseudoVelocity = 0; + var coordsToCheck; + var behaviorCoordsToCheck; + if(Array.isArray(data.behavior)) { + switch((pixel.r ?? 0) % 4) { + default: + case 0: + coordsToCheck = [0,1]; + behaviorCoordsToCheckOffset = [2,1]; + break; + case 1: + coordsToCheck = [-1,0]; + behaviorCoordsToCheckOffset = [1,0]; + break; + case 2: + coordsToCheck = [0,-1]; + behaviorCoordsToCheckOffset = [0,1]; + break; + case 3: + coordsToCheck = [1,0]; + behaviorCoordsToCheckOffset = [1,2]; + break; + }; + if(data.behavior[behaviorCoordsToCheckOffset[0]][behaviorCoordsToCheckOffset[1]] == "M1") { + if(isEmpty(pixel.x+coordsToCheck[0],pixel.y+coordsToCheck[1])) { + pseudoVelocity = 1; + } else { + if(!(isEmpty(pixel.x+behaviorCoordsToCheckOffset[0],pixel.y+behaviorCoordsToCheckOffset[1],true))) { + newPixel = pixelMap[pixel.x+behaviorCoordsToCheckOffset[0]][pixel.y+behaviorCoordsToCheckOffset[1]]; + newData = elements[newPixel.element]; + if(newData.id !== data.id && typeof(data.density) === "number" && typeof(newData.density) === "number") { + var chance = (data.density - newData.density)/(data.density + newData.density); + pseudoVelocity = chance + } + } + } + }; + if(pseudoVelocity) { + switch((pixel.r ?? 0) % 4) { + default: + case 0: + vy += pseudoVelocity; + break; + case 1: + vx -= pseudoVelocity; + break; + case 2: + vy -= pseudoVelocity; + break; + case 3: + vx += pseudoVelocity; + break; + } + }; + } else { + if(data.tick && [behaviors.POWDER,behaviors.LIQUID].includes(data.tick)) { + pseudoVelocity = 1; + } else if(data.tick == behaviors.UL_UR_OPTIMIZED) { + pseudoVelocity = -1; + } else if(pixel.element == "hail") { + pseudoVelocity = 2; + }; + vy += pseudoVelocity; + }; + * / + if(vx === 0 && vy === 0) { + return "rgb(15,15,15)" + } else { + var magnitude = Math.sqrt ((vx ** 2) + (vy ** 2)); + var direction = Math.atan2(pixel.vy ?? 0,pixel.vx ?? 0)*180/Math.PI; + if(direction < 0) { direction = scale(direction,-180,0,360,180) }; + hue = direction; + sat = 100; + lig = bound(scale(magnitude,0,100,10,100),0,100); + return "hsl("+hue+","+sat+"%,"+lig+"%)"; + } + }, + 6: function(pixel) { + var data = elements[pixel.element] ?? elements.unknown; + var originalColor = data.colorObject; + if(Array.isArray(originalColor)) { + originalColor = randomChoice(originalColor) + }; + return convertColorFormats(originalColor,"rgb"); + } + };*/ + + console.log("1/8 loaded") //True 1.8 is inside one viewColorFunctions[2] + + hiding = false + + function setView(n) { + if (!viewInfo[n]) { // reset view + view = 1; + } + else { + view = n; + } + } + didNewSettings = false; + runAfterLoad(function() { + viewInfo[2] = { // Thermal View + name: "thermal", + pixel: function(pixel,ctx) { + var temp = pixel.temp; + if (temp < -273.15) {temp = -273.15} + //if (temp > 165000) {temp = 165000} + // piecewise scale, linear from 250 (-273.15 degrees) to 225 (-50 degrees) and logarithmic from 225 (-50 degrees) to -90 (=275) (165,000 degrees) + var hue; + var sat = 100; + var lig = 50; + var extraHueDistance = 0; + if(temp < -50) { + hue = scale(-75,-273.15,-50,250,225); + } else if(temp <= 165000) { + hue = Math.round(225 - (Math.log(temp+50)/Math.log(6000+50))*225); + } + if(temp > 165000) { extraHueDistance = Math.abs(hue + 85) }; + hue %= 360; + if (hue > 250) {hue = 250} + if (hue < 0) {hue += 360} + if (temp > 6050 && hue < 275) {hue = 275} + if (temp > 165000 && temp < 1105000) { + sat -= (extraHueDistance * 2); if(sat < 0) { sat == 0 } + lig += extraHueDistance; if(lig > 100) { lig == 100 } + }; + if (temp > 1105000 && temp < 6305000) { hue = 120; sat = Math.round((extraHueDistance - 50) * (20/9)); lig = 100 - ((extraHueDistance - 50) * 16/9) } //painbow award contender + if (temp > 6305000 && temp < 3300000305000) { hue = (120 + (extraHueDistance - 95)) % 360; sat = 100; lig = 20 } + if (temp >= 3300000305000) { hue = 100; sat = 100; lig = 20 } //this scale is a painbow award contender + drawSquare(ctx,"hsl("+hue+","+sat+"%,"+lig+"%)",pixel.x,pixel.y) + } + }; + + if(didNewSettings) { return }; + //Setting + var settingsMenu = document.getElementById("settingsMenu").getElementsByClassName("menuText")[0]; + var settingNodes = [...settingsMenu.childNodes].filter(function(node) { return node.nodeType == 1 }); + var lastSetting = settingNodes[settingNodes.length - 1]; + //console.log(lastSetting); + //console.log(lastSetting.getAttribute("style")); + lastSetting.removeAttribute("style"); //restore padding for worldgen setting; + //console.log(lastSetting.getAttribute("style")); + //Shape setting + // var shapeSettingSpan = document.createElement("span"); + // shapeSettingSpan.setAttribute("setting","shapeMode"); + // shapeSettingSpan.setAttribute("class","setting-span"); + // shapeSettingSpan.textContent = "Pixel Shape (laggy) "; + // var settingDropdown = document.createElement("select"); + // settingDropdown.setAttribute("onchange","settings.shapeMode = parseInt(this.value); saveSettings();"); + // var options = { + // 0: "Squares", + // 1: "Circles", + // 2: "Triangles" + // }; + // for(value in options) { + // var newOption = document.createElement("option"); + // if(value == "0") { + // newOption.setAttribute("selected",""); + // }; + // newOption.setAttribute("value",value); + // newOption.innerText = options[value]; + // settingDropdown.appendChild(newOption); + // }; + // shapeSettingSpan.appendChild(settingDropdown); + // settingsMenu.appendChild(shapeSettingSpan); + //Acid function setting + // var acidFuncSpan = document.createElement("span"); + // acidFuncSpan.setAttribute("setting","acidFunction"); + // acidFuncSpan.setAttribute("class","setting-span"); + // acidFuncSpan.textContent = "\"Acid\" distortion function"; + // var settingDropdown = document.createElement("select"); + // settingDropdown.setAttribute("onchange","settings.acidFunction = this.value; saveSettings();"); + // var options = { + // "none": "None", + // "sin": "Sine", + // "tan": "Tangent" + // }; + // for(value in options) { + // var newOption = document.createElement("option"); + // if(value == "0") { + // console.log("selected",value); + // newOption.setAttribute("selected","") + // }; + // newOption.setAttribute("value",value); + // newOption.innerText = options[value]; + // settingDropdown.appendChild(newOption); + // }; + // acidFuncSpan.appendChild(settingDropdown); + // settingsMenu.appendChild(acidFuncSpan); + //Handle the styling of the last setting + settingNodes = [...settingsMenu.childNodes].filter(function(node) { return node.nodeType == 1 }); + lastSetting = settingNodes[settingNodes.length - 1]; + //console.log(lastSetting); + lastSetting.setAttribute("style","padding-bottom:0"); //remove padding from last setting; + settings ??= {}; + settings.burnoverlay ??= false; + var redBurnSettingSpan = document.createElement("span"); + redBurnSettingSpan.setAttribute("setting","burnoverlay"); + redBurnSettingSpan.setAttribute("title","Default: OFF"); + redBurnSettingSpan.classList.add("setting-span","multisetting"); + var settingInput = document.createElement("input"); + settingInput.setAttribute("type","button"); + settingInput.setAttribute("value","Burning Overlay"); + settingInput.setAttribute("state","0"); + settingInput.classList.add("toggleInput"); + settingInput.setAttribute("onclick","toggleInput(this,'burnoverlay',false)"); + var options = { + "false": "Disabled", + "true": "Enabled" + }; + var newHelpMark = document.createElement("span"); + newHelpMark.setAttribute("title","This adds a red overlay to burning pixels, like the yellow overlay for charged pixels."); + newHelpMark.classList.add("helpMark"); + newHelpMark.innerText = "?"; + redBurnSettingSpan.appendChild(settingInput); + redBurnSettingSpan.appendChild(newHelpMark); + var cheerfulSetting = document.querySelector('span[setting="cheerful"]'); + cheerfulSetting.after(redBurnSettingSpan); + settings.shockoverlay ??= true; + var yellowShockSettingSpan = document.createElement("span"); + yellowShockSettingSpan.setAttribute("setting","shockoverlay"); + yellowShockSettingSpan.setAttribute("title","Default: ON"); + yellowShockSettingSpan.classList.add("setting-span","multisetting"); + var settingInput = document.createElement("input"); + settingInput.setAttribute("type","button"); + settingInput.setAttribute("value","Charge Overlay"); + settingInput.setAttribute("state","1"); + settingInput.classList.add("toggleInput"); + settingInput.setAttribute("onclick","toggleInput(this,'shockoverlay',false)"); + var options = { + "false": "Disabled", + "true": "Enabled" + }; + var newHelpMark = document.createElement("span"); + newHelpMark.setAttribute("title","This adds a yellow overlay for charged pixels (vanilla feature)."); + newHelpMark.classList.add("helpMark"); + newHelpMark.innerText = "?"; + yellowShockSettingSpan.appendChild(settingInput); + yellowShockSettingSpan.appendChild(newHelpMark); + redBurnSettingSpan.after(yellowShockSettingSpan); + // settings.doacid ??= false; + // var acidSettingSpan = document.createElement("span"); + // acidSettingSpan.setAttribute("setting","doacid"); + // acidSettingSpan.setAttribute("title","Default: OFF"); + // acidSettingSpan.classList.add("setting-span","multisetting"); + // var settingInput = document.createElement("input"); + // settingInput.setAttribute("type","button"); + // settingInput.setAttribute("value",'"Acid" distortion'); + // settingInput.setAttribute("state","0"); + // settingInput.classList.add("toggleInput"); + // settingInput.setAttribute("onclick","toggleInput(this,'doacid',false)"); + // var options = { + // "false": "Disabled", + // "true": "Enabled" + // }; + // var newHelpMark = document.createElement("span"); + // newHelpMark.setAttribute("title","This adds a yellow overlay for charged pixels (vanilla feature)."); + // newHelpMark.classList.add("helpMark"); + // newHelpMark.innerText = "?"; + // acidSettingSpan.appendChild(settingInput); + // acidSettingSpan.appendChild(newHelpMark); + // yellowShockSettingSpan.after(acidSettingSpan); + var rankineSettingSpan = document.createElement("span"); + rankineSettingSpan.setAttribute("setting","userankine"); + rankineSettingSpan.setAttribute("title","Default: OFF"); + rankineSettingSpan.classList.add("setting-span","multisetting"); + var settingInput = document.createElement("input"); + settingInput.setAttribute("type","button"); + settingInput.setAttribute("value",'Use degrees Rankine'); + settingInput.setAttribute("state","0"); + settingInput.classList.add("toggleInput"); + settingInput.setAttribute("onclick","toggleInput(this,'userankine',false)"); + var options = { + "false": "Disabled", + "true": "Enabled" + }; + var newHelpMark = document.createElement("span"); + newHelpMark.setAttribute("title","Use degrees Rankine (Fahrenheit based around absolute zero) for temperature display. Only affects imperial units."); + newHelpMark.classList.add("helpMark"); + newHelpMark.innerText = "?"; + rankineSettingSpan.appendChild(settingInput); + rankineSettingSpan.appendChild(newHelpMark); + // acidSettingSpan.after(rankineSettingSpan); + yellowShockSettingSpan.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' }"); + var tinyOption = document.createElement("option"); + tinyOption.setAttribute("value","12"); + tinyOption.innerText = "Tiny"; + sizeDropdown.insertAdjacentElement("afterbegin",tinyOption); + var hugeOption = document.createElement("option"); + hugeOption.setAttribute("value","2"); + hugeOption.innerText = "Huge"; + sizeDropdown.insertAdjacentElement("beforeend",hugeOption); + var otherOption = document.createElement("option"); + otherOption.setAttribute("value","null"); + otherOption.innerText = "Other"; + sizeDropdown.insertAdjacentElement("beforeend",otherOption); + pixelSizeSettingDropdownOtherOptionIndex = (sizeDropdown.children.length) - 1; + //Append code to showSettings to set every setting toggle button's state according to its setting's value on page load (so if you turned the setting on, the button is already green when you load the page) + function showSettingsButtonAutoUpdateAppendFunction() { + var toggleButtonSettings = document.querySelectorAll('span.setting-span.multisetting[setting]:has(input[type="button"])'); + if(!(toggleButtonSettings?.forEach)) { return false }; + toggleButtonSettings.forEach(function(node) { + var setting = node.getAttribute("setting"); + if(!setting) { return false }; + var button = node.querySelector('input[type="button"]'); + if(!button) { return false }; + var settingValue = !!(settings?.[setting]); + var newState = settingValue ? "1" : "0"; + button.setAttribute("state",newState); + return true + }) + }; + oldShowSettings = eval(showSettings.toString()) //eval may be evil, but JS seems to have literally changed the way it works so functions are passed as references now instead of cloning, and so appending won't work; + showSettings = function() { + oldShowSettings(); + showSettingsButtonAutoUpdateAppendFunction() + }; + everyTick(function() { + if(paused) { return }; + for(var propName in specialProperties) { + //thanks, I hate not being able to pass arguments to filter functions + var _filterFunction = function(pixel) { + return pixel.hasOwnProperty(propName) + }; + var pixelsWithProp = currentPixels.filter(_filterFunction); + //console.log(pixelsWithProp.map(p => [p.x,p.y,propName,p[propName]].join(",")).join("\n")); + for(var i in pixelsWithProp) { + var propPixel = pixelsWithProp[i]; + if(!propPixel || propPixel.del) { continue }; + specialProperties[propName]?.specialFunction?.(propPixel); + }; + } + }) + didNewSettings = true + }); + //FUNCTION EXECUTION WHEN A PIXEL TRIES TO MOVE INTO ANOTHER (onTryMoveInto) ## + elements.on_try_move_into_test = { + color: _cc.w.h, + properties: { + ticks: 0, + attemptedMovesIntoPixel: 0 + }, + behavior: behaviors.POWDER, + reactions: { + "dirt": { elem1: "diamond" } + }, + state: "solid", + hidden: true, + excludeRandom: true, + category: "special", + density: 1000, + tick: function(pixel) { + pixel.ticks++; + }, + onTryMoveInto: function(pixel,otherPixel) { + pixel.attemptedMovesIntoPixel++; + var otherElement = otherPixel.element; + if(otherElement === "ash") { + console.log(`This is a test of potentially undesired multiplicate running. (tick: ${pixelTicks}, move attempts: ${pixel.attemptedMovesIntoPixel})`); + //if(deletePixel(pixel.x,pixel.y)) { + // console.log("This pixel has been deleted."); + //}; + }; + }, + desc: "Try burying this pixel and see what happens. (Use Debug)\n\nonTryMoveInto is run as part of tryMove, before reactions, while tick functions are run as part of pixelDraw.\nIn some circumstances, such as a pixel being buried under a pile of anything that isn't a sturdy powder, this function may run multiple times per tick. For example, bury this pixel in ash and look in the console.\n\nTo use this function, include in your element definition the \"onTryMoveInto\" key with a function value, similarly to tick functions. This function takes two arguments; \"otherPixel\" is the pixel that is trying to move and \"pixel\" is the pixel whose position otherPixel is trying to move into.", + related: ["debug", "ash"], + } + function tryMove(pixel,nx,ny,leaveBehind,force) { + if(!pixel) { return false }; + if ((typeof(pixel.tempDrag) !== "undefined") && (!force)) { + if(typeof(pixel.tempDrag) === "number" && pixel.tempDrag >= pixelTicks) { + return true + } else { + delete pixel.tempDrag + } + }; + if (pixel.drag && !force) { return true; } + var info = elements[pixel.element]; + var oob = outOfBounds(nx,ny); + if(pixelMap[nx] === undefined) { return false }; //safety + if (isEmpty(nx,ny,false,oob)) { // If coords is empty, move to coords + //console.log(`Moving ${pixel.element} (${pixel.x},${pixel.y}) to (${nx},${ny})`); + movePixel(pixel,nx,ny,leaveBehind); + return true; + } + else if (!oob) { + //console.log(`Moving ${pixel.element} (${pixel.x},${pixel.y}) to (${nx},${ny})`); + // Reactions + newPixel = pixelMap[nx][ny]; + var newInfo = elements[newPixel.element]; + if(typeof(newInfo) == "undefined") { return false }; + var returnVal = false; + if(newInfo.onTryMoveInto !== undefined) { + newInfo.onTryMoveInto(newPixel,pixel); + if(!pixel || pixel.del) { + return "deleted"; + }; + returnVal = false; + }; + var rr1 = false; + if (info.reactions !== undefined && info.reactions[newPixel.element] !== undefined) { + rr1 = reactPixels(pixel,newPixel) + if (rr1) { + return true; + } + } + if (!rr1 && elements[newPixel.element].reactions !== undefined && elements[newPixel.element].reactions[pixel.element] !== undefined && !elements[newPixel.element].reactions[pixel.element].oneway) { + if (reactPixels(newPixel,pixel)) { + return true; + } + } + // Density + if (elements[pixel.element].id !== elements[newPixel.element].id) { + if (info.density !== undefined && elements[newPixel.element].density !== undefined) { + // if the pixel's state + ">" + newPixel's state is in validDensitySwaps, and the pixel's density is larger than the newPixel's density, swap the pixels + if (validDensitySwaps[info.state][elements[newPixel.element].state] && info.density >= elements[newPixel.element].density) { + // chance depending on the difference in density + if (Math.random() < (info.density - elements[newPixel.element].density)/(info.density + elements[newPixel.element].density)) { + swapPixels(pixel,newPixel); + return true; + } + } + } + } + if(returnVal) { + return true; + } + } + return false; + } + //PIXEL MOVER TOOLS ## + elements.move_up = { + color: "#1C0000", + tool: function(pixel) { + for(var i = 0; i < (shiftDown ? 3 : 1); i++) { tryMove(pixel,pixel.x,pixel.y-1,null,true) }; + }, + category: "movement tools", + excludeRandom: true, + }, + elements.move_down = { + color: "#000038", + tool: function(pixel) { + for(var i = 0; i < (shiftDown ? 3 : 1); i++) { tryMove(pixel,pixel.x,pixel.y+1,null,true) }; + }, + category: "movement tools", + excludeRandom: true, + }, + elements.move_left = { + color: "#007000", + tool: function(pixel) { + for(var i = 0; i < (shiftDown ? 3 : 1); i++) { tryMove(pixel,pixel.x-1,pixel.y,null,true) }; + }, + category: "movement tools", + excludeRandom: true, + }, + elements.move_right = { + color: "#000E00", + tool: function(pixel) { + for(var i = 0; i < (shiftDown ? 3 : 1); i++) { tryMove(pixel,pixel.x+1,pixel.y,null,true) }; + }, + category: "movement tools", + excludeRandom: true, + }, + elements.move_up_left = { + color: "#E00000", + tool: function(pixel) { + for(var i = 0; i < (shiftDown ? 3 : 1); i++) { tryMove(pixel,pixel.x-1,pixel.y-1,null,true) }; + }, + category: "movement tools", + excludeRandom: true, + }, + elements.move_down_left = { + color: "#0001C0", + tool: function(pixel) { + for(var i = 0; i < (shiftDown ? 3 : 1); i++) { tryMove(pixel,pixel.x-1,pixel.y+1,null,true) }; + }, + category: "movement tools", + excludeRandom: true, + }, + elements.move_up_right = { + color: "#038000", + tool: function(pixel) { + for(var i = 0; i < (shiftDown ? 3 : 1); i++) { tryMove(pixel,pixel.x+1,pixel.y-1,null,true) }; + }, + category: "movement tools", + excludeRandom: true, + }, + elements.move_down_right = { + color: "#000007", + tool: function(pixel) { + for(var i = 0; i < (shiftDown ? 3 : 1); i++) { tryMove(pixel,pixel.x+1,pixel.y+1,null,true) }; + }, + category: "movement tools", + excludeRandom: true, + } + //TOOL THAT DELETES EVERY ELEMENT OF THE CLICKED TYPE(S) ## + elements.delete_all_of_element = { + name: "delete all of element", + color: ["#a7a7a7", "#a7a7a7", "#a7a7a7", "#a7a7a7", _cc.b.h, _cc.b.h, _cc.b.h, _cc.b.h], + tool: function(pixel) { + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + if (!isEmpty(i,j)) { + if(pixelMap[i][j].element == pixel.element) { + deletePixel(i,j) + } + } + } + } + }, + category: "tools", + excludeRandom: true, + }; + //TEMPERATURE TOOLS ## + //base syntax by sightnado + /*elements.warm = { + color: "#7fff7f", + tool: function(pixel) { + pixel.temp = 20; + pixelTempCheck(pixel) + }, + category: "tools", + };*/ + //warm is redundant due to vanilla room_temp + elements.room_temp.category = "tools"; + elements.slow_cool = { + color: elements.cook.color.map(colorCode => Object.values(convertColorFormats(colorCode,"json"))).map(x => RGBToHex(x.map(y => 255 - y))), + tool: function(pixel) { + pixel.temp -= (0.5 * (1 + shiftDown)); + }, + category: "energy", + excludeRandom: true + }; + elements.ultraheat = { + color: ["#ff0000", "#ffbf4f", "#ff0000", "#ffbf4f", "#ff0000", "#ffbf4f"], + tool: function(pixel) { + if(shiftDown) { pixel.temp += (350 * (1 + shiftDown)) } else { pixel.temp += 350 } + pixelTempCheck(pixel) + }, + category: "tools", + }; + elements.ultracool = { + color: ["#0000ff", "#4fbfff", "#0000ff", "#4fbfff", "#0000ff", "#4fbfff"], + tool: function(pixel) { + if(shiftDown) { pixel.temp -= (350 * (1 + shiftDown)) } else { pixel.temp -= 350 } + pixelTempCheck(pixel) + }, + category: "tools", + }; + elements.nan_temp = { + name: "NaN temp", + color: [_cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff"], + tool: function(pixel) { + pixel.temp = NaN; + pixelTempCheck(pixel) + }, + category: "cursed tools", + }; + elements.inf_temp = { + color: ["#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h, "#ff0000", _cc.w.h], + tool: function(pixel) { + pixel.temp = Infinity; + pixelTempCheck(pixel) + }, + category: "cursed tools", + }; + //OTHER TOOLS ## + elements.burn = { + color: ["#FF6B21", "#FFA600", "#FF4000"], + tool: function(pixel) { + pixel.burnStart = pixelTicks; + pixel.burning = true; + }, + category: "tools", + excludeRandom: true, + }; + elements.cursed_shock = { + color: ["#ffff00", "#00ff00", "#ffff00", "#00ff00", "#ffff00", "#00ff00", "#ffff00", "#00ff00"], + tool: function(pixel) { + if(pixel.chargeCD) { + delete pixel.chargeCD; + }; + if (!pixel.charge) { + pixel.charge = 1; + if (elements[pixel.element].colorOn) { + pixel.color = pixelColorPick(pixel); + }; + }; + }, + category: "cursed tools", + excludeRandom: true, + }; + //TROLL PIXELS ## + elements.troll_deleter = { + color: "#eeeeee", + tick: function(pixel) { + var target = randomChoice(currentPixels); + if(target && !(isEmpty(target.x,target.y))) { + deletePixel(target.x,target.y) + } + }, + category: "troll machines", + insulate: true, + state: "solid", + excludeRandom: true, + desc: "Deletes random pixels" + }, + elements.troll_hider = { + color: "#eeeeee", + tick: function(pixel) { + var target = randomChoice(currentPixels); + target.color = _cc.b.r + }, + category: "troll machines", + insulate: true, + state: "solid", + excludeRandom: true, + desc: "Paints random pixels black" + }, + elements.troll_swapper = { + color: "#eeeeee", + tick: function(pixel) { + var target = randomChoice(currentPixels); + if(target && !(isEmpty(target.x,target.y))) { + swapPixels(pixel,target) + } + }, + category: "troll machines", + insulate: true, + state: "solid", + excludeRandom: true, + desc: "Swaps with random pixels" + }, + elements.troll_exploder = { + color: "#eeeeee", + tick: function(pixel) { + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + var eeaa = (Math.floor(Math.random()*5))-2 + if(Math.random() < 0.00007) { explodeAt(i,j,9+eeaa) } + if(j == height) { j == 1 } + } + if(i == height) { i == 1 } + } + }, + category: "troll machines", + insulate: true, + hardness: 1.0, + state: "solid", + excludeRandom: true, + desc: "Causes random explosions" + }, + elements.offset_fourth_y = { + color: [_cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff"], + tool: function(pixel) { + tryMove(pixel,pixel.x,pixel.y+0.25); + pixelTempCheck(pixel) + }, + category: "cursed tools", + }, + elements.offset_half_y = { + color: [_cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff"], + tool: function(pixel) { + tryMove(pixel,pixel.x,pixel.y+0.5); + pixelTempCheck(pixel) + }, + category: "cursed tools", + }, + elements.offset_three_fourth_y = { + color: [_cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff", _cc.b.h, "#ff00ff"], + tool: function(pixel) { + tryMove(pixel,pixel.x,pixel.y+0.75); + pixelTempCheck(pixel) + }, + category: "cursed tools", + }, + elements.troll_rotator = { + color: "#eeeeee", + tick: function(pixel) { + var target = randomChoice(currentPixels); + if(target && !(isEmpty(target.x,target.y))) { + target.r ??= 0; + target.r += 1; target.r %= 4; + } + }, + category: "troll machines", + insulate: true, + state: "solid", + excludeRandom: true, + desc: "Randomly rotates pixels" + } + elements.troll_time_bender = { + color: "#eeeeee", + tick: function(pixel) { + if(pixel.temp < -273) { + pixel.temp = -273; + }; + if(isNaN(pixel.temp)) { + pixel.temp = -1; + }; + pixel.bemp = Math.floor(pixel.temp); + if(pixel.bemp > 273) { + pixel.bemp = 273; + }; + if(pixel.temp >= 4000) { + pixelTicks = -1; + pixel.temp = 4000; + } else { + pixelTicks += pixel.bemp; + }; + }, + category: "troll machines", + insulate: true, + state: "solid", + excludeRandom: true, + temp: -1, + desc: "Advances the simulation timer (not time travel) by its temperature" + }, + elements.troll_thermoderegulator = { + color: "#eeeeee", + tick: function(pixel) { + var offsetRange = pixel.temp + 273.15; + var target = randomChoice(currentPixels); + if(target && !(isEmpty(target.x,target.y)) && target.element !== pixel.element) { + target.temp += (Math.floor(Math.random() * offsetRange + 1) - (offsetRange/2)) + } + }, + category: "troll machines", + insulate: true, + temp: -272.15, + state: "solid", + excludeRandom: true, + desc: "Randomly heats and cools random pixels" + } + //TRIGGERABLE RANDOM-TOOL POWDERS ## + if(typeof(randomChoices) == "undefined") {randomChoices = []}; //this is generated after mod load, but JS will probably die if I don't have it defined in the code + elements.heat_random = { + name: "heat-randomized powder", + color: ["#4e5f8a","#b334ec","#fa96f9","#b6ecf6","#80ebc8","#e9286b","#8eed91","#b18b30"], //"random"'s colors plus 0x100000 + behavior: behaviors.POWDER, + tick: function(pixel) { + if(pixel.temp >= 50) { + changePixel(pixel,randomChoice(randomChoices),true); + }; + }, + state: "solid", + category: "special", + density: 1987, //average density of all vanilla elements: 1987.2842952763815 + temp: 20, + desc: "Turns to a random element when heated to 50°C", + }; + elements.cold_random = { + name: "cold-randomized powder", + color: ["#3e5f9a","#a334fc","#e490ff","#9de3ff","#70ebd8","#d9287b","#7eeda1","#a18b40"], //"random"'s colors plus 0x000010 except where the last byte was above 0xef, where substraction was done to the first two bytes to compensate + behavior: behaviors.POWDER, + tick: function(pixel) { + if(pixel.temp <= -50) { + changePixel(pixel,randomChoice(randomChoices),true); + }; + }, + state: "solid", + density: 1987, //average density of all vanilla elements: 1987.2842952763815 + temp: 20, + category: "special", + desc: "Turns to a random element when cooled to -50°C", + }; + elements.shock_random = { + name: "shock-randomized powder", + color: ["#4e6f8a","#b344ec","#faa6f9","#b6fcf6","#80fbc8","#e9386b","#8efd91","#b19b30"], //"random"'s colors plus 0x101000 + behavior: behaviors.POWDER, + tick: function(pixel) { + if(pixel.charge) { + delete pixel.charge; + pixel.chargeCD = 1; + changePixel(pixel,randomChoice(randomChoices),true); + }; + }, + conduct: 1, + state: "solid", + density: 1987, //average density of all vanilla elements: 1987.2842952763815 + temp: 20, + category: "special", + desc: "Turns to a random element when shocked", + }; + //ALKAHEST ## + alkahestBlacklist = ["alkahest","alkahest_fairy","wall","alkahest_spout"] + elements.alkahest = { + color: "#33eeee", + behavior: behaviors.LIQUID_OLD, + state: "liquid", + category: "liquids", + density: 3308, + hardness: 1, + tick: function(pixel) { + for(i = 0; i < adjacentCoords.length; i++) { + if(Math.random() < 0.1) { + var pX = pixel.x + var pY = pixel.y + var oX = adjacentCoords[i][0]; + var oY = adjacentCoords[i][1]; + var checkPosX = pX+oX; + var checkPosY = pY+oY; + if(!isEmpty(checkPosX,checkPosY,true)) { + var newPixel = pixelMap[checkPosX][checkPosY]; + var newElement = newPixel.element; + if(!alkahestBlacklist.includes(newElement)) { //unless someone's willing to implement dragon parts + if(typeof(pixel[newElement]) === "undefined") { + pixel[newElement] = 0; + }; + pixel[newElement]++; + deletePixel(checkPosX,checkPosY); + continue + }; + }; + }; + }; + }, + }; + //LIQUID ENERGY ELEMENTS ## + elements.liquid_plasma = { + color: ["#8800ff","#b184d9","#8800ff"], + behavior: [ + "XX|XX|XX", + "M2|DL%0.1|M2", + "M1|M1|M1", + ], + behaviorOn: [ + "XX|CL%5|XX", + "CL%5 AND M2|XX|CL%5 AND M2", + "M1|M1 AND CL%5|M1", + ], + temp:7065, + tempLow:5000, + stateLow: "liquid_fire", + category: "energy liquids", + state: "liquid", + density: 70, + charge: 0.5, + conduct: 1, + }, + elements.liquid_fire = { + color: ["#ff6b21","#ffa600","#ff4000"], + behavior: [ + "XX|M2|XX", + "M2|XX|M2", + "M1|M1|M1", + ], + reactions: { + "water": { "elem1": "liquid_smoke" } + }, + temp:600, + tempLow:100, + stateLow: "liquid_smoke", + tempHigh: 7000, + stateHigh: "liquid_plasma", + category: "energy liquids", + burning: true, + burnTime: 500, + burnInto: "liquid_smoke", + state: "liquid", + density: 21, + }, + elements.liquid_smoke = { + color: "#383838", + behavior: [ + "XX|XX|XX", + "M2|DL%0.1|M2", + "M1|M1|M1", + ], + reactions: { + "water": { "elem1": "dirty_water", "elem2": null }, + "steam": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] }, + "rain_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] }, + "snow_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] }, + "acid_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,15] }, + "fire_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,15] }, + "pyrocumulus": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,15] } + }, + temp: 114, + tempHigh: 605, + stateHigh: "liquid_fire", + category: "energy liquids", + state: "liquid", + density: 2180, + }, + elements.liquid_cold_fire = { + color: ["#21cbff","#006aff","#00ffff"], + behavior: [ + "XX|M2|XX", + "M2|CH:liquid_smoke%0.1|M2", + "M1|M1|M1", + ], + reactions: { + "fire": { "elem1": "liquid_smoke", "elem2": "liquid_smoke" }, + "plasma": { "elem1": "le_liquid_light", "elem2": "le_liquid_light" }, //prefixed to avoid conflict with F&M liquid_light + }, + temp:-200, + tempHigh:0, + stateHigh: "liquid_smoke", + category: "energy liquids", + state: "liquid", + density: 42, + }, + elements.le_liquid_light = { + color: "#ffffa8", + behavior: [ + "XX|XX|XX", + "M2|DL%0.1 AND RT%0.5|M2 AND BO", + "M1|M1|M1", + ], + temp: 40, + category: "energy liquids", + }, + elements.liquid_laser = { + color: "#ff0000", + behavior: [ + "XX|M2|XX", + "M2|DL%0.05 AND RT%0.5|M2 AND BO:1,2,3", + "XX|M1|XX", + ], + temp: 40, + category: "energy liquids", + }, + elements.liquid_electric = { + color: "#dddd00", + behavior: [ + "CL%3|CL%3 AND SH|CL%3", + "M2%15 AND CL%3 AND SH|SH%3 AND DL%15|M2%15 AND CL%3 AND SH", + "M1%15 AND CL%4|M1%50 AND CL%9 AND SH|M1%15 AND CL%4", + ], + charge: 3, + category: "energy liquids", + state: "solid", + density: 44.1, + }, + elements.liquid_radiation = { + color: ["#00ff00","#6fff00"], + behavior: [ + "XX|M2%50 AND HT|XX", + "M2%50 AND HT|DL%0.2|M2%50 AND HT", + "M1%50|M1 AND HT|M1%50", + ], + reactions: { + "water": { "elem2":"rad_steam", "chance":0.8 }, + "steam": { "elem2":"rad_steam", "chance":0.8 }, + "salt_water": { "elem2":"rad_steam", "chance":0.8 }, + "sugar_water": { "elem2":"rad_steam", "chance":0.8 }, + "dirty_water": { "elem2":"rad_steam", "chance":0.8 }, + "bubble": { "elem2":"rad_steam", "chance":0.8 }, + "foam": { "elem2":"rad_steam", "chance":0.8 }, + "ice": { "elem2":"rad_steam", "chance":0.8 }, + "snow": { "elem2":"rad_steam", "chance":0.8 }, + "packed_snow": { "elem2":"rad_steam", "chance":0.8 }, + "slime": { "elem2":"rad_steam", "chance":0.8 }, + "milk": { "elem2":"cheese", "chance":0.8 }, + "permafrost": { "elem1":"rad_steam", "elem2":"dirt", "chance":0.8 }, + "mud": { "elem1":"rad_steam", "elem2":"dirt", "chance":0.8 }, + "wet_sand": { "elem1":"rad_steam", "elem2":"sand", "chance":0.8 }, + "clay": { "elem1":"rad_steam", "elem2":"clay_soil", "chance":0.8 }, + "slaked_lime": { "elem1":"rad_steam", "elem2":"limestone", "chance":0.8 }, + "rain_cloud": { "elem2":"rad_cloud", "chance":0.8 }, + "snow_cloud": { "elem2":"rad_cloud", "chance":0.8 }, + "plant": { "elem2":"straw", "chance":0.8 }, + "grass": { "elem2":["straw","grass_seed","wheat_seed"], "chance":0.8 }, + "algae": { "elem2":["mushroom_spore","lichen","yeast"], "chance":0.8 }, + "mushroom_spore": { "elem2":["lichen","yeast"], "chance":0.8 }, + "mushroom_cap": { "elem2":["lichen","plant"], "chance":0.8 }, + "mushroom_stalk": { "elem2":["lichen","yeast"], "chance":0.8 }, + "mushroom_gill": { "elem2":["lichen","yeast"], "chance":0.8 }, + "flea": { "elem2":["ash","ant","termite"], "chance":0.8 }, + "ant": { "elem2":["ash","flea","termite"], "chance":0.8 }, + "termite": { "elem2":["ash","flea","ant"], "chance":0.8 }, + "fly": { "elem2":["ash","firefly","bee"], "chance":0.8 }, + "bee": { "elem2":["ash","firefly","fly"], "chance":0.8 }, + "firefly": { "elem2":["ash","bee","fly"], "chance":0.8 }, + "frog": { "elem2":["ash","meat","rotten_meat","cooked_meat"], "chance":0.8 }, + "fish": { "elem2":["ash","meat","rotten_meat","cooked_meat"], "chance":0.8 }, + "rat": { "elem2":["ash","meat","rotten_meat","cooked_meat","plague"], "chance":0.8 }, + "bone": { "elem2":["calcium","calcium","calcium","cancer"], "chance":0.8 }, + "meat": { "elem2":["ash","rotten_meat","cooked_meat"], "chance":0.8 }, + "rotten_meat": { "elem2":["ash","meat","cooked_meat"], "chance":0.8 }, + "cooked_meat": { "elem2":["ash","rotten_meat"], "chance":0.8 }, + "bamboo": { "elem2":["wood","plant","bamboo_plant"], "chance":0.8 }, + "bamboo_plant": { "elem2":["wood","plant","bamboo"], "chance":0.8 }, + "sapling": { "elem2":["wood","plant","tree_branch"], "chance":0.8 }, + "tree_branch": { "elem2":["wood","plant","sapling"], "chance":0.8 }, + "grass_seed": { "elem2":["straw","wheat_seed"], "chance":0.8 }, + "lichen": { "elem2":"algae", "chance":0.8 }, + "yeast": { "elem2":["algae","mushroom_spore","lichen"], "chance":0.8 }, + "wheat_seed": { "elem2":["straw","wheat","grass_seed"], "chance":0.8 }, + "flower_seed": { "elem2":["straw","grass","pistil","petal"], "chance":0.8 }, + "pistil": { "elem2":["straw","grass","flower_seed","petal"], "chance":0.8 }, + "petal": { "elem2":["straw","grass","flower_seed","pistil"], "chance":0.8 }, + "vine": { "elem1":["vine"], "chance":0.8 }, + "worm": { "elem2":"ash", "chance":0.8 }, + "corn": { "elem2":"corn_seed", "chance":0.8 }, + "corn_seed": { "elem2":"corn", "chance":0.8 }, + "potato": { "elem2":"potato_seed", "chance":0.8 }, + "potato_seed": { "elem2":"potato", "chance":0.8 }, + "slug": { "elem2":"slime", "chance":0.8 }, + "snail": { "elem2":"slime", "chance":0.8 }, + "cell": { "elem2":"cancer", "chance":0.8 }, + "blood": { "elem2":["infection","cancer"], "chance":0.8 }, + "antibody": { "elem2":"cancer", "chance":0.8 }, + "infection": { "elem2":"cancer", "chance":0.8 }, + "cancer": { "elem2":null, "chance":0.2 }, + }, + state: "liquid", + density: 4.2, + category: "energy liquids", + }, + elements.liquid_explosion = { + color: ["#ffb48f","#ffd991","#ffad91"], + behavior: [ + "XX|XX|XX", + "M2|EX:10>fire,fire,fire,liquid_explosion,liquid_explosion%0.4 AND DL%0.3|M2", + "M1|M1|M1", + ], + temp: 300, + category: "energy liquids", + state: "liquid", + density: 2000, + excludeRandom: true, + } + elements.everfire_liquid = { + "name": "everfire liquid", + "color": "#06142b", + "state": "liquid", + "behavior": behaviors.LIQUID, + "density": 1290, + "burn": 100, + "burnTime": 2000, + "burnInto": "extinguished_everfire_liquid", + "category": "energy liquids", + "fireColor": ["#0041a8","#8ab7ff"], + }, + elements.extinguished_everfire_liquid = { + "name": "extinguished everfire liquid", + "color": "#242d3b", + "state": "liquid", + "behavior": behaviors.LIQUID, + "density": 1290, + "fireColor": ["#0041a8","#8ab7ff"], + "category": "energy liquids", + "hidden": true, + }, + elements.liquid_magic = { + "name": "liquid magic", + "color": ["#a270ff","#f2d9ff"], + "state": "liquid", + "behavior": [ + "M2%50|M2%50|M2%50", + "M2|DL%0.2|M2", + "M1|M1|M1", + ], + "density": 21, + "category": "energy liquids", + "reactions": elements.magic.reactions, + }, + elements.liquid_mystic_fire = { + "name": "liquid mystic fire", + "color": ["#5454ff","#2020d4","#5800c4"], + "behavior": [ + "M2%50|M2%50 AND CR:liquid_mystic_fire%10 AND CR:mystic_fire%5|M2%50", + "M2 AND CR:liquid_mystic_fire%5|EX:15>liquid_mystic_fire%0.1|M2 AND CR:liquid_mystic_fire%5", + "M1|M1|M1", + ], + "temp":8500, + "tempChange":-100, + "tempLow":8000, + "stateLow": "liquid_fire", + "category": "energy liquids", + "burning": true, + }, + //concoction and essence are already liquid + elements.liquid_frostbomb = { + color: "#72dfed", + behavior: [ + "XX|XX|XX", + "M2|EX:15>frostwind,frostwind,frostwind,liquid_frostbomb%0.4 AND DL%0.2|M2", + "M1|M1|M1", + ], + temp: 300, + category: "energy liquids", + state: "liquid", + density: 2000, + excludeRandom: true, + } + //LIQUID VOID ## + elements.liquid_void = { + color: "#262626", + behavior: [ + "XX|DL|XX", + "DL AND M2|XX|DL AND M2", + "M1|DL AND M1|M1", + ], + ignore: ["liquid_void", "void", "wall", "cloner", "ecloner", "slow_cloner", "clone_powder", "floating_cloner", "clone_liquid", "liquid_cloner", "fire_cloner", "antigravity_powder_cloner", "floating_cloner_spout", "clone_liquid_spout", "liquid_cloner_spout", "fire_cloner_spout", "converter", "liquid_void_spout"], + /*The hardcoded array of cloners is used because I don't know how to detect them. + Generation code: + elementArray = Object.keys(elements); + for (let i = 0; i < elementArray.length; i++) { + var elementName = elementArray[i]; + if(elementName.indexOf("lone") !== -1) { + console.log(elementName); + }; + }; + */ + category: "liquids", + state: "liquid", + density: 6969, + excludeRandom: true, + } + if(!elements.void.ignore) { + elements.void.ignore = []; + }; + elements.void.ignore.push("liquid_void"); + //LIQUID CLONER ## + elements.clone_liquid = { + color: "#f0f000", + behavior: [ + "XX|CF|XX", + "CF AND M2|XX|CF AND M2", + "M1|CF AND M1|M1", + ], + ignore: ["cloner","ecloner","slow_cloner","floating_cloner","clone_powder","clone_liquid_spout"], + category:"machines", + insulate:true, + state:"gas", + density:2710, + hardness: 1, + }, + elements.floating_cloner.state = "gas"; + elements.floating_cloner.ignore.push("floating_cloner_spout"); + //ASSORTED RANDOM THINGS ## + //TPT reference + elements.warp = { + name: "warp", + color: "#111111", + behavior: [ + "M1%30 AND SW%30|M1%30 AND SW%30|M1%30 AND SW%30", + "M1%30 AND SW%30|DL%1|M1%30 AND SW%30", + "M1%30 AND SW%30|M1%30 AND SW%30|M1%30 AND SW%30", + ], + category: "special", + state: "gases", + }, + elements.unrealistically_flammable_gas = { + color: "#ddee11", + behavior: [ + "M1%05 AND SW%2 AND HT:1%1|M1%05 AND SW%2 AND HT:1%1|M1%05 AND SW%2 AND HT:1%1", + "M1%10 AND SW%2 AND HT:1%1|HT:1%1.000000000000000000|M1%10 AND SW%2 AND HT:1%1", + "M1%15 AND SW%2 AND HT:1%1|M1%15 AND SW%2 AND HT:1%1|M1%15 AND SW%2 AND HT:1%1", + ], + behaviorOn: [ + "M1%10 AND SW%4 AND HT:2%2|M1%10 AND SW%4 AND HT:2%2|M1%10 AND SW%4 AND HT:2%2", + "M1%20 AND SW%4 AND HT:2%2|HT:2%2 AND CH:plasma%0.01|M1%20 AND SW%4 AND HT:2%2", + "M1%30 AND SW%4 AND HT:2%2|M1%30 AND SW%4 AND HT:2%2|M1%30 AND SW%4 AND HT:2%2", + ], + category: "gases", + burn: 3000, + burnTime: 5, + burnInto: "burning_unrealistically_flammable_gas", + state: "gas", + density: 2, + tempHigh: 95, + stateHigh: "burning_unrealistically_flammable_gas", + conduct: 0.2 + }, + elements.burning_unrealistically_flammable_gas = { + color: "#eedd11", + behavior: [ + "M2 AND HT:3750%70 AND CR:plasma%10|M1 AND HT:3750%70 AND CR:plasma%10.000000000000000000000000000000000000000000|M2 AND HT:3750%70 AND CR:plasma%10", + "M1 AND HT:3750%70 AND CR:plasma%10|HT:3750%70.000000 AND CH:plasma%6.71 AND EX:9>plasma,plasma,burning_unrealistically_flammable_gas%0.25|M1 AND HT:3750%70 AND CR:plasma%10", + "M2 AND HT:3750%70 AND CR:plasma%10|M1 AND HT:3750%70 AND CR:plasma%10.000000000000000000000000000000000000000000|M2 AND HT:3750%70 AND CR:plasma%10", + ], + behaviorOn: [ + "M2 AND HT:7500%70 AND CR:plasma%15|M1 AND HT:7500%70 AND CR:plasma%15.00000000000000000000000000000000000|M2 AND HT:7500%70 AND CR:plasma%15", + "M1 AND HT:7500%70 AND CR:plasma%15|HT:7500%70 AND CH:plasma%5.60 AND EX:11>plasma,plasma,burning_unrealistically_flammable_gas%0.5|M2 AND HT:7500%70 AND CR:plasma%15", + "M2 AND HT:7500%70 AND CR:plasma%15|M1 AND HT:7500%70 AND CR:plasma%15.00000000000000000000000000000000000|M2 AND HT:7500%70 AND CR:plasma%15", + ], + category: "gases", + burn: 2000, + burnTime: 950, + burnInto: "plasma", + state: "gas", + density: 1.5, + tempHigh: 200001, + stateHigh: "plasma", + hidden: true, + excludeRandom: true, + }, + elements.unrealistically_flammable_powder = { + color: "#cddd22", + behavior: [ + "HT:2%2 AND CR:unrealistically_flammable_gas%3|HT:2%2 AND CR:unrealistically_flammable_gas%3|HT:2%2 AND CR:unrealistically_flammable_gas%3", + "HT:2%2 AND CR:unrealistically_flammable_gas%1|HT:2%2.00000000000000000000|HT:2%2 AND CR:unrealistically_flammable_gas%1", + "M2 AND HT:2%2.0000000000000|M1 AND HT:2%2.0000000000000|M2 AND HT:2%2.0000000000000", + ], + behaviorOn: [ + "HT:4%4 AND CR:unrealistically_flammable_gas%6|HT:4%4 AND CR:unrealistically_flammable_gas%6|HT:4%4 AND CR:unrealistically_flammable_gas%6", + "HT:4%4 AND CR:unrealistically_flammable_gas%2|HT:4%4.00000000000000000000|HT:4%4 AND CR:unrealistically_flammable_gas%2", + "M2 AND HT:4%4.0000000000000|M1 AND HT:4%4.0000000000000|M2 AND HT:4%4.0000000000000", + ], + category: "powders", + burn: 3000, + burnTime: 5, + burnInto: "burning_unrealistically_flammable_gas", + state: "powders", + density: 20, + tempHigh: 95, + stateHigh: "burning_unrealistically_flammable_gas", + conduct: 0.4, + }, + elements.burning_unrealistically_flammable_powder = { + color: "#ddcd22", + behavior: [ + "HT:89850%70 AND CR:burning_unrealistically_flammable_gas%7|HT:89850%70 AND CR:burning_unrealistically_flammable_gas%7.0000000000000000000000000000000000000000000000000000000000000000000000000000|HT:89850%70 AND CR:burning_unrealistically_flammable_gas%7", + "HT:89850%70 AND CR:burning_unrealistically_flammable_gas%7|HT:89850%70 AND CH:plasma%00000000005.60 AND EX:12>plasma,plasma,plasma,burning_unrealistically_flammable_gas,burning_unrealistically_flammable_powder%0.5|HT:89850%70 AND CR:burning_unrealistically_flammable_gas%7", + "M2 AND HT:89850%70 AND CR:burning_unrealistically_flammable_gas%7|M1 AND HT:89850%70 AND CR:burning_unrealistically_flammable_gas%7.00000000000000000000000000000000000000000000000000000000000000|M2 AND HT:89850%70 AND CR:burning_unrealistically_flammable_gas%7", + ], + behaviorOn: [ + "HT:179700%70 AND CR:burning_unrealistically_flammable_gas%9|HT:179700%70 AND CR:burning_unrealistically_flammable_gas%9.00000000000000000000000000000000000000000000000000000000000000000000000000|HT:179700%70 AND CR:burning_unrealistically_flammable_gas%9", + "HT:179700%70 AND CR:burning_unrealistically_flammable_gas%9|HT:179700%70 AND CH:plasma%00000000004.79 AND EX:13>plasma,plasma,plasma,burning_unrealistically_flammable_gas,burning_unrealistically_flammable_gas,burning_unrealistically_flammable_powder%1|HT:179700%70 AND CR:burning_unrealistically_flammable_gas%9", + "M2 AND HT:179700%70 AND CR:burning_unrealistically_flammable_gas%9|M1 AND HT:179700%70 AND CR:burning_unrealistically_flammable_gas%9.000000000000000000000000000000000000000000000000000000000000|M2 AND HT:179700%70 AND CR:burning_unrealistically_flammable_gas%9", + ], + category: "powders", + burn: 2000, + burnTime: 1150, + burnInto: "plasma", + state: "powders", + density: 15, + tempHigh: 200001, + stateHigh: "burning_unrealistically_flammable_gas", + conduct: 0.4, + hidden: true, + excludeRandom: true, + }, + elements.black_decay = { //random mystcraft mod reference + name: "black decay", + color: "#222222", + behavior: [ + "XX|CH:black_decay%2 AND DL:black_decay%30|XX", + "CH:black_decay%1|DL%0.2|CH:black_decay%1", + "XX|CH:black_decay%1 AND M1|XX", + ], + category: "special", + excludeRandom: true, + }, + elements.steel.behavior = behaviors.FAIRYKILL; + elements.tungstensteel = { + color: "#555589", + behavior: behaviors.FAIRYKILL, + tempHigh: 3600, + category: "solids", + density: 19000, + conduct: 0.48, + }, + elements.stainless_steel = { + color: "#7d8181", + behavior: behaviors.WALL, + reactions: { + "pool_water": { elem1:"rust", chance:0.0009 }, + "salt_water": { elem1:"rust", chance:0.0003 }, + "salt": { elem1:"rust", chance:0.0003 }, + "acid": { elem1:"rust" } + }, + tempHigh: 1455.5, + category: "solids", + density: 7930, + conduct: 0.42, + hardness: 0.85 + }; + elements.steel.reactions.acid = { elem1:"rust" }; + elements.molten_tungsten = { + density: 17600, + temp: 3500, + tempHigh: 5555, + stateHigh: "tungsten_gas", + }, + elements.pop_rock_pop = { + color: ["#ffb49f","#ffd9a1","#ffada1"], + behavior: [ + "XX|XX|XX", + "XX|EX:3>carbon_dioxide,sugar|XX", + "XX|XX|XX", + ], + category: "energy", + state: "gas", + density: 100, + excludeRandom: true, + hidden: true, + }, + elements.pop_rocks = { + color: ["#d4d4d4","#74d4d4","#d474d4","#7474d4","#d4d474","#74d474","#d47474","#747474","#2f2f2f","#8f2f2f","#2f8f2f","#8f8f2f","#2f2f8f","#8f2f8f","#2f8f8f","#8f8f8f","#606060","#c06060","#60c060","#c0c060","#6060c0","#c060c0","#60c0c0","#c0c0c0"], + behavior: behaviors.POWDER, + reactions: { + water: { elem1: "sugar_water", elem2: [null,"pop_rock_pop"] }, + salt_water: { elem1: "salt_water", elem2: [null,"pop_rock_pop"] }, + sugar_water: { elem1: "sugar_water", elem2: [null,"pop_rock_pop"] }, + pool_water: { elem1: "pool_water", elem2: [null,"pop_rock_pop"] }, + dirty_water: { elem1: "dirty_water", elem2: [null,"pop_rock_pop"] }, + radioactive_water: { elem1: "radioactive_water", elem2: [null,"pop_rock_pop"] }, + pure_water: { elem1: "sugar_water", elem2: [null,"pop_rock_pop"] }, + chilly_water: { elem1: "chilly_water", elem2: [null,"pop_rock_pop"] }, + ethanol: { elem1: "sugar", elem2: [null,"pop_rock_pop"], chance: 0.003 }, // 1g/170mL vs 200g/100mL + methanol: { elem1: "sugar", elem2: [null,"pop_rock_pop"], chance: 0.005 } // 1g/100mL cs 200g/100mL + }, + tempHigh: 138, + stateHigh: ["caramel","pop_rock_pop"], + category: "land", + state: "solid", + density: 333.333333333, //made-up + hardness: 0.2, + breakInto: ["sugar","pop_rock_pop"], + }, + elements.tungsten_gas = { + color: "#FFEEE2", + behavior: [ + "CR:plasma%0.625 AND M2|M1|CR:plasma%0.625 AND M2", + "M1|XX|M1", + "CR:plasma%0.625 AND M2|M1|CR:plasma%0.625 AND M2", + ], + density: 15800, //https://link.springer.com/article/10.1007/s11661-019-05262-5 + temp: 5600, + tempLow: 5555, + stateLow: "molten_tungsten", + category: "gases", + hidden: true, + }, + elements.molten_steel ??= {}; + elements.molten_steel.reactions ??= {}; + elements.molten_steel.reactions.molten_tungsten = { "elem1":"molten_tungstensteel", "elem2":"molten_tungstensteel" }; + elements.molten_steel.reactions.molten_chromium = { "elem1":"molten_stainless_steel", "elem2":["molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_stainless_steel"] }; + elements.unrealistically_flammable_substance_bomb = { + name: "unrealistically flammable bomb", + color: "#cdad52", + behavior: [ + "XX|XX|XX", + "XX|XX|XX", + "M2|M1 AND EX:10>plasma,burning_unrealistically_flammable_powder,unrealistically_flammable_powder,unrealistically_flammable_powder,unrealistically_flammable_powder,burning_unrealistically_flammable_gas,unrealistically_flammable_gas,unrealistically_flammable_gas,unrealistically_flammable_gas|M2", + ], + category: "weapons", + state: "solid", + density: 1300, + excludeRandom: true, + }, + elements.warp_bomb = { + name: "warp bomb", + color: "#422e4a", + behavior: [ + "XX|XX|XX", + "XX|CC:#5b3a69,#382740,#400e61|XX", + "M2|M1 AND EX:15>warp|M2", + ], + category: "weapons", + state: "solid", + density: 1300, + excludeRandom: true, + }, + elements.turbine = { + color: "#75726a", + tempHigh: elements.copper.tempHigh, + stateHigh: ["steel","molten_copper"], + conduct: 1, + behavior: behaviors.WALL, + tick: function(pixel) { + var neighbors = adjacentCoords.map(offsetPair => pixelMap[pixel.x+offsetPair[0]]?.[pixel.y+offsetPair[1]]).filter(function(pixelOrUndefined) { return typeof(pixelOrUndefined) == "object" }); + if(neighbors.length < 0) { return }; + var neighboringElements = neighbors.filter(function(px) { return !!px }).map(x => x.element); + var neighboringStates = neighboringElements.map(elemName => elements[elemName].state ?? "solid"); + var nonSolidNeighbors = neighboringStates.filter(function(string) { return (string !== "solid") }).length; + if(nonSolidNeighbors == 0) { return }; + pixel.charge ??= 0; + pixel.charge += nonSolidNeighbors / 8; + pixel.temp += (nonSolidNeighbors / 500); + }, + onTryMoveInto: function(pixel,otherPixel) { + pixel.charge ??= 0; + pixel.charge += 1/8; + pixel.temp += (1/500); + }, + hardness: averageNumericArray([elements.copper.hardness,elements.steel.hardness,elements.steel.hardness]), + breakInto: ["metal_scrap", "steel_scrap", "steel_scrap", "copper_scrap", "copper_scrap", "steel_scrap"], + state: "solid", + category: "machines", + density: averageNumericArray([elements.steel.density, elements.copper.density, airDensity]) + }; + elements.test_fader = { //basically an aray clone + color: _cc.w.h, + properties: { + "life": 100, + "fadeRate": 1 + }, + hardness: 0.8, + density: 0, + state: "solid", + tick: function(pixel) { + pixel.life ??= 100; + var alpha = isNaN(pixel.life) ? Math.floor(Math.random() * 256) : (pixel.life * 2.55); //ALPHA/??!?!?!?!/1/ CL REFERENCE??!?!?!?!?!?!?!?!??!? + //console.log("tick"); + var splitColor = convertColorFormats(pixel.color,"json"); + //console.log(pixel.color,splitColor); + splitColor.a = alpha; + pixel.color = convertColorFormats(splitColor,"hex"); + //console.log(pixel.color); + if(pixel.fadeRate == 0 || (pixel.fadeRate && isNaN(pixel.fadeRate))) { + } else { + pixel.life -= (pixel.fadeRate ?? 1); + }; + if(pixel.life < 0) { + deletePixel(pixel.x,pixel.y); + return + } + } + }; + sweepingLaserTransparencyWhitelist = ["glass","glass_shard","rad_glass","rad_glass_shard","glass_pane","rad_glass_pane","water","salt_water","sugar_water","pool_water"]; + sweepingLaserTransparencyBlacklist = []; + //Sweeping laser + elements.sweeping_laser = { + "color": "#905050", + customColor: true, + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","sweepingLaserRotationSpeed"); + p0.setAttribute("min","-4"); + p0.value = ambaPlaceProperties.sweepingLaserRotationSpeed; + }; + if(p0h) { + p0h.innerText = "Rotation Speed"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","sweepingLaserBeamLength"); + p1.setAttribute("min","1"); + p1.value = ambaPlaceProperties.sweepingLaserBeamLength; + }; + if(p1h) { + p1h.innerText = "Length"; + }; + + showSetterColumn("numeric",2); + var p2 = document.getElementById("propertynumeric2input"); + var p2h = document.getElementById("propertynumeric2heading"); + if(p2) { + p2.setAttribute("set","sweepingLaserBeamTemperature"); + p2.setAttribute("min","-99999999"); + p2.value = ambaPlaceProperties.sweepingLaserBeamTemperature; + }; + if(p2h) { + p2h.innerText = "Temperature"; + }; + + showSetterColumn("numeric",3); + var p3 = document.getElementById("propertynumeric3input"); + var p3h = document.getElementById("propertynumeric3heading"); + if(p3) { + p3.setAttribute("set","sweepingLaserBeamBrevity"); + p3.setAttribute("min","0"); + p3.value = ambaPlaceProperties.sweepingLaserBeamBrevity; + }; + if(p3h) { + p3h.innerText = "Brevity"; + } + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + + "tick": function(pixel) { + pixel.r ??= 0; + if(isNaN(pixel.r)) { return false }; + pixel.rSpeed ??= -(ambaPlaceProperties?.sweepingLaserRotationSpeed ?? -0.03); + pixel.beamLength ??= (ambaPlaceProperties?.sweepingLaserBeamLength ?? 10); + pixel.beamTemp ??= (ambaPlaceProperties?.sweepingLaserBeamTemperature ?? 2000); + pixel.brevity ??= (ambaPlaceProperties?.sweepingLaserBeamBrevity ?? 5); + pixel.untssa ??= Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER + 1)) * (Math.floor() < 0.5 ? -1 : 1); //unique number to stop self-addition + if(Object.is(pixel.untssa,-0)) { pixel.untssa = Number.MAX_SAFE_INTEGER + 3 }; + pixel.beamColor ??= currentColor; + + var beamElement = "test_fader"; + var rotation = -(((pixel.r ?? 0) % 4) + 1); //preserving the original behavior of 0 = up, 1 = left + var rotationInRadians = scale(rotation,0,4,0,Math.PI * 2); + var vector = [Math.cos(rotationInRadians), Math.sin(rotationInRadians)]; + var distance = Math.min(300,Math.max(2,(pixel.beamLength + 1) ?? 10)); + for(var i = 1; i <= distance; i += (3 ** -(Math.ceil(Math.log2(distance / 10))))) { //increase the tries to try to reduce gaps in the beam, as distance goes up + var newOffsets = vector.map(coord => Math.round(coord * i)); + var finalPos = {x: pixel.x + newOffsets[0], y: pixel.y + newOffsets[1]}; + //console.log(finalPos); + //console.log({x:pixel.x,y:pixel.y},finalPos); + if(!(isEmpty(finalPos.x,finalPos.y))) { + var otherPixel = pixelMap[finalPos.x]?.[finalPos.y]; + if(otherPixel && ( + [beamElement,pixel.element].concat(sweepingLaserTransparencyWhitelist).includes(otherPixel.element) || + elements[otherPixel.element].state === "gas" + ) && !(sweepingLaserTransparencyBlacklist.includes(otherPixel.element))) { + if(otherPixel.element == "test_fader") { //intentionally hard-coded + otherPixel.life = 100; + if(otherPixel.untssa !== pixel.untssa) { + var _pc = convertColorFormats(pixel.color,"hex",true).slice(0,7); + otherPixel.lastColors ??= [convertColorFormats(otherPixel.color,"hex",true).slice(0,7)]; + if(otherPixel.lastColors.indexOf(_pc) === -1) { + otherPixel.lastColors.push(_pc); + otherPixel.color = otherPixel.lastColors.reduce((a,b) => addColors(a,b,"hex").slice(0,7)).padEnd(9,"F") + } + } + }; + continue + } else { + break + } + }; + var newBeamPixel = tryCreatePixelReturn(beamElement,finalPos.x,finalPos.y); + if(!newBeamPixel) { + break + } else { + newBeamPixel.temp = pixel.beamTemp ?? 2000; + newBeamPixel.fadeRate = pixel.brevity ??= 5; + newBeamPixel.untssa = pixel.untssa; + newBeamPixel.color = (pixel.beamColor ?? "#FF0000"); + } + }; + pixel.r += pixel.rSpeed ?? 0.03; + }, + "reactions": { + "water": { elem1: "steel", charge1: 1 }, + }, + "category": "special", + "breakInto": "charcoal", + "tempHigh": 2700, + "stateHigh": "molten_steel", + }; + //hormones + //estrogens + elements.estradiol = { + color: "#f2fcee", //it absorbs shorter wavelength UV than testosterone and I am treating this like absorbing violet for convenience + //https://www.researchgate.net/publication/226065469_Optical_Properties_of_Two_Types_of_Sex_Hormones_of_the_Cyclopentenephenanthrene_Series + //http://depts.washington.edu/cmditr/modules/lum/color.html + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1200, + tempHigh: 173, + category: "powders", + }, + elements.molten_estradiol = { + tempHigh: 446, + stateHigh: "vaporized_estradiol", + }, + elements.vaporized_estradiol = { + color: ["#ffbf60","#ffdc60","#ff9d60"], //hormone gas wouldn't glow that brightly at these temperatures but just ignore that + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 972, + temp: 500, + tempLow: 446, + stateLow: "molten_estradiol", + }, + //progestogens + elements.progesterone = { + color: "#f7eefc", //slightly different? from testosterone but exaggerated + //https://downloads.hindawi.com/journals/ijps/2017/9603140.pdf + //these hormones all absorb in the uv region anyway so they would all look white to us + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1100, + tempHigh: 121, + category: "powders", + }, + elements.molten_progesterone = { + tempHigh: 447, + stateHigh: "vaporized_progesterone", + }, + elements.vaporized_progesterone = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 891, + tempLow: 447, + stateLow: "molten_progesterone", + } + //androgens + //plain testosterone + elements.testosterone = { + color: "#f7eef7", //it absorbs longer wavelength UV than estradiol and I am treating this like absorbing green for convenience + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1100, + tempHigh: 155, + category: "powders", + }, + elements.molten_testosterone = { + tempHigh: 433, + temp: 400, + stateHigh: "vaporized_testosterone", + }, + elements.vaporized_testosterone = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 891, + temp: 500, + tempLow: 433, + stateLow: "molten_testosterone", + }, + //undecanoate (form actually used in HRT) + elements.testosterone_undecanoate = { + color: "#f8f2fc", //more creatively-interpreted UV data: https://spectrabase.com/spectrum/5Yc7XCCDkA7 plus http://depts.washington.edu/cmditr/modules/lum/color.html and a lot of eyeballing and loose approximation + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1037, //https://www.chembk.com/en/chem/Testosterone%20Undecanoate + tempHigh: 63, + category: "powders", + }, + elements.molten_testosterone_undecanoate = { + tempHigh: 550, + stateHigh: "vaporized_testosterone_undecanoate", + hidden: true, + }, + elements.vaporized_testosterone_undecanoate = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 834, //made-up due to lack of data + temp: 600, + tempLow: 63, + stateLow: "molten_testosterone_undecanoate", + }, + //other + //anti-androgens + //CPA + elements.cyproterone_acetate = { + color: "#efeef8", //it absorbs far longer uv than the others, which i am rendering as red absorption + //https://www.researchgate.net/figure/UV-spectrum-for-drospirenone-cyproterone-acetate-desogestrel-and-ethinyl-estradiol-at-1_fig1_315746083 + //i didn't really expect to find a spectrum for this + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1068, + tempHigh: 200, + category: "powders", + }, + /* > Hazardous decomposition products: + > Hydrogen chloride (HCl) + > Carbon monoxide and carbon dioxide + > Hydrogen + > https://cdn.caymanchem.com/cdn/msds/16622m.pdf + so many interesting effects i can't add + */ + elements.molten_cyproterone_acetate = { + tempHigh: 569, + stateHigh: "vaporized_cyproterone_acetate", + }, + elements.vaporized_cyproterone_acetate = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 865, + tempLow: 569, + stateLow: "molten_cyproterone_acetate", + }, + //spironolactone + elements.spironolactone = { + color: "#f7eef1", //UV absorbance peak wavelength is slightly shorter than that of testosterone + //https://www.researchgate.net/publication/348592381_Quantification_of_Spironolactone_by_first_and_second_order_UV_Derivative_Spectrophotometry_in_bulk_and_tablet_dosage_form/link/6006b3cf299bf14088a649bd/download + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1200, + tempHigh: 207, + category: "powders", + }, + elements.molten_spironolactone = { + tempHigh: 597, + stateHigh: "vaporized_spironolactone", + /*should have more decomps + https://sci-hub.se/https://link.springer.com/article/10.1007/BF01979243 + > The TG-DTG curves of spironolactone in Fig. 7 demonstrate that the compound is thermally stable up to 200*C, and that its thermal decomposition occurs between 200 and 620*C. Four consecutive steps are observed in the TG-DTG curves. The first step, up to 260*C is ascribed to the elimination of the substituent group, SCOCH_{3} (TG= 19.59%, Calc. = 19.33%). The second step (260-370*C) and the third and fourth steps (370-700*C) involve losses of 42.93% and 37.48%, respectively, but do not permit a suggestion as to which parts of the compound are eliminated in each step. */ + }, + elements.vaporized_spironolactone = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 972, + tempLow: 597, + stateLow: "molten_spironolactone", + }, + //finasteride + elements.finasteride = { + color: "#fcfcf1", //UV absorbance peak wavelength is even shorter than that of estradiol + //https://www.researchgate.net/publication/312317200 + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1100, + tempHigh: 253, + category: "powders", + }, + elements.molten_finasteride = { + tempHigh: 577, + stateHigh: "vaporized_finasteride", + }, + elements.vaporized_finasteride = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 891, + tempLow: 577, + stateLow: "molten_finasteride", + }, + //dutasteride + elements.dutasteride = { + color: "#fbf6ee", //High UV absorbances around the peak wavelengths of both estradiol and testosterone + //https://sphinxsai.com/sphinxsaivol_2no.1/pharmtech_vol_2no.1/PharmTech_Vol_2No.1PDF/PT=18%20(113-117).pdf + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1303, //https://www.chemicalbook.com/ChemicalProductProperty_EN_CB3254628.htm + tempHigh: 243, + category: "powders", + }, + elements.molten_dutasteride = { + tempHigh: 620, //http://www.chemspider.com/Chemical-Structure.5293502.html + stateHigh: "vaporized_dutasteride", + }, + elements.vaporized_dutasteride = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 1055, + tempLow: 620, + stateLow: "molten_dutasteride", + }, + //bicalutamide + elements.bicalutamide = { + color: "#f4fcee", //peaks at 200-220 and at 270 + //i am probably mapping uv to visible wrong and misreading color.html + //https://www.researchgate.net/publication/257679318 + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1520, //https://www.chemicalbook.com/ProductMSDSDetailCB7457827_EN.htm + tempHigh: 192, + category: "powders", + }, + elements.molten_bicalutamide = { + tempHigh: 659, + stateHigh: "vaporized_bicalutamide", + }, + elements.vaporized_bicalutamide = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 1231, + tempLow: 659, + stateLow: "molten_bicalutamide", + }, + //puberty blockers + elements.leuprolide = { + color: "#f5eefb", //http://dspace.hmlibrary.ac.in:8080/jspui/bitstream/123456789/1143/11/11_Chapter%203.pdf + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1440, //https://www.chemicalbook.com/ProductMSDSDetailCB7457827_EN.htm + tempHigh: 150, + category: "powders", + }, + elements.molten_leuprolide = { + tempHigh: 1720, //https://web.archive.org/web/20210512074205/http://www.shreejipharmainternational.com/leuprolide-acetate-1177796.html + stateHigh: "vaporized_leuprolide", + }, + elements.vaporized_leuprolide = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + density: 1166, + tempLow: 1720, + stateLow: "molten_leuprolide", + }, + //histrelin + elements.histrelin = { + color: "#f8f5ee", //no spectrum available + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 1500, //https://www.chemicalbook.com/ProductMSDSDetailCB7457827_EN.htm + tempHigh: 1800, //https://www.chemsrc.com/en/cas/76712-82-8_1042020.html + stateHigh: "vaporized_histrelin", + category: "powders", + }, + elements.vaporized_histrelin = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: behaviors.GAS, + state: "gas", + category: "gases", + hidden: true, + tempLow: 1800, + stateLow: "histrelin", + }, + //end of hrt section + elements.densinium = { + color: ["#565656","#575657","#565257","#554d57","#554659"], + tempHigh: 4712, //arbitrary + hardness: 0.9991, //somewhat arbitrary + density: 39180, + conduct: 0.86, //arbitrary + behavior: behaviors.WALL, + state: "solid", + category: "solids", + } //this is effectively a mere interpretation of densinium + elements.molten_densinium = { + hardness: 0.9991, + } + elements.acid.ignore.push("densinium","molten_densinium") + //maxColorOffset will only be applied if maxColorOffset.js is enabled + elements.silk_velvet = { + color: ["#edece8", "#ede7e4"], + maxColorOffset: 7, + category: "land", + state: "solid", + behavior: [ + "XX|XX|XX", + "XX|XX|XX", + "XX|M1|XX", + ], + burnInto: "ash", + burn:72, + burnTime:25, + density: 182, + }; + elements.red_velvet = { + color: ["#a80508", "#b30b0e"], + maxColorOffset: 7, + category: "land", + state: "solid", + behavior: [ + "XX|XX|XX", + "XX|XX|XX", + "XX|M1|XX", + ], + tick: function(pixel) { //alias for velvet that is red + pixel.element = "silk_velvet"; + }, + burnInto: "ash", + burn: 72, + burnTime: 25, + density: 182, + }; + elements.netherrack.hardness = 0.07; + elements.netherrack.breakInto = ["crushed_netherrack","crushed_netherrack","crushed_netherrack","crushed_netherrack","crushed_netherrack","crushed_netherrack","crushed_netherrack","sulfur"] // and some copper, gold, iron, nickel after processing //sulfur closer to 1/7 in-game + elements.netherrack.burn = 9 + elements.netherrack.burnTime = 9007199254740995 + elements.netherrack.burnInto = "netherrack" + elements.crushed_netherrack = { + color: ["#e34b46","#b04235","#73431f","#522510","#7a3326"], + behavior: behaviors.POWDER, + category:"land", + tempHigh: 2750, + stateHigh: "molten_netherrack", + state: "solid", + density: 1680, + burn: 20, + burnTime: 9007199254740995, + hardness: 0.02, + hidden: true, + }; + elements.sencc = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + for (let i = -1; i < 2; i++) { + for (let j = -1; j < 2; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/8)*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc2 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + for (let i = -3; i < 4; i++) { + for (let j = -3; j < 4; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/24)*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc3 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + var squadius = 3 + for (let i = (-1*squadius); i < (squadius+1); i++) { + for (let j = (-1*squadius); j < (squadius+1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/((((squadius*2)+1)**2)-1))*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc4 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + var squadius = 4 + for (let i = (-1*squadius); i < (squadius+1); i++) { + for (let j = (-1*squadius); j < (squadius+1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/((((squadius*2)+1)**2)-1))*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc5 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + var squadius = 5 + for (let i = (-1*squadius); i < (squadius+1); i++) { + for (let j = (-1*squadius); j < (squadius+1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/((((squadius*2)+1)**2)-1))*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc6 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + var squadius = 6 + for (let i = (-1*squadius); i < (squadius+1); i++) { + for (let j = (-1*squadius); j < (squadius+1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/((((squadius*2)+1)**2)-1))*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc7 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + var squadius = 7 + for (let i = (-1*squadius); i < (squadius+1); i++) { + for (let j = (-1*squadius); j < (squadius+1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/((((squadius*2)+1)**2)-1))*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc8 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + var squadius = 8 + for (let i = (-1*squadius); i < (squadius+1); i++) { + for (let j = (-1*squadius); j < (squadius+1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/((((squadius*2)+1)**2)-1))*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc9 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + var squadius = 9 + for (let i = (-1*squadius); i < (squadius+1); i++) { + for (let j = (-1*squadius); j < (squadius+1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/((((squadius*2)+1)**2)-1))*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc10 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + var squadius = 10 + for (let i = (-1*squadius); i < (squadius+1); i++) { + for (let j = (-1*squadius); j < (squadius+1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/((((squadius*2)+1)**2)-1))*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc11 = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + var squadius = 11 + for (let i = (-1*squadius); i < (squadius+1); i++) { + for (let j = (-1*squadius); j < (squadius+1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/((((squadius*2)+1)**2)-1))*pixel.uwu + ",0,0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.sencc2b = { //same element neighbor count check + color: _cc.b.h, + uwu: 0, + owo: 0, + tick: function(pixel) { + pixel.uwu = 0 + pixel.owo = 0 + for (let i = -2; i < 3; i++) { + for (let j = -2; j < 3; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + if(pixel.uwu < 8) { + pixel.uwu++ + } else { + pixel.owo++ + } + } + } + } + } + pixel.owo -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu) || pixel.owo == undefined || pixel.owo == null || isNaN(pixel.owo)) { + pixel.color = "rgb(127,127,127)" + } else { + pixel.color = "rgb(" + (255/8)*pixel.uwu + "," + (255/16)*pixel.owo + ",0)" + } + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + }, + elements.discharge = { + color: "#7f7f7f", + tick: function(pixel) { + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + if (!isEmpty(i,j)) { + pixelMap[i][j].charge = 0 + } + } + } + deletePixel(pixel.x, pixel.y) + }, + category:"special", + insulate:true, + state: "solid", + behavior: behaviors.SELFDELETE, + }, + elements.troll_powder = { + color: [_cc.w.h,_cc.b.h], + tick: function(pixel) { + ddd = Math.random() + eee = Math.random() + fff = 1-eee + doHeat(pixel); + doBurning(pixel); + if(ddd < 0.9) { + if(!tryMove(pixel, pixel.x, pixel.y+1)) { + if(eee < 1/2) { tryMove(pixel, pixel.x-1, pixel.y+1) } else tryMove(pixel, pixel.x+1, pixel.y+1) + } + if(Math.random() < 0.0017) { + if(fff < 1/5) { tryMove(pixel, pixel.x-2, pixel.y-1) } + if(fff < 2/5) { tryMove(pixel, pixel.x-1, pixel.y-2) } + if(fff < 3/5) { tryMove(pixel, pixel.x, pixel.y-3) } + if(fff < 4/5) { tryMove(pixel, pixel.x+1, pixel.y-2) } + if(fff < 5/5) { tryMove(pixel, pixel.x+2, pixel.y-1) } + } + if(Math.random() < 0.0003) { tryMove(pixel, pixel.y, pixel.y); } + if(Math.random() < 0.0003) { tryMove(pixel, pixel.x, pixel.x); } + if(((Math.floor(pixel.x/2) % 2 == 0) && (Math.floor(pixel.y/2) % 2 == 0)) || ((Math.floor(pixel.x/2) % 2 == 1) && (Math.floor(pixel.y/2) % 2 == 1))) { + pixel.color = "rgb(32,32,32)" + } else { + pixel.color = "rgb(224,224,224)" + } + } + if(ddd >= 0.9) { + if(!tryMove(pixel, pixel.x, pixel.y-1)) { + if(eee < 1/2) { tryMove(pixel, pixel.x-1, pixel.y-1) } else tryMove(pixel, pixel.x+1, pixel.y-1) + } + if(Math.random() < 0.0017) { + if(fff < 1/5) { tryMove(pixel, pixel.x-2, pixel.y+1) } + if(fff < 2/5) { tryMove(pixel, pixel.x-1, pixel.y+2) } + if(fff < 3/5) { tryMove(pixel, pixel.x, pixel.y+3) } + if(fff < 4/5) { tryMove(pixel, pixel.x+1, pixel.y+2) } + if(fff < 5/5) { tryMove(pixel, pixel.x+2, pixel.y+1) } + } + if(Math.random() < 0.0003) { tryMove(pixel, pixel.y, pixel.y); } + if(Math.random() < 0.0003) { tryMove(pixel, pixel.x, pixel.x); } + if(((Math.floor(pixel.x/2) % 2 == 0) && (Math.floor(pixel.y/2) % 2 == 0)) || ((Math.floor(pixel.x/2) % 2 == 1) && (Math.floor(pixel.y/2) % 2 == 1))) { + pixel.color = "rgb(32,32,32)" + } else { + pixel.color = "rgb(224,224,224)" + } + pixel.temp = pixel.temp + ((Math.floor(Math.random()*3) - 1)*2) + } + }, + category: "powders", + state: "solid", + density: 1602, + }, + elements.void_first = { + color: "#262626", + tick: function(pixel) { + if(!pixel.void) { + //store 4 touching pixels in variables if the variables don't exist + if(!outOfBounds(pixel.x,pixel.y-1) && !isEmpty(pixel.x,pixel.y-1)) { + if(!pixel.dc1 && pixelMap[pixel.x][pixel.y-1].element != pixel.element) { + pixel.dc1 = pixelMap[pixel.x][pixel.y-1].element + } + } + if(!outOfBounds(pixel.x+1,pixel.y) && !isEmpty(pixel.x+1,pixel.y)) { + if(!pixel.dc2 && pixelMap[pixel.x+1][pixel.y].element != pixel.element) { + pixel.dc2 = pixelMap[pixel.x+1][pixel.y].element + } + } + if(!outOfBounds(pixel.x,pixel.y+1) && !isEmpty(pixel.x,pixel.y+1)) { + if(!pixel.dc3 && pixelMap[pixel.x][pixel.y+1].element != pixel.element) { + pixel.dc3 = pixelMap[pixel.x][pixel.y+1].element + } + } + if(!outOfBounds(pixel.x-1,pixel.y) && !isEmpty(pixel.x-1,pixel.y)) { + if(!pixel.dc3 && pixelMap[pixel.x-1][pixel.y].element != pixel.element) { + pixel.dc4 = pixelMap[pixel.x-1][pixel.y].element + } + } + //choose from 1 + if(pixel.dc1 && !pixel.dc2 && !pixel.dc3 && !pixel.dc4) { + if(!pixel.void) { + pixel.void = pixel.dc1 + } + } + if(!pixel.dc1 && pixel.dc2 && !pixel.dc3 && !pixel.dc4) { + if(!pixel.void) { + pixel.void = pixel.dc2 + } + } + if(!pixel.dc1 && !pixel.dc2 && pixel.dc3 && !pixel.dc4) { + if(!pixel.void) { + pixel.void = pixel.dc3 + } + } + if(!pixel.dc1 && !pixel.dc2 && !pixel.dc3 && pixel.dc4) { + if(!pixel.void) { + pixel.void = pixel.dc4 + } + } + ggg = Math.random() + hhh = Math.random() + iii = Math.random() + //choose from 2 + //1100 and 0011 + if(pixel.dc1 && pixel.dc2 && !pixel.dc3 && !pixel.dc4) { + if(!pixel.void) { + if(ggg < 1/2) { + pixel.void = pixel.dc1 + } else { + pixel.void = pixel.dc2 + } + } + } + if(!pixel.dc1 && !pixel.dc2 && pixel.dc3 && pixel.dc4) { + if(!pixel.void) { + if(ggg < 1/2) { + pixel.void = pixel.dc3 + } else { + pixel.void = pixel.dc4 + } + } + } + //1010 and 0101 + if(pixel.dc1 && !pixel.dc2 && pixel.dc3 && !pixel.dc4) { + if(!pixel.void) { + if(ggg < 1/2) { + pixel.void = pixel.dc1 + } else { + pixel.void = pixel.dc3 + } + } + } + if(!pixel.dc1 && pixel.dc2 && !pixel.dc3 && pixel.dc4) { + if(!pixel.void) { + if(ggg < 1/2) { + pixel.void = pixel.dc2 + } else { + pixel.void = pixel.dc4 + } + } + } + //0110 and 1001 + if(!pixel.dc1 && pixel.dc2 && pixel.dc3 && !pixel.dc4) { + if(!pixel.void) { + if(ggg < 1/2) { + pixel.void = pixel.dc2 + } else { + pixel.void = pixel.dc3 + } + } + } + if(pixel.dc1 && !pixel.dc2 && !pixel.dc3 && pixel.dc4) { + if(!pixel.void) { + if(ggg < 1/2) { + pixel.void = pixel.dc1 + } else { + pixel.void = pixel.dc4 + } + } + } + //choose from 3 + //0111 + if(!pixel.dc1 && pixel.dc2 && pixel.dc3 && pixel.dc4) { + if(!pixel.void) { + if(hhh < 1/3) { + pixel.void = pixel.dc2 + } else if(hhh < 2/3) { + pixel.void = pixel.dc3 + } else { + pixel.void = pixel.dc4 + } + } + } + //1011 + if(pixel.dc1 && !pixel.dc2 && pixel.dc3 && pixel.dc4) { + if(!pixel.void) { + if(hhh < 1/3) { + pixel.void = pixel.dc1 + } else if(hhh < 2/3) { + pixel.void = pixel.dc3 + } else { + pixel.void = pixel.dc4 + } + } + } + //1101 + if(pixel.dc1 && pixel.dc2 && !pixel.dc3 && pixel.dc4) { + if(!pixel.void) { + if(hhh < 1/3) { + pixel.void = pixel.dc1 + } else if(hhh < 2/3) { + pixel.void = pixel.dc2 + } else { + pixel.void = pixel.dc4 + } + } + } + //1110 + if(pixel.dc1 && pixel.dc2 && pixel.dc3 && !pixel.dc4) { + if(!pixel.void) { + if(hhh < 1/3) { + pixel.void = pixel.dc1 + } else if(hhh < 2/3) { + pixel.void = pixel.dc2 + } else { + pixel.void = pixel.dc3 + } + } + } + //choose from 4 + //1111 + if(pixel.dc1 && pixel.dc2 && pixel.dc3 && pixel.dc4) { + if(!pixel.void) { + if(iii < 1/4) { + pixel.void = pixel.dc1 + } else if(iii < 2/4) { + pixel.void = pixel.dc2 + } else if(iii < 3/4) { + pixel.void = pixel.dc3 + } else { + pixel.void = pixel.dc4 + } + } + } + } else if(pixel.void) { + if(pixel.dc1 || pixel.dc2 || pixel.dc3 || pixel.dc4) { + delete pixel.dc1; + delete pixel.dc2; + delete pixel.dc3; + delete pixel.dc4; + } + } + for(i = 0; i < adjacentCoords.length; i++) { + var pX = pixel.x; var pY = pixel.y; var oX = adjacentCoords[i][0]; var oY = adjacentCoords[i][1]; var nX = pX+oX; var nY = pY+oY; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY] + var newElement = newPixel.element; + if(newElement != pixel.element && newElement === pixel.void) { + deletePixel(nX,nY); + }; + }; + }; + }, + category:"special", + hardness: 1, + }, + elements.converter = { + color: "#2ec408", + tick: function(pixel) { + if(!pixel.changeTo) { + //store 4 touching pixels in variables if the variables don't exist + if(!outOfBounds(pixel.x,pixel.y-1) && !isEmpty(pixel.x,pixel.y-1)) { + if(!pixel.dc1 && pixelMap[pixel.x][pixel.y-1].element != pixel.element) { + pixel.dc1 = pixelMap[pixel.x][pixel.y-1].element + } + } + if(!outOfBounds(pixel.x+1,pixel.y) && !isEmpty(pixel.x+1,pixel.y)) { + if(!pixel.dc2 && pixelMap[pixel.x+1][pixel.y].element != pixel.element) { + pixel.dc2 = pixelMap[pixel.x+1][pixel.y].element + } + } + if(!outOfBounds(pixel.x,pixel.y+1) && !isEmpty(pixel.x,pixel.y+1)) { + if(!pixel.dc3 && pixelMap[pixel.x][pixel.y+1].element != pixel.element) { + pixel.dc3 = pixelMap[pixel.x][pixel.y+1].element + } + } + if(!outOfBounds(pixel.x-1,pixel.y) && !isEmpty(pixel.x-1,pixel.y)) { + if(!pixel.dc3 && pixelMap[pixel.x-1][pixel.y].element != pixel.element) { + pixel.dc4 = pixelMap[pixel.x-1][pixel.y].element + } + } + //choose from 1 + if(pixel.dc1 && !pixel.dc2 && !pixel.dc3 && !pixel.dc4) { + if(!pixel.changeTo) { + pixel.changeTo = pixel.dc1 + } + } + if(!pixel.dc1 && pixel.dc2 && !pixel.dc3 && !pixel.dc4) { + if(!pixel.changeTo) { + pixel.changeTo = pixel.dc2 + } + } + if(!pixel.dc1 && !pixel.dc2 && pixel.dc3 && !pixel.dc4) { + if(!pixel.changeTo) { + pixel.changeTo = pixel.dc3 + } + } + if(!pixel.dc1 && !pixel.dc2 && !pixel.dc3 && pixel.dc4) { + if(!pixel.changeTo) { + pixel.changeTo = pixel.dc4 + } + } + ggg = Math.random() + hhh = Math.random() + iii = Math.random() + //choose from 2 + //1100 and 0011 + if(pixel.dc1 && pixel.dc2 && !pixel.dc3 && !pixel.dc4) { + if(!pixel.changeTo) { + if(ggg < 1/2) { + pixel.changeTo = pixel.dc1 + } else { + pixel.changeTo = pixel.dc2 + } + } + } + if(!pixel.dc1 && !pixel.dc2 && pixel.dc3 && pixel.dc4) { + if(!pixel.changeTo) { + if(ggg < 1/2) { + pixel.changeTo = pixel.dc3 + } else { + pixel.changeTo = pixel.dc4 + } + } + } + //1010 and 0101 + if(pixel.dc1 && !pixel.dc2 && pixel.dc3 && !pixel.dc4) { + if(!pixel.changeTo) { //7989 yay soshi! + if(ggg < 1/2) { + pixel.changeTo = pixel.dc1 + } else { + pixel.changeTo = pixel.dc3 + } + } + } + if(!pixel.dc1 && pixel.dc2 && !pixel.dc3 && pixel.dc4) { + if(!pixel.changeTo) { + if(ggg < 1/2) { + pixel.changeTo = pixel.dc2 + } else { + pixel.changeTo = pixel.dc4 + } + } + } + //0110 and 1001 + if(!pixel.dc1 && pixel.dc2 && pixel.dc3 && !pixel.dc4) { + if(!pixel.changeTo) { + if(ggg < 1/2) { + pixel.changeTo = pixel.dc2 + } else { + pixel.changeTo = pixel.dc3 + } + } + } + if(pixel.dc1 && !pixel.dc2 && !pixel.dc3 && pixel.dc4) { + if(!pixel.changeTo) { + if(ggg < 1/2) { + pixel.changeTo = pixel.dc1 + } else { + pixel.changeTo = pixel.dc4 + } + } + } + //choose from 3 + //0111 + if(!pixel.dc1 && pixel.dc2 && pixel.dc3 && pixel.dc4) { + if(!pixel.changeTo) { + if(hhh < 1/3) { + pixel.changeTo = pixel.dc2 + } else if(hhh < 2/3) { + pixel.changeTo = pixel.dc3 + } else { + pixel.changeTo = pixel.dc4 + } + } + } + //1011 + if(pixel.dc1 && !pixel.dc2 && pixel.dc3 && pixel.dc4) { + if(!pixel.changeTo) { + if(hhh < 1/3) { + pixel.changeTo = pixel.dc1 + } else if(hhh < 2/3) { + pixel.changeTo = pixel.dc3 + } else { + pixel.changeTo = pixel.dc4 + } + } + } + //1101 + if(pixel.dc1 && pixel.dc2 && !pixel.dc3 && pixel.dc4) { + if(!pixel.changeTo) { + if(hhh < 1/3) { + pixel.changeTo = pixel.dc1 + } else if(hhh < 2/3) { + pixel.changeTo = pixel.dc2 + } else { + pixel.changeTo = pixel.dc4 + } + } + } + //1110 + if(pixel.dc1 && pixel.dc2 && pixel.dc3 && !pixel.dc4) { + if(!pixel.changeTo) { + if(hhh < 1/3) { + pixel.changeTo = pixel.dc1 + } else if(hhh < 2/3) { + pixel.changeTo = pixel.dc2 + } else { + pixel.changeTo = pixel.dc3 + } + } + } + //choose from 4 + //1111 + if(pixel.dc1 && pixel.dc2 && pixel.dc3 && pixel.dc4) { + if(!pixel.changeTo) { + if(iii < 1/4) { + pixel.changeTo = pixel.dc1 + } else if(iii < 2/4) { + pixel.changeTo = pixel.dc2 + } else if(iii < 3/4) { + pixel.changeTo = pixel.dc3 + } else { + pixel.changeTo = pixel.dc4 + } + } + } + } else if(pixel.changeTo) { + if(pixel.dc1 || pixel.dc2 || pixel.dc3 || pixel.dc4) { + delete pixel.dc1; + delete pixel.dc2; + delete pixel.dc3; + delete pixel.dc4; + } + } + for(i = 0; i < adjacentCoords.length; i++) { + var pX = pixel.x; var pY = pixel.y; var oX = adjacentCoords[i][0]; var oY = adjacentCoords[i][1]; var nX = pX+oX; var nY = pY+oY; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY] + var newElement = newPixel.element; + if((elements[pixel.element].ignore ?? []).includes(newElement)) { return }; + if(newElement != pixel.element) { + changePixel(newPixel,pixel.changeTo) + }; + }; + }; + }, + category:"special", + ignore: ["wall","cloner","liquid_cloner","slow_cloner","void","clone_powder","floating_cloner","void_first","converter"], + hardness: 1, + }, + conveyorIgnoreList = ["conveyor_1","conveyor_2","wall"] + elements.conveyor_1 = { + color: "#7f7f7f", + tick: function(pixel) { + //top right + if (!isEmpty(pixel.x,pixel.y-1) && !outOfBounds(pixel.x,pixel.y-1)) { + if (pixelMap[pixel.x][pixel.y-1].element == "body") { + if(!isEmpty(pixel.x,pixel.y-2) && !outOfBounds(pixel.x,pixel.y-2)) { + if (pixelMap[pixel.x][pixel.y-2].element == "head") { + if(isEmpty(pixel.x+1,pixel.y-1) && isEmpty(pixel.x+1,pixel.y-2) && !outOfBounds(pixel.x+1,pixel.y-1) && !outOfBounds(pixel.x+1,pixel.y-2)) { + tryMove(pixelMap[pixel.x][pixel.y-1],pixel.x+1,pixel.y-1) + tryMove(pixelMap[pixel.x][pixel.y-2],pixel.x+1,pixel.y-2) + } + } + } else { + if(isEmpty(pixel.x+1,pixel.y-1) && !outOfBounds(pixel.x+1,pixel.y-1)) { + tryMove(pixelMap[pixel.x][pixel.y-1],pixel.x+1,pixel.y-1) + } + } + } else if(!conveyorIgnoreList.includes(pixelMap[pixel.x][pixel.y-1].element)) { + tryMove(pixelMap[pixel.x][pixel.y-1],pixel.x+1,pixel.y-1) + } + } + //right down + if (!isEmpty(pixel.x+1,pixel.y) && !outOfBounds(pixel.x+1,pixel.y)) { + if (pixelMap[pixel.x+1][pixel.y].element == "body") { + if(!isEmpty(pixel.x+1,pixel.y-1) && !outOfBounds(pixel.x+1,pixel.y-1)) { + if (pixelMap[pixel.x+1][pixel.y-1].element == "head") { + if(isEmpty(pixel.x+1,pixel.y+1) && !outOfBounds(pixel.x+1,pixel.y+1)) { + tryMove(pixelMap[pixel.x+1][pixel.y],pixel.x+1,pixel.y+1) + tryMove(pixelMap[pixel.x+1][pixel.y-1],pixel.x+1,pixel.y) + } + } + } else { + if(isEmpty(pixel.x+1,pixel.y+1) && !outOfBounds(pixel.x+1,pixel.y+1)) { + tryMove(pixelMap[pixel.x+1][pixel.y],pixel.x+1,pixel.y+1) + } + } + } else if(!conveyorIgnoreList.includes(pixelMap[pixel.x+1][pixel.y].element)) { + tryMove(pixelMap[pixel.x+1][pixel.y],pixel.x+1,pixel.y+1) + } + } + //bottom left + if (!isEmpty(pixel.x,pixel.y+1) && !outOfBounds(pixel.x,pixel.y+1)) { + if (pixelMap[pixel.x][pixel.y+1].element == "head") { + if(!isEmpty(pixel.x,pixel.y+2) && !outOfBounds(pixel.x,pixel.y+2)) { + if (pixelMap[pixel.x][pixel.y+2].element == "body") { + if(isEmpty(pixel.x-1,pixel.y+1) && isEmpty(pixel.x-1,pixel.y+2) && !outOfBounds(pixel.x-1,pixel.y+2) && !outOfBounds(pixel.x-1,pixel.y+2)) { + tryMove(pixelMap[pixel.x][pixel.y+1],pixel.x-1,pixel.y+1) + tryMove(pixelMap[pixel.x][pixel.y+2],pixel.x-1,pixel.y+2) + } + } + } else { + if(isEmpty(pixel.x-1,pixel.y+1) && !outOfBounds(pixel.x-1,pixel.y+1)) { + tryMove(pixelMap[pixel.x][pixel.y+1],pixel.x-1,pixel.y+1) + } + } + } else if(!conveyorIgnoreList.includes(pixelMap[pixel.x][pixel.y+1].element)) { + tryMove(pixelMap[pixel.x][pixel.y+1],pixel.x-1,pixel.y+1) + } + } + //left up + if (!isEmpty(pixel.x-1,pixel.y) && !outOfBounds(pixel.x-1,pixel.y)) { + if (pixelMap[pixel.x-1][pixel.y].element == "head") { + if(!isEmpty(pixel.x-1,pixel.y+1) && !outOfBounds(pixel.x-1,pixel.y+1)) { + if (pixelMap[pixel.x-1][pixel.y+1].element == "body") { + if(isEmpty(pixel.x-1,pixel.y-1) && !outOfBounds(pixel.x-1,pixel.y-1)) { + tryMove(pixelMap[pixel.x-1][pixel.y],pixel.x-1,pixel.y-1) + tryMove(pixelMap[pixel.x-1][pixel.y+1],pixel.x-1,pixel.y) + } + } + } else { + if(isEmpty(pixel.x-1,pixel.y-1) && !outOfBounds(pixel.x-1,pixel.y-1)) { + tryMove(pixelMap[pixel.x-1][pixel.y],pixel.x-1,pixel.y-1) + } + } + } else if(!conveyorIgnoreList.includes(pixelMap[pixel.x-1][pixel.y].element)) { + tryMove(pixelMap[pixel.x-1][pixel.y],pixel.x-1,pixel.y-1) + } + } + }, + category: "machines", + insulate: true, + state: "solid", + }, + elements.conveyor_2 = { + color: "#7f7f7f", + tick: function(pixel) { + //top left + if (!isEmpty(pixel.x,pixel.y-1) && !outOfBounds(pixel.x,pixel.y-1)) { + if (pixelMap[pixel.x][pixel.y-1].element == "body") { + if(!isEmpty(pixel.x,pixel.y-2) && !outOfBounds(pixel.x,pixel.y-2)) { + if (pixelMap[pixel.x][pixel.y-2].element == "head") { + if(isEmpty(pixel.x-1,pixel.y-1) && isEmpty(pixel.x-1,pixel.y-2) && !outOfBounds(pixel.x-1,pixel.y-1) && !outOfBounds(pixel.x-1,pixel.y-2)) { + tryMove(pixelMap[pixel.x][pixel.y-1],pixel.x-1,pixel.y-1) + tryMove(pixelMap[pixel.x][pixel.y-2],pixel.x-1,pixel.y-2) + } + } + } else { + if(isEmpty(pixel.x-1,pixel.y-1) && !outOfBounds(pixel.x-1,pixel.y-1)) { + tryMove(pixelMap[pixel.x][pixel.y-1],pixel.x-1,pixel.y-1) + } + } + } else if(!conveyorIgnoreList.includes(pixelMap[pixel.x][pixel.y-1].element)) { + tryMove(pixelMap[pixel.x][pixel.y-1],pixel.x-1,pixel.y-1) + } + } + //right up + if (!isEmpty(pixel.x+1,pixel.y) && !outOfBounds(pixel.x+1,pixel.y)) { + if (pixelMap[pixel.x+1][pixel.y].element == "head") { + if(!isEmpty(pixel.x+1,pixel.y+1) && !outOfBounds(pixel.x+1,pixel.y+1)) { + if (pixelMap[pixel.x+1][pixel.y+1].element == "body") { + if(isEmpty(pixel.x+1,pixel.y-1) && !outOfBounds(pixel.x+1,pixel.y-1)) { + tryMove(pixelMap[pixel.x+1][pixel.y],pixel.x+1,pixel.y-1) + tryMove(pixelMap[pixel.x+1][pixel.y+1],pixel.x+1,pixel.y) + } + } + } else { + if(isEmpty(pixel.x+1,pixel.y-1) && !outOfBounds(pixel.x+1,pixel.y-1)) { + tryMove(pixelMap[pixel.x+1][pixel.y],pixel.x+1,pixel.y-1) + } + } + } else if(!conveyorIgnoreList.includes(pixelMap[pixel.x+1][pixel.y].element)) { + tryMove(pixelMap[pixel.x+1][pixel.y],pixel.x+1,pixel.y-1) + } + } + //bottom right + if (!isEmpty(pixel.x,pixel.y+1) && !outOfBounds(pixel.x,pixel.y+1)) { + if (pixelMap[pixel.x][pixel.y+1].element == "head") { + if(!isEmpty(pixel.x,pixel.y+2) && !outOfBounds(pixel.x,pixel.y+2)) { + if (pixelMap[pixel.x][pixel.y+2].element == "body") { + if(isEmpty(pixel.x+1,pixel.y+1) && isEmpty(pixel.x+1,pixel.y+2) && !outOfBounds(pixel.x+1,pixel.y+2) && !outOfBounds(pixel.x+1,pixel.y+2)) { + tryMove(pixelMap[pixel.x][pixel.y+1],pixel.x+1,pixel.y+1) + tryMove(pixelMap[pixel.x][pixel.y+2],pixel.x+1,pixel.y+2) + } + } + } else { + if(isEmpty(pixel.x+1,pixel.y+1) && !outOfBounds(pixel.x+1,pixel.y+1)) { + tryMove(pixelMap[pixel.x][pixel.y+1],pixel.x+1,pixel.y+1) + } + } + } else if(!conveyorIgnoreList.includes(pixelMap[pixel.x][pixel.y+1].element)) { + tryMove(pixelMap[pixel.x][pixel.y+1],pixel.x+1,pixel.y+1) + } + } + //left down + if (!isEmpty(pixel.x-1,pixel.y) && !outOfBounds(pixel.x-1,pixel.y)) { + if (pixelMap[pixel.x-1][pixel.y].element == "body") { + if(!isEmpty(pixel.x-1,pixel.y-1) && !outOfBounds(pixel.x-1,pixel.y-1)) { + if (pixelMap[pixel.x-1][pixel.y-1].element == "head") { + if(isEmpty(pixel.x-1,pixel.y+1) && !outOfBounds(pixel.x-1,pixel.y+1)) { + tryMove(pixelMap[pixel.x-1][pixel.y],pixel.x-1,pixel.y+1) + tryMove(pixelMap[pixel.x-1][pixel.y-1],pixel.x-1,pixel.y) + } + } + } else { + if(isEmpty(pixel.x-1,pixel.y+1) && !outOfBounds(pixel.x-1,pixel.y+1)) { + tryMove(pixelMap[pixel.x-1][pixel.y],pixel.x-1,pixel.y+1) + } + } + } else if(!conveyorIgnoreList.includes(pixelMap[pixel.x-1][pixel.y].element)) { + tryMove(pixelMap[pixel.x-1][pixel.y],pixel.x-1,pixel.y+1) + } + } + }, + category: "machines", + insulate: true, + state: "solid", + }, + elements.vanishing_wall = { + behavior: behaviors.WALL, + color: "#8080b0", + colorObject: hexToRGB("#8080b0"), + density: 3333, + tick: function(pixel) { + pixelTick(pixel) + if(pixel.charge) { + if(!isEmpty(pixel.x,pixel.y)) { + deletePixel(pixel.x,pixel.y) + } + } + }, + category: "special", + state: "solid", + hardness: 1, + insulate: true, + conduct: 1, + extraInfo: "It disappears when charged.", + }, + elements.vanishing_steel = { + color: "#71797E", + behavior: behaviors.WALL, + tick: function(pixel) { + pixelTick(pixel); + if(pixel.charge) { + if(!isEmpty(pixel.x,pixel.y)) { + deletePixel(pixel.x,pixel.y); + }; + }; + }, + category: "solids", + state: "solid", + density: 7850, + conduct: 1, + hardness: 0.8, + }; + elements.polka_dotted_powder = { + color: [_cc.b.h,_cc.b.h,"#7f7f7f",_cc.w.h,_cc.w.h], + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 1400, + tick: function(pixel) { + if(pixel.y % 6 == 0) { + if(pixel.x % 6 == 0) { + pixel.color = _cc.w.r + } else { + if(!settings.bg || settings.bg == _cc.b.h) { + pixel.color = "rgb(15,15,15)" + } else { + pixel.color = _cc.b.r + } + } + } else if((pixel.y + 3) % 6 == 0) { + if((pixel.x + 3) % 6 == 0) { + pixel.color = _cc.w.r + } else { + if(!settings.bg || settings.bg == _cc.b.h) { + pixel.color = "rgb(15,15,15)" + } else { + pixel.color = _cc.b.r + } + } + } else { + if(!settings.bg || settings.bg == _cc.b.h) { + pixel.color = "rgb(15,15,15)" + } else { + pixel.color = _cc.b.r + } + } + }, + tempHigh: 800, + }, + elements.molten_polka_dotted_powder = { + color: ["#ff7f00","#ff7f00","#ff9f00","#ffbf00","#ffbf00"], + density: 1100, + tick: function(pixel) { + if(pixel.y % 6 == 0) { + if(pixel.x % 6 == 0) { + pixel.color = "rgb(255,191,0)" + } else { + if(!settings.bg || settings.bg == "#ff7f00") { + pixel.color = "rgb(255,143,16)" + } else { + pixel.color = "rgb(255,127,16)" + } + } + } else if((pixel.y + 3) % 6 == 0) { + if((pixel.x + 3) % 6 == 0) { + pixel.color = "rgb(255,191,0)" + } else { + if(!settings.bg || settings.bg == "#ff7f00") { + pixel.color = "rgb(255,143,16)" + } else { + pixel.color = "rgb(255,127,16)" + } + } + } else { + if(!settings.bg || settings.bg == "#ff7f00") { + pixel.color = "rgb(255,143,16)" + } else { + pixel.color = "rgb(255,127,16)" + } + } + }, + temp: 850, + tempLow: 800, + stateLow: "polka_dotted_powder", + tempHigh: 2000, + stateHigh: "vaporized_polka_dotted_powder", + viscosity: 6, + hidden: true, + }, + elements.vaporized_polka_dotted_powder = { + color: ["#ffdf7f","#ffdf7f","#ffefbf",_cc.w.h,_cc.w.h], + behavior: behaviors.GAS, + category: "gases", + state: "gas", + density: 550, + tick: function(pixel) { + if(pixel.y % 6 == 0) { + if(pixel.x % 6 == 0) { + pixel.color = _cc.w.r + } else { + if(!settings.bg || settings.bg == "#ffdf7f") { + pixel.color = "rgb(255,233,137)" + } else { + pixel.color = "rgb(255,223,127)" + } + } + } else if((pixel.y + 3) % 6 == 0) { + if((pixel.x + 3) % 6 == 0) { + pixel.color = _cc.w.r + } else { + if(!settings.bg || settings.bg == "#ffdf7f") { + pixel.color = "rgb(255,143,16)" + } else { + pixel.color = "rgb(255,233,137)" + } + } + } else { + if(!settings.bg || settings.bg == "#ffdf7f") { + pixel.color = "rgb(255,233,137)" + } else { + pixel.color = "rgb(255,223,127)" + } + } + }, + temp: 2200, + tempLow: 2000, + stateLow: "molten_polka_dotted_powder", + tempHigh: 8000, + stateHigh: "ionized_polka_dotted_powder", + hidden: true, + }, + elements.ionized_polka_dotted_powder = { + color: ["#fffff0","#fffff0","#fffff7",_cc.w.h,_cc.w.h], + behavior: [ + "M2 AND CR:plasma%0.3|M1|M2 AND CR:plasma%0.3", + "M1|XX|M1", + "M2 AND CR:plasma%0.3|M1|M2 AND CR:plasma%0.3", + ], + category: "gases", + state: "gas", + density: 0.02, + tick: function(pixel) { + if(pixel.y % 6 == 0) { + if(pixel.x % 6 == 0) { + pixel.color = _cc.w.r + } else { + if(!settings.bg || settings.bg == "#fffff0") { + pixel.color = "rgb(255,255,247)" + } else { + pixel.color = "rgb(255,255,240)" + } + } + } else if((pixel.y + 3) % 6 == 0) { + if((pixel.x + 3) % 6 == 0) { + pixel.color = _cc.w.r + } else { + if(!settings.bg || settings.bg == "#fffff0") { + pixel.color = "rgb(255,255,247)" + } else { + pixel.color = "rgb(255,255,240)" + } + } + } else { + if(!settings.bg || settings.bg == "#fffff0") { + pixel.color = "rgb(255,255,247)" + } else { + pixel.color = "rgb(255,255,240)" + } + } + }, + temp: 8500, + tempLow: 8000, + stateLow: "vaporized_polka_dotted_powder", + hidden: true, + }, + elements.hdet = { + name: "heat- dependent explosion text", + color: "#33aa44", + behavior: behaviors.POWDER, + tick: function(pixel) { + if(pixel.charge > 0) { + var temp = pixel.temp + if(temp < 0) { + temp = 0 + } + if(temp >= 0 && temp < 1) { + temp = 1 + } + if(temp > 56000) { + temp = 56000 + } + if(isNaN(temp) || isNaN(pixel.temp)) { + temp = 20 + pixel.temp = 20 + } + var r = ((Math.sqrt((Math.log(temp)/Math.log(20)))*(temp**0.5))/(6000**0.126284318))/2 + explodeAt(pixel.x,pixel.y,Math.floor(r)) + if(temp > 200) { + if(Math.random() < (Math.log(temp)/Math.log(56000))**9) { + pixel.charge = 1 + if(pixel.chargeCD) { + delete pixel.chargeCD + } + } + } + if(isNaN(temp) || isNaN(pixel.temp)) { + temp = 20 + pixel.temp = 20 + } + } + }, + density: 1200, + conduct: 0.5, + state: "solid", + category: "special" + }, + function randInt(max) { + return Math.floor(Math.random() * (max + 1)) + } + function randIntR(min,max) { + if(min > max) { + var temp = max; //the need of a temporary space has always annoyed me + max = min; + min = temp; + }; + return Math.floor(Math.random() * (max - min + 1)) + min + }; + elements.lower_color_copy = { + behavior: behaviors.POWDER, + tick: function(pixel) { + if(!isEmpty(pixel.x,pixel.y+1,true)) { + pixel.color = pixelMap[pixel.x][pixel.y+1].color; + } else { + if(settings.bg) { + pixel.color = settings.bg; + } else { + pixel.color = _cc.b.h; + } + } + }, + color: [_cc.b.h, _cc.b.h, _cc.b.h, _cc.b.h, _cc.b.h, _cc.b.h, _cc.b.h, "#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#007FFF", "#0000FF", "#7F00FF"], + density: 1250, + breakInto: ["metal_scrap", "glass_shard"], + hardness: 0.7, + } + elements.brimstone_slag = { + color: ["#745B57","#534D4A","#463F53","#51113E","#6D283B","#BC4949","#EA9B4E"], + properties: { + needsOffset: true + }, + colorPattern: [ + "FGGFGFGGF", + "FFGFGFFGG", + "DEEDEEDEE", + "DEDEEEEED", + "BDCBACDCB", + "BCADCDDBB", + "ABBCAABCC" + ], + colorKey: { + "A": "#745B57", + "B": "#534D4A", + "C": "#463F53", + "D": "#51113E", + "E": "#6D283B", + "F": "#BC4949", + "G": "#EA9B4E", + }, + behavior: behaviors.POWDER, + hardness: 0.5, + enableOffsetsOnTextureColors: true, + breakInto: ["slag","sulfur"], + tempHigh: 1780, + state: "solid", + category: "solids", + tick: function(pixel) { + if(pixel.needsOffset) { + var offset = (elements[pixel.element].maxColorOffset ?? 15); + offset = randomIntegerFromZeroToValue(offset) * (Math.random() < 0.5 ? -1 : 1); + pixel.color = convertColorFormats(pixel.color,"json"); + for(var k in pixel.color) { pixel.color[k] += offset }; + pixel.color = convertColorFormats(pixel.color,"rgb"); + delete pixel.needsOffset; + return + } + } + }; + elements.molten_slag ??= {}; + elements.molten_slag.reactions ??= {}; + elements.molten_slag.reactions.sulfur = elements.molten_slag.reactions.molten_sulfur = elements.molten_slag.reactions.sulfur_gas = elements.molten_sulfur.reactions.slag = elements.sulfur.reactions.molten_slag = { elem1: "brimstone_slag", elem2: null }; + elements.slag.tempHigh = 1780; + var temp = "firesea,lektre,concoction,mistake,unstable_mistake,toxic_mistake".split(","); + for(var i = 0; i < temp.length; i++) { + temp[i].state = "liquid"; + temp[i].category = "liquids" + }; + elements.head.cutInto = ["bone","meat","blood"]; + elements.body.cutInto = ["bone","meat","meat","blood","blood"]; + elements.wood.cutInto = ["wood_plank","wood_plank","wood_plank","wood_plank","wood_plank","wood_plank","wood_plank","wood_plank","sawdust"]; + elements.fish.breakInto = ["meat","meat","bone","blood"]; + elements.fish.cutInto = ["meat","meat","bone","blood"]; + elements.bladesea = { + color: ["#959696", "#b1b3b3", "#d4d4d4", "#bfbdbd"], + state: "liquid", + viscosity: 5, + behavior: behaviors.LIQUID, + tick: function(pixel) { //Code from R74n/vanilla "smash" tool + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < adjacentCoords.length; i++) { + var oX = adjacentCoords[i][0]; + var oY = adjacentCoords[i][1]; + var fX = pX+oX; + var fY = pY+oY; + if(!isEmpty(fX,fY,true)) { + var checkPixel = pixelMap[fX][fY]; + var otherElement = elements[checkPixel.element]; + if (typeof(otherElement.cutInto) !== "undefined") { + var hardness = otherElement.hardness ?? 0; + if (Math.random() < (1 - hardness)) { + var cutInto = otherElement.cutInto; + // if breakInto is an array, pick one + if (Array.isArray(cutInto)) { + cutInto = randomChoice(cutInto); + }; + changePixel(checkPixel,cutInto); + } + } + }; + }; + }, + density: 200, + category: "liquids", + hidden: true, + reactions: { + "concoction": { "elem1": "bladesea", "elem2": "bladesea", "chance":0.005}, + }, + }; + function newLegacyFnmDye(colorName,hexColor) { + if(!(hexColor.startsWith("#"))) { hexColor = "#" + hexColor }; + colorName = colorName.toLowerCase(); + var key = `${colorName}_dye`; + var name = `${colorName.replaceAll("_"," ")} dye`; + var pixelColor = changeLightness(hexColor,0.73333333333333,"multiply","hex",null,false); + elements[key] = { + "name": name, + "color": pixelColor, + "state": "solid", + "behavior": [ + "XX|XX|XX", + `CC:${hexColor}|CC:${pixelColor}|CC:${hexColor}`, + `M2 AND CC:${hexColor}|M1 AND CC:${hexColor}|M2 AND CC:${hexColor}` + ], + "density": 100, + "category": "dyes" + }; + eLists.DYE.push(key); + elements.concoction.reactions[key] = { "elem1": "mistake", "elem2": null }; + return elements[key] + }; + var dyeColors = [ + ["rose", "#FF0067"], + ["orange", "#FF7F00"], + ["pink", "#FF7FFF"], + ["purple", "#C700CF"], + ["burgundy", "#9F005F"], + ["peach", "#ffbf7f"], + ["mint", "#4df0a9"], + ["gray", "#7F7F7F"], + ["lime", "#7FFF00"], + ["black", _cc.b.h], + ["white", _cc.w.h], + ["sky_blue", "#99d1f2"] + ]; + for(var i = 0; i < dyeColors.length; i++) { + newLegacyFnmDye(dyeColors[i][0],dyeColors[i][1]) + }; + eLists.LED = ["led_r","led_g","led_b"]; + function newLED(abbrev,hexColor,baseColorOverrideHex=null) { + if(!(hexColor.startsWith("#"))) { hexColor = "#" + hexColor }; + if(baseColorOverrideHex && !(baseColorOverrideHex.startsWith("#"))) { baseColorOverrideHex = "#" + baseColorOverrideHex }; + abbrev = abbrev.toLowerCase(); + var key = `led_${abbrev}`; + var pixelColor = baseColorOverrideHex ?? changeLightness(hexColor,0x66/0xff,"multiply","hex",null,false); + elements[key] = { + behavior: behaviors.WALL, + reactions: { + "light": {"charge1":1}, + "liquid_light": {"charge1":1}, + }, + color: pixelColor, + colorOn: hexColor, + category: "machines", + tempHigh: 1500, + stateHigh: ["molten_glass","molten_glass","molten_glass","molten_gallium"], + conduct: 1, + breakInto: "glass_shard" + }; + eLists.LED.push(key) + }; + var ledColors = [ + ["c", "#00FFFF"], //cyan + ["y", "#FFFF00"], //yellow + ["m", "#FF00FF"], //magenta (cursed) + ["p", "#AB00C2"], //purple (cursed) + ["v", "#7700FF"], //violet + ["w", _cc.w.h], //white (cursed) + ["gy", "#7F7F7F"], //gray (more cursed) + ["bl", _cc.b.h, "#2b2b2b"], //black (super cursed) + ["o", "#FF7F00"], //orange + ["a", "#FFBF00"], //amber + ["l", "#7FFF00"], //lime + ["rs", "#FF0067"], //rose (cursed) + ["pk", "#FF7FFF"], //pink (cursed) + ["bg", "#9F005F"], //burgundy (cursed) + ["pc", "#ffbf7f"], //peach + ["mg", "#4df0a9"], //mint green + ["sb", "#99d1f2"] //sky blue (cursed) + ]; + for(var i = 0; i < ledColors.length; i++) { + newLED(...ledColors[i]); + }; + for(var i = 0; i < eLists.LED.length; i++) { + var key = eLists.LED[i]; + elements.malware.reactions[key] = { elem2:eLists.LED, chance:0.01 } + }; + + function heatNeighbors(pixel,temp,trueIfMooreFalseIfNeumann=false) { + var neighborOffsets = trueIfMooreFalseIfNeumann ? squareCoords : adjacentCoords; + var pX = pixel.x; + var pY = pixel.y; + for(var i = 0; i < neighborOffsets.length; i++) { + var offsets = neighborOffsets[i]; + var newPixel = pixelMap[pX + offsets[0]]?.[pY + offsets[1]]; + if(!newPixel) { continue }; + newPixel["temp"] += temp; + pixelTempCheck(newPixel) + } + }; + + function getNeighborCount(pixel,useVonNeumannGrid=false) { + var x = pixel.x; + var y = pixel.y; + return ( + 0 + + (!isEmpty(x-1,y,true)) + + (!isEmpty(x+1,y,true)) + + (!isEmpty(x,y-1,true)) + + (!isEmpty(x,y+1,true)) + ) + ( + (!useVonNeumannGrid) && ( + (!isEmpty(x-1,y-1,true)) + + (!isEmpty(x+1,y+1,true)) + + (!isEmpty(x+1,y-1,true)) + + (!isEmpty(x-1,y+1,true)) + ) + ); + }; + + elements.amba_black_hole = { + color: _cc.b.h, + maxColorOffset: 0, + excludeRandom: true, + insulate: true, + onSelect: function() { + showPropertySetter(); + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","blackHoleRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.blackHoleRange; + }; + if(p0h) { + p0h.innerText = "Radius"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter + }, + hoverStat: (pixel => `r = ${(pixel.range?.toString() ?? "??")}`), + tick: function(pixel) { + pixel.color = _cc.b.r; + //if(pixelTicks == pixel.start) { console.log("spawn range",ambaPlaceProperties.blackHoleRange) }; + pixel.range ??= ambaPlaceProperties?.blackHoleRange ?? 15; + //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++) { + var newPixel = pixelMap[targets[i][0]]?.[targets[i][1]]; + if ((!newPixel) || newPixel.del) { continue }; + if(elements[pixel.element].ignore && elements[pixel.element].ignore.includes(newPixel.element)) { continue }; + newPixel.tempDrag = pixelTicks + 1; + var [mX, mY] = [pixel.x, pixel.y]; + var distanceComplement = (range / 2) - pyth(mX,mY,newPixel.x,newPixel.y); + var distanceProportion = 0.2 + (distanceComplement / (range / 2)); + var distanceModifier = distanceProportion ** 2; + var pullCount = (4 * distanceModifier) * (commonMovableCriteria(newPixel.element) ? 1 : 0.8) * (1 - (((getNeighborCount(newPixel)) / (8.1 + ((elements[newPixel.element].hardness ?? 0) * 3))))); + var pullCountIntegerPart = Math.floor(pullCount); + var pullCountFractionalPart = pullCount % 1; + var truePullCount = Math.min(3,pullCountIntegerPart + (Math.random() < pullCountFractionalPart)); + for(var j = 0; j < truePullCount; j++) { + if((pullCountIntegerPart >= 1) && (Math.random() < distanceProportion)) { tryBreak(newPixel) }; + var x = newPixel.x; + var y = newPixel.y; + var empty = checkForEmptyPixels(x, y); + let bestVal = Math.sqrt(Math.pow(mX - x, 2) + Math.pow(mY - y, 2)); + let best = null; + for (const pixelPair of empty) { + const x_ = x + pixelPair[0]; + const y_ = y + pixelPair[1]; + const c = Math.sqrt(Math.pow(mX - x_, 2) + Math.pow(mY - y_, 2)); + if (c < bestVal) { + bestVal = c; + best = pixelPair; + } + } + if (best) { + newPixel.vx ??= 0; + newPixel.vy ??= 0; + var _vx = (truePullCount * (best[0])); _vx += Math.sign(_vx); + var _vy = (truePullCount * (best[1])); _vy += Math.sign(_vy); + newPixel.vx += _vx; + newPixel.vy += _vy; + tryMove(newPixel, x + best[0], y + best[1], undefined, true); + if(haseuliteSpreadWhitelist.includes(newPixel.element)) { newPixel.value += ((15 + (distanceComplement / (distanceProportion ** 2))) * 3) }; + var heat = (20 * pullCount) * getNeighborCount(newPixel); + heatNeighbors(newPixel,heat); + pixel.temp += heat; + pixelTempCheck(pixel); + } + }; + 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) && (newPixel !== pixel)) { + //console.log("adding range on tick",pixelTicks); + pixel.range ??= (ambaPlaceProperties?.blackHoleRange ?? 15); + var rangeChange = (newPixel.range ?? (ambaPlaceProperties?.blackHoleRange ?? 15)); + switch(newPixel.element) { + case "amba_black_hole": + //console.log("range add",rangeChange); + pixel.range += rangeChange; + //console.log("new range",pixel.range); + break; + case "amba_white_hole": + //console.log("range remove",rangeChange); + pixel.range -= rangeChange; + //console.log("new range",pixel.range); + break; + } + //console.log("new range:",pixel.range); + newPixel.range = 0; + } else { + deletePixel(newPixel.x,newPixel.y); + continue + } + } + } + }, + state: undefined, + density: 1797.69313486e305, //about as close to Infinity as we can serializably get + category: "special", + hardness: 1, + maxSize: 1, + ignore: ["wall"].concat(eLists.CLONERS) + }; + + elements.amba_white_hole = { + color: _cc.w.h, + maxColorOffset: 0, + excludeRandom: true, + insulate: true, + onSelect: function() { + showPropertySetter(); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","whiteHoleRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.whiteHoleRange; + }; + if(p0h) { + p0h.innerText = "Radius"; + }; + }, + onUnselect: hidePropertySetter, + hoverStat: (pixel => `r = ${(pixel.range?.toString() ?? "??")}`), + tick: function(pixel) { + pixel.color = _cc.w.r; + pixel.range ??= (ambaPlaceProperties?.whiteHoleRange ?? 15); + if(pixel.range <= 0) { deletePixel(pixel.x,pixel.y); return }; + var range = (pixel.range ?? (ambaPlaceProperties?.whiteHoleRange ?? 15)) * 2; + var targets = mouseLikeRange(pixel.x,pixel.y,range,"circle",true); + shuffleArray(targets); + for (var i = 0; i < targets.length; i++) { + var newPixel = pixelMap[targets[i][0]]?.[targets[i][1]]; + if ((!newPixel) || newPixel.del) { continue }; + if(((newPixel.element == pixel.element) || (elements[pixel.element].ignore && elements[pixel.element].ignore.includes(newPixel.element))) || ((newPixel.x == pixel.x) && (newPixel.y == pixel.y))) { continue }; + newPixel.tempDrag = pixelTicks + 1; + var [mX, mY] = [pixel.x, pixel.y]; + var distanceComplement = (range / 2) - pyth(mX,mY,newPixel.x,newPixel.y); + var distanceProportion = 0.2 + (distanceComplement / (range / 2)); + var distanceModifier = distanceProportion ** 2; + var pullCount = (4 * distanceModifier) * (commonMovableCriteria(newPixel.element) ? 1 : 0.8); + var pullCountIntegerPart = Math.floor(pullCount); + var pullCountFractionalPart = pullCount % 1; + var truePullCount = Math.min(3,pullCountIntegerPart + (Math.random() < pullCountFractionalPart)); + for(var j = 0; j < truePullCount; j++) { + if((pullCountIntegerPart >= 1) && (Math.random() < pullCount / 3)) { tryBreak(newPixel) }; + var x = newPixel.x; + var y = newPixel.y; + var empty = checkForEmptyPixels(x, y); + let bestVal = Math.sqrt(Math.pow(mX - x, 2) + Math.pow(mY - y, 2)); + let best = null; + for (const pixelPair of empty) { + const x_ = x + pixelPair[0]; + const y_ = y + pixelPair[1]; + const c = Math.sqrt(Math.pow(mX - x_, 2) + Math.pow(mY - y_, 2)); + if (c < bestVal) { + bestVal = c; + best = pixelPair; + } + } + if (best) { + var destCoords = [x - best[0], y - best[1]]; + newPixel.vx ??= 0; + newPixel.vy ??= 0; + var _vx = -(truePullCount * (best[0])); _vx += Math.sign(_vx); + var _vy = -(truePullCount * (best[1])); _vy += Math.sign(_vy); + newPixel.vx += _vx; + newPixel.vy += _vy; + var moveResult = tryMoveAndReturnBlockingPixel(newPixel, destCoords[0], destCoords[1], undefined, true); + if((moveResult !== true) && !(outOfBounds(...destCoords))) { + swapPixels(newPixel,moveResult); + tryMove(newPixel, destCoords[0] - best[0], destCoords[1] - best[1], undefined, true) + }; + heatNeighbors(newPixel,20); + var a0 = settings.abszero ?? -273.15; + if(pixel.temp >= a0) { + if(pixel.temp <= (a0 + 20)) { + pixel.temp = a0 + } else { + pixel.temp -= 20 + } + } + } + } + } + }, + state: undefined, + density: -(1797.69313486e305), //about as close to -Infinity as we can serializably get + category: "special", + hardness: 1, + 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 + }; + elements.concoction.reactions.static = { //spread + elem1: "static", elem2: "static" + }; + elements.concoction.state = "liquid"; + elements.static.reactions ??= {}; elements.static.reactions.concoction = { "elem1": "static", "elem2": "static", "chance":0.005}, + /*function isRed(colorIn) { + var color = colorToHsl(colorIn,"json"); + var modularHue = color.h % 360; + if (!((modularHue >= -10 && modularHue <= 10) || modularHue >= 350)) { return false }; + if (color.s < 55) { return false }; + if (color.l < 40 || color.l > 60) { return false }; + return true + }; + function isWhite(colorIn) { + var color = colorToHsl(colorIn,"json"); + return color.s <= 15 && color.l >= 85 + };*/ + elements.rainbow.reactions ??= {}; + elements.rainbow.reactions.fire = { elem1: "fireshimmer", chance: 0.1 }; + elements.rainbow.reactions.plasma = { elem1: "plasmashimmer", chance: 0.1 }; + elements.rainbow.insulate = false; + elements.rainbow.burnInto = "fireshimmer"; + elements.rainbow.burn = 0.1; + elements.rainbow.burnTime = 150; + elements.rainbow.burnTempChange = 0.1; + elements.rainbow.reactions.bleach = { elem1: "pastel_rainbow" }; + elements.rainbow.reactions.ink = { elem1: "dark_rainbow" }; + elements.rainbow.reactions.coal = { elem1: "dark_rainbow" }; + elements.rainbow.reactions.charcoal = { elem1: "dark_rainbow" }; + elements.rainbow.reactions.coal_dust = { elem1: "dark_rainbow" }; + elements.rainbow.reactions.malware = { elem1: "glitchy_rainbow" }; + elements.rainbow.reactions.ruby = { elem1: "rubyshimmer" }; + elements.rainbow.reactions.molten_ruby = { elem1: "rubyshimmer" }; + elements.rainbow.reactions.topaz = { elem1: "topazshimmer" }; + elements.rainbow.reactions.bee = { elem1: "beeshimmer" }; + elements.rainbow.reactions.sap = { elem1: "ambershimmer" }; + elements.rainbow.reactions.amber = { elem1: "ambershimmer" }; + elements.rainbow.reactions.emerald = { elem1: "emeraldshimmer" }; + elements.rainbow.reactions.molten_emerald = { elem1: "emeraldshimmer" }; + elements.rainbow.reactions.cold_fire = { elem1: "iceshimmer" }; + elements.rainbow.reactions.sapphire = { elem1: "sapphireshimmer" }; + elements.rainbow.reactions.molten_sapphire = { elem1: "sapphireshimmer" }; + elements.rainbow.reactions.blue_dust = { elem1: "sapphireshimmer" }; + elements.rainbow.reactions.amethyst = { elem1: "amethystshimmer" }; + elements.rainbow.reactions.molten_amethyst = { elem1: "amethystshimmer" }; + elements.rainbow.reactions.spinel = { elem1: "spinelshimmer" }; + elements.rainbow.reactions.molten_spinel = { elem1: "spinelshimmer" }; + elements.rainbow.reactions.mullite = { elem1: "mulliteshimmer" }; + elements.rainbow.reactions.molten_mullite = { elem1: "mulliteshimmer" }; + elements.rainbow.reactions.dantite = { elem1: "dantiteshimmer" }; + elements.rainbow.reactions.molten_dantite = { elem1: "dantiteshimmer" }; + elements.rainbow.reactions.turquoise_dust = { elem1: "turquoiseshimmer" }; + elements.rainbow.reactions.diabaline = { elem1: "diabalineshimmer" }; + elements.rainbow.reactions.gold_coin = { elem1: "goldshimmer" }; + elements.rainbow.reactions.gold_scrap = { elem1: "goldshimmer" }; + elements.rainbow.reactions.gold = { elem1: "goldshimmer" }; + elements.rainbow.reactions.molten_gold = { elem1: "goldshimmer" }; + elements.rainbow.reactions.onyx = { elem1: "onyxshimmer" }; + elements.rainbow.reactions.opal = { elem1: "opalshimmer" }; + elements.rainbow.reactions.jadeite = { elem1: "jadeshimmer" }; + elements.rainbow.reactions.dirt = { elem1: "earthshimmer" }; + elements.rainbow.reactions.lead_scrap = { elem1: "leadshimmer" }; + elements.rainbow.reactions.lead = { elem1: "leadshimmer" }; + elements.rainbow.reactions.molten_lead = { elem1: "leadshimmer" }; + elements.rainbow.reactions.water = { elem1: "seashimmer" }; + elements.rainbow.reactions.ultramafic_magma = { elem1: "lavashimmer" }; + elements.rainbow.reactions.magma = { elem1: "lavashimmer" }; + elements.rainbow.reactions.intermediate_magma = { elem1: "lavashimmer" }; + elements.rainbow.reactions.intermediate_felsic_magma = { elem1: "lavashimmer" }; + elements.rainbow.reactions.felsic_magma = { elem1: "lavashimmer" }; + elements.rainbow.reactions.rainbow_magma = { elem1: "lavashimmer" }; + elements.rainbow.reactions.nellish_magma = { elem1: "lavashimmer" }; + elements.rainbow.reactions.crimson_magma = { elem1: "lavashimmer" }; + elements.rainbow.reactions.uranium = { elem1: "radioshimmer" }; + elements.rainbow.reactions.ash = { elem1: "ashenshimmer" }; + elements.rainbow.reactions.steel_scrap = { elem1: "steelshimmer" }; + elements.rainbow.reactions.molten_steel = { elem1: "steelshimmer" }; + elements.rainbow.reactions.steel = { elem1: "steelshimmer" }; + elements.rainbow.reactions.iron_scrap = { elem1: "steelshimmer" }; + elements.rainbow.reactions.molten_iron = { elem1: "steelshimmer" }; + elements.rainbow.reactions.iron = { elem1: "steelshimmer" }; + elements.rainbow.reactions.smoke = { elem1: "smokeshimmer" }; + elements.rainbow.reactions.smog = { elem1: "smokeshimmer" }; + elements.rainbow.reactions.cloud = { elem1: "cloudshimmer" }; + elements.rainbow.reactions.fog = { elem1: "cloudshimmer" }; + elements.rainbow.reactions.steam = { elem1: "cloudshimmer" }; + elements.rainbow.reactions.rain_cloud = { elem1: "cloudshimmer" }; + elements.rainbow.reactions.snow_cloud = { elem1: "cloudshimmer" }; + elements.rainbow.reactions.hail_cloud = { elem1: "cloudshimmer" }; + elements.rainbow.reactions.thunder_cloud = { elem1: "cloudshimmer" }; + elements.rainbow.reactions.fluorine = { elem1: "fluoroshimmer" }; + elements.rainbow.reactions.liquid_fluorine = { elem1: "fluoroshimmer" }; + elements.rainbow.reactions.fluorine_ice = { elem1: "fluoroshimmer" }; + elements.rainbow.reactions.chlorine = { elem1: "chloroshimmer" }; + elements.rainbow.reactions.liquid_chlorine = { elem1: "chloroshimmer" }; + elements.rainbow.reactions.chlorine_ice = { elem1: "chloroshimmer" }; + elements.rainbow.reactions.chlorine_snow = { elem1: "chloroshimmer" }; + elements.rainbow.reactions.bromine = { elem1: "bromoshimmer" }; + elements.rainbow.reactions.liquid_bromine = { elem1: "bromoshimmer" }; + elements.rainbow.reactions.bromine_snow = { elem1: "bromoshimmer" }; + elements.rainbow.reactions.bromine_ice = { elem1: "bromoshimmer" }; + elements.rainbow.reactions.iodine = { elem1: "iodoshimmer" }; + elements.rainbow.reactions.molten_iodine = { elem1: "iodoshimmer" }; + elements.rainbow.reactions.iodine_gas = { elem1: "iodoshimmer" }; + runAfterLoad(function() { elements.astatine.tempHigh = 302 }); + elements.molten_astatine ??= {}; elements.molten_astatine.tempHigh = 337; + elements.rainbow.reactions.astatine = { elem1: "astatoshimmer" }; + elements.rainbow.reactions.molten_astatine = { elem1: "astatoshimmer" }; + elements.rainbow.reactions.astatine_gas = { elem1: "astatoshimmer" }; + elements.rainbow.reactions.blood = { elem1: "bloodshimmer" }; + elements.rainbow.reactions.blood_snow = { elem1: "bloodshimmer" }; + elements.rainbow.reactions.blood_ice = { elem1: "bloodshimmer" }; + elements.rainbow.reactions.salt = { elem1: "saltshimmer" }; + elements.rainbow.reactions.molten_salt = { elem1: "saltshimmer" }; + elements.rainbow.reactions.sand = { elem1: "sandshimmer" }; + elements.rainbow.reactions.hot_sand = { elem1: "sandshimmer" }; + elements.rainbow.reactions.sandy_water = { elem1: "sandshimmer" }; + elements.rainbow.reactions.sand_sediment = { elem1: "sandshimmer" }; + elements.rainbow.reactions.sandstone = { elem1: "sandshimmer" }; + elements.rainbow.reactions.ichor = { elem1: "ichorshimmer" }; + elements.rainbow.reactions.quark_matter = { elem1: "quarkshimmer" }; + elements.rainbow.reactions.heejinite = { elem1: "heejinshimmer" }; + elements.rainbow.reactions.heejinite_powder = { elem1: "heejinshimmer" }; + elements.rainbow.reactions.molten_heejinite = { elem1: "heejinshimmer" }; + elements.rainbow.reactions.heejinite_gas = { elem1: "heejinshimmer" }; + /*elements.rainbow.reactions.dye = { + func: function(pixel,otherPixel) { + //The dye reactant is the otherPixel + //The rainbow is the pixel + if( + (typeof(otherPixel) == "undefined") + || isEmpty(otherPixel.x,otherPixel.y,true) + || typeof(pixelMap[otherPixel.x][otherPixel.y]) == "undefined" + || !(currentPixels.includes(pixelMap[otherPixel.x][otherPixel.y])) + ) { return }; + var dyeColorHSL = colorToHsl(otherPixel.color,"json"); + if(isRed(dyeColorHSL)) { + changePixel(pixel,"red_test"); + } else if(isWhite(dyeColorHSL)) { + changePixel(pixel,"rainbow_alt_test"); + }; + return true + } + };*/ + elements.rainbow.tick = function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks+pixel.x+pixel.y; + var r = Math.floor(127*(1-Math.cos(t*Math.PI/90))); + var g = Math.floor(127*(1-Math.cos(t*Math.PI/90+2*Math.PI/3))); + var b = Math.floor(127*(1-Math.cos(t*Math.PI/90+4*Math.PI/3))); + var baseColor = "rgb("+r+","+g+","+b+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //80% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.8); + //30% dye color, 70% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.7); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }; + elements.rainbow.state = "solid"; + elements.rainbow.reactions.dye = { + func: function(pixel,otherPixel) { + //The dye reactant is the otherPixel + //The rainbow is the pixel + if( + (typeof(otherPixel) == "undefined") + || isEmpty(otherPixel.x,otherPixel.y,true) + || typeof(pixelMap[otherPixel.x][otherPixel.y]) == "undefined" + || !(currentPixels.includes(pixelMap[otherPixel.x][otherPixel.y])) + ) { return false }; + if(pixel.dyeColor == otherPixel.color) { + return false + } else { + pixel.dyeColor = otherPixel.color; + return true + } + } + }; + rainbowMathlet = function(t,scale,offset) { return Math.cos(t*Math.PI/scale+offset*Math.PI/3) }; + elements.dark_rainbow = { + color: [_cc.b.h,"#ff0000",_cc.b.h,"#ff8800",_cc.b.h,"#ffff00",_cc.b.h,"#00ff00",_cc.b.h,"#00ffff",_cc.b.h,"#0000ff",_cc.b.h,"#ff00ff"], + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks+pixel.x+pixel.y; + var l = 50 * (Math.max(0,rainbowMathlet(t,17.1428571,0)) ** 4); + var h = (((t * 1.05) - 2) % 360); + var color = {h: h, s: 100, l: l}; + var baseColor = convertHslObjects(color,"rgb"); + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //80% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.8); + //30% dye color, 70% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.7); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.fireshimmer = { + color: ["#ff0000","#ff8800","#ffff00","#ff8800","#ff0000"], + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var g = Math.floor(127*((Math.max(0,1-(rainbowMathlet(t,25,0)))) ** 1.1)); + baseColor = "rgb(255,"+g+",0)"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + category: "special", + reactions: { + dye: elements.rainbow.reactions.dye, + plasma: {elem1: "plasmashimmer", tempMin: 10000} + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + movable: false, + }; + elements.plasmashimmer = { + color: ["#8800ff","#f2f2f2","#8800ff","#f2f2f2"], + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var value = Math.floor(127*((Math.max(0,1-(rainbowMathlet(t,25,0)))) ** 1.1)); + baseColor = "rgb(" + [Math.round(127 + (value/2)), value, 255].join(",") + ")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + category: "special", + reactions: { + dye: elements.rainbow.reactions.dye + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + movable: false, + }; + elements.glitchy_rainbow = { + color: ["#ff0000","#ff8800","#ffff00","#ff8800","#ff0000"], + tick: function(pixel) { + if(Math.random() < 0.25) { return }; + var dyeColor = pixel.dyeColor ?? null; + var t = ( + (Math.floor(pixelTicks / 3) * 3) + + (Math.floor(pixel.x / 3) * 3) + + pixel.y + + (Math.floor(Math.random() * 5)-2) + ); + var r = Math.floor(127*(1-Math.cos(t*Math.PI/90))); + var g = Math.floor(127*(1-Math.cos(t*Math.PI/90+2*Math.PI/3))); + var b = Math.floor(127*(1-Math.cos(t*Math.PI/90+4*Math.PI/3))); + var baseColor = Math.random() < 0.02 ? [g,r,b] : [r,g,b]; + baseColor = "rgb("+baseColor.join(",")+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //80% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.8); + //30% dye color, 70% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.7); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + category: "special", + reactions: { + dye: elements.rainbow.reactions.dye + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + movable: false, + }; + elements.rainbow.behavior = behaviors.WALL; + elements.dye.ignore ??= []; + elements.pastel_rainbow = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/240))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/240+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/240+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye, + dust: { elem1: "pastel_rainbow_small", elem2: null }, + flameshockwave4: { elem1: "pastel_rainbow_x", elem2: null }, //fs4 = left, fs5 = right + flameshockwave5: { elem1: "pastel_rainbow_x", elem2: null }, + flameshockwave2: { elem1: "pastel_rainbow_y", elem2: null }, //fs2 = up, fs7 = down + flameshockwave7: { elem1: "pastel_rainbow_y", elem2: null }, + flameshockwave1: { elem1: "pastel_rainbow_d", elem2: null }, //fs1 ul, fs3 ur, fs6 dl, fs8 dr + flameshockwave3: { elem1: "pastel_rainbow_d", elem2: null }, + flameshockwave6: { elem1: "pastel_rainbow_d", elem2: null }, + flameshockwave8: { elem1: "pastel_rainbow_d", elem2: null }, + slime: { elem1: "pastel_rainbow_t_d_x", elem2: null }, + feather: { elem1: "pastel_rainbow_t_d_y", elem2: null }, + glue: { elem1: "pastel_rainbow_x_p_y", elem2: null }, + plasma: { elem1: "pastel_rainbow_x_m_y", elem2: null }, + liquid_light: { elem1: "pastel_rainbow_x_t_y", elem2: null }, + tectonic_petrotheum: { elem1: "pastel_rainbow_x_d_y", elem2: null }, + seed: { elem1: "pastel_rainbow_x_e_y", elem2: null }, + dirt: { elem1: "pastel_rainbow_x_r_y", elem2: null }, + sawdust: { elem1: "pastel_rainbow_x_l_y", elem2: null }, + water: { elem1: "pastel_rainbow_s_x", elem2: null }, + magma: { elem1: "pastel_rainbow_c_x", elem2: null }, + gelid_cryotheum: { elem1: "pastel_rainbow_t_x", elem2: null }, + steam: { elem1: "pastel_rainbow_sh_x", elem2: null }, + vaporized_magma: { elem1: "pastel_rainbow_ch_x", elem2: null }, + blazing_pyrotheum: { elem1: "pastel_rainbow_th_x", elem2: null }, + rock: { elem1: "pastel_rainbow_sr_x", elem2: null }, + polka_dotted_powder: { elem1: "pastel_rainbow_x_mod_y", elem2: null } + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.rubyshimmer = { + color: ["#ff0000","#200000","#ff0000","#200000"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + pixel.color = "rgb("+Math.ceil((r*(7/8))+32)+","+0+","+0+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.topazshimmer = { + color: ["#ffff00","#202000","#ffff00","#202000"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+value+","+value+",0)"; + doHeat(pixel); + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.beeshimmer = { + color: ["#ffff00","#202000","#ffff00","#202000","#ffff00","#202000","#ffff00","#202000"], + tick: function(pixel) { + var t = pixelTicks*1.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/4))); + var value = Math.ceil(r/2); + pixel.color = "rgb("+value+","+value+",0)"; + doHeat(pixel); + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.ambershimmer = { + color: ["#ff7f00","#201000","#ff7f00","#201000"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+value+","+Math.round(value*0.5)+",0)"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.emeraldshimmer = { + color: ["#00ff00","#002000","#00ff00","#002000"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb(0,"+value+",0)"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.iceshimmer = { + color: ["#00ffff","#001520","#00ffff","#001520"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb(0,"+Math.round(value * 0.75)+","+value+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.sapphireshimmer = { + color: ["#0000ff","#000020","#0000ff","#000020"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb(0,0,"+value+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.amethystshimmer = { + color: ["#7f00ff","#100020","#7f00ff","#100020"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value/2)+",0,"+value+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.spinelshimmer = { + color: ["#ff00ff","#200020","#ff00ff","#200020"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+value+",0,"+Math.round(value * 0.5)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.mulliteshimmer = { + color: ["#f8eeff","#151020","#f8eeff","#151020"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var l = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((l*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.9)+","+Math.round(value * 0.75)+","+Math.round(value*0.85)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.dantiteshimmer = { + color: ["#00ff7f","#002010","#00ff7f","#002010"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var l = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((l*(7/8))+32); + pixel.color = "rgb(0,"+value+","+Math.round(value*0.5)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + nellfireImmune: true + }; + elements.turquoiseshimmer = { + color: ["#00ffff","#002020","#00ffff","#002020"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var l = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((l*(7/8))+32); + pixel.color = "rgb(0,"+value+","+value+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + nellfireImmune: true + }; + elements.diabalineshimmer = { + color: ["#af7fcf","#141018","#af7fcf","#141018"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.625)+","+Math.round(value*0.5)+","+Math.round(value*0.75)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.goldshimmer = { + color: ["#ffcf00","#201800","#ffcf00","#201800"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+value+","+Math.round(value*0.75)+",0)"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.leadshimmer = { + color: ["#2f2f4f","#040410","#2f2f4f","#040410"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.3)+","+Math.round(value*0.3)+","+Math.round(value*0.5)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.onyxshimmer = { + color: ["#1f1f1f","#040404","#1f1f1f","#040404"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+16); + pixel.color = "rgb("+Math.round(value*0.125)+","+Math.round(value*0.125)+","+Math.round(value*0.125)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.opalshimmer = { + color: function() { var rc = elements.rainbow.color; var rc2 = rc.map(x => lightenColor(x,127,"hex")); return rc2.concat(rc2) }(), + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t1 = pixelTicks+pixel.x+pixel.y; + var t2 = pixelTicks+pixel.x-pixel.y; + var scale = 20; + var r1 = Math.floor(127*(1-Math.cos(t1*Math.PI/scale))); + var g1 = Math.floor(127*(1-Math.cos(t1*Math.PI/scale+2*Math.PI/3))); + var b1 = Math.floor(127*(1-Math.cos(t1*Math.PI/scale+4*Math.PI/3))); + var r2 = Math.floor(127*(1-Math.cos(t2*Math.PI/scale))); + var g2 = Math.floor(127*(1-Math.cos(t2*Math.PI/scale+2*Math.PI/3))); + var b2 = Math.floor(127*(1-Math.cos(t2*Math.PI/scale+4*Math.PI/3))); + var r3 = (r1+r2)*0.75; + var g3 = (g1+g2)*0.75; + var b3 = (b1+b2)*0.75; + var baseColor = {r: r3, g: g3, b: b3}; + baseColor = averageColorObjects(baseColor,_cc.w.j,0.8); + baseColor = convertColorFormats(baseColor,"rgb"); + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //80% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.8); + //30% dye color, 70% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.7); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye, + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.jadeshimmer = { + color: ["#5f8f2f","#0c1206","#5f8f2f","#0c1206"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + pixel.color = "rgb("+Math.ceil((r*(4/16))+32)+","+Math.ceil((r*(8/16))+32)+","+Math.ceil((r*(1/8))+32)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.earthshimmer = { + color: ["#5f3f00","#0c0800","#5f3f00","#0c0800"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.375)+","+Math.round(value*0.2)+",0)"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.seashimmer = { + color: ["#007fff","#001020","#007fff","#001020"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb(0,"+Math.round(value*0.5)+","+value+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.lavashimmer = { + color: ["#ff3f00","#200800","#ff3f00","#200800"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+value+","+Math.round(value*0.25)+",0)"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.radioshimmer = { //if it captures and renders harmless fire+crimmagma+lava then it can provide radiation shielding + color: ["#7fff00","#102000","#7fff00","#102000"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.5)+","+value+",0)"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.ashenshimmer = { + color: ["#cfcfcf","#181818","#cfcfcf","#181818"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.75)+","+Math.round(value*0.75)+","+Math.round(value*0.75)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.steelshimmer = { + color: ["#7f7f7f","#101010","#7f7f7f","#101010"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.5)+","+Math.round(value*0.5)+","+Math.round(value*0.5)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.smokeshimmer = { + color: ["#3f3f3f","#050505","#3f3f3f","#050505"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.25)+","+Math.round(value*0.25)+","+Math.round(value*0.25)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.cloudshimmer = { + color: ["#dfefff","#1d1e20","#dfefff","#1d1e20"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+48); + pixel.color = "rgb("+Math.round(value*0.8)+","+Math.round(value*0.9)+","+value+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.fluoroshimmer = { + color: ["#5f6f2f","#081000","#5f6f2f","#081000"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.375)+","+Math.round(value*0.4375)+","+Math.round(value*0.1875)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.chloroshimmer = { + color: ["#3f7f00","#081000","#3f7f00","#081000"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.25)+","+Math.round(value*0.5)+",0)"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.bromoshimmer = { + color: ["#7f1f00","#100400","#7f1f00","#100400"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.5)+","+Math.round(value*0.125)+",0)"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.iodoshimmer = { + color: ["#5f007f","#0c0010","#5f007f","#0c0010"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.375)+",0,"+Math.round(value*0.5)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.astatoshimmer = { + color: ["#230f27","#050205","#230f27","#050205"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.137)+","+Math.round(value*0.059)+","+Math.round(value*0.153)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.bloodshimmer = { + color: ["#7f0000","#100000","#7f0000","#100000"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+Math.round(value*0.5)+",0,0)"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.saltshimmer = { + color: [_cc.w.h,"#202020",_cc.w.h,"#202020"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+value+","+value+","+value+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.sandshimmer = { + color: ["#ffdf7f","#201d10","#ffdf7f","#201d10"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+value+","+Math.round(value*0.8)+","+Math.round(value*0.5)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.ichorshimmer = { + color: ["#ffbf5f","#201804","#ffbf5f","#201804"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + var value = Math.ceil((r*(7/8))+32); + pixel.color = "rgb("+value+","+Math.round(value*0.75)+","+Math.round(value*0.375)+")"; + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.quarkshimmer = { + color: ["#ff0000","#00ff00","#0000ff","#ff0000","#00ff00","#0000ff"], + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(127*(((Math.max(0,1-(rainbowMathlet(t,20,0))) / 1.575) ** 5) * 1.575)); + var g = Math.floor(127*(((Math.max(0,1-(rainbowMathlet(t,20,2))) / 1.575) ** 5) * 1.575)); + var b = Math.floor(127*(((Math.max(0,1-(rainbowMathlet(t,20,4))) / 1.575) ** 5) * 1.575)); + baseColor = "rgb("+([r,g,b].join(","))+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + category: "special", + reactions: { + dye: elements.rainbow.reactions.dye + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + movable: false, + }; + elements.heejinshimmer = { + color: ["#ff007f","#200010","#ff007f","#200010"], + tick: function(pixel) { + var t = pixelTicks*2.5+pixel.x+pixel.y; + var t2 = pixelTicks/2; + //var r = Math.floor(255*(1-Math.cos(t*Math.PI/24))); + //var value = Math.ceil((r*(7/8))+32); + var l = Math.floor(40*(1-Math.cos(t*Math.PI/24))); + var h = (320 + Math.floor(30*(Math.cos(t2*Math.PI/24)))) % 360; + //pixel.color = "rgb("+value+",0,"+Math.round(value * 0.5)+")"; + pixel.color = convertHslObjects({h:h,s:100,l:l},"rgb"); + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants", + }; + elements.pastel_rainbow_small = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/60))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/60+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/60+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_t_d_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (time/x)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(pixelTicks/pixel.x)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(pixelTicks/pixel.x)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(pixelTicks/pixel.x)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_t_d_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (time/y)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(pixelTicks/pixel.y)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(pixelTicks/pixel.y)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(pixelTicks/pixel.y)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + behavior: behaviors.WALL, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (x distance)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/pixel.x))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/pixel.x+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/pixel.x+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye, + polka_dotted_powder: { elem1: "pastel_rainbow_d_mod_x", elem2: null } + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (y distance)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/pixel.y))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/pixel.y+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/pixel.y+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye, + polka_dotted_powder: { elem1: "pastel_rainbow_d_mod_y", elem2: null } + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_d = { + name: "pastel rainbow (diagonal distance)", + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.sqrt(pixel.x**2+pixel.y**2))))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.sqrt(pixel.x**2+pixel.y**2))+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.sqrt(pixel.x**2+pixel.y**2))+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye, + plasma: { elem1: "pastel_rainbow_d_m_x", elem2: null }, + liquid_light: { elem1: "pastel_rainbow_d_t_x", elem2: null } + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x_p_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (x+y)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x+pixel.y)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x+pixel.y)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x+pixel.y)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x_m_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (x-y)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x-pixel.y)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x-pixel.y)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x-pixel.y)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x_t_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (x*y)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x*pixel.y)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x*pixel.y)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x*pixel.y)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x_d_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (x/y)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x/pixel.y)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x/pixel.y)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x/pixel.y)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye, + flameshockwave1: { elem1: "pastel_rainbow_x_d_d", elem2: null }, + flameshockwave3: { elem1: "pastel_rainbow_x_d_d", elem2: null }, + flameshockwave6: { elem1: "pastel_rainbow_x_d_d", elem2: null }, + flameshockwave8: { elem1: "pastel_rainbow_x_d_d", elem2: null } + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x_e_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (x^y)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x**pixel.y)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x**pixel.y)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x**pixel.y)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x_r_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (ʸ√x)", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.pow(pixel.x, 1/pixel.y))))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.pow(pixel.x, 1/pixel.y))+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.pow(pixel.x, 1/pixel.y))+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x_l_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (logᵧ(x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.log(pixel.x) / Math.log(pixel.y))))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.log(pixel.x) / Math.log(pixel.y))+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.log(pixel.x) / Math.log(pixel.y))+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_s_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (sin(x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/Math.sin(pixel.x)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/Math.sin(pixel.x)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/Math.sin(pixel.x)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_c_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (cos(x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/Math.cos(pixel.x)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/Math.cos(pixel.x)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/Math.cos(pixel.x)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_t_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (tan(x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/Math.tan(pixel.x)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/Math.tan(pixel.x)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/Math.tan(pixel.x)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_sh_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (sinh(x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/Math.sinh(pixel.x)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/Math.sinh(pixel.x)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/Math.sinh(pixel.x)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_ch_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (cosh(x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/Math.cosh(pixel.x)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/Math.cosh(pixel.x)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/Math.cosh(pixel.x)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_th_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (tanh(x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/Math.tanh(pixel.x)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/Math.tanh(pixel.x)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/Math.tanh(pixel.x)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_sr_x = { + name: "pastel rainbow (√x))", + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/Math.sqrt(pixel.x)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/Math.sqrt(pixel.x)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/Math.sqrt(pixel.x)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_d_m_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (diagonal distance - x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.sqrt(pixel.x**2+pixel.y**2))-pixel.x))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.sqrt(pixel.x**2+pixel.y**2))-pixel.x+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.sqrt(pixel.x**2+pixel.y**2))-pixel.x+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_d_t_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (diagonal distance * x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.sqrt(pixel.x**2+pixel.y**2))*pixel.x))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.sqrt(pixel.x**2+pixel.y**2))*pixel.x+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(Math.sqrt(pixel.x**2+pixel.y**2))*pixel.x+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x_d_d = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (x / diagonal distance))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x/(Math.sqrt(pixel.x**2+pixel.y**2)))))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x/(Math.sqrt(pixel.x**2+pixel.y**2)))+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x/(Math.sqrt(pixel.x**2+pixel.y**2)))+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_x_mod_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (x % y))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x%pixel.y)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x%pixel.y)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/(pixel.x%pixel.y)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_d_mod_x = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (diagonal distance modulo x))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/((Math.sqrt(pixel.x**2+pixel.y**2))%pixel.x)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/((Math.sqrt(pixel.x**2+pixel.y**2))%pixel.x)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/((Math.sqrt(pixel.x**2+pixel.y**2))%pixel.x)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + elements.pastel_rainbow_d_mod_y = { + color: ["#ffaacc","#ffaacc","#aaccff","#aaccff","#ffffbb","#ffffbb"], + name: "pastel rainbow (diagonal distance modulo y))", + tick: function(pixel) { + var dyeColor = pixel.dyeColor ?? null; + var t = pixelTicks*3+pixel.x+pixel.y; + var r = Math.floor(255*(1-Math.cos(t*Math.PI/((Math.sqrt(pixel.x**2+pixel.y**2))%pixel.y)))); + var g = Math.floor(255*(1-Math.cos(t*Math.PI/((Math.sqrt(pixel.x**2+pixel.y**2))%pixel.y)+2*Math.PI/3))); + var b = Math.floor(255*(1-Math.cos(t*Math.PI/((Math.sqrt(pixel.x**2+pixel.y**2))%pixel.y)+4*Math.PI/3))); + var baseColor = "rgb("+Math.ceil((r/2)+127)+","+Math.ceil((g/2)+127)+","+Math.ceil((b/2)+127)+")"; + if(!dyeColor) { + pixel.color = baseColor + } else { + var baseJSON = convertColorFormats(baseColor,"json"); + var dyeJSON = convertColorFormats(dyeColor,"json"); + var dyedColor = multiplyColors(dyeJSON,baseJSON,"json"); + //70% multiplied + var semiDyedColor = averageColorObjects(dyedColor,baseJSON,0.7); + //35% dye color, 65% result + var finalColor = averageColorObjects(semiDyedColor,dyeJSON,0.65); + pixel.color = convertColorFormats(finalColor,"rgb") + } + }, + reactions: { + dye: elements.rainbow.reactions.dye + }, + state: "solid", + category: "rainbow variants" + }; + //ASSORTED RANDOMLY GENERATED MATERIALS ## + elements.fowtim_ice = { + color: "#6c6dec", + behavior: behaviors.WALL, + tempHigh: 9382.73, + temp: 9362.73, + stateHigh: "fowtim", + category: "random solids", + state: "solid", + density: 1009.20, + hardness: 0.54, + breakInto: "fowtim", + conduct: 0.14, + }; + elements.fowtim = { + color: "#2324e2", + behavior: behaviors.LIQUID, + tempLow: 9382.73, + temp: 9402.73, + tempHigh: 11187.06, + stateLow: "fowtim_ice", + stateHigh: "fowtim_gas", + category: "random liquids", + state: "liquid", + density: 905.92, + hardness: 0.27, + viscosity: 547.09, + breakInto: "fowtim_gas", + conduct: 0.078, + }; + elements.fowtim_gas = { + color: "#b6b6f5", + behavior: behaviors.GAS, + tempLow: 11187.06, + temp: 11207.06, + stateLow: "fowtim", + category: "random gases", + state: "gas", + density: 1.36, + hardness: 1, + }; + elements.shit = { + color: ["#756674","#554754","#827381"], + behavior: behaviors.POWDER, + tempHigh: 975.54, + category: "random rocks", + state: "solid", + density: 2902.23, + hardness: 0.45, + breakInto: ["dust","shit_gravel"], + stateHigh: "molten_shit" + }; + elements.shit_gravel = { + color: ["#b9a9b5","#97858d","#6e6068","#57454e"], + behavior: behaviors.POWDER, + tempHigh: 975.54, + stateHigh: "shit", + category: "random rocks", + state: "solid", + density: 1912.06, + hardness: 0.19, + breakInto: "dust", + }; + elements.loona = { + color: ["#6f7d54","#4f5d34","#7c8a61"], + behavior: behaviors.POWDER, + tempHigh: 1031.02, + category: "random rocks", + state: "solid", + density: 2466.73, + hardness: 0.51, + breakInto: ["dust","loona_gravel"], + stateHigh: "molten_loona" + }, + elements.loona_gravel = { + color: ["#b3be98","#919a6f","#68744b","#515931"], + behavior: behaviors.POWDER, + tempHigh: 1031.02, + stateHigh: "loona", + category: "random rocks", + state: "solid", + density: 1625.14, + hardness: 0.20, + breakInto: "dust", + }; + //ROSEYIEDE ## + elements.roseyiede = { + color: "#686118", + behavior: behaviors.LIQUID, + tempHigh: 103, + stateHigh: "gaseous_roseyiede", + tempLow: -8, + stateLow: "solid_roseyiede", + category: "liquids", + state: "liquid", + density: 1000, + }, + elements.gaseous_roseyiede = { + color: "#a49e4c", + behavior: behaviors.GAS, + tempLow: -8, + stateLow: "powdered_roseyiede", + tick: function(pixel) { + if((pixelTicks - pixel.start) % 5 == 0) { + if(pixel.temp < 3 && Math.random() < 0.00135) { + changePixel(pixel,"roseyiede",false); + } else if(pixel.temp < 23 && Math.random() < 0.001) { + changePixel(pixel,"roseyiede",false); + } else if(pixel.temp < 43 && Math.random() < 0.0007) { + changePixel(pixel,"roseyiede",false); + } else if(pixel.temp < 63 && Math.random() < 0.00045) { + changePixel(pixel,"roseyiede",false); + } else if(pixel.temp < 83 && Math.random() < 0.00025) { + changePixel(pixel,"roseyiede",false); + } else if(pixel.temp < 103 && Math.random() < 0.0001) { + changePixel(pixel,"roseyiede",false); + } + }; + }, + category: "gases", + state: "gas", + density: 0.63, + temp: 120, + }, + elements.solid_roseyiede = { + color: "#685e10", + behavior: behaviors.WALL, + tempHigh: 103, + stateHigh: "gaseous_roseyiede", + category: "solids", + state: "solid", + density: 1121, + hardness: 0.121, + breakInto: "powdered_roseyiede", + temp: -20, + }, + elements.powdered_roseyiede = { + color: "#6c641c", + behavior: behaviors.POWDER, + tempHigh: -8, + stateHigh: "roseyiede", + category: "powders", + state: "solid", + density: 182, + temp: -20, + } + console.log("1/4 loaded") + //Volatile Roseyiede + elements.explosive_roseyiede = { + color: "#986118", + behavior: behaviors.LIQUID, + tempHigh: 98, + stateHigh: "gaseous_explosive_roseyiede", + tempLow: -6, + stateLow: "solid_explosive_roseyiede", + burn: 11, + burnInto: ["explosive_roseyiede","explosive_roseyiede","explosive_roseyiede","explosive_roseyiede","explosive_roseyiede","explosive_roseyiede","explosive_roseyiede","explosive_roseyiede","explosive_roseyiede","explosive_roseyiede","fire","fire","fire","fire","fire","fire","fire","fire","fire","explosion"], + burnTime: 312, + tick: function(pixel) { + if((pixelTicks - pixel.start) % 5 == 0) { + if(pixel.temp < 3 && Math.random() < 0.0001) { + changePixel(pixel,"explosive_roseyiede_vapor",false) + } else if(pixel.temp < 23 && Math.random() < 0.0002) { + changePixel(pixel,"explosive_roseyiede_vapor",false) + } else if(pixel.temp < 43 && Math.random() < 0.0035) { + changePixel(pixel,"explosive_roseyiede_vapor",false) + } else if(pixel.temp < 63 && Math.random() < 0.00055) { + changePixel(pixel,"explosive_roseyiede_vapor",false) + } else if(pixel.temp < 83 && Math.random() < 0.0008) { + changePixel(pixel,"explosive_roseyiede_vapor",false) + } else if(pixel.temp < 98 && Math.random() < 0.0011) { + changePixel(pixel,"explosive_roseyiede_vapor",false) + } + } + }, + category: "liquids", + state: "liquid", + density: 1000, + }, + elements.gaseous_explosive_roseyiede = { + color: "#c89e4c", + behavior: behaviors.GAS, + tempLow: -6, + stateLow: "powdered_explosive_roseyiede", + burn: 88, + burnInto: ["gaseous_explosive_roseyiede","gaseous_explosive_roseyiede","gaseous_explosive_roseyiede","gaseous_explosive_roseyiede","gaseous_explosive_roseyiede","explosion","fire","fire","fire","fire","fire","fire","fire","fire","gaseous_explosive_roseyiede","explosion","fire","fire","fire","explosion"], + burnTime: 48, + tick: function(pixel) { + if((pixelTicks - pixel.start) % 5 == 0) { + if(pixel.temp < 3 && Math.random() < 0.00135) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 23 && Math.random() < 0.001) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 43 && Math.random() < 0.0007) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 63 && Math.random() < 0.00045) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 83 && Math.random() < 0.00025) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 98 && Math.random() < 0.0001) { + changePixel(pixel,"explosive_roseyiede",false); + } + } + }, + category: "gases", + state: "gas", + density: 0.63, + temp: 120, + }, + elements.explosive_roseyiede_vapor = { + color: "#c89449", + behavior: behaviors.GAS, + tempHigh: 98, + stateHigh: "gaseous_explosive_roseyiede", + tempLow: -6, + stateLow: "powdered_explosive_roseyiede", + burn: 88, + burnInto: ["explosive_roseyiede_vapor","explosive_roseyiede_vapor","explosive_roseyiede_vapor","explosive_roseyiede_vapor","explosive_roseyiede_vapor","explosion","fire","fire","fire","fire","fire","fire","fire","fire","explosive_roseyiede_vapor","fire","fire","fire","fire","explosion"], + burnTime: 48, + tick: function(pixel) { + if((pixelTicks - pixel.start) % 5 == 0) { + if(pixel.temp < 3 && Math.random() < 0.0011) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 23 && Math.random() < 0.0008) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 43 && Math.random() < 0.00055) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 63 && Math.random() < 0.00035) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 83 && Math.random() < 0.0002) { + changePixel(pixel,"explosive_roseyiede",false); + } else if(pixel.temp < 98 && Math.random() < 0.0001) { + changePixel(pixel,"explosive_roseyiede",false); + } + } + }, + category: "gases", + state: "gas", + density: 0.63, + temp: 40, + }, + elements.solid_explosive_roseyiede = { + color: "#985e10", + behavior: behaviors.WALL, + tempHigh: 98, + stateHigh: "gaseous_explosive_roseyiede", + burn: 4, + burnInto: ["solid_explosive_roseyiede","solid_explosive_roseyiede","solid_explosive_roseyiede","solid_explosive_roseyiede","solid_explosive_roseyiede","solid_explosive_roseyiede","solid_explosive_roseyiede","solid_explosive_roseyiede","solid_explosive_roseyiede","solid_explosive_roseyiede","fire","fire","fire","fire","fire","fire","fire","fire","fire","explosion"], + burnTime: 662, + category: "solids", + state: "solid", + density: 1121, + hardness: 0.121, + breakInto: "powdered_explosive_roseyiede", + temp: -20, + }, + elements.powdered_explosive_roseyiede = { + color: "#98641c", + behavior: behaviors.POWDER, + tempHigh: -6, + stateHigh: "explosive_roseyiede", + burn: 42, + burnInto: ["powdered_explosive_roseyiede","powdered_explosive_roseyiede","powdered_explosive_roseyiede","powdered_explosive_roseyiede","powdered_explosive_roseyiede","powdered_explosive_roseyiede","powdered_explosive_roseyiede","powdered_explosive_roseyiede","fire","fire","explosion","fire","fire","fire","fire","fire","fire","fire","fire","explosion"], + burnTime: 101, + category: "powders", + state: "solid", + density: 182, + temp: -20, + }, + elements.boiling_roseyiede = { + name: "forever- boiling roseyiede", + color: "#9e942e", + behavior: [ + "XX|M2%5 AND CR:gaseous_roseyiede%0|XX", //display CR + "M2|HT:0|M2", //display HT + "M1|M1|M1", + ], + reactions: { + "roseyiede": { elem2: "boiling_roseyiede", chance: 0.006 } + }, + tick: function(pixel) { + if((pixelTicks - pixel.start) % 3 == 0) { + if(pixel.temp > (-206/3)) { + if(Math.random() < (pixel.temp - 103) / (51500/3) + 0.01) { + tryCreatePixel("gaseous_roseyiede",pixel.x,pixel.y-1) + }; + } else if(pixel.temp < 103) { + pixel.temp += (((pixel.temp - 103) / 100) ** 2) / 2 + 1; + } + }; + }, + category: "liquids", + state: "liquid", + density: 956, + temp: 120, + } + //MINESWEEPER ## + msColorArray = ["#a0a0a0", "#0000ff", "#008000", "#ff0000", "#000080", "#800000", "#008080", _cc.b.h, "#808080"] + elements.msfield = { + name: "minefield", + color: "#c0c0c0", + conduct: 1, + insulate: true, + properties: { + uwu: 0, + revealed: false, + revealedAround: false + }, + tick: function(pixel) { + pixel.revealed ??= false; + pixel.uwu ??= 0; + pixel.revealedAround ??= false; + if(pixel.charge) { + if(!pixel.revealed) { pixel.revealed = true }; + delete pixel.charge + if(pixel.chargeCD) { + delete pixel.chargeCD + } + } + if(pixel.revealed) { + //count neighbors + pixel.uwu = 0 + for (let i = -1; i < 2; i++) { + for (let j = -1; j < 2; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == "msmine") { + pixel.uwu++ + } + } + } + } + if(typeof(pixel.uwu) === 'number' && isFinite(pixel.uwu) && !isNaN(pixel.uwu)) { + if(pixel.uwu >= 0 && pixel.uwu <= 8) { + pixel.color = msColorArray[pixel.uwu]; + pixel.displayText = pixel.uwu.toString() + } + } else { + pixel.color = "#ff00ff" + } + } else { + pixel.color = "#c0c0c0" //I feel bad suppressing the sand effect. + } + }, + maxColorOffset: 0, + category: "special", + state: "solid", + hidden: true, + }; + elements.msmine = { + name: "minefield", + color: "#c0c0c0", + conduct: 1, + insulate: true, + properties: { + uwu: 0, + revealed: false + }, + tick: function(pixel) { + if(pixel.charge) { + pixel.revealed = true + delete pixel.charge + if(pixel.chargeCD) { + delete pixel.chargeCD + } + } + if(pixel.revealed) { + pixel.color = ("#" + ((192 + Math.abs((pixelTicks * 4) % 64)).toString(16) + "c0c0").padStart(6, '0')); + //oldFillStyle = ctx.fillStyle + //ctx.fillStyle = "#ff0000"; + ////ctx.fillRect(pixel.x*pixelSize, pixel.y*pixelSize, pixelSize/2, pixelSize); + //ctx.fillRect(23*pixelSize, 23*pixelSize, pixelSize/2, pixelSize); + //ctx.fillStyle = oldFillStyle; + } else { + pixel.color = "#c0c0c0" + } + }, + category: "special", + state: "solid", + hidden: true, + }; + elements.ms = { //minesweeper = { + color: ["#c0c0c0", "#c0c0c0", "#ff0000", "#008000", "#ff0000", "#000080", "#800000", "#008080", _cc.b.h, "#808080", "#808080"], + behavior: [ + "XX|XX|XX", + "XX|CH:msfield,msfield,msfield,msfield,msfield,msfield,msfield,msfield,msfield,msmine|XX", + "XX|XX|XX" + ], + category: "special", + state: "solid", + }; + //LIFE-EATER VIRUS ## + var lifeEaterCategories = ["life","auto creepers","shit","cum","food","fantastic creatures","fey","auto fey"]; + var lifeEaterBlacklist = ["life_eater_virus","life_eater_slurry","life_eater_infected_dirt"]; + var lifeEaterWhitelist = ["blood","skin","hair","poop","blood_ice","wood","wood_plank","sawdust","straw","paper","birthpool","dried_poop","gloomfly","meat_monster","rotten_ravager","bone_beast","withery","withery_plant","banana","apple","rotten_apple","apioform_player","apioform_bee","apioform","apiodiagoform","sugar_cactus","sugar_cactus_seed","flowering_sugar_cactus","tree_branch","sap","silk","red_velvet","silk_velvet","ketchup", "enchanted_ketchup", "frozen_ketchup", "poisoned_ketchup", "frozen_poisoned_ketchup", "ketchup_spout", "ketchup_cloud", "poisoned_ketchup_cloud", "ketchup_snow", "ketchup_snow_cloud", "poisoned_ketchup_snow", "poisoned_ketchup_snow_cloud", "ketchup_gas", "poisoned_ketchup_gas", "ketchup_powder", "poisoned_ketchup_powder", "eketchup_spout", "ketchup_metal", "antiketchup", "dirty_ketchup", "ketchup_gold", "molten_ketchup_metal", "ketchup_fairy", "ketchup_metal_scrap", "ketchup_gold_scrap", "molten_ketchup_gold", "mycelium","vaccine","antibody","infection","sap","caramel","molasses","melted_chocolate","soda","mustard","fry_sauce","tomato_sauce","sugary_tomato_sauce","bio_ooze","zombie_blood","feather","tooth","decayed_tooth","plaque","tartar","bacteria","replacer_bacteria","pop_rocks"]; + var lifeEaterSubstitutions = { + "dirt": "life_eater_infected_dirt", + "crimsoil": "life_eater_infected_dirt", + "rainbow_dirt": "life_eater_infected_dirt" + }; + function tryCreatePlus(element,centerX,centerY) { + var plusCoords = adjacentCoords.concat([[0,0]]); + var pixels = 0; + for(let i = 0; i < plusCoords.length; i++) { + var newX = centerX + plusCoords[i][0]; + var newY = centerY + plusCoords[i][1]; + if(isEmpty(newX,newY)) { + while(element instanceof Array) { element = element[Math.floor(Math.random() * element.length)] }; + createPixel(element,newX,newY); + pixels++; + }; + }; + return pixels; + }; + function spreadLifeEater(pixel) { + var convertedPixels = []; + for(i = 0; i < adjacentCoords.length; i++) { //iterate through neighbor spots + if(!isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { //check for adjacentCoords + var newPixel = pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]] + var isLifeEaterFairy = (elements[newPixel.element].category == "auto fey" && newPixel.element.includes("life_eater_")) + //console.log(newPixel.element,isLifeEaterFairy); + if( + (lifeEaterCategories.includes(elements[newPixel.element].category) || lifeEaterWhitelist.includes(newPixel.element) || Object.keys(lifeEaterSubstitutions).includes(newPixel.element)) && + !lifeEaterBlacklist.includes(newPixel.element) && + !isLifeEaterFairy //exclude fairies which produce life eater + ) { + if(Object.keys(lifeEaterSubstitutions).includes(newPixel.element)) { + var data = lifeEaterSubstitutions[newPixel.element]; + while(data instanceof Array) { + data = data[Math.floor(Math.random() * data.length)]; + }; + if(data === null) { + if(newPixel) { deletePixel(newPixel.x,newPixel.y) }; + } else { + changePixel(newPixel,data); + convertedPixels.push(newPixel); + }; + } else { + changePixel(newPixel,"life_eater_slurry"); + convertedPixels.push(newPixel); + }; + }; + }; + }; + return convertedPixels; + }; + elements.life_eater_explosion = { + color: ["#96c785","#f0d654","#ffb47a"], + behavior: [ + "XX|XX|XX", + "XX|EX:9>plasma,fire,life_eater_virus|XX", + "XX|XX|XX", + ], + temp: 1600, + category: "energy", + state: "gas", + density: 1000, + excludeRandom: true, + hidden: true, + }, + elements.life_eater_virus = { + color: ["#7bb064", "#aabd60", "#9e9e29"], + behavior: behaviors.GAS, + tick: function(pixel) { + spreadLifeEater(pixel).forEach(infectedPixel => spreadLifeEater(infectedPixel)); + }, + category: "life", + state: "gas", + density: airDensity, + excludeRandom: true, + tempHigh: 300, + stateHigh: null, + }; + elements.life_eater_slurry = { + color: ["#3d6e29", "#666617", "#7d5716"], + behavior: behaviors.LIQUID, + properties: { + methaned: false + }, + tick: function(pixel) { + spreadLifeEater(pixel).forEach(infectedPixel => spreadLifeEater(infectedPixel)); + if(pixelTicks - pixel.start > 6) { + if(!pixel.methaned && Math.random() < 0.2) { + changePixel(pixel,Math.random() < 0.2 ? "life_eater_virus" : "methane"); + } else { + pixel.methaned = true; + }; + tryCreatePlus(["methane","methane","methane","methane","life_eater_virus"],pixel.x,pixel.y); + return; + }; + }, + category: "life", + state: "liquid", + density: 1050, + burn: 100, + burnTime: 10, + fireSpawnTemp: 1500, + burnTempChange: 200, + burnInto: ["life_eater_virus","plasma","fire","life_eater_explosion"], + excludeRandom: true, + }; + var crRule50 = "CR:life_eater_virus,methane,methane,methane%0.5"; + var crRule100 = "CR:life_eater_virus,methane,methane,methane%1"; + elements.life_eater_infected_dirt = { + behavior: [ + "XX|"+crRule100+"|XX", + crRule50+"|XX|"+crRule50, + "M2|M1 AND "+crRule50+"|M2", + ], + color: ["#757137","#617a35","#66622c","#707538"], + tick: function(pixel) { + spreadLifeEater(pixel).forEach(infectedPixel => spreadLifeEater(infectedPixel)); + }, + category: "life", + state: "liquid", + density: 1050, + burn: 70, + burnTime: 15, + fireSpawnTemp: 1400, + burnTempChange: 180, + burnInto: ["life_eater_virus","fire","plasma","life_eater_explosion"], + excludeRandom: true, + }; + for(i = 0; i < 4; i++) { + elements.life_eater_infected_dirt.burnInto.push(elements.dry_dirt ? "dry_dirt" : "sand"); + }; + elements.virus_bomb = { + color: "#accc70", + behavior: [ + "XX|EX:16>life_eater_virus|XX", + "XX|XX|XX", + "XX|EX:16>life_eater_virus AND M1|XX" + ], + density: 3500, + hardness: 0.95, + breakInto: "life_eater_virus", + tempHigh: 2400, + category: "weapons", + excludeRandom: true, + stateHigh: ["molten_iron","molten_aluminum","molten_tin","life_eater_virus","life_eater_virus","life_eater_virus"] + }; + //PLANET CRACKER ## + function planetCrackerHeat(pixel,x,y,radius,fire,smoke,power,damage) { + //console.log(`Radius: ${radius}\nPower: ${power}\nPixel: (${pixel.x},${pixel.y})\nDamage: ${damage}`); + //console.log(`Expected temperature increase for pixel at (${pixel.x},${pixel.y}): ${800 * ((1 + (7 * damage)) ** 2) * ((power ** 2) * 1.5)}`); + var reversedCloseness = ((radius / 6) ** 0.5) - 1; //mathematically inaccurate but properly correlated + pixel.temp += 500 * ((reversedCloseness * 2) + 1); + if(pixel.vx) { + pixel.vx *= 2; + }; + if(pixel.vy) { + pixel.vy *= 2; + }; + }; + function planetCrackerFinale(doColorChange=true,spawnElements=null) { + var bottomFortyPercent = Math.round(height * 0.6); + var bottomTwentyPercent = Math.round(height * 0.8); + var bottomTenPercent = Math.round(height * 0.9); + if(typeof(spawnElements) == "string") { + if(spawnElements.indexOf(",") >= 0) { + spawnElements = spawnElements.split(",") + } else { spawnElements = [spawnElements] }; + }; + if(Array.isArray(spawnElements)) { + spawnElements = spawnElements.filter(n => elementExists(n)); + if(spawnElements.length == 0) { + spawnElements = null + } + }; + var doSpawning = (spawnElements !== null); + for(x = 1; x < width; x++) { + for(y = bottomFortyPercent; y < height; y++) { + var chance = y > bottomTwentyPercent ? 0.03 : 0.01 + var radius = y > bottomTwentyPercent ? 8 : 6 + var newPixel = pixelMap[x]?.[y]; + if(doSpawning && y > bottomTenPercent && isEmpty(x,y,false) && (Math.random() < 0.6)) { + var _np = tryCreatePixelReturn(randomChoice(spawnElements),x,y); + if(_np) { newPixel = _np } + }; + if(newPixel) { + newPixel.vy ??= 0; + newPixel.vy -= 20; + }; + if(newPixel && y > bottomTenPercent) { + newPixel.temp += 2000; + pixelTempCheck(newPixel) + }; + if(Math.random() < chance) { + explodeAt(x,y,radius,spawnElements ? ((",plasma".repeat(spawnElements.length * 14).replace(",","")) + "," + spawnElements.join(",")) : "plasma"); + }; + }; + }; + if(doColorChange) { + settings.bg = [_cc.b.h,_cc.b.h,_cc.b.h,_cc.b.h,"#29180e","#663814","#9e6f19","#f7af2a"]; + settings.bgAngle = 90; + }; + }; + elements.planet_cracker = { + color: "#ffc8ba", + behavior: behaviors.WALL, + properties: { + active: true, + counter: 1, + }, + tick: function(pixel) { + if(!pixel?.active) { + return; + }; + if(outOfBounds(pixel.x,pixel.y+pixel.counter)) { + planetCrackerFinale(true,pixel._bottomStateHighCache ?? null); + pixel.active = false; + changePixel(pixel,"metal_scrap"); + }; + if(pixel.active) { + pixel._bottomStateHighCache ??= []; + pixel._bottomStateHighCache = currentPixels.filter(p => p.y == height - 1).map(p => p.element); + var bshcUniques = Array.from(new Set(pixel._bottomStateHighCache)); + pixel._bottomStateHighCache = bshcUniques.filter(elemNameToFilterBy => (pixel._bottomStateHighCache.filter(elemName => elemName == elemNameToFilterBy).length >= (pixel._bottomStateHighCache.length / 10))); + pixel._bottomStateHighCache = pixel._bottomStateHighCache.map(elemName => getStateHigh(elemName) ?? elemName); + var pixelDistanceFromBottom = height - pixel.y; + var counterDistanceFromBottom = height - (pixel.y + pixel.counter); + var yRisingFromBottomToHalfway = Math.round((height - 1) - (pixel.counter / 2)); + + var closenessToBottom = 1 - (counterDistanceFromBottom / pixelDistanceFromBottom); + var finalRadius = Math.round(((1 + closenessToBottom) ** 2) * 6); + + var earthquakeChance = ((closenessToBottom >= 0.5) && (0.1 + (0.05 * (1 + (Math.max(closenessToBottom - 0.5,0) * 25))))) + var earthquakeMaxTryCount = 2 + (Math.random() < (closenessToBottom - 0.5)) + (Math.random() < (closenessToBottom - 0.75)) + (Math.random() < earthquakeChance) + (Math.random() < (earthquakeChance ** 2)); + for(var i = 0; i < earthquakeMaxTryCount; i++) { + if((closenessToBottom >= 0.5) && (Math.random() < earthquakeChance)) { //random earthquake + var rX = (Math.floor(Math.random() * (width - 1)) + 1); + var rY = Math.round(height / 2) + Math.floor(Math.random() * height / 2); + var eq = createOrChangePixelAndReturn("earthquake",rX,rY); + if(eq) { + eq.stage = 1; + eq.mag = Math.floor(Math.random() * ((5 * (1 + (Math.max(closenessToBottom - 0.5,0) * 2.5))) + 1)) + 15; + } + } + }; + + for(var x = 1; x < width; x++) { + var y = yRisingFromBottomToHalfway; + if(isEmpty(x,y,true)) { continue }; + var newPixel = pixelMap[x][y]; + newPixel.temp += 400 + ((1 - closenessToBottom) * 100); + pixelTempCheck(newPixel) + }; + + explodeAtPlus(pixel.x,pixel.y+pixel.counter,finalRadius,"plasma","fire",null,planetCrackerHeat); + pixel.counter++; + }; + }, + state: "solid", + density: 10000, + category: "weapons", + hardness: 1, + }; + //ASSORTED CELL EDITS AND GOLDEN FAIRY CHAIN ## + goldObject = { + gold: 1, + gold_coin: 1, + gold_fairy: 3 + }; + goldObjectNameArray = Object.keys(goldObject); + if(urlParams.get('goldFairyAmount') != null) { //null check + goldFairyAmount = urlParams.get('goldFairyAmount') + if(isNaN(goldFairyAmount) || goldFairyAmount === "" || goldFairyAmount === null) { //NaN check + goldFairyAmount = 10 + } + goldFairyAmount = parseInt(goldFairyAmount) + if(goldFairyAmount > 10000) { + alert("Maximum amount of additional gold fairies is 10000.\nOnly 10000 fairies were added.") + } else if(goldFairyAmount < 1) { + alert("Minimum amount of additional gold fairies is 1.\n1 fairy was added.") + } + goldFairyAmount = Math.min(10000,Math.max(goldFairyAmount,1)) + } else { + goldFairyAmount = 10 + } + elements.cell_1 = { //heats up + color: ["#bbee00","#eeee00","#cfee00"], + behavior: [ + "XX|CL%0.5|XX", + "CL%0.5|HT:1%2|CL%0.5", + "M2%10|M1|M2%10", + ], + uwu: 0, + properties: { + uwu: 0 + }, + tick: function(pixel) { + pixel.uwu = 0; + for (let i = -1; i < 2; i++) { //neighbor count + for (let j = -1; j < 2; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++; + }; + }; + }; + }; + pixel.uwu -= 1; //exclude self + var tempGrowthDecrease = null; + if(pixel.temp < 70) { + tempGrowthDecrease = 0; + } else if(pixel.temp > 100) { + tempGrowthDecrease = 70; + } else { + tempGrowthDecrease = pixel.temp - 70; + } + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + uwu = 0; + } else { + var tempGain = pixel.uwu / 48; + var chanceGain = pixel.uwu / 96; + }; + if(Math.random() < ( (2 + chanceGain) - (tempGrowthDecrease / 30) )) { + pixel.temp += tempGain; + }; + }, + reactions: { + "infection": { "elem1":"cancer", "chance":0.01 }, + "blood": { "elem1":"blood", "chance":0.01 }, + "antibody": { "elem1":"antibody", "chance":0.01 }, + "sugar": { "elem2":"cell", "chance":0.03 }, + "sugar_water": { "elem2":"cell", "chance":0.04 }, + "alcohol": { "elem1":[null,"dna"], "chance":0.02 }, + "poison": { "elem1":null, "chance":0.02 }, + "oxygen": { "elem2":"carbon_dioxide", "chance":0.05 }, + "ammonia": { "elem2":"nitrogen", "chance":0.05 } + }, + tempHigh: 122, + stateHigh: "steam", + tempLow: -6, + stateLow: "ice", + state: "solid", + density: 1000.2, + category: "life", + breakInto: ["water","dna","dna","dna"] + }; + elements.cell_2 = { //Grows just a bit too fast + color: ["#32280b","#5a4711","#87660c"], + behavior: [ + "XX|CL%1.8|XX", + "CL%1.8|XX|CL%1.8", + "M2%5|M1|M2%5", + ], + reactions: { + "cancer": { "elem2":"cell_2", "chance":0.035 }, + "cell": { "elem2":"cell_2", "chance":0.001 }, + "frog": { "elem2":"cell_2", "chance":0.001 }, + "fish": { "elem2":"cell_2", "chance":0.001 }, + "rat": { "elem2":"cell_2", "chance":0.001 }, + "bird": { "elem2":"cell_2", "chance":0.001 }, + "sugar": { "elem2":"cell_2", "chance":0.035 }, + "sugar_water": { "elem2":"cell_2", "chance":0.045 }, + "alcohol": { "elem1":[null,"dna"], "chance":0.012 }, + "poison": { "elem1":[null,null,"poison","poison","poison","dna","dirty_water"], "chance":0.03 }, + "proton": { "elem1":[null,"cancer"], "chance":0.04 } + }, + tempHigh: 80, + stateHigh: ["steam","plague"], + state: "solid", + density: 1000.17, + category: "life", + breakInto: ["dirty_water","dna","dna","dna","dna"] + }; + elements.cell_3 = { //Likes gold + color: ["#bbee00","#eeee00","#cfee00"], + behavior: [ + "XX|CL%0.5|XX", + "CL%0.5|HT:1%2|CL%0.5", + "M2%10|M1|M2%10", + ], + uwu: 0, + properties: { + uwu: 0 + }, + tick: function(pixel) { + pixel.uwu = 0; + for (let i = -1; i < 2; i++) { //neighbor count + for (let j = -1; j < 2; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++; + }; + }; + }; + }; + pixel.uwu -= 1; //exclude self + var tempGrowthDecrease = null; + if(pixel.temp < 70) { + tempGrowthDecrease = 0; + } else if(pixel.temp > 100) { + tempGrowthDecrease = 70; + } else { + tempGrowthDecrease = pixel.temp - 70; + } + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + uwu = 0; + } else { + var tempGain = pixel.uwu / 48; + var chanceGain = pixel.uwu / 96; + }; + if(Math.random() < ( (2 + chanceGain) - (tempGrowthDecrease / 30) )) { + pixel.temp += tempGain; + }; + }, + reactions: { + "infection": { "elem1":"cancer", "chance":0.01 }, + "blood": { "elem1":"blood", "chance":0.01 }, + "antibody": { "elem1":"antibody", "chance":0.01 }, + "sugar": { "elem2":"cell", "chance":0.03 }, + "sugar_water": { "elem2":"cell", "chance":0.04 }, + "alcohol": { "elem1":[null,"dna"], "chance":0.02 }, + "poison": { "elem1":null, "chance":0.02 }, + "oxygen": { "elem2":"carbon_dioxide", "chance":0.05 }, + "ammonia": { "elem2":"nitrogen", "chance":0.05 } + }, + tempHigh: 122, + stateHigh: "steam", + tempLow: -6, + stateLow: "ice", + state: "solid", + density: 1000.2, + category: "life", + breakInto: ["water","dna","dna","dna"] + }; + elements.cell_3 = { + color: ["#eeee00","#eecc00","#dddd00"], + behavior: [ + "XX|CL%0|XX", //CL%0 for display purposes + "CL%0|XX|CL%0", + "M2%10|M1|M2%10", + ], + reactions: { + "infection": { "elem1":"cancer", "chance":0.01 }, + "blood": { "elem1":"blood", "chance":0.01 }, + "antibody": { "elem1":"antibody", "chance":0.01 }, + "sugar": { "elem2":"cell", "chance":0.03 }, + "sugar_water": { "elem2":"cell", "chance":0.04 }, + "alcohol": { "elem1":[null,"dna"], "chance":0.02 }, + "poison": { "elem1":null, "chance":0.02 }, + "oxygen": { "elem2":"carbon_dioxide", "chance":0.05 }, + "ammonia": { "elem2":"nitrogen", "chance":0.05 } + }, + tempHigh: 102, + stateHigh: "steam", + tempLow: -2, + stateLow: "ice", + state: "solid", + density: 1000.1, + category: "life", + breakInto: ["water","dna","dna","dna"], + gold: 0, + properties: { + gold: 0 + }, + tick: function(pixel) { + var pX = pixel.x + var pY = pixel.y + var baseEatRate = 1; + var eatRate = baseEatRate + (2 * pixel.gold * 0.01); + if(Math.random() < eatRate/100) { + var randomNeighborNumber = Math.floor(Math.random() * 4) + var oX = adjacentCoords[randomNeighborNumber][0]; + var oY = adjacentCoords[randomNeighborNumber][1]; + var checkPosX = pX+oX; + var checkPosY = pY+oY; + if(!isEmpty(checkPosX,checkPosY,true)) { + if(goldObjectNameArray.includes(pixelMap[checkPosX][checkPosY].element)) { + pixel.gold += goldObject[pixelMap[checkPosX][checkPosY].element]; + deletePixel(checkPosX,checkPosY); + }; + }; + }; + var baseGrowthRate = 0.5; + var growthRate = baseGrowthRate + (pixel.gold * 0.025); + if(pixel.gold > 150) { + var chance = (pixel.gold - 150) / 20 + var baseRadius = 10 + var radius = Math.max(20,Math.round((baseRadius + (pixel.gold / 25)))) + if(Math.random() < (chance / 100)) { + explodeAt(pX,pY,10,"gold_coin"); + }; + }; + if(Math.random() < growthRate/100) { + var randomNeighborNumber = Math.floor(Math.random() * 4) + var oX = adjacentCoords[randomNeighborNumber][0]; + var oY = adjacentCoords[randomNeighborNumber][1]; + var checkPosX = pX+oX; + var checkPosY = pY+oY; + if(isEmpty(checkPosX,checkPosY,false)) { + var halfGold1 = pixel.gold / 2; + var halfGold2 = pixel.gold / 2; + if(halfGold1 % 1 == 0.5) { + halfGold1 = Math.ceil(halfGold1); + halfGold2 = Math.floor(halfGold2); + }; + createPixel(pixel.element,checkPosX,checkPosY); + newPixel = pixelMap[checkPosX][checkPosY]; + pixel.gold = halfGold1; + newPixel.gold = halfGold2; + }; + }; + }, + reactions: { + "infection": { "elem1":"cancer", "chance":0.01 }, + "blood": { "elem1":"blood", "chance":0.01 }, + "antibody": { "elem1":"antibody", "chance":0.01 }, + "sugar": { "elem2":"cell", "chance":0.03 }, + "sugar_water": { "elem2":"cell", "chance":0.04 }, + "alcohol": { "elem1":[null,"dna"], "chance":0.02 }, + "poison": { "elem1":null, "chance":0.02 }, + "oxygen": { "elem2":"carbon_dioxide", "chance":0.05 }, + "ammonia": { "elem2":"nitrogen", "chance":0.05 } + }, + tempHigh: 122, + stateHigh: "steam", + tempLow: -6, + stateLow: "ice", + state: "solid", + density: 1000.2, + category: "life", + breakInto: ["water","dna","dna","dna"] + }; + runAfterLoad(function() { + elements.gold_fairy = { + name: "gold fairy", + color: ["#cfa65b","#e6df63","#faf673"], + behavior: [ + "XX|M1|M1", + "XX|FX%5|XX", + "XX|CR:gold_coin%0.25 AND CR:fairy_dust%0.005 AND M1|M1", + ], + state: "solid", + category: "fey" + }; + elements.fairy.reactions.gold = { elem1: "gold_fairy", "elem2": null }; + elements.fairy.reactions.gold_coin = { elem1: "gold_fairy", "elem2": null }; + if(!elements.gold.reactions) { + elements.gold.reactions = {}; + }; + if(!elements.gold_coin.reactions) { + elements.gold_coin.reactions = {}; + }; + elements.gold.reactions.fairy = { elem1: null, elem2: "gold_fairy" }; + elements.gold_coin.reactions.fairy = { elem1: null, elem2: "gold_fairy" }; + for (var i = 2; i <= goldFairyAmount + 1; i++) { + nameVar = `gold_${i-1}-fairy` + if(i === 2) { + nameVar = "gold_fairy"; + }; + elements[`gold_${i}-fairy`] = { + name: `gold_${i}-fairy`, + color: ["#cfa65b","#e6df63","#faf673"], + behavior: [ + "XX|M1|M1", + "XX|FX%5|XX", + "XX|CR:" + nameVar + "%0.25 AND CR:fairy_dust%0.005 AND M1|M1", + ], + reactions: { + "glitter": { "elem1": `gold_${i+1}-fairy`, "elem2": null } + }, + state: "solid", + excludeRandom:true, + category: "fey", + hidden: true, + } + goldObject[`gold_${i}-fairy`] = 3 * i; + if (i == goldFairyAmount) { elements[`gold_${i}-fairy`]["reactions"] = {}; } + } + //eList rebuilding { + eLists.FAIRY.push("gold_fairy"); + for (var i = 2; i <= goldFairyAmount + 1; i++) { + eLists.FAIRY.push(`gold_${i}-fairy`); + }; + elements.iron.behavior = [ + "XX|DL:"+eLists.FAIRY+"|XX", + "DL:"+eLists.FAIRY+"|XX|DL:"+eLists.FAIRY+"", + "XX|DL:"+eLists.FAIRY+"|XX" + ]; + elements.silver.behavior = [ + "XX|DL:"+eLists.FAIRY+"|XX", + "DL:"+eLists.FAIRY+"|XX|DL:"+eLists.FAIRY+"", + "XX|DL:"+eLists.FAIRY+"|XX" + ]; + elements.tungstensteel.behavior = [ + "XX|DL:"+eLists.FAIRY+"|XX", + "DL:"+eLists.FAIRY+"|XX|DL:"+eLists.FAIRY+"", + "XX|DL:"+eLists.FAIRY+"|XX", + ]; + elements.molten_tungstensteel ??= {}; + elements.molten_tungstensteel.behavior = [ + "XX|DL:"+eLists.FAIRY+" AND CR:fire%2.5|XX", + "DL:"+eLists.FAIRY+" AND M2|XX|DL:"+eLists.FAIRY+" AND M2", + "M1|DL:"+eLists.FAIRY+"|M1", + ] + //} + goldObjectNameArray = Object.keys(goldObject); + elements.rainbow.reactions.gold_fairy = { "elem1": "gold_2-fairy", "elem2": null } + }); + //FIRE SLIME ## + elements.fire_slime = { + color: ["#e6683e", "#e37636", "#e38f3b", "#e3b039"], + behavior: [ + "XX|CR:fire%5|XX", + "M2|XX|M2", + "M1%1 AND M2|M1|M1%1 AND M2" + ], + reactions: { + "bomb": { "elem2":"sticky_bomb", "elem2":null }, + }, + tick: function(pixel) { + if(Math.random() < 0.01) { + pixel.temp++; + pixelTempCheck(pixel); + }; + if(pixel.temp < 700) { + if(Math.random() < 0.02) { + pixel.temp++; + pixelTempCheck(pixel); + }; + }; + }, + viscosity: 3000, + temp: 700, + tempHigh: 6000, + stateHigh: "plasma", + tempLow: -13, + stateLow: "suppressed_fire_slime", + category: "liquids", + state: "liquid", + burning: true, + burnTime: Number.MAX_SAFE_INTEGER, + burn: 85, + burnInto: "fire_slime", + density: 1400, + stain: 0.05 + } + elements.suppressed_fire_slime = { + color: "#bf6a4e", + behavior: [ + "XX|CR:smoke%1|XX", + "M2|XX|M2", + "M1%0.5 AND M2|M1|M1%0.5 AND M2" + ], + reactions: { + "bomb": { "elem2":"sticky_bomb", "elem2":null }, + }, + tick: function(pixel) { + if(Math.random() < 0.001) { + pixel.temp++; + pixelTempCheck(pixel); + }; + }, + viscosity: 4000, + temp: -20, + tempHigh: -13, + stateHigh: "fire_slime", + category: "liquids", + state: "liquid", + burning: false, + burnTime: 1000, + burn: 1, + burnInto: "fire_slime", + density: 1550, + stain: 0.04, + hidden: true + } + //FWIBBLENS ## + elements.fwibblen = { + color: ["#73F092", "#EB7373", "#EDBC7B"], + behavior: [ + "XX|M2|M1", + "XX|FX%2|BO", + "XX|XX|M1", + ], + reactions: { + nickel: { elem2: ["fwibblen",null] }, + nickel_scrap: { elem2: ["fwibblen",null] } + }, + tempHigh: 1100, + stateHigh: "dead_fwibblen", + category:"life", + state: "solid", + density: 1500, + conduct: 0.4, + } + elements.dead_fwibblen = { + color: ["#729E7D", "#B58484", "#C4B299"], + behavior: behaviors.POWDER, + tempHigh: 1455, + stateHigh: ["molten_nickel","molten_nickel","molten_nickel","smoke"], + category:"life", + hidden: true, + state: "solid", + density: 1400, + conduct: 0.4, + } + elements.meffwibblen = { + color: ["#5CE697", "#F05B60", "#EB9560"], + behavior: [ + "XX|M2|M1", + "XX|FX%2|BO", + "XX|XX|M1", + ], + reactions: { + fwibblen: { elem2: ["meffwibblen",null] }, + dead_fwibblen: { elem2: ["meffwibblen",null] } + }, + tempHigh: 1150, + stateHigh: "dead_meffwibblen", + category:"life", + state: "solid", + density: 1800, + conduct: 0.44, + } + elements.dead_meffwibblen = { + color: ["#659E7D", "#B37073", "#B08F7B"], + behavior: behaviors.POWDER, + tempHigh: 1455, + stateHigh: ["molten_nickel","molten_nickel","molten_nickel","molten_nickel","smoke"], + category:"life", + hidden: true, + state: "solid", + density: 1800, + conduct: 0.44, + } + //Debug world + /*worldgentypes.nickel = { + layers: [ + [0, "nickel"], + ] + };*/ + //EXPERIMENTAL CONTROLLABLE PIXEL ## + sussyKey = null; + isShift = false; + isAlt = false; + document.addEventListener("keydown", function(modifierDownListener) { + // User presses shift + if (modifierDownListener.keyCode == 16) { + isShift = true; + } + // User presses alt + if (modifierDownListener.keyCode == 18) { + isAlt = true; + } + }); + document.addEventListener("keyup", function(modifierUpListener) { + // User releases shift + if (modifierUpListener.keyCode == 16) { + isShift = false; + } + // User releases alt + if (modifierUpListener.keyCode == 18) { + isAlt = false; + } + }); + document.addEventListener("keyup", function(sussyListener) { + switch (sussyListener.keyCode) { + case 87: + sussyKey = "W"; + break; + case 65: + sussyKey = "A"; + break; + case 83: + sussyKey = "S"; + break; + case 68: + sussyKey = "D"; + break; + case 81: + sussyKey = "Q"; + break; + case 88: + sussyKey = "X"; + break; + case 90: + sussyKey = "Z"; + break; + case 72: + sussyKey = "H"; + break; + }; + }); + function controllablePixelTryCreatePixelNullCheck(element,x,y) { + if(!elements[element]) { //catch the null + return false; + }; + if(isEmpty(x,y)) { + tryCreatePixel(element,x,y); + return true; + } else { + return false; + } + } + elements.controllable_pixel = { + color: _cc.w.h, + colorOn: "#FFFF00", + behavior: behaviors.WALL, + state: "solid", + density: 2000, + maxSize: 1, + conduct: 1, + hardness: 1, + tick: function(pixel) { + var xx = pixel.x; + var yy = pixel.y; + userElement = currentElement; + if(userElement === pixel.element) { + userElement = null; + }; + if(isShift && !isAlt) { + sussyKey === "Z" ? pixel.color = "rgb(255,191,127)" : pixel.color = "rgb(255,127,127)"; + } + if(isAlt && !isShift) { + sussyKey === "Z" ? pixel.color = "rgb(191,255,127)" : pixel.color = "rgb(127,255,127)"; + } + if(isAlt && isShift) { + sussyKey === "Z" ? pixel.color = "rgb(255,255,0)" : pixel.color = "rgb(255,255,127)"; + } + if(!isAlt && !isShift) { + sussyKey === "Z" ? pixel.color = "rgb(255,255,191)" : pixel.color = _cc.w.r; + } + if(sussyKey !== null) { + switch (sussyKey) { + case "W": + isAlt ? controllablePixelTryCreatePixelNullCheck(userElement,xx,yy-1) : tryMove(pixel,xx,yy-1); + if(!isShift) { + sussyKey = null; + } + break; + case "A": + isAlt ? controllablePixelTryCreatePixelNullCheck(userElement,xx-1,yy) : tryMove(pixel,xx-1,yy); + if(!isShift) { + sussyKey = null; + } + break; + case "S": + isAlt ? controllablePixelTryCreatePixelNullCheck(userElement,xx,yy+1) : tryMove(pixel,xx,yy+1); + if(!isShift) { + sussyKey = null; + } + break; + case "D": + tryMove(pixel,xx+1,yy); + if(!isShift) { + sussyKey = null; + } + break; + case "H": //Alt+D is something else in some browsers. + if(isAlt) { + controllablePixelTryCreatePixelNullCheck(userElement,xx+1,yy); + }; + if(!isShift) { + sussyKey = null; + } + break; + case "X": + explodeAt(xx,yy,5) + if(!isShift) { + sussyKey = null; + } + break; + case "Z": + if (!pixel.charge && !pixel.chargeCD && !isEmpty(pixel.x,pixel.y,true)) { + pixel.charge = 1; + } + if(!isShift === 0) { + sussyKey = null; + } + break; + case "Q": //Use if a key gets stuck + sussyKey = null; + isShift = null; + isAlt = null; + break; + } + } + }, + } + actExcludedElements = ["wall","alt_controllable_pixel"]; + function actTryMove(pixel,x,y) { + if(!tryMove(pixel,x,y)) { + if(outOfBounds(x,y)) { + return false; + }; + if(!isEmpty(x,y,true)) { + var newPixel = pixelMap[x][y]; + var newElement = newPixel.element; + if(actExcludedElements.includes(newElement)) { + return false; + }; + if(newElement == pixel.element) { //Copy-paste of "break" code + swapPixels(pixel,newPixel); + return true; + } else { + breakCircle(newPixel.x,newPixel.y,pixel.breakAroundRadius,false,false,false); //does nothing to elements without breakIntos if defaultBreakIntoDust is false + swapPixels(pixel,newPixel); + return true; + }; + } else { + return false; + }; + } else { + return true; + }; + }; + function cptaEapFunction(pixel,x,y,radius,fire,smoke,power,damage) { + var coords = circleCoords(pixel.x,pixel.y,radius); + for (var i = 0; i < coords.length; i++) { + var x = coords[i].x; + var y = coords[i].y; + if(isEmpty(x,y)) { //if there's space for fire + if (Array.isArray(fire)) { //this should remain "fire" + var newfire = fire[Math.floor(Math.random() * fire.length)]; + } else { + var newfire = fire; + }; + createPixel(newfire,x,y); //add fire + var firePixel = pixelMap[x][y]; + firePixel.temp = Math.max(elements[newfire].temp,firePixel.temp); + firePixel.burning = true; + }; + if(!isEmpty(x,y,true)) { + pixel.temp += (400 * ((1 + (5 * damage)) ** 2) * ((power ** 1.5) * 1.5)); + pixelTempCheck(pixel); + }; + if (!elements[pixel.element].excludeVelocity) { + var angle = Math.atan2(pixel.y-y,pixel.x-x); + pixel.vx = Math.round((pixel.vx|0) + Math.cos(angle) * (radius * power/10)); + pixel.vy = Math.round((pixel.vy|0) + Math.sin(angle) * (radius * power/10)); + } + }; + }; + alt_sussyKey = null; + isShift = false; + isAlt = false; + document.addEventListener("keydown", function(modifierDownListener) { + // User presses shift + if (modifierDownListener.keyCode == 16) { + isShift = true; + } + // User presses alt + if (modifierDownListener.keyCode == 18) { + isAlt = true; + } + }); + document.addEventListener("keyup", function(modifierUpListener) { + // User releases shift + if (modifierUpListener.keyCode == 16) { + isShift = false; + } + // User releases alt + if (modifierUpListener.keyCode == 18) { + isAlt = false; + } + }); + document.addEventListener("keyup", function(alt_sussyListener) { + switch (alt_sussyListener.keyCode) { + case 87: + alt_sussyKey = "W"; + break; + case 65: + alt_sussyKey = "A"; + break; + case 83: + alt_sussyKey = "S"; + break; + case 68: + alt_sussyKey = "D"; + break; + case 81: + alt_sussyKey = "Q"; + break; + case 88: + alt_sussyKey = "X"; + break; + case 90: + alt_sussyKey = "Z"; + break; + case 72: + alt_sussyKey = "H"; + break; + case 76: + alt_sussyKey = "L"; + break; + }; + }); + function controllablePixelTryCreatePixelNullCheck(element,x,y) { + if(!elements[element]) { //catch the null + return false; + }; + if(isEmpty(x,y)) { + tryCreatePixel(element,x,y); + return true; + } else { + return false; + } + } + elements.alt_controllable_pixel = { + color: "#7F7F7F", + colorOn: "#FFFF00", + behavior: behaviors.WALL, + state: "solid", + density: 2000, + maxSize: 1, + conduct: 1, + hardness: 1, + movable: true, + desc: "it breaks things more
Use the console or enable prop.js to set speed / explosionRadius / circleRadius / breakAroundRadius (though it has an Illogical™ random resetting bug)", + breakInto: "alt_controllable_pixel", + properties: { + speed: 1, + explosionRadius: 8, + circleRadius: 8, + breakAroundRadius: 4 + }, + excludeVelocity: true, + tick: function(pixel) { + userElement = currentElement; + if(userElement === pixel.element) { + userElement = null; + }; + if(isShift && !isAlt) { + alt_sussyKey === "Z" ? pixel.color = "rgb(255,191,127)" : pixel.color = "rgb(255,127,127)"; + } + if(isAlt && !isShift) { + alt_sussyKey === "Z" ? pixel.color = "rgb(191,255,127)" : pixel.color = "rgb(127,255,127)"; + } + if(isAlt && isShift) { + alt_sussyKey === "Z" ? pixel.color = "rgb(255,255,0)" : pixel.color = "rgb(255,255,127)"; + } + if(!isAlt && !isShift) { + alt_sussyKey === "Z" ? pixel.color = "rgb(255,255,191)" : pixel.color = _cc.w.r; + } + if(alt_sussyKey !== null) { + switch (alt_sussyKey) { + case "W": + if(isAlt) { controllablePixelTryCreatePixelNullCheck(userElement,pixel.x,pixel.y-1) } else { for(move = 0; move < pixel.speed; move++) { actTryMove(pixel,pixel.x,pixel.y-1) } }; + if(!isShift) { + alt_sussyKey = null; + } + break; + case "A": + if(isAlt) { controllablePixelTryCreatePixelNullCheck(userElement,pixel.x-1,pixel.y) } else { for(move = 0; move < pixel.speed; move++) { actTryMove(pixel,pixel.x-1,pixel.y) } }; + if(!isShift) { + alt_sussyKey = null; + } + break; + case "S": + if(isAlt) { controllablePixelTryCreatePixelNullCheck(userElement,pixel.x,pixel.y+1) } else { for(move = 0; move < pixel.speed; move++) { actTryMove(pixel,pixel.x,pixel.y+1) } }; + if(!isShift) { + alt_sussyKey = null; + } + break; + case "D": + for(move = 0; move < pixel.speed; move++) { actTryMove(pixel,pixel.x+1,pixel.y) }; + if(!isShift) { + alt_sussyKey = null; + } + break; + case "H": //Alt+D is something else in some browsers. + if(isAlt) { + controllablePixelTryCreatePixelNullCheck(userElement,pixel.x+1,pixel.y); + }; + if(!isShift) { + alt_sussyKey = null; + } + break; + case "X": + explodeAtPlus(pixel.x,pixel.y,pixel.explosionRadius,"plasma,fire,fire","fire,smoke,smoke",null,cptaEapFunction) + if(!isShift) { + alt_sussyKey = null; + } + break; + case "Z": + if (!pixel.charge && !pixel.chargeCD && !isEmpty(pixel.x,pixel.y,true)) { + pixel.charge = 1; + } + if(!isShift === 0) { + alt_sussyKey = null; + } + break; + case "L": + if(userElement !== null) { + fillCircle(currentElement,pixel.x,pixel.y,pixel.circleRadius,false); + if(!isShift === 0) { + alt_sussyKey = null; + } + }; + break; + case "Q": //Use if a key gets stuck + alt_sussyKey = null; + isShift = null; + isAlt = null; + break; + } + } + } + } + //PARTIAL APIOFORM GAME ## + playerKills = 0; + aplReceivedKey = null; + isShift = false; + isAlt = false; + document.addEventListener("keydown", function(modifierDownListener) { + // User presses shift + if (modifierDownListener.keyCode == 16) { + isShift = true; + } + // User presses alt + if (modifierDownListener.keyCode == 18) { + isAlt = true; + } + }); + document.addEventListener("keyup", function(modifierUpListener) { + // User releases shift + if (modifierUpListener.keyCode == 16) { + isShift = false; + } + // User releases alt + if (modifierUpListener.keyCode == 18) { + isAlt = false; + } + }); + entities = ["apioform_bee", "apioform", "apiodiagoform", "apiokinetoform", "apiopyroform", "cryoapioform", "apiopariform"] + function apioTryMove(pixel,nx,ny,leaveBehind=undefined) { + if(!isEmpty(nx,ny,true) && !outOfBounds(nx.ny)) { + otherPixel = pixelMap[nx][ny]; + var thisElement = pixel.element; + var otherElement = otherPixel.element; + if(thisElement == "apioform_player") { + if(entities.includes(otherElement)) { + deletePixel(nx,ny); + playerKills++; + movePixel(pixel,nx,ny,leaveBehind) + }; + } else if(entities.includes(thisElement)) { + if(otherElement == "apioform_player") { + deletePixel(nx,ny); + movePixel(pixel,nx,ny,leaveBehind) + }; + }; + } else { + tryMove(pixel,nx,ny,leaveBehind); + }; + } + document.addEventListener("keyup", function(apioformPlayerListener) { + switch (apioformPlayerListener.keyCode) { + case 38: //arrow up, letter 87: + aplReceivedKey = "W"; + break; + case 37: //arrow left, letter 65 + aplReceivedKey = "A"; + break; + case 40: //arrow down, letter 83 + aplReceivedKey = "S"; + break; + case 39: //arrow right, letter 68 + aplReceivedKey = "D"; + break; + case 27: //escape key, letter 81 + aplReceivedKey = "Q"; + break; + }; + }); + elements.apioform_player = { + color: "#7F7F7F", + behavior: behaviors.WALL, + state: "solid", + density: 2000, + maxSize: 1, + hardness: 0.8, + burnTime: 10, + burn: 50, + category: "apioform game", + tick: function(pixel) { + var xx = pixel.x; + var yy = pixel.y; + if(isShift) { + pixel.color = "rgb(255,127,127)"; + } else { + pixel.color = _cc.w.r; + } + if(aplReceivedKey !== null) { + switch (aplReceivedKey) { + case "W": + apioTryMove(pixel,xx,yy-1); + if(!isShift) { + aplReceivedKey = null; + } + break; + case "A": + apioTryMove(pixel,xx-1,yy); + if(!isShift) { + aplReceivedKey = null; + } + break; + case "S": + apioTryMove(pixel,xx,yy+1); + if(!isShift) { + aplReceivedKey = null; + } + break; + case "D": + apioTryMove(pixel,xx+1,yy); + if(!isShift) { + aplReceivedKey = null; + } + break; + case "Q": //Use if a key gets stuck + aplReceivedKey = null; + isShift = null; + break; + }; + }; + }, + } + elements.apioform_bee = { + color: ["#808020", "#A0A050"], + properties: { + stage: ((Math.random() < 0.5) ? 0 : 1), + }, + behavior: behaviors.WALL, + state: "solid", + category: "apioform game", + density: 2000, + maxSize: 1, + hardness: 0.8, + flippableY: true, + tick: function(pixel) { + if(isNaN(pixel.stage)) { + pixel.stage = ((Math.random() < 0.5) ? 0 : 1); + }; + pixel.stage = Math.min(1,Math.max(0,Math.round(pixel.stage))); //stage validation + var moveCoords = (pixel.flipY ? [pixel.x, pixel.y+1] : [pixel.x, pixel.y-1]); + //direction is randomized by flippableY + if(pixel.stage === 0) { //color + pixel.color = "rgb(128,128,32)"; + } else if(pixel.stage === 1) { + pixel.color = "rgb(160,160,80)"; + }; + if(pixelTicks - pixel.start > 0) { //don't move on first tick, to allow color to normalize + if(pixel.stage === 0) { //if in still stage + pixel.stage = 1; //change to moving stage + } else if(pixel.stage === 1) { //else if in moving stage + apioTryMove(pixel,moveCoords[0],moveCoords[1]); //move + pixel.flipY = !pixel.flipY; //flip that + pixel.stage = 0; //change to still stage + }; + }; + }, + } + elements.apioform = { + color: "#A08020", + behavior: behaviors.WALL, + state: "solid", + category: "apioform game", + density: 2000, + maxSize: 1, + hardness: 0.8, + rotatable: true, + tick: function(pixel) { + if(isNaN(pixel.r)) { + pixel.r = Math.floor(Math.random() * 4); + }; + pixel.r = Math.round(pixel.r) % 4; //rotation validation, probably redundant since rotation is from the base game + var moveCoords; + switch (pixel.r) { + case 0: + moveCoords = [pixel.x+1, pixel.y+0]; + break; + case 1: + moveCoords = [pixel.x+0, pixel.y-1]; + break; + case 2: + moveCoords = [pixel.x-1, pixel.y+0]; + break; + case 3: + moveCoords = [pixel.x+0, pixel.y+1]; + break; + }; + //rotation is randomized by rotatable + if(pixelTicks - pixel.start > 0) { //maintained for consistency + apioTryMove(pixel,moveCoords[0],moveCoords[1]); //move + pixel.r++; //increment rotation + }; + }, + } + elements.apiodiagoform = { + color: "#A080A0", + behavior: behaviors.WALL, + state: "solid", + category: "apioform game", + density: 2000, + maxSize: 1, + hardness: 0.8, + rotatable: true, + tick: function(pixel) { + if(isNaN(pixel.r)) { + pixel.r = Math.floor(Math.random() * 4); + }; + pixel.r = Math.round(pixel.r) % 4; //rotation validation, probably redundant since rotation is from the base game + var moveCoords; + switch (pixel.r) { + case 0: + moveCoords = [pixel.x-1, pixel.y-1]; + break; + case 1: + moveCoords = [pixel.x-1, pixel.y+1]; + break; + case 2: + moveCoords = [pixel.x+1, pixel.y+1]; + break; + case 3: + moveCoords = [pixel.x+1, pixel.y-1]; + break; + }; + //rotation is randomized by rotatable + if(pixelTicks - pixel.start > 0) { //maintained for consistency + apioTryMove(pixel,moveCoords[0],moveCoords[1]); //move + pixel.r++; //increment rotation + }; + }, + } + //AMOGUS ## + elements.amogus = { + name: "Amogus", + color: _cc.w.h, + cooldown: 6, + tick: function(pixel) { + pixel.arr=[["brick", "brick", "brick"], + ["brick", "glass", "glass"], + ["brick", "brick", "brick"], + ["brick", "air", "brick"]]; + pixel.carr=[[ "rgb(255,0,0)", "rgb(255,0,0)", "rgb(255,0,0)" ], + [ "rgb(255,0,0)", "rgb(0,255,255)", "rgb(0,255,255)"], + [ "rgb(255,0,0)", "rgb(255,0,0)", "rgb(255,0,0)" ], + [ "rgb(255,0,0)", "null", "rgb(255,0,0)" ]]; + var aa = (0 - (Math.floor(pixel.arr[0].length / 2))) //Center align code + var na = Math.abs(aa) + if(pixel.arr[0].length % 2 == 1) { + var bb = ((Math.floor(pixel.arr[0].length / 2)) + 1) + } else if(pixel.arr[0].length % 2 == 0) { + var bb = (Math.floor(pixel.arr[0].length / 2)) + } + var cc = (0 - (Math.floor(pixel.arr.length / 2))) + var nc = Math.abs(cc) + if(pixel.arr.length % 2 == 1) { + var dd = ((Math.floor(pixel.arr.length / 2)) + 1) + } else if(pixel.arr.length % 2 == 0) { + var dd = (Math.floor(pixel.arr.length / 2)) + } + for (let j = cc; j < dd; j++) { //Iterative placing and coloring of pixels + for (let i = aa; i < bb; i++) { + if(!isEmpty(pixel.x+i,pixel.y+j,true)) { + if(pixel.arr[j+nc][i+na] !== "null") { + deletePixel(pixel.x+i,pixel.y+j) + } + } + if(pixel.arr[j+nc][i+na]) { + if(isEmpty(pixel.x+i,pixel.y+j,false) && pixel.arr[j+nc][i+na] !== "null" && pixel.arr[j+nc][i+na] !== "air") { + createPixel(pixel.arr[j+nc][i+na],pixel.x+i,pixel.y+j) + if(pixel.carr[j+nc][i+na]) { + if(!isEmpty(pixel.x+i,pixel.y+j,true) && pixel.carr[j+nc][i+na] != "null") { + pixelMap[pixel.x+i][pixel.y+j].color = pixel.carr[j+nc][i+na] + } + } + } + } + } + } + }, + category:"structures", + insulate: true, + state: "solid", + excludeRandom: true, + }, + elements.amogus_seed = { + name: "Amogus Seed", + color: "#df2f47", + cooldown: 6, + behavior: [ + "DL:amogus_seed|DL:amogus_seed AND M2|DL:amogus_seed", + "DL:amogus_seed|C2:amogus|DL:amogus_seed", + "DL:amogus_seed|SW:amogus_seed AND DL:amogus_seed AND M1|DL:amogus_seed" + ], + category:"structures", + insulate: true, + state: "solid", + density: 2018, + } + //BACTERIA ## + elements.must = { + color: ["#0C433B","#2C3E2E","#173E2B","#053D20"], + behavior: behaviors.SUPPORTPOWDER, + category: "special", + state: "solid", + density: 1200, + conduct: 0.11 + }; + elements.water.reactions.bread = { + elem2: "must", chance: 0.005 + }; + elements.water.reactions.toast = { + elem2: "must", chance: 0.005 + }; + elements.water.reactions.must = { + elem2: "ripe_must", chance: 0.005 + }; + elements.ripe_must = { + color: ["#248f6d","#597a57","#3c8054","#1d8b40"], + behavior: behaviors.SUPPORTPOWDER, + category: "special", + state: "solid", + reactions: { + must: {elem1: "ripe_must", chance: 0.0004} + }, + density: 1210, + conduct: 0.15, + breakInto: "bacteria_glob", //"the original name "bunch of bacteria" doesn't fit the sandboxels context IMO + hardness: 0.1 + }; + elements.bacteria_glob = { + color: ["#e34634","#c24332", "#d1703f"], + behavior: [ + "XX|XX|XX", + "M2%25|XX|M2%25", + "M2|M1|M2" + ], + reactions: { + "metal_scrap": {elem1: "bacteria", elem2: ["metal_scrap",null] }, //substituting for redstone torches, which will be implemented later alongside another mod + "charcoal": {elem1: "replacer_bacteria", elem2: ["charcoal",null] } //substituting for coal, which will also be added later + }, + category: "special", + state: "liquid", + density: 1190, + conduct: 0.15, + breakInto: "bacteria_glob" //"the original name "bunch of bacteria" doesn't fit the sandboxels context IMO + }; + bacteriaBlacklist = ["bacteria","replacer_bacteria","jammer_block"]; + elements.bacteria = { + color: ["#e6d3f2", "#c098d9", "#6e318f", "#6e318f"], + behavior: behaviors.WALL, + tick: function(pixel) { + pixel.target ??= null; + if(pixel.charge) { //when shocked + if(!pixel.target) { + var elemsAbove = []; + var pX = pixel.x; var pY = pixel.y; + for(var counter = pY - 1; counter > 0; counter--) { + //console.log(pX,pY); + if(isEmpty(pX,counter,true)) { + break + }; + var newPixel = pixelMap[pX][counter]; + var newElement = newPixel.element; + if(bacteriaBlacklist.includes(newElement)) { break }; + if(!(elemsAbove.includes(newElement))) { + elemsAbove.push(newElement) + }; + }; + if(elemsAbove.length == 0) { + return + } else { + pixel.target = elemsAbove + //console.log(pixel.target); + pixel.charge = 0; + } + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //check if a pixel exists below to convert + if(!pixel.active && !(bacteriaBlacklist.includes(pixelMap[pixel.x][pixel.y+1].element))) { //exclude certain blocks and only fire once + pixel.active = true + } + } + }; + if(pixel.active) { + var pX = pixel.x; var pY = pixel.y; + if(pixel.target) { //safety + for(i = 0; i < adjacentCoords.length; i++) { //iterate through neighbor spots + var nX = pX + adjacentCoords[i][0]; var nY = pY + adjacentCoords[i][1]; + if(!isEmpty(nX,nY,true)) { //check for neighbors + var newPixel = pixelMap[nX][nY]; + if(pixel.target.includes(newPixel.element)) { //if neighbor element is the target + changePixel(newPixel,pixel.element) //change neighbors to itself + newPixel.target = pixel.target //set new bacteria target + newPixel.active = true //activate new bacteria + } + } + } + } + if(Math.random() < 0.02) { //decay + if(!isEmpty(pixel.x,pixel.y)) { //check if position is empty + deletePixel(pixel.x,pixel.y) + } + } + } + }, + category: "special", + state: "solid", + density: 1200, + conduct: elements.water.conduct + 0.1, + }, + elements.replacer_bacteria = { + color: ["#fcbbc0", "#f28089", "#f04f5c", "#f04f5c"], + behavior: behaviors.WALL, + tick: function(pixel) { + pixel.target ??= null; + pixel.replacement ??= null; + if(pixel.charge) { //when shocked + if(!pixel.target) { + if(!isEmpty(pixel.x,pixel.y+1,true)) { + pixelBelowElement = pixelMap[pixel.x][pixel.y+1].element; + if(!(bacteriaBlacklist.includes(pixelBelowElement))) { + pixel.target = pixelBelowElement; + } + } + }; + if(!pixel.replacement) { + if(!isEmpty(pixel.x,pixel.y-1,true)) { + pixelAboveElement = pixelMap[pixel.x][pixel.y-1].element + if(!(bacteriaBlacklist.includes(pixelAboveElement))) { + pixel.replacement = pixelAboveElement; + } + } + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //check if a pixel exists below to convert + if((!pixel.active) && pixel.target && pixel.replacement) { //only fire once + pixel.active = true + } + } + }; + if(pixel.active) { + var pX = pixel.x; var pY = pixel.y; + if(pixel.target) { //safety + for(i = 0; i < adjacentCoords.length; i++) { //iterate through neighbor spots + var nX = pX + adjacentCoords[i][0]; var nY = pY + adjacentCoords[i][1]; + if(!isEmpty(nX,nY,true)) { //check for neighbors + var newPixel = pixelMap[nX][nY]; + if(pixel.target == newPixel.element) { //if neighbor element is the target + changePixel(newPixel,pixel.element) //change neighbors to itself + newPixel.target = pixel.target //set new bacteria target + newPixel.replacement = pixel.replacement //set new bacteria replacement + newPixel.active = true //activate new bacteria + } + } + } + } + if(Math.random() < 0.05) { //decay + if(!isEmpty(pixel.x,pixel.y)) { //check if position is empty + changePixel(pixel,pixel.replacement) + } + } + } + }, + category: "special", + state: "solid", + density: 1200, + conduct: elements.water.conduct + 0.1, + }, + elements.jammer_block = { + color: "#c0cf7e", + behavior: behaviors.WALL, + tick: function(pixel) { + neighbors = [[-1,0],[0,-1],[1,0],[0,1]] + if(pixel.charge) { //when shocked + for (var i = 0; i < width; i++) { + for (var j = 0; j < height; j++) { + if(isEmpty(i,j,true) == false) { + if(pixelMap[i][j].element == "bacteria") { + if(isEmpty(i,j,true) == false) { deletePixel(i,j) } + } else if(pixelMap[i][j].element == "replacer_bacteria") { + if(pixelMap[i][j].replacement) { + if(isEmpty(i,j,true) == false) { changePixel(pixelMap[i][j],pixelMap[i][j].replacement) } + } else { + if(isEmpty(i,j,true) == false) { deletePixel(i,j) } + } + } + } + } + } + } + }, + category: "special", + state: "solid", + density: 3000, + conduct: elements.water.conduct + 0.1, + } + //REPLACE ALL + elements.replacer_bacteria.tempHigh = 10000000000; + elements.replacer_bacteria.stateHigh = "replace_all"; + elements.replace_all = { + color: "#ef7f3f", + behavior: behaviors.WALL, + tick: function(pixel) { + if(pixel.charge) { //when shocked + //console.log("ouch") + if(!outOfBounds(pixel.x,pixel.y+1) && !isEmpty(pixel.x,pixel.y+1) && !outOfBounds(pixel.x,pixel.y-1) && !isEmpty(pixel.x,pixel.y-1)) { //check if pixels exists above and below to store the elements of + //console.log("elems stored") + if(pixelMap[pixel.x][pixel.y-1].element != pixel.element) { //exclude self and only fire once + //console.log("self excluded from replacement") + pixel.target = pixelMap[pixel.x][pixel.y+1].element + //console.log("target set to " + pixel.target) + pixel.replacement = pixelMap[pixel.x][pixel.y-1].element + //console.log("replacement set to " + pixel.replacement) + pixel.active = true + //console.log("replacer activated") + } + } + } + if(pixel.active) { + //console.log("is this on now?") + if(pixel.target && pixel.replacement) { //safety + //console.log("target and replacement exist, iterating...") + for (var i = 0; i < width; i++) { + for (var j = 0; j < height; j++) { + if(isEmpty(i,j,true) == false) { + //console.log("pixel at (" + i + "," + j + ") exists") + if(pixelMap[i][j].element == pixel.target) { + //console.log(pixel.target + " detected, replacing") + if(isEmpty(i,j,true) == false) { changePixel(pixelMap[i][j],pixel.replacement) } + } + } + } + } + } + pixel.active = false //de-activate + if(pixel.charge) { delete pixel.charge} + if(pixel.chargeCD) { delete pixel.chargeCD} + } + }, + category: "special", + state: "solid", + density: 1, + conduct: elements.water.conduct + 0.1, + }; + //CONFIGURABLE PIXEL SIZE ## + //The "pixelSize" query parameter sets the size of the pixels; this is inversely proportional to the pixel "resolution", so bigger numbers mean less pixels fit on the screen and smaller numbers mean that more pixels will fit. + //Depending on your screen's size, the default pixelSize is either 5 or 6 (6 on larger screens). + //Making the pixels twice as big will decrease the pixel capacity by *slightly over* 4, and the reverse is also true. (I don't know why that is.) + if(urlParams.get('pixelSize') != null) { //null check + pixelSize = urlParams.get('pixelSize') + if(isNaN(pixelSize) || pixelSize === "" || pixelSize === null) { //NaN check + //Vanilla code + pixelSize = settings.pixelsize || 6; + if (window.innerWidth < 700) { + pixelSize--; + } + } + pixelSize = parseFloat(pixelSize) + pixelSize = Math.min(194.73749999999999,Math.max(pixelSize,0.05)) + } else { + //Vanilla code + pixelSize = settings.pixelsize || 6; + if (window.innerWidth < 700) { + pixelSize--; + } + } + //OLD SAVE SYSTEM MOD ## + //used for reasons unrelated to dependencies + try { + if(typeof(rebuildCurrentPixels) !== "function") { + rebuildCurrentPixels = function() { + var currPix = []; //rebuild currentPixels from pixelMap to try to fix bug + for(pmi = 0; pmi < pixelMap.length; pmi++) { + var pixelMapPart = pixelMap[pmi]; + for(pmj = 0; pmj < pixelMapPart.length; pmj++) { + var pixelMapUnit = pixelMapPart[pmj]; + if(typeof(pixelMapUnit) === "object") { + if(pixelMapUnit !== null) { + currPix.push(pixelMapUnit); + }; + }; + }; + }; + currentPixels = currPix; + }; + }; + //https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API + function storageAvailable(type) { + let storage; + try { + storage = window[type]; + const x = "__storage_test__"; + storage.setItem(x, x); + storage.removeItem(x); + return true; + } catch (e) { + return ( + e instanceof DOMException && + // everything except Firefox + (e.code === 22 || + // Firefox + e.code === 1014 || + // test name field too, because code might not be present + // everything except Firefox + e.name === "QuotaExceededError" || + // Firefox + e.name === "NS_ERROR_DOM_QUOTA_REACHED") && + // acknowledge QuotaExceededError only if there's something already stored + storage && + storage.length !== 0 + ); + } + } + function zeroToNull(val) { + if(val === 0) { return null }; + return val; + }; + if(!localStorage.slSaveSettings) { + localStorage.setItem("slSaveSettings", '{"roundTemps":true}'); + }; + slSaveSettings = JSON.parse(localStorage.slSaveSettings); + function epsilonRound(num,precision) { + return Math.round((num + Number.EPSILON) * (10 ** precision)) / (10 ** precision); + }; + function getSimulationState() { + var simulationState = { + //currentPixels: currentPixels, + pixelMap: structuredClone ? structuredClone(pixelMap) : JSON.parse(JSON.stringify(pixelMap)), + width: width, + height: height, + pixelTicks: pixelTicks, + pixelSize: pixelSize, + settings: settings, + 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++) { + if(column[j] === null || typeof(column[j]) === "undefined") { + column[j] = 0; + }; + }; + }; + if(slSaveSettings.roundTemps) { + for(i = 0; i < simulationState.pixelMap.length; i++) { + var column = simulationState.pixelMap[i]; + for(j = 0; j < column.length; j++) { + var pixel = column[j]; + if(pixel?.temp) { + pixel.temp = epsilonRound(pixel.temp,3); + }; + }; + }; + }; + return simulationState; + }; + //https://stackoverflow.com/a/46118025 + function copyToClipboard(text) { + var dummy = document.createElement("textarea"); + // to avoid breaking orgain page when copying more words + // cant copy when adding below this code + // dummy.style.display = 'none' + document.body.appendChild(dummy); + //Be careful if you use textarea. setAttribute('value', value), which works with "input" does not work with "textarea". – Eduard + dummy.value = text; + dummy.select(); + document.execCommand("copy"); + document.body.removeChild(dummy); + } + const saveTemplateAsFile = (filename, dataObjToWrite) => { //from https://stackoverflow.com/a/65939108 + const blob = new Blob([JSON.stringify(dataObjToWrite)], { type: "text/json" }); + const link = document.createElement("a"); + link.download = filename; + link.href = window.URL.createObjectURL(blob); + link.dataset.downloadurl = ["text/json", link.download, link.href].join(":"); + const evt = new MouseEvent("click", { + view: window, + bubbles: true, + cancelable: true, + }); + link.dispatchEvent(evt); + link.remove() + }; + function formatCurrentDate() { //derived from https://gist.github.com/Ivlyth/c4921735812dd2c0217a + var d = new Date(); + var year = d.getFullYear().toString(); + var month = (d.getMonth()+1).toString(); + if(month.length == 1) { month = "0" + month }; + var day = d.getDate().toString(); + if(day.length == 1) { day = "0" + day }; + var hour = d.getHours().toString(); + if(hour.length == 1) { hour = "0" + hour }; + var minute = d.getMinutes().toString(); + if(minute.length == 1) { minute = "0" + minute }; + var second = d.getSeconds().toString(); + if(second.length == 1) { second = "0" + second }; + var date_format_str = `${year}-${month}-${day} ${hour}-${minute}-${second}`; + return date_format_str; + }; + function savePrompt() { + var filename = prompt("Please enter the desired filename, without the .json (defaults to current date)"); + if(filename === null) { + return false; + }; + if(filename === "") { + filename = `Sandboxels save ${formatCurrentDate()}`; + }; + filename += ".json"; + downloadSave(filename) + }; + function downloadSave(filename=null) { + if(filename === null) { + filename = `Sandboxels save ${formatCurrentDate()}.json`; + }; + saveTemplateAsFile(filename, getSimulationState()); + }; + rebuildCurrentPixels ??= function() { + var currPix = []; //rebuild currentPixels from pixelMap to try to fix bug + for(pmi = 0; pmi < pixelMap.length; pmi++) { + var pixelMapPart = pixelMap[pmi]; + for(pmj = 0; pmj < pixelMapPart.length; pmj++) { + var pixelMapUnit = pixelMapPart[pmj]; + if(typeof(pixelMapUnit) === "object") { + if(pixelMapUnit !== null) { + currPix.push(pixelMapUnit); + }; + }; + }; + }; + currentPixels = currPix; + }; + function quicksave(doSuccessAlert=true,doFailAlert=true) { + if(storageAvailable("localStorage")) { + rebuildCurrentPixels(); + localStorage.setItem("quicksave",JSON.stringify(getSimulationState())); + if(doSuccessAlert) { alert("Quicksave saved") }; + return true; + } else { + if(doFailAlert) { alert("Could not save quicksave in localStorage") }; + throw new Error("Could not save quicksave in localStorage"); + }; + }; + function quickload(pause=true,doSuccessAlert=true,doFailAlert=true) { + var save = localStorage.getItem("quicksave"); + if(!save) { + if(doFailAlert) { alert("No save exists") }; + return false; + } else { + clearAll(); + rebuildCurrentPixels(); + importJsonState(JSON.parse(save)); + if(doSuccessAlert) { alert("Quicksave loaded") }; + if(pause) { + paused = true; + document.getElementById("pauseButton").setAttribute("on","true"); + } else { + paused = false; + document.getElementById("pauseButton").setAttribute("on","false"); + }; + return true; + }; + rebuildCurrentPixels(); + }; + function copySaveJSON(doAlert=true) { + copyToClipboard(JSON.stringify(getSimulationState())); + if(doAlert) { alert("Save copied as JSON") }; + }; + function loadFile() { + //Initialize + var json; + //load JSON + var file = document.getElementById('myfile').files[0]; + if(file === undefined) { + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "red"; + document.getElementById("fileFormStatus").innerHTML = "No file was uploaded!"; + }; + throw new Error("No file was uploaded"); + }; + var reader = new FileReader(); + reader.readAsText(file, 'UTF-8'); + //after loading + reader.onload = function(evt) { + json = evt.target.result; + //validate + try { + json = JSON.parse(json); + } catch (error) { + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "red"; + document.getElementById("fileFormStatus").innerHTML = "The file wasn't valid JSON!"; + }; + throw error; + }; + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "yellow"; + document.getElementById("fileFormStatus").innerHTML = "JSON was parsed successfully"; + }; + //return json; + return importJsonState(json); + }; + }; + function loadText() { + //Initialize + var json; + //load JSON + var json = document.getElementById('mytext').value; + if(json === "") { + if(document.getElementById("textFormStatus") !== null) { + document.getElementById("textFormStatus").style.color = "red"; + document.getElementById("textFormStatus").innerHTML = "No text was present!"; + }; + throw new Error("No text was present"); + }; + //validate + try { + json = JSON.parse(json); + } catch (error) { + if(document.getElementById("textFormStatus") !== null) { + document.getElementById("textFormStatus").style.color = "red"; + document.getElementById("textFormStatus").innerHTML = "The text wasn't valid JSON!"; + }; + throw error; + }; + if(document.getElementById("textFormStatus") !== null) { + document.getElementById("textFormStatus").style.color = "yellow"; + document.getElementById("textFormStatus").innerHTML = "JSON was parsed successfully"; + }; + //return json; + return importJsonState(json); + }; + function importJsonState(json) { + //check keys + var jsonKeys = Object.keys(json); + var requiredKeys = [/*"currentPixels", */"pixelMap", "width", "height", "pixelSize"]; + var hasrequiredKeys = true; + for(i = 0; i < requiredKeys.length; i++) { + var key = requiredKeys[i]; + if(!jsonKeys.includes(key)) { + hasrequiredKeys = false; + break; + }; + }; + if(!hasrequiredKeys) { + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "red"; + document.getElementById("fileFormStatus").innerHTML = "JSON is not a valid save!"; + }; + throw new Error("JSON is missing required keys!"); + }; + //Set values + width = json.width; + height = json.height; + pixelSize = json.pixelSize; + pixelTicks = (json.pixelTicks ?? 0); + //currentPixels = json.currentPixels; + for(i = 0; i < json.pixelMap.length; i++) { + json.pixelMap[i] = json.pixelMap[i].map(x => zeroToNull(x)); + }; + pixelMap = json.pixelMap; + var settingsExcludedKeys = ["unlocked","suppressModdedSaveLoadWarning"]; + if(json.settings) { + for(var key in json.settings) { + if(settingsExcludedKeys.includes(key)) { continue }; + settings[key] = json.settings[key]; + } + saveSettings + }; + if((settings.dopressure) && json.pressureMap) { pressureMap = json.pressureMap }; + //enabledMods handling { + var enMods = "[]"; + if(typeof(json.enabledMods) !== "undefined") { + enMods = json.enabledMods; + }; + enMods = JSON.parse(enMods); + //console.log(enMods); + var currentEnmods = JSON.parse(localStorage.enabledMods); //should already exist if you're using this mod in the first place + for(emi = 0; emi < enMods.length; emi++) { //load mods additively to prevent self-disabling and the inconvenience of having to readd your mod list when you get bored + var mod = enMods[emi]; + if(!currentEnmods.includes(mod)) { + currentEnmods.push(mod); + }; + }; + localStorage.setItem("enabledMods",JSON.stringify(currentEnmods)); + if((enMods.length > 0 && enMods[0] !== modName) || enMods.length > 1) { + if(!(settings.suppressModdedSaveLoadWarning)) { + alert("Saves with other mods might require a reload (and then importing the save file again).\nIf you see a blank screen, try refreshing and loading the file again before you panic.") + } + }; + //} + var currPix = []; //rebuild currentPixels from pixelMap to try to fix bug + for(pmi = 0; pmi < pixelMap.length; pmi++) { + var pixelMapPart = pixelMap[pmi]; + for(pmj = 0; pmj < pixelMapPart.length; pmj++) { + var pixelMapUnit = pixelMapPart[pmj]; + if(typeof(pixelMapUnit) === "object") { + if(pixelMapUnit !== null) { + currPix.push(pixelMapUnit); + }; + }; + }; + }; + currentPixels = currPix; + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "green"; + document.getElementById("fileFormStatus").innerHTML = "JSON was loaded successfully."; + }; + return true; + }; + function setPixelSize(size=null) { + if(size === null) { + if(document.getElementById("pixelSize") !== null) { + size = document.getElementById("pixelSize").value; + } else { + throw new Error("No size could be read"); + }; + }; + size = parseFloat(size); + if(isNaN(size) || size <= 0) { //NaN check + if(document.getElementById("pixelSizeStatus") !== null) { + document.getElementById("pixelSizeStatus").style.color = "red"; + document.getElementById("pixelSizeStatus").innerHTML = "Pixel size is empty or invalid"; + }; + throw new Error("NaN or negative size"); + }; + if(document.getElementById("pixelSizeStatus") !== null) { + document.getElementById("pixelSizeStatus").style.color = "green"; + document.getElementById("pixelSizeStatus").innerHTML = "Pixel size set successfully"; + }; + pixelSize = size; + return true; + }; + var saveLoaderDescription = `
+Download simulation +Alternatively, copy simulation JSON +No file loader status +One file, please: + +Or paste JSON +No text loader status + + +No size setter status +Pixel size (rendering only): (Use if the save looks cut off) + +
`; + elements.save_loader = { + behavior: behaviors.SELFDELETE, + excludeRandom: true, + color: _cc.w.h, + desc: saveLoaderDescription, + }; + + function formatTempWithAbbreviation(temp) { // temp is Celcius + var _temp; + var suffix; + var unitSetting = settings?.["units"] ?? "m"; + switch(unitSetting) { + default: + case "m": + _temp = temp; + suffix = "°C"; + break; + case "i": + if(settings.userankine == true) { + _temp = (temp*1.8)+491.67; + suffix = "°R"; + } else { + _temp = temp*1.8+32; + suffix = "°F" + }; + break; + case "s": + _temp = temp+273.15; + suffix = "°K"; + break; + }; + var displayTemp = Math.round(_temp); + if(displayTemp > 999999999) { + var shrinkage = (10 ** (Math.floor(Math.log10(_temp)) - 4)); + displayTemp = [Math.floor(_temp/shrinkage),"e",Math.log10(shrinkage),suffix].join(""); + } else { + displayTemp = Math.round(_temp).toString() + suffix; + }; + return displayTemp + }; + + //Somehow, for some illogical reason, quicksaving causes updateStats to somehow disregard its if-statement and fucking TypeError when you mouse over an empty space; this is an attempt to fix it with overkill-level existence checks. + function updateStats() { + var statsDiv = document.getElementById("stats"); + var stats = "x"+mousePos.x+",y"+mousePos.y+""; + stats += "Pxls:" + currentPixels.length+""; + stats += "" + tps+"tps"; + 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]; + if (typeof(currentPixel) !== "undefined" && currentPixel && currentPixel !== undefined && currentPixel.element) { + var displayName = ((elements[currentPixel?.element]?.name || currentPixel?.element) ?? "undefined").toUpperCase(); + if(currentPixel?.displayText) { //old way that might have predated vanilla hoverText but now I'm not sure... kept for compatibility just in case + displayName += ` (${currentPixel?.displayText})` + }; + stats += "Elem:"+displayName+""; + stats += "Temp:"+formatTempWithAbbreviation(currentPixel.temp)+""; + if (currentPixel.charge) { + stats += "C"+currentPixel.charge+""; + } + if (currentPixel.burning) { + stats += "Burning"; + } + if (elements[currentPixel.element].hoverStat) { + stats += ""+elements[currentPixel.element].hoverStat(currentPixel)+""; + } + else if (elements[currentElement].toolHoverStat) { + stats += ""+elements[currentElement].toolHoverStat(currentPixel).toString().replaceAll("<","<")+""; + } + else if (currentPixel.clone) { + stats += ""+currentPixel.clone.toUpperCase()+""; + } + else if (currentPixel.con && currentPixel.con.element) { + stats += ""+currentPixel.con.element.toUpperCase()+""; + } + } + } + if (shiftDown) { + stats += ""+shiftDownTypes[shiftDown]+""; + } + // If the view is not null, show the view in all caps + if (view !== null) { + stats += ""+(viewInfo[view] ? viewInfo[view].name : view)+""; + } + statsDiv.innerHTML = stats; + var _width = getComputedStyle(statsDiv).width; + if(_width !== null) { + _width = parseFloat(_width.match(/[\d\/]+/)); + if(_width <= 752) { + statsDiv.style["font-size"] = "50%" + } else if(_width <= 940) { + statsDiv.style["font-size"] = "75%" + } + } + } + //Moved to window.onload where gameDiv should be guaranteed to exist + } catch (error) { + alert(`save_loading error: ${error.toString()}`); + }; + //ASSORTED EXPERIMENTS + function radialTest(centerX,centerY,integer,chosenElement,createPixels=true,replacePixels=true,radialColor=false) { + if(!elements[chosenElement]) { + alert("Element " + chosenElement + " doesn't exist!"); + return false; + }; + integer = Math.round(integer); + for(i = 1; i < width; i++) { + for(j = 1; j < height; j++) { + var distance = pyth(centerX,centerY,i,j); + if(Math.round(distance) % integer == 0) { + if(isEmpty(i,j,false) && createPixels) { + createPixel(chosenElement,i,j); + } else if(!isEmpty(i,j,true) && !outOfBounds(i,j) && replacePixels) { + changePixel(pixelMap[i][j],chosenElement); + }; + }; + }; + }; + return true; + }; + function rctest(centerX,centerY,integer,chosenElement,createPixels=true,replacePixels=true,distanceScale=1,saturation=50,lightness=50) { + saturation = Math.max(0,Math.min(100,saturation)) + lightness = Math.max(0,Math.min(100,lightness)) + if(!elements[chosenElement]) { + alert("Element " + chosenElement + " doesn't exist!"); + return false; + }; + integer = Math.round(integer); + for(i = 1; i < width; i++) { + for(j = 1; j < height; j++) { + var distance = pyth(centerX,centerY,i,j); + if(Math.round(distance) % integer == 0) { + if(isEmpty(i,j,false) && createPixels) { + createPixel(chosenElement,i,j); + var distancePrevariable = Math.round(distance * distanceScale) % 360 + pixelMap[i][j].color = `hsl(${distancePrevariable},${saturation}%,${lightness}%)`; + } else if(!isEmpty(i,j,true) && !outOfBounds(i,j) && replacePixels) { + changePixel(pixelMap[i][j],chosenElement); + var distancePrevariable = Math.round(distance * distanceScale) % 360 + pixelMap[i][j].color = `hsl(${distancePrevariable},${saturation}%,${lightness}%)`; + }; + }; + }; + }; + return true; + }; + function canSupportWithEdge(x,y) { + if(outOfBounds(x,y)) { //count edges + return true; + } else { + if(!isEmpty(x,y,true)) { //if there is a pixel + if(elements[pixelMap[x][y].element]?.state === "solid") { + return true; + } else { + return false; + }; + }; + }; + }; + /*var csweCharacter = function(x,y) { //Debug function + if(canSupportWithEdge(x,y)) { + return "X"; + } else { + return "."; + }; + };*/ + var powderMovementSnippet = function(pixel) { //Unused + if (!tryMove(pixel, pixel.x, pixel.y+1)) { + if (Math.random() < 0.5) { + if (!tryMove(pixel, pixel.x+1, pixel.y+1)) { + tryMove(pixel, pixel.x-1, pixel.y+1); + }; + } else { + if (!tryMove(pixel, pixel.x-1, pixel.y+1)) { + tryMove(pixel, pixel.x+1, pixel.y+1); + }; + }; + }; + }; + var sturdyMovementSnippet = function(pixel) { //readability wrapper + tryMove(pixel, pixel.x, pixel.y+1); + }; + if(typeof(includesArray) === "undefined") { + function includesArray(parentArray, testArray) { //from portals.js + for (let i = 0; i < parentArray.length; i++) { + if (parentArray[i].every(function(value, index) { return value === testArray[index]})) { + return true; + }; + }; + return false; + }; + }; + ddAnchorArray = []; + distanceScale = 15; + elements.hsl_tool_test = { //with help from ryan + color: ["#cf3030","cf7f30","#cfcf30"], + tool: function(pixel) { + pixel.color = "hsl("+(pixelTicks%360)+",50%,50%)" + }, + customColor: true, + category: "color tools", //the toolbar is getting cluttered + excludeRandom: true, //the toolbar is getting cluttered + }; + elements.temporal_wall_test = { + color: ["#8f8f8f","3f3f3f"], + behavior: behaviors.WALL, + properties: { + counter: 1, + active: true + }, + tick: function(pixel) { + for(i = 0; i < 1; i++) { //dummy for + if(!pixel) { + break; + }; + if(pixel.active) { + if(pixel.counter == width) { + pixel.active = false; + }; + if(!isEmpty(pixel.counter,pixel.y) || outOfBounds(pixel.counter,pixel.y)) { + pixel.counter++; + } else { + createPixel("wall",pixel.counter,pixel.y); + pixel.counter++; + }; + }; + }; + }, + state: "solid", + density: 1000, + category: "special", + }; + elements.steel_silk = { + color: ["#DCDEDF", "#C7C9CA", "#B9BBBC"], + tick: function(pixel) { + var px = pixel.x; + var py = pixel.y; + var supportCondition1 = (canSupportWithEdge(px-1,py-1) && canSupportWithEdge(px+1,py-1)) // V shape + var supportCondition2 = (canSupportWithEdge(px-1,py) && canSupportWithEdge(px+1,py)) // - shape + var supportCondition3 = (canSupportWithEdge(px-1,py+1) && canSupportWithEdge(px+1,py+1)) // Λ shape + var supportCondition4 = (canSupportWithEdge(px-1,py+1) && canSupportWithEdge(px+1,py-1)) // / shape + var supportCondition5 = (canSupportWithEdge(px-1,py-1) && canSupportWithEdge(px+1,py+1)) // \ shape + var supportCondition6 = (canSupportWithEdge(px-1,py-1) && canSupportWithEdge(px+1,py)) // '- shape + var supportCondition7 = (canSupportWithEdge(px-1,py+1) && canSupportWithEdge(px+1,py)) // ,- shape + var supportCondition8 = (canSupportWithEdge(px+1,py-1) && canSupportWithEdge(px-1,py)) // -' shape + var supportCondition9 = (canSupportWithEdge(px+1,py+1) && canSupportWithEdge(px-1,py)) // -, shape + //var supportCondition6 = xor(canSupportWithEdge(px-1,py-1),canSupportWithEdge(px+1,py+1)) // one-side support + var supports = (supportCondition1 || supportCondition2 || supportCondition3 || supportCondition4 || supportCondition5 || supportCondition6 || supportCondition7 || supportCondition8 || supportCondition9); + /*if(pixelTicks % 10 == 0) { + console.log(`Pixel at (${px},${py})`); + console.log(`> ${csweCharacter(px-1,py-1)} ${csweCharacter(px+1,py-1)}\n> ${csweCharacter(px-1,py*1)} ${csweCharacter(px+1,py*1)}\n> ${csweCharacter(px-1,py+1)} ${csweCharacter(px+1,py+1)}`); + };*/ + if (pixel.start === pixelTicks) {return} + if (pixel.charge && elements[pixel.element].behaviorOn) { + pixelTick(pixel) + }; + if(!supports) { + powderMovementSnippet(pixel); + }; + }, + tempHigh: 1455.5, + stateHigh: ["molten_steel", "molten_steel", "molten_steel", "molten_steel", "molten_steel", "molten_steel", "molten_steel", "molten_steel", "molten_steel", null], + category: "solids", + conduct: 0.48, + hardness: 0.79, + movable: true, + category: "solids", + state: "solid", + density: 6850, + breakInto: "metal_scrap", + }; + function distanceScalePrompt() { + var _distanceScale = prompt("Enter the value you want to use"); + //value check + if(isNaN(parseFloat(_distanceScale))) { + //empty string + if(_distanceScale === "" || _distanceScale === null) { + alert("No value was specified! Defaulting to 15"); + _distanceScale = 15; + } else { + alert("Invalid value! The value must be a number (defaulting to 15)"); + _distanceScale = 15; + }; + }; + _distanceScale = parseFloat(_distanceScale); + distanceScale = _distanceScale; + updateDistanceDisplayDescription(); + }; + elements.distance_display = { + color: "#00FFFF", + properties: { + distanceGetter: null + }, + tick: function(pixel) { + var distance = Infinity; + var oldDistance = Infinity; + //if(!ddAnchorArray) { ddAnchorArray = [] } + /*if(!Array.isArray(ddAnchorArray)) { ddAnchorArray = [] } + for (var i = 1; i < width; i++) { //Find and store all anchor pixels + for (var j = 1; j < height; j++) { + }; + };*/ + var px = pixel.x; + var py = pixel.y; + if(ddAnchorArray.length > 0) { + for(i = 0; i < ddAnchorArray.length; i++) { + var newX = ddAnchorArray[i][0]; + var newY = ddAnchorArray[i][1]; + if(isEmpty(newX,newY)) { + ddAnchorArray.splice(i,1); + } else { + var checkPixel = pixelMap[newX][newY]; + if(checkPixel.element !== "distance_display_anchor") { + ddAnchorArray.splice(i,1); + } else { + distanceCandidate = pyth(px,py,newX,newY); + if(distanceCandidate < distance) { + distance = pyth(px,py,newX,newY); + }; + }; + }; + }; + } else { + distance = null; + }; + pixel.distanceGetter = distance; + if(distance !== null) { + var processedDistance = Math.min(255,Math.max(0,Math.round(distance * distanceScale))); + pixel.color = `rgb(0,${processedDistance},255)`; + } else { + pixel.color = `rgb(0,255,255)`; + }; + }, + category: "machines", + state: "solid", + desc: `It gets more blue the closer it gets to a distance display anchor. The current scale factor is ${distanceScale} (bigger number = smaller blue radius). Click here to open the scale prompt.
Note: Info pages do not update automatically and must be closed and reopened to show the changed scale.`, + }; + elements.distance_display_anchor = { + color: "#0000FF", + behavior: behaviors.WALL, + tick: function(pixel) { + var px = pixel.x; + var py = pixel.y; + if(!includesArray(ddAnchorArray,[px,py])) { + ddAnchorArray.push([px,py]); + }; + pixel.color = "rgb(0,0,255)"; + }, + category: "machines", + state: "solid", + desc: `Distance display pixels get blue in its distance.`, + }; + /* + blackObject = _cc.b.j; + pinkObject = {r: 255, g: 148, b: 255}; + elements.black_pink_test = { + color: [_cc.b.h,"#FF94FF"], + behavior: behaviors.WALL, + properties: { + offset: Math.floor(Math.random() * (Math.random() > 0.5 ? -1 : 1) * Math.random() * 15) + }, + tick: function(pixel) { + if(typeof(pixel.offset) !== "number") { + pixel.offset = Math.floor(Math.random() * (Math.random() > 0.5 ? -1 : 1) * Math.random() * 15); + }; + var fraction = Math.min(1.0,Math.max(0.0,scale(pixel.y,1,height-1,0.0,1.0))); + var color = averageColorObjects(pinkObject,blackObject,fraction); + var offsettedColor = lightenColor(color,pixel.offset,"rgb"); + pixel.color = offsettedColor; + }, + category: "machines", + state: "solid", + desc: "blackpink in your area", + }; + */ + function updateDistanceDisplayDescription() { + elements.distance_display.desc = `It gets more blue the closer it gets to a distance display anchor. The current scale factor is ${distanceScale} (bigger number = smaller blue radius). Click here to open the scale prompt.
Note: Info pages do not update automatically and must be closed and reopened to show the changed scale.`; + }; + function createDownAtFirstAvailableSpace(element,x) { + //Get the Y of the first empty pixel on a row which is on a full pixel or the bottom of the canvas + //1. map(x => !x) coerces empty pixels' `undefined` values to !false = true, while full pixels are coerced to !true = false + //2. spread with false adds a sentinel value for the bottom of the canvas + //3. slice(1) removes empty (OOB) position at y=0 + //4. indexOf(false) always shows the first matching item + //5. an offset I don't understand (probably from that slice) shifts the first match to the empty spot above the first full pixel + var firstEmptyY = [...pixelMap[x].map(obj => !obj),false].slice(1).indexOf(false); + if(firstEmptyY == -1) { + return false; + }; + createPixel(element,x,firstEmptyY); + return true; + }; + function createReplacingGases(element,x,y) { + if(isEmpty(x,y,false)) { + createPixel(element,x,y); + return true; + }; + if(!isEmpty(x,y,true)) { + var isGas = (elements[pixelMap[x][y].element].state == "gas"); + if(isGas) { + deletePixel(x,y); + createPixel(element,x,y); + }; + return isGas; + }; + }; + function cdafasIgnoringGas(element,x) { + //Get the Y of the first empty pixel on a row which is on a full pixel or the bottom of the canvas + //1. map(x => !x) coerces empty pixels' `undefined` values to !false = true, while full pixels are coerced to !true = false + //2. spread with false adds a sentinel value for the bottom of the canvas + //3. slice(1) removes empty (OOB) position at y=0 + //4. indexOf(false) always shows the first matching item + //5. an offset I don't understand (probably from that slice) shifts the first match to the empty spot above the first full pixel + var firstEmptyY = [...pixelMap[x].map(obj =>!obj || elements[obj.element].state == "gas"),false].slice(1).indexOf(false); + if(firstEmptyY == -1) { + return false; + }; + createReplacingGases(element,x,firstEmptyY); + return true; + }; + elements.temporal_fire_test = { + color: ["#8f8f8f","3f3f3f"], + behavior: behaviors.WALL, + properties: { + direction: 1, + counter: 1, + active: true, + fromX: null + }, + tick: function(pixel) { + if(pixel.fromX == null) { + pixel.fromX = pixel.x; + }; + if(!pixel) { + return; + }; + if(!pixel.active) { + return; + }; + var newX = pixel.fromX + pixel.counter; + if(outOfBounds(newX,1)) { + pixel.active = false; + newX = pixel.fromX + pixel.counter; //reset + pixel.counter = 1; + return; + }; + cdafasIgnoringGas("fire",newX); + pixel.counter += pixel.direction; + }, + state: "gas", + category: "special", + }; + //ASSORTED ORGANIC COMPOUNDS ## + /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\ + %TODO: Pentyl line physical properties% + \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + //Most hydrocarbons are fantastically colored for convenience + //Benzene ring + //Benzene is actually yellowish + //For combinations, it will represent a + shift in hue + //Isomerism + //Isomers like isopropane have slightly increased hue and sat + //L/S (left) isomers are darkened and hued up, while D/R (right) isomers are lightened and hued down + //Cis- isomers are darkened and trans- isomers are lightened + //Chain length + //Methyl line is purple + //plus benzene = pink + //Ethyl line is rose + //plus benzene = red + //Propyl line is green + //plus benzene = mint + //Butyl line is cyan + //plus benzene = blue + //Pentyl line is vermillion + //plus benzene = orange + //Alcohols + //Alcohols are paler + //Bond type + //Alkanes are lightest + //Alkenes are darker + //Alkynes are darkest + //Benzene is non-ternary with respect to single vs double bond + //Benzene + elements.benzene = { + color: "#edf099", + behavior: behaviors.LIQUID, + state: "liquid", + category: "liquids", + density: 876, + burn: 80, + burnTime: 20, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","dirty_water","dirty_water"], + reactions: { + "head": { elem2: "cancer", chance: 0.0017}, + "body": { elem2: "cancer", chance: 0.0017}, + }, + tempLow: 5.53, + tempHigh: 80.1, + }; + elements.benzene_gas = { + density: 2.77 * airDensity, + }; + elements.benzene_ice = { + density: 1012, + }; + //Alk*nes and their substituted benzenes + //Single carbon line + //Lowest bond order + //1 carbon = purple + elements.methane.color = "#bfabc9"; + elements.liquid_methane ??= {}; + elements.liquid_methane.density = 423; + //Methene and methyne don't make sense + //Methanol + elements.methanol = { + color: "#d5ced9", + behavior: behaviors.LIQUID, + reactions: { + "virus": { "elem2":null }, + "plague": { "elem2":null }, + "head": { "elem2":"rotten_meat", "chance": 0.8 }, + "body": { "elem2":"rotten_meat", "chance": 0.8 }, + }, + viscosity: 0.56, + //tempHigh: 64.7, + burn: 100, + burnTime: 2, + fireColor: "#b2c5d1", + category: "liquids", + state: "liquid", + density: 792, + stain: -0.25, + } + //Benzene ver. + elements.toluene = { + //meth- purple + benzene hue up = pink + //liquid initial = more vivid + color: "#de76cf", + behavior: behaviors.LIQUID, + state: "liquid", + category: "liquids", + density: 862, + burn: 80, + burnTime: 20, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam"], + reactions: { + "head": { elem2: "cancer", chance: 0.001 }, //unknown/unclassifiable carcinogenicity + "body": { elem2: "cancer", chance: 0.001 }, + }, + tempHigh: 110.6, + tempLow: -95, + }; + elements.toluene_gas = { + density: 3.1 * airDensity, + }; + //Double carbon line + //Lowest bond order + //Rose + elements.ethane = { + color: "#cfa3bb", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 872, //artifically raised by 400 degrees to prevent interference with ethylbenzene dehydrogenation + stateHigh: "fire", + reactions: { + "head": { elem2: "rotten_meat", chance: 0.00015}, + "body": { elem2: "rotten_meat", chance: 0.00015}, + }, + tempLow: -88.5, + burn: 85, + burnTime: 5, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","steam","steam","steam"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 1.3562, //absolute density + }; + elements.liquid_ethane = { + tempLow: -182.8, + density: 544, + }; + //Double bonds + //ethylene = ethene + elements.ethylene = { + color: "#c991b0", + behavior: behaviors.GAS, + state: "gas", + category: "gases", + tempHigh: 425, + stateHigh: "fire", + density: 1.18, + burn: 80, + burnTime: 20, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","steam","steam"], + reactions: { + "head": { elem2: "rotten_meat", chance: 0.0001}, //no mechanism for prolonged exposure causing harm + "body": { elem2: "rotten_meat", chance: 0.0001}, + "benzene_gas": { tempMin: 220, elem1: null, elem2: "ethylbenzene_gas" }, + }, + tempLow: -103.7 + }; + elements.liquid_ethylene = { + tempLow: -169.2, + density: 577, //unknown solid density + }; + //Triple bonds + //acetylene = ethyne + elements.acetylene = { + color: "#b8819f", + behavior: behaviors.GAS, + state: "gas", + category: "gases", + reactions: { + oxygen: { elem1: ["acetylene","oxy_fuel"], elem2: null }, + }, + tick: function(pixel) { //tick-based autoignition point to trigger acetylene fire properties + if(pixel.temp > 325 && !pixel.burning) { + pixel.burning = true; + }; + }, + density: 1.1772, //absolute + burn: 100, + burnTime: 10, + burnTempChange: 20, + fireSpawnTemp: 2700, //overheat by 500 degrees to compensate for natural cooling effects + fireSpawnChance: 5, //reduce own flame to reduce said effects from smoke + fireColor: "#5da8fc", + burnInto: ["fire","plasma","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam"], + tempLow: -84, + stateLow: "acetylene_ice", + }; + elements.acetylene_ice = { + color: "#ffa8d8", + behavior: behaviors.POWDER, + state: "solid", + category: "states", + reactions: { + liquid_oxygen: { elem1: ["acetylene_ice","oxy_fuel_slush"], elem2: null, changeTemp: false }, + oxygen_ice: { elem1: ["acetylene_ice","oxy_fuel_snow"], elem2: null, changeTemp: false }, + }, + tick: function(pixel) { + if(pixel.temp > 325 && !pixel.burning) { + pixel.burning = true; + }; + }, + density: 720, + burn: 25, //cold + burnTime: 5, + burnTempChange: 20, + fireSpawnTemp: 2700, + fireSpawnChance: 5, + fireColor: "#5da8fc", + burnInto: ["fire","plasma","fire","fire","fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam"], + "temp": -100, + "tempHigh": -84, + "stateHigh": "acetylene", + hidden: true, + }; + //Ethanol (Vanilla) + elements.alcohol.name = "ethanol"; + elements.alcohol.color = "#d4b9c8"; + elements.alcohol.viscosity = 1.144; + elements.water.viscosity = 1; //define reference viscosity of 1 + //Benzene ver. + elements.ethylbenzene = { + color: "#de7676", + //ethene's rose plus benzene's yellow = red + behavior: behaviors.LIQUID, + state: "liquid", + category: "liquids", + density: 867, + burn: 75, + burnTime: 25, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + reactions: { + "head": { elem2: "cancer", chance: 0.0017 }, + "body": { elem2: "cancer", chance: 0.0017 }, + }, + tempHigh: 136, + tempLow: -95, + }; + elements.ethylbenzene_gas = { + density: 3.7 * airDensity, + reactions: { + "head": { elem2: "cancer", chance: 0.0017 }, + "body": { elem2: "cancer", chance: 0.0017 }, + "steam": { tempMin: 600, elem1: ["styrene","hydrogen","styrene","hydrogen","styrene","hydrogen","styrene","hydrogen","styrene","hydrogen","styrene","hydrogen","styrene","hydrogen","styrene","hydrogen","toluene","benzene","methane","ethane"], elem2: "steam", temp1: -3, temp2: -3 }, + }, + }; + //Triple carbon line + //Single bond + elements.propane.color = "#b8d4a5"; + elements.propane.tempHigh = 493; + //Double bond + elements.propylene = { //propene + color: "#a4c48d", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 458, + stateHigh: "fire", + tempLow: -47.6, + burn: 100, + burnTime: 5, + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 1.745, //abs. at 25*C + }; + elements.liquid_propylene = { + tempLow: -185.2, + density: 613.9, + }; + //Triple bond + elements.propyne = { //also methylacetylene + color: "#8bad72", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 340, + stateHigh: "fire", + tempLow: -25.15, + burn: 85, + burnTime: 5, + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 1.6656, //abs. at 25*C + }; + elements.liquid_propylene = { + tempLow: -102.7, + density: 671.963, + }; + //Propanol + //Linear + elements.propanol = { + color: "#d4e0cc", + behavior: behaviors.LIQUID, + reactions: { + "virus": { "elem2":null }, + "plague": { "elem2":null }, + }, + viscosity: 2.23, //EXERCISE 8: VISCOSITY OF PURE LIQUIDS AND SOLUTIONS + //tempHigh: 97, + burn: 100, + burnTime: 3, + fireColor: "#ced8de", + category: "liquids", + state: "liquid", + density: 803, + stain: -0.25, + } + //Triangular + elements.isopropanol = { + color: "#d1e4c8", + behavior: behaviors.LIQUID, + reactions: { + "virus": { "elem2":null }, + "plague": { "elem2":null }, + }, + viscosity: 2.38, //http://www.ddbst.com/en/EED/PCP/VIS_C95.php (293K is close enough) + //tempHigh: 82.5, + burn: 100, + burnTime: 3, + fireColor: "#d1c958", + category: "liquids", + state: "liquid", + density: 786, + stain: -0.25, + } + //Benzene ver. + //more obscure organic compound + elements.propylbenzene = { + color: "#92debd", + behavior: behaviors.LIQUID, + state: "liquid", + category: "liquids", + density: 862, + burn: 75, + burnTime: 25, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + reactions: { + //we can probably still assume that it's carcinogenic because it has the phenyl group + "head": { elem2: "cancer", chance: 0.0017 }, + "body": { elem2: "cancer", chance: 0.0017 }, + }, + tempHigh: 159.2, + tempLow: -99.5, + }; + elements.propylbenzene_gas = { + density: 4.14 * airDensity, + reactions: { + "head": { elem2: "cancer", chance: 0.0017 }, + "body": { elem2: "cancer", chance: 0.0017 }, + }, + }; + //Quadruple carbon line + //Single bond + elements.butane = { + color: "#a7dbd9", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 287, + stateHigh: "fire", + reactions: { + "head": { elem2: "rotten_meat", chance: 0.00015}, + "body": { elem2: "rotten_meat", chance: 0.00015}, + }, + tempLow: 1, + burn: 85, + burnTime: 5, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.076 * airDensity, + }; + elements.liquid_butane = { + tempLow: -134, + density: 604, + }; + elements.isobutane = { + color: "#9cbddb", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 460, + stateHigh: "fire", + reactions: { + "head": { elem2: "rotten_meat", chance: 0.00015}, + "body": { elem2: "rotten_meat", chance: 0.00015}, + }, + tempLow: -11.7, + burn: 85, + burnTime: 5, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.51, + }; + elements.liquid_isobutane = { + tempLow: -159.42, + density: 563, + }; + //Double bond + elements.butylene = { //butene + name: "1-butylene", + color: "#95cfcd", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 385, + stateHigh: "fire", + tempLow: -6.47, + burn: 100, + burnTime: 5, + fireColor: ["#00ffff","#00ffdd"], + burnInto: ["fire","carbon_dioxide","carbon_dioxide","steam","steam"], + state: "gas", + density: 1.93 * airDensity, + }; + elements.liquid_butylene = { + tempLow: -185.3, + density: 625.63, + }; + elements.water.reactions.butylene = { + elem1: ["l_secbutanol","r_secbutanol"], elem2: ["l_secbutanol","r_secbutanol"] + }; elements.water.reactions.liquid_butylene = elements.water.reactions.butylene; + elements.trans_2_butylene = { + name: "t-butylene-2", + color: "#a1c9d4", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 324, + stateHigh: "fire", + tempLow: 0.8, + burn: 85, + burnTime: 5, + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2 * airDensity, + }; + elements.liquid_trans_2_butylene = { + tempLow: -105.5, + density: 626, + }; + elements.water.reactions.trans_2_butylene = { + elem1: ["l_secbutanol","r_secbutanol"], elem2: ["l_secbutanol","r_secbutanol"] + }; elements.water.reactions.liquid_trans_2_butylene = elements.water.reactions.trans_2_butylene; + elements.cis_2_butylene = { + name: "c-butylene-2", + color: "#8cbcca", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 324, + stateHigh: "fire", + tempLow: 3.7, + burn: 85, + burnTime: 5, + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2 * airDensity, + }; + elements.liquid_cis_2_butylene = { + tempLow: -138.9, + density: 641, + }; + elements.water.reactions.cis_2_butylene = { + elem1: ["l_secbutanol","r_secbutanol"], elem2: ["l_secbutanol","r_secbutanol"] + }; elements.water.reactions.liquid_cis_2_butylene = elements.water.reactions.cis_2_butylene; + //Triple bond + elements.butyne = { + color: "#81a2b3", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 444, //Unknown autoignition + stateHigh: "fire", + tempLow: 8.08, + burn: 100, + burnTime: 5, + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.12 * airDensity, //made-up due to also unknown vapor density + }; + elements.liquid_butyne = { + tempLow: -125.7, + density: 678.3, + }; + //Alcohol + //Line + elements.butanol = { + color: "#d0e7e6", + behavior: behaviors.LIQUID, + reactions: { + "virus": { "elem2":null }, + "plague": { "elem2":null }, + }, + viscosity: 3.0011, //https://www.sciencedirect.com/science/article/abs/pii/S0021961416301446?via%3Dihub + tempHigh: 117.7, + tempLow: -89.8, + burn: 100, + burnTime: 3, + category: "liquids", + state: "liquid", + density: 810, + stain: -0.25, + } + //Hydroxyl in internal carbon (like isopropanol) + //Left + elements.l_secbutanol = { + color: "#b9dbe4", + behavior: behaviors.LIQUID, + reactions: { + "virus": { "elem2":null }, + "plague": { "elem2":null }, + }, + viscosity: 3.0011, //https://www.sciencedirect.com/science/article/abs/pii/S0021961416301446?via%3Dihub + tempHigh: 100, + tempLow: -115, + burn: 100, + burnTime: 3, + category: "liquids", + state: "liquid", + density: 810, + stain: -0.25, + } + //Right + elements.r_secbutanol = { + color: "#def1f2", + behavior: behaviors.LIQUID, + reactions: { + "virus": { "elem2":null }, + "plague": { "elem2":null }, + }, + viscosity: 3.0011, //https://www.sciencedirect.com/science/article/abs/pii/S0021961416301446?via%3Dihub + tempHigh: 100, + tempLow: -115, + burn: 100, + burnTime: 3, + category: "liquids", + state: "liquid", + density: 810, + stain: -0.25, + }; + //Racemic + elements.secbutanol = { + color: "#cce7ea", + behavior: [ + "XX|XX|XX", + "M2|CH:r_secbutanol,l_secbutanol|M2", + "M1|M1|M1" + ], + reactions: { + "virus": { "elem2":null }, + "plague": { "elem2":null }, + }, + viscosity: 3.0011, //https://www.sciencedirect.com/science/article/abs/pii/S0021961416301446?via%3Dihub + tempHigh: 100, + stateHigh: ["l_secbutanol_gas","r_secbutanol_gas"], + tempLow: -115, + stateLow: ["l_secbutanol_ice","r_secbutanol_ice"], + burn: 100, + burnTime: 3, + category: "liquids", + state: "liquid", + density: 810, + stain: -0.25, + }; + //Branched chain + elements.isobutanol = { + color: "#c9e3ee", + behavior: behaviors.LIQUID, + reactions: { + "virus": { "elem2":null }, + "plague": { "elem2":null }, + }, + viscosity: 3.0011, //https://www.sciencedirect.com/science/article/abs/pii/S0021961416301446?via%3Dihub + tempHigh: 107.89, + tempLow: -108, + burn: 100, + burnTime: 3, + category: "liquids", + state: "liquid", + density: 810, + stain: -0.25, + } + //Branched with internal hydroxyl + elements.tertbutanol = { + color: "#c5def1", + behavior: behaviors.LIQUID, + reactions: { + "virus": { "elem2":null }, + "plague": { "elem2":null }, + }, + viscosity: 3.0011, //https://www.sciencedirect.com/science/article/abs/pii/S0021961416301446?via%3Dihub + tempHigh: 83, + temp: 40, + tempLow: 25, + burn: 100, + burnTime: 3, + category: "liquids", + state: "liquid", + density: 810, + stain: -0.25, + } + //Benzene ver. + elements.butylbenzene = { + color: "#7b8ae0", + behavior: behaviors.LIQUID, + state: "liquid", + category: "liquids", + density: 860.1, + burn: 75, + burnTime: 25, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + reactions: { + "head": { elem2: "cancer", chance: 0.0017 }, + "body": { elem2: "cancer", chance: 0.0017 }, + }, + tempHigh: 183.3, + tempLow: -87.9, + }; + elements.butylbenzene_gas = { + density: 4.6 * airDensity, + reactions: { + "head": { elem2: "cancer", chance: 0.0017 }, + "body": { elem2: "cancer", chance: 0.0017 }, + }, + }; + elements.cumene = { + color: "#8873e6", + behavior: behaviors.LIQUID, + state: "liquid", + category: "liquids", + density: 862, + burn: 75, + density: 0.777, + burnTime: 25, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam"], + reactions: { + "head": { elem2: "cancer", chance: 0.0017 }, + "body": { elem2: "cancer", chance: 0.0017 }, + }, + tempHigh: 152, + tempLow: -96, + }; + elements.cumene_gas = { + density: 4.1 * airDensity, + reactions: { + "head": { elem2: "cancer", chance: 0.0017 }, + "body": { elem2: "cancer", chance: 0.0017 }, + }, + }; + //Quintuple carbon line + //Single bond + elements.pentane = { + color: "#b5685b", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 533, + stateHigh: "fire", + reactions: { + "head": { elem2: "rotten_meat", chance: 0.00015}, + "body": { elem2: "rotten_meat", chance: 0.00015}, + }, + tempLow: 36.1, + burn: 85, + burnTime: 5, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam","steam"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.48 * airDensity, + }; + elements.liquid_pentane = { + color: "#a62711", + tempLow: -130.2, + density: 626, + }; + elements.isopentane = { + color: "#bb6c54", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 427, + stateHigh: "fire", + reactions: { + "head": { elem2: "rotten_meat", chance: 0.00015}, + "body": { elem2: "rotten_meat", chance: 0.00015}, + }, + tempLow: -11.7, + burn: 85, + burnTime: 5, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.48 * airDensity, + }; + elements.liquid_isopentane = { + color: "#ab320d", + tempLow: -160, + density: 616, + }; + elements.neopentane = { + color: "#c1724e", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 427, + stateHigh: "fire", + reactions: { + "head": { elem2: "rotten_meat", chance: 0.00015}, + "body": { elem2: "rotten_meat", chance: 0.00015}, + }, + tempLow: 9.5, + burn: 85, + burnTime: 5, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 3.255, + }; + elements.liquid_neopentane = { + color: "#af3d08", + tempLow: -16.5, + density: 601.172, + }; + //Double bond + elements.pentylene = { //pentene + name: "1-pentylene", + color: "#af5a4b", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 527, + stateHigh: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + tempLow: 30, + temp: 40, + burn: 100, + burnTime: 5, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.4 * airDensity, + }; + elements.liquid_pentylene = { + tempLow: -165.2, + density: 640, + }; + elements.trans_2_pentylene = { + name: "t-pentylene-2", + color: "#924b3f", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 324, //Unknown + stateHigh: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + tempLow: 36.3, + burn: 85, + burnTime: 5, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.4 * airDensity, + }; + elements.liquid_trans_2_pentylene = { + tempLow: -140.2, + density: 643.1, + }; + elements.cis_2_pentylene = { + name: "c-pentylene-2", + color: "#9d5143", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 324, //Unknown + stateHigh: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + tempLow: 36.9, + burn: 85, + burnTime: 5, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","steam","steam"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2 * airDensity, + }; + elements.liquid_cis_2_pentylene = { + tempLow: -151.4, + density: 655.6, + }; + //Triple bond + elements.pentyne = { + name: "1-pentyne", + color: "#9d5143", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 454, //Unknown + stateHigh: ["fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","oxygen","oxygen","oxygen"], + tempLow: 40.2, + temp: 55, + burn: 100, + burnTime: 5, + burnInto: ["fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","oxygen","oxygen","oxygen"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.6 * airDensity, //made-up due to also unknown vapor density + }; + elements.liquid_1_pentyne = { + tempLow: -105.5, + density: 691, + }; + elements.pentyne_2 = { + name: "2-pentyne", + color: "#9d5143", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 454, //Unknown + stateHigh: ["fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","oxygen","oxygen","oxygen"], + tempLow: 56.5, + temp: 65, + burn: 70, + burnTime: 5, + burnInto: ["fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","oxygen","oxygen","oxygen"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.6 * airDensity, //made-up due to also unknown vapor density + }; + elements.liquid_pentyne_2 = { + tempLow: -109, + density: 710, + }; + elements.isopentyne = { + color: "#a6533a", + behavior: behaviors.GAS, + category: "gases", + tempHigh: 454, //Unknown + stateHigh: ["fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","oxygen","oxygen","oxygen"], + tempLow: 29.5, + temp: 40, + burn: 70, + burnTime: 5, + burnInto: ["fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_dioxide","oxygen","oxygen","oxygen"], + fireColor: ["#00ffff","#00ffdd"], + state: "gas", + density: 2.6 * airDensity, //made-up due to also unknown vapor density + }; + elements.liquid_isopentyne = { + tempLow: -89.7, + density: 666, + }; + //Alcohol + //Fuck no I'm not doing 8 isomers + //Benzene ver. + //i'm tired + //Vodka + elements.alcohol.reactions.water = { //50% is close enough to the standard 40% + elem1: "vodka", + elem2: "vodka", + } + elements.vodka = { + color: "#9FAEC5", + behavior: behaviors.LIQUID, + reactions: { + "virus": { "elem2":null, chance: 0.05 }, //it still adds up over ticks + "plague": { "elem2":null, chance: 0.05 }, + }, + tick: function(pixel) { + //Thermal split + if(pixel.temp > elements.alcohol.tempHigh && exposedToAir(pixel)) { + var randomNeighbor = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)] + var rnx = randomNeighbor[0] + var rny = randomNeighbor[1] + if(isEmpty(pixel.x+rnx, pixel.y+rny, false)) { + createPixel("alcohol_gas", pixel.x+rnx, pixel.y+rny); + changePixel(pixel,pixel.temp >= 100 ? "steam" : "water",false); + } + } + }, + tempLow: -36, + category: "liquids", + state: "liquid", + density: 914, + stain: -0.25, + } + //Other organic compounds + //Oxygen plus acetylene mixture + elements.oxy_fuel = { + color: "#ff5eb4", + behavior: behaviors.GAS, + state: "gas", + category: "gases", + tick: function(pixel) { //tick-based autoignition point to trigger acetylene fire properties + if(pixel.temp > 325 && !pixel.burning) { + pixel.burning = true; + }; + }, + density: 1.25, + burn: 100, + burnTime: 10, + burnTempChange: 330, + fireSpawnTemp: 3100, + fireSpawnChance: 5, //reduce own flame to reduce said effects from smoke + fireElement: ["fire","plasma"], + fireColor: "#5e91ff", + burnInto: ["fire","plasma"], + tempLow: -84, + stateLow: ["oxygen","oxygen","acetylene_ice"], + hidden: true, + }; + elements.oxy_fuel_slush = { + color: "#d85fed", + behavior: behaviors.LIQUID, + viscosity: 100, + state: "liquid", + category: "liquids", + tick: function(pixel) { //tick-based autoignition point to trigger acetylene fire properties + if(pixel.temp > 325 && !pixel.burning) { + pixel.burning = true; + }; + }, + density: 873, //made-up + burn: 100, + burnTime: 10, + burnTempChange: 330, + fireSpawnTemp: 3100, + fireSpawnChance: 5, //reduce own flame to reduce said effects from smoke + fireElement: ["oxy_fuel","fire","plasma"], + fireColor: "#5e91ff", + burnInto: "oxy_fuel", + temp: -200, + tempLow: -218.8, + stateLow: "oxy_fuel_snow", + tempHigh: -183.94, + stateHigh: ["oxygen","oxygen","acetylene_ice"], + hidden: true, + }; + elements.oxy_fuel_snow = { + color: "#dd9afc", + behavior: behaviors.POWDER, + state: "solid", + category: "powders", + tick: function(pixel) { //tick-based autoignition point to trigger acetylene fire properties + if(pixel.temp > 325 && !pixel.burning) { + pixel.burning = true; + }; + }, + density: 912, //made-up + burn: 100, + temp: -250, + burnTime: 10, + burnTempChange: 330, + fireSpawnTemp: 3100, + fireSpawnChance: 5, //reduce own flame to reduce said effects from smoke + fireElement: ["fire","plasma"], + fireColor: "#5e91ff", + burnInto: "oxy_fuel", + tempHigh: -218.8, + stateHigh: "oxy_fuel_slush", + hidden: true, + }; + //Styrene and its polymer + elements.styrene = { + color: "#d9d6c3", + behavior: behaviors.LIQUID, + state: "liquid", + category: "liquids", + density: 909, + burn: 80, + burnTime: 25, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","steam"], + reactions: { + "head": { elem2: "cancer", chance: 0.0017 }, + "body": { elem2: "cancer", chance: 0.0017 }, + "hydrogen": { elem1: "ethylbenzene", elem2: null, chance: 0.005 }, + "benzoyl_peroxide": { elem1: "polystyrene", elem2: "benzoic_acid" }, + "polystyrene": { elem1: "polystyrene" }, + "molten_polystyrene": { elem1: "polystyrene" }, + }, + tempLow: -30, + tempHigh: 145, + }; + elements.styrene_gas = { + density: 3.6 * airDensity, + }; + elements.polystyrene = { + color: "#f5f5f5", + behavior: behaviors.WALL, + state: "solid", + category: "solids", + density: 965, + tempHigh: 100, + //above this it does thermoplastic things + }; + elements.molten_polystyrene = { + color: "#e3e3e3", + tempLow: 100, + behavior: behaviors.LIQUID, + viscosity: 1000, + reactions: { + "foam": { elem1: "styrofoam", elem2: ["styrofoam","molten_polystyrene","molten_polystyrene","molten_polystyrene"] }, + }, + }; + elements.styrofoam = { + color: "#f5f5f5", + behavior: behaviors.WALL, + state: "solid", + category: "solids", + density: 50, + breakInto: ["packing_peanuts","polystyrene","foam","foam"], + cutInto: "packing_peanuts", + tempHigh: 160, //reaction grace period + stateHigh: [null,null,null,"molten_polystyrene"], + }; + elements.packing_peanuts = { + color: "#f1f1f1", + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + breakInto: ["polystyrene","foam","foam"], + density: 40, + tempHigh: 160, //reaction grace period + stateHigh: [null,null,null,null,"molten_polystyrene"], + }; + //Benzoyl peroxide + elements.benzoyl_peroxide = { + color: "#ededed", + behavior: behaviors.POWDER, + state: "solid", + category: "powders", + density: 1.334, + tempHigh: 103, + stateHigh: ["benzoic_acid","benzoic_acid","benzoic_acid","fire","explosion"], + }; + //Benzoic acid + elements.benzoic_acid = { + color: "#c9c9c9", + behavior: behaviors.POWDER, + state: "solid", + category: "powders", + density: 1.2659, + tempHigh: 122, + }; + elements.molten_benzoic_acid = { + behavior: behaviors.LIQUID, + color: "#b5b2b0", + tempHigh: 250, + density: 1074.9, + reactions: { + "molten_copper_sulfate": { tempMin: 200, elem1: ["phenol","phenol","carbon_dioxide"] }, //using air oxygen + }, + }; + elements.benzoic_acid_gas = { + density: 4.21 * airDensity, + reactions: { + "oxygen": { tempMin: 350, elem1: "phenol", elem2: "carbon_dioxide" }, + "molten_copper_sulfate": { tempMin: 200, elem1: ["phenol","phenol","carbon_dioxide"] }, + }, + }; + //Phenol + elements.phenol = { + color: "#dbd3d3", + behavior: behaviors.POWDER, + state: "solid", + category: "powders", + density: 1070, + burn: 40, + burnTime: 70, + burnInto: ["fire","fire","carbon_dioxide","carbon_dioxide","carbon_dioxide","steam","steam","steam","dioxin"], + reactions: { + "head": { elem2: "rotten_meat", chance: 0.003 }, + "body": { elem2: "rotten_meat", chance: 0.003 }, + }, + tempHigh: 40.5, + tempLow: -95, + }; + elements.molten_phenol = { + color: "#cfc2c2", + behavior: behaviors.LIQUID, + viscosity: 8, + reactions: { + "head": { elem2: "rotten_meat", chance: 0.003 }, + "body": { elem2: "rotten_meat", chance: 0.003 }, + }, + tempHigh: 181.7, + }; + elements.phenol_gas = { + reactions: { + "head": { elem2: "rotten_meat", chance: 0.003 }, + "body": { elem2: "rotten_meat", chance: 0.003 }, + }, + density: 3.24, + }; + //Inorganic compounds + runAfterAutogen(function() { + var waters = searchElements("water").filter(function(name) { return getState(name) == "liquid" }); + var whitelistWaters = ["hail","rime","cloud","rain_cloud","thunder_cloud","snow_cloud","hail_cloud","rain_cloud_cloud","snow_cloud_cloud","hail_cloud_cloud","cloud_cloud","heaviest_water_cloud","heaviest_snow_cloud","snow_cloud_floater","jinsoulite","jinsoulite_gas","jinsoulite_powder","molten_jinsoulite"]; + var jvoWaters = Object.keys(jinsouliteValueObject).filter(function(name) { return !(name.includes("bomb")) }); + var coldWaters = (getStateLow(waters,true) ?? []).filter( + function(name) { return typeof(name) == "string" } + ); + var colderWaters = (getStateLow(coldWaters,true) ?? []).filter( + function(name) { return typeof(name) == "string" } + ); + var hotWaters = (getStateHigh(waters,true) ?? []).filter( + function(name) { return typeof(name) == "string" } + ); + var brokenColdWaters = (getBreakInto(coldWaters,true) ?? []).filter( + function(name) { return typeof(name) == "string" } + ); + var coldBrokenColdWaters = (getStateLow(brokenColdWaters,true) ?? []).filter( + function(name) { return typeof(name) == "string" } + ); + wateroids = [waters,coldWaters,colderWaters,hotWaters,brokenColdWaters,coldBrokenColdWaters,whitelistWaters,jvoWaters].flat() + //moved vivite-related code to where it happens + for(var i = 0; i < wateroids.length; i++) { + if(elements[wateroids[i]]) { elements[wateroids[i]].noViviteSlag = true }; + }; + }); + //Hydrogen sulfide (in chem.js) + _h_2s = ["hydrogen_sulfide","liquid_hydrogen_sulfide","hydrogen_sulfide_ice"]; + runAfterLoad(function() { + elements.hydrogen_sulfide.density = 1.19 * airDensity; + elements.hydrogen_sulfide.reactions ??= {}; + elements.hydrogen_sulfide.reactions.head = { elem2: "rotten_meat", chance: 0.4}; + elements.hydrogen_sulfide.fireColor = { elem2: "rotten_meat", chance: 0.4}; + elements.hydrogen_sulfide.tempHigh = 1200; + elements.hydrogen_sulfide.stateHigh = ["sulfur_gas","steam"]; + delete elements.sulfur_dioxide.reactions.water; + delete elements.sulfur_dioxide.reactions.steam; + delete elements.water.reactions.sulfur; + elements.sulfur_dioxide.tick = function(pixel) { + var neighbors = adjacentCoords.map(offsetPair => pixelMap[pixel.x+offsetPair[0]]?.[pixel.y+offsetPair[1]]).filter(function(pixelOrUndefined) { return typeof(pixelOrUndefined) == "object" }); + if(neighbors.length < 2) { return }; + var neighboringElements = neighbors.filter(function(px) { return !!px }).map(x => x.element); + var waterNeighbor = null; + var sulfideNeighbor = null; + for(var i = 0; i < neighboringElements.length; i++) { + if(_h_2s.includes(neighboringElements[i])) { + sulfideNeighbor = adjacentCoords[i]; + }; + if(wateroids.includes(neighboringElements[i])) { + waterNeighbor = adjacentCoords[i]; + }; + if(sulfideNeighbor && waterNeighbor) { + if(sulfideNeighbor !== undefined && sulfideNeighbor?.x && sulfideNeighbor?.y) { + if(!sulfideNeighbor || isEmpty(sulfideNeighbor?.x,sulfideNeighbor?.y,true)) { return }; + changePixel(sulfideNeighbor,getStateAtTemp("sulfur",pixel.temp)); + changePixel(pixel,getStateAtTemp("water",pixel.temp)); + } + } + } + } + }); + //Carbon monoxide + elements.carbon_monoxide = { + color: "#8f8f8f", + behavior: behaviors.GAS, + state: "gas", + category: "gases", + density: 1.145, + reactions: { + "head": { elem2: "rotten_meat", chance: 0.0017}, + "body": { elem2: "rotten_meat", chance: 0.0017}, + }, + tempLow: -191.5, + }; + elements.liquid_carbon_monoxide = { + tempLow: -205.02, + density: 789, //unknown solid density + }; + //Water + elements.steam.reactions ??= {}; + elements.steam.reactions.charcoal = { tempMin: 680, elem1: "hydrogen", elem2: "carbon_monoxide" }; + elements.steam.reactions.diamond = { tempMin: 680, elem1: "hydrogen", elem2: "carbon_monoxide" }; + //Oil refining + delete elements.oil.tempHigh; + function liquidMoveCustomViscosity(pixel,viscosity) { + if (pixel.start === pixelTicks) {return} + if (pixel.charge && elements[pixel.element].behaviorOn) { + pixelTick(pixel) + } + var viscosityPass = ((Math.random()*100) < 100 / Math.pow(viscosity, 0.25)); + if (!viscosityPass) { + var move1Spots = [ + [pixel.x, pixel.y+1] + ] + } + else { + var move1Spots = [ + [pixel.x+1, pixel.y+1], + [pixel.x, pixel.y+1], + [pixel.x-1, pixel.y+1], + ] + } + var moved = false; + for (var i = 0; i < move1Spots.length; i++) { + var coords = move1Spots[Math.floor(Math.random()*move1Spots.length)]; + if (tryMove(pixel, coords[0], coords[1])) { moved = true; break; } + else { move1Spots.splice(move1Spots.indexOf(coords), 1); } + } + if (!moved) { + if (viscosityPass) { + if (Math.random() < 0.5) { + if (!tryMove(pixel, pixel.x+1, pixel.y)) { + tryMove(pixel, pixel.x-1, pixel.y); + } + } else { + if (!tryMove(pixel, pixel.x-1, pixel.y)) { + tryMove(pixel, pixel.x+1, pixel.y); + } + } + } + } + doDefaults(pixel); + }; + elements.light_petroleum_fuel_gas = { //it's not liquified, and sandboxels doesn't even have a pressure system, and there is no generic name for uncompressed, gaseous "L"PG, so we need a different name + burn: 100, + color: "#b5b5b3", + density: 3.5, + tempLow: -44, + tick: function(pixel) { + if (pixel.temp >= 495 && !pixel.burning) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } + }, + burnInto: "explosion,explosion,fire,fire,fire,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(","), + state: "gas", + behavior: behaviors.GAS, + }; + elements.lamp_oil.tempHigh = 170; + elements.lamp_oil.stateHigh = "lamp_oil_gas"; + elements.lamp_oil.density = 810; + elements.lamp_oil.name = "kerosene"; + elements.lamp_oil.forceAutoGen = true; + elements.lamp_oil.burnInto = "smoke,smoke,fire,fire,fire,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(","); + elements.lamp_oil_gas = { + name: "kerosene gas", + burn: 100, + density: 4.5, + tick: elements.lamp_oil.tick, + tempLow: 170, + stateLow: "lamp_oil", + burnInto: "explosion,smoke,smoke,fire,fire,fire,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(",") + }; + elements.gasoline = { + color: "#d1cf9d", + behavior: behaviors.LIQUID, + tick: function(pixel) { + if (pixel.temp > 263 && !pixel.burning) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } + }, + reactions: { + "styrofoam": { elem1: ["gasoline","gasoline","gasoline","gasoline","napalm"], elem2: null }, //behold, the joke + "packing_peanuts": { elem1: ["gasoline","gasoline","gasoline","gasoline","napalm"], elem2: null }, + "polystyrene": { elem1: "napalm", elem2: ["polystyrene","polystyrene",null] }, + "molten_polystyrene": { elem1: "napalm", elem2: ["molten_polystyrene","molten_polystyrene",null] }, + "glue": {elem2:null, chance:0.05}, + "wax": {elem2:null, chance:0.005}, + "melted_wax": {elem2:null, chance:0.025}, + }, + forceAutoGen: true, + category: "liquids", + tempHigh: 70, + tempLow: -60, + burn: 20, + temp: 20, + burnTime: 500, + burnInto: "fire,fire,fire,fire,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(","), + viscosity: 7.04, + state: "liquid", + density: 755, + alias: "petrol" + }; + elements.gasoline_gas = { + burn: 100, + burnTime: 10, + density: 3.5, + tick: elements.gasoline.tick, + burnInto: "explosion,fire,fire,fire,fire,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(",") + }; + elements.naphtha = { + color: "#d1d1d1", + behavior: behaviors.LIQUID, + tick: function(pixel) { + if (pixel.temp > 270 && !pixel.burning) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } + }, + reactions: { + "styrofoam": { elem1: ["naphtha","naphtha","naphtha","naphtha","napalm"], elem2: null }, + "polystyrene": { elem1: "napalm", elem2: ["polystyrene","polystyrene",null] }, + "molten_polystyrene": { elem1: "napalm", elem2: ["molten_polystyrene","molten_polystyrene",null] }, + "glue": {elem2:null, chance:0.05}, + "wax": {elem2:null, chance:0.005}, + "melted_wax": {elem2:null, chance:0.025}, + }, + category: "liquids", + tempHigh: 120, + tempLow: -30, + forceAutoGen: true, + burn: 80, + burnTime: 500, + burnInto: "fire,fire,fire,fire,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(","), + viscosity: 5.77, + state: "liquid", + density: 740 + }; + elements.naphtha_gas = { + burn: 100, + burnTime: 10, + density: 3.5, + tick: elements.naphtha.tick, + burnInto: "explosion,fire,fire,fire,fire,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(",") + }; + elements.diesel = { + color: "#d3d9b4", + behavior: behaviors.LIQUID, + tick: function(pixel) { + if (pixel.temp > 300 && !pixel.burning) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } + }, + reactions: { + "glue": {elem2:null, chance:0.05}, + "wax": {elem2:null, chance:0.005}, + "melted_wax": {elem2:null, chance:0.025}, + }, + category: "liquids", + tempHigh: 260, + forceAutoGen: true, + tempLow: -25, + burn: 20, + burnTime: 500, + burnInto: "fire,fire,fire,fire,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(","), + viscosity: 7.04, + state: "liquid", + density: 755, + }; + elements.diesel_gas = { + burn: 100, + burnTime: 12, + density: 3.5, + tick: elements.diesel.tick, + burnInto: "explosion,fire,fire,fire,fire,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(",") + }; + elements.lubricating_oil = { + color: "#d3d9b4", + behavior: behaviors.LIQUID, + tick: function(pixel) { + if (pixel.temp > 450 && !pixel.burning) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } + }, + category: "liquids", + tempHigh: 350, + tempLow: -40, + burn: 20, + burnTime: 600, + forceAutoGen: true, + burnInto: "fire,fire,fire,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(","), + viscosity: 7.04, + state: "liquid", + density: 800, + }; + elements.lubricating_oil_gas = { + burn: 100, + burnTime: 13, + density: 3.5, + tick: elements.lubricating_oil.tick, + burnInto: "explosion,fire,fire,fire,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(",") + }; + elements.heavy_fuel_oil = { + color: "#1c1a18", + stain: 0.3, + behavior: behaviors.LIQUID, + tick: function(pixel) { + if (pixel.temp > 407 && !pixel.burning) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + }; + if(pixel.burning && Math.random() < 0.01) { + var emptyNeighbors = []; + for(i = 0; i < adjacentCoords.length; i++) { + if(isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],false)) { + emptyNeighbors.push(adjacentCoords[i]); + }; + }; + if(emptyNeighbors.length > 0) { + var randomEmptyNeighbor = emptyNeighbors[Math.floor(Math.random() * emptyNeighbors.length)]; + createPixelReturn(["smoke","carbon_dioxide"],pixel.x+randomEmptyNeighbor[0],pixel.y+randomEmptyNeighbor[1]).temp = pixel.temp + }; + } + }, + reactions: { + "polystyrene": { elem1: "napalm", elem2: "napalm", chance:0.05 }, //the joke + "glue": {elem2:null, chance:0.05}, + "wax": {elem2:null, chance:0.005}, + "melted_wax": {elem2:null, chance:0.025}, + }, + category: "liquids", + tempHigh: 300, + forceAutoGen: true, + tempLow: 0, + burn: 10, + viscosity: 700, + burnTime: 800, + fireElement: ["fire","fire","fire","smoke","smoke","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_monoxide","carbon_monoxide","carbon_monoxide","sulfur_dioxide","sulfur_trioxide_gas","poison_gas"], + burnInto: "fire,fire,fire,fire,fire,fire,ash,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,poison_gas".split(","), + viscosity: 500, + state: "liquid", + density: 755, + alias: "petrol" + }; + elements.heavy_fuel_oil_gas = { + burn: 80, + burnTime: 60, + density: 2.5, + tick: elements.heavy_fuel_oil.tick, + fireElement: ["explosion","fire","fire","fire","smoke","smoke","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_monoxide","carbon_monoxide","carbon_monoxide","sulfur_dioxide","sulfur_trioxide_gas","poison_gas"], + burnInto: "fire,fire,fire,fire,fire,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,steam,steam".split(",") + }; + elements.bitumen = { + color: "#0d0c0c", + maxColorOffset: 5, + tick: function(pixel) { + var viscosity = 1e16 / (1.09 ** pixel.temp); + liquidMoveCustomViscosity(pixel,viscosity) + }, + reactions: { + "polystyrene": { elem1: "napalm", elem2: "napalm", chance:0.05 }, //the joke + "glue": {elem2:null, chance:0.05}, + "wax": {elem2:null, chance:0.005}, + "melted_wax": {elem2:null, chance:0.025}, + }, + category: "liquids", + tempHigh: 750, + stateHigh: ["bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","bitumen","fire","fire","fire","smoke","smoke","smoke","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_monoxide","carbon_monoxide","carbon_monoxide","sulfur_dioxide","sulfur_trioxide_gas","poison_gas"], + burn: 2, + burnTime: 800, + fireElement: ["fire","fire","fire","smoke","smoke","smoke","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_monoxide","carbon_monoxide","carbon_monoxide","sulfur_dioxide","sulfur_trioxide_gas","poison_gas"], + burnInto: "fire,fire,fire,fire,fire,fire,ash,ash,ash,carbon_monoxide,carbon_monoxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,carbon_dioxide,steam,steam,steam,poison_gas".split(","), + viscosity: 7.04, + state: "liquid", + density: 1050, + reactions: { + gravel: { elem1: "asphalt", elem2: "asphalt" } + } + }; + elements.asphalt ={ + color: "#191919", + behavior: behaviors.STURDYPOWDER, + tempHigh: 750, + stateHigh: ["asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","asphalt","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel",,"fire","fire","fire","smoke","smoke","smoke","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_monoxide","carbon_monoxide","carbon_monoxide","sulfur_dioxide","sulfur_trioxide_gas","poison_gas"], + category: "land", + state: "solid", + density: 2322, + burn: 0.5, + burnTime: 5000, + burnInto: ["gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel","gravel",,"fire","fire","fire","smoke","smoke","smoke","carbon_dioxide","carbon_dioxide","carbon_dioxide","carbon_monoxide","carbon_monoxide","carbon_monoxide","sulfur_dioxide","sulfur_trioxide_gas","poison_gas"], + fireElement: ["smoke","smoke","smoke","carbon_dioxide","carbon_monoxide","fire","fire","fire","fire","fire","fire"], + fireChance: 2, + hardness: 0.5, + breakInto: ["bitumen","gravel"], + reactions: { + light: { temp1: 0.25, elem2: null } + } + }; + elements.oil.temp = 20; + delete elements.oil.behavior; + elements.oil.tick = function(pixel) { + if(!pixel.role) { + var value = Math.random(); + if(value <= 0.03) { + pixel.role = "lpg"; + } else if(value <= 0.45) { //42% + pixel.role = "gasoline"; + } else if(value <= 0.60) { //15% + pixel.role = "naphtha"; + } else if(value <= 0.70) { //10% + pixel.role = "kerosene"; //kerosene/lamp oil/jet fuel are apparently the same funny bunch of hydrocarbons + } else if(value <= 0.9) { //20% + pixel.role = "diesel"; + } else if(value <= 0.91) { //1% + pixel.role = "lubricant"; + } else if(value <= 0.97) { //6% + pixel.role = "heavy_fuel_oil"; //700 cP + } else if(value < 1) { //3% + pixel.role = "bitumen"; + }; + }; + + var _viscosity; + switch(pixel.role) { + case "lpg": + _viscosity = 1; + break + case "gasoline": + _viscosity = 28.16; + break + case "naphtha": + _viscosity = 23.08; + break + case "kerosene": + _viscosity = 12; + break + case "diesel": + _viscosity = 24; + break + default: + _viscosity = 250; + break + case "lubricant": + _viscosity = 300; + break + case "heavy_fuel_oil": + _viscosity = 600; + break + case "bitumen": + _viscosity = 100000; + break + }; + _viscosity /= (1.09 ** pixel.temp); + + liquidMoveCustomViscosity(pixel,_viscosity); + doDefaults(pixel); + + if(pixel.temp > 35 && pixel.role == "lpg" && Math.random() < ((pixel.temp - 34) / 210)) { //https://www.crownoil.co.uk/guides/crude-oil-fractional-distillation/: Butane and propane and other petroleum gases are formed right at the top of the distillation tower, where it is coolest, a very mild 25°C: the temperature range that forms these gases is between 25°C and 50°C. These gases are the lightest products formed in crude oil distillation and are flammable gases. + changePixel(pixel,"light_petroleum_fuel_gas") + } else if(pixel.temp > 70 && pixel.role == "gasoline" && Math.random() < ((pixel.temp - 69) / 420)) { //The numbers in the equation are mathematical coincidence. + changePixel(pixel,"gasoline_gas") + } else if(pixel.temp > 120 && pixel.role == "naphtha" && Math.random() < ((pixel.temp - 119) / 720)) { + changePixel(pixel,"naphtha_gas") + } else if(pixel.temp > 170 && pixel.role == "kerosene" && Math.random() < ((pixel.temp - 169) / 770)) { + changePixel(pixel,"lamp_oil_gas") + } else if(pixel.temp > 270 && pixel.role == "diesel" && Math.random() < ((pixel.temp - 269) / 870)) { + changePixel(pixel,"diesel_gas") + } else if(pixel.temp > 300 && pixel.role == "heavy_fuel_oil" && Math.random() < ((pixel.temp - 299) / 1200)) { + changePixel(pixel,"heavy_fuel_oil_gas") + } else if(pixel.temp > 350 && Math.random() < ((pixel.temp - 349) / 1350)) { + changePixel(pixel,pixel.role == "lubricant" ? "lubricating_oil_gas" : "bitumen") + } + }; + //UREA ## + elements.urea = { + color: "#fef7ee", //once again mapping UV absorbances to the visible range + //https://www.researchgate.net/publication/266458879 + //http://depts.washington.edu/cmditr/modules/lum/color.html + behavior: behaviors.POWDER, + state: "solid", + density: 1320, + tempHigh: 133, + category: "powders", + }, + elements.molten_urea = { + tempHigh: 350, //https://pubs.acs.org/doi/pdf/10.1021/ie034052j + stateHigh: ["ammonia","vaporized_isocyanic_acid"], + }, + elements.liquid_isocyanic_acid = { + color: "#ffe5f0", //now it's an IR spectrum + //https://www.researchgate.net/publication/231057584 + behavior: behaviors.LIQUID, + reactions: { + "water": { "elem1": "carbon_dioxide", "elem2": "ammonia" }, + "steam": { "elem1": "carbon_dioxide", "elem2": "ammonia" }, + "ice": { "elem1": "carbon_dioxide", "elem2": "ammonia" }, + }, + state: "liquid", + density: 1140, + tempHigh: 23, + stateHigh: "vaporized_isocyanic_acid", + tempLow: -86, + stateLow: "frozen_isocyanic_acid", + category: "liquids", + }, + elements.frozen_isocyanic_acid = { + color: "#ffe5f0", + behavior: behaviors.WALL, + reactions: { + "water": { "elem1": "carbon_dioxide", "elem2": "ammonia" }, + "steam": { "elem1": "carbon_dioxide", "elem2": "ammonia" }, + "ice": { "elem1": "carbon_dioxide", "elem2": "ammonia" }, + }, + state: "solid", + density: 1267, + tempHigh: -86, + stateHigh: "liquid_isocyanic_acid", + category: "powders", + }, + elements.vaporized_isocyanic_acid = { + color: "#ffe5f0", + behavior: behaviors.GAS, + reactions: { + "water": { "elem1": "carbon_dioxide", "elem2": "ammonia" }, + "steam": { "elem1": "carbon_dioxide", "elem2": "ammonia" }, + "ice": { "elem1": "carbon_dioxide", "elem2": "ammonia" }, + }, + state: "gas", + density: 1026, + tempLow: 23, + stateLow: "liquid_isocyanic_acid", + category: "gases", + } + //INVISIBLE WALL AND DYE ## + if(!settings) { + settings = {} + } + settings.bg ??= _cc.b.h; + function getBackgroundColorOrAverageAsJSON() { + if(!(settings?.bg)) { + return _cc.b.j; + } else if(!(settings.bg instanceof Array)) { + return convertColorFormats(settings.bg,"json") + } else { + return convertColorFormats(averageRgbPrefixedColorArray(settings.bg.map(color => convertColorFormats(color,"rgb"))),"json"); + }; + }; + function makePixelInvisible(pixel) { + var backgroundColor = getBackgroundColorOrAverageAsJSON(); + pixel.color = `rgba(${backgroundColor.r},${backgroundColor.g},${backgroundColor.b},0)`; + }; + elements.invisible_wall = { + color: settings.bg, + behavior: behaviors.WALL, + tick: function(pixel) { makePixelInvisible(pixel) }, + insulate: true, + hardness: 1, + category: "special", + state: "solid", + }; + elements.invisible_dye = { + color: settings.bg, + behavior: behaviors.LIQUID, + tick: function(pixel) { makePixelInvisible(pixel) }, + hardness: 0.8, + breakInto: "invisible_dye_gas", + tempHigh: 110, + stateHigh: "invisible_dye_gas", + category: "special", + state: "liquid", + density: 1, + stain: elements.dye.stain, + }; + elements.invisible_dye_gas = { + color: settings.bg, + behavior: behaviors.GAS, + tick: function(pixel) { makePixelInvisible(pixel) }, + hardness: 0.5, + breakInto: "invisible_dye_gas", + tempLow: 109, + stateLow: "invisible_dye", + category: "special", + state: "liquid", + density: 1, + stain: elements.spray_paint.stain, + }; + var _temporary = { + invisible_wall: "asdfg", + invisible_dye: 2, + invisible_dye_gas: false + }; + for(var elemName in _temporary) { + elements[elemName].desc = "Note: Invisible dyes do not work (and are not supported) with gradient backgrouds"; + }; + //BANANAS AND BANANA PLANTS ## + randomNumberFromOneToThree = function() { + return 1 + Math.floor(Math.random() * 3) + }; + bananaDebugSpeedGrowth = false; + logLeaves = false; + bananaAttachWhitelist = ["banana_pseudostem","banana_peduncle_1","banana_peduncle_2","petal","banana_leaf","banana_plant_top","banana"]; + bananaDirtElements = ["dirt","mud","sand","wet_sand","clay_soil","mycelium","grass"]; + function logPixelCoords(pixel) { + return `(${pixel.x}, ${pixel.y})` + }; + function hasPixel(x,y,elementInput) { + if(isEmpty(x,y,true)) { //if empty, it can't have a pixel + return false; + } else { + if(elementInput.includes(",")) { //CSTA + elementInput = elementInput.split(","); + }; + if(Array.isArray(elementInput)) { //if element list + return elementInput.includes(pixelMap[x][y].element); + } else { //if single element + return pixelMap[x][y].element === elementInput; + }; + }; + }; + elements.banana_seed = { + color: "#3b3b2e", + tick: function(pixel) { + if(pixel.bananaRange === null) { + pixel.bananaRange = randomNumberFromOneToThree(); + }; + if (isEmpty(pixel.x,pixel.y+1)) { + movePixel(pixel,pixel.x,pixel.y+1); + } else { + if (Math.random() < (bananaDebugSpeedGrowth ? 0.09 : 0.03) && pixel.age > (bananaDebugSpeedGrowth ? 20 : 50) && pixel.temp < 100) { + if (!outOfBounds(pixel.x,pixel.y+1)) { + var dirtPixel = pixelMap[pixel.x][pixel.y+1]; + if (bananaDirtElements.includes(dirtPixel.element)) { + changePixel(dirtPixel,"root"); + }; + }; + if (isEmpty(pixel.x,pixel.y-1)) { + movePixel(pixel,pixel.x,pixel.y-1); + createPixel("banana_pseudostem",pixel.x,pixel.y+1); + pixelMap[pixel.x][pixel.y+1].bananaRange = pixel.bananaRange; //pass banana range down to pseudostem + }; + } else if (pixel.age > (bananaDebugSpeedGrowth ? 500 : 1000)) { + changePixel(pixel,"banana_plant_top"); + }; + pixel.age++; + }; + if(Math.random() < 0.01 && pixel.age > 200) { + changePixel(pixel,"banana_plant_top"); + }; + doDefaults(pixel); + }, + properties: { + "age": 0, + "bananaRange": null + }, + tempHigh: 100, + stateHigh: "dead_plant", + tempLow: -2, + stateLow: "frozen_plant", + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + category: "life", + state: "solid", + density: 1500, + cooldown: defaultCooldown, + }; + elements.banana_pseudostem = { + hidden: true, + color: "#d5e39f", + tick: function(pixel) { + if(pixel.bananaRange === null) { + pixel.bananaRange = randomNumberFromOneToThree(); + }; + if (pixel.age > 60 && pixel.temp < 100 && !pixel.grewPeduncle) { + var peduncleOffsets = [-1, 1]; //placed to the left, placed to the right + for(i = 0; i < peduncleOffsets.length; i++) { + if (isEmpty(pixel.x+peduncleOffsets[i],pixel.y,false)) { + if (Math.random() < 0.005) { + createPixel("banana_peduncle_1",pixel.x+peduncleOffsets[i],pixel.y); + pixelMap[pixel.x+peduncleOffsets[i]][pixel.y].dir = Math.sign(peduncleOffsets[i]); + pixelMap[pixel.x+peduncleOffsets[i]][pixel.y].bananaRange = pixel.bananaRange; //pass banana range down to peduncle + if(Math.random() < 0.8) { pixel.grewPeduncle = true; } //20% chance to not mark as true, allowing for a chance to try another peduncle + }; + }; + }; + }; + pixel.age++; + doDefaults(pixel); + }, + properties: { + "age": 0, + "grewPeduncle": false, + "bananaRange": null + }, + tempHigh: 100, + stateHigh: "dead_plant", + tempLow: -2, + stateLow: "frozen_plant", + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + category: "life", + state: "solid", + density: 1500, + }; + elements.banana = { + color: "#ede84c", + isFood: true, + tick: function(pixel) { + if(pixel.attached) { + var attachCoords = [pixel.x+Math.sign(pixel.attachDirection), pixel.y]; + if(isEmpty(attachCoords[0],attachCoords[1],false)) { + pixel.attached = false; + }; + } else { //Move if not attached + if (!tryMove(pixel, pixel.x, pixel.y+1)) { + if(Math.random() < 0.9) { + if (Math.random() < 0.5) { + if (!tryMove(pixel, pixel.x+1, pixel.y+1)) { + tryMove(pixel, pixel.x-1, pixel.y+1); + }; + } else { + if (!tryMove(pixel, pixel.x-1, pixel.y+1)) { + tryMove(pixel, pixel.x+1, pixel.y+1); + }; + }; + }; + }; + }; + doDefaults(pixel); + var shouldSpoil = true; //spoil by default + if(pixel.attached) { //if it's attached + if(!isEmpty(attachCoords[0],attachCoords[1],true)) { //if the attachment coords are a pixel and not OOB + var attachPixel = pixelMap[attachCoords[0]][attachCoords[1]]; + var attachElement = attachPixel.element; + if(bananaAttachWhitelist.includes(attachElement)) {//if the element is a whitelisted "don't spoil" element + shouldSpoil = false; //then don't spoil + }; + }; + }; + if(shouldSpoil) { //spoil if not attached + if(pixel.temp > -14 && pixel.temp <= 4) { //(no spoiling below 14C) + pixel.spoilage += Math.max(Math.min(scale(pixel.temp,-14,4,0,9),9),0) + } else if(pixel.temp > 4) { + pixel.spoilage += Math.max(Math.min(scale(pixel.temp,4,20,9,30),40),0) + }; + }; + if(pixel.spoilage > 14400) { //3600 = 120 ticks at 20C + if(Math.random() < 0.05) { + changePixel(pixel,"spoiled_banana"); + }; + }; + }, + properties: { + "spoilage":0, + "attached": false, + "attachDirection": (!Math.floor(Math.random() * 2)) ? 1 : -1 + }, + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + tempHigh: 200, + stateHigh: ["steam", "ash"], + onTryMoveInto: function(pixel,otherPixel) { + var otherInfo = elements[otherPixel.element] + if(typeof(otherInfo.state) === "string" && otherInfo.state !== "gas") { + pixel.attached = false; + }; + }, + }; + elements.banana_peduncle_1 = { + hidden: true, + name: "banana peduncle (offshoot)", + color: "#acb55b", + tick: function(pixel) { + if(pixel.bananaRange === null) { + pixel.bananaRange = randomNumberFromOneToThree(); + }; + if (pixel.age > 20 && pixel.temp < 100) { + var peduncleCoords1 = [pixel.x + pixel.dir, pixel.y]; + var peduncleCoords2 = [pixel.x + pixel.dir, pixel.y + 1]; + if(isEmpty(peduncleCoords1[0],peduncleCoords1[1],false) && isEmpty(peduncleCoords2[0],peduncleCoords2[1],false)) { + if(Math.random() < 0.5) { + createPixel(pixel.element,peduncleCoords1[0],peduncleCoords1[1]); + pixelMap[peduncleCoords1[0]][peduncleCoords1[1]].dir = pixel.dir; + pixelMap[peduncleCoords1[0]][peduncleCoords1[1]].bananaRange = pixel.bananaRange; //pass banana range down to next pixel of peduncle horizontal + } else { + createPixel("banana_peduncle_2",peduncleCoords2[0],peduncleCoords2[1]); + pixelMap[peduncleCoords2[0]][peduncleCoords2[1]].bananaRange = pixel.bananaRange; //pass banana range down to diagonal offshoot + }; + }; + }; + pixel.age++; + doDefaults(pixel); + }, + properties: { + "dir": (!Math.floor(Math.random() * 2)) ? 1 : -1, + "age": 0, + //"bananaRange": (1 + (Math.floor(Math.random() * 3))), //1-3 + "bananaRange": null + }, + tempHigh: 100, + stateHigh: "dead_plant", + tempLow: -2, + stateLow: "frozen_plant", + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + category: "life", + state: "solid", + density: 1500, + }; + elements.banana_peduncle_2 = { + hidden: true, + name: "banana peduncle (hanging)", + color: "#9bad51", + tick: function(pixel) { + if(pixel.bananaRange === null) { + pixel.bananaRange = randomNumberFromOneToThree(); + }; + // Grow/Flower + if (pixel.age > 20 && pixel.temp < 100) { + var growthCoords = [pixel.x, pixel.y + 1]; + if(isEmpty(...growthCoords)) { + if(Math.random() < 0.9) { + createPixel(pixel.element,...growthCoords); + pixelMap[growthCoords[0]][growthCoords[1]].bananaRange = pixel.bananaRange; //pass banana range down to next pixel of peduncle vertical + } else { + createPixel("petal",...growthCoords); //the sexual dimorphism of the banana plant has zonked me + }; + }; + }; + //Make bananas + if (pixel.age > 40 && pixel.temp < 100) { + var bananaOffsets = [-1, 1]; //placed to the left, placed to the right + for(i = 0; i < bananaOffsets.length; i++) { + //console.log(`Looping through left and right positions: ${bananaOffsets}`); + for(j = 1; j < pixel.bananaRange + 1; j++) { //for max banana distance, using the banana range + //console.log(`Looping through banana offset multipliers: ${j}`); + if (isEmpty(pixel.x+(j * bananaOffsets[i]),pixel.y,false)) { //if there's an empty space + //console.log(`Banana position is empty: [${j * bananaOffsets[i]}, 0]\nTrying banana at (${pixel.x+(j * bananaOffsets[i])},${pixel.y})`); + if (Math.random() < (bananaDebugSpeedGrowth ? 0.05 : 0.005)) { //try to place the banana + //console.log(`Placing banana`); + createPixel("banana",pixel.x+(j * bananaOffsets[i]),pixel.y); + pixelMap[pixel.x+(j * bananaOffsets[i])][pixel.y].attached = true; + pixelMap[pixel.x+(j * bananaOffsets[i])][pixel.y].attachDirection = -1 * Math.sign(bananaOffsets[i]); //attach dir is the opposite of placement dir so it attaches towards the stem + } else { + //console.log(`NOT placing banana`); + }; + //console.log(`Banana tried, stopping iteration`); + break; //and then stop iteration + } else { + //console.log(`Banana position is NOT empty: [${j * bananaOffsets[i]}, 0]\nSkipping this offset`); + continue; //if not empty, skip that pixel and move on the next distance + }; + //console.log(`====End of side try====`); + }; + //console.log(`%%%%End of side iterator%%%%`); + }; + //console.log(`>>>>End of banana iterator<<<<`); + }; + pixel.age++; + doDefaults(pixel); + //console.log(`\nEnd of peduncle tick\n`); + }, + properties: { + "age": 0, + //"bananaRange": (1 + (Math.floor(Math.random() * 3))), //1-3 + "bananaRange": null + }, + tempHigh: 100, + stateHigh: "dead_plant", + tempLow: -2, + stateLow: "frozen_plant", + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + category: "life", + state: "solid", + density: 1500, + }; + elements.spoiled_banana = { + hidden: true, + color: "#594b29", + behavior: [ + "XX|CR:stench,fly%0.1|XX", + "M2%0.5|CH:dirty_water,fly,fly%0.007|M2%0.5", + "M2|M1|M2" + ], + stain: 0.01, + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + tempHigh: 200, + stateHigh: ["steam", "ash"], + }; + elements.fly.reactions.spoiled_banana = { "elem2":null, chance:0.15, func:behaviors.FEEDPIXEL }; + elements.banana_leaf = { + hidden: true, + color: "#9df24e", + tick: function(pixel) { + if(pixel.bananaRange === null) { + pixel.bananaRange = randomNumberFromOneToThree(); + }; + if(pixel.attached) { + var attachCoords = [pixel.x + pixel.attachOffsets[0], pixel.y + pixel.attachOffsets[1]]; + if(isEmpty(attachCoords[0],attachCoords[1],false)) { //consider OOB full + pixel.attached = false; + }; + } else { //Move if not attached + if(Math.random() < 0.2) { + if (!tryMove(pixel, pixel.x, pixel.y+1)) { + if(Math.random() < 0.4) { + if (Math.random() < 0.5) { + if (!tryMove(pixel, pixel.x+1, pixel.y+1)) { + tryMove(pixel, pixel.x-1, pixel.y+1); + }; + } else { + if (!tryMove(pixel, pixel.x-1, pixel.y+1)) { + tryMove(pixel, pixel.x+1, pixel.y+1); + }; + }; + }; + }; + }; + }; + doDefaults(pixel); + }, + properties: { + "attached": false, + "attachOffsets": [(!Math.floor(Math.random() * 2)) ? 1 : -1, 0], + "bananaRange": null + }, + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + tempHigh: 200, + stateHigh: ["steam", "ash"], + onTryMoveInto: function(pixel,otherPixel) { //Move through + var otherElement = otherPixel.element; //var element for readability + var otherInfo = elements[otherElement]; //var info + var otherState = "solid"; //consider things solid by default + if(typeof(otherInfo.state) === "string") { + otherState = otherInfo.state; //get actual state if it exists + }; + var otherDensity = 1000; //consider density 1000 by default + if(typeof(otherInfo.density) === "number") { + otherDensity = otherInfo.density; //get actual density if it exists + }; + var react = false; //default to no reaction + if(typeof(otherInfo.reactions) === "object") { //look for reactions + if(typeof(otherInfo.reactions[pixel.element]) === "object") { //look for reactions involving this element + react = true; //if there are any, set reaction flag to true + }; + }; + if(otherElement.endsWith("head") || otherElement.endsWith("body")) { + //i don't want to make general MPL handling so I'll just try to exclude them; + if(otherElement !== "antibody") { + //exclude antibody from exclusion + return false; + }; + }; + if(otherElement !== pixel.element) { //allow this element from piling on itself + if(logLeaves) { console.log("Other element is not banana leaves") }; //yes, this code is for banana leaves + if(react) { //if there was a reaction in that previous step + if(logLeaves) { console.log("Reacting pixels") }; + reactPixels(otherPixel,pixel); //react + } else { //if no such reaction existed, move through + if(logLeaves) { console.log("Moving pixels") }; + if((otherState !== "solid") || (otherState === "solid" && otherDensity > 100)) { //admit any non-solid, or any solid with a density over 100 + var pX = pixel.x; //var pixel coords for no particular reason + var pY = pixel.y; + var oX = otherPixel.x; //var other pixel's coords for no particular reason + var oY = otherPixel.y; + if(logLeaves) { console.log(`${otherElement} pixel (${oX},${oY}) trying to move info leaf block (${pX},${pY})`) }; + var dX = oX - pX; //get the difference between this's X and other's X; if the other pixel is moving from the space immediately to the right, this dX value should be 1 + var dY = oY - pY; + var iDX = -1 * dX; //get the additive inverse; if we want to move such a pixel from the right to the left, we would change its +1 X offset to a -1 X offset for the coord sto move it to + var iDY = -1 * dY; + if(logLeaves) { console.log(`Old offset (relative to leaf): [${dX},${dY}], new offset [${iDX},${iDY}]`) }; + var fX = pX + iDX; //combine this pixel's X with the inverted offset we just made; + //assuming this pixel is (23,31) and the other pixel is trying to move in to the left into this from (24,31), + //the dX would be [1, 0], signifying that the other pixel is 1 pixel to the right of this + //the space to the left of this, where it would go, is (22,31), and the offset for that pixel relative to this is [-1, 0] + //to get the [-1, 0], we'd need to flip that [1, 0] offset (lmao flip that the song by loona), hence the inverse + var fY = pY + iDY; + if(logLeaves) { console.log(`Calculated final position: (${fX},${fY}), moving other pixel from (${oX},${oY})`) }; + tryMove(otherPixel,fX,fY); + }; + }; + }; + }, + }; + /*if(!elements.diamond.reactions) { //test reaction + elements.diamond.reactions = {}; + }; + elements.diamond.reactions.banana_leaf = { "elem2": "dead_plant" };*/ + elements.banana_plant_top = { + hidden: true, + color: "#d5e39f", + tick: function(pixel) { + if(pixel.bananaRange === null) { + pixel.bananaRange = randomNumberFromOneToThree(); + }; + if (pixel.age > 30 && pixel.temp < 100) { + if(!pixel.grewLeftLeaves) { + for(i = (0 - pixel.leafRange); i < 0; i++) { //left half + if(i == 0) { + continue; + }; + var leafOffset = i; //readability + var leafX = pixel.x + leafOffset; //set X to banana_plant_top pixel's X + offset/index + var leafAttachOffset = [1, 0]; //difference 1: attaches rightwards (+) for leaves left (-) of center + var leafY = pixel.y; //set Y to default banana_plant_top pixel's Y + if(Math.abs(leafOffset) == pixel.leafRange) { + leafY++; //place edge leaves 1 pixel downwards; + leafAttachOffset[1] = -1; //compensate by subtracting 1 from Y attach offset (less Y = higher position, so they attach diagonally up-right or up-left) + }; + if(outOfBounds(leafX,leafY)) { + continue; + }; + if (isEmpty(leafX,leafY,false)) { + createPixel("banana_leaf",leafX,leafY); + pixelMap[leafX][leafY].attached = true; //set leaf's attached to true + pixelMap[leafX][leafY].attachOffsets = leafAttachOffset; //array of 2 numbers + pixelMap[leafX][leafY].bananaRange = pixel.bananaRange; + pixel.grewLeftLeaves = true; //difference 2: separate flag for left side + } else { + break; + }; + }; + }; + if(!pixel.grewRightLeaves) { + for(i = 1; i < (pixel.leafRange + 1); i++) { //right half + if(i == 0) { + continue; + }; + var leafOffset = i; //readability + var leafX = pixel.x + leafOffset; //set X to banana_plant_top pixel's X + offset/index + var leafAttachOffset = [-1, 0]; //difference 1: attaches leftwards (-) for leaves right (+) of center + var leafY = pixel.y; //set Y to default banana_plant_top pixel's Y + if(Math.abs(leafOffset) == pixel.leafRange) { + leafY++; //place edge leaves 1 pixel downwards; + leafAttachOffset[1] = -1; //compensate by subtracting 1 from Y attach offset (less Y = higher position, so they attach diagonally up-right or up-left) + }; + if(outOfBounds(leafX,leafY)) { + continue; + }; + if (isEmpty(leafX,leafY,false)) { + createPixel("banana_leaf",leafX,leafY); + pixelMap[leafX][leafY].attached = true; //set leaf's attached to true + pixelMap[leafX][leafY].attachOffsets = leafAttachOffset; //array of 2 numbers + pixelMap[leafX][leafY].bananaRange = pixel.bananaRange; + pixel.grewRightLeaves = true; //difference 2: separate flag for right side + } else { + break; + }; + }; + }; + }; + pixel.age++; + doDefaults(pixel); + }, + properties: { + "age": 0, + "leafRange": 2 + (Math.floor(Math.random() * 3)), //2-4 + "grewLeftLeaves": false, + "grewRightLeaves": false, + "bananaRange": null + }, + tempHigh: 100, + stateHigh: "dead_plant", + tempLow: -2, + stateLow: "frozen_plant", + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + category: "life", + state: "solid", + density: 1500, + }; + /*elements.cocoa_bean = { + color: ["#f2ede9", "#f0dfce", "#e8cfb5"], + behavior: behaviors.SOLID, + category: "liquids", + viscosity: 100000, + state: "liquid", + density: 593, + tick: function + };*/ + //THERMAL FOUNDATION LIQUIDS, FU BIO-OOZE, AND SOME PYROGENIC MATERIALS + nonAdjacentCoords = [ + [1, 1], + [1, -1], + [-1, 1], + [-1, -1] + ]; + //pyrotheum + elements.blazing_pyrotheum = { + color: "#ffdd55", + behavior: behaviors.LIQUID, + tick: function(pixel) { + if(pixel.temp >= -273 && pixel.temp <= 3707) { //temperature minimum of 3727 + pixel.temp += 50 + } else if(pixel.temp > 3677 && pixel.temp < 3727) { + pixel.temp = 3727 + } + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < adjacentCoords.length; i++) { + var oX = adjacentCoords[i][0]; + var oY = adjacentCoords[i][1]; + var fX = pX+oX; + var fY = pY+oY; + if(!isEmpty(fX,fY,true)) { + var checkPixel = pixelMap[fX][fY]; + var thisElementName = pixel.element; + var otherElementName = checkPixel.element; + var thisElement = elements[pixel.element]; + var otherElement = elements[checkPixel.element]; + thisElementName === otherElementName ? checkPixel.temp+=0.1 : checkPixel.temp==10; + } else if(isEmpty(fX,fY,false)) { + if(Math.random() < 0.05) { + createPixel("fire",fX,fY); + var checkPixel = pixelMap[fX][fY]; + checkPixel.temp = pixel.temp; + } + }; + }; + }, + viscosity: 1.2**4, + category: "liquids", + state: "liquid", + density:1994, + insulate:false, + temp: 3727, + }, + elements.gelid_cryotheum = { + color: "#00ddff", + behavior: behaviors.LIQUID, + tick: function(pixel) { + if(pixel.temp >= -223) { //temperature maximum of -223 + pixel.temp -= 50 + } else if(pixel.temp > -223 && pixel.temp < -273) { + pixel.temp = -223 + } + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < adjacentCoords.length; i++) { + var oX = adjacentCoords[i][0]; + var oY = adjacentCoords[i][1]; + var fX = pX+oX; + var fY = pY+oY; + if(!isEmpty(fX,fY,true)) { + var checkPixel = pixelMap[fX][fY]; + var thisElementName = pixel.element; + var otherElementName = checkPixel.element; + var thisElement = elements[pixel.element]; + var otherElement = elements[checkPixel.element]; + thisElementName === otherElementName ? checkPixel.temp-=0.1 : checkPixel.temp-=10; + }; + }; + /*for(i = 0; i < nonAdjacentCoords.length; i++) { + var oX = nonAdjacentCoords[i][0]; + var oY = nonAdjacentCoords[i][1]; + var fX = pX+oX; + var fY = pY+oY; + if(isEmpty(fX,fY,false)) { + if(Math.random() < 0.025) { + createPixel("snow",fX,fY); + var checkPixel = pixelMap[fX][fY]; + checkPixel.temp = pixel.temp; + }; + }; + };*/ //It should create snow, but the snow freezes into ice and it leaves unsightly floating ice everywhere. + }, + viscosity: 3**4, + category: "liquids", + state: "liquid", + density:3988, + insulate:false, + temp: -223, + }, + elements.tectonic_petrotheum = { + color: ["#342414","#3C2414","#2C1C14","#543424","#643C28","#74442C"], + behavior: [ + "XX|XX|XX", + "M2|XX|M2", + "M1|SW:dust AND M1|M1", + ], + tick: function(pixel) { //Code from R74n/vanilla "smash" tool + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < adjacentCoords.length; i++) { + var oX = adjacentCoords[i][0]; + var oY = adjacentCoords[i][1]; + var fX = pX+oX; + var fY = pY+oY; + if(!isEmpty(fX,fY,true)) { + var checkPixel = pixelMap[fX][fY]; + var otherElement = elements[checkPixel.element]; + if (typeof(otherElement.breakInto) !== "undefined") { + var hardness = otherElement.hardness ?? 0; + if (Math.random() < (1 - hardness)) { + var breakInto = otherElement.breakInto; + // if breakInto is an array, pick one + if (Array.isArray(breakInto)) { + breakInto = breakInto[Math.floor(Math.random() * breakInto.length)]; + }; + changePixel(checkPixel,breakInto); + } + } + }; + }; + }, + temp: 120, + viscosity: 1.5**4, + category: "liquids", + state: "liquid", + density:3988, + insulate:false, + }; + elements.zephyrean_aerotheum = { + color: ["#FFFCD9","#FEFFFC","#FDFFDB","#FFFFE8","#FBF6D3","#F1EDD0"], + behavior: behaviors.AGLIQUID, + viscosity: 0.1**4, + category: "liquids", + state: "liquid", + density:-800, + insulate:false, + tick: function(pixel) { + //"Projectiles that come into contact with zephyrean aerotheum are sent flying in a random direction away from the fluid." + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < adjacentCoords.length; i++) { + var oX = adjacentCoords[i][0]; + var oY = adjacentCoords[i][1]; + var fX = pX+oX; + var fY = pY+oY; + if(!isEmpty(fX,fY,true)) { + var checkPixel = pixelMap[fX][fY]; + var thisElementName = pixel.element; + var otherElementName = checkPixel.element; + var thisElement = elements[pixel.element]; + var otherElement = elements[checkPixel.element]; + if(otherElement.movable) { + if(typeof(checkPixel.vx) === "undefined") { + checkPixel.vx = 0; + }; + if(typeof(checkPixel.vy) === "undefined") { + checkPixel.vy = 0; + }; + if(Math.random() < 1/3) { + var randomVxChange = Math.floor(Math.random() * 9) - 4; //random value from -3 to 3 + var randomVyChange = Math.floor(Math.random() * 9) - 4; //different random value from -3 to 3 + //Notes + /* + Positive vx = right + Positive vy = down + adjacentCoords[0]: [0, 1] is downward; when it is detected, the pixel there should be sent farther down (positive vy). + adjacentCoords[1]: [0, -1] is upward; when it is detected, the pixel there should be sent farther up (negative vy). + adjacentCoords[2]: [1, 0] is rightward; when it is detected, the pixel there should be sent farther right (positive vx). + adjacentCoords[3]: [-1, 0] is leftward; when it is detected, the pixel there should be sent farther left (negative vx). + */ + switch(i) { + case 0: + randomVyChange = Math.abs(randomVyChange); + break; + case 1: + randomVyChange = (Math.abs(randomVyChange) * -1) - 1; + break; + case 2: + randomVxChange = Math.abs(randomVxChange); + break; + case 3: + randomVxChange = Math.abs(randomVxChange) * -1; + break; + default: + console.log("Uh-oh, i was somehow above 3!") + }; + if(otherElementName !== thisElementName) { + checkPixel.vx += randomVxChange; + checkPixel.vy += randomVyChange; + } + } + } + } + } + } + }; + elements.energized_glowstone = { + color: ["#fbb204", "#fcf605", "#fce704", "#f8c414", "#f8e814"], + behavior: [ + "M1 AND SW:light|M1 AND CR:light%40 AND SW:light|M1 AND SW:light", + "M2 AND CR:light%40|XX|M2 AND CR:light%40", + "XX|CR:light%40|XX", + ], + viscosity: 0.1**4, + category: "liquids", + state: "liquid", + density:-500, + insulate:false, //TODO: > Energized glowstone source blocks will gradually float upwards if there are no blocks above them. If they float at high levels (layers 120 and above by default) they will condense back into solid glowstone. They will also condense at 80% of this height if the fluid has no space to flow. + }, + elements.resonant_ender = { + color: ["#062c2c", "#062c2c", "#19a8a8", "#0a4646", "#1f8c8e", "#0c5c54", "#0c5c54"], + behavior: behaviors.LIQUID, + tick: function(pixel) { + for (let i = -2; i < 3; i++) { + for (let j = -2; j < 3; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (lifeArray.includes(pixelMap[pixel.x+j][pixel.y+i].element)) { + pixel.eeex = pixel.x + Math.floor(Math.random() * ((2 * 8) + 1)) - 8 + pixel.eeey = pixel.y + Math.floor(Math.random() * ((2 * 8) + 1)) - 8 + //if human + //handle heads + if(pixelMap[pixel.x+j][pixel.y+i].element == "head") { + if(isEmpty(pixel.eeex,pixel.eeey) && !outOfBounds(pixel.eeex,pixel.eeey) && isEmpty(pixel.eeex,pixel.eeey+1) && !outOfBounds(pixel.eeex,pixel.eeey+1)) { + tryMove(pixelMap[pixel.x+j][pixel.y+i],pixel.eeex,pixel.eeey) + tryMove(pixelMap[pixel.x+j][pixel.y+i+1],pixel.eeex,pixel.eeey+1) + } + } else if(pixelMap[pixel.x+j][pixel.y+i].element == "body") { + } else { + if(isEmpty(pixel.eeex,pixel.eeey) && !outOfBounds(pixel.eeex,pixel.eeey)) { + tryMove(pixelMap[pixel.x+j][pixel.y+i],pixel.eeex,pixel.eeey) + } + } + } + } + } + } + }, + category: "liquids", + density: 3000, + state: "liquid", + viscosity: 3**4, + } + elements.redstone_dust.tempHigh = 2500; + elements.redstone_dust.stateHigh = "destabilized_redstone"; + elements.redstone_dust.conduct = 0.9; + elements.redstone_dust.colorOn = ["#FF2424","#FF0000","#FF1200"]; + elements.redstone_dust.color = ["#7f0000","#5f0000","#5f0500"]; + elements.destabilized_redstone = { + color: ["#9e0303", "#98061a", "#b80704", "#c4020c", "#f70008", "#9e0303", "#98061a", "#b80704", "#e3020a", "#8c0303", "#8c0303"], + behavior: [ + "XX|SH|XX", + "M2 AND SH|XX|M2 AND SH", + "M1|M1 AND SH|M1", + ], + viscosity: 1.5**4, + category: "liquids", + state: "liquid", + density:1200, + } + elements.signalum = { + color: "#ff9321", + behavior: behaviors.WALL, + category: "solids", + density: 10500, + conduct: 1, + tempHigh: 1550, + stateHigh: "molten_signalum", + state: "solid", + } + elements.molten_sterling ??= {}; + elements.molten_sterling.reactions ??= {}; + elements.molten_sterling.reactions.destabilized_redstone = { "elem1": null, "elem2": "molten_signalum" } + elements.molten_signalum = { + color: "#f17414", + behavior: behaviors.MOLTEN, + density: 10500*0.9, + conduct: 0.30, + temp:600, + tempLow: 550, + stateLow: "signalum", + category: "liquids", + state: "liquid", + hidden: true, + } + elements.energized_glowstone.reactions ??= {}; + elements.energized_glowstone.reactions.gelid_cryotheum = { "elem1":"glowstone_dust" }; + elements.glowstone_dust.behavior = [ //it should emit light, right? + "CR:light%0.025|CR:light%0.025|CR:light%0.025", + "CR:light%0.025|XX|CR:light%0.025", + "CR:light%0.025|CR:light%0.025|CR:light%0.025" + ]; + runAfterLoad(function() { + lifeArray = Object.keys(elements).filter(function(e) { + return elements[e].category == "life"; + }) + }); + 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, + category: "liquids", + heatCapacity: 3.52, //unimplemented feature + name: "bio-ooze", + reactions: { + "water": { "elem1":"slime", "elem2":"slime" }, //balance + "poison": { "elem1":"slime", "elem2":"slime" }, //balance + //"acid": { "elem1":"wastestone" }, //acid should be sulfuric acid and product should be wastestone + //"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": ["bio_ooze","bio_ooze","poison","slime",null], "elem2": "infection" }, + "soap": { "elem1": "slime", "chance": 0.02 }, + "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) + "elem1": null, // First element transforms into; in this case, water deletes itself + "elem2": "mud", // Second element transforms into; in this case, dirt turns to mud + }, + "sand": { "elem1": null, "elem2": "wet_sand", }, + "salt": { "elem1": "salt_water", "elem2": null }, + "sugar": { "elem1": "sugar_water", "elem2": null, }, + "dust": { "elem1": "dirty_water", "elem2": null, }, + "ash": { "elem1": "dirty_water", "elem2": null, }, + "cyanide": { "elem1": "dirty_water", "elem2": null, }, + "carbon_dioxide": { "elem1": "seltzer", "elem2": null, "oneway":true }, + "sulfur": { "elem1": "dirty_water", "elem2": null, }, + "rat": { "elem1": "dirty_water", chance:0.005 }, + "plague": { "elem1": "dirty_water", "elem2": null, }, + "rust": { "elem1": "dirty_water", chance:0.005 }, + "fallout": { "elem1": "dirty_water", chance:0.25 }, + "radiation": { "elem1": "dirty_water", chance:0.25 }, + "uranium": { "elem1": "dirty_water", chance:0.25 }, + "quicklime": { "elem1": null, "elem2": "slaked_lime", }, + "rock": { "elem2": "wet_sand", "chance": 0.00035 }, + "ruins": { "elem2": "rock", "chance": 0.00035 }, + "mudstone": { "elem2": "mud", "chance": 0.00035 }, + "methane": { "elem1":"primordial_soup", "elem2":"primordial_soup", tempMin:60, charged:true }, + "ammonia": { "elem1":"primordial_soup", "elem2":"primordial_soup", tempMin:60, charged:true } + },*/ + state: "liquid", + density: 1.03, + conduct: 0.0008, + stain: 0.2, + viscosity: 60, + description: "A particularly potent toxic sludge loaded with parasites and ickiness.", + }; + function threshholdedPyrogen(pixel,threshholdTemp,baseHeat,divisor) { + if(pixel.temp < threshholdTemp) { + pixel.temp += Math.max(baseHeat,(threshholdTemp - pixel.temp) / divisor); + } else { + pixel.temp += baseHeat; + }; + }; + function tpHeatCalc(startTemp,threshholdTemp,baseHeat,divisor) { + if(startTemp < threshholdTemp) { + return Math.max(baseHeat,(threshholdTemp - startTemp) / divisor); + } else { + return baseHeat; + }; + }; + function itfChanceCalc(baseHeat,divisor,chanceLimit) { + return Math.min(chanceLimit,(baseHeat / divisor)) + }; + function inferniumTempFire(pixel,divisor,chanceLimit) { + if(Math.random() < Math.min(chanceLimit,(pixel.temp / divisor))) { //fire depending on temp + var randomCoord = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; //random place + var nx = pixel.x+randomCoord[0]; + var ny = pixel.y+randomCoord[1]; + if(isEmpty(nx,ny)) { //if empty + createPixel("fire",nx,ny); //create fire at coord coords + pixelMap[nx][ny].temp = pixel.temp; //set temp + }; + }; + }; + elements.pyreite = { + color: ["#cc674b","#e06e41","#f77320","#f77320","#fa9b28","#fac228"], + behavior: behaviors.WALL, + tempHigh: 5500, //Instead of making my own headcanons, I'm using Omniblog Of Starbound's headcanons :eggTF: + category: "solids", + density: 14600, + conduct: 0.66, + hardness: 0.79, + tick: function(pixel) { + threshholdedPyrogen(pixel,1722,0.25,512); + }, + }; + elements.infernium = { + color: ["#bf4b39","#e68453","#f7994d","#f7994d","#ffa154","#ffe875"], + behavior: [ + "XX|CR:fire%0.01|XX", + "XX|XX|XX", + "XX|XX|XX" + ], + tempHigh: 5526, //I have no headcanon to turn to for this mod so have multiples of 2763 + category: "solids", + density: 13815, + conduct: 0.691, + hardness: 0.79, + tick: function(pixel) { + threshholdedPyrogen(pixel,1763,0.32,512); + inferniumTempFire(pixel,40000,0.2); + }, + }; + elements.molten_pyreite = { + tick: function(pixel) { + pixel.temp += 0.25; + }, + reactions: { + molten_infernium: { elem1: "molten_infernyrite", elem2: "molten_infernyrite", temp1: 304, temp2: 304 } + }, + }; + elements.molten_infernium = { + tick: function(pixel) { + pixel.temp += 0.32; + inferniumTempFire(pixel,40000,0.2); + }, + }; + elements.infernyrite = { + color: ["#d45f2c","#f59449","#f7994d","#fcaa4c","#fab973","#ffea8c"], + behavior: [ + "XX|CR:fire%0.01|XX", + "XX|XX|XX", + "XX|XX|XX" + ], + tempHigh: 9901, + category: "solids", + density: 14197, + conduct: 0.63, + hardness: 0.8, + tick: function(pixel) { + threshholdedPyrogen(pixel,2501,0.79,480); + inferniumTempFire(pixel,40000,0.2); + }, + }; + elements.molten_infernyrite = { + tick: function(pixel) { + pixel.temp += 0.79; + inferniumTempFire(pixel,40000,0.2); //"canonically", the dilution and the pyrogenic synergy or whatever you want to call it just so happen to cancel out + }, + reactions: { + blazing_pyrotheum: { + elem1: "molten_infernyreitheum", + elem2: ["blazing_pyrotheum","molten_infernyreitheum"], + temp1: 1043, + temp2: 1043 + } + }, + }; + elements.infernyreitheum = { + color: ["#f2a863","#faaf4d","#ffb547","#fcaa4c","#fcd64c","#fff6ba"], + behavior: [ + "XX|CR:fire%0.08|XX", + "XX|XX|XX", + "XX|XX|XX" + ], + tempHigh: 11052, + category: "solids", + density: 15064, + conduct: 0.52, + hardness: 0.8, + tick: function(pixel) { + threshholdedPyrogen(pixel,8932,2.03,416); + inferniumTempFire(pixel,25000,0.4); //pyrotheum makes a lot of fire + }, + }; + function pyrestoneInfernyreitheumReaction(pyrestoneName) { + return { + elem1: "molten_pyrinfernyreitheum", + elem2: [pyrestoneName,pyrestoneName,"molten_pyrinfernyreitheum"], + temp1: 3085, + temp2: 3085 + }; + }; + elements.molten_infernyreitheum = { + tick: function(pixel) { + pixel.temp += 2.03; + inferniumTempFire(pixel,25000,0.4); + }, + reactions: { + unignited_pyrestone: pyrestoneInfernyreitheumReaction("unignited_pyrestone"), + ignited_pyrestone: pyrestoneInfernyreitheumReaction("ignited_pyrestone"), + heated_pyrestone: pyrestoneInfernyreitheumReaction("heated_pyrestone"), + burning_pyrestone: pyrestoneInfernyreitheumReaction("burning_pyrestone"), + blazing_pyrestone: pyrestoneInfernyreitheumReaction("blazing_pyrestone"), + fiery_pyrestone: pyrestoneInfernyreitheumReaction("fiery_pyrestone") + } + }; + elements.pyrinfernyreitheum = { + color: ["#e6c087","#f7c76d","#ffd79c","#ffd79c","#ffe387","#ffffd9"], + behavior: [ + "XX|CR:fire%2|XX", + "XX|XX|XX", + "XX|XX|XX" + ], + tempHigh: 18254, + category: "solids", + density: 17042, + conduct: 0.25, + hardness: 0.8, + tick: function(pixel) { + threshholdedPyrogen(pixel,15944,7.01,352); + inferniumTempFire(pixel,15000,0.8); //pyrotheum makes a lot of fire + }, + }; + elements.molten_pyrinfernyreitheum = { + tick: function(pixel) { + pixel.temp += 7.01; + inferniumTempFire(pixel,15000,0.8); //pyrotheum makes a lot of fire + } + }; + //MORE MORTAL PLANTS ## + var killerArray = ["radiation", "alcohol", "soap", "acid", "ammonia", "acid_gas", "bleach", "poison", "ice_nine", "methanol", "propanol", "butanol", "isopropanol", "phenol"]; //https://pubmed.ncbi.nlm.nih.gov/16986801/ + var plantArray = ["plant", "frozen_plant", "grass", "algae", "sapling", "seeds", "grass_seed", "wheat_seed", "wheat", "flower_seed", "pistil", "petal", "vine", "bamboo", "bamboo_plant", "corn_seed", "potato_seed", "root", "berry_seed", "old_berry_leaf", "berry_leaf", "berry", "banana_pseudostem", "banana_peduncle_1", "banana_peduncle_2", "petal", "banana_leaf", "banana_plant_top"]; + runAfterAutogen(function() { + //Try to detect radioactive elements (it's not perfect, but it's better than nothing) + var rads = Object.keys(elements).filter(function(name) { + return !!((elements[name].behavior ?? "undefined").toString().match(/C[RH]:[A-Za-z0-9_,]*radiation/)) + }); + killerArray = killerArray.concat(rads); + for(i = 0; i < killerArray.length; i++) { + if(!elements[killerArray[i]].reactions) { + elements[killerArray[i]].reactions = {} + } + for(j = 0; j < plantArray.length; j++) { + elements[killerArray[i]].reactions[plantArray[j]] = { "elem1":null, "elem2":"dead_plant" } + }; + }; + }); + //PAINT EVENT ## + randomEvents.paint = function() { + // set the color of a random circle to a random color + var x = Math.floor(Math.random()*(width-1))+1; + var y = Math.floor(Math.random()*(height-1))+1; + var randomR = Math.floor(Math.random() * 256); + var randomG = Math.floor(Math.random() * 256); + var randomB = Math.floor(Math.random() * 256); + var radius = Math.floor(Math.random()*5)+2; + var rColor = "rgb(" + randomR + "," + randomG + "," + randomB + ")"; + var coords = circleCoords(x,y,radius); + for (var i = 0; i < coords.length; i++) { + var coord = coords[i]; + if (!outOfBounds(coord.x,coord.y) && !isEmpty(coord.x,coord.y)) { + pixelMap[coord.x][coord.y].color = rColor; + }; + }; + }; + //CONFIGURABLE PAGE BACKGROUND COLOR ## + var backgroundUseStrings = ["bg","background","settings.bg"] + if(urlParams.get('pageColor') != null) { //null check + //Old method (as a query parameter) + color = urlParams.get('pageColor'); + if(color === "" || color === null) { //NaN check + color = "black"; + }; + if(backgroundUseStrings.includes(color.toLowerCase())) { + !settings.bg ? color = "black" : color = settings.bg; + color = settings.bg; + }; + color_Would_Be_A_Triplet_If_It_Started_With_An_Octothorpe = null; + color_Is_Supported_As_A_Background_By_The_Browser = null; + if( /^#([0-9A-F]{3}){1,2}$/i.test("#" + color) ) { + color_Would_Be_A_Triplet_If_It_Started_With_An_Octothorpe = true + } else { + color_Would_Be_A_Triplet_If_It_Started_With_An_Octothorpe = false + } + if( CSS.supports('background',color) ) { + color_Is_Supported_As_A_Background_By_The_Browser = true + } else { + color_Is_Supported_As_A_Background_By_The_Browser = false + } + if(color_Is_Supported_As_A_Background_By_The_Browser == false && color_Would_Be_A_Triplet_If_It_Started_With_An_Octothorpe == true) { + color = "#" + color + } + document.body.style.background = color; + } else { + //As a setting + runAfterLoad(function() { + var settingsMenu = document.getElementById("settingsMenu").getElementsByClassName("menuText")[0]; + var settingNodes = [...settingsMenu.childNodes].filter(function(node) { return node.nodeType == 1 }); + var lastSetting = settingNodes[settingNodes.length - 1]; + //console.log(lastSetting); + //console.log(lastSetting.getAttribute("style")); + //console.log(lastSetting.getAttribute("style")); + //Shape setting + var bgSettingSpan = document.createElement("span"); + bgSettingSpan.setAttribute("setting","pageBG"); + bgSettingSpan.setAttribute("class","setting-span"); + bgSettingSpan.textContent = "Page Background "; + var settingPicker = document.createElement("input"); + settingPicker.setAttribute("type","color"); + settingPicker.setAttribute("value",settings.pageBG ?? _cc.b.h); + settingPicker.setAttribute("onchange","settings.pageBG = this.value; document.body.style.background = this.value; saveSettings();"); + bgSettingSpan.appendChild(settingPicker); + settingsMenu.appendChild(bgSettingSpan); + }); + settings.pageBG ??= _cc.b.h; saveSettings(); + document.body.style["background-color"] = settings.pageBG; + }; + //GASEOUS FORMS AND BOILING OF SOME ELEMENTS ## + //glass { + elements.molten_glass = { + tempHigh: 2200, + stateHigh: "vaporized_glass", + } + elements.vaporized_glass = { + color: ["#D6B049","#E8D957","#E8AE57"], + behavior: [ + "M2|M1|M2", + "M1|XX|M1", + "M2|M1|M2", + ], + reactions: { + "vaporized_glass": { "elem1": null, "elem2": "hot_glass_cloud", "chance":0.3, "y":[0,15] }, + "hot_glass_cloud": { "elem1": "hot_glass_cloud", "chance":0.4, "y":[0,15] }, + }, + density: 2, //very rough approximation based on https://nvlpubs.nist.gov/nistpubs/jres/46/jresv46n3p176_A1b.pdf + temp: 2300, //https://www.sciencealert.com/did-this-piece-of-glass-really-break-a-law-of-thermodynamics + tempLow: 2200, + stateLow: "molten_glass", + category: "gases", + state: "gas", + hidden: true + }, + elements.hot_glass_cloud = { + color: ["#B69089","#C8B997","#C88E77"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:molten_glass%0.05|M1%7", + "XX|XX|XX", + ], + density: 2, + temp: 2300, + tempLow: 2200, + stateLow: "cold_glass_cloud", + category: "gases", + state: "gas" + }, + elements.cold_glass_cloud = { + color: ["#967089","#A89997","#A86E77"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:glass_shard%0.05|M1%7", + "XX|XX|XX", + ], + density: 2, + temp: 2000, + tempHigh: 2200, + stateHigh: "hot_glass_cloud", + category: "gases", + state: "gas" + }, + //} + // nitroglycerin { + elements.nitro_gas = { + color: "#89d162", + behavior: behaviors.GAS, + behaviorOn: [ + "XX|XX|XX", + "XX|EX:10|XX", + "XX|XX|XX", + ], + conduct: 1, + category: "weapons", + tempHigh: 218, + stateHigh: "explosion", + tempLow: 50, + stateLow: "nitro", + burn: 100, + burnTime: 1, + burnInto: "explosion", + breakInto: "explosion", + viscosity: 36, + state: "liquid", + density: 1600, + excludeRandom: true, + alias: "nitroglycerin gas" + }; + elements.nitro.tempHigh = 50; + elements.nitro.stateHigh = "nitro_gas"; + //} + // ash { + elements.ash.tempHigh = 1200 //https://www.quora.com/Can-you-melt-ashes + elements.ash.stateHigh = "molten_ash" //https://www.sciencedirect.com/science/article/pii/S1877705817326772 + elements.molten_ash = { + color: ["#df6f30","#df8c30","#df4d30"], + behavior: behaviors.MOLTEN, + temp: 1300, + tempLow: 1200, + stateLow: "ash", + tempHigh: 1700, //https://authors.library.caltech.edu/58447/1/018-Senior.pdf + //https://pubs.acs.org/doi/10.1021/ef049693l + stateHigh: "vaporized_ash", + viscosity: 10000, + category: "liquids", + state: "liquid", + density: 2725 + }, + elements.vaporized_ash = { + color: ["#df9f50","#dfbc50","#df7d50"], + behavior: [ + "M2|M1|M2", + "M1|XX|M1", + "M2|M1|M2", + ], + reactions: { + "vaporized_ash": { "elem1": null, "elem2": "hot_ash_cloud", "chance":0.3, "y":[0,15] }, + "hot_ash_cloud": { "elem1": "hot_ash_cloud", "chance":0.4, "y":[0,15] }, + }, + temp: 1800, + tempLow: 1700, + stateLow: "molten_ash", + category: "gases", + state: "gas", + hidden: true, + density: 3 + }, + elements.hot_ash_cloud = { + color: ["#bf8f50","#bfac50","#bf7d50"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:molten_ash%0.05|M1%7", + "XX|XX|XX", + ], + density: 0.7, + temp: 1800, + tempLow: 1700, + stateLow: "cold_ash_cloud", + category: "gases", + state: "gas" + }, + console.log("3/8 loaded"); + elements.cold_ash_cloud = { + color: ["#af8f50","#ab9c50","#af6d50"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:ash%0.05|M1%7", + "XX|XX|XX", + ], + density: 0.7, + temp: 1600, + tempHigh: 1700, + stateHigh: "hot_ash_cloud", + category: "gases", + state: "gas" + }, + //} + // charcoal { + elements.charcoal.tempHigh = 800 + elements.charcoal.stateHigh = "carbon_dioxide" + //} + //carbon dioxide { + /*fuck this, i can't work out the offset-infested math + function carbonDioxideDecompRatio(temp) { + // + // K is the ratio of O_2 to CO_2 + // If K = 100, there is 100 times more O_2 + // If K = 1, there is a 1:1 ratio + // + return Math.E**(((1110190+(13.083*(temp-298)))-(temp*(149.498+(13.083*(Math.log(temp/298))))))/(-8.31446*temp)) + } + function carbonDioxideDecompChance(temp) { + //Expected 0.5 at 6275.6434478747902 + if(typeof(temp) === "undefined") { + throw new Error("Temp must be specified~"); + }; + if(typeof(temp) == "string") { + temp = parseFloat(temp); + }; + if(isNaN(temp)) { + throw new TypeError(typeof(temp) == "number" ? "Temp cannot be NaN~" : "Temp must be a number~"); + }; + if(temp == Infinity) { + return 1; + }; + if(temp <= 0) { + return 0; + }; + var K = carbonDioxideDecompRatio(temp); + return 1-(1/(K+1)); + }; + */ + //Mass given is the molar mass of O_2 molecule (31.999 g) + //O_2 bond energy is 495 kJ/mol + //Heat capacity is 0.918 J/(g*K) + //in case the link goes down: c = Q/(m * delta-T); c = capacity, m = mass, delta-T = temp change, energy = Q + //https://www.calctool.org/thermodynamics/specific-heat + tupleAdverbs = ['Nullly', 'Singly', 'Doubly', 'Triply', 'Quadruply', 'Quintuply', 'Sextuply', 'Septuply', 'Octuply', 'Nonuply', 'Decuply', 'Undecuply', 'Duodecuply', 'Tredecuply', 'Quattuordecuply', 'Quindecuply', 'Sexdecuply', 'Septendecuply', 'Octodecuply', 'Novemdecuply', 'Vigintuply', 'Unvigintuply', 'Duovigintuply', 'Trevigintuply', 'Quattuorvigintuply', 'Quinvigintuply', 'Sexvigintuply', 'Septenvigintuply', 'Octovigintuply', 'Novemvigintuply', 'Trigintuply'].map(x => x.toLowerCase()); + //} + // baking soda { + elements.baking_soda.tempHigh = 150, + elements.baking_soda.stateHigh = ["water","carbon_dioxide","calcined_soda"] + // decomposition result { + elements.calcined_soda = { //TODO: decomposition? + color: "#ededed", + behavior: behaviors.POWDER, + reactions: { + "water": { "elem1": "washing_soda", "elem2": null } //should be 10x water + //"carbon_dioxide": not possible: Na_{2}CO_{3} + CO_{2} + H_{2}O → 2NaHCO_{3} + }, + category: "powders", + state: "solid", + density: 2540, + tempHigh: 851, + } + if(!elements.molten_calcined_soda) { + elements.molten_calcined_soda = {} + } + elements.molten_calcined_soda.temp = 1700 + elements.molten_calcined_soda.tempHigh = 1600 + elements.molten_calcined_soda.stateHigh = "vaporized_calcined_soda" + elements.molten_calcined_soda.density = 1920 + elements.vaporized_calcined_soda = { + color: ["#ffbf60","#ffdc60","#ff9d60"], + behavior: [ + "M2|M1|M2", + "M1|XX|M1", + "M2|M1|M2", + ], + reactions: { + "vaporized_calcined_soda": { "elem1": null, "elem2": "hot_calcined_soda_cloud", "chance":0.3, "y":[0,15] }, + "hot_calcined_soda_cloud": { "elem1": "hot_calcined_soda_cloud", "chance":0.4, "y":[0,15] }, + }, + temp: 1700, + tempLow: 1600, + stateLow: "molten_calcined_soda", + category: "gases", + state: "gas", + hidden: true, + density: 1.5, //bs + }, + elements.hot_calcined_soda_cloud = { + color: ["#cfbf70","#cfcc70","#cf9d70"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:molten_calcined_soda%0.05|M1%7", + "XX|XX|XX", + ], + density: 0.7, + temp: 1700, + tempLow: 1600, + stateLow: "cold_calcined_soda_cloud", + category: "gases", + state: "gas", + }, + elements.cold_calcined_soda_cloud = { + color: ["#afaf70","#afac70","#af8d70"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:calcined_soda%0.05|M1%7", + "XX|XX|XX", + ], + density: 0.7, + temp: 1500, + tempHigh: 1600, + stateHigh: "hot_calcined_soda_cloud", + category: "gases", + state: "gas", + }, + //} + // decomp hydrate { + elements.washing_soda = { + color: "#ededed", + behavior: behaviors.POWDER, + //no reactions because it always requires ******* water + category: "powders", + state: "solid", + density: 1460, + tempHigh: 400, + stateHigh: ["water","calcined_soda"], + } + //} + //alkalinities { + elements.acid.reactions.baking_soda = { "elem1":"neutral_acid", "elem2":null } + elements.acid.reactions.calcined_soda = { "elem1":"neutral_acid", "elem2":null } + elements.acid.reactions.washing_soda = { "elem1":"neutral_acid", "elem2":null } + //} + //} + // calcium { + elements.molten_calcium = { + tempHigh: 2200, + stateHigh: "vaporized_calcium", + } + elements.vaporized_calcium = { + color: ["#ffc94a", "#fcd34c", "#ffae36", "#ff9c40","#ffcd90","#cf8d50"], + behavior: [ + "M2|M1|M2", + "M1|XX|M1", + "M2|M1|M2", + ], + reactions: { + "vaporized_calcium": { "elem1": null, "elem2": "hot_calcium_cloud", "chance":0.3, "y":[0,15] }, + "hot_calcium_cloud": { "elem1": "hot_calcium_cloud", "chance":0.4, "y":[0,15] }, + }, + density: 1.5, //most of these density values are complete bullshit due to a lack of research + temp: 1550, + tempLow: 1484, + stateLow: "molten_calcium", + category: "gases", + state: "gas", + hidden: true + }, + elements.hot_calcium_cloud = { + color: ["#dfa98a", "#dcb38c", "#df8e76", "#ef8c60","#efbdb0","#af8d70"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:molten_calcium%0.05|M1%7", + "XX|XX|XX", + ], + density: 1.5, + temp: 1550, + tempLow: 842, + stateLow: "cold_calcium_cloud", + category: "gases", + state: "gas" + }, + elements.cold_calcium_cloud = { + color: ["#bf998a", "#bca38c", "#bf8e76", "#cf8c60","#cfadb0","#9f8d70"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:calcium%0.05|M1%7", + "XX|XX|XX", + ], + density: 2, + temp: 800, + tempHigh: 842, + stateHigh: "hot_calcium_cloud", + category: "gases", + state: "gas", + } + //} + // clay { + if(!elements.baked_clay) { + elements.baked_clay = {} + } + elements.baked_clay.tempHigh = 1600 //the range of melting points online is so fucking wide + elements.baked_clay.stateHigh = "molten_clay" + elements.molten_clay = { + color: ["#ff6d23","#ff5723","#ff4100"], + behavior: [ + "XX|CR:fire%2.5|XX", + "M2|XX|M2", + "M1|M1|M1", + ], + temp: 1700, + tempLow: 1600, + stateLow: "baked_clay", + viscosity: 10000, + hidden: true, + state: "liquid", + density: 1800, + tempHigh: 2980, + stateHigh: "vaporized_clay", + category: "liquids", + } + elements.vaporized_clay = { + color: ["#ff8d43","#ff7743","#ff6120"], + behavior: [ + "M2|M1|M2", + "M1|XX|M1", + "M2|M1|M2", + ], + reactions: { + "vaporized_clay": { "elem1": null, "elem2": "hot_clay_cloud", "chance":0.3, "y":[0,15] }, + "hot_clay_cloud": { "elem1": "hot_clay_cloud", "chance":0.4, "y":[0,15] }, + }, + density: 1.6, + temp: 1700, + tempLow: 1600, + stateLow: "molten_clay", + category: "gases", + state: "gas", + hidden: true + }, + elements.hot_clay_cloud = { + color: ["#ff9945", "#fca347", "#ff7e31"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:molten_clay%0.05|M1%7", + "XX|XX|XX", + ], + density: 1.5, + temp: 1550, + tempLow: 842, + stateLow: "cold_clay_cloud", + category: "gases", + state: "gas" + }, + elements.cold_clay_cloud = { + color: ["#ef7945", "#ec8347", "#ef5e31"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:baked_clay%0.05|M1%7", + "XX|XX|XX", + ], + density: 2, + temp: 800, + tempHigh: 842, + stateHigh: "hot_clay_cloud", + category: "gases", + state: "gas" + }, + //} + // salt { + elements.molten_salt = { + tempHigh: 1465, + stateHigh: "vaporized_salt", + } + elements.vaporized_salt = { + color: ["#ff9f60","#ffbc60","#ff7d60"], + behavior: [ + "M2|M1|M2", + "M1|XX|M1", + "M2|M1|M2", + ], + reactions: { + "vaporized_salt": { "elem1": null, "elem2": "hot_salt_cloud", "chance":0.3, "y":[0,15] }, + "hot_salt_cloud": { "elem1": "hot_salt_cloud", "chance":0.4, "y":[0,15] }, + }, + density: 1946, + temp: 1550, + tempLow: 1465, + stateLow: "molten_salt", + category: "gases", + state: "gas", + hidden: true + }, + elements.hot_salt_cloud = { + color: ["#ef8f30","#efac60","#ef6d60"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:molten_salt%0.05|M1%7", + "XX|XX|XX", + ], + density: 2.2, + temp: 1550, + tempLow: 801, + stateLow: "cold_salt_cloud", + category: "gases", + state: "gas" + }, + elements.cold_salt_cloud = { + color: ["#cf7f60","#cf9c60","#cf7d60"], + behavior: [ + "XX|XX|XX", + "M1%7|CH:salt%0.05|M1%7", + "XX|XX|XX", + ], + density: 2.2, + temp: 700, + tempHigh: 801, + stateHigh: "hot_salt_cloud", + category: "gases", + state: "gas", + } + //} + runAfterLoad(function() { + if(elements.acid_gas.tempHigh) { + delete elements.acid_gas.tempHigh + } + if(elements.acid_gas.stateHigh) { + delete elements.acid_gas.stateHigh + } + elements.acid.stateHigh = "acid_gas" + elements.acid_gas.tempLow = 400 + elements.acid_gas.stateLow = "acid" + elements.yogurt.tempHigh = 400 + elements.yogurt.stateHigh = "ash" + elements.dust.tempHigh = 400 + elements.dust.stateHigh = "fire" + elements.concoction.reactions.vaporized_glass = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.hot_glass_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.cold_glass_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.molten_ash = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.vaporized_ash = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.hot_ash_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.cold_ash_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.calcined_soda = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.molten_calcined_soda = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.vaporized_calcined_soda = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.hot_calcined_soda_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.cold_calcined_soda_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.washing_soda = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.vaporized_calcium = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.hot_calcium_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.cold_calcium_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.molten_clay = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.vaporized_clay = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.hot_clay_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.cold_clay_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.vaporized_salt = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.hot_salt_cloud = { "elem1": "mistake", "elem2": null } + elements.concoction.reactions.cold_salt_cloud = { "elem1": "mistake", "elem2": null } + }); + //CHLORINE TRIFLUORIDE ## + function finishBurn(pixel) { + var info = elements[pixel.element]; + var burnInto = info.burnInto; + if (burnInto == undefined) { + burnInto = 'fire'; + } + else if (burnInto instanceof Array) { + burnInto = burnInto[Math.floor(Math.random()*burnInto.length)]; + } + if (burnInto == undefined) { + burnInto = 'fire'; + } + changePixel(pixel,burnInto,(burnInto !== "smoke")); + if (info.fireColor != undefined && burnInto == "fire") { + pixel.color = pixelColorPick(pixel,info.fireColor); + } + else { + pixel.color = pixelColorPick(pixel) + } + }; + function clf3Tick(pixel) { + for(i = 0; i < adjacentCoords.length; i++) { + var oX = adjacentCoords[i][0]; + var oY = adjacentCoords[i][1]; + var fX = pixel.x+oX; + var fY = pixel.y+oY; + if(!isEmpty(fX,fY,true)) { + var otherPixel = pixelMap[fX][fY]; + var otherElement = otherPixel.element; + if(otherElement === "water") { + explodeAt(otherPixel.x,otherPixel.y,7,"fire,hydrofluoric_acid,oxygen,acid,chlorine") + }; + if(!elements.chlorine_trifluoride.ignore.includes(otherElement)) { + if(!otherPixel.burning) { otherPixel.burning = true }; + if(!otherPixel.burnStart) { otherPixel.burnStart = pixelTicks }; + var instaburnChance = 0.05 + (pixelTicks - otherPixel.burnStart) / 1000 + if(Math.random() < instaburnChance) { + finishBurn(otherPixel); + }; + }; + }; + }; + }; + var clf3IgnoreList = ["foof","solid_foof","oxygen","liquid_oxygen","oxygen_ice","chlorine","liquid_chlorine","liquid_hydrogen_fluoride","liquid_fluorine","fluorine","fluorine_ice","hydrogen_fluoride","hydrofluoric_acid","hydrofluoric_acid_gas","fire","acid_gas","neutral_acid","acid","acid_cloud","nitrogen","helium","liquid_helium","tralphium","liquid_tralphium","neon","liquid_neon","solid_neon","neon_ice","neon_snow","argon","liquid_argon","solid_argon","argon_ice","argon_snow", "krypton","liquid_krypton","solid_krypton","krypton_ice","krypton_snow", "xenon","liquid_xenon","solid_xenon","xenon_ice","xenon_snow", "radon","liquid_radon","solid_radon","radon_ice","radon_snow","ionized_helium","ionized_tralphium","wall","chlorine_trifluoride","chlorine_trifluoride_ice","chlorine_trifluoride_gas","quartz"]; + //todo: PTFE, passivation + elements.chlorine_trifluoride = { + color: "#8aa65b", + behavior: behaviors.LIQUID, + //ignore list copied from chem.js + ignore: clf3IgnoreList, //the elements that don't exist won't trigger any error here becuase the code's just checking pixels' elements against this list + tick: function(pixel) { + clf3Tick(pixel); + }, + category:"liquids", + state: "liquid", + density: 1770, + tempLow: -76.34, + tempHigh: 11.75, + temp: 5, + }; + elements.chlorine_trifluoride_gas = { + tick: function(pixel) { + clf3Tick(pixel); + }, + density: 3.78, //variously or 3.18, + //tempHigh: 220, //variously or 180, + //stateHigh: ["chlorine_fluoride","fluorine"], + }; + elements.chlorine_trifluoride_ice = { + tick: function(pixel) { + clf3Tick(pixel); + }, + }; + //GLENN'S GASES PARTIAL PORT ## + //Glenn's Gases is licensed under the GNU LGPL. + //http://www.gnu.org/licenses/lgpl.html + //https://www.jamieswhiteshirt.com/minecraft/mods/gases/information/?Licensing + //Coal exists in NM + runAfterLoad(function() { + elements.coal.breakInto = "coal_dust" + }); + elements.coal_dust = { + color: "#363023", + behavior: behaviors.GAS, + tick: function(pixel) { + if(pixel.burning) { + explodeAt(pixel.x,pixel.y,5,"fire,ignited_gas") + } + }, + category: "gases", + density: 561, + state: "gas", + burn: 132, + burnTime: 10, + burnInto: ["ash", "fire", "carbon_dioxide"], + }; + //Chlorine exists. + //Natural gas is mostly ammonia, which exists. + elements.red_gas = { + color: "#c74c52", + behavior: behaviors.GAS, + tick: function(pixel) { + if(pixel.burning) { + explodeAt(pixel.x,pixel.y,8,"fire,ignited_gas") + } + }, + category: "gases", + density: 1.5, + state: "gas", + burn: 300, + burnTime: 10, + burnInto: ["fire", "explosion", "explosion"], + }; + elements.nitrous_gas = { + color: "#854428", + behavior: behaviors.GAS, + reactions: { + "water": {"elem1": "acidic_vapour", "elem2": "acidic_vapour"} + }, + category: "gases", + density: 1.5, + state: "gas", + }; + elements.acidic_vapour = { + color: ["#5282d1", "#4e6fad"], + behavior: [ + "M2|M1 AND DB|M2", + "M1 AND DB|XX|M1 AND DB", + "M2%50|M1%50 AND DB|M2%50", + ], + ignore: elements.acid.ignore, + category: "gases", + density: 1.5, + state: "gas", + }; + elements.void_gas = { + color: "#111111", + behavior: behaviors.GAS, + reactions: { + "light": { "elem1": null, "chance": 0.1 }, + "fire": { "elem1": null, "chance": 0.08 } + }, + category: "gases", + density: 1.5, + state: "gas", + }; + elements.electric_gas = { + color: ["#3693b3", "#246e64"], + behavior: [ + "M2%33.3 AND CR:electric%1 AND CR:lightning%0.005|M1%33.3 AND CR:electric%1 AND CR:lightning%0.005|M2%33.3 AND CR:electric%1 AND CR:lightning%0.005", + "M1%33.3 AND CR:electric%1 AND CR:lightning%0.005|XX%000000000000000000000000000000000000000000000|M1%33.3 AND CR:electric%1 AND CR:lightning%0.005", + "M2%33.3 AND CR:electric%1 AND CR:lightning%0.005|M1%33.3 AND CR:electric%1 AND CR:lightning%0.005|M2%33.3 AND CR:electric%1 AND CR:lightning%0.005", + ], + hardness: 0.8, + reactions: { + "corrosive_gas": { "elem2": "turquoise_dust", "elem1": "blue_dust", "chance": 0.5 }, + "blue_dust": { "elem1": null, "elem2": "turquoise_dust", "chance": 0.5 } + }, + category: "gases", + density: 1.225, + state: "gas" + }; + corrosiveGasMaxHardness = 0.6 + elements.corrosive_gas = { + color: ["#2929e6", "#151cad"], + behavior: [ + "M2%33.3|M1%33.3|M2%33.3", + "M1%33.3|XX%0000|M1%33.3", + "M2%33.3|M1%33.3|M2%33.3", + ], + hardness: 0.8, + tick: function(pixel) { + //delete neighbors + for(i = 0; i < adjacentCoords.length; i++) { + nx = pixel.x + adjacentCoords[i][0]; + ny = pixel.y + adjacentCoords[i][1]; + if(!isEmpty(nx,ny,true)) { + if((elements[pixelMap[nx][ny].element].hardness || 0) <= corrosiveGasMaxHardness) { + if(Math.random() < 0.2) { + if(Math.random() < 1 - ((pixel.hardness || 0))) { + deletePixel(nx,ny); + }; + }; + }; + }; + }; + }, + reactions: { + "electric_gas": { "elem2": "blue_dust", "elem1": "turquoise_dust", "chance": 0.5 }, + "turquoise_dust": { "elem1": null, "elem2": "blue_dust", "chance": 0.5 } + }, + category: "gases", + density: 1.225, + state: "gas", + }; + elements.blue_dust = { + color: ["#063ca1", "#042d94", "#063ca1", "#042d94", "#1d66ff"], + behavior: behaviors.POWDER, + hardness: 0.6, + category: "powders", + state: "solid", + density: 1600, + } + elements.turquoise_dust = { + color: ["#12a6a6","#1aa3a3","#12a6a6","#1aa3a3","#00ffff"], + behavior: behaviors.POWDER, + hardness: 0.6, + category: "powders", + state: "solid", + density: 1600, + } + if(!settings) { + settings = {} + } + if(!settings.bg) { + settings.bg = _cc.b.h + } + elements.black_damp = { + color: settings.bg, + behavior: behaviors.GAS, + reactions: { + "fire": { elem2: null } + }, + tick: function(pixel) { + /*var baseColor = settings.bg instanceof Array ? averageRgbPrefixedColorArray(settings.bg.map(x => convertColorFormats(x,"rgb"))) : convertColorFormats(settings.bg,"rgb"); + baseColor = convertColorFormats(baseColor,"json"); + pixel.color = "rgba(" + Object.values(baseColor).join(",") + ",0)"*/ + pixel.color = "rgba(0,0,0,0)" + }, + hardness: 0.6, + category: "gases", + density: 1.225, + state: "gas", + }; + if(!elements.torch.reactions) { + elements.torch.reactions = {} + } + elements.torch.reactions.black_damp = { elem1: "wood" } + elements.rock_dust = { + color: "#878783", + behavior: behaviors.GAS, + reactions: { + "water": {"elem1": "dirty_water", "elem2": null } + }, + category: "gases", + density: 2.45, + state: "gas", + tempHigh: 950, + stateHigh: [null,null,null,null,"magma"], + } + elements.rock.breakInto.push("rock_dust") + lightArray = ["fire", "plasma", "cold_fire", "light", "laser", "electric", "radiation", "mystic_fire", "liquid_fire", "liquid_plasma", "liquid_cold_fire", "le_liquid_light", "liquid_laser", "liquid_electric", "liquid_radiation", "liquid_mystic_fire", "magma", "liquid_light", "solid_light"] + ledArray = ["led_r", "led_g", "led_b"] + elements.iocalfaeus_gas = { + color: ["#562173", "#481b61"], + behavior: behaviors.GAS, + tick: function(pixel) { + if(!pixel.hot) { + pixel.hot = false + } + for (let i = -2; i < 3; i++) { + for (let j = -2; j < 3; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if ((lightArray.includes(pixelMap[pixel.x+j][pixel.y+i].element)) || (pixelMap[pixel.x+j][pixel.y+i].temp >= 525) || (ledArray.includes(pixelMap[pixel.x+j][pixel.y+i].element) && pixelMap[pixel.x+j][pixel.y+i].charge)) { + pixel.hot = true + } + } + } + } + if(pixel.hot == true) { + pixel.temp += 16 + } + if(pixel.hot == true && Math.random() < 0.02) { + pixel.hot = false + } + }, + category: "gases", + density: 0.97, + state: "gas", + } + //Helium exists. + finineRange = 6 + elements.finine = { + color: ["#ffffec", "#fafade", "#ebebd5", "#c9c9b7", "#80806f"], + behavior: [ + "M2%33.3|M1%33.3|M2%33.3", + "M1%33.3|XX%0000|M1%33.3", + "M2%33.3|M1%33.3|M2%33.3", + ], + tick: function(pixel) { + for(i = 0; i < adjacentCoords.length; i++) { + nx = pixel.x + adjacentCoords[i][0]; + ny = pixel.y + adjacentCoords[i][1]; + if(!isEmpty(nx,ny,true)) { + if (lifeArray.includes(pixelMap[nx][ny].element)) { + pixel.eeex = pixel.x + Math.floor(Math.random() * ((2 * finineRange) + 1)) - finineRange + pixel.eeey = pixel.y + Math.floor(Math.random() * ((2 * finineRange) + 1)) - finineRange + //if human + //handle heads + if(pixelMap[nx][ny].element == "head") { + if(isEmpty(pixel.eeex,pixel.eeey,false) && isEmpty(pixel.eeex,pixel.eeey+1,false)) { + tryMove(pixelMap[nx][ny],pixel.eeex,pixel.eeey) + tryMove(pixelMap[nx][ny+1],pixel.eeex,pixel.eeey+1) + }; + } else if(pixelMap[nx][ny].element == "body") { + if(isEmpty(pixel.eeex,pixel.eeey,false) && isEmpty(pixel.eeex,pixel.eeey-1,false)) { + tryMove(pixelMap[nx][ny],pixel.eeex,pixel.eeey) + tryMove(pixelMap[nx][ny-1],pixel.eeex,pixel.eeey-1) + }; + } else { + if(isEmpty(pixel.eeex,pixel.eeey,false)) { + tryMove(pixelMap[nx][ny],pixel.eeex,pixel.eeey) + }; + }; + }; + }; + }; + }, + category: "gases", + density: 1.225, + state: "gas", + } + //Smoke exists. + elements.ignited_gas = { + color: ["#fc9a58", "#faae3c", "#ffef3d"], + behavior: [ + "M2|M1 AND CR:fire%0.5|M2", + "M1 AND CR:fire%0.5|XX|M1 AND CR:fire%0.5", + "M2|M1 AND CR:fire%0.5|M2", + ], + category: "gases", + state: "gas", + density: 0.306, + burning: true, + burnTime: 30, + temp: elements.fire.temp, + burnInto: ["fire","smoke"], + hidden: true, + } + //Diabaline + elements.diabaline = { + color: "#7e4e9c", + behavior: behaviors.POWDER, + tick: function(pixel) { + if(!pixel.glow) { + pixel.glow = false + } + if(!pixel.oldColor) { + pixel.oldColor = pixel.color + } + if(!pixel.rA) { + pixel.rA = 0 + } + if(!pixel.gA) { + pixel.gA = 0 + } + if(!pixel.bA) { + pixel.bA = 0 + } + if(!pixel.rB) { + pixel.rB = 0 + } + if(!pixel.gB) { + pixel.gB = 0 + } + if(!pixel.bB) { + pixel.bB = 0 + } + if(!pixel.finalR) { + pixel.finalR = 0 + } + if(!pixel.finalG) { + pixel.finalG = 0 + } + if(!pixel.finalB) { + pixel.finalB = 0 + } + if(!pixel.finalColor) { + pixel.finalColor = "" + } + for (let i = -1; i < 2; i++) { + for (let j = -1; j < 2; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (elements[pixelMap[pixel.x+j][pixel.y+i].element].category == "gases") { + pixel.glow = true + } + } + } + } + if(pixel.glow == true) { + if(pixel.oldColor.split(",")) { + pixel.rA = pixel.oldColor.split(",")[0].slice(4) + pixel.gA = pixel.oldColor.split(",")[1] + pixel.bA = pixel.oldColor.split(",")[2].slice(0,-1) + pixel.finalR = parseInt(pixel.rA) + 86 + pixel.finalG = parseInt(pixel.gA) + 93 + pixel.finalB = parseInt(pixel.bA) + 81 + pixel.finalColor = "rgb(" + pixel.finalR + "," + pixel.finalG + "," + pixel.finalB + ")" + pixel.color = pixel.finalColor + }; + } + if(pixel.glow == true && Math.random() < 0.02) { + pixel.glow = false + } + if(pixel.glow == false) { + pixel.color = pixel.oldColor + } + }, + category: "solids", + density: 1500, + state: "solid", + } + runAfterLoad(function() { + lifeArray = Object.keys(elements).filter(function(e) { + return elements[e].category == "life"; + }); + if(!elements.void_gas.reactions) { + elements.void_gas.reactions = {} + }; + for(i = 0; i < lifeArray.length; i++) { + elements.void_gas.reactions[lifeArray[i]] = { "elem2": null, "chance": 0.7 } + }; + for(i = 0; i < lifeArray.length; i++) { + elements.black_damp.reactions[lifeArray[i]] = { "elem2": null, "chance": 0.7 } + }; + elements.acidic_vapour.ignore = elements.acid.ignore + /* + elements.radioactive_rock_dust = { + color: "#839e78", + behavior: behaviors.RAD_GAS, + reactions: { + "water": {"elem1": "radioactive_water", "elem2": null } + }, + category: "gases", + density: 2.45, + state: "gas", + tempHigh: 950, + stateHigh: [null,null,null,null,"radioactive_magma"], + } + elements.radioactive_rock.breakInto.push("radioactive_rock_dust") + */ + elements.rock_dust.tempHigh = 3000 + elements.rock_dust.stateHigh = "vaporized_rock" + /* + elements.radioactive_rock_dust.tempHigh = 3000 + elements.radioactive_rock_dust.stateHigh = "vaporized_rock" + */ + }); + //IOCALFAEUS CLONES + elements.iorefrius_gas = { + color: ["#217349", "#1b5f3c"], + behavior: behaviors.GAS, + tick: function(pixel) { + if(!pixel.cold) { + pixel.cold = false + } + for (let i = -2; i < 3; i++) { + for (let j = -2; j < 3; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i,true)) { + var newPixel = pixelMap[pixel.x+j][pixel.y+i]; + if ((lightArray.includes(newPixel.element)) || (newPixel.temp >= 525) || (ledArray.includes(newPixel.element) && newPixel.charge) || (newPixel.cold && Math.random() < 0.04)) { + pixel.cold = true; + }; + }; + }; + }; + if(pixel.cold == true) { + pixel.temp -= 16; + }; + if(pixel.cold == true && Math.random() < 0.02) { + pixel.cold = false; + }; + }, + category: "gases", + density: 0.97, + state: "gas", + }; + elements.iolucius_gas = { + color: ["#e9c5ed", "#e2b0e8"], + behavior: behaviors.GAS, + tick: function(pixel) { + if(!pixel.lit) { + pixel.lit = false + } + for (let i = -2; i < 3; i++) { + for (let j = -2; j < 3; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i,true)) { + var newPixel = pixelMap[pixel.x+j][pixel.y+i]; + if ((lightArray.includes(newPixel.element)) || (newPixel.temp >= 525) || (ledArray.includes(newPixel.element) && newPixel.charge) || (newPixel.lit && Math.random() < 0.04)) { + pixel.lit = true; + }; + }; + }; + }; + if(Math.random() < 0.05) { + if(pixel.lit == true) { + var randomLightOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var lightX = pixel.x + randomLightOffset[0]; + var lightY = pixel.y + randomLightOffset[1]; + if(isEmpty(lightX,lightY,false)) { + createPixel("light",lightX,lightY); + }; + }; + }; + if(pixel.lit == true && Math.random() < 0.02) { + pixel.lit = false; + }; + }, + category: "gases", + density: 0.97, + state: "gas", + }; + elements.ioradius_gas = { + color: ["#a6a258", "#97944e"], + behavior: behaviors.GAS, + tick: function(pixel) { + if(!pixel.rlit) { + pixel.rlit = false + } + for (let i = -2; i < 3; i++) { + for (let j = -2; j < 3; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i,true)) { + var newPixel = pixelMap[pixel.x+j][pixel.y+i]; + if ((lightArray.includes(newPixel.element)) || newPixel.element === "radiation" || (newPixel.temp >= 525) || (ledArray.includes(newPixel.element) && newPixel.charge) || (newPixel.rlit && Math.random() < 0.04)) { + pixel.rlit = true; + }; + }; + }; + }; + if(Math.random() < 0.05) { + if(pixel.rlit == true) { + var randomRadiationOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var radiationX = pixel.x + randomRadiationOffset[0]; + var radiationY = pixel.y + randomRadiationOffset[1]; + if(isEmpty(radiationX,radiationY,false)) { + createPixel("radiation",radiationX,radiationY); + }; + }; + }; + if(pixel.rlit == true && Math.random() < 0.02) { + pixel.rlit = false; + }; + }, + category: "gases", + density: 0.97, + state: "gas", + }; + //SOME METALS AND OTHER ASSORTED SUBSTANCES ## + elements.iron.hardness = 0.74 + //https://www.engineeringtoolbox.com/bhn-brinell-hardness-number-d_1365.html + //https://en.wikipedia.org/wiki/Hardnesses_of_the_elements_(data_page) + //"Annealed chissel steel" hardness and then divided by iron hardness (Brinell) + //sqrt()ed like IACS-derived conductivities and scaled to the 0.8 hardness of steel + //and because 1 means infinite hardness, the others are derived using + //1-(0.26/(otherThingBHN/200)) + //it doesn't matter much anyway but I'd like to have some semblance/veneer of accuracy + //Then I nerfed and buffed some of them with inconsistent rounding. + elements.chromium = { + color: ["#c8cccb", "#dce3e0", "#ebedeb"], + behavior: behaviors.WALL, + reactions: { + }, + tempHigh: 1907, + category: "solids", + density: 7190, + conduct: 0.35, + hardness: 0.985, + state: "solid", + }; + //Makes thinner nichrome wires get hotter + nichromeDoNeighborCount = true; + function nichromeNeighborLogic(count) { + if(count < 3) { return 2.5 }; + return count == 3 ? 1.25 : 0; + }; + elements.nichrome = { + color: ["#d1cfcb", "#dbd7ce", "#e8e2d5"], + behavior: behaviors.WALL, + tempHigh: 1400, + category: "solids", + density: 8300, + conduct: 0.75, + hardness: 0.7, //??? + state: "solid", + tick: function(pixel) { + if(nichromeDoNeighborCount) { + var neighbors = 0; + for(i = 0; i < adjacentCoords.length; i++) { + if(!isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { + var newPixel = pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]]; + if(elements[newPixel.element].conduct) { neighbors++ }; + }; + }; + }; + if(pixel.charge) { + pixel.temp += ((1.1 + nichromeNeighborLogic(neighbors)) * pixel.charge); + }; + }, + }; + elements.molten_chromium = { + density: 6300, + temp: 2000, + reactions: { //(test.hello ??= {}).world + molten_nichrome: { elem1: "molten_nichrome", elem2: "molten_chromium", chance: 0.4, changeTemp: false, oneway: true } + }, + }; + elements.molten_nichrome = { + reactions: { //(test.hello ??= {}).world + molten_nickel: { elem1: "molten_nickel", elem2: "molten_nichrome", chance: 0.4, changeTemp: false, oneway: true }, + molten_haseulite: { elem2: "molten_hanichrite", elem1: ["molten_nichrome","molten_nichrome","molten_nichrome","molten_nichrome","molten_nichrome","molten_nichrome","molten_nichrome","molten_nichrome","molten_nichrome","molten_hanichrite"], changeTemp: false } + }, + tick: function(pixel) { + if(nichromeDoNeighborCount) { + var neighbors = 0; + for(i = 0; i < adjacentCoords.length; i++) { + if(!isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { + var newPixel = pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]]; + if(elements[newPixel.element].conduct) { neighbors++ }; + }; + }; + }; + if(pixel.charge) { + pixel.temp += ((1.1 + nichromeNeighborLogic(neighbors)) * pixel.charge) * 1.1; + }; + }, + }; + elements.gold.reactions ??= {}; + elements.molten_gold.reactions.molten_nickel = { + elem1: "molten_white_gold", + elem2: new Array(9).fill("molten_nickel").concat("molten_white_gold"), + changeTemp: false + }; + elements.molten_copper.reactions.molten_gold = { + elem1: ["molten_copper","molten_copper","molten_rose_gold"], + elem2: "molten_rose_gold", + changeTemp: false + }; + elements.white_gold = { + color: ["#c2c2c2","#9e9e9e","#e8e8e8"], + behavior: behaviors.WALL, + tempHigh: 937, + category: "solids", + density: 15900, + conduct: 0.83, //Has never been measured x>:( + hardness: 0.48, + }; + elements.rose_gold.color = ["#f58eb1","#d06c7d","#f58eb1"]; + elements.molten_copper.reactions.molten_rose_gold = { + elem1: ["molten_copper","molten_red_gold"], + elem2: "molten_red_gold", + changeTemp: false + }; + elements.red_gold = { + color: ["#d97b6a","#c95c49","#d97b6a"], + behavior: behaviors.WALL, + tempHigh: 975, //https://www.researchgate.net/figure/Gold-copper-phase-diagram-with-melting-points-of-gold-and-copper-adapted-from-AMS_fig51_233765846 + category: "solids", + density: 12220, //https://www.handymath.com/cgi-bin/density.cgi?naym1=Gold&weight1=10&den1=19.3&naym2=Copper&weight2=10&den2=8.94&aloynaym=Red+Gold&submit=Calculate&numnum=2&moreless=1&decimal=5 + conduct: 0.85, //Has never been measured x>:( + hardness: 0.5, //??? + }; + worldgentypes.test = { + layers: [[0.3, "pointer"], [0, "molten_nickel"]], + temperature: 2000 + }; + runAfterAutogen(function() { + if(!elements.molten_nickel.reactions) { + elements.molten_nickel.reactions = {}; + }; + elements.molten_nickel.reactions.molten_chromium = { elem1: "molten_nichrome", elem2: ["molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_nichrome"], changeTemp: false }; + }); + //Copper exists + elements.ruthenium = { + color: ["#e8ebca","#eaebd5"], //color pulled from my ass because I don't want another gray metal + behavior: behaviors.WALL, + tempHigh: 2334, + category: "solids", + state: "solid", + density: 12450, + conduct: 0.45, + hardness: 0.97, + }, + elements.molten_ruthenium = { + density: 10650, + tempHigh: 4150 + }, + elements.rhodium = { + color: ["#f0e4df","#f7eae4"], //it looked slightly reddish on Wikipedia + behavior: behaviors.WALL, + tempHigh: 1964, + category: "solids", + state: "solid", + density: 12410, + conduct: 0.59, + hardness: 0.95, + }, + elements.molten_rhodium = { + density: 10700, + tempHigh: 3695 + }, + elements.palladium = { + color: ["#fff8ed","#f5e6ce","#faeccf"], //Terraria reference + behavior: behaviors.WALL, + tempHigh: 1555, + category: "solids", + state: "solid", + density: 12023, + conduct: 0.38, + hardness: 0.83, + }, + elements.molten_palladium = { + density: 10380, + tempHigh: 2963 + }, + //Silver exists + elements.rhenium = { + color: ["#e5f0d1","#e6edda"], //it looks like almost every other metal but in some pictures the lighting makes it look ever-so-slightly greenish + behavior: behaviors.WALL, + tempHigh: 3186, + category: "solids", + state: "solid", + density: 21020, + conduct: 0.29, + hardness: 0.96, + }, + elements.molten_rhenium = { + density: 18900, + tempHigh: 5630 + }, + elements.osmium = { + color: ["#d8e1eb","#cee1f0"], //it looks bluish + behavior: behaviors.WALL, + tempHigh: 3033, + category: "solids", + state: "solid", + density: 22590, + conduct: 0.40, + hardness: 0.98, + }, + elements.molten_osmium = { + density: 2e4, + tempHigh: 5008 + }, + elements.iridium = { + color: ["#dfb9f0","#d6a9eb","#dfd1ed","#eeeeee"], //Minecraft and Stardew Valley reference + behavior: behaviors.WALL, + tempHigh: 2446, + category: "solids", + state: "solid", + density: 22560, + conduct: 0.54, + hardness: 0.97, + }, + elements.molten_iridium = { + density: 19000, + tempHigh: 4130 + }, + elements.platinum = { + color: ["#dddddd","#d7d7d7"], + behavior: behaviors.WALL, + tempHigh: 1768, + category: "solids", + state: "solid", + density: 21450, + conduct: 0.38, + hardness: 0.83226, + }, + elements.molten_platinum = { + density: 19770, + tempHigh: 3825 + }, + //Gold exists + elements.molten_gold ??= {}; + elements.molten_gold.density = 17310; + elements.molten_gold.tempHigh = 2970; + + elements.mercury = { + color: ["#d1d1d1", "#bababa"], + behavior: behaviors.LIQUID, + tempHigh: 357, + stateHigh: "mercury_gas", + tempLow: -39, + stateLow: "frozen_mercury", + state: "solid", + category: "liquids", + density: 13534, + conduct: 0.13, + breakInto: "mercury_gas", + }, + elements.frozen_mercury = { + color: ["#d1d1d1", "#bababa"], + density: 14184, + behavior: behaviors.WALL, + conduct: 0.13, + tempHigh: -39, + temp: -50, + stateHigh: "mercury", + category: "solids", + state: "solid", + state: "solid", + hidden: true, + hardness: 0.2775, //(desperately scaled Mohs hardness) + }, + elements.mercury_gas = { //hg d@bp extrapolated from density change with temperature: 12743 + density: 8.477, + color: ["#d1d1d1", "#bababa"], + colorOn: ["#96ffbf", "#9cffc2", "#9effe7"], + conduct: 0.13, + behavior: behaviors.GAS, + tempLow: 357, + temp: 400, + stateLow: "mercury", + category: "gases", + state: "gas", + hidden: true, + } + var mooreNeighborhood = [[-1,-1],[0,-1],[1,1],[-1,0],[1,0],[-1,1],[0,1],[1,1]]; + var bismuthCrystalColorArray = [ + "#f58887", + "#fcd19a", + "#fcf588", + "#aef29d", + "#9af5e4", + "#b3bef5", + "#dbb9f0", + "#f2acdb" + ] + var bismuthCrystalElements = ["amba_bismuth","amba_molten_bismuth"]; + quadriCoords = [[-1,1],[0,1],[1,1],[1,0]]; + //i'm not replacing pixelTick for this shit + /*function mooreDoHeat(pixel) { + // Check right and bottom adjacent pixels + for (var i = 0; i < quadriCoords.length; i++) { + var x = pixel.x+quadriCoords[i][0]; + var y = pixel.y+quadriCoords[i][1]; + if (!isEmpty(x,y,true)) { + var newPixel = pixelMap[x][y]; + // Skip if both temperatures are the same + if (pixel.temp == newPixel.temp || elements[newPixel.element].insulate == true) { + continue; + } + // Set both pixel temperatures to their average + var avg = (pixel.temp + newPixel.temp)/2; + pixel.temp = avg; + newPixel.temp = avg; + pixelTempCheck(pixel); + pixelTempCheck(newPixel); + }; + }; + };*/ + function bismuthCrystallization(pixel) { + if(pixel.temp < elements.amba_bismuth.tempHigh) { //initial crystal on cool + //pixel.color = "rgb(255,0,0)"; + //initialize CCC + pixel.crystalColorCounter ??= Math.floor(Math.random() * 8); //initialize CCC + //pixel.crystalColorCounter ??= 0; + if(pixel.element !== "amba_bismuth") { + pixel.temp -= 0.05; //incentivize cooling + pixel.element = "amba_bismuth" + //console.log(`pixel (${pixel.x},${pixel.y}) frozen by bismuthCrystallization`) + pixel.color = pixelColorPick(pixel,bismuthCrystalColorArray[pixel.crystalColorCounter % 8]); + }; //solidify + }; + if(pixel.crystalColorCounter !== undefined) { + for(i = 0; i < mooreNeighborhood.length; i++) { + var newX = pixel.x + mooreNeighborhood[i][0]; + var newY = pixel.y + mooreNeighborhood[i][1]; + if(isEmpty(newX,newY,true)) { + continue; + } else { + var newPixel = pixelMap[newX][newY]; + if(bismuthCrystalElements.includes(newPixel.element)) { + if(newPixel.temp < elements.amba_bismuth.tempHigh) { + newPixel.temp -= 0.05; + newPixel.element = "amba_bismuth"; + newPixel.crystalColorCounter = (pixel.crystalColorCounter + 1) % 8; + newPixel.color = pixelColorPick(pixel,bismuthCrystalColorArray[pixel.crystalColorCounter % 8]); + }; + }; + }; + }; + }; + //mooreDoHeat(pixel); + }; + elements.amba_molten_bismuth = { + color: "#d1c6b0", //not really hot enough to be red + behavior: behaviors.LIQUID, + tempLow: -Infinity, //suppress normal freezing mechanism + stateLow: "amba_molten_bismuth", + tick: function(pixel) { + bismuthCrystallization(pixel); + }, + density: 10050, + state: "liquid", + category: "liquids", + temp: 300, + tempHigh: 1560, + fireColor: "#4275db", + }; + runAfterAutogen(function() { + delete elements.amba_molten_bismuth.tempLow; + delete elements.amba_molten_bismuth.stateLow; + }); + elements.amba_bismuth = { + color: "#d1c6b0", + behavior: behaviors.WALL, + /*reactions: { + },*/ + tempHigh: 271.5, + category: "solids", + density: 9780, + conduct: 0.12, + hardness: 0.22, + state: "solid", + fireColor: "#4275db", + }; + elements.amba_bismuth_gas = { + density: 9, //made-up number + fireColor: "#4275db", + }; + zirconoids = ["zirconium","molten_zirconium","zirconium_gas"]; + function zirconiumMoveContainedNeutron(pixelFrom,pixelTo) { + if(!pixelFrom || !pixelTo) { + return false + }; + pixelFrom.neutrons ??= 0; + if(pixelFrom.neutrons < 1) { + return false; + }; + pixelTo.neutrons ??= 0; + pixelFrom.neutrons--; + pixelTo.neutrons++; + }; + function neutronAbsorbency(pixel,otherPixel) { + if(isNaN(pixel.neutrons)) { + pixel.neutrons = 0; + }; + pixel.neutrons ??= 0; //probably redundant with the above + if(!otherPixel) { + return null; + }; + if(otherPixel.element === "neutron") { + /*var otherIndex = currentPixels.indexOf(otherPixel); + if(otherIndex !== -1) { currentPixels.splice(otherIndex,1) }; + pixelMap[otherPixel.x][otherPixel.y] = undefined;*/ + deletePixel(otherPixel.x,otherPixel.y); + pixel.neutrons++; + return true; + } else { + return false; + }; + }; + function neutronMovement(pixel,whitelist=null) { + if(!pixel.oldColor) { + pixel.oldColor = pixel.color; + }; + if(isNaN(pixel.neutrons)) { + pixel.neutrons = 0; + }; + pixel.neutrons ??= 0; //probably redundant with the above + if(pixel.oldColor === null) { pixel.oldColor = pixel.color }; + var color = convertColorFormats(pixel.oldColor,"json"); + //color.g += (pixel.neutrons * 4); + //color.b += (pixel.neutrons * 6); + color.g += (pixel.neutrons * 32); + color.b += (pixel.neutrons * 48); + color = convertColorFormats(color,"rgb"); + pixel.color = color; + for(i = 0; i < pixel.neutrons; i++) { + if(pixel.neutrons < 1) { break }; + var vx = Math.floor(Math.random() * 3) - 1; + var vy = Math.floor(Math.random() * 3) - 1; + if (vx===0 && vy===0) { + if (Math.random() < 0.5) { vx = Math.random() < 0.5 ? 1 : -1; } + else { vy = Math.random() < 0.5 ? 1 : -1; } + }; + var newPos = {x: pixel.x+vx, y: pixel.y+vy}; + if(outOfBounds(newPos.x,newPos.y)) { + continue; + }; + if(isEmpty(newPos.x,newPos.y,false)) { + createPixelReturn("neutron",newPos.x,newPos.y).temp = pixel.temp; + pixel.neutrons--; + if(pixel.neutrons < 1) { break }; + } else if(!isEmpty(newPos.x,newPos.y,true)) { + var newPixel = pixelMap[newPos.x][newPos.y]; + if(whitelist == null || whitelist.includes(newPixel.element)) { + zirconiumMoveContainedNeutron(pixel,newPixel); + if(pixel.neutrons < 1) { break }; + }; + }; + }; + }; + elements.neutron = { + color: "#a6ffff", + behavior: [ + "XX|XX|XX", + "XX|CH:proton%0.25 AND DL%0.25|XX", //"b nnnnnnnnn" - the dog stepping on the keyboard + "XX|XX|XX" + ], + tick: behaviors.BOUNCY, + rotatable: true, + reactions: { + uranium: { + temp2: 100 + }, + plant: { + elem2: "wood", + chance: 0.05 + }, + gunpowder: { + elem2: "dust", + chance: 0.05 + }, + yeast: { + elem2: "bread", + chance: 0.05 + }, + silver: { + elem1: null, + chance: 0.25 + }, + firework: { + chance: 0.01, + func: function(pixel1,pixel2) { + pixel2.burning=true; + pixel2.burnStart=pixelTicks + } + }, + hydrogen: { + elem1: null, + elem2: "deuterium", + chance: 0.02 //nerfs not in original NM + }, + deuterium: { + elem1: null, + elem2: "tritium", + chance: 0.01 + }, + heavy_water: { + elem1: null, + elem2: "heavy_water" + }, + heavy_steam: { + elem1: null, + elem2: "heavy_steam" + }, + heavy_ice: { + elem1: null, + elem2: "heavy_ice" + }, + heavy_snow: { + elem1: null, + elem2: "heavy_snow" + }, + plutonium: { + temp2: 100 + }, + molten_plutonium: { + temp2: 100 + } + }, + temp: 35, + category: "energy", + state: "gas", + density: 0.00003, + ignoreAir: true, + nellfireImmune: true + }; + + elements.zirconium = { + color: ["#ccc59b", "#dbd3a4"], + behavior: behaviors.WALL, + properties: { + oldColor: null + }, + onTryMoveInto: function(pixel,otherPixel) { + neutronAbsorbency(pixel,otherPixel); + }, + tick: function(pixel) { + neutronMovement(pixel,zirconoids); + }, + tempHigh: 1855, + category: "solids", + density: 6520, + conduct: 0.19, + hardness: 0.5, + }, + elements.molten_zirconium = { + density: 5803, + tempHigh: 4409, + behavior: behaviors.MOLTEN, + onTryMoveInto: function(pixel,otherPixel) { + neutronAbsorbency(pixel,otherPixel); + }, + tick: function(pixel) { + neutronMovement(pixel,zirconoids); + }, + }; + elements.zirconium_gas = { + density: 3, //Unknown/Unmeasured value + behavior: behaviors.GAS, + onTryMoveInto: function(pixel,otherPixel) { + neutronAbsorbency(pixel,otherPixel); + }, + tick: function(pixel) { + neutronMovement(pixel,zirconoids); + }, + }; + elements.neutron.state = "gas"; + elements.neutron.ignoreAir = "true"; + neighbors = [[-1,0],[0,-1],[1,0],[0,1]] + function tryTarnish(pixel,element,chance) { + if(exposedToAir(pixel)) { + if(Array.isArray(element)) { + if(Math.random() < chance) { + changePixel(pixel,randomChoice(element)) + } + } else { + if(Math.random() < chance) { + changePixel(pixel,element) + } + } + } + } + //Non-element: Liquid ammonia + elements.liquid_ammonia = { + color: "#bab6a9", + behavior: behaviors.LIQUID, + reactions: { + "methane": { "elem1":null, "elem2":"cyanide", "chance":0.25 }, + "plant": { "elem1":"plant", "chance":0.05 }, + "wheat_seed": { "elem1":"wheat", "chance":0.05 }, + "grass": { "elem1":"grass", "chance":0.05 }, + "grass_seed": { "elem1":"grass", "chance":0.05 }, + "bamboo_plant": { "elem1":"bamboo", "chance":0.05 }, + "flower_seed": { "elem1":"flower_seed", "chance":0.05 }, + "petal": { "elem1":"flower_seed", "chance":0.05 }, + "vine": { "elem1":"vine", "chance":0.05 }, + "sapling": { "elem1":"tree_branch", "chance":0.05 }, + "tree_branch": { "elem1":"tree_branch", "chance":0.05 }, + "corn_seed": { "elem1":"corn", "chance":0.05 }, + "root": { "elem1":"root", "chance":0.05 }, + "dirt": { "elem1":"grass", "chance":0.05 }, + "mud": { "elem1":"grass", "chance":0.05 }, + "potato_seed": { "elem1":"potato", "chance":0.05 }, + "yeast": { "elem1":"yeast", "chance":0.05 }, + "fish": { "elem2":"meat" }, + "frog": { "elem2":"meat" } + }, + tempHigh: -78, + stateHigh: "ammonia", + category: "liquids", + state: "liquid", + hidden: true, + density: 681.9, + } + elements.ammonia.tempLow = -78 + elements.ammonia.stateLow = "liquid_ammonia" + //Hydrogen + //Hydrogen exists, but its solid form doesn't. + elements.liquid_hydrogen.tempLow = -259.16 + elements.liquid_hydrogen.stateLow = "hydrogen_ice" + elements.hydrogen_ice = { + color: "#E6E6FF", + behavior: behaviors.WALL, + density: 76, + category: "solids", + state: "solid", + hidden: true, + tempHigh: -259, + stateHigh: "liquid_hydrogen", + } + //Lithium (incomplete/hiatus) + elements.lithium = { + color: "#b0ab9d", + behavior: behaviors.WALL, + tick: function(pixel) { + tryTarnish(pixel,"lithium_oxide",0.007) + if(pixel.temp >= 179) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } + }, + reactions: { + "steam": { "elem1": "hydrogen", "elem2": "lithium_hydroxide" }, + "water": { "elem1": "hydrogen", "elem2": "lithium_hydroxide" }, + "nitrogen": { "elem1": "lithium_nitride", "elem2": "lithium_nitride" }, + "liquid_nitrogen": { "elem1": "lithium_nitride", "elem2": "lithium_nitride" }, + "liquid_hydrogen": { "elem1": "lithium_hydride", "elem2": "lithium_hydride" }, + "ammonia": { "elem1": ["hydrogen",null], "elem2": "lithium_amide" }, + "liquid_ammonia": { "elem1": ["hydrogen",null], "elem2": "lithium_amide" }, + }, + density: 534, + category: "solids", + state: "solid", + conduct: 0.42697, + hardness: 0.019, + tempHigh: 180, + burn: 20, + burnTime: 130, + burnInto: "lithium_oxide", + fireColor: "#fc0a22", + } + elements.molten_lithium = { //too damn reactive + color: "#b0ab9d", + behavior: [ + "XX|HT:1%1|XX", + "M2 AND HT:0.1%1|HT:1%1|M2 AND HT:1%1", + "M1|M1 AND HT:1%1|M1" + ], + tick: function(pixel) { + tryTarnish(pixel,"lithium_oxide",0.014) + }, + reactions: { + "steam": { "elem1": "hydrogen", "elem2": "lithium_hydroxide" }, + "water": { "elem1": "hydrogen", "elem2": "lithium_hydroxide" }, + "nitrogen": { "elem1": "lithium_nitride", "elem2": "lithium_nitride" }, + "liquid_nitrogen": { "elem1": "lithium_nitride", "elem2": "lithium_nitride" }, + "hydrogen": { "elem1": "lithium_hydride", "elem2": "lithium_hydride" }, + "liquid_hydrogen": { "elem1": "lithium_hydride", "elem2": "lithium_hydride" }, + "ammonia": { "elem1": ["hydrogen",null], "elem2": "lithium_amide" }, + "liquid_ammonia": { "elem1": ["hydrogen",null], "elem2": "lithium_amide" }, + }, + burning: true, + burnInto: "lithium_oxide", + fireColor: "#fc0a22", + density: 512, + } + elements.lithium_oxide = { + color: "#eee9ec", //HRT UV-to-visible strategy again + behavior: behaviors.POWDER, + reactions: { + "steam": { "elem1": "lithium_hydroxide", "elem2": "lithium_hydroxide", chance: 0.03 }, + "water": { "elem1": "lithium_hydroxide", "elem2": "lithium_hydroxide", chance: 0.03 }, + "carbon_dioxide": { "elem1": null, "elem2": "lithium_carbonate" } + }, + density: 2013, + category: "powders", + state: "solid", + hidden: true, + tempHigh: 1438, + } + elements.lithium_hydroxide = { + color: "#eeeeee", + behavior: behaviors.POWDER, + reactions: { + "steam": { "elem1": null, "elem2": "lithium_hydroxide_monohydrate" }, + "water": { "elem1": null, "elem2": "lithium_hydroxide_monohydrate" }, + "carbon_dioxide": { "elem1": "water", "elem2": [null,"lithium_carbonate"], chance: 0.5 }, + }, + density: 1460, + category: "powders", + state: "solid", + hidden: true, + tempHigh: 462, + } + elements.lithium_hydroxide_monohydrate = { + color: "#e0e4e7", + behavior: behaviors.POWDER, + reactions: { + "carbon_dioxide": { "elem1": "water", "elem2": [null,"lithium_carbonate"], chance: 0.5 }, + }, + tick: function(pixel) { + emptyNeighborArray = [] + for(i=0;i<4;i++) { + if(isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],false)) { + emptyNeighborArray.push(adjacentCoords[i]) + } + } + if(pixel.temp >= 100) { + if(emptyNeighborArray.length > 0) { + var placement = randomChoice(emptyNeighborArray) + if(isEmpty(pixel.x+placement[0],pixel.y+placement[1])) { + createPixel("steam",pixel.x+placement[0],pixel.y+placement[1]) + changePixel(pixel,"lithium_hydroxide") + } + } + } + }, + density: 1510, + category: "powders", + state: "solid", + hidden: true, + } + elements.lithium_carbonate = { //todo + color: "#eeeeee", + behavior: behaviors.POWDER, + density: 2110, + category: "powders", + state: "solid", + hidden: true, + tempHigh: 723, + } + elements.lithium_nitride = { + color: "#eeeeee", + behavior: behaviors.POWDER, + reactions: { + "steam": { "elem1": "lithium_hydroxide", "elem2": "ammonia" }, + "water": { "elem1": "lithium_hydroxide", "elem2": "ammonia" }, + "hydrogen": { "elem1": "lithium_hydride", "elem2": "lithium_amide" }, + "liquid_hydrogen": { "elem1": "lithium_hydride", "elem2": "lithium_amide" }, + }, + density: 1270, + category: "powders", + state: "solid", + hidden: true, + tempHigh: 813, + } + elements.lithium_hydride = { + color: "#eeeeee", + behavior: behaviors.POWDER, + reactions: { //the acid part of lye + "ammonia": { "elem1": "hydrogen", "elem2": "lithium_amide" }, + "liquid_ammonia": { "elem1": "hydrogen", "elem2": "lithium_amide" } + }, + density: 780, + category: "powders", + state: "solid", + hidden: true, + tempHigh: 689, + } + elements.lithium_amide = { + color: "#eeeeee", + behavior: behaviors.POWDER, + reactions: { + "steam": { "elem1": "lithium_hydroxide", "elem2": "ammonia" }, + "water": { "elem1": "lithium_hydroxide", "elem2": "ammonia" } + }, + density: 1178, + category: "powders", + state: "solid", + hidden: true, + tempHigh: 375, + } + //Sodium exists + //... + //at request of Serioustar#1337 + elements.niobium = { + color: ["#dedede","#edead8","#e8e9ed"], + behavior: behaviors.WALL, + tempHigh: 2477, + category: "solids", + density: 8570, + conduct: 0.35, + hardness: 0.7, //idk lol + }; + //Random bullshit go + elements.kurshunjukium = { + color: ["#435564","#536371"], + behavior: behaviors.WALL, + tempHigh: 308, + category: "solids", + state: "solid", + density: 10304, + conduct: 0.53, + hardness: 0.12, + desc: 'Fictional
Kurshunjukium, whose name comes from the Turkish "kurşuncuk", meaning "little lead", is a blue-gray metal that is soft and dense, similarly to its namesake.' + }; + elements.molten_kurshunjukium = { + density: 7989, + tempHigh: 1411, + desc: "Fictional" + }; + elements.kurshunjukium_gas = { + density: 18 + }; + elements.kurshuth_alloy = { + color: "#7d806f", + behavior: behaviors.LIQUID, + tempHigh: 863, + stateHigh: "kurshuth_alloy_vapor", + tempLow: 14, + stateLow: "solid_kurshuth_alloy", + category: "liquids", + state: "liquid", + density: 5606, + conduct: 0.13, + hardness: 0.02, + desc: 'Fictional
Kurshuth alloy is a highly eutectic alloy composed of bismuth and kurshunjukium. It has an extremely low melting point, and isn\'t very useful.' + }; + elements.kurshuth_alloy_vapor = { + density: 23, + behavior: behaviors.GAS, + category: "states", + state: "gas", + tempLow: 863, + stateLow: "kurshuth_alloy", + hidden: true, + color: elements.magma.color.map(x => changeSaturation(changeHue(multiplyColors(x,"#DD4444","json"),10,"add","hsl_json"),0.7,"multiply","hex")), //metals.js already uses code_library + desc: "Fictional" + }; + elements.solid_kurshuth_alloy = { + color: "#5d604f", + behavior: behaviors.WALL, + tempHigh: 14, + stateHigh: "kurshuth_alloy", + category: "solids", + state: "solid", + density: 6006, + desc: "Fictional" + }; + runAfterAutogen(function() { + var kesddfroged = ["kurshunjukium","molten_kurshunjukium"]; + var kesddfroged2 = ["amba_bismuth","amba_molten_bismuth"]; + for(var i in kesddfroged) { + for(var j in kesddfroged2) { + elements[kesddfroged[i]].reactions ??= {}; elements[kesddfroged[i]].reactions[kesddfroged2[j]] = {"elem1": "kurshuth_alloy", "elem2": "kurshuth_alloy"} + } + }; + for(var i in kesddfroged2) { + for(var j in kesddfroged) { + elements[kesddfroged2[i]].reactions ??= {}; elements[kesddfroged2[i]].reactions[kesddfroged[j]] = {"elem1": "kurshuth_alloy", "elem2": "kurshuth_alloy"} + } + }; + }); + function newMetal(name,color,meltingPoint,boilingPoint,hardness,density,gasDensity,conduct,categoryOverride = null) { + var temp = { + "name": name, + "color": color, + "meltingPoint": meltingPoint, + "boilingPoint": boilingPoint, + "hardness": hardness, + "density": density, + "gasDensity": gasDensity, + "conduct": conduct + }; + var tempNulls = Object.keys(temp).filter(key => (typeof(temp[key]) == undefined || temp[key] == null)); + if(tempNulls.length > 0) { + var errorMessage = capitalizeFirstLetter(englishFormatList(tempNulls)); + throw new Error(`newMetal: ${errorMessage} {tempNulls.length == 1 ? "is" : "are"} required (generating "${name}")`); + }; + var scrapColor = gravelizeToHex(color); + elements[name] = { + "color": (window["gameLoaded"] ?? false) ? (Array.isArray(color) ? color.map(x => convertColorFormats(x,"rgb")) : convertColorFormats(color,"rgb")) : color, + "colorObject": (window["gameLoaded"] ?? false) ? (Array.isArray(color) ? color.map(x => convertColorFormats(x,"json")) : convertColorFormats(color,"json")) : undefined, + "behavior": behaviors.WALL, + "category": "solids", + "state": "solid", + "density": density, + "conduct": conduct, + "tempHigh": meltingPoint, + "breakInto": `${name}_scrap`, + "hardness": hardness + }; + if(categoryOverride) { elements[name].category = categoryOverride }; + elements[`molten_${name}`] = { + "tempHigh": boilingPoint + }; + elements[`${name}_gas`] = { + "density": gasDensity + }; + elements[`${name}_scrap`] = { + "color": (window["gameLoaded"] ?? false) ? (Array.isArray(scrapColor) ? scrapColor.map(x => convertColorFormats(x,"rgb")) : convertColorFormats(scrapColor,"rgb")) : scrapColor, + "colorObject": (window["gameLoaded"] ?? false) ? (Array.isArray(scrapColor) ? scrapColor.map(x => convertColorFormats(x,"json")) : convertColorFormats(scrapColor,"json")) : undefined, + "behavior": (window["gameLoaded"] ?? false) ? undefined : behaviors.POWDER, + "tick": (window["gameLoaded"] ?? false) ? behaviors.POWDER : undefined, + "tempHigh": meltingPoint, + "stateHigh": `molten_${name}`, + "category": "powders", + "state": "solid", + "hidden": true, + "density": density * 0.09, + "conduct": conduct * 0.4, + "movable": true, + }; + if(window["gameLoaded"] ?? false) { + delete elements[`${name}_scrap`].behavior; + createElementButton(name); + if(settings.unhide == 1) { createElementButton(`${name}_scrap`) } + autoGen(`molten_${name}`,name,"molten"); + elements[`molten_${name}`].color = elements[`molten_${name}`].color.map(x => convertColorFormats(x,"rgb")); + elements[`molten_${name}`].colorObject = elements[`molten_${name}`].color.map(x => convertColorFormats(x,"json")); + } else { + delete elements[`${name}_scrap`].tick; + }; + return [elements[name],elements[`${name}_scrap`]]; + }; + //newMetal( "exidmaden", ["#F8EDCF", "#EEAAAE", "#E5678D", "#A6659C", "#6763AD"], 2134, 6769, 0.8, 32333, 49.9, 0.88 ); + //newMetal( "jisooium", "#9d0ac2", 8367, 10003, 0.63, 15024, 12.2, 0.9 ); + //newMetal( "twicium", ["#F9C596", "#FC5D9D"], 10240, 18018, 0.88, 29029, 24.3, 0.91 ); + //ASSORTED LOONA-THEMED MATERIALS ## + haseuliteSpreadWhitelist = ["haseulite","haseulite_powder","molten_haseulite","haseulite_gas"]; + jinsouliteSpreadWhitelist = ["jinsoulite","jinsoulite_powder","molten_jinsoulite","jinsoulite_gas"]; + function coldExplosionAfterCooling(pixel,x,y,radius,fire,smoke,power,damage) { + pixel.temp -= 2*damage*radius*power; + }; + function jinsouliteRxnStealerImmutableEl2DoNotUse(pixel,newPixel,reactionTarget,ignoreSelf=true,_chanceMultMeantForJinsoulites=1) { + if(!elements[reactionTarget]) { + throw new Error(`No such element ${reactionTarget}!`); + }; + if(typeof(newPixel) === "undefined") { //timing issue? + return false; + }; + var newElement = newPixel.element; + if(ignoreSelf && newElement === pixel.element) { + return false; + }; + var newInfo = elements[newElement]; + if(typeof(newInfo.reactions) === "undefined") { + return false; + }; + if(typeof(newInfo.reactions[reactionTarget]) === "undefined") { + return false; + }; + var pixel2 = pixel; + var pixel1 = newPixel; + var r = JSON.parse(JSON.stringify(newInfo.reactions[reactionTarget])); + if (r.setting && settings[r.setting]===0) { + return false; + } + // r has the attribute "y" which is a range between two y values + // r.y example: [10,30] + // return false if y is defined and pixel1's y is not in the range + if (r.tempMin !== undefined && pixel1.temp < r.tempMin) { + return false; + } + if (r.tempMax !== undefined && pixel1.temp > r.tempMax) { + return false; + } + if (r.charged && !pixel.charge) { + return false; + } + if (r.chance !== undefined && Math.random() < (r.chance * _chanceMultMeantForJinsoulites)) { + return false; + } + if (r.y !== undefined && (pixel1.y < r.y[0] || pixel1.y > r.y[1])) { + return false; + } + if(r.elem1 !== undefined && r.elem2 !== undefined) { + if(r.elem1 !== null && r.elem2 !== null) { + r.elem1 = [r.elem1,r.elem2].flat(); + }; + }; + if (r.elem1 !== undefined) { + // if r.elem1 is an array, set elem1 to a random element from the array, otherwise set it to r.elem1 + if (Array.isArray(r.elem1)) { + var elem1 = r.elem1[Math.floor(Math.random() * r.elem1.length)]; + } else { var elem1 = r.elem1; } + if (elem1 == null) { + deletePixel(pixel1.x,pixel1.y); + } + else { + changePixel(pixel1,elem1); + } + } + if(pixel1) { + if (r.charge1) { pixel1.charge = r.charge1; } + if (r.temp1) { pixel1.temp += r.temp1; pixelTempCheck(pixel1); } + if (r.color1) { // if it's a list, use a random color from the list, else use the color1 attribute + pixel1.color = pixelColorPick(pixel1, Array.isArray(r.color1) ? r.color1[Math.floor(Math.random() * r.color1.length)] : r.color1); + } + if (r.attr1) { // add each attribute to pixel1 + for (var key in r.attr1) { + pixel1[key] = r.attr1[key]; + } + } + if (r.charge2) { pixel2.charge = r.charge2; } + if (r.temp2) { pixel2.temp += r.temp2; pixelTempCheck(pixel2); } + if (r.color2) { // if it's a list, use a random color from the list, else use the color2 attribute + pixel2.color = pixelColorPick(pixel2, Array.isArray(r.color2) ? r.color2[Math.floor(Math.random() * r.color2.length)] : r.color2); + } + if (r.attr2) { // add each attribute to pixel2 + for (var key in r.attr2) { + pixel2[key] = r.attr2[key]; + } + } + if (r.func) { r.func(pixel1,pixel2); } + } + return r.elem1!==undefined; + }; + elements.loona = { + color: ["#6f7d54","#4f5d34","#7c8a61"], + behavior: behaviors.POWDER, + tempHigh: 1031, + category: "random rocks", + state: "solid", + density: 2466.73, + hardness: 0.56, + breakInto: ["rock","sulfur","loona_gravel","loona_gravel","loona_gravel","haseulite_powder", "rock","sulfur","loona_gravel","loona_gravel","loona_gravel","jinsoulite_powder", "rock","sulfur","loona_gravel","loona_gravel","loona_gravel","heejinite_powder"], + }; + var backupCategoryWhitelist = ["land","powders","weapons","food","life","corruption","states","fey","Fantastic Creatures","dyes","energy liquids","random liquids","random gases","random rocks"]; + var backupElementWhitelist = ["mercury", "chalcopyrite_ore", "chalcopyrite_dust", "copper_concentrate", "fluxed_copper_concentrate", "unignited_pyrestone", "ignited_pyrestone", "everfire_dust", "extinguished_everfire_dust", "mistake", "polusium_oxide", "vaporized_polusium_oxide", "glowstone_dust", "redstone_dust", "soul_mud", "wet_soul_sand", "nitrogen_snow", "fusion_catalyst", "coal", "coal_coke", "blast_furnace_fuel", "molten_mythril"]; + function spoutCriteria(name) { + if(typeof(elements[name]) !== "object") { + throw new Error(`Nonexistent element ${name}`); + }; + var info = elements[name]; + //console.log(`${name} (${JSON.stringify(elements[name])})`); + if(typeof(info.state) === "undefined") { + var state = null; + } else { + var state = info.state; + }; + if(typeof(info.category) === "undefined") { + var category = "other"; + } else { + var category = info.category; + }; + if(excludedSpoutElements.includes(name)) { + return false + }; + var include = false; + if(["liquid","gas"].includes(state)) { + include = true; + }; + if(info.movable) { + include = true; + }; + if(backupCategoryWhitelist.includes(category)) { + include = true; + }; + if(backupElementWhitelist.includes(name)) { + include = true; + }; + if(category.includes("mudstone")) { + include = true; + }; + //console.log(include); + return include; + }; + function heejiniteHeatCriteria(name) { + if(typeof(elements[name]) !== "object") { + throw new Error(`Nonexistent element ${name}`); + }; + var info = elements[name]; + //console.log(`${name} (${JSON.stringify(elements[name])})`); + if(typeof(info.tempLow) === "undefined") { + return false; + }; + if(typeof(info.tempHigh) !== "undefined" && info.tempHigh < elements.heejinite.tempHigh) { + return false; + }; + return (info.tempLow < elements.heejinite.tempHigh) || ((typeof(info.state) !== "undefined") && (info.state === "gas")); + }; + spoutCriteria = function(name) { + if(typeof(elements[name]) !== "object") { + throw new Error(`Nonexistent element ${name}`); + }; + var info = elements[name]; + //console.log(`${name} (${JSON.stringify(elements[name])})`); + if(typeof(info.state) === "undefined") { + var state = null; + } else { + var state = info.state; + }; + if(typeof(info.category) === "undefined") { + var category = "other"; + } else { + var category = info.category; + }; + var include = false; + if(["liquid","gas"].includes(state)) { + include = true; + }; + if(info.movable) { + include = true; + }; + if(backupCategoryWhitelist.includes(category)) { + include = true; + }; + if(backupElementWhitelist.includes(name)) { + include = true; + }; + if(category.includes("mudstone")) { + include = true; + }; + //console.log(include); + return include; + }; + //it doesn't want to acknowledge spoutCriteria, so... + runAfterAutogen(function() { + elements.loona.stateHigh = ["molten_loona","rock","rock","rock","sulfur_gas","sulfur_gas","molten_haseulite","molten_loona","rock","rock","rock","sulfur_gas","sulfur_gas","molten_jinsoulite","molten_loona","rock","rock","rock","sulfur_gas","sulfur_gas","molten_heejinite"]; + hotHeejiniteElements = Object.keys(elements).filter(function(e) { + return spoutCriteria(e) && heejiniteHeatCriteria(e) && !elements[e].excludeRandom && !e.startsWith("rad"); + }); + }); + elements.loona_gravel = { + color: ["#b3be98","#919a6f","#68744b","#515931"], + behavior: behaviors.POWDER, + tempHigh: 1031, + stateHigh: ["molten_loona","rock","rock","rock","sulfur_gas","sulfur_gas","molten_haseulite","molten_loona","rock","rock","rock","sulfur_gas","sulfur_gas","molten_jinsoulite","molten_loona","rock","rock","rock","sulfur_gas","sulfur_gas","molten_heejinite"], + category: "random rocks", + state: "solid", + density: 1625.14, + hardness: 0.97, + breakInto: ["rock","sulfur","rock","haseulite_powder","rock","sulfur","rock","jinsoulite_powder","rock","sulfur","rock","heejinite_powder"], + }; + jinsouliteValueObject = { + cloud: 0.5, + cloud_cloud: {value: 0.5, remainder: "cloud"}, + snow_cloud: {value: 0.75}, + hail_cloud: {value: 0.75}, + steam: 1, + steam_cloud: {value: 0.5, remainder: "steam"}, + rain_cloud_cloud: {value: 0.5, remainder: "rain_cloud"}, + snow_cloud_cloud: {value: 0.5, remainder: "snow_cloud"}, + hail_cloud_cloud: {value: 0.5, remainder: "hail_cloud"}, + rain_cloud: {value: 1, remainder: "cloud"}, + water_cloud: {value: 1, remainder: "cloud"}, + snow: {value: 0.125}, + soda: {value: 0.8984375, remainder: "sugar"}, + blood: {value: 0.90625, remainder: "dna"}, + infection: {value: 0.90625, remainder: "dna"}, + packed_snow: {value: 0.90625}, + slime: {value: 0.9609375, remainder: "salt"}, //:eggTF: + slush: {value: 0.9609375}, + ice: {value: 0.98046875}, + salt_water: {value: 1, remainder: "salt"}, //should be 0.965 but simplified here + dirty_water: {value: 1, remainder: ["ash","dust","carbon_dioxide","ash","dust","carbon_dioxide","infection"]}, + sugar_water: {value: 1, remainder: "sugar"}, + seltzer: {value: 1, remainder: "carbon_dioxide"}, + pool_water: {value: 1, remainder: "chlorine"}, + water: {value: 1, tempMin: 80}, + water_bomb: 59, + water_bomb_2: 164.5, + water_bomb_3: 322.5, + water_bomb_4: 534, + water_bomb_5: 798, + water_bomb_6: 1112.5, + water_bomb_7: 1480, + water_bomb_8: 1901.5, + water_bomb_9: 2373, + water_bomb_10: 2898, //average rates from in-game simulation since I can't come up with an exponential function + water_bomb_bomb: 59*59, + water_bomb_bomb_2: 59*164.5, + water_bomb_bomb_3: 59*322.5, + water_bomb_bomb_4: 59*534, + water_bomb_bomb_5: 59*798, + water_bomb_bomb_6: 59*1112.5, + water_bomb_bomb_7: 59*1480, + water_bomb_bomb_8: 59*1901.5, + water_bomb_bomb_9: 59*2373, + water_bomb_bomb_10: 59*2898, //creates up to around 2,898 water bombs, each of which theoretically create up to around 59 water + water_bomb_bomb: 59*59, + water_bomb_2_bomb: 164.5*59, + water_bomb_3_bomb: 322.5*59, + water_bomb_4_bomb: 534*59, + water_bomb_5_bomb: 798*59, + water_bomb_6_bomb: 1112.5*59, + water_bomb_7_bomb: 1480*59, + water_bomb_8_bomb: 1901.5*59, + water_bomb_9_bomb: 2373*59, + water_bomb_10_bomb: 2898*59, //creates up to around 59 water bombs, each of which theoretically create up to around 2,898 water + water_bomb_10_bomb_10: 2898*2898, //skipping to the funny + water_bomb_cloud: 30, + }; + /*function customStaining(pixel,customColorRgb,stainOverride=null) { + if (settings["stainoff"]) { return } + var stain = (stainOverride !== null ? stainOverride : elements[pixel.element].stain); + if (stain > 0) { + var newColor = customColorRgb.match(/\d+/g); + } + else { + var newColor = null; + } + for (var i = 0; i < adjacentCoords.length; i++) { + var x = pixel.x+adjacentCoords[i][0]; + var y = pixel.y+adjacentCoords[i][1]; + if (!isEmpty(x,y,true)) { + var newPixel = pixelMap[x][y]; + if (elements[pixel.element].ignore && elements[pixel.element].ignore.indexOf(newPixel.element) !== -1) { + continue; + } + if ((elements[newPixel.element].id !== elements[pixel.element].id || elements[newPixel.element].stainSelf) && (solidStates[elements[newPixel.element].state] || elements[newPixel.element].id === elements[pixel.element].id)) { + if (Math.random() < Math.abs(stain)) { + if (stain < 0) { + if (newPixel.origColor) { + newColor = newPixel.origColor; + } + else { continue; } + } + else if (!newPixel.origColor) { + newPixel.origColor = newPixel.color.match(/\d+/g); + } + // if newPixel.color doesn't start with rgb, continue + if (!newPixel.color.match(/^rgb/)) { continue; } + // parse rgb color string of newPixel rgb(r,g,b) + var rgb = newPixel.color.match(/\d+/g); + if (elements[pixel.element].stainSelf && elements[newPixel.element].id === elements[pixel.element].id) { + // if rgb and newColor are the same, continue + if (rgb[0] === newColor[0] && rgb[1] === newColor[1] && rgb[2] === newColor[2]) { continue; } + var avg = []; + for (var j = 0; j < rgb.length; j++) { + avg[j] = Math.round((rgb[j]*(1-Math.abs(stain))) + (newColor[j]*Math.abs(stain))); + } + } + else { + // get the average of rgb and newColor, more intense as stain reaches 1 + var avg = []; + for (var j = 0; j < rgb.length; j++) { + avg[j] = Math.floor((rgb[j]*(1-Math.abs(stain))) + (newColor[j]*Math.abs(stain))); + } + } + // set newPixel color to avg + newPixel.color = "rgb("+avg.join(",")+")"; + } + } + } + } + }*/ + function valueSpreading(pixel,whitelist=null) { + var randomNeighborOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var rX = randomNeighborOffset[0]; + var rY = randomNeighborOffset[1]; + var rfX = pixel.x+rX; + var rfY = pixel.y+rY; + if(!isEmpty(rfX,rfY,true)) { + var rOtherPixel = pixelMap[rfX][rfY]; + var rOtherElement = rOtherPixel.element; + if(whitelist === null || (whitelist !== null && whitelist.includes(rOtherElement))) { + if(typeof(rOtherPixel.value) !== "number") { + rOtherPixel.value = 0; + }; + if(typeof(rOtherPixel) === "undefined" || isEmpty(rfX,rfY,true)) { + return false; + }; + var averageValue = (pixel.value + rOtherPixel.value) / 2; + pixel.value = averageValue; + rOtherPixel.value = averageValue; + }; + }; + return true; + }; + function valueAbsorbency(pixel,valueObject) { + for(i = 0; i < adjacentCoords.length; i++) { + var oX = adjacentCoords[i][0]; + var oY = adjacentCoords[i][1]; + var fX = pixel.x+oX; + var fY = pixel.y+oY; + if(!isEmpty(fX,fY,true)) { + var otherPixel = pixelMap[fX][fY]; + var otherElement = otherPixel.element; + var otherInfo = elements[otherElement]; + if(valueObject[otherElement]) { + //console.log(`${otherElement} in your area`) + if(typeof(otherPixel) === "undefined" || isEmpty(fX,fY,true)) { + console.log(`nope`) + return false; + }; + var ValueData = valueObject[otherElement]; + //console.log(ValueData.toString()) + if(typeof(ValueData) == "object") { + var tempMin = ValueData.tempMin ?? null; + if(pixel.temp < tempMin) { + continue; + }; + var finalElement = ValueData.remainder ?? null; + if(finalElement instanceof Array) { + finalElement = finalElement[Math.floor(Math.random() * finalElement.length)]; + }; + if(finalElement !== 0) { + if(finalElement !== null) { + changePixel(otherPixel,finalElement); + } else { + deletePixel(otherPixel.x,otherPixel.y); + }; + }; + pixel.value += ValueData.value; + } else if(typeof(ValueData) === "number") { + deletePixel(otherPixel.x,otherPixel.y); + pixel.value += ValueData; + }; + }; + }; + }; + return true; + }; + function valueFunction(pixel,valueObject,elementWhitelist=null) { + if(typeof(pixel.value) === "undefined") { + pixel.value = 0; + }; + var oldValue = pixel.value; + if(!valueAbsorbency(pixel,valueObject) || isNaN(pixel.value)) { + pixel.value = oldValue; + }; + var oldValue = pixel.value; + if(!valueSpreading(pixel,elementWhitelist) || isNaN(pixel.value)) { + pixel.value = oldValue; + }; + } + //this important thing somehow disappeared + haseuliteValueObject = { + light: 1, + radiation: 4, + fire: {value: 6, remainder: "smoke"}, + rad_fire: {value: 10, remainder: "rad_smoke"}, + liquid_fire: {value: 12, remainder: ["fire","liquid_smoke","smoke"]}, + plasma: {value: 15, remainder: "fire"}, + holy_fire: 25, + god_slayer_fire: 40, + liquid_rad_fire: {value: 20, remainder: [null,"rad_fire","rad_fire","rad_smoke","rad_smoke"]}, + liquid_plasma: {value: 30, remainder: ["plasma","liquid_fire","fire"]}, + liquid_holy_fire: {value: 50, remainder: ["holy_fire","bless","light"]}, + liquid_god_slayer_fire: {value: 80, remainder: ["god_slayer_fire",null,null]}, + liquid_irradium: {value: 4, remainder: 0} + }; + function haseulitoidTick(pixel) { + if(pixel.value == undefined) { pixel.value = 0 }; + valueFunction(pixel,haseuliteValueObject,haseuliteSpreadWhitelist); + if(pixel.radioactive && Math.random() < 0.05) { pixel.value += 4 }; + if(pixel.oldColor === undefined) { pixel.oldColor = pixelColorPick(pixel) }; + if(pixel.oldColor === null) { pixel.oldColor = pixel.color }; + if(isNaN(pixel.value)) { pixel.value = 0 }; + pixel.color = lightenColor(pixel.oldColor,pixel.value / 3); + var mVal = elements[pixel.element].haseulitoidMaxValue ?? 350; + if(pixel.value >= mVal) { + var coldBoomChance = Math.max(0.008 * ((pixel.value - mVal) / (mVal * 2/7)), 0.001); + if(Math.random() < coldBoomChance) { + var coldBoomRadius = Math.min(40,Math.floor(7 + ((pixel.value - mVal) / (mVal * 2/7)))); + explodeAtPlus(pixel.x,pixel.y,coldBoomRadius,"cold_fire","cold_smoke",null,coldExplosionAfterCooling); + }; + }; + } + elements.haseulite = { + color: ["#3cb00e", "#25d119", "#79f553"], + fireColor: ["#08a953", "#2ea332", "#d1e0d3"], + properties: { + oldColor: null + }, + behavior: behaviors.WALL, + tick: function(pixel) { haseulitoidTick(pixel) }, + excludeVelocity: true, //wall shouldn't move + tempHigh: 1757, + onExplosionBreakOrSurvive: function(pixel,x,y,radius) { + /*power is always radius/10 + r 5: value 7 + r 10: value 14 + r 15: value 28 + r 20: value 56 + r 25: value 112 + r 30: value 224 + */ + pixel.value += (2**(((radius) / 5) - 1) * 7); + }, + category: "solids", + state: "solid", + density: 7550, + hardness: 0.93, + breakInto: "haseulite_powder", + conduct: 0.84, + }; + if(!elements.steel.reactions) { + elements.steel.reactions = {}; + }; + elements.steel.reactions.haseulite_powder = { + elem1: "haseulite_vent", + elem2: null, + chance: 0.01, + tempMin: 1200, + }; + adjacentCoordsInverted = [[0,-1],[0,1],[-1,0],[1,0]]; + elements.haseulite_vent = { + color: "#88b058", + fireColor: ["#08a953", "#2ea332", "#d1e0d3"], + behavior: behaviors.WALL, + rotatable: true, + desc: "This uses rotation, so just use debug to see the r value. r 0 means it vents haseulite below it upwards, r 1 means it vents haseulite above it downwards, r 2 means it vents left, and r 3 means it vents right.", + tick: function(pixel) { + if(isNaN(pixel.r)) { + pixel.r = 0; + }; + pixel.r = pixel.r % 4; + var coord = adjacentCoords[pixel.r]; + var invertCoord = adjacentCoordsInverted[pixel.r]; + var fX = pixel.x+coord[0]; + var fY = pixel.y+coord[1]; + if(!isEmpty(fX,fY,true)) { + var otherPixel = pixelMap[fX][fY]; + var otherElement = otherPixel.element; + var otherInfo = elements[otherElement]; + if(typeof(otherPixel) === "undefined" || isEmpty(fX,fY,true)) { + return false; + }; + if(haseuliteSpreadWhitelist.includes(otherElement)) { + var ventLimit = Math.min(10,Math.floor(1 + (Math.sqrt(Math.max(otherPixel.value,1)) / 2))); + for(i = 1; i <= ventLimit; i++) { + if(otherPixel.value >= 3) { + var fIX = pixel.x+(invertCoord[0] * i); + var fIY = pixel.y+(invertCoord[1] * i); + if(isEmpty(fIX,fIY,false)) { + var newCF = createPixelReturn("cold_fire",fIX,fIY); + newCF.temp -= (otherPixel.value / 4); + otherPixel.value -= 3; + } else { //if the pixel to place isn't empty + if(!outOfBounds(fIX,fIY)) { //if it isn't OoB + if(pixelMap[fIX][fIY].element !== "cold_fire") { //if it isn't cold fire + break; + }; + } else { //if it is OoB + break; + }; + }; + } else { + break; + }; + }; + }; + }; + return true; + }, + excludeVelocity: true, //wall shouldn't move + tempHigh: elements.steel.tempHigh, + stateHigh: ["molten_steel","haseulite_powder"], + breakInto: ["metal_scrap","haseulite_powder"], + category: "machines", + state: "solid", + density: 7550, + hardness: 0.93, + breakInto: "haseulite_powder", + conduct: 0.84, + } + elements.haseulite_powder = { + color: ["#5fb33e", "#32ba29", "#63d141"], + properties: { + oldColor: null + }, + category: "powders", + fireColor: ["#08a953", "#2ea332", "#d1e0d3"], + tempHigh: 1757, + behavior: behaviors.POWDER, + tick: function(pixel) { haseulitoidTick(pixel) }, + onExplosionBreakOrSurvive: function(pixel,x,y,radius) { + /*power is always radius/10 + r 5: value 7 + r 10: value 14 + r 15: value 28 + r 20: value 56 + r 25: value 112 + r 30: value 224 + */ + pixel.value += (2**(((radius) / 5) - 1) * 7); + }, + stateHigh: "molten_haseulite", + category: "powders", + state: "solid", + hidden: true, + density: 4512, + hardness: 0.7, + conduct: 0.43, + }; + elements.molten_haseulite = { + color: ["#cbf569","#f1ffd6","#fdffb5", "#fffa99"], + fireColor: ["#08a953", "#2ea332", "#d1e0d3"], + properties: { + oldColor: null + }, + behavior: behaviors.LIQUID, //fire creation is problematic due to smoke cooling + tick: function(pixel) { haseulitoidTick(pixel) }, + onExplosionBreakOrSurvive: function(pixel,x,y,radius) { + /*power is always radius/10 + r 5: value 7 + r 10: value 14 + r 15: value 28 + r 20: value 56 + r 25: value 112 + r 30: value 224 + */ + pixel.value += (2**(((radius) / 5) - 1) * 7); + }, + density: 7214, + hardness: 0.52, + breakInto: "haseulite_gas", + temp: 1957, + tempHigh: 3100, + conduct: 0.23, + }; + elements.haseulite_gas = { + color: ["#ffff9d", _cc.w.h, "#e9ffe6", "#ffffe5"], + fireColor: ["#08a953", "#2ea332", "#d1e0d3"], + properties: { + oldColor: null + }, + tick: function(pixel) { haseulitoidTick(pixel) }, + onExplosionBreakOrSurvive: function(pixel,x,y,radius) { + /*power is always radius/10 + r 5: value 7 + r 10: value 14 + r 15: value 28 + r 20: value 56 + r 25: value 112 + r 30: value 224 + */ + pixel.value += (2**(((radius) / 5) - 1) * 7); + }, + density: 0.289, + temp: 3700, + hardness: 1, + conduct: 0.13, + }; + elements.hanichrite = { //the names nickel, chrome, and haseulite do not mix + color: ["#dde6bc", "#ebf2ef", "#e8fab1"], + behavior: behaviors.WALL, + tempHigh: 1560, + category: "solids", + density: 8218, + conduct: 0.75, + hardness: 0.78, + state: "solid", + tick: function(pixel) { + if(nichromeDoNeighborCount) { + var neighbors = 0; + for(i = 0; i < adjacentCoords.length; i++) { + if(!isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { + var newPixel = pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]]; + if(elements[newPixel.element].conduct) { neighbors++ }; + }; + }; + }; + if(pixel.charge) { + pixel.temp -= ((1.13 + nichromeNeighborLogic(neighbors)) * pixel.charge); + }; + }, + }; + elements.molten_hanichrite = { + tick: function(pixel) { + if(nichromeDoNeighborCount) { + var neighbors = 0; + for(i = 0; i < adjacentCoords.length; i++) { + if(!isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { + var newPixel = pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]]; + if(elements[newPixel.element].conduct) { neighbors++ }; + }; + }; + }; + if(pixel.charge) { + pixel.temp -= ((1.13 + nichromeNeighborLogic(neighbors)) * pixel.charge) * 1.09; + }; + }, + }; + /* + var shimmeringColor = convertHslObjects(hslColorStringToObject(`hsl(${(pixelTicks / 2) % 360},100%,50%)`,"rgb")); + customStaining(pixel,shimmeringColor,0.2); + */ + function heejinitoidTick(pixel) { + pixel.color ??= pixelColorPick(pixel); + if(pixel.oldColor === null) { pixel.oldColor = pixel.color }; + if(pixel.oldColor === undefined) { pixel.oldColor = pixelColorPick(pixel) }; + var color = rgbStringToHSL((convertColorFormats(pixel.oldColor,"rgb") ?? pixelColorPick(pixel)),"json"); + var heejiniteHueSpread = 30 + (pixel.temp/9.25) + var hueOffset = (Math.sin(pixelTicks / 11) * heejiniteHueSpread) + 15; color.h += hueOffset; + var color = convertHslObjects(color,"rgb"); + pixel.color = color; + }; + function hotHeejinitoidTick(pixel) { + if(pixel.oldColor === undefined) { pixel.oldColor = pixelColorPick(pixel) }; + if(Math.random() < (pixel.temp >= 1500 ? 0.02 : 0.01)) { + if(pixel.temp >= 1387.5) { + var randomNeighborOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var rX = randomNeighborOffset[0]; + var rY = randomNeighborOffset[1]; + var rfX = pixel.x+rX; + var rfY = pixel.y+rY; + if(isEmpty(rfX,rfY,false)) { + var randomEligibleHotElement = hotHeejiniteElements[Math.floor(Math.random() * hotHeejiniteElements.length)]; + createPixel(randomEligibleHotElement,rfX,rfY); + pixelMap[rfX][rfY].temp = pixel.temp; + }; + }; + }; + } + elements.heejinite = { + color: ["#cf1172", "#fa1977", "#ff619e"], + fireColor: ["#a9085e", "#a32e61", "#fca7c6"], + properties: { + oldColor: null + }, + behavior: behaviors.WALL, + tick: function(pixel) { heejinitoidTick(pixel) }, + excludeVelocity: true, //wall shouldn't move + tempHigh: 837, + category: "solids", + state: "solid", + density: 3773, + stain: 0.1, + hardness: 0.79, + breakInto: "heejinite_powder", + conduct: 0.86, + }; + elements.heejinite_powder = { + color: ["#d64790", "#e63e84", "#f054ac"], + fireColor: ["#a9085e", "#a32e61", "#fca7c6"], + properties: { + oldColor: null + }, + behavior: behaviors.POWDER, + tick: function(pixel) { heejinitoidTick(pixel) }, + excludeVelocity: true, //wall shouldn't move + tempHigh: 837, + hidden: true, + stateHigh: "molten_heejinite", + category: "powders", + state: "solid", + density: 1412, + stain: 0.1, + hardness: 0.66, + breakInto: "heejinite_powder", + conduct: 0.42, + }; + elements.molten_heejinite = { + color: ["#ff0f77","#ff59c2","#ff405c", "#fa5a48"], + fireColor: ["#a9085e", "#a32e61", "#fca7c6"], + properties: { + oldColor: null + }, + tick: function(pixel) { + heejinitoidTick(pixel); + hotHeejinitoidTick(pixel); + }, + density: 3121, + hardness: 0.5, + breakInto: "heejinite_gas", + temp: 1000, + tempHigh: 1501, + conduct: 0.22, + }; + elements.heejinite_gas = { + color: ["#fffab8", "#ffdab3", "#ffd1d1", "#ffc4df", "#ffb0eb"], + fireColor: ["#a9085e", "#a32e61", "#fca7c6"], + properties: { + oldColor: null + }, + tick: function(pixel) { + heejinitoidTick(pixel); + hotHeejinitoidTick(pixel); + }, + density: 0.117, + temp: 1800, + hardness: 1, + conduct: 0.12, + }; + jinsouliteReducedSwapWhitelist = ["slime","glue","soda","milk","chocolate_milk","fruit_milk","ink","blood","vaccine","antibody","infection","sap","ketchup","spirit_tear","enchanted_ketchup","lean","poisoned_ketchup","dirty_ketchup","zombie_blood"]; + function jinsouliteDissolution(pixel) { + var did = false; + for(i = 0; i < 2; i++) { + var randomNeighborOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var rfX = pixel.x+randomNeighborOffset[0]; + var rfY = pixel.y+randomNeighborOffset[1]; + if(!isEmpty(rfX,rfY,true)) { + var rOtherPixel = pixelMap[rfX][rfY]; + if(!rOtherPixel) { return false }; + var rOtherElement = rOtherPixel.element; + if(rOtherElement.endsWith("water") || (Math.random() < 0.3 && jinsouliteReducedSwapWhitelist.includes(rOtherElement))) { + swapPixels(pixel,rOtherPixel); + did = true; + }; + }; + }; + return did; + }; + function jinsouliteMovement(pixel,move1Spots,move2Spots) { + if(move1Spots.length > 0) { + var randomMove1 = move1Spots[Math.floor(Math.random() * move1Spots.length)]; + if(!tryMove(pixel, pixel.x+randomMove1[0], pixel.y+randomMove1[1])) { + //console.log((pixel.x+randomMove1[0]) + " " + (pixel.y+randomMove1[1])) + var newPixel = null; + if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + }; + if(outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1]) || !jinsouliteRxnStealerImmutableEl2DoNotUse(pixel,newPixel,"water",true,2)) { + if(move2Spots.length > 0) { + var randomMove2 = move2Spots[Math.floor(Math.random() * move2Spots.length)]; + if(!tryMove(pixel, pixel.x+randomMove2[0], pixel.y+randomMove2[1])) { + var newPixel = null; + if(!outOfBounds(pixel.x+randomMove1[0],pixel.y+randomMove1[1])) { + newPixel = pixelMap[pixel.x+randomMove1[0]][pixel.y+randomMove1[1]]; //newPixel is AAA + }; + if(newPixel !== null) { jinsouliteRxnStealerImmutableEl2DoNotUse(pixel,newPixel,"water",true,2) }; + }; + }; + }; + }; + }; + doDefaults(pixel); + }; + function jinsouliteSolidNonWaterSideReactions(pixel) { + var randomNeighborOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var rfX = pixel.x+randomNeighborOffset[0]; + var rfY = pixel.y+randomNeighborOffset[1]; + if(!isEmpty(rfX,rfY,true)) { + var rOtherPixel = pixelMap[rfX][rfY]; + if(typeof(rOtherPixel) === "undefined" || isEmpty(rfX,rfY,true)) { + return false; + }; + jinsouliteRxnStealerImmutableEl2DoNotUse(pixel,rOtherPixel,"water",true,2); + }; + return true; + }; + function jinsouliteSolidWaterSideReactions(pixel) { + var randomNeighborOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var rfX = pixel.x+randomNeighborOffset[0]; + var rfY = pixel.y+randomNeighborOffset[1]; + if(!isEmpty(rfX,rfY,true)) { + var pixel2 = pixelMap[rfX][rfY]; + var pixel1 = pixel; + if(typeof(pixel2) === "undefined" || isEmpty(rfX,rfY,true)) { + return false; + }; + if(typeof(pixel1) === "undefined" || isEmpty(pixel.x,pixel.y,true)) { + return false; + }; + var rOtherElement = pixel2.element; + var waterReactions = elements.water.reactions; + if(rOtherElement === pixel.element) { + return false; + }; + if(waterReactions[rOtherElement]) { + var r = waterReactions[rOtherElement]; + if (r.setting && settings[r.setting]===0) { + return false; + } + // r has the attribute "y" which is a range between two y values + // r.y example: [10,30] + // return false if y is defined and pixel1's y is not in the range + if (r.tempMin !== undefined && pixel1.temp < r.tempMin) { + return false; + } + if (r.tempMax !== undefined && pixel1.temp > r.tempMax) { + return false; + } + if (r.charged && !pixel.charge) { + return false; + } + if (r.chance !== undefined && Math.random() < (r.chance * 2)) { + return false; + } + if (r.y !== undefined && (pixel1.y < r.y[0] || pixel1.y > r.y[1])) { + return false; + } + if (r.charge1) { pixel1.charge = r.charge1; } + if (r.temp1) { pixel1.temp += r.temp1; pixelTempCheck(pixel1); } + if (r.color1) { // if it's a list, use a random color from the list, else use the color1 attribute + pixel1.color = pixelColorPick(pixel1, Array.isArray(r.color1) ? r.color1[Math.floor(Math.random() * r.color1.length)] : r.color1); + } + if (r.attr1) { // add each attribute to pixel1 + for (var key in r.attr1) { + pixel1[key] = r.attr1[key]; + } + } + var elem1 = r.elem1 + if (elem1 !== undefined && elem1 instanceof Array) { + elem1 = elem1[Math.floor(Math.random() * elem1.length)]; + }; + if (r.elem2 !== undefined) { + // if r.elem2 is an array, set elem2 to a random element from the array, otherwise set it to r.elem2 + if (Array.isArray(r.elem2)) { + var elem2 = r.elem2[Math.floor(Math.random() * r.elem2.length)]; + } else { var elem2 = r.elem2; } + if (elem2 == null) { + if(elem1 !== undefined) { changePixel(pixel2,elem1) }; + } + else { + changePixel(pixel2,elem2); + } + } + if (r.charge2) { pixel2.charge = r.charge2; } + if (r.temp2) { pixel2.temp += r.temp2; pixelTempCheck(pixel2); } + if (r.color2) { // if it's a list, use a random color from the list, else use the color2 attribute + pixel2.color = pixelColorPick(pixel2, Array.isArray(r.color2) ? r.color2[Math.floor(Math.random() * r.color2.length)] : r.color2); + } + if (r.attr2) { // add each attribute to pixel2 + for (var key in r.attr2) { + pixel2[key] = r.attr2[key]; + } + } + if (r.func) { r.func(pixel1,pixel2); } + return r.elem1!==undefined || r.elem2!==undefined; + }; + }; + return true; + }; + function jinsouliteValue(pixel) { + valueFunction(pixel,jinsouliteValueObject,jinsouliteSpreadWhitelist); + if(pixel.oldColor === null) { pixel.oldColor = pixel.color }; + if(isNaN(pixel.value)) { pixel.value = 0 }; + pixel.color = changeSaturation(pixel.oldColor,pixel.value / 3,"subtract","rgb") + if(pixel.value > 1) { + if(Math.random() < Math.min((pixel.value / 200),0.5)) { + var randomNeighborOffset = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)]; + var rX = randomNeighborOffset[0]; + var rY = randomNeighborOffset[1]; + var rfX = pixel.x+rX; + var rfY = pixel.y+rY; + if(isEmpty(rfX,rfY,false)) { + createPixel("water",rfX,rfY); + pixel.value--; + }; + }; + /*for(g = 0; g < adjacentCoords.length; g++) { + var oX = adjacentCoords[g][0]; + var oY = adjacentCoords[g][1]; + var fX = pixel.x+oX; + var fY = pixel.y+oY; + if(isEmpty(fX,fY,false)) { + createPixel("water",fX,fY); + pixel.value--; + }; + };*/ + }; + } + function jinsoulitoidTick(pixel,move1Spots=[],move2Spots=[]) { + if(pixel.oldColor === undefined) { pixel.oldColor = pixelColorPick(pixel) }; + if(pixel.value == undefined) { pixel.value = 0 }; + if(jinsouliteDissolution(pixel)) { + return; + }; + jinsouliteValue(pixel); + jinsouliteMovement(pixel,move1Spots,move2Spots); + }; + elements.jinsoulite = { + color: ["#0e51b0", "#2129ff", "#3b3dbf"], + fireColor: ["#121978", "#6a9fe6", "#5963d9"], + behavior: [ + "XX|CR:water%0.05|XX", + "CR:water%0.05|XX|CR:water%0.05", + "XX|CR:water%0.05|XX" + ], + behaviorOn: [ + "XX|CR:water%0.15|XX", + "CR:water%0.15|XX|CR:water%0.15", + "XX|CR:water%0.15|XX" + ], + properties: { + oldColor: null + }, + tick: function(pixel) { + if(pixel.value == undefined) { pixel.value = 0 }; + if(pixel.oldColor === undefined) { pixel.oldColor = pixelColorPick(pixel) }; + jinsouliteValue(pixel); + jinsouliteSolidNonWaterSideReactions(pixel); + jinsouliteSolidWaterSideReactions(pixel); + }, + tempHigh: 2606, + category: "solids", + state: "solid", + density: 8331, + hardness: 0.82, + breakInto: "jinsoulite_powder", + conduct: 0.93, + }; + elements.jinsoulite_powder = { + color: ["#4580ba", "#355eb0", "#2d6fc4"], + fireColor: ["#121978", "#6a9fe6", "#5963d9"], + tempHigh: 2606, + behavior: [ + "XX|CR:water%0.05|XX", + "CR:water%0.05|XX|CR:water%0.05", + "XX|CR:water%0.05|XX" + ], + properties: { + oldColor: null + }, + category: "powders", + behaviorOn: [ + "XX|CR:water%0.15|XX", + "CR:water%0.15|XX|CR:water%0.15", + "XX|CR:water%0.15|XX" + ], + tick: function(pixel) { jinsoulitoidTick(pixel,[[0,1]],[[-1,1],[1,1]]) }, + stateHigh: "molten_jinsoulite", + category: "powders", + state: "solid", + hidden: true, + density: 5801, + hardness: 0.7, + conduct: 0.43, + }; + elements.molten_jinsoulite = { + behavior: [ + "XX|CR:fire,fire,steam%0.5|XX", + "XX|XX|XX", + "XX|XX|XX" + ], + behaviorOn: [ + "XX|CR:fire,steam,steam%0.7|XX", + "CR:steam%0.1|XX|CR:steam%0.1", + "XX|CR:steam%0.1|XX" + ], + properties: { + oldColor: null + }, + color: ["#4e35db","#7767eb","#a876f5", "#78acff"], + fireColor: ["#121978", "#6a9fe6", "#5963d9"], + fireElement: ["fire","fire","steam"], + tick: function(pixel) { jinsoulitoidTick(pixel,[[-1,1],[0,1],[1,1]],[[-1,0],[1,0]]); }, + density: 6448, + hardness: 0.61, + breakInto: "jinsoulite_gas", + temp: 3000, + tempHigh: 5532.8509, + conduct: 0.34, + }; + elements.jinsoulite_gas = { + color: ["#c0f0ef", "#c2c1db", "#c0bff5", "#cdcce6"], + behavior: [ + "XX|CR:steam%0.5|XX", + "CR:steam%0.5|XX|CR:steam%0.5", + "XX|CR:steam%0.5|XX", + ], + behaviorOn: [ + "XX|CR:steam%1|XX", + "CR:steam%1|XX|CR:steam%1", + "XX|CR:steam%1|XX", + ], + fireColor: ["#08a953", "#2ea332", "#d1e0d3"], + properties: { + oldColor: null + }, + tick: function(pixel) { jinsoulitoidTick(pixel,adjacentCoords,[[-1,-1],[1,-1],[1,1],[-1,1]]) }, + density: 0.5833, + temp: 6000, + hardness: 1, + conduct: 0.19, + }; + //apples (used for yvesite) + appleAttachWhitelist = ["wood","tree_branch"]; + elements.apple = { + color: ["#ad2333", "#b51616", "#d6782f", "#e3c634", "#99de31"], + isFood: true, + tick: function(pixel) { + if(pixel.attached) { //only attaches upwards + if(isEmpty(pixel.x,pixel.y-1,true)) { + pixel.attached = false; + }; + } else { //Move if not attached + if (!tryMove(pixel, pixel.x, pixel.y+1)) { + if(Math.random() < 0.3) { + if (Math.random() < 0.5) { + if (!tryMove(pixel, pixel.x+1, pixel.y+1)) { + tryMove(pixel, pixel.x-1, pixel.y+1); + }; + } else { + if (!tryMove(pixel, pixel.x-1, pixel.y+1)) { + tryMove(pixel, pixel.x+1, pixel.y+1); + }; + }; + }; + }; + }; + doDefaults(pixel); + var shouldSpoil = true; //spoil by default + if(pixel.attached) { //if it's attached + if(!isEmpty(pixel.x,pixel.y-1,true)) { //if the attachment coords are a pixel and not OOB + var attachPixel = pixelMap[pixel.x][pixel.y-1]; + var attachElement = attachPixel.element; + if(appleAttachWhitelist.includes(attachElement)) {//if the element is a whitelisted "don't spoil" element + shouldSpoil = false; //then don't spoil + }; + }; + }; + if(shouldSpoil) { //spoil if not attached + if(pixel.temp > -18 && pixel.temp <= 4) { //(no spoiling below -18C) + pixel.spoilage += Math.max(Math.min(scale(pixel.temp,-18,4,0,9),9),0) + } else if(pixel.temp > 4) { + pixel.spoilage += Math.max(Math.min(scale(pixel.temp,4,20,9,30),40),0) + }; + }; + if(pixel.spoilage > 14400) { //3600 = 120 ticks at 20C + if(Math.random() < 0.05) { + changePixel(pixel,"rotten_apple"); + }; + }; + }, + properties: { + "spoilage": 0, + "attached": false + }, + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + tempHigh: 200, + stateHigh: ["steam", "ash"], + onTryMoveInto: function(pixel,otherPixel) { + var otherInfo = elements[otherPixel.element] + if(typeof(otherInfo.state) === "string" && otherInfo.state !== "gas") { + pixel.attached = false; + }; + }, + }; + elements.rotten_apple = { + hidden: true, + color: ["#802e24", "#9c4227", "#a34b26"], + behavior: [ + "XX|CR:stench,fly%0.1|XX", + "M2%0.5|CH:dirty_water,fly,fly%0.007|M2%0.5", + "M2|M1|M2" + ], + stain: 0.01, + burn: 5, + burnInto: ["steam", "ash"], + burnTime: 600, + tempHigh: 200, + stateHigh: ["steam", "ash"], + }; + //Yvesite + var yvesiteAppleSpots = [[-1, 1], [0, 1], [1, 1]]; + var yvesitePowderAppleSpots = [[-1, 0], [0, -1], [1, 0]]; + function yvesiteApples(pixel,offsets) { + if(pixel.charge) { + var probAdd = Math.min(0,Math.max(pixel.temp,100)) / 2500; + if(Math.random() < (0.02 + probAdd)) { + var appleOffset = randomChoice(offsets); + if(tryCreatePixel("apple",pixel.x+appleOffset[0],pixel.y+appleOffset[1])) { + var apple = pixelMap[pixel.x+appleOffset[0]][pixel.y+appleOffset[1]]; + apple.color = pixelColorPick(apple,"#c40e2c"); + apple.spoilage = -Infinity; + return true; + } else { + return null; + }; + }; + }; + return false; + }; + elements.yvesite = { + color: ["#850f2c", "#9c0e3d", "#911f3b", "#701625"], + fireColor: ["#b5103f", "#ab3254", "#cc2157", "#ba0936"], + behavior: behaviors.WALL, + tick: function(pixel) { + yvesiteApples(pixel,yvesiteAppleSpots); + }, + reactions: { + heejinite: {temp1: 1, temp2: 1}, + molten_heejinite: {temp1: 2, temp2: 2}, + heejinite_powder: {temp1: 2, temp2: 2}, + heejinite_gas: {temp1: 3, temp2: 3} + }, + tempHigh: 1545, + category: "solids", + state: "solid", + density: 3601, + hardness: 0.88, + breakInto: "yvesite_powder", + conduct: 0.94, + }; + elements.yvesite_powder = { + color: ["#8f1734", "#962638", "#b32749", "#911a3e"], + fireColor: ["#b5103f", "#ab3254", "#cc2157", "#ba0936"], + behavior: behaviors.POWDER, + tick: function(pixel) { + yvesiteApples(pixel,yvesitePowderAppleSpots); + }, + reactions: { + heejinite: {temp1: 2, temp2: 2}, + molten_heejinite: {temp1: 3, temp2: 3}, + heejinite_powder: {temp1: 3, temp2: 3}, + heejinite_gas: {temp1: 4, temp2: 4} + }, + tempHigh: 1545, + stateHigh: "molten_yvesite", + category: "solids", + state: "solid", + density: 1500, + hardness: 0.43, + breakInto: "yvesite_powder", + conduct: 0.84, + }; + elements.molten_yvesite = { + color: ["#e81554", "#c90842", "#db4d70", "#cf2b54"], + fireColor: ["#b5103f", "#ab3254", "#cc2157", "#ba0936"], + behavior: behaviors.MOLTEN, + density: 3608, + state: "liquid", + hardness: 0.57, + breakInto: "yvesite_gas", + temp: 1545, + tempHigh: 3145, + stateHigh: "yvesite_gas", + tempLow: 1545, + stateLow: "yvesite", + conduct: 0.22, + }; + elements.yvesite_gas = { + color: ["#e34070", "#d13060", "#c2234a", "#db4866"], + fireColor: ["#b5103f", "#ab3254", "#cc2157", "#ba0936"], + behavior: behaviors.GAS, + state: "gas", + tempLow: 3145, + stateLow: "molten_yvesite", + density: 8.16, + temp: 3300, + hardness: 1, + conduct: 0.11, + }; + elements.fly.reactions.rotten_apple = { "elem2":null, chance:0.15, func:behaviors.FEEDPIXEL }; + //Vivite + elements.vivite = { + color: ["#ffb5ee", "#fc9fe0", "#fcd9fb"], + colorOn: ["#ff8ca9", "#f76583", "#fcb8d5"], + fireColor: ["#ff66ba", "#ff85ef", "#ff99f7"], + behavior: behaviors.WALL, + onCharge: function(pixel) { + if(isNaN(pixel.charge)) { pixel.charge = 0 }; + if(isNaN(pixel.temp)) { pixel.temp = 20 }; + if(!pixel) { return false }; + doElectricity(pixel); + for(i = 0; i < adjacentCoords.length; i++) { + var newPixel = pixelMap[pixel.x+adjacentCoords[i][0]]?.[pixel.y+adjacentCoords[i][1]]; + if(newPixel) { + newPixel.charge ??= 0; + if(isNaN(newPixel.charge)) { newPixel.charge = 0 }; + try { + newPixel.charge ??= 0; + if(isNaN(newPixel.charge)) { newPixel.charge = 0 }; + doElectricity(newPixel) + if(isNaN(newPixel.charge)) { newPixel.charge = 0 }; + } catch (error) { + if(error.toString().includes("Maximum call stack size exceeded")) { + return; + }; + throw error; + }; + } else { + continue; + }; + }; + }, + tick: function(pixel) { + if(isNaN(pixel.charge)) { pixel.charge = 0 }; + if(isNaN(pixel.temp)) { pixel.temp = 20 }; + if(Math.random() < 0.013 && exposedToAir(pixel)) { + changePixel(pixel,"vivite_oxide",false); + pixel.temp += 4; + }; + }, + burnTime: 160, + burnTempChange: 10.65, + burnInto: "vivite_oxide_powder", + noResistance: true, + reactions: { + "ice": { elem1: "vivite_oxide", elem2: null, temp1: 0.2 }, + "water": { elem1: "vivite_oxide", elem2: null, temp1: 0.2 }, + "steam": { elem1: "vivite_oxide", elem2: null, temp1: 0.2 }, + "seltzer": { elem1: "vivite_oxide", elem2: "carbon_dioxide" }, + "seltzer_ice": { elem1: "vivite_oxide", elem2: "carbon_dioxide" }, + "sugar_water": { elem1: "vivite_oxide", elem2: "sugar" }, + "sugar_ice": { elem1: "vivite_oxide", elem2: "sugar" }, + "pool_water": { elem1: "vivite_oxide", elem2: "chlorine" }, + "pool_ice": { elem1: "vivite_oxide", elem2: "chlorine" }, + "salt_water": { elem1: "vivite_oxide", elem2: "salt" }, + "salt_ice": { elem1: "vivite_oxide", elem2: "salt" } + }, + tempHigh: 938, + category: "solids", + state: "solid", + density: 11013, + hardness: 0.98, + breakInto: "vivite_powder", + conduct: 1, + }; + elements.vivite_powder = { + color: ["#f7a6e5", "#fa70d1", "#f0bbf2"], + fireColor: ["#ff66ba", "#ff85ef", "#ff99f7"], + behavior: behaviors.POWDER, + tick: function(pixel) { + if(Math.random() < 0.027 && exposedToAir(pixel)) { + if(getEmptyMooreNeighbors(pixel).length > 4) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } else { + pixel.temp += 18; + changePixel(pixel,getStateAtTemp("vivite_oxide_powder",pixel.temp),false); + }; + if(pixel.burning && ((pixel.temp + (2 * elements[pixel.element].burnTempChange)) > elements[pixel.element].tempHigh)) { + changePixel(pixel,elements[pixel.element].burnInto,false); + pixel.temp += 213; + }; + }; + }, + burnTime: 8, + burnTempChange: 213, + burnInto: "vivite_oxide_powder", + reactions: { + "ice": { elem1: "vivite_oxide_powder", elem2: null, temp1: 0.2 }, + "water": { elem1: "vivite_oxide_powder", elem2: null, temp1: 0.2 }, + "steam": { elem1: "vivite_oxide_powder", elem2: null, temp1: 0.2 }, + "seltzer": { elem1: "vivite_oxide_powder", elem2: "carbon_dioxide" }, + "seltzer_ice": { elem1: "vivite_oxide_powder", elem2: "carbon_dioxide" }, + "sugar_water": { elem1: "vivite_oxide_powder", elem2: "sugar" }, + "sugar_ice": { elem1: "vivite_oxide_powder", elem2: "sugar" }, + "pool_water": { elem1: "vivite_oxide_powder", elem2: "chlorine" }, + "pool_ice": { elem1: "vivite_oxide_powder", elem2: "chlorine" }, + "salt_water": { elem1: "vivite_oxide_powder", elem2: "salt" }, + "salt_ice": { elem1: "vivite_oxide_powder", elem2: "salt" } + }, + noResistance: true, + tempHigh: 1725, + stateHigh: "molten_vivite", + category: "solids", + state: "solid", + density: 8471, + hardness: 0.99613, + breakInto: "vivite_powder", + conduct: 0.89, + }; + elements.smog.stateLow = ["water","dirty_water"]; + /*Object.keys(elements).filter( + function(elem) { + return ( + !wateroids.includes(elem) && ( + wateroids.includes(elements[elem].stateHigh) || + wateroids.includes(elements[elem].stateLow) + ) + ) + } + );*/ + function doViviteSlag(pixel,target) { + if(!target) { //No pixel + return false; + }; + var newElement = target.element; + if(newElement.includes("vivite")) { //Is vivite + return false; + }; + if(elements[pixel.element].reactions?.[newElement]) { //Pre-existing reaction + return false; + }; + if(elements[newElement].noViviteSlag || elements[pixel.element].ignore?.includes(newPixel.element)) { //Excluded + return false; + }; + if(elements[newElement].state == "gas" && !elements[newElement].viviteSlag) { + return false; + }; + changePixel(newPixel,"vivite_slag",false); + if(Math.random() < 1/3) { changePixel(pixel,"molten_vivite_slag",false) }; + return true; + }; + elements.molten_vivite = { + color: ["#f7a6e5", "#fa70d1", "#f0bbf2"], + colorOn: ["#ff63ac", "#ff21bd", "#e81af0"], + fireColor: ["#ff66ba", "#ff85ef", "#ff99f7"], + ignore: ["wall","heejinite","jinsoulite","haseulite","molten_heejinite","molten_jinsoulite","molten_haseulite","yvesite","molten_yvesite"], + tick: function(pixel) { + var info = elements[pixel.element]; + if(Math.random() < 0.022 && exposedToAir(pixel)) { + changePixel(pixel,pixel.temp > 7315.27 ? "molten_vivite_oxide" : "vivite_oxide_powder",false) + pixel.temp += 18; + }; + if(Math.random() < 0.025) { + var fireColor = info.fireColor; + while(fireColor instanceof Array) { fireColor = fireColor[Math.floor(Math.random() * fireColor.length)] }; + var newFire = tryCreatePixelReturn("fire",pixel.x,pixel.y-1); + if(newFire) { + newFire.color = pixelColorPick(newFire,fireColor); + newFire.temp = pixel.temp; + }; + }; + var viscosityMoveChance = ((Math.random()*100) < 100 / ((info.viscosity ?? 1) ** 0.25)); //returns true if doing m2 + var move1Spots = viscosityMoveChance ? [[-1,1],[0,1],[1,1]] : [[0,1]]; + var move1Offset = move1Spots[Math.floor(Math.random() * move1Spots.length)]; + if(!tryMove(pixel, pixel.x + move1Offset[0], pixel.y + move1Offset[1])) { + var newPixel = pixelMap[pixel.x + move1Offset[0]]?.[pixel.y + move1Offset[1]]; + if(newPixel) { + doViviteSlag(pixel,newPixel); + }; + var move2Spots = [[-1,0],[1,0]]; + var move2Offset = move2Spots[Math.floor(Math.random() * move2Spots.length)]; + //console.log(info.viscosity); + var viscosityMoveChance = ((Math.random()*100) < 100 / ((info.viscosity ?? 1) ** 0.25)); //returns true if doing m2 + if(viscosityMoveChance) { + if(!tryMove(pixel, pixel.x + move2Offset[0], pixel.y + move2Offset[1])) { + var newPixel = pixelMap[pixel.x + move2Offset[0]]?.[pixel.y + move2Offset[1]]; + if(newPixel) { + doViviteSlag(pixel,newPixel); + }; + }; + }; + }; + }, + density: 8212, + state: "liquid", + burnTime: 160, + burnTempChange: 10.65, + burnInto: "vivite_oxide_powder", + hardness: 0.88, + viscosity: 10000, + breakInto: "vivite_gas", + temp: 2000, + tempHigh: 2256, + stateHigh: "vivite_gas", + tempLow: 938, + stateLow: "vivite", + conduct: 1, + stain: 0.03, + behavior: behaviors.WALL, //placeholder to prevent an auto-behaviors.MOLTEN process + }; + elements.vivite_slag = { + color: ["#d962b1", "#bf67b6", "#b57b9e"], + fireColor: ["#fa5584", "#e85a70", "#e05c69", "#ed8672"], + behavior: [ + "XX|XX|XX", + "XX|XX|XX", + "M2%25|M1|M2%25" + ], + density: 9777, + category: "solids", + state: "solid", + hardness: 0.71, + breakInto: ["vivite_powder","slag"], + tempHigh: 953, + conduct: 0.27, + }; + elements.vivite_oxide = { + color: ["#f5b3c5", "#ffb0c6"], + fireColor: ["#ff1f69", "#ff0004", "#ff006a"], + reactions: { + "carbon_monoxide": { elem1: "vivite", elem2: "carbon_dioxide", tempMin: 543, chance: 0.03 } + }, + behavior: behaviors.WALL, + reactions: { + "ice": { elem1: "vivite_hydroxide", elem2: null, temp1: 234, tempMax: 733}, + "water": { elem1: "vivite_hydroxide", elem2: null, temp1: 234, tempMax: 733}, + "steam": { elem1: "vivite_hydroxide", elem2: null, temp1: 234, tempMax: 733}, + "seltzer": { elem1: "vivite_hydroxide", elem2: "carbon_dioxide", temp1: 234, tempMax: 733}, + "seltzer_ice": { elem1: "vivite_hydroxide", elem2: "carbon_dioxide", temp1: 234, tempMax: 733}, + "sugar_water": { elem1: "vivite_hydroxide", elem2: "sugar", temp1: 234, tempMax: 733}, + "sugar_ice": { elem1: "vivite_hydroxide", elem2: "sugar", temp1: 234, tempMax: 733}, + "pool_water": { elem1: "vivite_hydroxide", elem2: "chlorine", temp1: 234, tempMax: 733}, + "pool_ice": { elem1: "vivite_hydroxide", elem2: "chlorine", temp1: 234, tempMax: 733}, + "salt_water": { elem1: "vivite_hydroxide", elem2: "salt", temp1: 234, tempMax: 733}, + "salt_ice": { elem1: "vivite_hydroxide", elem2: "salt", temp1: 234, tempMax: 733} + }, + density: 5135, + category: "solids", + state: "solid", + hardness: 0.9983, + breakInto: "vivite_oxide_powder", + tempHigh: 7315.27, + }; + elements.vivite_oxide_powder = { + color: ["#f5b3c5", "#ffb0c6"], + fireColor: ["#ff1f69", "#ff0004", "#ff006a"], + behavior: behaviors.POWDER, + reactions: { + "carbon_monoxide": { elem1: "vivite_powder", elem2: "carbon_dioxide", tempMin: 543, chance: 0.03 } + }, + reactions: { + "ice": { elem1: "vivite_hydroxide", elem2: null, temp1: 234, tempMax: 733}, + "water": { elem1: "vivite_hydroxide", elem2: null, temp1: 234, tempMax: 733}, + "steam": { elem1: "vivite_hydroxide", elem2: null, temp1: 234, tempMax: 733}, + "seltzer": { elem1: "vivite_hydroxide", elem2: "carbon_dioxide", temp1: 234, tempMax: 733}, + "seltzer_ice": { elem1: "vivite_hydroxide", elem2: "carbon_dioxide", temp1: 234, tempMax: 733}, + "sugar_water": { elem1: "vivite_hydroxide", elem2: "sugar", temp1: 234, tempMax: 733}, + "sugar_ice": { elem1: "vivite_hydroxide", elem2: "sugar", temp1: 234, tempMax: 733}, + "pool_water": { elem1: "vivite_hydroxide", elem2: "chlorine", temp1: 234, tempMax: 733}, + "pool_ice": { elem1: "vivite_hydroxide", elem2: "chlorine", temp1: 234, tempMax: 733}, + "salt_water": { elem1: "vivite_hydroxide", elem2: "salt", temp1: 234, tempMax: 733}, + "salt_ice": { elem1: "vivite_hydroxide", elem2: "salt", temp1: 234, tempMax: 733} + }, + density: 4813, + category: "solids", + state: "solid", + hardness: 0.964, + breakInto: "vivite_oxide_powder", + tempHigh: 7315.27, + stateHigh: "molten_vivite_oxide", + }; + elements.vivite_hydroxide = { + color: ["#f19cf7", "#d88aff"], + behavior: behaviors.POWDER, + density: 6112, + category: "solids", + state: "solid", + hardness: 0.9983, + breakInto: "vivite_hydroxide", + tempHigh: 733, + stateHigh: ["vivite_oxide_powder","water"], + }; + elements.molten_vivite_oxide = { + fireColor: ["#ff1f69", "#ff0004", "#ff006a"], + reactions: { + "carbon_monoxide": { elem1: "vivite_gas", elem2: "carbon_dioxide", tempMin: 543, chance: 0.03 } + }, + density: 6113, + hardness: 0.9993, + breakInto: "molten_vivite_oxide", + temp: 8000, + tempHigh: 15500, + }; + elements.vivite_oxide_gas = { + reactions: { + "carbon_monoxide": { elem1: "vivite", elem2: "carbon_dioxide", tempMin: 543, chance: 0.03 } + }, + }; + elements.molten_vivite_slag = { + color: ["#d44e88", "#db7258", "#eda455"], + fireColor: ["#fa5584", "#e85a70", "#e05c69", "#ed8672"], + density: 9213, + hardness: 0.71, + breakInto: ["molten_vivite","slag"], + temp: 1100, + conduct: 0.18, + stain: 0.04 + }; + elements.vivite_gas = { + 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)) { + if(getEmptyMooreNeighbors(pixel).length > 4) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + } else { + pixel.temp += 18; + changePixel(pixel,pixel.temp > 15500 ? "vivite_oxide_gas" : pixel.temp > 7315.27 ? "molten_vivite_oxide" : "vivite_oxide_powder",false); + }; + }; + }, + behavior: behaviors.GAS, + state: "gas", + tempLow: 2256, + stateLow: "molten_vivite", + burnTime: 8, + burnTempChange: 213, + burnInto: "vivite_oxide_powder", + density: 18.02, + temp: 3300, + hardness: 1, + conduct: 1, + }; + runAfterLoad(function() { + if(elements.carbon_monoxide) { + elements.carbon_monoxide.reactions ??= {}; + elements.carbon_monoxide.reactions.vivite_oxide = { elem1: "carbon_dioxide", elem2: "vivite", tempMin: 543, chance: 0.03 }; + elements.carbon_monoxide.reactions.vivite_oxide_powder = { elem1: "carbon_dioxide", elem2: "vivite_powder", tempMin: 543, chance: 0.03 }; + elements.carbon_monoxide.reactions.molten_vivite_oxide = { elem1: "carbon_dioxide", elem2: "vivite_gas", tempMin: 543, chance: 0.03 }; + elements.carbon_monoxide.reactions.vivite_oxide_gas = { elem1: "carbon_dioxide", elem2: "vivite_gas", tempMin: 543, chance: 0.03 }; + } + }); + runAfterLoad(function() { + for(key in elements.water.reactions) { + var value = JSON.parse(JSON.stringify(elements.water.reactions[key])); + if(typeof(value.chance) === "number") { + value.chance = Math.min(1,value.chance * 2); + }; + if(value.elem2 === null && value.elem1 !== null) { + value.elem2 = value.elem1; + }; + delete value.elem1; + var movableJinsoulitoids = ["jinsoulite_powder","molten_jinsoulite","jinsoulite_gas"]; + for(j = 0; j < movableJinsoulitoids.length; j++) { + var jinsoulitoid = movableJinsoulitoids[j]; + if(typeof(elements[jinsoulitoid].reactions) === "undefined") { + elements[jinsoulitoid].reactions = {}; + }; + if(typeof(elements[jinsoulitoid].reactions[key]) === "undefined") { + elements[jinsoulitoid].reactions[key] = value; + }; + }; + }; + }); + //IDOL BIRTHDAY STUFF + fakeDate = urlParams.get('fakeDate'); + shortenedTest = (urlParams.get('shortenedTest') !== null); + //Current iteration copied from the updated version used in https://orbit-loona.github.io/index.html + //Colors may not be official for all groups, and even in groups with official colors, the exact color may vary. + //Sometimes hex codes are given and sometimes it's only generic color words. + //LOONA colors are official but exact shades vary + //Kep1er member colors are fan-made colors taken from https://www.reddit.com/r/kep1er/comments/qomf2x/giving_kep1er_member_colours_with_reasoning/ + //Dayeon's color is directly from the sample, as hex code => rgb(188,188,226) doesn't match and is too similar to Yeseo + //BLACKPINK colors are taken from a random blackpink.fandom forum post, and probably made up but whatever + //Everglow, STAYC, IZ*ONE, and IVE colors are from Fandom + //TWICE colors are official but may vary; the specific shades were compiled at https://aminoapps.com/c/once/page/blog/twice-hex-codes-revamped/WJxj_DxnSXuRRgkoavejJjm7b7zokrnBRBb + //Dreamcatcher colors are from //https://www.reddit.com/r/dreamcatcher/comments/lxs6bv/member_colors/ + //SNSD colors are from //https://colorcodedstuff.fandom.com/wiki/Girls%27_Generation + //NewJeans (tends to shuffle) + //(G)I-DLE colors are from kprofiles + //2NE1 colors are color-picked from https://aminoapps.com/c/2ne1/page/blog/2ne1s-microphone-infos-facts-meaning-and-etc/vDQj_bDfnu6P0MdQ0XWnKaB07xN4BG2mLG + //aespa colors are influenced by https://kprofiles.com/poll-which-colours-do-you-think-fits-each-aespa-member-personality/ but ultimately picked arbitrarily + //Apink colors color-picked (but altered) from microphones from https://twitter.com/LegendaryApink/status/1606153363606880256 and https://www.pinterest.com/pin/705587466610023284/ (https://i.pinimg.com/736x/70/dc/60/70dc602675f5bcd86f635b77a5d2e938.jpg) + //Billlie colors from kprofiles + //ALICE, APRIL, Cherry Bullet, Cignature, CLC, CSR, DIA, DreamNote, and fromis_9 colors taken from colorcodedlyrics because their color assignments seem to be consistent + //ALICE group color color-picked arbitrarily from the new header image: https://www.reddit.com/gallery/u0jeqq + //cignature group color color-picked arbitrarily from the fanclub name announcement image + //CSR group color arbitrarily color-picked from the subreddit icon + //fromis_9 group colors are fan-made: https://twitter.com/fromis_9pic/status/1478175338089705472/photo/1 + //DreamNote colors are official: https://aminoapps.com/c/imedreamnoteofficial/page/blog/dreamnote-offical-colors/aVNW_ewVu0uaxWgV7V33Z6pmpXD7md0owwb + //tripleS colors are taken from the wiki and corrected by color-picking from Welcome To The Haus, but birthdays were compiled by infichandesu on tripleS Discord https://discord.com/channels/968385909730971668/990903693140434964/1231563006731882518 + //1 Seoyeon, 2 Hyerin, 3 Jiwoo, 4 Chaeyeon, 5 Yooyeon, 6 Soomin, 7 Nakyoung, 8 Yubin, 9 Kaede, 10 Seo Dahyun, 11 Kotone, 12 Yeonji, 13 Nien, 14 Sohyun, 15 Xinyu, 16 Mayu, 17 Lynn, 18 Joobin, 19 Hayeon, 20 Shion, 21 Kim Chaewon, 22 Sullin, 23 Seoah, 24 Jiyeon + //Seoyeon: 2003, Hyerin: 2007, Jiwoo: 2005, Chaeyeon: 2004, Yooyeon: 2001, Soomin: 2007, Nakyoung: 2002, Yubin: 2005, Kaede: 2005, Seo Dahyun: 2003, Kotone: 2004, Yeonji: 2008, Nien: 2003, Sohyun: 2002, Xinyu: 2002, Mayu: 2002, Lynn: 2006, Joobin: 2009, Hayeon: 2007, Shion: 2006, Kim Chaewon: 2007, Sullin: 2006, Seoah: 2010, Jiyeon: 2004 + //Yunah 2004, Park Minju 2004, Moka 2004, Wonhee 2007, Iroha 2008 + idolData = { + "01-01": { member: "Winter", color: "rgb(209,221,236)", group: "aespa"}, + "03-01": { member: "Jisoo", color: "rgb(159,0,191)", group: "BlackPink" }, + "05-01": { member: "Karin", color: "rgb(238,18,137)", group: "ALICE"}, + "06-01": [ { member: "Shuhua", color: "rgb(255,255,153)", group: "(G)I-DLE"}, { member: "Chloe", color: "rgb(219,244,167)", group: "cignature" }, { member: "Eunbin", color: "rgb(255,215,0)", group: "CLC" } ], + "07-01": [ { member: "Yoohyeon", color: "rgb(31,237,21)", group: "Dreamcatcher"}, { member: "Saerom", color: "rgb(254,66,4)", group: "fromis_9" } ], + "08-01": [{ member: "Seo Dahyun", color: "rgb(255,154,214)", group: "tripleS" }, { member: "Yeonji", color: "rgb(89,116,255)", group: "tripleS" }], + "10-01": { member: "Haeyoon", color: "rgb(246,145,125)", group: "Cherry Bullet" }, + "11-01": { member: "Lee Chaeyeon", color: "rgb(166,220,222)", group: "IZ*ONE"}, + "13-01": [ {member: "Mia", color: "rgb(103,5,6)", group: "Everglow"}, { member: "Haram", color: "rgb(255,153,204)", group: "Billlie"} ], + "15-01": [{ member: "Suhyeon", color: "rgb(63,196,96)", group: "Billlie"}, { member: "Yunah", color: "rgb(255,207,112)", group: "ILLIT" }], + "16-01": [{ member: "Jennie", color: "rgb(204,108,169)", group: "BlackPink"}, { member: "Joobin", color: "rgb(183,245,76)", group: "tripleS" }], + "18-01": { member: "Minzy", color: "rgb(195,108,230)", group: "2NE1"}, + "22-01": { member: "Lee Seoyeon", color: "rgb(0,83,133)", group: "fromis_9" }, + "23-01": { member: "Isa", color: _cc.b.r, group: "STAYC"}, + "26-01": { member: "Somyi", color: "rgb(199,56,164)", group: "DIA" }, + "28-01": { member: "Sheon", color: "rgb(255,153,0)", group: "Billlie"}, + "30-01": { member: "Haruna", color: "rgb(9,151,222)", group: "Billlie"}, + "31-01": { member: "Miyeon", color: _cc.b.r, group: "(G)I-DLE"}, + "01-02": { member: "Jihyo", color: "rgb(250,200,87)", group: "TWICE"}, + "02-02": { member: "Do-A", color: "rgb(204,0,255)", group: "ALICE"}, + "03-02": [ {member: "Gahyeon", color: "rgb(186,9,191)", group: "Dreamcatcher"}, {member: "Rei", color: "rgb(105,195,45)", group: "IVE"}, { member: "Yubin", color: "rgb(255,227,226)", group: "tripleS" } ], + "04-02": { member: "Iroha", color: "rgb(71,145,255)", group: "ILLIT" }, + "05-02": [ { member: "Kim Minju", color: _cc.w.r, group: "IZ*ONE"}, { member: "Hyunjoo", color: "rgb(100,190,193)", group: "APRIL" } ], + "07-02": { member: "Sunn", color: "rgb(255,173,173)", group: "cignature" }, + "09-02": { member: "Yooyeon", color: "rgb(219,12,116)", group: "tripleS" }, + "10-02": [ {member: "Kim Lip", color: "rgb(234,2,1)", group: "LOONA"}, {member: "Sooyoung", color: "rgb(255,92,205)", group: "Girls' Generation"}, { member: "Son Naeun", color: "rgb(196,179,107)", group: "Apink" }, { member: "Irene", color: "rgb(255,251,0)", group: "Red Velvet"} ], + "11-02": { member: "Rose'", color: "rgb(0,94,255)", group: "BlackPink"}, + "13-02": { member: "Jiyeon", color: "rgb(255,171,98)", group: "tripleS" }, + "16-02": { member: "Siyoon", color: "rgb(255,255,0)", group: "Billlie"}, + "21-02": [ { member: "Leeseo", color: "rgb(255,240,1)", group: "IVE"}, { member: "Wendy", color: "rgb(0,95,255)", group: "Red Velvet"} ], + "26-02": { member: "CL", color: "rgb(226,217,137)", group: "2NE1"}, + "02-03": { member: "Dayeon", color: "rgb(100,150,235)", group: "Kep1er"}, + "03-03": [ { member: "Chorong", color: "rgb(230,0,86)", group: "Apink" }, { member: "Bora", color: "rgb(122,205,233)", group: "Cherry Bullet" } ], + "04-03": { member: "Geumhee", color: "rgb(4,173,87)", group: "CSR" }, + "05-03": [ { member: "Yuju", color: "rgb(134,4,148)", group: "Cherry Bullet" }, { member: "Yeri", color: "rgb(159,31,191)", group: "Red Velvet" } ], + "07-03": [ { member: "Dami", color: "rgb(255, 244, 15)", group: "Dreamcatcher"}, { member: "Eunjo", color: "rgb(86,255,89)", group: "DreamNote" } ], + "09-03": [ { member: "Taeyeon", color: "rgb(100,149,237)", group: "Girls' Generation"}, { member: "Soojin", color: "rgb(247,152,141)", group: "Girls' Generation"} ], + "10-03": [{ member: "HaBin", color: "rgb(86,255,204)", group: "DreamNote" }, { member: "Kotone", color: "rgb(253,224,0)", group: "tripleS" }], + "12-03": [ { member: "Hikaru", color: "rgb(251,234,140)", group: "Kep1er"}, { member: "Hwang Sihyeon", color: "rgb(4,182,243)", group: "CSR" } ], + "13-03": [ { member: "Bae Sumin", color: "rgb(255,192,203)", group: "STAYC"}, { member: "Chaerin", color: "rgb(93,52,195)", group: "Cherry Bullet" } ], + "19-03": { member: "Sakura", color: "rgb(241,210,231)", group: "IZ*ONE"}, + "20-03": { member: "Park Jiwon", color: "rgb(134,171,17)", group: "fromis_9" }, + "24-03": [ { member: "Mina", color: "rgb(111,197,194)", group: "TWICE"}, { member: "Bom", color: "rgb(118,212,174)", group: "2NE1"} ], + "26-03": [ { member: "Handong", color: _cc.b.r, group: "Dreamcatcher"}, { member: "Mirae", color: "rgb(185,74,214)", group: "Cherry Bullet" }, { member: "An Seoyeon", color: "rgb(246,98,15)", group: "CSR" } ], + "27-03": { member: "Lisa", color: "rgb(255,250,0)", group: "BlackPink"}, + "29-03": { member: "Irene", color: "rgb(255,127,223)", group: "Red Velvet"}, + "01-04": { member: "Jeewon", color: "rgb(0,153,148)", group: "cignature" }, + "03-04": { member: "Shion", color: "rgb(255,66,138)", group: "tripleS" }, + "10-04": { member: "Semi", color: "rgb(186,99,247)", group: "cignature" }, + "11-04": [ { member: "Danielle", color: "rgb(0,238,0)", group: "NewJeans"}, { member: "Karina", color: "rgb(168,44,230)", group: "aespa"} ], + "12-04": [{ member: "Hyerin", color: "rgb(146,0,255)", group: "tripleS" }, { member: "Lynn", color: "rgb(172,98,183)", group: "tripleS" }], + "14-04": { member: "Yoon", color: "rgb(50,205,50)", group: "STAYC"}, + "15-04": { member: "Namjoo", color: "rgb(203,108,176)", group: "Apink" }, + "17-04": { member: "Jiheon", color: "rgb(249,234,77)", group: "fromis_9" }, + "18-04": { member: "Jessica", color: "rgb(209,0,86)", group: "Girls' Generation"}, + "21-04": { member: "Hyein", color: "rgb(31,95,255)", group: "NewJeans"}, + "23-04": [ { member: "Son Chaeyoung", color: "rgb(232,25,51)", group: "TWICE"}, { member: "Yuna", color: "rgb(111,67,164)", group: "CSR" } ], + "24-04": { member: "YOUI", color: "rgb(255,86,86)", group: "DreamNote" }, + "26-04": [ { member: "Chaehyun", color: "rgb(255,183,206)", group: "Kep1er"}, { member: "Remi", color: "rgb(179,249,107)", group: "Cherry Bullet" } ], + "28-04": { member: "Duna", color: "rgb(229,111,182)", group: "CSR" }, + "02-05": { member: "Kim Chaewon", color: "rgb(199,163,224)", group: "tripleS" }, + "05-05": { member: "Lee Naeun", color: "rgb(186,76,129)", group: "APRIL" }, + "07-05": { member: "Minji", color: "rgb(255,248,31)", group: "NewJeans"}, + "11-05": { member: "Park Minju", color: "rgb(186,69,69)", group: "ILLIT" }, + "12-05": { member: "Mayu", color: "rgb(254,142,118)", group: "tripleS" }, + "14-05": { member: "Lee Chaeyoung", color: "rgb(35,248,84)", group: "fromis_9" }, + "15-05": [ {member: "Sunny", color: "rgb(107,142,35)", group: "Girls' Generation"}, {member: "Haerin", color: _cc.w.r, group: "NewJeans"} ], + "17-05": { member: "JiU", color: _cc.w.r, group: "Dreamcatcher"}, + "18-05": { member: "Onda", color: "rgb(179,4,105)", group: "Everglow"}, + "19-05": { member: "E:U", color: "rgb(107,86,163)", group: "Everglow"}, + "22-05": { member: "Yang Yena", color: "rgb(255,178,79)", group: "APRIL" }, + "24-05": { member: "Yves", color: "rgb(125,0,30)", group: "LOONA"}, + "25-05": { member: "Xinyu", color: "rgb(213,19,19)", group: "tripleS" }, + "26-05": { member: "Eunchae", color: "rgb(40,119,255)", group: "DIA" }, + "28-05": { member: "Dahyun", color: _cc.w.r, group: "TWICE"}, + "30-05": { member: "Yoona", color: "rgb(0,105,148)", group: "Girls' Generation"}, + "01-06": { member: "Nagyung", color: "rgb(255,145,102)", group: "fromis_9" }, + "02-06": { member: "Nien", color: "rgb(255,149,63)", group: "tripleS" }, + "03-06": { member: "Seunghee", color: "rgb(250,55,137)", group: "DIA" }, + "04-06": { member: "Choerry", color: "rgb(92,44,146)", group: "LOONA"}, + "07-06": { member: "Jueun", color: "rgb(247,183,240)", group: "DIA" }, + "11-06": { member: "Seoah", color: "rgb(207,243,255)", group: "tripleS" }, + "13-06": { member: "JinSoul", color: "rgb(20,36,176)", group: "LOONA"}, + "14-06": [ {member: "Seeun", color: "rgb(135,206,235)", group: "STAYC"}, {member: "Tzuyu", color: "rgb(1,108,186)", group: "TWICE"} ], + "16-06": { member: "Huihyeon", color: "rgb(103,78,167)", group: "DIA" }, + "18-06": { member: "Nako", color: "rgb(183,211,233)", group: "IZ*ONE"}, + "20-06": { member: "Seline", color: "rgb(247,99,215)", group: "cignature" }, + "26-06": { member: "Wonhee", color: "rgb(198,255,217)", group: "ILLIT" }, + "28-06": { member: "Seohyun", color: "rgb(251,206,177)", group: "Girls' Generation"}, + "05-07": [ { member: "Hyewon", color: "rgb(219,112,108)", group: "IZ*ONE"}, { member: "Linlin", color: "rgb(191,27,43)", group: "Cherry Bullet" } ], + "07-07": { member: "Chaekyung", color: "rgb(255,183,197)", group: "APRIL" }, + "13-07": { member: "Yebin", color: "rgb(211,0,0)", group: "DIA" }, + "14-07": { member: "Chaesol", color: "rgb(100,207,255)", group: "cignature" }, + "19-07": { member: "Oh Hayoung", color: "rgb(210,176,160)", group: "Apink" }, + "21-07": { member: "Aisha", color: _cc.b.r, group: "Everglow"}, + "23-07": { member: "Sua", color: "rgb(0,220,220)", group: "CSR" }, + "27-07": { member: "Huening Bahiyyih", color: "rgb(255,177,109)", group: "Kep1er"}, + "01-08": [ {member: "Sieun", color: _cc.w.r, group: "STAYC"}, {member: "Kim Chaewon", color: "rgb(206,229,213)", group: "IZ*ONE"}, {member: "Tiffany", color: "rgb(169,32,62)", group: "Girls' Generation"}, { member: "Dohee", color: "rgb(175,27,63)", group: "cignature" }, { member: "Hayeon", color: "rgb(83,217,190)", group: "tripleS" } ], + "05-08": { member: "Kim Sihyeon", color: "rgb(199,210,167)", group: "Everglow" }, + "06-08": { member: "Seoyeon", color: "rgb(34,174,255)", group: "tripleS" }, + "09-08": { member: "Lara", color: "rgb(145,86,255)", group: "DreamNote" }, + "10-08": [ { member: "SuA", color: "rgb(255,0,0)", group: "Dreamcatcher"}, { member: "Yeeun", color: "rgb(194,0,130)", group: "CLC" } ], + "12-08": { member: "Choi Yujin", color: "rgb(249,123,144)", borderOverride: "rgb(247,127,14)", group: "CLC/Kep1er"}, + "13-08": [ { member: "EJ", color: "rgb(255,170,66)", group: "ALICE"}, { member: "Bomi", color: "rgb(188,169,203)", group: "Apink" } ], + "18-08": [ { member: "Eunji", color: "rgb(230,171,71)", group: "Apink" }, { member: "HaSeul", color: "rgb(0,191,0)", group: "LOONA"} ], + "22-08": [ { member: "Yeseo", color: "rgb(162,207,254)", group: "Kep1er"}, { member: "Somin", color: "rgb(153,230,179)", group: "APRIL" } ], + "26-08": [ { member: "Soyeon", color: "rgb(245,176,190)", group: "(G)I-DLE"}, { member: "Chaejeong", color: "rgb(94,247,140)", group: "ALICE"} ], + "28-08": { member: "Rachel", color: "rgb(84,143,247)", group: "APRIL" }, + "31-08": [ { member: "Wonyoung", color: "rgb(255,0,30)", borderOverride: "rgb(217,89,140)", group: "IZ*ONE/IVE"}, { member: "Eunjin", color: "rgb(255,79,27)", group: "DIA" } ], + "01-09": { member: "An Yujin", color: "rgb(255,57,154)", borderOverride: "rgb(86,122,206)", group: "IZ*ONE/IVE"}, + "02-09": { member: "Eunice", color: "rgb(237,157,37)", group: "DIA" }, + "03-09": { member: "Joy", color: "rgb(0,223,23)", group: "Red Velvet" }, + "04-09": { member: "Huh Jiwon", color: "rgb(234,61,165)", group: "Cherry Bullet" }, + "07-09": { member: "Park Sumin", color: "rgb(255,0,255)", group: "DreamNote" }, + "09-09": { member: "Moon Sua", color: "rgb(204,153,255)", group: "Billlie"}, + "14-09": { member: "Jenny", color: "rgb(0,171,219)", group: "DIA" }, + "21-09": { member: "Tsuki", color: "rgb(255,153,161)", group: "Billlie"}, + "22-09": [ {member: "Nayeon", color: "rgb(128,202,241)", group: "TWICE"}, {member: "Hyoyeon", color: "rgb(147,197,114)", group: "Girls' Generation"}, { member: "Hong Yukyung", color: "rgb(212,212,212)", group: "Apink" } ], + "23-09": { member: "Yuqi", color: "rgb(38,201,140)", group: "(G)I-DLE"}, + "24-09": { member: "Gaeul", color: "rgb(0,85,168)", group: "IVE"}, + "27-09": { member: "Eunbi", color: "rgb(187,176,220)", group: "IZ*ONE"}, + "29-09": [ { member: "Choi Yena", color: "rgb(252,246,149)", group: "IZ*ONE"}, { member: "Song Hayoung", color: "rgb(120,94,253)", group: "fromis_9" } ], + "01-10": { member: "Siyeon", color: "rgb(15,47,255)", group: "Dreamcatcher"}, + "03-10": { member: "Soomin", color: "rgb(252,131,164)", group: "tripleS" }, + "06-10": [ { member: "Hitomi", color: "rgb(241,195,170)", group: "IZ*ONE"}, {member: "Hanni", color: "rgb(255,191,255)", group: "NewJeans"} ], + "07-10": { member: "Yeham", color: "rgb(220,0,147)", group: "CSR" }, + "08-10": { member: "Moka", color: "rgb(216,109,157)", group: "ILLIT" }, + "09-10": { member: "Ye Ah", color: "rgb(69,109,255)", group: "cignature" }, + "10-10": { member: "Oh Seunghee", color: "rgb(161,107,250)", group: "CLC" }, + "13-10": [{ member: "HanByeol", color: "rgb(255,238,86)", group: "DreamNote" }, { member: "Nakyoung", color: "rgb(101,153,164)", group: "tripleS" }, { member: "Sohyun", color: "rgb(20,35,180)", group: "tripleS" }], + "15-10": { member: "Yeonje", color: "rgb(255,227,3)", group: "ALICE" }, + "19-10": { member: "HeeJin", color: "rgb(255,0,143)", group: "LOONA"}, + "20-10": { member: "Chuu", color: "rgb(246,144,126)", group: "LOONA"}, + "22-10": { member: "Jo Yuri", color: "rgb(243,170,81)", group: "IZ*ONE"}, + "23-10": [ { member: "Minnie", color: "rgb(155,203,235)", group: "(G)I-DLE"}, { member: "Ningning", color: "rgb(238,23,43)", group: "aespa"} ], + "24-10": { member: "Jiwoo", color: "rgb(255,248,1)", group: "tripleS" }, + "25-10": { member: "MISO", color: "rgb(86,168,255)", group: "DreamNote" }, + "30-10": [ { member: "Giselle", color: "rgb(3,14,6)", group: "aespa"}, { member: "BoNi", color: "rgb(255,156,86)", group: "DreamNote" } ], + "01-11": [ { member: "Jeongyeon", color: "rgb(188,215,118)", group: "TWICE"}, { member: "Kokoro", color: "rgb(0,221,147)", group: "Cherry Bullet" } ], + "02-11": { member: "Elkie", color: "rgb(97,179,41)", group: "CLC" }, + "03-11": { member: "Belle", color: "rgb(0,195,26)", group: "cignature" }, + "05-11": { member: "Lee Yukyung", color: "rgb(55,253,252)", group: "ALICE"}, + "06-11": { member: "Seungyeon", color: "rgb(199,3,30)", group: "CLC" }, + "08-11": { member: "Kim Chaewon", color: "rgb(255,255,122)", group: "APRIL" }, + "09-11": { member: "Momo", color: "rgb(248,207,215)", group: "TWICE"}, + "11-11": { member: "YeoJin", color: "rgb(244,111,31)", group: "LOONA"}, + "12-11": [ {member: "Xiaoting", color:"rgb(195,142,199)", group: "Kep1er"}, {member: "Dara", color:"rgb(244,135,105)", group: "2NE1"} ], + "13-11": { member: "Hyeju", color: "rgb(143,143,143)", group: "LOONA"}, + "15-11": { member: "HyunJin", color: "rgb(255,234,0)", group: "LOONA"}, + "16-11": { member: "May", color: "rgb(252,80,80)", group: "Cherry Bullet" }, + "18-11": { member: "Sorn", color: "rgb(0,144,199)", group: "CLC" }, + "19-11": { member: "Go Won", color: "rgb(48,225,146)", group: "LOONA"}, + "21-11": { member: "Liz", color: "rgb(0,195,245)", group: "IVE"}, + "23-11": { member: "Jisun", color: "rgb(238,83,146)", group: "fromis_9" }, + "30-11": { member: "Sullin", color: "rgb(123,186,141)", group: "tripleS" }, + "01-12": { member: "Jung Chaeyeon", color: "rgb(0,160,21)", group: "DIA" }, + "04-12": [ { member: "Jinsol", color: "rgb(205,121,206)", group: "APRIL" }, { member: "Chaeyeon", color: "rgb(141,191,65)", group: "tripleS" }], + "05-12": { member: "Kwon Yuri", color: "rgb(0,51,102)", group: "Girls' Generation"}, + "09-12": [ {member: "ViVi", color: "rgb(255,152,180)", group: "LOONA"}, {member: "J", color: "rgb(255,0,0)", group: "STAYC"} ], + "16-12": { member: "Mashiro", color: "rgb(253,238,244)", group: "Kep1er"}, + "20-12": { member: "Kaede", color: "rgb(255,201,53)", group: "tripleS" }, + "27-12": [ { member: "Youngeun", color: "rgb(147,197,114)", group: "Kep1er"}, { member: "Gyuri", color: "rgb(33,150,254)", group: "fromis_9" } ], + "29-12": [ { member: "Sana", color: "rgb(156,158,207)", group: "TWICE"}, {member: "Yiren", color: _cc.w.r, group: "Everglow"} ], + "31-12": { member: "Sohee", color: "rgb(246,110,186)", group: "ALICE"}, + }; + var chaos = []; + for(date in idolData) { + if(date == "chaos") { continue }; + if(!(idolData[date] instanceof Array)) { idolData[date] = [idolData[date]] }; //array wrap + chaos = chaos.concat(idolData[date]); + }; + idolData.chaos = chaos; + var february10Override = false; + function getDayMonth() { + var d = february10Override ? new Date(1549800000000) : new Date(); + var month = (d.getMonth()+1).toString(); + if(month.length == 1) { month = "0" + month }; + var day = d.getDate().toString(); + if(day.length == 1) { day = "0" + day }; + var dayMonth = day + "-" + month; + return (fakeDate === null ? dayMonth : fakeDate); + } + function registerElemClick(elementName,memberDataIndex) { + var dateData = idolData[getDayMonth()]; + if(!dateData) { + alert("No birthday data here"); + return false; + }; + var memberData = dateData[memberDataIndex]; + var fakeDateMessage = ""; + if(fakeDate !== null) { + fakeDateMessage += "(Fake date) "; + }; + var shortenedTestMessage = ""; + if(shortenedTest) { + shortenedTestMessage += "(Shortened) "; + }; + memberName = memberData.member; + if(clickedElements[memberName][elementName] === false) { + clickedElements[memberName][elementName] = true; + }; + if(evaluateTheClickedElements(memberName)) { + alert( + `You have clicked on all ${Object.keys(clickedElements[memberName]).length} birthday messages spread throughout some of the elements.` + + "\n" + + `Member: ${fakeDateMessage}${shortenedTestMessage}${memberName}. Stan ${memberData.group}!` + ); + }; + return typeof(clickedElements[memberName][elementName]) === "boolean"; + }; + function evaluateTheClickedElements(memberName) { + var done = true; + for(element in clickedElements[memberName]) { + done = done && clickedElements[memberName][element]; + }; + return done; + }; + function highlightButton(element,color,blurRadius="15px",spreadRadius="5px") { + var button = document.getElementById(`elementButton-${element}`); + if(button == null) { + throw new Error(`Nonexistent button for ${element}`) + }; + if(typeof(blurRadius) == "number") { blurRadius = blurRadius + "px" }; + if(typeof(spreadRadius) == "number") { spreadRadius = spreadRadius + "px" }; + document.getElementById(`elementButton-${element}`).style["-webkit-box-shadow"] = `0px 0px ${blurRadius} ${spreadRadius} ${color}`; + document.getElementById(`elementButton-${element}`).style["box-shadow"] = `0px 0px ${blurRadius} ${spreadRadius} ${color}`; + }; + clickedElements = {}; + runAfterAutogen(function() { + var alreadyHighlightedElements = []; + var changingDescElements = ["distance_display","find_toggle","prop","number_adjuster","replace","alt_replace","alt_alt_replace","change","alt_change","alt_alt_change"]; + var blacklist = ["toxin","poison","blood","cancer","rotten_meat","frozen_rotten_meat","zombie_blood","plague","stench","infection","acid","acid_gas","rot","shit","shit_gravel","poo","dioxin","lean","cyanide"]; + var dayMonth = getDayMonth(); + var baseArray = ["heejinite","heejinite_powder","molten_heejinite","heejinite_gas","haseulite","haseulite_powder","molten_haseulite","haseulite_gas","jinsoulite","jinsoulite_powder","molten_jinsoulite","jinsoulite_gas","haseulite_vent","loona","loona_gravel","molten_loona"]; + var loonaTheHTML = ""; + var randomElementSets = {}; + if(idolData[dayMonth]) { + var data = idolData[dayMonth]; + for(var memberIndex = 0; memberIndex < data.length; memberIndex++) { + var member = data[memberIndex].member; + randomElementSets[member] = Object.keys(elements).filter(function(e) { + var cat = elements[e].category; + if(cat == undefined) { cat = "other" }; + cat = cat.toLowerCase(); + return ( + cat !== "clouds" && + cat !== "auto creepers" && + cat !== "auto bombs" && + cat !== "auto fey" && + cat !== "spouts" && + cat !== "singularities" && + cat !== "random" && + cat !== "weapons" && + cat !== "idk" && + cat !== "corruption" && + cat !== "radioactive" && + cat !== "piss" && + cat !== "shit" && + cat !== "vomit" && + cat !== "cum" && + !e.includes("head") && + (!e.includes("body") || e.includes("antibody")) && + !cat.includes("random") && + !cat.includes("udstone") && + !elements[e].nocheer && + !changingDescElements.includes(e) && + !blacklist.includes(e) && + !alreadyHighlightedElements.includes(e) && + !elements[e].hidden && + !baseArray.includes(e) + ); + }); shuffleArray(randomElementSets[member]); randomElementSets[member] = randomElementSets[member].slice(0,shortenedTest ? 2 : 12); + clickedElements[member] = {}; + alreadyHighlightedElements = alreadyHighlightedElements.concat(randomElementSets[member]); + for(i = 0; i < randomElementSets[member].length; i++) { + var elemName = randomElementSets[member][i]; + clickedElements[member][elemName] = false; + }; + runAfterButtons(function() { + var data = idolData[getDayMonth()]; + //console.log(data); + for(var memberIndex = 0; memberIndex < data.length; memberIndex++) { + var member = data[memberIndex].member; + //console.log(member, data[memberIndex]); + var elems = Object.keys(clickedElements[member]); + //console.log(elems); + for(j = 0; j < elems.length; j++) { + var name = elems[j]; + var color = data[memberIndex].color; + if(data.gradient) { + color = _cc.w.r; + }; + //console.log(name); + //console.log(color); + color == _cc.b.r ? highlightButton(name,color,15,12) : highlightButton(name,color,7,2); + }; + }; + }); + var funnyElements = ["heejinite","heejinite_powder","molten_heejinite","heejinite_gas","haseulite","haseulite_powder","molten_haseulite","haseulite_gas","jinsoulite","jinsoulite_powder","molten_jinsoulite","jinsoulite_gas","haseulite_vent","loona","loona_gravel","molten_loona"].concat(randomElementSets[member]); + //console.log(member, funnyElements); + for(element in funnyElements) { + var elemName = funnyElements[element]; + var info = elements[elemName]; + if(!info) { console.log(element) }; + var memberData = data[memberIndex]; + if(typeof(info.desc) === "undefined") { + info.desc = "" + } else { + info.desc += "
" + }; + var normalDesc = baseArray.includes(elemName); + loonaTheHTML = normalDesc ? `Happy birthday, ${memberData.group} ${memberData.member} (Local time)!` : `Happy birthday, ${memberData.member}!`; + info.desc += loonaTheHTML; + }; + }; + }; + }); + //OTHER FICTIONAL MATERIALS ## + function splitRgbColor(color) { + var colorTempArray = color.split(",") + var r = colorTempArray[0].split(",")[0].substring(4) + var g = colorTempArray[1] + var b = colorTempArray[2].slice(0,-1) + return [r,g,b] + } + elements.laetium = { + color: "#f57f87", + tempHigh: 2950, + hardness: 0.87252, + density: 6719, + conduct: 4.7E210, + behavior: behaviors.WALL, + state: "solid", + category: "solids", + reactions: { + sand: {temp1: 5} + } + } + elements.molten_laetium = { + color: ['#ff9f44', '#ff7f44', '#ff5f00'], + behavior: behaviors.MOLTEN, + reactions: { + "ash": { "elem1": null, "elem2": "laetium_slag"}, + "dust": { "elem1": null, "elem2": "laetium_slag"}, + "magma": { "elem1": null, "elem2": "laetium_slag"}, + "sand": { temp1: 5 } + }, + density: 6100, + temp: 3000, + tempLow: 2944, + stateLow: "laetium", + tempHigh: 5837, + stateHigh: "vaporized_laetium", + viscosity: 1.517, + hidden: true, + state: "liquid", + category: "molten", + } + elements.vaporized_laetium = { + color: ['#efdf54', '#efbf54', '#efaf10'], + behavior: behaviors.GAS, + reactions: { + "sand": { temp1: 5 } + }, + density: 49, + temp: 6000, + tempLow: 5837, + stateLow: "molten_laetium", + viscosity: 0.1, + hidden: true, + state: "gas", + category: "gases", + } + elements.atisanium = { + color: "#8dadb8", + conduct: 0.87, + colorOn: ["#ff00ff", "#e600e6", "#a300cc", "#ce07e8"], + tempLow: -44, + stateLow: "liquid_atisanium", + density: 1.225, + behavior: [ + "M1 AND M1|M1 AND M1|M1 AND M1", + "M1 AND M1|XX|M1 AND M1", + "M1 AND M1|M1 AND M1|M1 AND M1" + ], + state: "gas", + category: "gases" + } + elements.liquid_atisanium = { + color: "#3f878a", + conduct: 0.96, + colorOn: ["#8307eb", "#8c00ff", "#9617ff", "#a02eff"], + tempHigh: -45, + stateHigh: "atisanium", + tempLow: -214, + stateLow: "alpha_atisanium", + temp: -100, + density: 1594.1, + behavior: behaviors.LIQUID, + state: "liquid", + category: "liquids", + tick: function(pixel) { + var moveSpotsA = [[-1,1],[0,1],[1,1]] + var moveSpotsB = [[-1,0],[1,0]] + var msaChoice = randomChoice(moveSpotsA) + var msbChoice = randomChoice(moveSpotsB) + if(isEmpty(msaChoice[0],pixel.y+msaChoice[1],true)) { + if(!tryMove(pixel,pixel.x+msaChoice[0],pixel.y+msaChoice[1])) { + tryMove(pixel,pixel.x+msbChoice[0],pixel.y+msbChoice[1]) + } + } + if(pixel.chargeCD) { + if(pixel.chargeCD > 2) { + pixel.chargeCD = 2 + } + } + if(pixel.chargeCD) { + if(pixel.chargeCD > 1) { + if(Math.random() < 0.2) { + pixel.chargeCD = 1 + } + } + } + }, + } + elements.alpha_atisanium = { + color: "#00382a", + conduct: 0.987, + colorOn: ["#3700ff", "#6820f7", "#4b15bf"], + tempHigh: -213, + stateHigh: "liquid_atisanium", + tempLow: -261, + stateLow: "beta_atisanium", + temp: -240, + density: 5129.5, + behavior: behaviors.WALL, + state: "solid", + category: "solids", + tick: function(pixel) { + if(pixel.chargeCD) { + if(pixel.chargeCD > 2) { + pixel.chargeCD = 2 + } + } + if(pixel.chargeCD) { + if(pixel.chargeCD > 1) { + if(Math.random() < 0.4) { + pixel.chargeCD = 1 + } + } + } + }, + } + elements.beta_atisanium = { + color: "#750e35", + conduct: Infinity, //This is where I would make it a superconductor. + colorOn: ["#0f0021", "#120324", "#4b106e", "#a6058e", "#42043a"], //pretend this is UV becoming more pronounced + tempHigh: -260, + stateHigh: "alpha_atisanium", + temp: -270, + density: 11129.5, + behavior: behaviors.WALL, + state: "solid", + category: "solids", + tick: function(pixel) { + if(pixel.chargeCD) { + if(pixel.chargeCD > 3) { + pixel.chargeCD = 3 + } + } + }, + } + elements.polusium = { + color: "#dedc9e", + tempHigh: 1213, + hardness: 0.921952, + density: 4113, + conduct: 0.98, + behavior: behaviors.WALL, + state: "solid", + category: "solids", + tick: function(pixel) { + var emptyNeighbors = []; + for(i = 0; i < adjacentCoords.length; i++) { + if(isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { + emptyNeighbors.push(adjacentCoords[i]); + }; + }; + if(Math.random() < 0.002) { + if(emptyNeighbors.length > 0) { + var randomEmptyNeighbor = emptyNeighbors[Math.floor(Math.random() * emptyNeighbors.length)]; + changePixel(pixel,"polusium_oxide") + createPixel("nitrogen",pixel.x+randomEmptyNeighbor[0],pixel.y+randomEmptyNeighbor[1]) + }; + }; + }, + reactions: { + water: { elem1: "polusium_oxide", elem2: ["water","water","water","water","hydrogen"], chance: 0.006 }, + salt_water: { elem1: "polusium_oxide", elem2: ["salt_water","salt_water","salt_water","salt","hydrogen"], chance: 0.012 }, + bleach: { elem1: "polusium_oxide", chance: 0.02 } + }, + } + elements.molten_polusium = { + tick: function(pixel) { + neighbors = [[-1,0],[0,-1],[1,0],[0,1]] + for(i = 0; i < neighbors.length; i++) { + if(isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],false)) { + if(Math.random() < 0.004) { + changePixel(pixel,"molten_polusium_oxide") + } + } + if(!isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { + if(pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]].element == "salt_water") { + if(Math.random() < 0.024) { + changePixel(pixel,"molten_polusium_oxide") + } + } + } + } + }, + density: 3410, + temp: 1300, + tempLow: 1212, + stateLow: "polusium", + tempHigh: 3110, + stateHigh: "vaporized_polusium", + viscosity: 13, + } + elements.vaporized_polusium = { + color: ["#fdffd1", "#edf2cb", "#fcfac7"], + behavior: behaviors.GAS, + tick: function(pixel) { + neighbors = [[-1,0],[0,-1],[1,0],[0,1]] + for(i = 0; i < neighbors.length; i++) { + if(isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],false)) { + if(Math.random() < 0.015) { + changePixel(pixel,"vaporized_polusium_oxide") + } + } + if(!isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { + if(pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]].element == "salt_water") { + if(Math.random() < 0.06) { + changePixel(pixel,"vaporized_polusium_oxide") + } + } + } + } + }, + density: 21, + temp: 3200, + tempLow: 3109, + stateLow: "molten_polusium", + viscosity: 0.2, + hidden: true, + state: "gas", + category: "gases", + } + elements.polusium_oxide = { + color: "#a9b594", + tempHigh: 1300, + hardness: 0.511952, + density: 3717, + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + viscosity: 13, + } + elements.molten_polusium_oxide = { + temp: 1350, + tempHigh: 1400, + stateHigh: "vaporized_polusium_oxide", + density: 2917, + } + elements.vaporized_polusium_oxide = { + color: "#faffc7", + temp: 1500, + tempLow: 1399, + stateLow: "molten_polusium_oxide", + density: 10, + behavior: behaviors.GAS, + } + runAfterLoad(function() { + elements.laetium_slag = JSON.parse(JSON.stringify(elements.slag)) + elements.laetium_slag.color = ['#a05c5a', '#af6967', '#b06d6d', '#ae6b6c', '#b67a7a'] + elements.laetium_slag.tempHigh = 2950 + elements.laetium_slag.stateHigh = ["molten_slag","molten_laetium"] + }); + //MORE BROKEN FORMS ## + elements.loose_straw = { + color: ["#F9E3A1","#93734E","#C7AA83"], + behavior: behaviors.POWDER, + tempHigh: 380, + stateHigh: "fire", + burn: 80, + burnTime: 30, + burnInto: "ash", + category: "powders", + state: "solid", + density: 47.5, + hidden: true, + }, + elements.straw.breakInto = "loose_straw" + elements.plastic_scrap = { + color: "#c3cccc", + behavior: behaviors.POWDER, + category: "powders", + tempHigh: 200, + stateHigh: "molten_plastic", + burn: 15, + burnTime: 350, + burnInto: "dioxin", + state: "solid", + density: 952, + hidden: true, + }, + elements.plastic.breakInto = ["plastic_scrap","dioxin"] + elements.insulation.breakInto = ["plastic_scrap","dioxin","glass_shard"] + elements.copper_scrap = { + color: ["#B96242","#CE5332","#D77045","#994222","#AE3312","#B75025","#A95232","#BE4322","#C76035"], + behavior: [ + "XX|XX|XX", + "XX|CH:oxidized_copper%0.005|XX", + "M2|M1|M2", + ], + reactions: { + "water": { "elem1":"oxidized_copper", chance:0.0035 }, + "salt_water": { "elem1":"oxidized_copper", chance:0.006 }, + "dirty_water": { "elem1":"oxidized_copper", chance:0.045 }, + "sugar_water": { "elem1":"oxidized_copper", chance:0.0045 } + }, + category: "powders", + tempHigh: 1085, + stateHigh: "molten_copper", + density: 5960, + conduct: 0.90, + hidden: true, + }, + elements.oxidized_copper_scrap = { + color: ["#507565","#52665A","#618374","#305545","#32463A","#416354","#406555","#42564A","#517364"], + behavior: behaviors.POWDER, + category: "powders", + hidden: true, + tempHigh: 1085, + stateHigh: "molten_copper", + density: 5960, + conduct: 0.80, + hidden: true, + } + elements.copper.breakInto = ["copper_scrap","copper_scrap","copper_scrap","copper_scrap","copper_scrap","oxidized_copper_scrap"] + elements.dry_ice.breakInto = "carbon_dioxide" + elements.frozen_ketchup.breakInto = "ketchup_snow" + elements.frozen_poisoned_ketchup.breakInto = "poisoned_ketchup_snow" + regularShinyThingArray = ["iron", "zinc", "tin", "nickel", "silver", "aluminum", "lead", "tungsten", "brass", "bronze", "sterling", "steel", "white_gold", "blue_gold", "rose_gold", "red_gold", "solder", "gold", "pyrite", "mythril", "mithril_mythril_alloy", "titanium", "ilitium", "mithril", "beryllium", "boron", "ruthenium", "rhodium", "palladium", "rhenium", "osmium", "iridium", "platinum", "frozen_mercury", "lithium", "niobium", "ketchup_metal", "ketchup_gold", "tungstensteel", "densinium", "mithril", "signalum", "laetium", "kurshunjukium", "zirconium", "jinsoulite"]; + elements.nitrogen_snow = { + color: "#efefef", + behavior: behaviors.POWDER, + category: "solids", + temp: -259.86, + tempHigh: -209.86, + stateHigh: "liquid_nitrogen", + state: "solid", + density: 850, + hidden: true, + } + elements.nitrogen_ice.breakInto = "nitrogen_snow" + runAfterLoad(function() { + for(i = 0; i < regularShinyThingArray.length; i++) { + var thing = regularShinyThingArray[i]; + if(typeof(elements[thing]) == "object") { + if(typeof(elements[thing]?.breakInto) == "undefined") { + elements[`${thing}_scrap`] = { + color: gravelizeToHex(elements[thing].color), + behavior: behaviors.POWDER, + tempHigh: elements[thing].tempHigh, + stateHigh: thing, + category: "powders", + hidden: true, + density: elements[thing].density * 0.09, + conduct: elements[thing].conduct * 0.4, + movable: true, + }; + if(elements[thing].reactions) { + elements[`${thing}_scrap`].reactions = elements[thing].reactions; + }; + elements[thing].breakInto = `${thing}_scrap`; + }; + elements[thing].cutInto = elements[thing].breakInto + } + }; + elements.acid.ignore.push("densinium_scrap") + elements.lithium_scrap.tick = function(pixel) { + tryTarnish(pixel,"lithium_oxide",0.021) + if(pixel.temp >= 178) { + pixel.burning = true; + pixel.burnStart = pixelTicks; + }; + }; + elements.laetium_scrap.reactions.sand = { temp1: 7 } + }); + //SOIL AND ROCKS, WORLDGEN PRESETS, AND MINERALS (the_ground.js and friends) ## + /* + TODO: + Soils + More sedimentary rocks + Metamorphic rocks + Ersatz pressure + Merge crimson? + Proper classification of limestone within these code comments + */ + //Variables + eLists.WHL = "water,salt_water,sugar_water,dirty_water,swamp_water,heavy_water,radioactive_water,crimwater,pure_water,chilly_water,honey,magma" + var vitreousFelsicName = "obsidian"; + var vitreousInterfelsicName = "dacidian"; + var vitreousIntermediateName = "diodian"; + var vitreousMaficName = "basidian"; + var vitreousUltramaficName = "komatidian"; + var sandSimplification = ["gravel","granite_gravel","granodiorite_gravel","diorite_gravel","basalt_gravel","peridotite_gravel","rhyolite_gravel","dacite_gravel","andesite_gravel","komatiite_gravel","pumice_gravel","intermediate_pumice_gravel","scoria_gravel","mafic_scoria_gravel","ultramafic_scoria_gravel", "dacidian_shard", "andesidian_shard", "basalidian_shard", "komatidian_shard"]; + var rocks = [ "granite", "granodiorite", "diorite", "rock", "peridotite", "rhyolite", "dacite", "andesite", "basalt", "komatiite", "pumice", "intermediate_pumice", "scoria", "mafic_scoria", "ultramafic_scoria", "obsidian", vitreousInterfelsicName, vitreousIntermediateName, vitreousMaficName, vitreousUltramaficName]; + var gravels = [ "granite_gravel", "granodiorite_gravel", "diorite_gravel", "gravel", "peridotite_gravel", "rhyolite_gravel", "dacite_gravel", "andesite_gravel", "basalt_gravel", "komatiite_gravel", "pumice_gravel", "intermediate_pumice_gravel", "scoria_gravel", "mafic_scoria_gravel", "ultramafic_scoria_gravel", "obsidian_shard", "dacidian_shard", "andesidian_shard", "basalidian_shard", "komatidian_shard" ]; + //Functions + //Stepped rainbow colors (used for rainbow earth) + function makeRegularRainbow(steps,s,l,outputFormat="rgb") { + var hslArray = []; + var divisionSize = 360 / steps; + for(i = 0; i < 360; i += divisionSize) { + hslArray.push({h: i, s: s, l: l}); + }; + return hslArray.map(x => convertHslObjects(x,outputFormat)); + }; + crimsonObject = { + grass: "crimson_grass", + ice: "red_ice", + water: "crimwater", + salt_water: "crimwater", + sugar_water: "crimwater", + dirty_water: "crimwater", + pool_water: "pool_water,pool_water,water", + snow: "crimsnow", + packed_snow: "crimsnow", + vine: "crimson_vine", + fish: "vicious_goldfish", + sapling: "shadewood_sapling", + sandy_water: "crimsandy_water", + muddy_water: "crimmuddy_water" + }; + crimRate = 0.001 + //Crimson spreaders + function grassSpread(pixel,dirt,grass,chance) { + pixel.dirtArray = [] //initialize dirt neighbor list + for (i = -2; i < 3; i++) { //iterate around + for (j = -2; j < 3; j++) { + if (!isEmpty(pixel.x+i,pixel.y+j,true)) { //check for a pixel to see if it's dirt + if(Array.isArray(dirt)) { + if(dirt.includes(pixelMap[pixel.x+i][pixel.y+j].element)) { //see if it's dirt + if(!includesArray(pixel.dirtArray,[i,j])) { //avoid duplicate dirt entry + pixel.dirtArray.push([i,j]) //store dirt + } + } + } else { + if(pixelMap[pixel.x+i][pixel.y+j].element == dirt) { //see if it's dirt + if(!includesArray(pixel.dirtArray,[i,j])) { //avoid duplicate dirt entry + pixel.dirtArray.push([i,j]) //store dirt + } + } + } + } + } + } + for (k = 0; k < pixel.dirtArray.length; k++) { //iterate through dirt list + if(Math.random() < chance) { //random chance + if(isEmpty(pixel.x+pixel.dirtArray[k][0],pixel.y+pixel.dirtArray[k][1]-1)) { //check for empty space to grow grass + createPixel(grass,pixel.x+pixel.dirtArray[k][0],pixel.y+pixel.dirtArray[k][1]-1) //place grass above dirt + } + } + } + } + function crimSpread(pixel) { + for (let i = -2; i < 3; i++) { + for (let j = -2; j < 3; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i,true)) { + var destPixel = pixelMap[pixel.x+j][pixel.y+i]; + if(!destPixel) { continue }; + var elementToCheck = destPixel.element; + if(Math.random() < crimRate) { + if(crimsonObject[elementToCheck]) { + var result = crimsonObject[elementToCheck]; + if((typeof(result) == "string") && result.indexOf(",") !== -1 && !(elementExists(result))) { + result = result.split(",") + }; + while(Array.isArray(result)) { + result = randomChoice(result) + }; + if(result == "null") { //fsr null gets ignored + deletePixel(destPixel.x,destPixel.y); + } else { + changePixel(destPixel,result); + } + }; + grassSpread(pixel,["dirt","crimsoil","rainbow_dirt"],"crimson_grass",0.5); + }; + }; + }; + }; + }; + //Nellfire code (used for the obvious) + function doNellfire(pixel) { + var info = elements[pixel.element]; + if((info.nellfireImmune && info.nellfireImmune !== "torch") && pixel.nellburn) { + delete pixel.nellburn; + delete pixel.nellburnStart; + return; + }; + if (pixel.nellburn) { // Burning + if(!settings.burning) { return }; + pixel.nellburnStart ??= pixelTicks; + var nellburnTempChange = info.nellburnTempChange ?? 1; + var fire = info.nellFireElement === undefined ? "nellfire" : info.nellFireElement; //allow null but disallow undefined + //console.log(info.nellFireElement,fire); + while(fire instanceof Array) { + fire = fire[Math.floor(Math.random()*fire.length)]; + }; + var nellFireTemp = info.nellFireSpawnTemp ?? pixel.temp; + var nellFireChance = info.nellFireSpawnChance ?? 20; + pixel.temp += nellburnTempChange ?? 4; + pixelTempCheck(pixel); + for (var i = 0; i < adjacentCoords.length; i++) { // Burn adjacent pixels + var x = pixel.x+adjacentCoords[i][0]; + var y = pixel.y+adjacentCoords[i][1]; + if (!isEmpty(x,y,true)) { + var newPixel = pixelMap[x][y]; + var newInfo = elements[newPixel.element]; + var spreadChance = newInfo.nellburn ?? 15 + if (spreadChance && !newPixel.nellburn) { + if (Math.floor(Math.random()*100) < spreadChance) { + newPixel.nellburn = true; + newPixel.nellburnStart = pixelTicks; + } + } + } + } + if (info.nellfireImmune !== "torch" && (pixelTicks - pixel.nellburnStart > (info.nellburnTime || 150)) && Math.floor(Math.random()*100)<(info.nellburn || 25)) { + var burnInto = info.nellburnInto; + //console.log(burnInto); + if(burnInto === undefined) { burnInto = nellburnObject[pixel.element] }; + //console.log(burnInto); + if(burnInto === undefined) { burnInto = [null,"nell_ash"] }; + //console.log(burnInto); + while(burnInto instanceof Array) { + burnInto = burnInto[Math.floor(Math.random()*burnInto.length)]; + }; + //console.log(burnInto); + if(burnInto == null) { deletePixel(pixel.x,pixel.y); return } else { changePixel(pixel,burnInto,burnInto !== "smoke") }; + if(!elements[burnInto].nellBurningWhenConverted) { + delete pixel.nellburn; + delete pixel.nellburnStart; + }; + //console.log("ass"); + pixel.temp = nellFireTemp; + if (info.nellFireColor != undefined && burnInto == "nellfire") { + pixel.color = pixelColorPick(pixel,info.nellFireColor); + } + else { + pixel.color = pixelColorPick(pixel) + } + } + else if (Math.floor(Math.random()*100) 1000000) { + //console.log('case 1'); + coolingFactor = logistic + }; + if(pixel.temp <= 1000000 && pixel.temp > 100000) { + //console.log('case 2'); + //console.log("l",logistic); + coolingFactor = scale(pixel.temp,1000000,100000,logistic,0.99999); + //if(pixelAge % 10 == 0 || pixel.temp < 100500) { console.log(coolingFactor) }; + }; + if(pixel.temp < 100000) { + //console.log('case 3'); + coolingFactor = 0.99999 + }; + //console.log(coolingFactor); + pixel.temp = ((pixel.temp - (settings.abszero ?? -273.15)) * coolingFactor) + (settings.abszero ?? -273.15); + for (var i = 0; i < adjacentCoords.length; i++) { + var x = pixel.x+adjacentCoords[i][0]; + var y = pixel.y+adjacentCoords[i][1]; + if (isEmpty(x,y)) { + if (Math.random() <= c) { + createPixel(Math.random() < 0.995 ? "light" : "neutron", x, y); + pixelMap[x][y].color = pixel.color; + }; + } else if (!outOfBounds(x,y)) { + var newPixel = pixelMap[x][y]; + //console.log(elements[newPixel.element].conduct); + if(ferromagneticMaterials.includes(newPixel.element) && (Math.random() < 0.1)) { newPixel.charge = 20 }; //no magnetism in sb + //console.log(whitelist,newPixel.element,whitelist.includes(newPixel.element)); + if (pixel.temp!==newPixel.temp && whitelist.includes(newPixel.element)) { + var avg = (pixel.temp + newPixel.temp)/2; + pixel.temp = avg; + newPixel.temp = avg; + pixelTempCheck(pixel); + pixelTempCheck(newPixel); + } + } + } + }; + function almostSun(pixel,lightScale=1,whitelist=["sun"]) { + starLightAndConduction(pixel,starColor(pixel) * lightScale,whitelist); + }; + function nsTick(pixel,lightScale=1,whitelist=["sun"]) { + neutronStarLightAndConduction(pixel,starColor(pixel) * lightScale,whitelist); + }; + elements.sun.tick = function(pixel) { + almostSun(pixel); + }; + //"Generalized" sedimentation function + function sedimentation(pixel,finalRock,chance=0.0003) { + if(finalRock == undefined) { return false }; + if(Math.random() < chance) { + var validNeighborArray = Array.apply(null, Array(adjacentCoords.length)).map(function() {return false}); + //sedimentSandstoneTries++; + for(i = 0; i < adjacentCoords.length; i++) { + //sedimentSandstoneTryIterations++; + if(isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],false)) { + validNeighborArray[i] = false; + //sedimentSandstoneNoDetects++; + } else if(!isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { + /*if(sedimentNeighborTable.includes(pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]].element)) { + validNeighborArray[i] = true; + //sedimentSandstoneDetects++; + } else { + validNeighborArray[i] = false; + //sedimentSandstoneNoDetects++; + };*/ + //validNeighborArray[i] = sedimentNeighborTable.includes(pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]].element); + validNeighborArray[i] = (pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]].state ?? "solid") == "solid"; + }; + }; + if(validNeighborArray.includes(true)) { + //sandstoneFormations++; + //console.log(finalRock); + changePixel(pixel,finalRock); + }/* else { + sandstoneFailures++; + }*/; + }; + }; + //Function for mass replacement according to an object + function transformAround(pixel,range,substitutionObject,reverse=false) { + var radius1 = (-1 * range); + var radius2 = (range + 1); + for (let i = radius1; i < radius2; i++) { + for (let j = radius1; j < radius2; j++) { + if(reverse) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + var destPixel = pixelMap[pixel.x+j][pixel.y+i]; + var elementToCheck = destPixel.element; + if(getKeyByValue(substitutionObject,elementToCheck)) { + changePixel(destPixel,getKeyByValue(substitutionObject,elementToCheck)); + }; + }; + } else { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + var destPixel = pixelMap[pixel.x+j][pixel.y+i]; + var elementToCheck = destPixel.element; + if(substitutionObject[elementToCheck]) { + changePixel(destPixel,substitutionObject[elementToCheck]); + }; + }; + }; + }; + }; + }; + //Previous function with adjacentCoords + function transformAdjacent(pixel,substitutionObject,reverse=false) { + for(k = 0; k < adjacentCoords.length; k++) { + var i = adjacentCoords[k][0] + var j = adjacentCoords[k][1] + if(reverse) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + var destPixel = pixelMap[pixel.x+j][pixel.y+i]; + var elementToCheck = destPixel.element; + if(getKeyByValue(substitutionObject,elementToCheck)) { + changePixel(destPixel,getKeyByValue(substitutionObject,elementToCheck)); + }; + }; + } else { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + var destPixel = pixelMap[pixel.x+j][pixel.y+i]; + var elementToCheck = destPixel.element; + if(substitutionObject[elementToCheck]) { + changePixel(destPixel,substitutionObject[elementToCheck]); + }; + }; + }; + }; + }; + //Cooling rate-varied magma solidification + function magmaRateBasedCooling(pixel,freezingPoint,vitriteName,vitriteThreshold,aphaniteName,aphaniteThreshold,phaneriteName) { + pixel.lastTemperatures ??= []; + pixel.lastTemperatures.push(pixel.temp); //due to how it's structured, last temp will always equal pixel.temp; + while(pixel.lastTemperatures.length > 2) { + pixel.lastTemperatures.shift(); + }; + if(pixel.lastTemperatures.length > 1) { + var overallTemperatureChangeRate = (pixel.temp - pixel.lastTemperatures[0]) / (pixel.lastTemperatures.length - 1); + //console.log(overallTemperatureChangeRate); + if(overallTemperatureChangeRate >= 0) { + return; + }; + if(pixel.temp > freezingPoint) { + return; + }; + //console.log(pixel.x,pixel.y,overallTemperatureChangeRate) + var stateLow; + if(overallTemperatureChangeRate < vitriteThreshold) { //numbers made up + //console.log("f99fd90"); + stateLow = vitriteName; + } else if(overallTemperatureChangeRate < aphaniteThreshold) { + //console.log("aaaaaaaaaa"); + stateLow = aphaniteName; + } else { + //console.log("03"); + stateLow = phaneriteName; + }; + var stateLowInfo = elements[stateLow]; + var slHasHotRock = (stateLowInfo.stateLow == "hot_" + stateLow); + var changeToRockIsHot = false; + if(slHasHotRock) { + var hotRockPoint = stateLowInfo.tempHigh; + if(pixel.temp >= hotRockPoint) { + changeToRockIsHot = true; + }; + }; + changePixel(pixel,changeToRockIsHot ? "hot_" + stateLow : stateLow,false); + }; + }; + //Gravel finder + function getGravelElementName(rockName) { + if(rockName == "rock") { + return "gravel"; + }; + var gravelBasedName = rockName + "_gravel"; + if(elements[gravelBasedName]) { + return gravelBasedName; + }; + var shardBasedName = rockName + "_shard"; + if(elements[shardBasedName]) { + return shardBasedName; + }; + return false; + }; + //Sand finder + function getSandElementName(sandName) { + var theName = sandName; + if(getGravelElementName(theName)) { //will fire if it was a rock with a valid gravel + theName = getGravelElementName(theName) + }; + if(["komatiite","peridotite","komatiite_gravel","peridotite_gravel"].includes(theName)) { + return "olivine_sand"; + }; + if(theName == "gravel" || sandSimplification.includes(theName)) { + return "sand"; + }; + theName = theName.replace(/(gravel|shard)/,"sand"); + if(elements[theName]) { + return theName; + }; + return false; + }; + /*Metamorphism test + function metamorphosisPressureHandler(rockBeingSquished,rockDoingSquishing) { + pixel.lastPressures ??= []; + while(pixel.lastPressures.length > 2) { + pixel.lastPressures.shift(); + }; + var squisherInfo = elements[rockDoingSquishing.element]; + var squisheeInfo = elements[rockBeingSquished.element]; + rockBeingSquished._squishers ??= {}; + rockBeingSquished._squishers[pixelTicks] ??= {}; + rockBeingSquished._squishers[pixelTicks][`x${rockDoingSquishing.x}y${rockDoingSquishing.y}`] = (squisherInfo.density ?? 2500) + (rockDoingSquishing._receivedPressure ?? 0); + rockBeingSquished._receivedPressure = sumNumericArray(Object.values(rockBeingSquished._squishers[pixelTicks])); + if(squisheeInfo.metamorphismFunction) { + squisheeInfo.metamorphismFunction(rockBeingSquished) + }; + }; + function removeLastSquishers(pixel) { + if(!pixel._squishers) { + return false; + }; + if(pixel._squishers[pixelTicks - 1]) { + delete pixel._squishers[pixelTicks - 1]; + }; + }; + elements.metal_scrap.onTryMoveInto = function(pixel,otherPixel) { + metamorphosisPressureHandler(pixel,otherPixel); + }; + elements.metal_scrap.tick = function(pixel) { + removeLastSquishers(pixel); + }; + elements.metal_scrap.metamorphismFunction = function(pixel) { + pixel.temp = pixel._receivedPressure; + }; + elements.metal_scrap.insulate = true; + delete elements.metal_scrap.tempHigh; + delete elements.metal_scrap.stateHigh; + */ + /*Erosion + function toGravelErodeOtmi(pixel,otherPixel,erosionChanceDivisor=5500) { + var gravelName = getGravelElementName(pixel.element); + //console.log(gravelName); + if(!gravelName) { return false }; + var otherState = elements[otherPixel.element].state ?? "solid"; + if(otherState == "solid") { + return false; + }; + //console.log(otherState); + var otherDensity = elements[otherPixel.element].density ?? otherState == "gas" ? 1.3 : 1000; + var erosionChance = ((otherState == "gas" ? otherDensity * 5 : otherDensity) ** 1/1.7) / erosionChanceDivisor; + if(Math.random() < erosionChance) { + changePixel(pixel,gravelName,false); + //changePixelReturn(pixel,gravelName,false).color = "rgb(255,0,0)"; + }; + }; + function toSandErodeOtmi(pixel,otherPixel,erosionChanceDivisor=5500) { + var sandName = getSandElementName(pixel.element); + //console.log(sandName); + if(!sandName) { return false }; + var otherState = elements[otherPixel.element].state ?? "solid"; + if(otherState == "solid") { + return false; + }; + var otherDensity = elements[otherPixel.element].density ?? otherState == "gas" ? 1.3 : 1000; + var erosionChance = ((otherState == "gas" ? otherDensity * 5 : otherDensity) ** 1/1.7) / erosionChanceDivisor; + if(Math.random() < erosionChance) { + changePixel(pixel,sandName,false); + //changePixelReturn(pixel,sandName,false).color = "rgb(255,255,0)"; + }; + };*/ + //I really hate boilerplate + //Array maker + function twoPartRepeatedArray(value1,amount1,value2,amount2) { + var array1 = Array(amount1).fill(value1); + var array2 = Array(amount2).fill(value2); + return array1.concat(array2) + }; + //Powder maker + function newPowder(name,color,density=null,tempHigh=null,stateHigh=null,breakInto=null) { //boilerplate my dick + if(tempHigh == null) { + stateHigh = null; + }; + elements[name] = { + color: color, + behavior: behaviors.POWDER, + category: "solids", + state: "solid", + density: density ?? 1000, + }; + if(tempHigh !== null) { + elements[name].tempHigh = tempHigh; + }; + if(tempHigh !== null && stateHigh !== null) { + elements[name].stateHigh = stateHigh; + }; + if(breakInto !== null) { + elements[name].breakInto = breakInto; + }; + return elements[name]; + }; + //Color gen + //Gravels + function gravelizeToHex(colorIn) { + var colorInput = colorIn; //side effects? + //console.log(`gravelizeToHex: ${colorInput}`) + //make sure in is array + if(!(colorInput instanceof Array)) { + colorInput = [colorInput]; + }; + //console.log(`gravelizeToHex: ${colorInput}`) + //console.log(colorInput); + //prepare final color + var finalColor = []; + //console.log(colorInput); + for(var i = 0; i < colorInput.length; i++) { + finalColor.push(colorInput[i]); + finalColor.push(colorInput[i]); + finalColor.push(colorInput[i]); + }; + //vary lightness + for(i = 0; i < finalColor.length; i+=3) { + finalColor[i] = changeLightness(finalColor[i],1.25,"multiply","hsljson"); + }; + //leave offset-1 colors as-is + for(i = 2; i < finalColor.length; i+=3) { + finalColor[i] = changeLightness(finalColor[i],0.85,"multiply","hsljson"); + }; + //desaturate + for(i = 0; i < finalColor.length; i++) { + finalColor[i] = changeSaturation(finalColor[i],0.9,"multiply","hex"); + }; + //finish + //console.log(finalColor); + return finalColor; + }; + //Sands + function sandizeToHex(rockColor,type="normal",sBringTo=31,sBringFactor=0.4,lBringTo=70,lBringFactor=0.6) { + if(elements[rockColor]) { + //Assuming an element was given, for compatibility + rockColor = elements[rockColor].color + }; + if(!["normal","n","wet","w","packed","p"].includes(type.toLowerCase())) { + throw new Error("Type must be 'normal', 'wet', or 'packed'"); + }; + var sandColor = []; + //var sandColorObject = []; + if(!(rockColor instanceof Array)) { + rockColor = [rockColor]; + }; + for(i = 0; i < rockColor.length; i++) { + var colorAsHsl = normalizeColorToHslObject(rockColor[i]); + if(colorAsHsl.s > 0) { colorAsHsl.s = sBringTo + (-sBringFactor * (sBringTo - colorAsHsl.s)) }; //bring towards 31; + colorAsHsl.l = lBringTo + (-lBringFactor * (lBringTo - colorAsHsl.l)); //bring towards 70 + switch(type.toLowerCase()) { + case "normal": + case "n": + break; + case "wet": + case "w": + if(colorAsHsl.s > 0) { colorAsHsl.s += 3 }; + colorAsHsl.l -= 15; + break; + case "packed": + case "p": + colorAsHsl.s = Math.max(colorAsHsl.s - 11, 0); + colorAsHsl.l += 6; + break; + default: + break; + }; + sandColor.push(convertHslObjects(colorAsHsl,"hex")); + //sandColorObject.push(convertHslObjects(colorAsHsl,"rgbjson")); + }; + return sandColor; + }; + function dustizeToHex(rockColor,sBringTo=25,sBringFactor=0.4,lBringTo=55,lBringFactor=0.6) { + if(elements[rockColor]) { + //Assuming an element was given, for compatibility + rockColor = elements[rockColor].color + }; + //console.log(rockName); + var dustColor = []; + //var dustColorObject = []; + if(!(rockColor instanceof Array)) { + rockColor = [rockColor]; + }; + for(i = 0; i < rockColor.length; i++) { + var colorAsHsl = normalizeColorToHslObject(rockColor[i]); + if(colorAsHsl.s > 0) { colorAsHsl.s = sBringTo + (-sBringFactor * (sBringTo - colorAsHsl.s)) }; //bring towards 31; + colorAsHsl.l = lBringTo + (-lBringFactor * (lBringTo - colorAsHsl.l)); //bring towards 70 + dustColor.push(convertHslObjects(colorAsHsl,"hex")); + //dustColorObject.push(convertHslObjects(colorAsHsl,"rgbjson")); + }; + return dustColor; + }; + //Sandstones + function sandstonizeToHex(sandName,type="normal") { + //console.log(sandName); + var sandInfo = elements[sandName]; + if(!sandInfo) { throw new Error("No such element '" + sandName + "'") }; + var finalColor = []; + //var sandColorObject = []; + var sandColor = sandInfo.color; + if(!(sandColor instanceof Array)) { + sandColor = [sandColor]; + }; + //console.log(sandColor); + for(var i = 0; i < sandColor.length; i++) { + //console.log(i,sandColor[i]); + var colorAsHsl = normalizeColorToHslObject(sandColor[i]); + //console.log(colorAsHsl); + if(colorAsHsl.s > 5 && colorAsHsl.h !== 0) { colorAsHsl.h -= 10 }; + if(colorAsHsl.s > 5 && colorAsHsl.h !== 0) { colorAsHsl.s = 21 + (-0.8 * (21 - colorAsHsl.s)) }; //bring towards 21; + colorAsHsl.l = 58 + (-0.8 * (58 - colorAsHsl.l)); //bring towards 58 + if(colorAsHsl.s > 5 && colorAsHsl.h !== 0) { colorAsHsl.s -= 4 }; + colorAsHsl.l += 2; + //console.log(colorAsHsl); + finalColor.push(convertHslObjects(colorAsHsl,"hex")); + //sandColorObject.push(convertHslObjects(colorAsHsl,"rgbjson")); + }; + return finalColor; + }; + function sedimentHslOffset(hslJsonColor) { + return {h: hslJsonColor.h - 4, s: hslJsonColor.s - 20, l: hslJsonColor.l - 25}; + }; + //Magmas + function makeMoltenColor(colorIn) { //Edited vanilla code + //console.log(colorIn); + var newcolor = colorIn; + var moltenColorFactors = [ [2,1.25,0.5], [2,1,0.5], [2,0.75,0] ]; + var colorList = []; + var colorObjectList = []; + // if newcolor is not an array, put it in an array + if (!(newcolor instanceof Array)) { newcolor = [newcolor]; } + newcolor = newcolor.map(x => convertColorFormats(x,"json")); + // for every color in the newcolor array, add a new color with the same value, but with the r and g values increased + for (var i = 0; i < newcolor.length; i++) { + var c = newcolor[i]; + for (var j = 0; j < moltenColorFactors.length; j++) { + var newc = moltenColorFactors[j]; + //console.log(c,newc); + r = Math.floor(c.r * newc[0]); + g = Math.floor(c.g * newc[1]); + b = Math.floor(c.b * newc[2]); + if (r > 255) {r = 255}; if (g > 255) {g = 255}; + //edit: to hex + var rHex = r.toString(16); if(rHex.length == 1) { rHex = "0" + rHex }; + var gHex = g.toString(16); if(gHex.length == 1) { gHex = "0" + gHex }; + var bHex = b.toString(16); if(bHex.length == 1) { bHex = "0" + bHex }; + colorList.push("#"+rHex+gHex+bHex); + } + } + return colorList; + } + //Magma vapors + function magmavaporizeToHex(colorIn) { + var color = colorIn; + if(!(color instanceof Array)) { + color = [color]; + }; + color = color.map(x => normalizeColorToHslObject(x)); + for(i = 0; i < color.length; i++) { + color[i].h += 5; + color[i].s -= 5; + color[i].l += 20; + }; + color = color.map(x => convertHslObjects(x,"hex")); + return color; + }; + function magmacloudizeToHex(colorIn) { + var color = colorIn; + if(!(color instanceof Array)) { + color = [color]; + }; + color = color.map(x => normalizeColorToHslObject(x)); + for(i = 0; i < color.length; i++) { + color[i].h += 5; + color[i].s -= 8; + color[i].l += 5; + }; + color = color.map(x => convertHslObjects(x,"hex")); + return color; + }; + function rockcloudizeToHex(colorIn) { + var color = colorIn; + if(!(color instanceof Array)) { + color = [color]; + }; + color = color.map(x => normalizeColorToHslObject(x)); + for(i = 0; i < color.length; i++) { + color[i].h -= 12; + color[i].s *= 0.12; + color[i].l -= 6; + }; + color = color.map(x => convertHslObjects(x,"hex")); + return color; + }; + //Generate an entire composition family at once + function redHotColorgen(colorIn,outputFormat="rgb") { + var color = colorIn; + //console.log(color); + if(!Array.isArray(color)) { + color = [color]; + }; + //console.log(color); + color = color.map(x => convertColorFormats(x,"json")); + //console.log(color); + for(i = 0; i < color.length; i++) { + var subcolor = color[i]; + //console.log(i); + subcolor.r += 48; + subcolor.r *= 1.7; + subcolor.g += 24; + subcolor.g *= 1.2; + subcolor.g -= 16; + subcolor.b -= 10; + subcolor.b *= 0.75; + for(colorlet in subcolor) { + subcolor[colorlet] = Math.round(rgbColorBound(subcolor[colorlet])); + }; + //console.log(color); + }; + //console.log(color); + color = color.map(x => convertColorFormats(x,outputFormat)); + if(color.length == 1) { color = color[0] }; + return color; + }; + var sands = ["sand", "dirt", "crimsoil", "rainbow_dirt"]; //Some sources suggest the existence of topsoil sediment, so for the purposes of sedimentary rock generation, dirt is now a sand /hj + var wetSands = ["wet_sand", "mud"]; + var sandSuspensions = []; + var sandSediments = ["radioactive_sand_sediment","clay_sediment"]; + var sandstones = ["radioactive_sandstone","shale"]; + var vaporizedMagmas = []; + var magmaClouds = []; + var rockClouds = []; + function nicffunc_getReactions(elemName) { + if(!(elements[elemName])) { + return null; + }; + if(!(elements[elemName].reactions)) { + return null; + }; + var reactions = elements[elemName].reactions; + if(structuredClone) { + return !!reactions ? structuredClone(reactions) : null; + } else { + return !!reactions ? JSON.parse(JSON.stringify(reactions)) : null; + }; + }; + function simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) { + if(pixel.exposedToAir) { return }; + if(pixel.temp > 200 && Math.random () < 0.0001) { + changePixel(pixel,elements[pixel.element].metamorphite) + }; + return + }; + function newIgneousCompositionFamily( + compositionFamilyName, + magmaViscosity, magmaDensity, vitriteCoolingRateThreshold, aphaniteCoolingRateThreshold, magmaBoilingPoint, + phaneriteName, phaneriteColor, phaneriteMeltingPoint, phaneriteDensity, + metaphaneriteName, metaphaneriteColor, metaphaneriteMeltingPoint, metaphaneriteDensity, + aphaniteName, aphaniteColor, aphaniteMeltingPoint, aphaniteDensity, + metaaphaniteName, metaaphaniteColor, metaaphaniteMeltingPoint, metaaphaniteDensity, + vesiculiteName, vesiculiteColor, vesiculiteMeltingPoint, vesiculiteDensity, + metavesiculiteName, metavesiculiteColor, metavesiculiteMeltingPoint, metavesiculiteDensity, + vitriteName, vitriteColor, vitriteMeltingPoint, vitriteDensity, + metavitriteName, metavitriteColor, metavitriteMeltingPoint, metavitriteDensity, + sandFormationReactionRegularSandCount, sandFormationReactionSpecificSandCount, + ) { + //console.log(compositionFamilyName,vesiculiteMeltingPoint,vitriteMeltingPoint); + //Auto names + //Sand + //gabbro_sand instead of rock_sand for rock's unique sand + var phaneriteSandName = compositionFamilyName == "mafic" ? "gabbro_sand" : phaneriteName + "_sand"; + var aphaniteSandName = aphaniteName + "_sand"; + var vesiculiteSandName = vesiculiteName + "_sand"; + var vitriteSandName = vitriteName + "_sand"; + var metaphaneriteSandName = metaphaneriteName + "_sand"; + var metaaphaniteSandName = metaaphaniteName + "_sand"; + var metavesiculiteSandName = metavesiculiteName + "_sand"; + var metavitriteSandName = metavitriteName + "_sand"; + //Solid rocks (rock walls) + //keep rock_wall to replace vanilla rock wall + var phaneriteWallName = compositionFamilyName == "mafic" ? "rock_wall" : phaneriteName + "_wall"; + var aphaniteWallName = aphaniteName + "_wall"; + var vesiculiteWallName = vesiculiteName + "_wall"; + var vitriteWallName = vitriteName + "_wall"; + var metaphaneriteWallName = metaphaneriteName + "_wall"; + var metaaphaniteWallName = metaaphaniteName + "_wall"; + var metavesiculiteWallName = metavesiculiteName + "_wall"; + var metavitriteWallName = metavitriteName + "_wall"; + //Gravel + //gravel instead of rock_gravel for normal gravel (as rock's unique gravel) + var phaneriteGravelName = compositionFamilyName == "mafic" ? "gravel" : phaneriteName + "_gravel"; + var aphaniteGravelName = aphaniteName + "_gravel"; + var vesiculiteGravelName = vesiculiteName + "_gravel"; + var vitriteGravelName = vitriteName + "_shard"; + var metaphaneriteGravelName = metaphaneriteName + "_gravel"; + var metaaphaniteGravelName = metaaphaniteName + "_gravel"; + var metavesiculiteGravelName = metavesiculiteName + "_gravel"; + var metavitriteGravelName = metavitriteName + "_shard"; + //Dust + //gabbro_dust instead of rock_dust for rock's unique dust + var phaneriteDustName = compositionFamilyName == "mafic" ? "gabbro_dust" : phaneriteName + "_dust"; + var aphaniteDustName = aphaniteName + "_dust"; + var vesiculiteDustName = vesiculiteName + "_dust"; + var vitriteDustName = vitriteName + "_dust"; + var metaphaneriteDustName = metaphaneriteName + "_dust"; + var metaaphaniteDustName = metaaphaniteName + "_dust"; + var metavesiculiteDustName = metavesiculiteName + "_dust"; + var metavitriteDustName = metavitriteName + "_dust"; + //Push future sand names and wet sand names to sand list for sandstone system generation + sands.push(phaneriteSandName); + sands.push(aphaniteSandName); + sands.push(vesiculiteSandName); + sands.push(vitriteSandName); + sands.push(metaphaneriteSandName); + sands.push(metaaphaniteSandName); + sands.push(metavesiculiteSandName); + sands.push(metavitriteSandName); + wetSands.push("wet_" + phaneriteSandName); + wetSands.push("wet_" + aphaniteSandName); + wetSands.push("wet_" + vesiculiteSandName); + wetSands.push("wet_" + vitriteSandName); + wetSands.push("wet_" + metaphaneriteSandName); + wetSands.push("wet_" + metaaphaniteSandName); + wetSands.push("wet_" + metavesiculiteSandName); + wetSands.push("wet_" + metavitriteSandName); + //Magma and magma derivative names + var magmaName = compositionFamilyName == "mafic" ? "magma" : compositionFamilyName + "_magma"; + var magmaCloudName = magmaName + "_cloud" + var rockCloudName = compositionFamilyName + "_rock_cloud" + //Create rocks, transplant existing reactions if they exist, add/change erosion reactions to match, and create corresponding physical variants + //Phanerite + var phaneriteOldReactions = nicffunc_getReactions(phaneriteName); + elements[phaneriteName] = { + color: phaneriteColor, + behavior: behaviors.POWDER, + category: "rock", + state: "solid", + tempHigh: phaneriteMeltingPoint, + stateHigh: magmaName, + density: phaneriteDensity, + hardness: 0.75, + breakInto: phaneriteGravelName, + _data: [compositionFamilyName,"phanerite","igneous_rock"], + metamorphite: metaphaneriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + if(phaneriteOldReactions) { + elements[phaneriteName].reactions = phaneriteOldReactions; + }; + //replace water rock-erosion reaction + elements.water.reactions[phaneriteName] = { "elem2": phaneriteGravelName, "chance": 0.00035 } + //create unique gravel + elements[phaneriteGravelName] = { + color: gravelizeToHex(phaneriteColor), + behavior: behaviors.POWDER, + category: "gravel", + state: "solid", + tempHigh: phaneriteMeltingPoint, + stateHigh: magmaName, + breakInto: phaneriteDustName, + density: phaneriteDensity * 0.55, + _data: [compositionFamilyName,"phanerite","igneous_gravel"], + metamorphite: metaphaneriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements[phaneriteDustName] = { + color: dustizeToHex(phaneriteName), + behavior: behaviors.GAS, + category: "rock dust", + state: "gas", + tempHigh: phaneriteMeltingPoint, + stateHigh: ["fire",magmaName], + reactions: { + [phaneriteDustName]: {elem1: phaneriteSandName, elem2: null, chance: 0.003}, + }, + density: airDensity + (phaneriteDensity / 1000), //unmeasured value + _data: [compositionFamilyName,"phanerite","dust"], + }; + //generate water gravel-erosion reaction using rock family's sand ratio + elements.water.reactions[phaneriteGravelName] = { "elem2": twoPartRepeatedArray(phaneriteSandName,sandFormationReactionSpecificSandCount,"sand",sandFormationReactionRegularSandCount), "chance": 0.0005 }; + //generate unique solid version + elements[phaneriteWallName] = { + color: phaneriteColor, + behavior: behaviors.WALL, + category: "solid rock", + state: "solid", + tempHigh: phaneriteMeltingPoint, + stateHigh: magmaName, + density: phaneriteDensity, + hardness: 0.8, + breakInto: phaneriteName, + _data: [compositionFamilyName,"phanerite","solid_igneous_rock"], + metamorphite: metaphaneriteWallName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[phaneriteWallName] = { "elem2": phaneriteName, "chance": 0.00035 } + //Sand and sand variants + elements[phaneriteSandName] = { + color: sandizeToHex(phaneriteName,"normal"), + behavior: behaviors.POWDER, + category: "sand", + state: "solid", + tempHigh: phaneriteMeltingPoint, + stateHigh: vitriteName, + density: phaneriteDensity * 0.595, + _data: [compositionFamilyName,"phanerite","particulate"], + metamorphite: metaphaneriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + //console.log(phaneriteSandName, elements[phaneriteSandName].color); + elements["wet_" + phaneriteSandName] = { + color: sandizeToHex(phaneriteName,"wet"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_" + phaneriteSandName, + tempLow: -50, + stateLow:"packed_" + phaneriteSandName, + density: phaneriteDensity * 0.595 + 150, + _data: [compositionFamilyName,"phanerite","wet_particulate"], + metamorphite: metaphaneriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements["packed_" + phaneriteSandName] = { + color: sandizeToHex(phaneriteName,"packed"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: phaneriteMeltingPoint, + stateHigh: vitriteName, + density: phaneriteDensity * 0.59, + breakInto: phaneriteSandName, + _data: [compositionFamilyName,"phanerite","packed_particulate"], + metamorphite: metaphaneriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[phaneriteSandName] = { + "elem1": null, "elem2": "wet_" + phaneriteSandName, + }; + //Metaphanerite + var metaphaneriteOldReactions = nicffunc_getReactions(phaneriteName); + elements[metaphaneriteName] = { + color: metaphaneriteColor, + behavior: behaviors.POWDER, + category: "rock", + state: "solid", + tempHigh: metaphaneriteMeltingPoint, + stateHigh: magmaName, + density: metaphaneriteDensity, + hardness: 0.75, + breakInto: metaphaneriteGravelName, + _data: [compositionFamilyName,"metaphanerite","metamorphic_rock"], + }; + if(metaphaneriteOldReactions) { + elements[metaphaneriteName].reactions = metaphaneriteOldReactions; + }; + //replace water rock-erosion reaction + elements.water.reactions[metaphaneriteName] = { "elem2": metaphaneriteGravelName, "chance": 0.00035 } + //create unique gravel + elements[metaphaneriteGravelName] = { + color: gravelizeToHex(metaphaneriteColor), + behavior: behaviors.POWDER, + category: "gravel", + state: "solid", + tempHigh: metaphaneriteMeltingPoint, + stateHigh: magmaName, + breakInto: metaphaneriteDustName, + density: metaphaneriteDensity * 0.55, + _data: [compositionFamilyName,"metaphanerite","metamorphic_gravel"], + }; + elements[metaphaneriteDustName] = { + color: dustizeToHex(metaphaneriteName), + behavior: behaviors.GAS, + category: "rock dust", + state: "gas", + tempHigh: metaphaneriteMeltingPoint, + stateHigh: ["fire",magmaName], + reactions: { + [metaphaneriteDustName]: {elem1: metaphaneriteSandName, elem2: null, chance: 0.003}, + }, + density: airDensity + (metaphaneriteDensity / 1000), //unmeasured value + _data: [compositionFamilyName,"metaphanerite","dust"], + }; + //generate water gravel-erosion reaction using rock family's sand ratio + elements.water.reactions[metaphaneriteGravelName] = { "elem2": twoPartRepeatedArray(metaphaneriteSandName,sandFormationReactionSpecificSandCount,"sand",sandFormationReactionRegularSandCount), "chance": 0.0005 }; + //generate unique solid version + elements[metaphaneriteWallName] = { + color: metaphaneriteColor, + behavior: behaviors.WALL, + category: "solid rock", + state: "solid", + tempHigh: metaphaneriteMeltingPoint, + stateHigh: magmaName, + density: metaphaneriteDensity, + hardness: 0.8, + breakInto: metaphaneriteName, + _data: [compositionFamilyName,"metaphanerite","solid_metamorphic_rock"], + }; + elements.water.reactions[metaphaneriteWallName] = { "elem2": metaphaneriteName, "chance": 0.00035 } + //Sand and sand variants + elements[metaphaneriteSandName] = { + color: sandizeToHex(metaphaneriteName,"normal"), + behavior: behaviors.POWDER, + category: "sand", + state: "solid", + tempHigh: metaphaneriteMeltingPoint, + stateHigh: vitriteName, + density: metaphaneriteDensity * 0.595, + _data: [compositionFamilyName,"metaphanerite","particulate"], + }; + //console.log(metaphaneriteSandName, elements[metaphaneriteSandName].color); + elements["wet_" + metaphaneriteSandName] = { + color: sandizeToHex(metaphaneriteName,"wet"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_" + metaphaneriteSandName, + tempLow: -50, + stateLow:"packed_" + metaphaneriteSandName, + density: metaphaneriteDensity * 0.595 + 150, + _data: [compositionFamilyName,"metaphanerite","wet_particulate"], + }; + elements["packed_" + metaphaneriteSandName] = { + color: sandizeToHex(metaphaneriteName,"packed"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: metaphaneriteMeltingPoint, + stateHigh: vitriteName, + density: metaphaneriteDensity * 0.59, + breakInto: metaphaneriteSandName, + _data: [compositionFamilyName,"metaphanerite","packed_particulate"], + }; + elements.water.reactions[metaphaneriteSandName] = { + "elem1": null, "elem2": "wet_" + metaphaneriteSandName, + }; + //Aphanite + var aphaniteOldReactions = nicffunc_getReactions(aphaniteName); + elements[aphaniteName] = { + color: aphaniteColor, + behavior: behaviors.POWDER, + category: "rock", + state: "solid", + tempHigh: aphaniteMeltingPoint, + stateHigh: magmaName, + density: aphaniteDensity, + hardness: 0.75, + breakInto: aphaniteGravelName, + _data: [compositionFamilyName,"aphanite","igneous_rock"], + metamorphite: metaaphaniteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + if(aphaniteOldReactions) { + elements[aphaniteName].reactions = aphaniteOldReactions; + }; + elements.water.reactions[aphaniteName] = { "elem2": aphaniteGravelName, "chance": 0.00035 } + elements[aphaniteGravelName] = { + color: gravelizeToHex(aphaniteColor), + behavior: behaviors.POWDER, + category: "gravel", + state: "solid", + tempHigh: aphaniteMeltingPoint, + stateHigh: magmaName, + breakInto: aphaniteDustName, + density: aphaniteDensity * 0.55, + _data: [compositionFamilyName,"aphanite","igneous_gravel"], + metamorphite: metaaphaniteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements[aphaniteDustName] = { + color: dustizeToHex(aphaniteName), + behavior: behaviors.GAS, + category: "rock dust", + state: "gas", + tempHigh: aphaniteMeltingPoint, + stateHigh: ["fire",magmaName], + reactions: { + [aphaniteDustName]: {elem1: aphaniteSandName, elem2: null, chance: 0.003}, + }, + density: airDensity + (aphaniteDensity / 1000), //unmeasured value + _data: [compositionFamilyName,"aphanite","dust"], + }; + elements.water.reactions[aphaniteGravelName] = { "elem2": twoPartRepeatedArray(aphaniteSandName,sandFormationReactionSpecificSandCount,"sand",sandFormationReactionRegularSandCount), "chance": 0.0005 }; + elements[aphaniteWallName] = { + color: aphaniteColor, + behavior: behaviors.WALL, + category: "solid rock", + state: "solid", + tempHigh: aphaniteMeltingPoint, + stateHigh: magmaName, + density: aphaniteDensity, + hardness: 0.8, + breakInto: aphaniteName, + _data: [compositionFamilyName,"aphanite","solid_igneous_rock"], + metamorphite: metaaphaniteWallName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[aphaniteWallName] = { "elem2": aphaniteName, "chance": 0.00035 } + //Sand and sand variants + elements[aphaniteSandName] = { + color: sandizeToHex(aphaniteName,"normal"), + behavior: behaviors.POWDER, + category: "sand", + state: "solid", + tempHigh: aphaniteMeltingPoint, + stateHigh: vitriteName, + density: aphaniteDensity * 0.595, + _data: [compositionFamilyName,"aphanite","particulate"], + metamorphite: metaaphaniteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements["wet_" + aphaniteSandName] = { + color: sandizeToHex(aphaniteName,"wet"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_" + aphaniteSandName, + tempLow: -50, + stateLow:"packed_" + aphaniteSandName, + density: aphaniteDensity * 0.595 + 150, + _data: [compositionFamilyName,"aphanite","wet_particulate"], + metamorphite: metaaphaniteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements["packed_" + aphaniteSandName] = { + color: sandizeToHex(aphaniteName,"packed"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: aphaniteMeltingPoint, + stateHigh: vitriteName, + density: aphaniteDensity * 0.59, + breakInto: aphaniteSandName, + _data: [compositionFamilyName,"aphanite","packed_particulate"], + metamorphite: metaaphaniteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[aphaniteSandName] = { + "elem1": null, "elem2": "wet_" + aphaniteSandName, + }; + //Metaaphanite + var metaaphaniteOldReactions = nicffunc_getReactions(phaneriteName); + elements[metaaphaniteName] = { + color: metaaphaniteColor, + behavior: behaviors.POWDER, + category: "rock", + state: "solid", + tempHigh: metaaphaniteMeltingPoint, + stateHigh: magmaName, + density: metaaphaniteDensity, + hardness: 0.75, + breakInto: metaaphaniteGravelName, + _data: [compositionFamilyName,"metaaphanite","metamorphic_rock"], + }; + if(metaaphaniteOldReactions) { + elements[metaaphaniteName].reactions = metaaphaniteOldReactions; + }; + //replace water rock-erosion reaction + elements.water.reactions[metaaphaniteName] = { "elem2": metaaphaniteGravelName, "chance": 0.00035 } + //create unique gravel + elements[metaaphaniteGravelName] = { + color: gravelizeToHex(metaaphaniteColor), + behavior: behaviors.POWDER, + category: "gravel", + state: "solid", + tempHigh: metaaphaniteMeltingPoint, + stateHigh: magmaName, + breakInto: metaaphaniteDustName, + density: metaaphaniteDensity * 0.55, + _data: [compositionFamilyName,"metaaphanite","metamorphic_gravel"], + }; + elements[metaaphaniteDustName] = { + color: dustizeToHex(metaaphaniteName), + behavior: behaviors.GAS, + category: "rock dust", + state: "gas", + tempHigh: metaaphaniteMeltingPoint, + stateHigh: ["fire",magmaName], + reactions: { + [metaaphaniteDustName]: {elem1: metaaphaniteSandName, elem2: null, chance: 0.003}, + }, + density: airDensity + (metaaphaniteDensity / 1000), //unmeasured value + _data: [compositionFamilyName,"metaaphanite","dust"], + }; + //generate water gravel-erosion reaction using rock family's sand ratio + elements.water.reactions[metaaphaniteGravelName] = { "elem2": twoPartRepeatedArray(metaaphaniteSandName,sandFormationReactionSpecificSandCount,"sand",sandFormationReactionRegularSandCount), "chance": 0.0005 }; + //generate unique solid version + elements[metaaphaniteWallName] = { + color: metaaphaniteColor, + behavior: behaviors.WALL, + category: "solid rock", + state: "solid", + tempHigh: metaaphaniteMeltingPoint, + stateHigh: magmaName, + density: metaaphaniteDensity, + hardness: 0.8, + breakInto: metaaphaniteName, + _data: [compositionFamilyName,"metaaphanite","solid_metamorphic_rock"], + }; + elements.water.reactions[metaaphaniteWallName] = { "elem2": metaaphaniteName, "chance": 0.00035 } + //Sand and sand variants + elements[metaaphaniteSandName] = { + color: sandizeToHex(metaaphaniteName,"normal"), + behavior: behaviors.POWDER, + category: "sand", + state: "solid", + tempHigh: metaaphaniteMeltingPoint, + stateHigh: vitriteName, + density: metaaphaniteDensity * 0.595, + _data: [compositionFamilyName,"metaaphanite","particulate"], + }; + //console.log(metaaphaniteSandName, elements[metaaphaniteSandName].color); + elements["wet_" + metaaphaniteSandName] = { + color: sandizeToHex(metaaphaniteName,"wet"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_" + metaaphaniteSandName, + tempLow: -50, + stateLow:"packed_" + metaaphaniteSandName, + density: metaaphaniteDensity * 0.595 + 150, + _data: [compositionFamilyName,"metaaphanite","wet_particulate"], + }; + elements["packed_" + metaaphaniteSandName] = { + color: sandizeToHex(metaaphaniteName,"packed"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: metaaphaniteMeltingPoint, + stateHigh: vitriteName, + density: metaaphaniteDensity * 0.59, + breakInto: metaaphaniteSandName, + _data: [compositionFamilyName,"metaaphanite","packed_particulate"], + }; + elements.water.reactions[metaaphaniteSandName] = { + "elem1": null, "elem2": "wet_" + metaaphaniteSandName, + }; + //Vesiculite + elements[vesiculiteName] = { + color: vesiculiteColor, + behavior: behaviors.POWDER, + category: "rock", + state: "solid", + tempHigh: vesiculiteMeltingPoint, + stateHigh: magmaName, + density: vesiculiteDensity, + hardness: 0.75, + breakInto: vesiculiteGravelName, + _data: [compositionFamilyName,"vesiculite","igneous_rock"], + maxColorOffset: 40, + metamorphite: metavesiculiteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[vesiculiteName] = { "elem2": vesiculiteGravelName, "chance": 0.00035 } + elements[vesiculiteGravelName] = { + color: gravelizeToHex(vesiculiteColor), + behavior: behaviors.POWDER, + category: "gravel", + state: "solid", + tempHigh: vesiculiteMeltingPoint, + stateHigh: magmaName, + breakInto: vesiculiteDustName, + density: vesiculiteDensity * 3.2, + _data: [compositionFamilyName,"vesiculite","igneous_gravel"], + maxColorOffset: 40, + metamorphite: metavesiculiteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements[vesiculiteDustName] = { + color: dustizeToHex(vesiculiteName), + behavior: behaviors.GAS, + category: "rock dust", + state: "gas", + tempHigh: vesiculiteMeltingPoint, + stateHigh: ["fire",magmaName], + reactions: { + [vesiculiteDustName]: {elem1: vesiculiteSandName, elem2: null, chance: 0.003}, + }, + density: airDensity + (vesiculiteDensity / 800), //unmeasured value + _data: [compositionFamilyName,"vesiculite","dust"], + }; + elements.water.reactions[vesiculiteGravelName] = { "elem2": twoPartRepeatedArray(vesiculiteSandName,sandFormationReactionSpecificSandCount,"sand",sandFormationReactionRegularSandCount), "chance": 0.0005 }; + elements[vesiculiteWallName] = { + color: vesiculiteColor, + behavior: behaviors.WALL, + category: "solid rock", + state: "solid", + tempHigh: vesiculiteMeltingPoint, + stateHigh: magmaName, + density: vesiculiteDensity, + hardness: 0.8, + breakInto: vesiculiteName, + _data: [compositionFamilyName,"vesiculite","solid_igneous_rock"], + maxColorOffset: 40, + metamorphite: metavesiculiteWallName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[vesiculiteWallName] = { "elem2": vesiculiteName, "chance": 0.00035 } + //Sand and sand variants + elements[vesiculiteSandName] = { + color: sandizeToHex(vesiculiteName,"normal"), + behavior: behaviors.POWDER, + category: "sand", + state: "solid", + tempHigh: vesiculiteMeltingPoint, + stateHigh: vitriteName, + density: vesiculiteDensity * 1.9, + _data: [compositionFamilyName,"vesiculite","particulate"], + metamorphite: metavesiculiteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements["wet_" + vesiculiteSandName] = { + color: sandizeToHex(vesiculiteName,"wet"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_" + vesiculiteSandName, + tempLow: -50, + stateLow:"packed_" + vesiculiteSandName, + density: vesiculiteDensity * 1.9 + 150, + _data: [compositionFamilyName,"vesiculite","wet_particulate"], + metamorphite: metavesiculiteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements["packed_" + vesiculiteSandName] = { + color: sandizeToHex(vesiculiteName,"packed"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: vesiculiteMeltingPoint, + stateHigh: vitriteName, + density: vesiculiteDensity * 1.888, + breakInto: vesiculiteSandName, + _data: [compositionFamilyName,"vesiculite","packed_particulate"], + metamorphite: metavesiculiteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[vesiculiteSandName] = { + "elem1": null, "elem2": "wet_" + vesiculiteSandName, + }; + //Metavesiculite + elements[metavesiculiteName] = { + color: metavesiculiteColor, + behavior: behaviors.POWDER, + category: "rock", + state: "solid", + tempHigh: metavesiculiteMeltingPoint, + stateHigh: magmaName, + density: metavesiculiteDensity, + hardness: 0.75, + breakInto: metavesiculiteGravelName, + _data: [compositionFamilyName,"metavesiculite","igneous_rock"], + maxColorOffset: 35 + }; + elements.water.reactions[metavesiculiteName] = { "elem2": metavesiculiteGravelName, "chance": 0.00035 } + elements[metavesiculiteGravelName] = { + color: gravelizeToHex(metavesiculiteColor), + behavior: behaviors.POWDER, + category: "gravel", + state: "solid", + tempHigh: metavesiculiteMeltingPoint, + stateHigh: magmaName, + breakInto: metavesiculiteDustName, + density: metavesiculiteDensity * 3.2, + _data: [compositionFamilyName,"metavesiculite","metamorphic_gravel"], + maxColorOffset: 35 + }; + elements[metavesiculiteDustName] = { + color: dustizeToHex(metavesiculiteName), + behavior: behaviors.GAS, + category: "rock dust", + state: "gas", + tempHigh: metavesiculiteMeltingPoint, + stateHigh: ["fire",magmaName], + reactions: { + [metavesiculiteDustName]: {elem1: metavesiculiteSandName, elem2: null, chance: 0.003}, + }, + density: airDensity + (metavesiculiteDensity / 800), //unmeasured value + _data: [compositionFamilyName,"metavesiculite","dust"], + }; + elements.water.reactions[metavesiculiteGravelName] = { "elem2": twoPartRepeatedArray(metavesiculiteSandName,sandFormationReactionSpecificSandCount,"sand",sandFormationReactionRegularSandCount), "chance": 0.0005 }; + elements[metavesiculiteWallName] = { + color: metavesiculiteColor, + behavior: behaviors.WALL, + category: "solid rock", + state: "solid", + tempHigh: metavesiculiteMeltingPoint, + stateHigh: magmaName, + density: metavesiculiteDensity, + hardness: 0.8, + breakInto: metavesiculiteName, + _data: [compositionFamilyName,"metavesiculite","solid_metamorphic_rock"], + maxColorOffset: 35 + }; + elements.water.reactions[metavesiculiteWallName] = { "elem2": metavesiculiteName, "chance": 0.00035 } + //Sand and sand variants + elements[metavesiculiteSandName] = { + color: sandizeToHex(metavesiculiteName,"normal"), + behavior: behaviors.POWDER, + category: "sand", + state: "solid", + tempHigh: metavesiculiteMeltingPoint, + stateHigh: vitriteName, + density: metavesiculiteDensity * 1.9, + _data: [compositionFamilyName,"metavesiculite","particulate"], + }; + elements["wet_" + metavesiculiteSandName] = { + color: sandizeToHex(metavesiculiteName,"wet"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_" + metavesiculiteSandName, + tempLow: -50, + stateLow:"packed_" + metavesiculiteSandName, + density: metavesiculiteDensity * 1.9 + 150, + _data: [compositionFamilyName,"metavesiculite","wet_particulate"], + }; + elements["packed_" + metavesiculiteSandName] = { + color: sandizeToHex(metavesiculiteName,"packed"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: metavesiculiteMeltingPoint, + stateHigh: vitriteName, + density: metavesiculiteDensity * 1.888, + breakInto: metavesiculiteSandName, + _data: [compositionFamilyName,"metavesiculite","packed_particulate"], + }; + elements.water.reactions[metavesiculiteSandName] = { + "elem1": null, "elem2": "wet_" + metavesiculiteSandName, + }; + //Vitrite + elements[vitriteName] = { + color: vitriteColor, + behavior: behaviors.POWDER, + category: "rock", + state: "solid", + tempHigh: vitriteMeltingPoint, + stateHigh: magmaName, + density: vitriteDensity, + hardness: 0.75, + breakInto: vitriteGravelName, + _data: [compositionFamilyName,"vitrite","igneous_rock"], + metamorphite: metavitriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[vitriteName] = { "elem2": vitriteGravelName, "chance": 0.00035 } + elements[vitriteGravelName] = { + color: gravelizeToHex(vitriteColor), + behavior: behaviors.POWDER, + category: "gravel", + state: "solid", + tempHigh: vitriteMeltingPoint, + stateHigh: magmaName, + breakInto: vitriteDustName, + density: vitriteDensity * 0.55, + _data: [compositionFamilyName,"vitrite","glass_shard"], + metamorphite: metavitriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements[vitriteDustName] = { + color: dustizeToHex(vitriteName), + behavior: behaviors.GAS, + category: "rock dust", + state: "gas", + tempHigh: vitriteMeltingPoint, + stateHigh: ["fire",magmaName], + reactions: { + [vitriteDustName]: {elem1: vitriteSandName, elem2: null, chance: 0.003}, + }, + density: airDensity + (vitriteDensity / 1000), //unmeasured value + _data: [compositionFamilyName,"vitrite","dust"], + }; + elements.water.reactions[vitriteGravelName] = { "elem2": twoPartRepeatedArray(vitriteSandName,sandFormationReactionSpecificSandCount,"sand",sandFormationReactionRegularSandCount), "chance": 0.0005 }; + elements[vitriteWallName] = { + color: vitriteColor, + behavior: behaviors.WALL, + category: "solid rock", + state: "solid", + tempHigh: vitriteMeltingPoint, + stateHigh: magmaName, + density: vitriteDensity, + hardness: 0.8, + breakInto: vitriteName, + _data: [compositionFamilyName,"vitrite","solid_igneous_rock"], + metamorphite: metavitriteWallName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[vitriteWallName] = { "elem2": vitriteName, "chance": 0.00035 } + //Sand and sand variants + elements[vitriteSandName] = { + color: sandizeToHex(vitriteName,"normal"), + behavior: behaviors.POWDER, + category: "sand", + state: "solid", + tempHigh: vitriteMeltingPoint, + stateHigh: vitriteName, + density: vitriteDensity * 0.595, + _data: [compositionFamilyName,"vitrite","particulate"], + metamorphite: metavitriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements["wet_" + vitriteSandName] = { + color: sandizeToHex(vitriteName,"wet"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_" + vitriteSandName, + tempLow: -50, + stateLow:"packed_" + vitriteSandName, + density: vitriteDensity * 0.595 + 150, + _data: [compositionFamilyName,"vitrite","wet_particulate"], + metamorphite: metavitriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements["packed_" + vitriteSandName] = { + color: sandizeToHex(vitriteName,"packed"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: vitriteMeltingPoint, + stateHigh: vitriteName, + density: vitriteDensity * 0.59, + breakInto: vitriteSandName, + _data: [compositionFamilyName,"vitrite","packed_particulate"], + metamorphite: metavitriteName, + onTryMoveInto: function(pixel,otherPixel) { simplifiedSingleMetamorphiteMetamorphismOTMI(pixel,otherPixel) } + }; + elements.water.reactions[vitriteSandName] = { + "elem1": null, "elem2": "wet_" + vitriteSandName, + }; + //Metavitrite + elements[metavitriteName] = { + color: metavitriteColor, + behavior: behaviors.POWDER, + category: "rock", + state: "solid", + tempHigh: metavitriteMeltingPoint, + stateHigh: magmaName, + density: metavitriteDensity, + hardness: 0.75, + breakInto: metavitriteGravelName, + _data: [compositionFamilyName,"metavitrite","metamorphic_rock"], + }; + elements.water.reactions[metavitriteName] = { "elem2": metavitriteGravelName, "chance": 0.00035 } + elements[metavitriteGravelName] = { + color: gravelizeToHex(metavitriteColor), + behavior: behaviors.POWDER, + category: "gravel", + state: "solid", + tempHigh: metavitriteMeltingPoint, + stateHigh: magmaName, + breakInto: metavitriteDustName, + density: metavitriteDensity * 0.55, + _data: [compositionFamilyName,"metavitrite","glass_shard"], + }; + elements[metavitriteDustName] = { + color: dustizeToHex(metavitriteName), + behavior: behaviors.GAS, + category: "rock dust", + state: "gas", + tempHigh: metavitriteMeltingPoint, + stateHigh: ["fire",magmaName], + reactions: { + [metavitriteDustName]: {elem1: metavitriteSandName, elem2: null, chance: 0.003}, + }, + density: airDensity + (metavitriteDensity / 1000), //unmeasured value + _data: [compositionFamilyName,"metavitrite","dust"], + }; + elements.water.reactions[metavitriteGravelName] = { "elem2": twoPartRepeatedArray(metavitriteSandName,sandFormationReactionSpecificSandCount,"sand",sandFormationReactionRegularSandCount), "chance": 0.0005 }; + elements[metavitriteWallName] = { + color: metavitriteColor, + behavior: behaviors.WALL, + category: "solid rock", + state: "solid", + tempHigh: metavitriteMeltingPoint, + stateHigh: magmaName, + density: metavitriteDensity, + hardness: 0.8, + breakInto: metavitriteName, + _data: [compositionFamilyName,"metavitrite","solid_metamorphic_rock"], + }; + elements.water.reactions[metavitriteWallName] = { "elem2": metavitriteName, "chance": 0.00035 } + //Sand and sand variants + elements[metavitriteSandName] = { + color: sandizeToHex(metavitriteName,"normal"), + behavior: behaviors.POWDER, + category: "sand", + state: "solid", + tempHigh: metavitriteMeltingPoint, + stateHigh: metavitriteName, + density: metavitriteDensity * 0.595, + _data: [compositionFamilyName,"metavitrite","particulate"], + }; + elements["wet_" + metavitriteSandName] = { + color: sandizeToHex(metavitriteName,"wet"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_" + metavitriteSandName, + tempLow: -50, + stateLow:"packed_" + metavitriteSandName, + density: metavitriteDensity * 0.595 + 150, + _data: [compositionFamilyName,"metavitrite","wet_particulate"], + }; + elements["packed_" + metavitriteSandName] = { + color: sandizeToHex(metavitriteName,"packed"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: metavitriteMeltingPoint, + stateHigh: metavitriteName, + density: metavitriteDensity * 0.59, + breakInto: metavitriteSandName, + _data: [compositionFamilyName,"metavitrite","packed_particulate"], + }; + elements.water.reactions[metavitriteSandName] = { + "elem1": null, "elem2": "wet_" + metavitriteSandName, + }; + //Magma + var magmaOldReactions = nicffunc_getReactions(magmaName); + var magmaOldColor = elements.magma.color; + elements[magmaName] = { + reactions: { + "ash": { "elem1": "molten_slag", "elem2": null }, + "dust": { "elem1": "molten_slag", "elem2": null }, + }, + _magmaCoolingPassToElement: { + vitreous: [vitriteCoolingRateThreshold,vitriteName], + aphanitic: [aphaniteCoolingRateThreshold,aphaniteName], + phaneritic: [Infinity,phaneriteName], + meltingPoints: { + vitreous: vitriteMeltingPoint, + vesicular: vesiculiteMeltingPoint, + aphanitic: aphaniteMeltingPoint, + phaneritic: phaneriteMeltingPoint, + }, + }, + tick: function(pixel) { + var coolingInfo = elements[pixel.element]._magmaCoolingPassToElement; + magmaRateBasedCooling( + pixel, + Math.min( + coolingInfo.meltingPoints.vitreous, + coolingInfo.meltingPoints.vesicular, + coolingInfo.meltingPoints.aphanitic, + coolingInfo.meltingPoints.phaneritic + ) - 20, + coolingInfo.vitreous[1], + coolingInfo.vitreous[0], + coolingInfo.aphanitic[1], + coolingInfo.aphanitic[0], + coolingInfo.phaneritic[1] + ); + }, + "color": makeMoltenColor(phaneriteColor), + "behavior": behaviors.MOLTEN, + "temp": Math.max(phaneriteMeltingPoint,metaphaneriteMeltingPoint,aphaniteMeltingPoint,metaaphaniteMeltingPoint,vesiculiteMeltingPoint,metavesiculiteMeltingPoint,vitriteMeltingPoint,metavitriteMeltingPoint) + 100, + "tempLow": -Infinity, //cosmetic info + "stateLow": [aphaniteName,phaneriteName,vitriteName], + "tempHigh": magmaBoilingPoint, + "stateHigh": "vaporized_" + magmaName, + "viscosity": magmaViscosity, + "hidden": true, + "state": "liquid", + "category": "magmas", + "density": magmaDensity, + "_data": [compositionFamilyName,"magma","liquid"], + }; + if(magmaOldReactions) { + elements[magmaName].reactions = magmaOldReactions; + }; + if(magmaName == "magma") { + elements.magma.color = magmaOldColor; + }; + elements[magmaName].reactions.foam = { "elem1": vesiculiteName, "elem2": vesiculiteName }; + elements["vaporized_" + magmaName] = { + color: magmavaporizeToHex(elements[magmaName].color), + behavior: behaviors.GAS, + reactions: { + ["vaporized_" + magmaName]: { elem1: null, elem2: magmaCloudName, chance:0.3, "y":[0,15], "setting":"clouds" } + }, + density: magmaDensity * 0.0028, + temp: magmaBoilingPoint + 100, + tempLow: magmaBoilingPoint, + stateLow: magmaName, + category: "magma vapor", + state: "gas", + hidden: true, + _data: [compositionFamilyName,"magma","vaporized"], + }; + vaporizedMagmas.push("vaporized_" + magmaName); + elements[magmaCloudName] = { + color: magmacloudizeToHex(elements[magmaName].color), + behavior: [ + "XX|XX|XX", + "M1%7|CH:" + magmaName + "%0.05|M1%7", + "XX|XX|XX", + ], + density: magmaDensity * 0.0021, + temp: magmaBoilingPoint + 100, + tempLow: Math.min(phaneriteMeltingPoint,metaphaneriteMeltingPoint,aphaniteMeltingPoint,metaaphaniteMeltingPoint,vesiculiteMeltingPoint,metavesiculiteMeltingPoint,vitriteMeltingPoint,metavitriteMeltingPoint) - 50, + stateLow: rockCloudName, + category: "magma cloud", + state: "gas", + _data: [compositionFamilyName,"magma","cloud"], + }; + magmaClouds.push(magmaName + "_cloud"); + elements[rockCloudName] = { + color: rockcloudizeToHex(elements[magmaName].color), + behavior: [ + "XX|XX|XX", + "M1%7|CH:" + [aphaniteName,aphaniteGravelName,aphaniteDustName].join(",") + "%0.05|M1%7", + "XX|XX|XX", + ], + density: magmaDensity * 0.0024, + temp: Math.min(phaneriteMeltingPoint,metaphaneriteMeltingPoint,aphaniteMeltingPoint,metaaphaniteMeltingPoint,vesiculiteMeltingPoint,metavesiculiteMeltingPoint,vitriteMeltingPoint,metavitriteMeltingPoint) - 300, + tempHigh: Math.min(phaneriteMeltingPoint,metaphaneriteMeltingPoint,aphaniteMeltingPoint,metaaphaniteMeltingPoint,vesiculiteMeltingPoint,metavesiculiteMeltingPoint,vitriteMeltingPoint,metavitriteMeltingPoint) - 50, + stateHigh: magmaCloudName, + category: "rock cloud", + state: "gas", + _data: [compositionFamilyName,"magma","cloud"], + }; + 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] = { + color: gravelizeToHex(elements[elementName].color), + behavior: behaviors.POWDER, + state: "solid", + }; + if(density !== null) { + if(density == "auto") { + elements[newName].density = (elements[elementName].density ?? 2000) * 0.55; + } else { + elements[newName].density = density; + }; + }; + if(category !== null) { + elements[newName].category = category; + }; + if(tempHigh !== null) { + if(tempHigh == "auto") { + elements[newName].tempHigh = elements[elementName].tempHigh; + } else { + elements[newName].tempHigh = tempHigh; + }; + }; + if(stateHigh !== null) { + if(stateHigh == "auto") { + elements[newName].stateHigh = elements[elementName].stateHigh; + } else { + elements[newName].stateHigh = stateHigh; + }; + }; + if(breakInto !== null) { + elements[newName].breakInto = breakInto; + }; + if(addBreakIntoToSourceElement) { + if(!elements[elementName].breakInto) { + elements[elementName].breakInto = newName; + } else { + if(!(elements[elementName].breakInto instanceof Array)) { + elements[elementName].breakInto = [elements[elementName].breakInto]; + }; + elements[elementName].breakInto.push(newName); + }; + }; + return elements[newName]; + }; + function makeSandstoningElements(sandName) { + var sandInfo = elements[sandName]; + if(!sandInfo) { + throw new Error("No such element '" + sandName + "'"); + }; + var suspensionName, wetSandName, sedimentName, sandstoneName, dustName; + switch(sandName) { + case "dirt": + suspensionName = "muddy_water"; + wetSandName = "mud"; //needs special code to not generate a wet dirt and instead use vanilla mud as the wet "sand" here + sedimentName = "soil_sediment"; + sandstoneName = "soilstone"; //differentiated from mudstone, which is actually the *packed* dirt (analogously to sand's relationship to packed sand) + dustName = "dirt_dust"; + break; + case "rainbow_dirt": + suspensionName = "rainbow_muddy_water"; + wetSandName = "rainbow_mud"; + sedimentName = "rainbow_soil_sediment"; + sandstoneName = "rainbow_soilstone"; + dustName = "rainbow_dirt_dust"; + break; + case "crimsoil": + suspensionName = "crimmuddy_water"; + wetSandName = "crimmud"; + sedimentName = "crimsoil_sediment"; + sandstoneName = "crimsoilstone"; + dustName = "crimsoil_dust"; + break; + case 143: //sorry, i had to + default: + suspensionName = sandName + "y_water"; + wetSandName = "wet_" + sandName; + sedimentName = sandName + "_sediment"; + sandstoneName = sandName + "stone"; + dustName = sandName.replace("_sand","_dust"); + }; + //console.log(sandName,suspensionName); + //Water reaction to pick up the fine material (this is very simplified) + elements.water.reactions[wetSandName] = { + "elem1": suspensionName, + "elem2": [wetSandName,wetSandName,wetSandName,suspensionName], + chance: 0.01 + }; + //Sediment suspension + //Color generation + var sandColor = sandInfo.color; + if(!(sandColor instanceof Array)) { + sandColor = [sandColor]; + }; + var waterColor = "#2167ff"; + //console.log(sandColor); + suspensionColor = sandColor.map(sandSubcolor => lerpColors(waterColor,sandSubcolor,"hex",weight1=0.5)); //lerp all with half water + var sedimentColor = sandColor.map(sandSubcolor => convertHslObjects(sedimentHslOffset(normalizeColorToHslObject(sandSubcolor)),"hex")); + //console.log(sandInfo); + elements[suspensionName] = { + color: suspensionColor, + behavior: behaviors.LIQUID, + tempHigh: 100, + stateHigh: ["steam","steam",sandName], + //tempLow: 0, + //stateLow: "sandy_ice", + category: "suspensions", + reactions: { + "dirt": { // React with (water reacts with dirt to make mud) + "elem1": [null,null,wetSandName], // First element transforms into; in this case, water deletes itself + "elem2": "mud", // Second element transforms into; in this case, dirt turns to mud + }, + "water": { "elem1":"water", "elem2":suspensionName, "chance":0.025 }, //swap reaction + "sand": { "elem1": [null,null,wetSandName], "elem2": wetSandName, }, + [suspensionName]: { "elem1":"water", "elem2":sedimentName, "chance": 0.001 }, + [wetSandName]: { "elem1": "water", "elem2":sedimentName, "chance": 0.0005 }, + //"salt": { "elem1": "salt_water", "elem2": null }, + //"sugar": { "elem1": "sugar_water", "elem2": null, }, + "dust": { "elem1": "dirty_water", "elem2": null, }, + "ash": { "elem1": "dirty_water", "elem2": null, }, + "cyanide": { "elem1": "dirty_water", "elem2": null, }, + //"carbon_dioxide": { "elem1": "seltzer", "elem2": null, "oneway":true }, + "sulfur": { "elem1": "dirty_water", "elem2": null, }, + "rat": { "elem1": "dirty_water", chance:0.005 }, + "plague": { "elem1": "dirty_water", "elem2": null, }, + "rust": { "elem1": "dirty_water", chance:0.005 }, + "fallout": { "elem1": "dirty_water", chance:0.25 }, + "radiation": { "elem1": "dirty_water", chance:0.25 }, + "uranium": { "elem1": "dirty_water", chance:0.25 }, + "rotten_meat": { "elem1": "dirty_water", chance:0.25 }, + "quicklime": { "elem1": [null,null,wetSandName], "elem2": "slaked_lime", }, + "rock": { "elem2": wetSandName, "chance": 0.00035 }, + "ruins": { "elem2": "rock", "chance": 0.00035 }, + "mudstone": { "elem2": "mud", "chance": 0.00035 }, + //"methane": { "elem1":"primordial_soup", "elem2":"primordial_soup", tempMin:60, charged:true }, + //"ammonia": { "elem1":"primordial_soup", "elem2":"primordial_soup", tempMin:60, charged:true }, + "fly": { "elem2":"dead_bug", "chance":0.1, "oneway":true }, + "firefly": { "elem2":"dead_bug", "chance":0.1, "oneway":true }, + "bee": { "elem2":"dead_bug", "chance":0.05, "oneway":true }, + "stink_bug": { "elem2":"dead_bug", "chance":0.1, "oneway":true }, + }, + state: "liquid", + density: 1000 + (sandInfo.density * 0.06), + conduct: 0.02, + stain: 0.01, + _data: [sandInfo?._data?.[0] ?? "unknown", sandInfo?._data?.[1] ?? "unknown", "suspension"], + } + if(elements[dustName]) { + elements[dustName].reactions ??= {}; + elements[dustName].reactions.water = { + elem1: null, elem2: suspensionName + }; + }; + //Sediment element where lithification code resides + elements[sedimentName] = { + hidden: true, + color: sedimentColor, + hardness: 0.2, + tick: function(pixel) { + if(!tryMove(pixel,pixel.x,pixel.y+1)) { + var newPixel = pixelMap[pixel.x]?.[pixel.y+1]; + if(!newPixel) { + return; + }; + var newElement = newPixel.element; + var thisSandName = pixel.element.slice(0,-9); //ABCD_sand_sediment - _sediment + var thisWetSandName = "wet_" + thisSandName; + var thisSuspensionName = pixel.element.slice(0,-9) + "y_water"; + var sandstoneName = thisSandName + "stone"; + if(Math.random() < 0.005 && ["sediment","wet_particulate"].includes(elements[newElement]._data?.[2])) { //0.5% chance to swap with wet + swapPixels(pixel,newPixel); + return; + }; + if(Math.random() < 0.001 && elements[newElement]._data?.[2] == "particulate") { //0.1% chance to give water away + var newWetParticulateName = elements.water.reactions[newElement]?.elem2; + if(!newWetParticulateName) { return }; + if(elements[thisWetSandName] && elements[newWetParticulateName]) { + //console.log(thisSandName); + //console.log(newWetSandName); + changePixel(pixel,thisSandName,false); + changePixel(newPixel,newWetParticulateName,false); + }; + }; + if(Math.random() < 0.001 && newElement == "water") { //0.1% chance to give dissolve in water + if(elements[thisSuspensionName]) { + //console.log(thisSuspensionName); + changePixel(pixel,thisSuspensionName,false); + changePixel(newPixel,thisSuspensionName,false); + }; + }; + if(Math.random() < 0.001 && elements[newElement]._data?.[2] == "suspension") { //0.1% chance to sediment a suspension + var newSedimentName = elements[newPixel.element].reactions[newPixel.element].elem2; + //console.log(newSedimentName); + if(elements[newSedimentName]) { + changePixel(newPixel,newSedimentName,false); + }; + }; + }; + //console.log(sandstoneName); + sedimentation(pixel,sandstoneName) + }, + tempHigh: sandInfo.tempHigh, + stateHigh: sandInfo.stateHigh, + category: "sediment", + state: "solid", + density: elements[wetSandName].density + 150, + breakInto: sandName, + _data: [sandInfo?._data?.[0] ?? "unknown", sandInfo?._data?.[1] ?? "unknown", "sediment"] + }; + //Final rock + //console.log(sandName); + elements[sandstoneName] = { + color: sandstonizeToHex(sandName), //["#b27853", "#d1a784", "#d1a784", "#d4996e"] + behavior: behaviors.WALL, + tempHigh: function() { + switch(sandName) { + case "dirt": + return elements.dry_dirt.tempHigh; + case "crimsoil": + return elements.crimsoil.tempHigh; + case "rainbow_dirt": + return elements.rainbow_dirt.tempHigh; + default: + return elements[sandName].tempHigh + } + }(), + stateHigh: function() { + switch(sandName) { + case "sand": + return "glass"; + case "gabbro_sand": + return "magma"; + case "dirt": + return "hot_soilstone"; + case "rainbow_dirt": + return "hot_rainbow_dirt"; + case "crimsoil": + return "hot_crimsoilstone"; + default: + var elementNameAssumingSandNameIsOfTheFormatFOOBAR_sand = sandName.slice(0,-5); + var correspondingElementObject = elements[elementNameAssumingSandNameIsOfTheFormatFOOBAR_sand]; + if(!correspondingElementObject) { + throw new Error(`makeSandstoningElements: Could not generate the sandstone for ${sandName} because this script couldn't find a corresponding parent rock ${elementNameAssumingSandNameIsOfTheFormatFOOBAR_sand}`); + }; + return correspondingElementObject.stateHigh + } + }(), //apparently you can do this + category: "solid rock", + state: "solid", + density: sandInfo.density * 1.5, //wide range + hardness: 0.5, + breakInto: sandName, + maxColorOffset: 30, + _data: [sandInfo?._data?.[0] ?? "unknown", (sandInfo?._data?.[1] ?? "unknown") + "_sandstone", "sedimentary_rock"], + }; + }; + function makeNonSandSedimentationElements(particulateName,suspensionName,rockName) { + var particulateInfo = elements[particulateName]; + if(!particulateInfo) { + throw new Error("No such element '" + particulateName + "'"); + }; + var sedimentName = particulateName + "_sediment"; + //Water reaction to pick up the fine material (this is very simplified) + elements.water.reactions[particulateName] = { + "elem1": suspensionName, + "elem2": [particulateName,particulateName,particulateName,suspensionName], + chance: 0.001 + }; + //Sediment suspension + //Color generation + var particulateColor = particulateInfo.color; + if(!(particulateColor instanceof Array)) { + particulateColor = [particulateColor]; + }; + var waterColor = "#2167ff"; + //console.log(particulateColor); + suspensionColor = particulateColor.map(sandSubcolor => lerpColors(waterColor,sandSubcolor,"hex",weight1=0.5)); //lerp all with half water + var sedimentColor = particulateColor.map(sandSubcolor => convertHslObjects(sedimentHslOffset(normalizeColorToHslObject(sandSubcolor)),"hex")); + //console.log(particulateInfo); + elements[suspensionName] = { + color: suspensionColor, + behavior: behaviors.LIQUID, + tempHigh: 100, + stateHigh: ["steam","steam",particulateName], + category: "suspensions", + reactions: { + "dirt": { // React with (water reacts with dirt to make mud) + "elem1": [null,null,particulateName], // First element transforms into; in this case, water deletes itself + "elem2": "mud", // Second element transforms into; in this case, dirt turns to mud + }, + "water": { "elem1":"water", "elem2":suspensionName, "chance":0.025 }, //swap reaction + "particulateName": { "elem1": [null,null,particulateName], "elem2": particulateName, }, + //"salt": { "elem1": "salt_water", "elem2": null }, + //"sugar": { "elem1": "sugar_water", "elem2": null, }, + "dust": { "elem1": "dirty_water", "elem2": null, }, + "ash": { "elem1": "dirty_water", "elem2": null, }, + "cyanide": { "elem1": "dirty_water", "elem2": null, }, + //"carbon_dioxide": { "elem1": "seltzer", "elem2": null, "oneway":true }, + "sulfur": { "elem1": "dirty_water", "elem2": null, }, + "rat": { "elem1": "dirty_water", chance:0.005 }, + "plague": { "elem1": "dirty_water", "elem2": null, }, + "rust": { "elem1": "dirty_water", chance:0.005 }, + "fallout": { "elem1": "dirty_water", chance:0.25 }, + "radiation": { "elem1": "dirty_water", chance:0.25 }, + "uranium": { "elem1": "dirty_water", chance:0.25 }, + "rotten_meat": { "elem1": "dirty_water", chance:0.25 }, + "quicklime": { "elem1": [null,null,particulateName], "elem2": "slaked_lime", }, + "rock": { "elem2": particulateName, "chance": 0.00035 }, + "ruins": { "elem2": "rock", "chance": 0.00035 }, + "mudstone": { "elem2": "mud", "chance": 0.00035 }, + //"methane": { "elem1":"primordial_soup", "elem2":"primordial_soup", tempMin:60, charged:true }, + //"ammonia": { "elem1":"primordial_soup", "elem2":"primordial_soup", tempMin:60, charged:true }, + "fly": { "elem2":"dead_bug", "chance":0.1, "oneway":true }, + "firefly": { "elem2":"dead_bug", "chance":0.1, "oneway":true }, + "bee": { "elem2":"dead_bug", "chance":0.05, "oneway":true }, + "stink_bug": { "elem2":"dead_bug", "chance":0.1, "oneway":true }, + }, + state: "liquid", + density: 1000 + (particulateInfo.density * 0.06), + conduct: 0.02, + stain: 0.01, + _data: [particulateInfo._data[0], particulateInfo._data[1], "suspension"], + } + elements[suspensionName].reactions[suspensionName] = { "elem1":"water", "elem2":sedimentName, "chance": 0.001 }, + elements[suspensionName].reactions[particulateName] = { "elem1": "water", "elem2":sedimentName, "chance": 0.0005 }, + //Sediment element where lithification code resides + elements[sedimentName] = { + hidden: true, + color: sedimentColor, + hardness: 0.2, + tick: function(pixel) { + if(!tryMove(pixel,pixel.x,pixel.y+1)) { + var newPixel = pixelMap[pixel.x]?.[pixel.y+1]; + if(!newPixel) { + return; + }; + var newElement = newPixel.element; + var particulateName = pixel.element.slice(0,-9); //ABCD_sand_sediment - _sediment + var thisSuspensionName = elements[pixel.element]._sedimentationPassToElement.correspondingSuspension + var rockName = elements[pixel.element]._sedimentationPassToElement.finalRock; + if(Math.random() < 0.005 && ["sediment","wet_particulate"].includes(elements[newElement]._data?.[2])) { //0.5% chance to swap with wet + swapPixels(pixel,newPixel); + return; + }; + if(Math.random() < 0.001 && elements[newElement]._data?.[2] == "particulate") { //0.1% chance to give water away + var newWetParticulateName = elements.water.reactions[newElement].elem2; + if(elements[particulateName] && elements[newWetParticulateName]) { + changePixel(pixel,particulateName,false); + changePixel(newPixel,newWetParticulateName,false); + }; + }; + if(Math.random() < 0.001 && newElement == "water") { //0.1% chance to dissolve in water + if(elements[thisSuspensionName]) { + //console.log(thisSuspensionName); + changePixel(pixel,thisSuspensionName,false); + changePixel(newPixel,thisSuspensionName,false); + }; + }; + if(Math.random() < 0.001 && elements[newElement]._data?.[2] == "suspension") { //0.1% chance to sediment a suspension + //new sediment should be the elem2 of a suspension's reaction with itself + var newSedimentName = elements[newPixel.element].reactions[newPixel.element].elem2; + //console.log(newSedimentName); + if(elements[newSedimentName]) { + changePixel(newPixel,newSedimentName,false); + }; + }; + }; + //console.log(rockName); + sedimentation(pixel,rockName) + }, + tempHigh: particulateInfo.tempHigh, + stateHigh: particulateInfo.stateHigh, + _sedimentationPassToElement: { + finalRock: rockName, + correspondingSuspension: suspensionName, + }, + category: "sediment", + state: "solid", + density: elements[particulateName].density + 150, + breakInto: particulateName, + _data: [particulateInfo._data[0], particulateInfo._data[1], "sediment"], + }; + //Final rock + //console.log(particulateName); + if(rockName !== "limestone") { + elements[rockName] = { + color: sandstonizeToHex(particulateName), //["#b27853", "#d1a784", "#d1a784", "#d4996e"] + behavior: behaviors.WALL, + tempHigh: particulateInfo.tempHigh, + stateHigh: particulateInfo.stateHigh, + category: "solid rock", + state: "solid", + density: particulateInfo.density * 1.5, //wide range + hardness: 0.7, + breakInto: particulateName, + maxColorOffset: 30, + _data: [particulateInfo._data[0], "rock", "sedimentary_rock"], + }; + }; + }; + newPowder("calcite","#f5ecd0",2711,825,["carbon_dioxide","quicklime"],"calcium_carbonate_dust"); + newPowder("aragonite","#e3c58d",2830,825,["carbon_dioxide","quicklime"],"calcium_carbonate_dust"); + newPowder("vaterite","#e8ebd8",2540,825,["carbon_dioxide","quicklime"],"calcium_carbonate_dust"); + newPowder("calcium_carbonate_dust","#f7f7f5",2930,825,["carbon_dioxide","quicklime"]); + //i forgot what data[1]s mean and at this point it doesn't really matter + elements.calcite._data = ["calcium","crystalline","mineral"]; + elements.aragonite._data = ["calcium","crystalline","mineral"]; + elements.vaterite._data = ["calcium","crystalline","mineral"]; + elements.calcium_carbonate_dust._data = ["calcium","crystalline","particulate"]; + elements.limestone._data = ["calcium", "sedimentary", "sedimentary_rock"]; + elements.aragonite.tick = function(pixel) { + if(Math.random() < (0.001 + Math.max(0,(pixel.temp - 300) / 100))) { + changePixel(pixel,"calcite",false); + }; + }; + elements.vaterite.tick = function(pixel) { + if(Math.random() < (0.01 + Math.max(0,(pixel.temp - 30) / 10))) { + changePixel(pixel,"calcite",false); + }; + }; + makeNonSandSedimentationElements("calcium_carbonate_dust","calcium_carbonate_solution","limestone") + var calcitoids = ["calcite","aragonite","vaterite"]; + for(i = 0; i < calcitoids.length; i++) { + var mineral = calcitoids[i]; + elements.water.reactions[mineral] = { + "elem1":"calcium_carbonate_solution", + "elem2":[mineral,mineral,mineral,"calcium_carbonate_solution"], + "chance":0.004 + }; + elements.seltzer.reactions[mineral] = { + "elem1":"calcium_carbonate_solution", + "elem2":[mineral,mineral,mineral,"calcium_carbonate_solution"], + "chance":0.02 + }; + }; + runAfterLoad(function() { + for(i = 0; i < sands.length; i++) { + switch(sands[i]) { + case "dirt": + sandSuspensions.push("muddy_water"); + sandSediments.push("soil_sediment"); + sandstones.push("soilstone"); + break; + case "crimsoil": + sandSuspensions.push("crimmuddy_water"); + sandSediments.push("crimsoil_sediment"); + sandstones.push("crimsoilstone"); + break; + case "rainbow_dirt": + sandSuspensions.push("rainbow_muddy_water"); + sandSediments.push("rainbow_soil_sediment"); + sandstones.push("rainbow_soilstone"); + break; + default: + sandSuspensions.push(sands[i] + "y_water"); + sandSediments.push(sands[i] + "_sediment"); + sandstones.push(sands[i] + "stone"); + }; + makeSandstoningElements(sands[i]); + }; + elements.sand.category = "sand"; + elements.gravel.category = "gravel"; + elements.rock.category = "rock"; + elements.wet_sand.category = "wet sand"; + elements.packed_sand.category = "packed sand"; + elements.clay._data = ["clay","clay","particulate"], + makeNonSandSedimentationElements("clay","clay_water","shale"); + elements.shale.color = ["#787b80","#535557","#695e58", "#696969", "#6b5d5b"]; + elements.shale.maxColorOffset = 15; + elements.shale.tempHigh = 200; //shale does get baked (https://pubs.usgs.gov/pp/0108a/report.pdf), but it feels wrong for it to happen so soon + elements.shale.behavior = behaviors.POWDER; + elements.shale.category = "solid rock"; + for(fei = 0; fei < sandSuspensions.length; fei++) { + var suspensionToAddReactionTo = sandSuspensions[fei]; + //console.log(suspensionToAddReactionTo); + if(!(elements[suspensionToAddReactionTo])) { + console.error(`MI ${suspensionToAddReactionTo}`); + continue + }; + elements[suspensionToAddReactionTo].reactions ??= {}; + for(sei = 0; sei < sandSuspensions.length; sei++) { + var suspensionToReactWith = sandSuspensions[sei]; + var firstSedimentName = suspensionToAddReactionTo.replace("y_water","_sediment"); + var secondSedimentName = suspensionToReactWith.replace("y_water","_sediment"); + elements[suspensionToAddReactionTo].reactions[suspensionToReactWith] = { + elem1: "water", "elem2": [firstSedimentName,secondSedimentName], "chance": 0.001, + }; + }; + for(sej = 0; sej < wetSands.length; sej++) { + var wetSandToReactWith = wetSands[sej]; + var firstSedimentName = suspensionToAddReactionTo.replace("y_water","_sediment"); + var secondSedimentName = wetSandToReactWith.replace("wet_","") + "_sediment"; + elements[suspensionToAddReactionTo].reactions[wetSandToReactWith] = { + elem1: "water", "elem2": [firstSedimentName,secondSedimentName], "chance": 0.0005, + }; + }; + }; + //lithificationElements = sandSediments.concat(sandstones); + for(fei = 0; fei < vaporizedMagmas.length; fei++) { + var vaporToAddReactionTo = vaporizedMagmas[fei]; + //console.log(vaporToAddReactionTo); + elements[vaporToAddReactionTo].reactions ??= {}; + for(sei = 0; sei < vaporizedMagmas.length; sei++) { + var vaporToReactWith = vaporizedMagmas[sei]; + var firstCloudName = vaporToAddReactionTo.replace("vaporized_","") + "_cloud"; + var secondCloudName = vaporToReactWith.replace("vaporized_","") + "_cloud"; + elements[vaporToAddReactionTo].reactions[vaporToReactWith] = { + elem1: null, "elem2": [firstCloudName,secondCloudName], "chance": 0.3, y: [0,15] + }; + }; + for(sej = 0; sej < magmaClouds.length; sej++) { + var cloudToReactWith = magmaClouds[sej]; + var firstCloudName = vaporToAddReactionTo.replace("vaporized_","") + "_cloud"; + elements[vaporToAddReactionTo].reactions[cloudToReactWith] = { + elem1: firstCloudName, "chance": 0.4, y: [0,15] + }; + }; + }; + newPowder("silica","#faf9f0",2196,1713).hardness = 0.7; + elements.silica.reactions = { + intermediate_felsic_magma: { elem1: "felsic_magma", elem2: "felsic_magma", chance: 0.9 }, + intermediate_magma: { elem1: "intermediate_felsic_magma", elem2: "intermediate_felsic_magma", chance: 0.9 }, + magma: { elem1: "intermediate_magma", elem2: "intermediate_felsic_magma", chance: 0.9 }, + ultramafic_magma: { elem1: "magma", elem2: "magma", chance: 0.9 }, + }; + elements.molten_silica = { + tempHigh: 2950, + viscosity: 1e14, //idk lol + reactions: { + intermediate_felsic_magma: { elem1: "felsic_magma", elem2: "felsic_magma", chance: 0.9 }, + intermediate_magma: { elem1: "intermediate_felsic_magma", elem2: "intermediate_felsic_magma", chance: 0.9 }, + magma: { elem1: "intermediate_magma", elem2: "intermediate_felsic_magma", chance: 0.9 }, + ultramafic_magma: { elem1: "magma", elem2: "magma", chance: 0.9 }, + }, + }; + elements.felsic_magma.reactions ??= {}; + elements.felsic_magma.reactions.intermediate_magma = { + elem1: "intermediate_felsic_magma", elem2: "intermediate_felsic_magma", chance: 0.8, + }; + elements.intermediate_felsic_magma.reactions ??= {}; + elements.intermediate_felsic_magma.reactions.magma = { + elem1: "intermediate_magma", elem2: "intermediate_magma", chance: 0.7, + }; + elements.felsic_magma.reactions ??= {}; + elements.felsic_magma.reactions.magma = { //mafic magma + elem1: "intermediate_magma", elem2: "intermediate_magma", chance: 0.7, + }; + elements.felsic_magma.reactions ??= {}; + elements.felsic_magma.reactions.ultramafic_magma = { //mafic magma + elem1: "intermediate_magma", elem2: "magma", chance: 0.6, + }; + elements.intermediate_magma.reactions ??= {}; + elements.intermediate_magma.reactions.ultramafic_magma = { //mafic magma + elem1: "magma", elem2: "magma", chance: 0.6, + }; + elements.molten_dirt.tempHigh = 3313; + var rockStateHigh = JSON.parse(JSON.stringify(vaporizedMagmas)); + //only real magmas in dirt + if(rockStateHigh.includes("vaporized_nellish_magma")) { + rockStateHigh.splice(rockStateHigh.indexOf("vaporized_nellish_magma")); + }; + if(rockStateHigh.includes("vaporized_rainbow_magma")) { + rockStateHigh.splice(rockStateHigh.indexOf("vaporized_rainbow_magma")); + }; + if(rockStateHigh.includes("vaporized_crimson_magma")) { + rockStateHigh.splice(rockStateHigh.indexOf("vaporized_crimson_magma")); + }; + if(rockStateHigh.includes("vaporized_blackpinkinitic_magma")) { + rockStateHigh.splice(rockStateHigh.indexOf("vaporized_blackpinkinitic_magma")); + }; + elements.molten_dirt.stateHigh = rockStateHigh; //assuming mixture + for(var sandIndex in sands) { + sandIndex = parseInt(sandIndex); + var sandName = sands[sandIndex]; + var usedSandColor = elements[sandName].color; + if(!(usedSandColor instanceof Array)) { + usedSandColor = [usedSandColor]; + }; + var newSandyClayColor = usedSandColor.map(subcolor => lerpColors(subcolor,elements.clay.color,"hex",weight1=0.5)); + var newSandyLoamColor = []; + for(var dirtSubcolorIndex in elements.dirt.color) { + dirtSubcolorIndex = parseInt(dirtSubcolorIndex); + dirtSubcolor = elements.dirt.color[dirtSubcolorIndex]; + //for each dirt subcolor, to the final new color concatenate the result of mapping each of the sand color's subcolors to one of dirt's subcolors + newSandyLoamColor = newSandyLoamColor.concat(usedSandColor.map(subcolor => lerpColors(subcolor,dirtSubcolor,"hex",weight1=0.6))); + }; + var newLoamySandColor = []; + for(var dirtSubcolorIndex in elements.dirt.color) { + dirtSubcolorIndex = parseInt(dirtSubcolorIndex); + dirtSubcolor = elements.dirt.color[dirtSubcolorIndex]; + //for each dirt subcolor, to the final new color concatenate the result of mapping each of the sand color's subcolors to one of dirt's subcolors + newLoamySandColor = newLoamySandColor.concat(usedSandColor.map(subcolor => lerpColors(subcolor,dirtSubcolor,"hex",weight1=0.4))); + }; + var newSandyClayLoamColor = newSandyLoamColor.map(subcolor => lerpColors(subcolor,elements.clay.color,"hex",weight1=2/3)); + var newSandyLoamColor = elements.dirt.color.map(subcolor => lerpColors(subcolor,elements.clay.color,"hex",weight1=0.5)); + } + var newClayLoamColor = elements.dirt.color.map(subcolor => changeHue(lerpColors(subcolor,elements.clay.color,"hex",weight1=0.5),0.9,"multiply","hex")); + var newDryClayLoamColor = newClayLoamColor.map(x => changeSaturation(changeLightness(x,15,"add","hsljson"),0.9,"multiply","hex")); + newPowder("clay_loam",newClayLoamColor,1500,100,"dry_clay_loam",["dirt","clay_soil"]); + elements.clay_loam._data = ["clay_loam","soil","particulate"]; + //manual addition due to autogen fuckery and i don't feel like calling in runAfterAutogen + elements.molten_clay_loam = { + "behavior": behaviors.MOLTEN, + "hidden": true, + "state": "liquid", + "category": "states", + "color": [ "rgb(255,217,75)", "rgb(255,174,75)", "rgb(255,130,0)", "rgb(255,205,70)", "rgb(255,164,70)", "rgb(255,123,0)", "rgb(255,202,68)", "rgb(255,162,68)", "rgb(255,121,0)", "rgb(255,210,72)", "rgb(255,168,72)", "rgb(255,126,0)" ].map(x => convertColorFormats(x,"hex")), + "tempLow": 1250, + "stateLow": "dry_clay_loam", + "density": 1350, + "viscosity": 10000 + }; + newPowder("dry_clay_loam",newDryClayLoamColor,1500,1250,"molten_clay_loam",["dry_dirt","clay_soil"]); + elements.dry_clay_loam.data = ["clay_loam","dry_soil","particulate"]; + //newPowder(name,color,density=null,tempHigh=null,stateHigh=null,breakInto=null) + }); + //Terrain + //Soils + //Wet + //Wet Clay + //TODO + //Wet Silty clay + //TODO + //Wet Silty Clay Loam + //TODO + //Wet Silty Loam + //TODO + //Wet Silt + //TODO + //Wet Clay Loam + //TODO + //Wet Medium Loam + //Mud exists + //Wet Sandy Clay + //TODO + //Wet Sandy Clay Loam + //TODO + //Wet Sandy Loam + //TODO + //Wet Loamy Sand + //TODO + //Wet Sand + //Wet Sand exists + //Permafrost + //Clay Permafrost + //TODO + //Silty clay Permafrost + //TODO + //Silty Clay Loam Permafrost + //TODO + //Silty Loam Permafrost + //TODO + //Silt Permafrost + //TODO + //Clay Loam Permafrost + //TODO + //Medium Loam Permafrost + //Permafrost exists + //Sandy Clay Permafrost + //TODO + //Sandy Clay Loam Permafrost + //TODO + //Sandy Loam Permafrost + //TODO + //Loamy Sand Permafrost + //TODO + //Sand Permafrost + //TODO + //Radioactive (unmoved/TODO) + //Dry + //Radioactive Clay + //Clay exists + //Radioactive Silty clay + //TODO + //Radioactive Silty Clay Loam + //TODO + //Radioactive Silty Loam + //TODO + //Radioactive Silt + //TODO + //Radioactive Clay Loam + //Clay Soil exists + //Radioactive Medium Loam + //Dirt exists + //Radioactive Sandy Clay + //TODO + //Radioactive Sandy Clay Loam + //TODO + //Radioactive Sandy Loam + //TODO + //Radioactive Loamy Sand + //TODO + //Radioactive Sand + //Sand exists + //Wet + //Radioactive Wet Clay + //TODO + //Radioactive Wet Silty clay + //TODO + //Radioactive Wet Silty Clay Loam + //TODO + //Radioactive Wet Silty Loam + //TODO + //Radioactive Wet Silt + //TODO + //Radioactive Wet Clay Loam + //TODO + //Radioactive Wet Medium Loam + //Mud exists + //Radioactive Wet Sandy Clay + //TODO + //Radioactive Wet Sandy Clay Loam + //TODO + //Radioactive Wet Sandy Loam + //TODO + //Radioactive Wet Loamy Sand + //TODO + //Radioactive Wet Sand + //Wet Sand exists + //Permafrost + //Radioactive Clay Permafrost + //TODO + //Radioactive Silty clay Permafrost + //TODO + //Radioactive Silty Clay Loam Permafrost + //TODO + //Radioactive Silty Loam Permafrost + //TODO + //Radioactive Silt Permafrost + //TODO + //Radioactive Clay Loam Permafrost + //TODO + //Radioactive Medium Loam Permafrost + //Permafrost exists + //Radioactive Sandy Clay Permafrost + //TODO + //Radioactive Sandy Clay Loam Permafrost + //TODO + //Radioactive Sandy Loam Permafrost + //TODO + //Radioactive Loamy Sand Permafrost + //TODO + //Radioactive Sand Permafrost + //TODO + //Rocks + //Igneous + //Felsic + newIgneousCompositionFamily( + "felsic", + 1e12, 2200, -85, -20, 2850, + //Not much data on metamorphites besides gneiss + "granite", ["#F3C3AD", "#F0AB75", "#DDA888", "#BD927E", "#998473", "#5C5E53", "#BD8366"], 1215, 2691, + "gneiss", ["#C5C1B4", "#605A5E", "#424449", "#EDECE9", "#73503A", "#92866F"], 1215, 2750, + "rhyolite", ["#A67153","#BF967E","#D9B5A0","#8C533E","#C99F86","#C5997E","#BB8A69"], 800, 1254, + "metarhyolite", ["#C0C7D3","#CAD0D9","#AEB7B7","#728189","#798B96","#B09F98","#515155"], 800, 2584, //https://www.researchgate.net/figure/Physical-properties-of-the-metarhyolites_tbl2_245002845 also there are pictures yay + "pumice", ["#ebe1c3", "#ada386", "#f0bd9e", "#ab846c", "#bfbebd", "#75726f", "#f5e595", "#ab9e60", "#ad683d", "#633d25", "#6e6d6d", "#3b3a39"], 1350, 641, + //it is said to flatten out and have smaller vesicles but the color is pulled out of my ass + "metapumice", ["#a6a295", "#787a6f", "#8f847e", "#917c6e", "#858382", "#696460", "#8a6d5c", "#6e5749", "#5c5b55", "#53594f"], 1350, 2328, + vitreousFelsicName, ["#252422", "#171616", "#161915", "#161018"], 1000, 2488, + //if metamorphism sometimes involves recrystallization and obsidian is the way it is due to being amorphous and lacking a crystal structure then perhaps obsidian might be somewhat like granite with its new crystals + "meta" + vitreousFelsicName, ["#453f3c", "#1f1a18", "#36342b", "#1c1519", "#3d3133", "#1f1b1a", "#453a32"], 1000, 2513, + 7,3 + ); + elements.water.reactions.obsidian_shard.elem2 = ["obsidian_sand","obsidian_sand","obsidian_sand","sand","sand"] + elements.obsidian_sand.color = ["#3b3730", "#211e1e", "#293321", "#31133b"]; + elements.obsidian_shard.desc = 'crushed obsidian my beloved'; + //Intermediate felsic + newIgneousCompositionFamily( + "intermediate_felsic", + 1e10, 2320, -95, -23, 2900, + "granodiorite", ["#B1AB9D", "#262001", "#A6A292", "#D6C5BC", "#F2F2F2", "#DED8C2", "#978871", "#A8AAA7"], 1277, 2644, //Color from image: By Rudolf Pohl - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=7788350; melting point made-up/interpolated from granite and diorite; last 2 digits of density are made up again + "metagranodiorite", ["#F3EDDC","#F0ECD8","#EDECDC","#D0C9A9","#BDB192","#BBA27A","#86744E","#323026","#262417","#202012"], 1277, 2711, + "dacite", ["#D9CCC5", "#F2E9E4", "#877670", "#A69B97"], 1050, 2654, //https://books.google.ca/books?id=ObUPAAAAIAAJ&pg=PA181&lpg=PA181&dq=dacite+specific+gravity&source=bl&ots=qn8B4sirWi&sig=Wp_MHqPuUGPNQobcuNP5c5wqkpU&hl=en&sa=X&ei=cimtUaH8Eab7yAH8joDABQ#v=onepage&q=dacite%20specific%20gravity&f=false + "metadacite", ["#91847d", "#e0c9bc", "#735a56", "#bfa59b", "#696563"], 1050, 2727, + "intermediate_pumice", ["#dbd4bd", "#b5ad94", "#e3ceb6", "#bda891", "#c2c2c2", "#a1a1a1", "#e6c8a1", "#b8a48c"], 1190, 991, + "intermediate_metapumice", ["#777868", "#5a5c51", "#82756f", "#6e6057", "#96918f", "#70665e"], 1190, 2623, + vitreousInterfelsicName, ["#4f4b42", "#474646", "#4a4d49", "#342f36"], 1040, 2640, + "meta" + vitreousInterfelsicName, ["#3d3c39", "#696262", "#313630", "#625966"], 1040, 2772, + 6,4 + ); + //Intermediate + newIgneousCompositionFamily( + "intermediate", + 1e8, 2450, -105, -26, 2950, + "diorite", ["#E1E1E1","#B0A696","#707271","#434459","#242424"], 1300, 2822, //Extracted from image and blended; Michael C. Rygel - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=31124755 https://commons.wikimedia.org/w/index.php?curid=7788350; last 2 digits made up again + "metadiorite", ["#D1D2D7","#C3C2AF","#AEACB1","#A1A29D","#C3C4BC","#C3C9CA","#B5AEA4","#B6AC91","#AEA582","#5A6992"], 1300, 2929, + "andesite", ["#6F7575", "#C5C9CB", "#818787", "#797F7F", "#B5B9BA", "#6D7371", "#909696"], 1215, 2474, //https://books.google.ca/books?id=ObUPAAAAIAAJ&pg=PA181&lpg=PA181&dq=dacite+specific+gravity&source=bl&ots=qn8B4sirWi&sig=Wp_MHqPuUGPNQobcuNP5c5wqkpU&hl=en&sa=X&ei=cimtUaH8Eab7yAH8joDABQ#v=onepage&q=dacite%20specific%20gravity&f=false + "metaandesite", ["#5b5c5b", "#a3a6a2", "#6e665e", "#b39b92", "#756763", "#91817d", "#73524d"], 1215, 2553, + "scoria", ["#594545", "#573b31", "#522e28"], 1085, 2550, + "metascoria", ["#403835","#75574c","#4f302b","#8a7c75"], 1085, 2670, + vitreousIntermediateName, ["#636059", "#707070", "#5f615f", "#504b52"], 1085, 2710, + "meta" + vitreousIntermediateName, ["#4a4845", "#75716e", "#43453f", "#5e4b53", "#66554d"], 1085, 2744, + 5,5 + ); + elements.scoria_gravel.density = 2790; + //Mafic + elements.rock.name = "Gabbro"; + elements.rock.tempHigh = 1200; + elements.rock.density = 3300; + elements.rock.breakInto = ["gravel"]; + elements.gravel.breakInto = ["gabbro_dust"]; + elements.gravel.name = "Gabbro Gravel"; + delete elements.wet_sand.reactions.gravel; + elements.rock._data = ["mafic","phanerite","igneous_rock"], + elements.magma.name = "mafic magma"; + elements.magma.density = 2650; + elements.magma.category = "magmas"; + elements.magma._magmaCoolingPassToElement = { + vitreous: [-115,vitreousMaficName], + aphanitic: [-29,"basalt"], + phaneritic: [Infinity,"gabbro"], + meltingPoints: { + vitreous: 1200, + vesicular: 1298, + aphanitic: 1122, + phaneritic: 1200, + }, + }, + elements.magma.tick = function(pixel) { + magmaRateBasedCooling(pixel,1180,vitreousMaficName,-115,"basalt",-29,"rock"); + }; + elements.magma.temp = 1400; + elements.magma.tempLow = -Infinity; + elements.magma.stateLow = ["basalt","gabbro",vitreousMaficName] + elements.magma.reactions ??= {}; + elements.magma.reactions.foam = { "elem1": "mafic_scoria", "elem2": "mafic_scoria" }; + elements.magma._data = ["mafic","magma","liquid"], + elements.basalt.tempHigh = 1122; + elements.basalt.density = 2949; + elements.basalt.breakInto = "basalt_gravel", + elements.rock._data = ["mafic","phanerite","igneous_rock"], + elements.gravel._data = ["mafic","phanerite","igneous_gravel"], + elements.basalt._data = ["mafic","aphanite","igneous_rock"], + elements.sand._data = ["silica","crystalline","particulate"], + elements.wet_sand._data = ["silica","crystalline","wet_particulate"], + elements.packed_sand._data = ["silica","crystalline","packed_particulate"], + newIgneousCompositionFamily( + "mafic", + 10000, 2200, -115, -29, 3000, + "rock", ["#808080","#4f4f4f","#949494"], 1474, 3300, + "metagabbro", ["#F6F6F5", "#EEEFEC", "#E7E6DD","#C0BBA3","#A9ABA7", "#8A8C8C", "#727271", "#61635F", "#595A59", "#454641", "#4E514A"], 1474, 3350, + "basalt", ["#2e2e2e","#333333","#3d3d3d"], 1122, 2949, + "metabasalt", ["#292e26","#474d3d","#2e2e29","#4a574f"], 1122, 3070, + "mafic_scoria", ["#756666", "#695751", "#737272"], 1298, 2717, + "mafic_metascoria", ["#856d6d","#4f4139","#8c8373","#494a39"], 1298, 2773, + vitreousMaficName, ["#6e615d", "#706767", "#6a6b63", "#6e5e68"], 1200, 2900, + "meta" + vitreousMaficName, ["#7a685d", "#3c4235", "#7c7869", "#3f3138"], 1200, 2991, + 3,7 + ); + elements.mafic_scoria.tempHigh = 1298; + elements.mafic_scoria.stateHigh = "magma"; + elements.mafic_scoria_gravel.density = 2993; + elements.basalt.behavior = behaviors.STURDYPOWDER; + elements.metabasalt.behavior = behaviors.STURDYPOWDER; + //Ultramafic + newIgneousCompositionFamily( + "ultramafic", + 800, 2800, -125, -32, 3050, + "peridotite", ["#848a5e","#68785b","#8a9967","#3f403d","#33312e","#4c4f45"], 1400, 3347, //appr from https://agupubs.onlinelibrary.wiley.com/doi/abs/10.1029/GL003i009p00509#:~:text=Abstract,and%20the%20bulk%20rock%20analyses. + "metaperidotite", ["#7d604f","#959c98","#454443","#363432","#5e4840"], 1400, 3404, + "komatiite", ["#6e7d6e","#858c8a","#768270","#767a77"], 1600, 3100, + "metakomatiite", ["#AEB5AE","#A9B8B5","#7B8881","#858B87","#949F97","#66655d","#5e4d48"], 1600, 3066, + "ultramafic_scoria", ["#636555", "#6a6751", "#828382"], 1400, 2924, + "ultramafic_metascoria", ["#574e47", "#6a7357", "#3b3430", "#4d4939"], 1400, 3003, + vitreousUltramaficName, ["#6e6d5e", "#5f6659", "#54574b", "#665d55"], 1300, 3200, + "meta" + vitreousUltramaficName, ["#4a443d", "#5e5e4a", "#3a4036", "#4d524f"], 1300, 3266, + 2,8 + ); + elements.ultramafic_scoria_gravel.density = 3132; + elements.basalt_gravel._data = ["mafic","aphanite","igneous_gravel"], + elements.limestone_gravel = { + color: ["#c7baa1", "#e8d8b7", "#fcf3d7", "#fffce6"], + behavior: behaviors.POWDER, + tempHigh: 825, + stateHigh: "quicklime", + category: "gravel", + state: "solid", + density: 1380, + hardness: 0.16, + breakInto: ["quicklime","calcium","dust"], + } + elements.limestone.breakInto = "limestone_gravel"; + elements.worm.reactions.limestone_gravel = { "elem2":"calcium", "chance":0.1 }, + elements.acid.reactions.limestone_gravel = { "elem1":"neutral_acid", "elem2":null }, + newPowder("aluminum_oxide","#f2f2f2",3987,2072).hardness = 0.93; + elements.molten_aluminum_oxide = { + tempHigh: 2977, + }; + newPowder("sulfur_trioxide","#ededed",1995,16.9).reactions = { + water: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + steam: { elem1: "sulfuric_acid", elem2: "acid" }, + ice: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + snow: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + packed_snow: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + slush: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + }; elements.sulfur_trioxide.temp = 10; + elements.molten_sulfur_trioxide = { + color: "#c0c0c0", + behavior: behaviors.LIQUID, + density: 1920, + viscosity: 5, //idk idc + tempHigh: 45, + reactions: { + water: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, //no H2SO4, hydronium doesn't really seem to be its own substance + steam: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + ice: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + snow: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + packed_snow: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + slush: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + }, + }; + elements.sulfur_trioxide_gas = { + color: "#c0c0c0", + density: 2.3, //idk idc + reactions: { + water: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, //no H2SO4, hydronium doesn't really seem to be its own substance + steam: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + ice: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + snow: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + packed_snow: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + slush: { elem1: "sulfuric_acid", elem2: "sulfuric_acid" }, + }, + }; + var tempaaa = { + sulfur_trioxide: "value doesn't matter", + molten_sulfur_trioxide: "stan loona", + sulfur_trioxide_gas: "aaaaaaa" + }; + delete elements.concrete.tempHigh; + delete elements.concrete.stateHigh; + if(elements.hanging_concrete) { + delete elements.hanging_concrete.tempHigh; + delete elements.hanging_concrete.stateHigh; + }; + if(elements.crumbling_concrete) { + delete elements.crumbling_concrete.tempHigh; + delete elements.crumbling_concrete.stateHigh; + }; + if(elements.attach_concrete) { + delete elements.attach_concrete.tempHigh; + delete elements.attach_concrete.stateHigh; + }; + delete elements.quicklime.stateHigh; + elements.quicklime.tempHigh = 2572; + elements.molten_quicklime = { + tempHigh: 2850 + }; + elements.concrete.properties ??= {}; + elements.concrete.properties.composition = "mafic"; + newConcreteTick = function(pixel) { + pixel.composition ??= "mafic"; + pixel.wet ??= Math.random() < 0.03 ? 1 : 0; + pixel.frozen ??= false; + pixel.didColorChange ??= 0; + pixel.lastTemperatures ??= []; + pixel.lastTemperatures.push(pixel.temp); //due to how it's structured, last temp will always equal pixel.temp; + while(pixel.lastTemperatures.length > 2) { + pixel.lastTemperatures.shift(); + }; + var overallTemperatureChangeRate = (pixel.temp - pixel.lastTemperatures[0]) / (pixel.lastTemperatures.length - 1); + var magmaName = (pixel.composition == "mafic") ? "magma" : pixel.composition + "_magma"; + var magmaTempHigh = Math.max(...Object.values(elements[magmaName]._magmaCoolingPassToElement.meltingPoints)); + if(pixel.wet && !pixel.frozen && pixel.temp < 0) { + if(Math.random() < (pixel.wet / 25)) { //if unfrozen, crack apart (freezing damage) with small chance, and then mark survivors as frozen + //console.log("freezing"); + explodeAt(pixel.x,pixel.y,2,"ice"); + if(!pixel) { //if deleted + return; + }; + if(pixel.element !== "ice") { //chance to just change to ice after the fact if survivor + if(Math.random() < (pixel.wet / 8)) { + changePixel(pixel,"ice",false); + }; + }; + if(pixel.element !== "concrete") { //if changed, stop execution + return; + }; + }; + //if unchanged and undeleted, mark as frozen + pixel.frozen = true; + }; + if(pixel.frozen && pixel.temp > 0) { + pixel.frozen = false; + }; + //console.log(pixel.temp,pixel.didColorChange); + if(pixel.temp > 300 && pixel.didColorChange < 1) { + if(Math.random() < 0.02) { breakPixel(pixel) }; + var colorWasHSL = pixel.color.startsWith("hsl"); + var oldColor = convertHslObjects(normalizeColorToHslObject(pixel.color),"rgbjson"); + if(oldColor == null) { oldColor = pixelColorPick(pixel) }; + oldColor.r += 81/2; + oldColor.g += 60/2; + oldColor.b += 56/2; + pixel.color = convertHslObjects(normalizeColorToHslObject(oldColor),colorWasHSL ? "hsl" : "rgb"); + pixel.didColorChange = 1; + } else if(pixel.temp > 500 && pixel.didColorChange < 2) { + if(Math.random() < 0.04) { breakPixel(pixel) }; + var colorWasHSL = pixel.color.startsWith("hsl"); + var oldColor = convertHslObjects(normalizeColorToHslObject(pixel.color),"rgbjson"); + if(oldColor == null) { oldColor = pixelColorPick(pixel) }; + oldColor.r += 81/4; + oldColor.g += 60/4; + oldColor.b += 56/4; + pixel.color = convertHslObjects(normalizeColorToHslObject(oldColor),colorWasHSL ? "hsl" : "rgb"); + pixel.didColorChange = 2; + } else if(pixel.temp > 700 && pixel.didColorChange < 3) { + if(Math.random() < 0.06) { breakPixel(pixel) }; + var colorWasHSL = pixel.color.startsWith("hsl"); + var oldColor = convertHslObjects(normalizeColorToHslObject(pixel.color),"rgbjson"); + if(oldColor == null) { oldColor = pixelColorPick(pixel) }; + oldColor.r += 81/7; + oldColor.g += 60/7; + oldColor.b += 56/7; + pixel.color = convertHslObjects(normalizeColorToHslObject(oldColor),colorWasHSL ? "hsl" : "rgb"); + pixel.didColorChange = 3; + } else if(pixel.temp > 900 && pixel.didColorChange < 4) { + if(Math.random() < 0.08) { breakPixel(pixel) }; + var colorWasHSL = pixel.color.startsWith("hsl"); + var oldColor = convertHslObjects(normalizeColorToHslObject(pixel.color),"rgbjson"); + if(oldColor == null) { oldColor = pixelColorPick(pixel) }; + oldColor.r += 81/8; + oldColor.g += 60/8; + oldColor.b += 56/8; + pixel.color = convertHslObjects(normalizeColorToHslObject(oldColor),colorWasHSL ? "hsl" : "rgb"); + pixel.didColorChange = 4; + }; + pixel.role ??= randomChoice(["aggregate","aggregate","aggregate","aggregate","sand","sand","cement"]); + if(pixel.role == "cement") { + var chooserValue = Math.random(); + if(chooserValue < 0.65) { + pixel.role = "lime"; + } else if(chooserValue < 0.85) { + pixel.role = "silica"; + } else if(chooserValue < 0.91) { + pixel.role = "alumina"; + } else if(chooserValue < 0.96) { + pixel.role = "ferricOxide"; + } else { + pixel.role = "sulfurTrioxide"; + }; + }; + if(pixel.wet && pixel.temp > 300) { + if(overallTemperatureChangeRate > 25) { //if temp change is fast enough, always spall + if(Math.random() < Math.max(0.1,0.35 - (pixel.wet/20))) { //decreasingly less likely to spall as it gets wetter, for balance + explodeAt(pixel.x,pixel.y,Math.random() < 1/3 ? 2 : 1,"steam,dust") + if(!pixel || pixel.element !== "concrete") { //if destroyed or changed + return; + }; + }; + pixel.wet--; + } else { //if exposed, continuously try to boil off to random neighbor + if(exposedToAir(pixel)) { + var randomNeighbor = adjacentCoords[Math.floor(Math.random() * adjacentCoords.length)] + var rnx = randomNeighbor[0] + var rny = randomNeighbor[1] + if(isEmpty(pixel.x+rnx, pixel.y+rny, false)) { + createPixel("steam", pixel.x+rnx, pixel.y+rny) + pixel.wet--; + var colorWasHSL = pixel.color.startsWith("hsl"); + pixel.color = changeLightness(pixel.color,6,"+",colorWasHSL ? "hsl" : "rgb"); + }; + } else { //if surrounded, lower chance to spall and higher chance to dissipate + if(Math.random() < 0.03) { + if(Math.random() < 2/5) { + explodeAt(pixel.x,pixel.y,Math.random() < 1/2 ? 2 : 3,"steam,dust") + if(!pixel || pixel.element !== "concrete") { //if destroyed or changed + return; + }; + }; + pixel.wet--; + var colorWasHSL = pixel.color.startsWith("hsl"); + pixel.color = changeLightness(pixel.color,6,"+",colorWasHSL ? "hsl" : "rgb"); + }; + }; + }; + return; + }; + if(Math.random() < 1/3) { //tick wetness behavior only 1/3 of the time, for performance + var randomCoords = JSON.parse(JSON.stringify(adjacentCoords)); //so we don't need both an adjacentCoords for *and* a random coord iterator + shuffleArray(randomCoords); + for(i = 0; i < randomCoords.length; i++) { + var coords = [ + pixel.x+randomCoords[i][0], + pixel.y+randomCoords[i][1] + ]; + if(isEmpty(coords[0],coords[1],true)) { + continue; + } else { + var newPixel = pixelMap[coords[0]]?.[coords[1]]; + if(newPixel?.element) { + if(newPixel.element === "water" && pixel.wet < 4) { + //console.log("touching water",pixel.wet); + if(pixel.wet < 1) { + var colorWasHSL = pixel.color.startsWith("hsl"); + pixel.color = changeLightness(pixel.color,6,"-",colorWasHSL ? "hsl" : "rgb"); + pixel.wet = 1; + if(Math.random() < 0.8) { deletePixel(newPixel.x,newPixel.y) }; + break; + }; + if(pixel.wet == 1) { + var colorWasHSL = pixel.color.startsWith("hsl"); + pixel.color = changeLightness(pixel.color,6,"-",colorWasHSL ? "hsl" : "rgb"); + pixel.wet = 2; + if(Math.random() < 0.6) { deletePixel(newPixel.x,newPixel.y) }; + break; + }; + if(pixel.wet == 2) { + var colorWasHSL = pixel.color.startsWith("hsl"); + pixel.color = changeLightness(pixel.color,6,"-",colorWasHSL ? "hsl" : "rgb"); + pixel.wet = 3; + if(Math.random() < 0.4) { deletePixel(newPixel.x,newPixel.y) }; + break; + }; + if(pixel.wet == 3) { + var colorWasHSL = pixel.color.startsWith("hsl"); + pixel.color = changeLightness(pixel.color,6,"-",colorWasHSL ? "hsl" : "rgb"); + pixel.wet = 4; + if(Math.random() < 0.2) { deletePixel(newPixel.x,newPixel.y) }; + break; + }; + } else { + //console.log(3); + if(pixel.wet > 1 && !pixel.frozen && newPixel.element.endsWith("concrete") && newPixel.wet != undefined && newPixel.wet < pixel.wet) { + if(pixel.wet <= 1) { break }; + pixel.wet--; + var colorWasHSL = pixel.color.startsWith("hsl"); + pixel.color = changeLightness(pixel.color,6,"+",colorWasHSL ? "hsl" : "rgb"); + newPixel.wet++; + var newColorWasHSL = newPixel.color.startsWith("hsl"); + newPixel.color = changeLightness(newPixel.color,6,"-",newColorWasHSL ? "hsl" : "rgb"); + }; + }; + }; + }; + }; + }; + if(pixel.role == "sand" && pixel.temp > elements.sand.tempHigh) { + changePixel(pixel,"molten_glass",false); + return; + }; + if(pixel.role == "aggregate" && pixel.temp > magmaTempHigh) { + changePixel(pixel,magmaName,false); + return; + }; + if(pixel.role == "alumina" && pixel.temp > elements.aluminum_oxide.tempHigh) { + changePixel(pixel,"molten_aluminum_oxide",false); + return; + }; + if(pixel.role == "ferricOxide" && pixel.temp > elements.rust.tempHigh) { + changePixel(pixel,"molten_iron",false); + return; + }; + if(pixel.role == "sulfurTrioxide" && pixel.temp > magmaTempHigh) { //arbitrary choice: leave when the aggregate leaves + changePixel(pixel,"sulfur_trioxide_gas",false); + return; + }; + if(pixel.role == "lime" && pixel.temp > 550) { + changePixel(pixel,"slaked_lime",false); + return; + }; + if(pixel.role == "silica") { + pixel.didQuartzThermalExpansion ??= false; + if(pixel.temp > 573 && !pixel.didQuartzThermalExpansion) { + if(Math.random() < 0.13) { + changePixel(pixel,"pop",false); + }; + pixel.didQuartzThermalExpansion = true; + }; + if(pixel.temp > elements.silica.tempHigh) { + changePixel(pixel,"molten_silica",false); + return; + }; + }; + }; + elements.concrete.tick = newConcreteTick; + //Crimson + //Made-up lore: Crimson naturally drives rocks towards a somewhat mafic comp. + elements.crimglass = { + color: ["#d1808d", "#b8a770", "#a4b386"], + behavior: behaviors.WALL, + tempHigh: 1765, + category: "solids", + state: "solid", + density: 2711, + breakInto: "crimglass_shard", + noMix: true, + tick: function(pixel) { crimSpread(pixel) } + }; + elements.crimglass_shard = { + color: gravelizeToHex(["#d1808d", "#b8a770", "#a4b386"]), + behavior: behaviors.POWDER, + tempHigh: 1765, + stateHigh: "molten_crimglass", + category: "powders", + state: "solid", + density: 2711 * 0.5, + tick: function(pixel) { crimSpread(pixel) } + }; + elements.molten_crimglass = { + tick: function(pixel) { crimSpread(pixel) } + }; + elements.crimson_grass = { + color: ["#e82535","#cc471f","#d6153c","#c20e29","#b81a2c"], + behavior: [ + "XX|CR:vicious_mushroom%0.01|XX", + "XX|XX|XX", + "XX|M1|XX", + ], + tick: function(pixel) { + crimSpread(pixel) + }, + tempHigh: 100, + stateHigh: "dead_plant", + tempLow: -2, + stateLow: "frozen_plant", + burn:50, + burnTime:20, + category:"life", + state: "solid", + density: 1400, + } + elements.red_ice = { + color: ["#f0ccc5", "#f7d8d2", "#eba39b"], + behavior: behaviors.WALL, + tick: function(pixel) { + crimSpread(pixel) + }, + temp: 0, + tempHigh: 5, + stateHigh: "crimwater", + category: "solids", + state: "solid", + density: 917, + breakInto: "crimsnow", + } + elements.crimwater = { //you shouldn't be able to purify ice by melting it + color: "#e45c7c", + behavior: behaviors.LIQUID, + tick: function(pixel) { + crimSpread(pixel) + }, + tempLow: 0, + stateLow: "red_ice", + tempHigh: 100, + stateHigh: "steam", + viscosity: 1, + category: "liquids", + reactions: { + "quicklime": { "elem1": null, "elem2": "slaked_lime", }, + "ruins": { "elem2": "rock", "chance": 0.00035 }, + }, + state: "liquid", + density: 997, + conduct: 0.02, + stain: 0.02, + } + elements.crimsnow = { //BIG break from canon but you shouldn't be able to purify ice by grinding it either + color: "#fce1e4", + behavior: behaviors.POWDER, + tick: function(pixel) { + crimSpread(pixel) + }, + temp: 0, + tempHigh: 5, + stateHigh: "crimwater", + category: "land", + state: "solid", + density: 100, + } + elements.vicious_mushroom = { + color: "#e36554", + behavior: behaviors.POWDER, + tick: function(pixel) { + crimSpread(pixel) + }, + category: "life", + hidden: true, + tempHigh: 225, + stateHigh: "fire", + burn: 10, + burnTime: 65, + state: "solid", + density: 90.445, + }; + elements.crimtane_ore = { + color: ["#d83a3b", "#85242c", "#5d5d5d", "#540c14"], + behavior: behaviors.POWDER, + category: "land", + tempHigh: 1552, //using palladium's melting point as an upper bound + stateHigh: ["molten_slag","molten_slag","molten_crimtane"], //:sunglasses: can't turn things into slag if you're already slag + state: "solid", + density: 5854, //arbitrarily chosen, average of ((average of gold and palladium densities) + (crimstone density) + (crimstone density)) + }; + elements.crimson = { + color: ["#e82535","#cc471f", "#782b2e", "#8c2e26", "#86241d", "#9d2b20"], + tool: crimSpread, + tick: function(pixel) { + getMooreNeighbors(pixel).forEach(crimSpread) + }, + hardness: 0.8, + density: 2500, + state: "solid", + tempHigh: 1200, + stateHigh: "ash", + category: "special", + desc: "Spreads the Crimson", + excludeRandom: true + }; + elements.crimtane = { + color: ["#fc141e", "#C62A2F", "#903f3f", "#752E2E", "#5a1c1c", "#5B3C3C", "#5c5c5c"], + behavior: behaviors.SOLID, + category: "solids", + tempHigh: 1200, //i want a behaviors.WALL form of crimtane... and I'm letting the game Autogenerate molten_crimtane because I'm going to use it. + //just pretend it got sintered somehow + state: "solid", + hidden: true, + density: 15661, + } + elements.shadewood_tree_branch = { + color: "#677a8f", + behavior: [ + "CR:crimson_leaf,shadewood_tree_branch%2|CR:crimson_leaf,crimson_leaf,crimson_leaf,shadewood_tree_branch%2|CR:crimson_leaf,shadewood_tree_branch%2", + "XX|XX|XX", + "XX|XX|XX", + ], + tempHigh: 400, + stateHigh: ["fire","sap"], + tempLow: -30, + stateLow: "wood", + category: "solids", + burn: 40, + burnTime: 50, + burnInto: ["sap","ember","charcoal"], + hidden: true, + state: "solid", + density: 1500, + hardness: 0.15, + breakInto: ["sap","sawdust"], + hidden: true, + } + elements.crimson_vine = { + color: "#de3323", + behavior: [ + "XX|SP|XX", + "XX|XX|XX", + "XX|CL%1 AND M1|XX", + ], + tick: function(pixel) { + crimSpread(pixel) + }, + tempHigh: 100, + stateHigh: "dead_plant", + tempLow: -2, + stateLow: "frozen_plant", + burn: 35, + burnTime: 100, + category: "life", + state: "solid", + density: 1050, + } + elements.shadewood = { + color: "#677a8f", + behavior: behaviors.WALL, + tempHigh: 400, + stateHigh: ["ember","charcoal","fire","fire","fire"], + category: "solids", + burn: 5, + burnTime: 300, + burnInto: ["ember","charcoal","fire"], + state: "solid", + hardness: 0.15, + breakInto: "shadewood_sawdust", + density: 930, //used tigerwood + } + elements.shadewood_sapling = { + color: ["#e64029", "#d43b26"], + behavior: [ + "XX|M2%2|XX", + "XX|L2:shadewood,shadewood_tree_branch%80|XX", + "XX|M1|XX", + ], + tempHigh: 100, + stateHigh: "dead_plant", + tempLow: -2, + stateLow: "frozen_plant", + burn: 65, + burnTime: 15, + category: "life", + state: "solid", + density: 1500, + } + elements.shadewood_sawdust = { + color: ["#95abcf","#8190a3"], + behavior: behaviors.POWDER, + tempHigh: 400, + stateHigh: "fire", + category: "powders", + burn: 25, + burnTime: 150, + burnInto: ["ash","fire","fire","fire"], + state: "solid", + density: 493, + hidden: true, + } + elements.crimson_leaf = { + color: "#de3323", + behavior: behaviors.WALL, + category:"life", + tempHigh: 100, + stateHigh: "dead_plant", + tempLow: -1.66, + stateLow: "frozen_plant", + burn:65, + burnTime:60, + burnInto: "dead_plant", + state: "solid", + density: 500, + hidden: true, + } + elements.ichor = { + color: ["#ffec70", "#ffcb52"], + behavior: behaviors.LIQUID, + reactions: { + "head": { "elem2":"meat" }, //sb has no defense to reduce so i just made it deadly (as in greek mythology) + "body": { "elem2":"meat" }, + }, + category: "liquids", + viscosity: 1, + state: "liquid", + density: 1010, + stain: 0.02, + } + elements.vicious_goldfish = { + color: "#e64230", + behavior: [ + "SW:"+eLists.WHL+",blood%2|M2%5 AND SW:"+eLists.WHL+",blood%1|XX", //this is where M3 would have been useful + "SW:"+eLists.WHL+",blood%40|FX%0.01|BO%1", //i have no idea what i'm doing + "SW:"+eLists.WHL+",blood%2 AND M2|M1|XX", + ], + reactions: { + "algae": { "elem2":null, chance:0.5 }, + "plant": { "elem2":null, chance:0.125 }, + "fly": { "elem2":null, chance:0.5 }, + "firefly": { "elem2":null, chance:0.5 }, + "worm": { "elem2":null, chance:0.25 }, + "head": { "elem2":[null,"blood"], chance:0.25 }, + "body": { "elem2":[null,"blood"], chance:0.25 }, + "oxygen": { "elem2":"carbon_dioxide", chance:0.5 }, + }, + tick: function(pixel) { + pixel.color = pixelColorPick(pixel) + }, + temp: 20, + tempHigh: 42, + stateHigh: "meat", + tempLow: -20, + stateLow: "frozen_meat", + category:"life", + burn:40, + burnTime:100, + state: "solid", + density: 1080, + conduct: 0.2, + } + elements.crimsand = { + color: ["#4c4c44", "#6c645c", "#5c544c", "#847c6c", "#24241c", "#4c4c44", "#6c645c", "#5c544c", "#847c6c", "#24241c", "#3c140c", "#842c24"], + behavior: behaviors.POWDER, + tempHigh: 1765, + stateHigh: "molten_crimson_glass", + category: "sand", + state: "solid", + density: 1602, + _data: ["crimson","crystalline","sand"], + tick: function(pixel) { crimSpread(pixel) } + } + elements.wet_crimsand = { + color: sandizeToHex("crimsand","w"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"crimsand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_crimsand", + tempLow: -50, + stateLow:"packed_crimsand", + density: elements.crimsand.density * 0.595 + 150, + _data: ["crimson","crystalline","wet_particulate"], + nellfireImmune: true, + tick: function(pixel) { crimSpread(pixel) } + }; + elements.packed_crimsand = { + color: sandizeToHex("crimsand","p"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: 1911, + stateHigh: "crimglass", + density: elements.crimsand.density * 0.59, + breakInto: "nellsand", + _data: ["nellish","crystalline","packed_particulate"], + nellfireImmune: true, + tick: function(pixel) { crimSpread(pixel) } + }; + function newDirtType(names,dirtColor,density,meltingPoint,frostingPoint) { + if(!(dirtColor instanceof Array)) { dirtColor = [dirtColor] }; + var mudColor = dirtColor.map(x => colorToHsl(x,"json")); mudColor.forEach(function(x) { x.s *= (41/21); x.l *= (15/26) }); mudColor = mudColor.map(function(x) { return hslToHex(...Object.values(x)) }); + if(mudColor.length == 1) { mudColor = mudColor[0] }; + var mudstoneColor = dirtColor.map(x => colorToHsl(x,"json")); mudstoneColor.forEach(function(x) { x.h += 6; x.s *= (31/41); if(x.s > 0) { x.s += 5 }; x.l *= (22/15); x.l = bound(x.l + 5,0,60) }); mudstoneColor = mudstoneColor.map(function(x) { return hslToHex(...Object.values(x)) }); + if(mudstoneColor.length == 1) { mudstoneColor = mudstoneColor[0] }; + var dryDirtColor = dirtColor.map(x => colorToHsl(x,"json")); dryDirtColor.forEach(function(x) { x.h += 4; x.s *= (8/11); x.l *= (34/50); x.l += 5 }); dryDirtColor = dryDirtColor.map(function(x) { + x = convertHslObjects(x,"rgbjson"); + x.r += 10; + x.g += 5; //XG??!?!??!?!?!??!?!!??!?!?!?!??!?!?!?!/1/1/1?!/!?!?1?1??!/!1//! + x.b -= 10; + x.g *= 0.94; + x.b *= 0.88; + return convertColorFormats(x,"hex"); + }); + if(dryDirtColor.length == 1) { dryDirtColor = dryDirtColor[0] }; + var permafrostColor = dirtColor.map(x => colorToHsl(x,"json")); permafrostColor.forEach(function(x) { x.h -= 6; x.s *= (3/5); x.l -= 3 }); permafrostColor = permafrostColor.map(function(x) { return hslToHex(...Object.values(x)) }); + if(permafrostColor.length == 1) { permafrostColor = permafrostColor[0] }; + var dryPermafrostColor = dirtColor.map(x => colorToHsl(x,"json")); dryPermafrostColor.forEach(function(x) { x.h -= 6; x.s *= (3/5); x.s -= 5; x.l += 2 }); dryPermafrostColor = dryPermafrostColor.map(function(x) { return hslToHex(...Object.values(x)) }); + if(dryPermafrostColor.length == 1) { dryPermafrostColor = permafrostColor[0] }; + //names should b an obj w keys dirt,mud,mudstone,permafrost + elements[names.dirt] = { + color: dirtColor, + behavior: behaviors.POWDER, + tempHigh: 100, + stateHigh: "dry_" + names.dirt, + tempLow: frostingPoint, + forceAutoGen: true, + stateLow: names.permafrost, + category:"land", + state: "solid", + density: density + }; + elements["dry_" + names.dirt] = { + color: dryDirtColor, + behavior: behaviors.POWDER, + tempHigh: meltingPoint, + stateHigh: "molten_" + names.dirt, + tempLow: frostingPoint, + stateLow: "dry_" + names.permafrost, + forceAutoGen: true, + stateLow: names.permafrost, + category:"land", + state: "solid", + density: density + }; + elements[names.mud] = { + color: mudColor, + behavior: behaviors.STURDYPOWDER, + reactions: { + "dirt": { elem1:names.dirt, elem2:"mud", chance:0.0005, oneway:true }, + ["dry_" + names.dirt]: { elem1:names.dirt, elem2: ["dry_" + names.dirt], chance:0.0005, oneway:true }, + "sand": { elem1:names.mud, elem2:"wet_sand", chance:0.0005, oneway:true }, + "sawdust": { elem1:"mulch", "elem2":null }, + "evergreen": { elem1:"mulch", "elem2":null }, + }, + tempHigh: 100, + stateHigh: names.mudstone, + tempLow: frostingPoint, + stateLow: names.permafrost, + category: "land", + state: "solid", + density: density * (17/12), + stain: 0.02 + }; + elements[names.mudstone] = { + color: mudstoneColor, + behavior: behaviors.SUPPORT, + tempHigh: meltingPoint, + stateHigh: "molten_" + names.dirt, + tempLow: frostingPoint, + stateLow: names.permafrost, + category: "land", + state: "solid", + density: density * 1.2, + breakInto: "dirt" + }; + elements[names.permafrost] = { + color: permafrostColor, + behavior: behaviors.SUPPORT, + temp: frostingPoint - 20, + tempHigh: frostingPoint, + stateHigh: names.mud, + category: "land", + state: "solid", + density: density / 1.7 + }; + elements["dry_" + names.permafrost] = { + color: dryPermafrostColor, + behavior: behaviors.POWDER, + temp: frostingPoint - 20, + tempHigh: 0, + stateHigh: "dry_" + names.dirt, + category: "land", + state: "solid", + density: density + }; + elements.water.reactions[names.dirt] = { elem1: null, elem2: names.mud }; + elements.water.reactions["dry_" + names.dirt] = { elem1: null, elem2: names.dirt, chance: 0.005 }; + elements.mud.reactions["dry_" + names.dirt] = { elem1: "dirt", elem2: names.dirt, chance: 0.005 }; + }; + newDirtType( + {dirt: "crimsoil", mud: "crimmud", permafrost: "crimson_permafrost", mudstone: "crimmudstone"}, + ["#782b2e", "#8c2e26", "#86241d", "#9d2b20", "#632f52"], + 1260, 903, -70 + ); + elements.mud._data = ["loam","soil","wet_particulate"]; + elements.mudstone._data = ["loam","soil","packed_particulate"]; + elements.permafrost._data = ["loam","soil","icy_particulate"]; + elements.crimsoil._data = ["crimson","soil","particulate"]; + elements.dry_crimsoil._data = ["crimson","soil","particulate"]; + elements.crimmud._data = ["crimson","soil","wet_particulate"]; + elements.crimmudstone._data = ["crimson","soil","packed_particulate"]; + elements.crimson_permafrost._data = ["crimson","soil","icy_particulate"]; + elements.dry_crimson_permafrost._data = ["crimson","soil","particulate"]; + makeNonSandSedimentationElements("crimsand","crimsandy_water","crimsandstone"); + newIgneousCompositionFamily( + "crimson", + 13000, 2420, -76, -17, 2877, + "crimstone", ["#cb4444", "#953333", "#611c1c", "#b43434", "#752424"], 1223, 4234, + "metacrimstone", ["#b31010","#8f1111","#80282a","#b31010","#8f1111","#80282a","#bb4e45"], 1223, 4544, + "crimsalt", ["#9e5041", "#a33345"], 1151, 3226, + "metacrimsalt", ["#ab5c4f","#c25c4c","#cf4452"], 1151, 3044, + "crimscoria", ["#914c57", "#ba7b85", "#6b2e38", "#b3626f"], 1032, 2903, + "metacrimscoria", ["#bf2636","#961b12","#b84040"], 1032, 3534, + "crimidian", ["#5a1b1c", "#622b33", "#762733", "#76322c"], 1122, 3050, + "metacrimidian", ["#701b1c","#783628","#802419","#872323"], 1122, 3169, + 3,7 + ); + elements.crimsalt.behavior = behaviors.STURDYPOWDER; + elements.metacrimsalt.behavior = behaviors.STURDYPOWDER; + elements.crimson_magma.temp = elements.crimson_magma.tempHigh * 0.8; + //var elems2 = Object.keys(elements); + //var deltaElems = elems2.filter(function(name) { return !(elems1.includes(name)) }); + runAfterLoad(function() { runAfterAutogen(function() { runAfterAutogen(function() { //i need this to happen last + var rockCrimmies = Object.keys(elements).filter(function(name) {return (name.match(/((wet|packed|hot|vaporized)_|meta|)crim/) && !(name.match(/crimtane/)))}); + lifeEaterWhitelist = lifeEaterWhitelist.concat(rockCrimmies); //crimson is alive, so LEV should eat it + rockCrimmies.forEach(function(name) { + var data = elements[name]; + if(data.tick) { + var oldTick = data.tick; + data.tick = function(pixel) { + oldTick(pixel); + if(pixel && !isEmpty(pixel.x,pixel.y)) { + crimSpread(pixel); + } + } + } else { + data.tick = function(pixel) { crimSpread(pixel) } + }; + }); + for(var key in elements) { + var data = elements[key]; + if(!(elements[key].tick)) { continue }; + if(elements[key].tick.toString().includes("crimSpread")) { + data.excludeRandom = true + } + }; + })})}); + elements.molten_crimsoil = { + tempLow: elements.dry_crimsoil.tempHigh, + stateLow: "dry_crimsoil", + stateHigh: "crimson_magma" + }; + crimsonObject.dirt = "crimsoil"; + crimsonObject.dry_dirt = "dry_crimsoil"; + crimsonObject.mud = "crimmud"; + crimsonObject.mudstone = "crimmudstone"; + crimsonObject.permafrost = "crimson_permafrost"; + crimsonObject.molten_dirt = "molten_crimsoil"; + crimsonObject.dry_permafrost = "dry_crimson_permafrost"; + runAfterLoad(function() { + elements.molten_crimsoil.tempHigh = elements.crimson_magma.tempHigh; + elements.crimsandy_water.color = ["#985460", "#a8606c", "#a05864", "#b46c74", "#84404c", "#985460", "#a8606c", "#a05864", "#b46c74", "#84404c", "#903844", "#b44450" ] //manual: use crimwater for the lerp in crimsand suspension's color + elements.crimmuddy_water.color = ["#ed4154", "#f25259", "#f2444c", "#f25a62", "#df428d" ]; //same for crimsoil (crimmud) susp. + elements.crimwater.reactions.crimsand = elements.water.reactions.crimsand; + elements.crimwater.reactions.crimmud = elements.water.reactions.crimmud; + for(var k in elements.crimsandy_water.reactions) { + var reactionObject = elements.crimsandy_water.reactions[k]; + var e1 = reactionObject.elem1; var e2 = reactionObject.elem2; + if(e1 == "water") { reactionObject.elem1 = "crimwater" }; + if(e2 == "water") { reactionObject.elem2 = "crimwater" }; + }; + for(var k in elements.crimmuddy_water.reactions) { + var reactionObject = elements.crimmuddy_water.reactions[k]; + var e1 = reactionObject.elem1; var e2 = reactionObject.elem2; + if(e1 == "water") { reactionObject.elem1 = "crimwater" }; + if(e2 == "water") { reactionObject.elem2 = "crimwater" }; + }; + elements.crimmuddy_water.reactions.crimmuddy_water.elem2 = "crimsoil_sediment"; + elements.rainbow_muddy_water.reactions.rainbow_muddy_water.elem2 = "rainbow_soil_sediment" + elements.crimsoilstone.tempHigh = 800; + }); + //Blackpinkinitic (why? because FU. that's why. jk lol it's because i want to test metamorphism but there's so little data on the melting of metamorphic rocks--especially clay-based ones--that it's proving to be a real PITA. + newIgneousCompositionFamily( + "blackpinkinitic", + 403, 2602, -95, -30, 4007, + "blinkinite", ["#e39dc6", "#e378b7", "#d1589f", "#a1306e", "#6e274e", "#170e13", "#121212"], 1821, 3291, + "lisaslate", ["#f26fbc", "#f26fbc", "#e344a1", "#d42686", "#b52daa", "#8a3683", "#5c324f", "#572c6e", "#421530", "#120c10", "#120c10"], 1821, 3333, + "roselite", ["#eda4c6","#de90ae","#cf9db0","#cf9db0","#d97ca0"], 1715, 2551, + "rosephyllite", ["#e895bb", "#c46286", "#a34b73", "#6e2e4b", "#301921", "#1a1315"], 1715, 3021, + "jisoovesite", ["#bf56af", "#a15495", "#70416f", "#e87de6", "#381a47"], 1977, 719, + "vesimelite", ["#a35097", "#854e72", "#8a5789", "#d47dd2", "#462452"], 1977, 1132, + "jennitrite", ["#8a2966", "#801357", "#75074c", "#4a012f", "#360e27", "#1a0e15"], 2111, 2603, + "harmonitrite", ["#9e3979", "#9e186c", "#a3146e", "#380324", "#2e1028", "#361a2a"], 2111, 2663, + 3,7 + ); + elements.roselite.name = elements.roselite.alias = "rosélite"; + elements.rosephyllite.colorPattern = [ // diagonal foliation + "PPppDDddBBbbBBddDDpp", + "ppDDddBBbbBBddDDppPP", + "DDddBBbbBBddDDppPPpp", + "ddBBbbBBddDDppPPppDD", + "BBbbBBddDDppPPppDDdd", + "bbBBddDDppPPppDDddBB", + "BBddDDppPPppDDddBBbb", + "ddDDppPPppDDddBBbbBB", + "DDppPPppDDddBBbbBBdd", + "ppPPppDDddBBbbBBddDD" + ]; + elements.rosephyllite.colorKey = { + "P": "#e895bb", + "p": "#c46286", + "D": "#a34b73", + "d": "#6e2e4b", + "B": "#301921", + "b": "#1a1315" + }; + elements.blinkinite.behavior = behaviors.POWDER; + elements.rosephyllite.name = elements.rosephyllite.alias = "roséphyllite"; + elements.jisoovesite.maxColorOffset = 30; + //Rainbow (actually let's call them Iridian) + //Required manual elements + elements.rainbow_glass = { + color: makeRegularRainbow(12,100,70,"hex"), + behavior: behaviors.WALL, + tempHigh: 1765, + category: "solids", + state: "solid", + density: 2711, + breakInto: "rainbow_glass_shard", + noMix: true, + nellfireImmune: true, + }; + elements.rainbow_glass_shard = { + color: makeRegularRainbow(18,70,65,"hex"), + behavior: behaviors.POWDER, + tempHigh: 1784, + stateHigh: "molten_rainbow_glass", + category: "powders", + state: "solid", + density: 2213, + nellfireImmune: true, + }; + elements.rainbow_sand = { + color: makeRegularRainbow(7,100,94,"hex").concat("#fbfbfb"), + behavior: behaviors.POWDER, + tempHigh: 1731, + stateHigh: "molten_rainbow_glass", + category: "land", + state: "solid", + density: 1733, + _data: ["iridian","crystalline","particulate"], + nellfireImmune: true, + }; + elements.wet_rainbow_sand = { + color: makeRegularRainbow(7,100,84,"hex").concat("#e0e0e0"), + behavior: behaviors.STURDYPOWDER, + category: "land", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_rainbow_sand", + tempLow: -50, + stateLow:"packed_rainbow_sand", + density: 1733 * 0.595 + 150, + _data: ["iridian","crystalline","wet_particulate"], + nellfireImmune: true, + }; + elements.packed_rainbow_sand = { + color: makeRegularRainbow(7,70,86,"hex").concat("#dbdbdb"), + behavior: behaviors.SUPPORT, + category: "land", + state: "solid", + tempHigh: 1731, + stateHigh: "rainbow_glass", + density: 1733 * 0.59, + breakInto: "rainbow_sand", + _data: ["iridian","crystalline","packed_particulate"], + nellfireImmune: true, + }; + newDirtType( + {dirt: "rainbow_dirt", mud: "rainbow_mud", permafrost: "rainbow_permafrost", mudstone: "rainbow_mudstone"}, + "#b09eac #b0bfa1 #d9c0b6 #b09eac #b0bfa1 #d9c0b6 #ed7777 #a7d975 #7bd4d4 #ab77e0 #e0cf77".split(" "), + 1533, 942, -110 + ); + elements.rainbow_dirt._data = ["iridian","soil","particulate"]; + elements.dry_rainbow_dirt._data = ["iridian","soil","particulate"]; + elements.rainbow_mud._data = ["iridian","soil","wet_particulate"]; + elements.rainbow_mudstone._data = ["iridian","soil","packed_particulate"]; + elements.rainbow_permafrost._data = ["iridian","soil","icy_particulate"]; + elements.dry_rainbow_permafrost._data = ["iridian","soil","particulate"]; + elements.molten_rainbow_dirt = { + tempLow: elements.dry_rainbow_dirt.tempHigh, + stateLow: "dry_rainbow_dirt", + stateHigh: "rainbow_magma" + }; + runAfterLoad(function() { + elements.rainbow_soilstone.tempHigh = 800; + elements.molten_rainbow_dirt.tempHigh = elements.rainbow_magma.tempHigh; + }); + //Vanilla changes + elements.water.reactions.rainbow_sand = { elem1: null, elem2: "wet_rainbow_sand" }; + elements.water.reactions.wet_rainbow_sand = { "elem1": "rainbow_sand_water", "elem2": [ "rainbow_sand", "rainbow_sand", "rainbow_sand", "rainbow_sand_water" ], "chance": 0.01 }; + //Actual rock generation + //Sedimentary + makeNonSandSedimentationElements("rainbow_sand","rainbow_sand_water","rainbow_sandstone"); + elements.rainbow_sandstone.color = makeRegularRainbow(14,90,71,"hex").concat("#b5b5b5"); + //Igneous + newIgneousCompositionFamily( + "rainbow", + 133487, 5512, -71, -17, 4555, + "phirite", makeRegularRainbow(6,70,45,"hex"), 1671, 4004, + "metaphirite", makeRegularRainbow(7,60,35,"hex"), 1671, 4244, + "aphirite", makeRegularRainbow(24,63,75,"hex").concat("#bfbfbf"), 1685, 3951, + "metaaphirite", makeRegularRainbow(23,83,65,"hex").concat("#afafaf"), 1685, 4191, + "vesirite", makeRegularRainbow(7,55,30,"hex").concat(makeRegularRainbow(7,75,70,"hex")), 1712, 2918, + "metavesirite", makeRegularRainbow(5,66,80,"hex").concat(makeRegularRainbow(5,56,60,"hex")), 1712, 3118, + "vitirite", makeRegularRainbow(30,70,35,"hex").concat("#595959"), 2054, 3741, + "metavitirite", makeRegularRainbow(15,60,45,"hex").concat("#494949").concat(makeRegularRainbow(15,60,55,"hex")).concat("#797979"), 2054, 3941, + 3,7 + ); + //Nellfire immunization + var immunes = ["rainbow_sand_water", "rainbow_sand_sediment", "rainbow_sandstone", "phirite", "phirite_wall", "phirite_gravel", "aphirite", "aphirite_wall", "phirite_sand", "wet_phirite_sand", "packed_phirite_sand", "aphirite_gravel", "aphirite_sand", "wet_aphirite_sand", "packed_aphirite_sand", "vesirite", "vesirite_wall", "vesirite_gravel", "vesirite_sand", "wet_vesirite_sand", "packed_vesirite_sand", "vitirite", "vitirite_wall", "vitirite_shard", "vitirite_sand", "wet_vitirite_sand", "packed_vitirite_sand", "rainbow_magma", "vaporized_rainbow_magma", "rainbow_magma_cloud", "phirite_sandy_water", "phirite_sand_sediment", "phirite_sandstone", "aphirite_sandy_water", "aphirite_sand_sediment", "aphirite_sandstone", "vesirite_sandy_water", "vesirite_sand_sediment", "vesirite_sandstone", "vitirite_sandy_water", "vitirite_sand_sediment", "vitirite_sandstone", "phirite_dust", "aphirite_dust", "vesirite_dust", "vitirite_dust" ]; + runAfterLoad(function() { + for(i = 0; i < immunes.length; i++) { + if(!elements[immunes[i]]) { + console.error(immunes[i]); + continue; + } + elements[immunes[i]].nellfireImmune = true; + }; + }); + //Nellish + //Manuals + elements.nellglass = { + color: ["#a161b0", "#b06177", "#b59159"], + behavior: behaviors.WALL, + tempHigh: 1765, + category: "solids", + state: "solid", + density: 5012, + breakInto: "nellglass_shard", + noMix: true, + nellfireImmune: true, + }; + elements.nellglass_shard = { + color: ["#7d5f8c","#875966","#9e7b47"], + behavior: behaviors.POWDER, + tempHigh: 1765, + stateHigh: "molten_nellglass", + category: "powders", + state: "solid", + density: 4212, + nellfireImmune: true, + }; + elements.nellsand = { + color: ["#906fa8", "#80747a", "#b08464"], + behavior: behaviors.POWDER, + tempHigh: 1911, + stateHigh: "molten_nellglass", + category: "sand", + state: "solid", + density: 3742, + _data: ["nellish","crystalline","particulate"], + nellfireImmune: true, + }; + elements.water.reactions.nellsand = { elem1: null, elem2: "wet_nellsand" }; + elements.water.reactions.wet_nellsand = { "elem1": "nellsand_water", "elem2": [ "nellsand", "nellsand", "nellsand", "nellsand_water" ], "chance": 0.01 }; + elements.wet_nellsand = { + color: sandizeToHex("nellsand","w"), + behavior: behaviors.STURDYPOWDER, + category: "wet sand", + reactions: { + "dirt": { "elem1":"sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + state: "solid", + tempHigh: 100, + stateHigh: "packed_nellsand", + tempLow: -50, + stateLow:"packed_nellsand", + density: 3742 * 0.595 + 150, + _data: ["nellish","crystalline","wet_particulate"], + nellfireImmune: true, + }; + elements.packed_nellsand = { + color: sandizeToHex("nellsand","p"), + behavior: behaviors.SUPPORT, + category: "packed sand", + state: "solid", + tempHigh: 1911, + stateHigh: "nellglass", + density: 3742 * 0.59, + breakInto: "nellsand", + _data: ["nellish","crystalline","packed_particulate"], + nellfireImmune: true, + }; + nellburnObject = { + "dirt": "nellsand", + "dry_dirt": "nellsand", + "mud": "wet_nellsand", + "mudstone": "packed_nellsand", + "brass": ["nell_ash","zinc"], + "thermite": ["nell_ash","zinc"], + "rose_gold": ["nell_ash","gold"], + "electrum": ["nell_ash","gold"], + "molten_brass": ["nell_ash","molten_zinc"], + "molten_thermite": ["nell_ash","molten_zinc"], + "molten_rose_gold": ["nell_ash","molten_gold"], + "molten_electrum": ["nell_ash","molten_gold"], + "sun": "nellsun", + }; + var otherImmunes = ["fire","smoke","plasma","cold_fire","radiation","light","proton","neutron","electron","positron","antimatter","cold_smoke","rad_fire","rad_smoke","laser","liquid_fire","liquid_smoke","liquid_plasma","liquid_cold_fire","liquid_cold_smoke","liquid_rad_fire","liquid_rad_fire","liquid_rad_smoke","le_liquid_light","liquid_laser","pure_ice","pure_water","pure_steam","magic","gold","zinc","molten_gold","molten_zinc","pyreite","infernium","molten_pyreite","molten_infernium","infernyrite","molten_infernyrite","infernyreitheum","molten_infernyreitheum","pyrinfernyreitheum","molten_pyrinfernyreitheum","stellar_plasma","liquid_stellar_plasma","hydrogen","liquid_hydrogen","hydrogen_ice","neutronium","molten_neutronium","liquid_neutronium","neutronium_gas","liquid_degenerate_neutronium","gaseous_degenerate_neutronium"].concat(["water","ice","slush","snow","packed_snow","steam", "heavy_steam","heavy_water","heavy_ice","heavy_snow", "hydrogen_ice","liquid_hydrogen","hydrogen","ionized_hydrogen", "liquid_helium","helium","ionized_helium", "tralphium","liquid_tralphium","ionized_tralphium", "carbon","charcoal","diamond","molten_carbon", "carbon_monoxide","liquid_carbon_monoxide","carbon_monoxide_ice", "carbon_dioxide","dry_ice","seltzer","seltzer_ice","foam","cloud","rain_cloud","snow","snow_cloud","snow_cloud_floater","ice","thunder_cloud","hail_cloud"]); + for(let i = 0; i < otherImmunes.length; i++) { + var element = otherImmunes[i]; + if(elements[element]) { elements[element].nellfireImmune = true }; + }; + elements.nell_ash = { + color: ["#ab9393","#947873"], + behavior: behaviors.POWDER, + /*reactions: { + "steam": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "rain_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "snow_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "hail_cloud": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "acid_cloud": { "elem1": "pyrocumulus", "chance":0.05, "y":[0,12], "setting":"clouds" }, + "pyrocumulus": { "elem1": "pyrocumulus", "chance":0.08, "y":[0,12], "setting":"clouds" }, + "stench": { "elem2":null, "chance":0.1 } + },*/ + category:"powders", + state: "solid", + nellfireImmune: true, + tick: function(pixel) { + if(Math.random() < 0.01 && pixel.temp < 25) { + pixel.temp++; + }; + }, + density: 810, + //tempHigh: 2000, + //forceAutoGen: true, + //stateHigh: ["molten_ash","smoke","smoke","smoke"] + }; + elements.wall.nellfireImmune = true; + //Massive block of code that programatically assigns nellburn results to rocks based on their properties + runAfterLoad(function() { + var rockdataElements = Object.keys(elements).filter(function(name) { + return ( + elements[name]._data && + !["blackened_carbonate","nellish","nellsand_material"].includes(elements[name]._data[0]) + ) + }); + for(i = 0; i < rockdataElements.length; i++) { + var name = rockdataElements[i]; + var info = elements[name]; + switch(info._data[1]) { + case "phanerite": + switch(info._data[2]) { + case "igneous_rock": + nellburnObject[name] = "gehennite" + break; + case "solid_igneous_rock": + nellburnObject[name] = "gehennite_wall" + break; + case "igneous_gravel": + nellburnObject[name] = "gehennite_gravel" + break; + case "particulate": + nellburnObject[name] = "gehennite_sand" + break; + case "dust": + nellburnObject[name] = "gehennite_dust" + break; + case "wet_particulate": + nellburnObject[name] = "wet_gehennite_sand" + break; + case "packed_particulate": + nellburnObject[name] = "packed_gehennite_sand" + break; + case "sediment": + nellburnObject[name] = "gehennite_sand_sediment" + break; + case "suspension": + nellburnObject[name] = "gehennite_sandy_water" + break; + }; + break; + case "aphanite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + nellburnObject[name] = "nellrock" + break; + case "solid_igneous_rock": + nellburnObject[name] = "nellrock_wall" + break; + case "igneous_gravel": + nellburnObject[name] = "nellrock_gravel" + break; + case "particulate": + nellburnObject[name] = "nellrock_sand" + break; + case "dust": + nellburnObject[name] = "nellrock_dust" + break; + case "wet_particulate": + nellburnObject[name] = "wet_nellrock_sand" + break; + case "packed_particulate": + nellburnObject[name] = "packed_nellrock_sand" + break; + case "sediment": + nellburnObject[name] = "nellrock_sand_sediment" + break; + case "suspension": + nellburnObject[name] = "nellrock_sandy_water" + break; + }; + break; + case "vesiculite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + nellburnObject[name] = "hadean_sponge" + break; + case "solid_igneous_rock": + nellburnObject[name] = "hadean_sponge_wall" + break; + case "igneous_gravel": + nellburnObject[name] = "hadean_sponge_gravel" + break; + case "particulate": + nellburnObject[name] = "hadean_sponge_sand" + break; + case "dust": + nellburnObject[name] = "hadean_sponge_dust" + break; + case "wet_particulate": + nellburnObject[name] = "wet_hadean_sponge_sand" + break; + case "packed_particulate": + nellburnObject[name] = "packed_hadean_sponge_sand" + break; + case "sediment": + nellburnObject[name] = "hadean_sponge_sand_sediment" + break; + case "suspension": + nellburnObject[name] = "hadean_sponge_sandy_water" + break; + }; + break; + case "vitrite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + nellburnObject[name] = "gehidian" + break; + case "solid_igneous_rock": + nellburnObject[name] = "gehidian_wall" + break; + case "igneous_gravel": + nellburnObject[name] = "gehidian_gravel" + break; + case "particulate": + nellburnObject[name] = "gehidian_sand" + break; + case "dust": + nellburnObject[name] = "gehidian_dust" + break; + case "wet_particulate": + nellburnObject[name] = "wet_gehidian_sand" + break; + case "packed_particulate": + nellburnObject[name] = "packed_gehidian_sand" + break; + case "sediment": + nellburnObject[name] = "gehidian_sand_sediment" + break; + case "suspension": + nellburnObject[name] = "gehidian_sandy_water" + break; + }; + break; + case "metaphanerite": + switch(info._data[2]) { + case "igneous_rock": + nellburnObject[name] = "sheolite" + break; + case "solid_igneous_rock": + nellburnObject[name] = "sheolite_wall" + break; + case "igneous_gravel": + nellburnObject[name] = "sheolite_gravel" + break; + case "particulate": + nellburnObject[name] = "sheolite_sand" + break; + case "dust": + nellburnObject[name] = "sheolite_dust" + break; + case "wet_particulate": + nellburnObject[name] = "wet_sheolite_sand" + break; + case "packed_particulate": + nellburnObject[name] = "packed_sheolite_sand" + break; + case "sediment": + nellburnObject[name] = "sheolite_sand_sediment" + break; + case "suspension": + nellburnObject[name] = "sheolite_sandy_water" + break; + }; + break; + case "metaaphanite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + nellburnObject[name] = "nellbolite" + break; + case "solid_igneous_rock": + nellburnObject[name] = "nellbolite_wall" + break; + case "igneous_gravel": + nellburnObject[name] = "nellbolite_gravel" + break; + case "particulate": + nellburnObject[name] = "nellbolite_sand" + break; + case "dust": + nellburnObject[name] = "nellbolite_dust" + break; + case "wet_particulate": + nellburnObject[name] = "wet_nellbolite_sand" + break; + case "packed_particulate": + nellburnObject[name] = "packed_nellbolite_sand" + break; + case "sediment": + nellburnObject[name] = "nellbolite_sand_sediment" + break; + case "suspension": + nellburnObject[name] = "nellbolite_sandy_water" + break; + }; + break; + case "metavesiculite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + nellburnObject[name] = "metahadiculite" + break; + case "solid_igneous_rock": + nellburnObject[name] = "metahadiculite_wall" + break; + case "igneous_gravel": + nellburnObject[name] = "metahadiculite_gravel" + break; + case "particulate": + nellburnObject[name] = "metahadiculite_sand" + break; + case "dust": + nellburnObject[name] = "metahadiculite_dust" + break; + case "wet_particulate": + nellburnObject[name] = "wet_metahadiculite_sand" + break; + case "packed_particulate": + nellburnObject[name] = "packed_metahadiculite_sand" + break; + case "sediment": + nellburnObject[name] = "metahadiculite_sand_sediment" + break; + case "suspension": + nellburnObject[name] = "metahadiculite_sandy_water" + break; + }; + break; + case "metavitrite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + nellburnObject[name] = "metagehitrite" + break; + case "solid_igneous_rock": + nellburnObject[name] = "metagehitrite_wall" + break; + case "igneous_gravel": + nellburnObject[name] = "metagehitrite_gravel" + break; + case "particulate": + nellburnObject[name] = "metagehitrite_sand" + break; + case "dust": + nellburnObject[name] = "metagehitrite_dust" + break; + case "wet_particulate": + nellburnObject[name] = "wet_metagehitrite_sand" + break; + case "packed_particulate": + nellburnObject[name] = "packed_metagehitrite_sand" + break; + case "sediment": + nellburnObject[name] = "metagehitrite_sand_sediment" + break; + case "suspension": + nellburnObject[name] = "metagehitrite_sandy_water" + break; + }; + break; + case "phanerite_sandstone": + nellburnObject[name] = "gehennite_sandstone" + break; + case "aphanite_sandstone": + nellburnObject[name] = "nellrock_sandstone" + break; + case "vesiculite_sandstone": + nellburnObject[name] = "hadean_sponge_sandstone" + break; + case "vitrite_sandstone": + nellburnObject[name] = "gehidian_sandstone" + break; + case "metaphanerite_sandstone": + nellburnObject[name] = "sheolite_sandstone" + break; + case "metaaphanite_sandstone": + nellburnObject[name] = "nellbolite_sandstone" + break; + case "metavesiculite_sandstone": + nellburnObject[name] = "metahadiculite_sandstone" + break; + case "metavitrite_sandstone": + nellburnObject[name] = "metagehitrite_sandstone" + break; + case "silica_sandstone": + nellburnObject[name] = "nellsandstone" + break; + case "magma": + switch(info._data[2]) { + case "liquid": + nellburnObject[name] = "nellish_magma" + break; + case "vaporized": + nellburnObject[name] = "vaporized_nellish_magma" + break; + case "cloud": + nellburnObject[name] = "nellish_magma_cloud" + break; + }; + break; + case "rock": + switch(name) { + case "shale": + nellburnObject[name] = "nellsandstone" + break; + case "limestone": + nellburnObject[name] = "black_limestone" + break; + }; + break; + case "calcium": + switch(info._data[2]) { + case "particulate": + case "mineral": + nellburnObject[name] = bccd + break; + case "sediment": + nellburnObject[name] = "blackened_calcium_carbonate_dust_sediment" + break; + case "suspension": + nellburnObject[name] = bccs + break; + }; + break; + case "silica": + case "crystalline": + switch(info._data[2]) { + case "particulate": + nellburnObject[name] = "nellsand" + break; + case "wet_particulate": + nellburnObject[name] = "wet_nellsand" + break; + case "packed_particulate": + nellburnObject[name] = "packed_nellsand" + break; + case "suspension": + nellburnObject[name] = "nellsand_water" + break; + case "sediment": + nellburnObject[name] = "nellsand_sediment" + break; + }; + break; + case "soil": + case "dry_soil": + case "clay": + switch(info._data[2]) { + case "particulate": + nellburnObject[name] = "nellsand" + break; + case "suspension": + nellburnObject[name] = "nellsand_water" + break; + case "sediment": + nellburnObject[name] = "nellsand_sediment" + break; + }; + break; + case "crystalline_sandstone": + case "soil_sandstone": + nellburnObject[name] = "nell_ash" + break; + case "sedimentary": + if(info._data[0] == "calcium") { + nellburnObject[name] = "black_limestone"; + break + }; + default: + console.log("Nellburn assignment: Unknown _data[1] value for element",name,info._data); + }; + }; + nellfireSpawnBlacklist = ["nellfire"]; + }); + //Glad that's over + //Rocks + makeNonSandSedimentationElements("nellsand","nellsand_water","nellsandstone"); + newIgneousCompositionFamily( + "nellish", + 10, 3012, -96, -12, 3812, + "gehennite", ["#857c71", "#b5a98d", "#91847c", "#948b68", "#8a834a", "#adad34"], 2011, 3432, + "sheolite", ["#785848","#8c7e5b","#9c745c","#80463b"], 2011, 3852, + "nellrock", ["#a15a42","#997849","#946043","#8c533e","#a66658"], 2036, 3371, + "nellbolite", ["#7a3017","#693d21","#8a673a"], 2036, 3671, + "hadean_sponge", ["#e66785", "#b54761", "#cc8156", "#dbc760", "#ab9a44"], 2213, 1012, + "metahadiculite", ["#a0a35d","#665d37", "#7b804d", "#869151", "#6e443f"], 2213, 1412, + "gehidian", ["#754c2f", "#855d3a", "#702a1c", "#691a41"], 2054, 3112, + "metagehitrite", ["#5e4f2a","#53544e", "#68787a", "#454f46", "#5e584b"], 2054, 3312, + 1,9 + ); + //More sedimentaries because nellfire also transforms limestone, and the associated baggage + var bccd = "blackened_calcium_carbonate_dust"; + var bccs = "blackened_calcium_carbonate_solution"; + elements.calcium_carbonate_dust.reactions ??= {}; + elements.calcium_carbonate_dust.reactions.charcoal = { + elem1: bccd, elem2: [bccd, "charcoal", "charcoal"] + }; + newPowder(bccd,"#948e87",2771,804,["carbon_dioxide","quicklime","charcoal","ash"]); + elements[bccd]._data = ["blackened_carbonate","blackened_carbonate","particulate"]; + elements[bccd].nellfireImmune = true; + makeNonSandSedimentationElements(bccd,bccs,"black_limestone"); + elements.blackened_calcium_carbonate_dust_sediment.color = "#756e64"; + elements.blackened_calcium_carbonate_dust_sediment.nellfireImmune = true; + elements.black_limestone.color = "#575148"; + elements.black_limestone.stateHigh = ["fire","smoke","charcoal","carbon_dioxide","quicklime","quicklime","quicklime","quicklime","quicklime","quicklime","quicklime","quicklime"]; + elements[bccs].nellfireImmune = true; + elements.water.reactions[bccd] = { + "elem1":"calcium_carbonate_solution", + "elem2":[bccd,bccd,bccd,bccs], + "chance":0.004 + }; + elements.seltzer.reactions[bccd] = { + "elem1":bccs, + "elem2":[bccd,bccd,bccd,bccs], + "chance":0.02 + }; + var resultingAutoElems = [ "gehennite", "gehennite_wall", "gehennite_gravel", "nellrock", "nellrock_wall", "gehennite_sand", "gehennite_dust", "wet_gehennite_sand", "packed_gehennite_sand", "nellrock_gravel", "nellrock_sand", "nellrock_dust", "wet_nellrock_sand", "packed_nellrock_sand", "hadean_sponge", "hadean_sponge_wall", "hadean_sponge_gravel", "hadean_sponge_sand", "hadean_sponge_dust", "wet_hadean_sponge_sand", "packed_hadean_sponge_sand", "gehidian", "gehidian_wall", "gehidian_shard", "gehidian_sand", "gehidian_dust", "wet_gehidian_sand", "packed_gehidian_sand", "nellish_magma", "vaporized_nellish_magma", "nellish_magma_cloud", "gehennite_sandy_water", "gehennite_sand_sediment", "gehennite_sandstone", "nellrock_sandy_water", "nellrock_sand_sediment", "nellrock_sandstone", "hadean_sponge_sandy_water", "hadean_sponge_sand_sediment", "hadean_sponge_sandstone", "gehidian_sandy_water", "gehidian_sand_sediment", "gehidian_sandstone", bccd, bccs, "black_limestone" ] //hard-coded + runAfterLoad(function() { + for(i = 0; i < resultingAutoElems.length; i++) { + if(!elements[resultingAutoElems[i]]) { + console.error(resultingAutoElems[i]); + continue; + } + elements[resultingAutoElems[i]].nellfireImmune = true; + }; + }); + //Crimson transformation assigner + runAfterAutogen(function() { + crimsonAssignmentUnknownData1Errors = {}; + var rockdataElements = Object.keys(elements).filter(function(name) { + return ( + elements[name]._data && + !["crimson"].includes(elements[name]._data[0]) + ) + }); + for(i = 0; i < rockdataElements.length; i++) { + var name = rockdataElements[i]; + var info = elements[name]; + switch(info._data[1]) { + case "phanerite": + switch(info._data[2]) { + case "igneous_rock": + crimsonObject[name] = "crimstone" + break; + case "solid_igneous_rock": + crimsonObject[name] = "crimstone_wall" + break; + case "igneous_gravel": + crimsonObject[name] = "crimstone_gravel" + break; + case "particulate": + crimsonObject[name] = "crimstone_sand" + break; + case "dust": + crimsonObject[name] = "crimstone_dust" + break; + case "wet_particulate": + crimsonObject[name] = "wet_crimstone_sand" + break; + case "packed_particulate": + crimsonObject[name] = "packed_crimstone_sand" + break; + case "sediment": + crimsonObject[name] = "crimstone_sand_sediment" + break; + case "suspension": + crimsonObject[name] = "crimstone_sandy_water" + break; + }; + break; + case "aphanite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + crimsonObject[name] = "crimsalt" + break; + case "solid_igneous_rock": + crimsonObject[name] = "crimsalt_wall" + break; + case "igneous_gravel": + crimsonObject[name] = "crimsalt_gravel" + break; + case "particulate": + crimsonObject[name] = "crimsalt_sand" + break; + case "dust": + crimsonObject[name] = "crimsalt_dust" + break; + case "wet_particulate": + crimsonObject[name] = "wet_crimsalt_sand" + break; + case "packed_particulate": + crimsonObject[name] = "packed_crimsalt_sand" + break; + case "sediment": + crimsonObject[name] = "crimsalt_sand_sediment" + break; + case "suspension": + crimsonObject[name] = "crimsalt_sandy_water" + break; + }; + break; + case "vesiculite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + crimsonObject[name] = "crimscoria" + break; + case "solid_igneous_rock": + crimsonObject[name] = "crimscoria_wall" + break; + case "igneous_gravel": + crimsonObject[name] = "crimscoria_gravel" + break; + case "particulate": + crimsonObject[name] = "crimscoria_sand" + break; + case "dust": + crimsonObject[name] = "crimscoria_dust" + break; + case "wet_particulate": + crimsonObject[name] = "wet_crimscoria_sand" + break; + case "packed_particulate": + crimsonObject[name] = "packed_crimscoria_sand" + break; + case "sediment": + crimsonObject[name] = "crimscoria_sand_sediment" + break; + case "suspension": + crimsonObject[name] = "crimscoria_sandy_water" + break; + }; + break; + case "vitrite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + crimsonObject[name] = "crimidian" + break; + case "solid_igneous_rock": + crimsonObject[name] = "crimidian_wall" + break; + case "igneous_gravel": + crimsonObject[name] = "crimidian_gravel" + break; + case "particulate": + crimsonObject[name] = "crimidian_sand" + break; + case "dust": + crimsonObject[name] = "crimidian_dust" + break; + case "wet_particulate": + crimsonObject[name] = "wet_crimidian_sand" + break; + case "packed_particulate": + crimsonObject[name] = "packed_crimidian_sand" + break; + case "sediment": + crimsonObject[name] = "crimidian_sand_sediment" + break; + case "suspension": + crimsonObject[name] = "crimidian_sandy_water" + break; + }; + break; + case "metaphanerite": + switch(info._data[2]) { + case "igneous_rock": + crimsonObject[name] = "metacrimstone" + break; + case "solid_igneous_rock": + crimsonObject[name] = "metacrimstone_wall" + break; + case "igneous_gravel": + crimsonObject[name] = "metacrimstone_gravel" + break; + case "particulate": + crimsonObject[name] = "metacrimstone_sand" + break; + case "dust": + crimsonObject[name] = "metacrimstone_dust" + break; + case "wet_particulate": + crimsonObject[name] = "wet_metacrimstone_sand" + break; + case "packed_particulate": + crimsonObject[name] = "packed_metacrimstone_sand" + break; + case "sediment": + crimsonObject[name] = "metacrimstone_sand_sediment" + break; + case "suspension": + crimsonObject[name] = "metacrimstone_sandy_water" + break; + }; + break; + case "metaaphanite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + crimsonObject[name] = "metacrimsalt" + break; + case "solid_igneous_rock": + crimsonObject[name] = "metacrimsalt_wall" + break; + case "igneous_gravel": + crimsonObject[name] = "metacrimsalt_gravel" + break; + case "particulate": + crimsonObject[name] = "metacrimsalt_sand" + break; + case "dust": + crimsonObject[name] = "metacrimsalt_dust" + break; + case "wet_particulate": + crimsonObject[name] = "wet_metacrimsalt_sand" + break; + case "packed_particulate": + crimsonObject[name] = "packed_metacrimsalt_sand" + break; + case "sediment": + crimsonObject[name] = "metacrimsalt_sand_sediment" + break; + case "suspension": + crimsonObject[name] = "metacrimsalt_sandy_water" + break; + }; + break; + case "metavesiculite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + crimsonObject[name] = "metacrimscoria" + break; + case "solid_igneous_rock": + crimsonObject[name] = "metacrimscoria_wall" + break; + case "igneous_gravel": + crimsonObject[name] = "metacrimscoria_gravel" + break; + case "particulate": + crimsonObject[name] = "metacrimscoria_sand" + break; + case "dust": + crimsonObject[name] = "metacrimscoria_dust" + break; + case "wet_particulate": + crimsonObject[name] = "wet_metacrimscoria_sand" + break; + case "packed_particulate": + crimsonObject[name] = "packed_metacrimscoria_sand" + break; + case "sediment": + crimsonObject[name] = "metacrimscoria_sand_sediment" + break; + case "suspension": + crimsonObject[name] = "metacrimscoria_sandy_water" + break; + }; + break; + case "metavitrite": + //console.log(info._data[2]); + switch(info._data[2]) { + case "igneous_rock": + crimsonObject[name] = "metacrimidian" + break; + case "solid_igneous_rock": + crimsonObject[name] = "metacrimidian_wall" + break; + case "igneous_gravel": + crimsonObject[name] = "metacrimidian_gravel" + break; + case "particulate": + crimsonObject[name] = "metacrimidian_sand" + break; + case "dust": + crimsonObject[name] = "metacrimidian_dust" + break; + case "wet_particulate": + crimsonObject[name] = "wet_metacrimidian_sand" + break; + case "packed_particulate": + crimsonObject[name] = "packed_metacrimidian_sand" + break; + case "sediment": + crimsonObject[name] = "metacrimidian_sand_sediment" + break; + case "suspension": + crimsonObject[name] = "metacrimidian_sandy_water" + break; + }; + break; + case "phanerite_sandstone": + crimsonObject[name] = "crimstone_sandstone" + break; + case "aphanite_sandstone": + crimsonObject[name] = "crimsalt_sandstone" + break; + case "vesiculite_sandstone": + crimsonObject[name] = "crimscoria_sandstone" + break; + case "vitrite_sandstone": + crimsonObject[name] = "crimidian_sandstone" + break; + case "phanerite_sandstone": + crimsonObject[name] = "metacrimstone_sandstone" + break; + case "aphanite_sandstone": + crimsonObject[name] = "metacrimsalt_sandstone" + break; + case "vesiculite_sandstone": + crimsonObject[name] = "metacrimscoria_sandstone" + break; + case "vitrite_sandstone": + crimsonObject[name] = "metacrimidian_sandstone" + break; + case "crystalline_sandstone": + case "silica_sandstone": + case "sedimentary": + case "rock": + case "sandstone": + case "silica": + crimsonObject[name] = "crimsandstone" + break; + case "soil_sandstone": + crimsonObject[name] = "crimsoilstone" + break; + case "rainbow_soil_sandstone": + crimsonObject[name] = "crimsoilstone" + break; + case "magma": + switch(info._data[2]) { + case "liquid": + crimsonObject[name] = "crimson_magma" + break; + case "vaporized": + crimsonObject[name] = "vaporized_crimson_magma" + break; + case "cloud": + crimsonObject[name] = "crimson_magma_cloud" + break; + }; + break; + case "crystalline": + switch(info._data[2]) { + case "particulate": + crimsonObject[name] = "crimsand" + break; + case "wet_particulate": + crimsonObject[name] = "wet_crimsand" + break; + case "packed_particulate": + crimsonObject[name] = "packed_crimsand" + break; + case "suspension": + crimsonObject[name] = "crimsand_water" + break; + case "sediment": + crimsonObject[name] = "crimsand_sediment" + break; + }; + break; + case "soil": + case "dry_soil": + case "clay": + switch(info._data[2]) { + case "particulate": + crimsonObject[name] = "crimsand" + break; + case "suspension": + crimsonObject[name] = "crimsand_water" + break; + case "sediment": + crimsonObject[name] = "crimsand_sediment" + break; + }; + break; + default: + crimsonAssignmentUnknownData1Errors[name] = info._data; + }; + }; + //Manual overrides because JavaScript sees the cases and pretends it doesn't know what they mean + crimsonObject.soilstone = "crimsoilstone"; + crimsonObject.sandstone = "crimsandstone"; + crimsonObject.rosephyllite = "metacrimsalt"; + }); + //Assigner end + /* //Rocks + //Igneous + //Phaneritic + //Ultramafic: peridotite + var molten_olivine = ["molten_fayalite","molten_forsterite","molten_forsterite"]; + //apparently olivine sand exists + elements.olivine_sand = { + color: ['#b5a773', '#b5af78', '#b2b471', '#bab07b', '#b4ae74', '#b4b471', '#b5a970', '#b4b476'], + behavior: behaviors.POWDER, + tempHigh: 1750, //https://www.indiamart.com/olivineindia/olivine-sand.html + stateHigh: molten_olivine, + category: "land", + state: "solid", + density: 1720, + }; + elements.wet_olivine_sand = { + color: ["#a08d4b","#918949","999c49","#aa9b50","#8f8743","#adad53","#9d8f48","#838f43"], + behavior: behaviors.STURDYPOWDER, + reactions: { + "sand": { "elem1":"sand", "elem2":"wet_olivine_sand", "chance":0.0005, "oneway":true }, + "olivine_sand": { "elem1":"olivine_sand", "elem2":"wet_olivine_sand", "chance":0.0005, "oneway":true }, + "dirt": { "elem1":"olivine_sand", "elem2":"mud", "chance":0.0005, "oneway":true }, + }, + tempHigh: 100, + stateHigh: "packed_olivine_sand", + tempLow: -50, + stateLow: "packed_olivine_sand", + category: "land", + state: "solid", + density: 2002, + }; + elements.packed_olivine_sand = { + color: ["#968f64","#969669","#8d9362","#9d996c","#959465","#8f9362","#949061","#909366"], + behavior: behaviors.SUPPORT, + tempHigh: 1700, + stateHigh: molten_olivine, + category: "land", + state: "solid", + density: 1811, + breakInto: "olivine_sand", + }; + elements.water.reactions.olivine_sand = { "elem1": null, "elem2": "wet_olivine_sand" }; + newPowder("fayalite",["#bf7432","#ad8e3e"],4390,1200,null,null); + newPowder("forsterite","#cccccc",3270,1890,null,null); + elements.molten_forsterite = { + reactions: { + "molten_fayalite": { elem1: "olivine", elem2: ["molten_fayalite","olivine"], tempMax: 1890 }, + }, + }; + elements.olivine = { + color: ["#7fa14f","#7dba52"], + behavior: behaviors.POWDER, + tempHigh: 1890, + stateHigh: molten_olivine, + category: "solids", + state: "solid", + density: 2700, + breakInto: "olivine_shard", + }, + newPowder("olivine_shard",["#97ba65","#7a994e","#99d96c","#7cb553"],2811,1890,molten_olivine,null); + */ + //Gems + //There is a mineral classification scheme, but it will take a while to implement if I ever get around to it. + //We're assuming that the crystal structures reform properly because I don't want to have to research and implement refrozen amorphous forms. + //Emerald + elements.emerald = { + color: ["#31e31e", "#88fa5a", "#28d419", "#54e823", "#64f235"], + tempHigh: 1287, + //1: I can't be arsed to find out what happens to emerald in extreme heat. Apparently, neither can anyone else, and Google is useless for this. + //2: So I'm just assuming that the chromium impurities are polite and remain in suspension with the molten beryl. + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 2710, //within natural variation + hardness: 0.8, //Mohs scaled to diamond + }; + //Amethyst + elements.amethyst = { + color: ["#c569e0", "#bd43e0", "#e37aeb", "#ab2fe0", "#b05bd4", "#9b2cdb"], + tempHigh: 1650, + //1: Gee, another quartz-like... + //2: Like with emerald, I'm trusting the impurities to stay dissolved because I don't exactly have any amethyst laying around to melt. + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 2650, + hardness: 0.7, + }; + standaloneBrokenFormMaker("iron","scrap",true,"powders","auto","auto","molten_iron",null).hidden = true; + standaloneBrokenFormMaker("chromium","scrap",true,"powders","auto","auto","molten_chromium",null).hidden = true; + standaloneBrokenFormMaker("amethyst","shard",true,"powders","auto","auto","molten_amethyst",["silica","silica","silica","silica","silica","silica","silica","silica","silica","iron_scrap"]).hidden = true; + //Corundum + elements.corundum = { + color: ["#e3e3e3", "#d9d9d9", "#cccccc", "#dbdbdb", "#f2f2f2"], + tempHigh: 2072, + stateHigh: "molten_alumina", + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 4020, + hardness: 0.9, + breakInto: "alumina", + }; + elements.molten_titanium ??= {}; elements.molten_titanium.tempHigh = 3287; + elements.molten_iron ??= {}; elements.molten_iron.tempHigh = 2861; + elements.molten_chromium ??= {}; elements.molten_chromium.tempHigh = 2671; + elements.molten_copper ??= {}; elements.molten_copper.tempHigh = 4700; + elements.molten_alumina ??= {}; + elements.molten_alumina.tempHigh = 5400; + elements.molten_alumina.state = "liquid"; + elements.molten_alumina.autoType = "gas"; + elements.molten_alumina.reactions ??= {}; + elements.molten_alumina.reactions.iron_scrap = {elem1: "molten_sapphire", elem2: ["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","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","molten_iron","molten_iron",null] }; + elements.molten_alumina.reactions.molten_iron = {elem1: "molten_sapphire", elem2: ["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","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","molten_iron","molten_iron",null] }; + elements.molten_alumina.reactions.titanium_scrap = {elem1: "molten_sapphire", elem2: ["molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium",null] }; + elements.molten_alumina.reactions.molten_titanium = {elem1: "molten_sapphire", elem2: ["molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium","molten_titanium",null] }; + elements.molten_alumina.reactions.chromium_scrap = {elem1: "molten_ruby", elem2: ["molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium",null] }; + elements.molten_alumina.reactions.molten_chromium = {elem1: "molten_ruby", elem2: ["molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium","molten_chromium",null] }; + elements.molten_alumina.stateLow = "corundum"; + //Sapphire + elements.sapphire = { + color: ["#2d43e3", "#4d5fe3", "#1f30cc", "#375fdb", "#2d39e3"], + tempHigh: 2040, + //1: You can actually grow corundum-based gems through the Verneuil process + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 3980, + hardness: 0.9, + }; + elements.molten_sapphire ??= {}; elements.molten_sapphire.tick = function(pixel) { + if(pixel.temp >= 5040) { + if(Math.random() < 0.005) { //the real proportion of 0.01% is so low that you'd just melt sapphire and get back corundum + changePixel(pixel,Math.random() < 0.5 ? "titanium_gas" : "iron_gas",false) + } else { + changePixel(pixel,"alumina_gas",false) + } + }; + }; + standaloneBrokenFormMaker("sapphire","shard",true,"powders","auto","auto","molten_sapphire",["alumina","alumina","alumina","alumina","alumina","alumina","alumina","alumina","alumina","iron_scrap","titanium_scrap"]).hidden = true; + //Ruby + elements.ruby = { + //Corundum with different impurities, so I can copy/paste everything but the color + color: ["#ff1222", "#ff4545", "#e30b13", "#fa253b", "#f2120f"], + tempHigh: 2040, + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 3980, + hardness: 0.9, + }; + elements.molten_ruby ??= {}; elements.molten_ruby.tick = function(pixel) { + if(pixel.temp >= 5040) { + if(Math.random() < 0.02) { //1% also too low + changePixel(pixel,"chromium_gas",false) + } else { + changePixel(pixel,"alumina_gas",false) + } + }; + }; + standaloneBrokenFormMaker("ruby","shard",true,"powders","auto","auto","molten_sapphire",["alumina","alumina","alumina","alumina","alumina","alumina","alumina","alumina","alumina","chromium_scrap"]).hidden = true; + //Spinel (kek) + elements.spinel = { + color: ["#ff1261", "#db2776", "#f20732", "#f71670", "#f7167f"], + tempHigh: 2130, + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 3600, + hardness: 0.85, + } + //Topaz + elements.topaz = { + color: ["#f7f431", "#ffff5c", "#f7e048", "#fae43e", "#fff86e", "#ede321"], + tempHigh: 1340, + stateHigh: "mullite", //thermal decomposition + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 3500, + hardness: 0.8, + }; + //Mullite + elements.mullite = { + color: ["#f2d7bf", "#f5cbdc", "#f2dfd3"], //hardly a gemstone, but i will color it like the others regardless + tempHigh: 1840, + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 3110, + hardness: 0.7, + }; + //Onyx + elements.onyx = { + color: ["#1a1919", "#070605", "#111313"], + tempHigh: 1650, //another silicate mineral + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 2650, + hardness: 0.7, + }; + //Opal + elements.opal = { + color: ["#ffcfcf", "#fff0d9", "#fcf7c5", "#e4ffd4", "#d1fff5", "#dcecfa", "#dfdbff", "#f5e0ff", "#f7d0f1"], + tempHigh: 100, + stateHigh: ["broken_opal", "broken_opal", "broken_opal", "broken_opal", "broken_opal", "broken_opal", "broken_opal", "broken_opal", "broken_opal", "steam"], + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 2090, + hardness: 0.6, + breakInto: ["quartz", "quartz", "quartz", "quartz", "quartz", "quartz", "quartz", "quartz", "quartz", "water"], + }; + elements.broken_opal = { + color: ["#f5e6e6", "#ebe2d5", "#f7f6ed", "#e4eddf", "#d8ebe7", "#d8e0e8", "#e4e3e8", "#f4edf7", "#ebebeb"], + tempHigh: 1650, + stateHigh: "molten_quartz", + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 2322, + hardness: 0.55, //it cracks + }; + //Quartz + elements.quartz = { //silicates, silicates, and more silicates + color: ["#f0f0f0", "#e3e3e3", "#f7f7f7"], + tempHigh: 1650, + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 2650, + hardness: 0.7, + }; + //Re-add molten quartz because it stopped auto-generating + elements.molten_quartz = {"behavior":behaviors.MOLTEN,"hidden":true,"state":"liquid","category":"states","color":['#ffff78', '#fff078', '#ffb400', '#ffff71', '#ffe371', '#ffaa00', '#ffff7b', '#fff77b', '#ffb900'],"temp":1650,"tempLow":1550,"stateLow":"quartz","density":2385,"viscosity":10000,"reactions":{"ash":{"elem1":null,"elem2":"molten_slag"},"dust":{"elem1":null,"elem2":"molten_slag"},"magma":{"elem1":null,"elem2":"molten_slag"}},"movable":true} + //Use in glass + elements.molten_quartz.reactions = { + quicklime: { elem1: "molten_glass", elem2: ["quicklime", "quicklime", "quicklime", "quicklime", "quicklime", "quicklime", "quicklime", "quicklime", "quicklime", null]} //lack of vanilla washing soda, lack of tripartite reactions + }; + /* + elements.elem1.reactions = { + elem2: { elem1: "elem1_becomes", elem2: "elem2_becomes"} + }; + */ + //Pearl (not a mineral) + elements.pearl = { + color: ["#e3e3e3", "#e3e0d1", "#eddbce", "#eef2c9", "#d5f5dc", "#d8f2ec", "#fadcf9", "#e3d1c1", "#f2edc9", "#e0f5d7", "#e2beeb", "#e3e3e3", "#e3e0d1", "#eddbce", "#eef2c9", "#d5f5dc", "#d8f2ec", "#fadcf9", "#e3d1c1", "#f2edc9", "#e0f5d7", "#e2beeb", "#38332e"], + tempHigh: 1340, //yay, more thermal decomposition elements + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 772, //It is partly made of proteins and is said to burn, but I can't find an ignition point, so here it melts. + hardness: 0.45, + }; + //Jade + elements.jadeite = { + color: ["#3D7D31", "#2D6D1F", "#538A2F", "#6A9A37"], + tempHigh: 1000, + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 3400, + hardness: 0.65, + }; + //Soil + //Dry dirt + elements.dirt.forceAutoGen = true; + elements.dry_dirt = { + color: ["#a88e5e","#8f7950","#8a7045","#9e804c"], + behavior: [ + "XX|SW:dirt%3 AND SW:mud%6|XX", + "XX|XX|XX", + "M2|M1|M2", + ], + tempHigh: 1200, + stateHigh: "molten_dirt", + tempLow: -50, + stateLow: "dry_permafrost", + category:"land", + state: "solid", + density: 1100, + _data: ["loam","dry_soil","particulate"] + }, + elements.water.reactions.dry_dirt = { + elem1: null, elem2: "dirt", chance: 0.005 + }; + elements.mud.reactions.dry_dirt = { + elem1: "dirt", elem2: "dirt", chance: 0.005 + }; + elements.dirt._data = ["loam","soil","particulate"]; + elements.molten_dirt = { //added manually because the change to dirt will prevent molten_dirt from being auto-generated + "behavior": behaviors.MOLTEN, + "name": "molten_loam", + "hidden": true, + "state": "liquid", + "category": "states", + "color": ["#EC6A15", "#EC5515", "#EC3F00", "#B85210", "#B84210", "#B83100", "#AE4B0D", "#AE3C0D", "#AE2D00", "#D65A0F", "#D6480F", "#D63600"], + "temp": 1200, + "tempLow": 1100, + "stateLow": "dry_dirt", + "density": 1098, + "viscosity": 10000 + } + elements.molten_dirt.tempHigh = 3000; + elements.molten_dirt.stateHigh = "vaporized_rock"; + elements.dry_permafrost = { + color: ["#5B7870","#535D51","#52746A","#5A7A6F"], + behavior: behaviors.POWDER, //not enough water for cementing + temp: -50, + tempHigh: 10, + stateHigh: "dry_dirt", + category: "land", + state: "solid", + state: "solid", + density: 1200, + _data: ["loam","soil","particulate"] + } + elements.dirt.tempHigh = 110; + elements.dirt.stateHigh = "dry_dirt"; + //Land Element Cults + /* + "Cult" is used similarly to its EoD sense; here, it signifies a set of elements that systematically replicates another set of elements except for a given modification. + In this case, they replicate some land elements; a "yellow" cult, for example, would have yellow_dirt, yellow_mud, yellow_mudstone, yellow_permafrost, yellow_sand... + */ + //Radiation + //System with new special_property_library.js to replace the old mass manual recreation + radioactiveTransforms = { + steam: "rad_steam", + glass: "rad_glass", + molten_glass: "molten_rad_glass" + }; + radioactiveTransforms.fire = "rad_fire" + radioactiveTransforms.torch = "rad_torch" + specialProperties.radioactive = { + /*specialColorFunction: function(pixel,oldColor) { + var colorJSON = convertColorFormats(oldColor,"json"); + colorJSON.r *= 0.85; + colorJSON.r += 8; + colorJSON.g *= 1.4; + colorJSON.g += 16; + colorJSON.b *= 0.4; + colorJSON.b -= 0.4; + return convertColorFormats(colorJSON,"rgb"); + },*/ + specialFunction: function(pixel) { + if(radioactiveTransforms[pixel.element]) { + var result = radioactiveTransforms[pixel.element]; + while(result instanceof Array) { + result = result[Math.floor(Math.random() * result.length)] + }; + changePixel(pixel,result,false); + return + }; + for(var i in adjacentCoords) { + if(Math.random() < 0.005) { + pixel.temp++; + var newCoords = [ + pixel.x+adjacentCoords[i][0], + pixel.y+adjacentCoords[i][1] + ]; + if(isEmpty(newCoords[0],newCoords[1],false)) { + createPixel("radiation",newCoords[0],newCoords[1]) + } + }; + }; + if(Math.random() < 0.05) { + pixel.temp+=0.5; + if(Math.random() < 0.005) { + delete pixel.radioactive + } + } + } + } + console.log("Radioactive property defined"); + //Main elements + elements.liquid_irradium = { + color: "#5499FF", + behavior: behaviors.LIQUID, + tick: function(pixel) { + for(var i = 0; i < adjacentCoords.length; i++) { + //transformAdjacent(pixel,radioactiveTransforms) + var newCoords = [ + pixel.x+adjacentCoords[i][0], + pixel.y+adjacentCoords[i][1] + ]; + var newPixel = pixelMap[newCoords[0]]?.[newCoords[1]]; + if(newPixel && newPixel.element !== pixel.element) { + newPixel.radioactive = true + } + } + }, + //Becomes rainbow sand by water or poison, as well as by protocite, or bio-ooze + //Becomes sulfuric acid on contact with it + //Becomes corrupt slime by elder fluid + //Converts black tar and organic soup into itself + //Turns either grav liquid into aether dust, as well as liquid crystal + //Turns blood into bloodstone + //Turns blue slime into black slime + //Made by {mercury or bio-ooze} and protocite + category:"liquids", + state: "liquid", + density: 18180, //Cherry-picked from a Tumblr headcanon + //https://omniblog-of-starbound.tumblr.com/post/188424072728/starbound-element-headcannon-modded-metals + viscosity: 80.1, //probably misinterpreting tickDelta, and w/o the game assets, I can't compare against water, so this is in relation to H2SO4 scaled to its density in cP and under the assumption that water visc = 1 + } + /* + //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"], + tempHigh: 200, + stateHigh: "felsic_magma", + category: "solid rock", + state: "solid", + density: 2640, + hardness: 0.7, + maxColorOffset: 15, + hardness: 0.3, + _data: ["clay", "rock", "sedimentary_rock"] + }; + elements.shale.onTryMoveInto = function(pixel,otherPixel) { + var otherData = elements[otherPixel.element]; + if(otherData.category == "magmas" && Math.random() < 0.005 && pixel.temp > 650) { + var around = getCirclePixels(pixel.x,pixel.y,2); + around.forEach(pixel => changePixel(pixel,"hornfels")); + return + }; + if(pixel.exposedToAir) { return }; + if(pixel.temp > 800 && Math.random () < 0.0007) { + changePixel(pixel,"migmatite") + } else if(pixel.temp > 600 && Math.random () < 0.001) { + changePixel(pixel,"gneiss") + } else if(pixel.temp > 400 && Math.random () < 0.001) { + changePixel(pixel,"schist") + } else if(pixel.temp > 200 && Math.random () < 0.001) { + changePixel(pixel,"slate") + }; + return + };*/ + runAfterLoad(function() { + rocksSandsAndSoilsToGiveHotForms = Object.keys(elements).filter( + function(elemName) { + //console.log(elemName,elements[elemName]._data?.[2]); + return (!(["clay","limestone","black_limestone"].includes(elemName)) && !(elemName.endsWith("permafrost")) && ["igneous_rock","solid_igneous_rock","igneous_gravel","sedimentary_rock","particulate","packed_particulate","metamorphic_rock","solid_metamorphic_rock","metamorphic_gravel"].includes(elements[elemName]._data?.[2])) + } + ); + if(rocksSandsAndSoilsToGiveHotForms.includes("clay")) { rocksSandsAndSoilsToGiveHotForms.splice(rocksSandsAndSoilsToGiveHotForms.indexOf("clay"),1) }; + hotRockFunction(); //needs to happen after dry dirt is defined + elements.hot_crimsoilstone.stateHigh = "molten_crimsoil"; + elements.crimsoil.tempHigh = 100; + elements.crimsoil.stateHigh = "dry_crimsoil"; + elements.hot_rainbow_soilstone.stateHigh = "molten_rainbow_dirt"; + elements.rainbow_dirt.tempHigh = 100; + elements.rainbow_dirt.stateHigh = "dry_rainbow_dirt"; + }); + //Generation + //TNT world + //Supplementary elements + elements.oil_cloud = { + color: "#8c4331", + behavior: [ + "XX|XX|XX", + "XX|CH:oil%0.05|M1%2.5 AND BO", + "XX|XX|XX", + ], + category:"gases", + temp: 30, + state: "gas", + density: 0.5, + burn: 60, + burnTime: 15, + burnInto: "explosion", //atomization moment + ignoreAir: true, + stain: 0.02, + }; + elements.oil_cloud_floater = { + color: "#8c4331", + behavior: [ + "M2|M1|M2", + "M1%80|CH:oil_cloud%0.2|M1%80", + "M%60|XX|M2%60", + ], + reactions: { + "oil_cloud_floater": { elem1: "oil_cloud", elem2: "oil_cloud", chance: 0.003 }, + "oil_cloud": { elem1: "oil_cloud", elem2: "oil_cloud", chance: 0.01 } + }, + category:"gases", + temp: 30, //otherwise identical + state: "gas", + density: 0.5, + burn: 60, + burnTime: 15, + burnInto: "explosion", //atomization moment + stain: 0.02, + }; + //Main preset + worldgentypes.tnt_world = { + name: "TNT World", //unimplemented + layers: [ + [0.9, "red_gas", 0.50], + [0.9, "oil_cloud_floater"], + [0.65, "coal", 0.1], + [0.65, "nitro"], + [0.55, "nitro", 0.5], + [0.2, "coal", 0.2], + [0.2, "tnt"], + [0.05, "coal", 0.3], + [0.05, "c4"], + [0.0, "coal", 0.4], + [0.0, "lamp_oil"] + ] + }; + //Ice world + //Supplementary elements + elements.snow_cloud_floater = { + color: "#7e8691", + behavior: [ + "M2|M1|M2", + "M1%80|CH:snow_cloud%0.2|M1%80", + "M%60|XX|M2%60", + ], + reactions: { + "snow_cloud_floater": { elem1: "snow_cloud", elem2: "snow_cloud", chance: 0.003 }, + "snow_cloud": { elem1: "snow_cloud", elem2: "snow_cloud", chance: 0.01 } + }, + category:"gases", + temp:-10, + tempHigh:30, + stateHigh:"rain_cloud", + tempLow:-200, + stateLow:"hail_cloud", + state:"gas", + density:0.55, + conduct:0.01, + movable:true, + isGas:true + }; + //Main preset + worldgentypes.ice = { + layers: [ + //[0.95, "snow_cloud_floater"], //le cutting room floor has arrived + [0.9, "snow"], + [0.65, "ice"], + [0.6, "gravel"], + [0.35, "permafrost"], + [0, "rock"] + ], + temperature: -20 + }; + /*worldgentypes.nuclear_wasteland = { + layers: [ + [0.9, "smoke", 0.5], + [0.9, "rad_snow_cloud_floater", 0.75], + [0.82, "fallout", 0.4], + [0.7, "liquid_irradium", 0.05], + [0.7, "dead_plant", 0.12], + [0.55, "radioactive_dirt"], + [0.45, "radioactive_rock"], + [0.25, "uranium", 0.4], + [0.35, "radioactive_rock", 0.5], + [0.3, "radioactive_gravel", 0.5], + [0.2, "uranium", 0.2], + [0.05, "rock"], + [0, "basalt"], + ], + temperature: -5 //nuclear winter + };*/ + //Dark world + worldgentypes.dark = { + layers: [ + [0.8, "carbon_dioxide"], + [0.65, "ink"], + [0.5, "charcoal"], + [0, "basalt"] + ] + }; + //Money world + worldgentypes.money = { + layers: [ + [0.9, "emerald"], + [0.6, "diamond"], + [0.3, "gold_coin"], + [0.1, "ruby", 1/3], + [0.1, "amethyst", 1/2], + [0.1, "sapphire"], + [-0.1, "pearl", 0.4], + [-0.1, "onyx"] + ] + }; + //Concrete + worldgentypes.concrete = { + layers: [ + [0.13, "concrete"], + [0.1, "concrete", 0.5], + [-0.1, "dirt"] + ], + heightVariance: 0.00000000000000000000000000000001, //R74n didn't use the nullish ??, so 0 is disallowed. + }; + //Star world + //If GWSN can have a decidedly Earth-y name and a space concept, then I should be able to do the same + //Supplementary elements + elements.liquid_stellar_plasma = { + color: "#ffffbd", + colorOn: "#ffffbd", + behavior: [ + "XX|M2%5 AND CR:plasma%1|XX", + "M2|XX|M2", + "M1|M1|M1", + ], + behaviorOn: [ + "XX|M2%10 AND M1%0.5 AND CR:plasma%2.3|XX", + "M2|XX|M2", + "M1|M1|M1", + ], + tick: function(pixel) { + almostSun(pixel,0.6,stellarPlasmaSpreadWhitelist); + }, + temp:5500, + isGas: true, + tempLow:2300, + stateLow: "plasma", + category: "liquids", + state: "liquid", + density: 1000, //density actually depends on depth in the star: https://astronomy.stackexchange.com/a/32734 + conduct: 0.5, + }; + elements.stellar_plasma = { + color: "#ffffbd", + colorOn: "#ffffbd", + behavior: [ + "M2|M1 AND CR:plasma%0.6|M2", + "M1 AND CR:plasma%0.6|XX|M1 AND CR:plasma%0.6", + "M2|M1 AND CR:plasma%0.6|M2", + ], + behaviorOn: [ + "M2|M1 AND CR:plasma%1|M2", + "M1 AND CR:plasma%1|XX|M1 AND CR:plasma%1", + "M2|M1 AND CR:plasma%1|M2", + ], + tick: function(pixel) { + almostSun(pixel,0.5,stellarPlasmaSpreadWhitelist); + }, + temp:5500, + tempLow:2300, + stateLow: "plasma", + category: "gases", + state: "gas", + density: 10, + conduct: 0.5, + }; + elements.neutron_star = { + color: "#e9eaf7", + colorOn: "#ffffbd", + behavior: [ + "XX|CR:neutron%0.1|XX", //no neutrinos though + "CR:neutron%0.1|XX|CR:neutron%0.1", + "XX|CR:neutron%0.1|XX" + ], + tick: function(pixel) { + nsTick(pixel,0.7,stellarPlasmaSpreadWhitelist); + }, + temp: 1e8, + category: "special", + state: "gas", + density: 1e17, + insulate: true, + conduct: 1, + }; + elements.liquid_degenerate_neutronium = { + color: "#e9eaf7", + behavior: [ + "XX|M2%5 AND CR:neutron%0.6|XX", + "M2|XX|M2", + "M1|M1|M1", + ], + behaviorOn: [ + "XX|M2%10 AND M1%0.5 AND CR:neutron%1.2|XX", + "M2|XX|M2", + "M1|M1|M1", + ], + tick: function(pixel) { + nsTick(pixel,0.7,stellarPlasmaSpreadWhitelist); + }, + temp:1e6, + isGas: true, + tempLow:2300, + stateLow: elements.liquid_neutronium ? "liquid_neutronium" : "neutron", + category: "liquids", + state: "liquid", + density: 100000, //i'm not doing any more research on these neutron stars because google is useless + conduct: 1, + }; + elements.gaseous_degenerate_neutronium = { + color: "#e9eaf7", + behavior: [ + "M2|M1 AND CR:neutron%0.6|M2", + "M1 AND CR:neutron%0.6|XX|M1 AND CR:neutron%0.6", + "M2|M1 AND CR:neutron%0.6|M2", + ], + behaviorOn: [ + "M2|M1 AND CR:neutron%1|M2", + "M1 AND CR:neutron%1|XX|M1 AND CR:neutron%1", + "M2|M1 AND CR:neutron%1|M2", + ], + tick: function(pixel) { + nsTick(pixel,0.6,stellarPlasmaSpreadWhitelist); + }, + temp:1e6, + isGas: true, + tempLow:2300, + stateLow: "neutron", + category: "gases", + state: "gas", + density: 10000, //i'm not doing any more research on these neutron stars because google is useless + conduct: 1, + }; + elements.supernova.behavior[1] = elements.supernova.behavior[1].split("|"); + elements.supernova.behavior[1][1] = elements.supernova.behavior[1][1].replace("void","amba_black_hole") + ",neutron_star,neutron_star,amba_black_hole,amba_black_hole,amba_black_hole" + elements.supernova.behavior[1] = elements.supernova.behavior[1].join("|"); + elements.plasma.noConduct = ["plasma_torch","stellar_plasma","liquid_stellar_plasma","liquid_degenerate_neutronium","gaseous_degenerate_neutronium","neutron_star"]; //I can't suppress the charge overlay and keep the tick color, only effective with noConduct.js but not strictly required + //Tangentially linked + elements.rainbow_sun = { + color: ["#ffbdbd", "#f2ffbd", "#bdffd7", "#bdd7ff", "#f2bdff"], + tick: function(pixel) { + starLightAndConduction(pixel,rainbowSunColor(pixel),["sun","nellsun","rainbow_sun"]) + }, + reactions: { + "hydrogen": { "elem2":"helium", "temp1":5 }, + "helium": { "elem2":"carbon_dioxide", "temp1":5, "tempMax":3600 }, + "carbon_dioxide": { "elem2":"neon", "temp1":5, "tempMax":1800 }, + }, + temp: 5700, + tempLow: -100, + stateLow: "supernova", + category: "special", + state: "gas", + //density: 1408, + insulate: true, + nellfireImmune: true, + }; + //Tangentially linked + elements.rainbow_fire = { + color: [ + {h: 330, s: 100, l: 56}, + {h: 0, s: 100, l: 59}, + {h: 22, s: 100, l: 58}, + {h: 42, s: 100, l: 57}, + {h: 60, s: 100, l: 55}, + {h: 73, s: 100, l: 49}, + {h: 120, s: 100, l: 49.5}, + {h: 159, s: 100, l: 52}, + {h: 159, s: 100, l: 52}, + {h: 180, s: 100, l: 49.5}, + {h: 197, s: 100, l: 59}, + {h: 240, s: 100, l: 58.5}, + {h: 280, s: 94, l: 53}, + {h: 307, s: 100, l: 55} + ].map(x => convertHslObjects(x,"hex")), + behavior: behaviors.UL_UR, + reactions: { + "fire": { "elem1": "rainbow_fire" }, + "water": { "elem1": "color_smoke" }, + "steam": { "elem1": "color_smoke" }, + "carbon_dioxide": { "elem1": "color_smoke" }, + "dirty_water": { "elem1": "color_smoke" }, + "salt_water": { "elem1": "color_smoke" }, + "sugar_water": { "elem1": "color_smoke" }, + }, + nellfireImmune: true, + fireSpawnChance: 0, + temp:610, + tempLow:102, + stateLow: "color_smoke", + tempHigh: 7000, + stateHigh: "plasma", + category: "energy", + burning: true, + burnTime: 44, + burnInto: "color_smoke", + state: "gas", + density: 0.21, + ignoreAir: true, + noMix: true, + }; + elements.color_smoke.tempHigh = 610; + elements.color_smoke.stateHigh = "rainbow_fire"; + //I guess other worlds do still fall under the purview of TGJS (future alice: i forgot what tgjs stands for) + function nellsunColor(pixel) { + if (pixel.temp < 0) { pixel.color = pixelColorPick(pixel,"#615e5e"); var c=0 } + else if (pixel.temp < 300) { pixel.color = pixelColorPick(pixel,"#664962"); var c=0 } + else if (pixel.temp < 500) { pixel.color = pixelColorPick(pixel,"#714487"); var c=0.00004 } + else if (pixel.temp < 850) { pixel.color = pixelColorPick(pixel,"#6a43bf"); var c=0.00015 } + else if (pixel.temp < 1300) { pixel.color = pixelColorPick(pixel,"#c356db"); var c=0.0005 } + else if (pixel.temp < 1800) { pixel.color = pixelColorPick(pixel,"#f04ac4"); var c=0.0015 } + else if (pixel.temp < 2100) { pixel.color = pixelColorPick(pixel,"#f788c5"); var c=0.004 } + else if (pixel.temp < 2400) { pixel.color = pixelColorPick(pixel,"#f7a3b8"); var c=0.007 } + else if (pixel.temp < 3200) { pixel.color = pixelColorPick(pixel,"#ffd1d9"); var c=0.01 } + else if (pixel.temp < 3900) { pixel.color = pixelColorPick(pixel,"#fce1e1"); var c=0.02 } + else if (pixel.temp < 4600) { pixel.color = pixelColorPick(pixel,"#fff5f5"); var c=0.035 } + else if (pixel.temp < 6100) { pixel.color = pixelColorPick(pixel,_cc.w.h); var c=0.05 } + else if (pixel.temp < 7200) { pixel.color = pixelColorPick(pixel,"#f4fad9"); var c=0.075 } + else if (pixel.temp < 8300) { pixel.color = pixelColorPick(pixel,"#e4f2c2"); var c=0.1 } + else if (pixel.temp < 10400) { pixel.color = pixelColorPick(pixel,"#c6f2a2"); var c=0.125 } + else if (pixel.temp < 12500) { pixel.color = pixelColorPick(pixel,"#90f277"); var c=0.15 } + else if (pixel.temp < 15600) { pixel.color = pixelColorPick(pixel,"#75f754"); var c=0.175 } + else if (pixel.temp < 18700) { pixel.color = pixelColorPick(pixel,"#5aff30"); var c=0.2 } + else if (pixel.temp < 21800) { pixel.color = pixelColorPick(pixel,"#1df54f"); var c=0.25 } + else if (pixel.temp < 28900) { pixel.color = pixelColorPick(pixel,"#3ce873"); var c=0.3 } + else if (pixel.temp < 36000) { pixel.color = pixelColorPick(pixel,"#4fdb90"); var c=0.35 } + else if (pixel.temp < 45600) { pixel.color = pixelColorPick(pixel,"#5dcfa7"); var c=0.4 } + else if (pixel.temp < 52200) { pixel.color = pixelColorPick(pixel,"#4fe3af"); var c=0.45 } + else if (pixel.temp < 58300) { pixel.color = pixelColorPick(pixel,"#3cfad4"); var c=0.5 } + else if (pixel.temp < 63400) { pixel.color = pixelColorPick(pixel,"#26f8ff"); var c=0.6 } + else if (pixel.temp < 68500) { pixel.color = pixelColorPick(pixel,"#19d9ff"); var c=0.7 } + else if (pixel.temp < 73600) { pixel.color = pixelColorPick(pixel,"#08b1ff"); var c=0.8 } + else { pixel.color = pixelColorPick(pixel,"#0099ff"); var c=0.9 } + return c; + }; + function nellSLAC(pixel,c,whitelist=["sun","nellsun"]) { + for (var i = 0; i < adjacentCoords.length; i++) { + var x = pixel.x+adjacentCoords[i][0]; + var y = pixel.y+adjacentCoords[i][1]; + if (isEmpty(x,y)) { + if (Math.random() <= c) { + createPixel("light", x, y); + pixelMap[x][y].color = pixel.color; + }; + } else if (!outOfBounds(x,y)) { + var newPixel = pixelMap[x][y]; + //console.log(whitelist,newPixel.element,whitelist.includes(newPixel.element)); + if (pixel.temp!==newPixel.temp && whitelist.includes(newPixel.element)) { + var avg = (pixel.temp + newPixel.temp)/2; + pixel.temp = avg; + newPixel.temp = avg; + pixelTempCheck(pixel); + pixelTempCheck(newPixel); + } + } + } + }; + elements.nellsun = { + color: ["#ff26ac", "#ffb8e4", _cc.w.h, "#b7ffa8", "#2df7b4"], + tick: function(pixel) { + nellSLAC(pixel,nellsunColor(pixel)); + }, + reactions: { + "hydrogen": { "elem2":"helium", "temp1":5 }, + "helium": { "elem2":"carbon_dioxide", "temp1":5, "tempMax":3600 }, + "carbon_dioxide": { "elem2":"neon", "temp1":5, "tempMax":1800 }, + }, + temp: 5504, + tempLow: -100, + stateLow: "supernova", + category: "special", + state: "gas", + //density: 1408, + insulate: true, + nellfireImmune: true, + }; + elements.nellfire = { + excludeRandom: true, + color: ["#ff8929","#ffb429","#ffde0a"], + behavior: [ + "M1|M1|M1", + "M1%10 AND M2|HT%2|M1%10 AND M2", + "XX|M2|XX" + ], + reactions: { + "fire": { "elem2": "nellfire" }, + /*"water": { "elem1": "smoke" }, + "steam": { "elem1": "smoke" }, + "carbon_dioxide": { "elem1": "smoke" }, + "dirty_water": { "elem1": "smoke" }, + "salt_water": { "elem1": "smoke" }, + "sugar_water": { "elem1": "smoke" },*/ + }, + tick: function(pixel) { + if(pixel.burning) { + delete pixel.burning; + delete pixel.burnStart; + pixel.nellburn = true; + pixel.nellburnStart ??= pixelTicks; + }; + }, + nellBurningWhenConverted: true, + temp:900, + nellFireSpawnChance: 0, + //tempLow:200, + //stateLow: "smoke", + category: "energy", + burning: true, + nellburnTime: 50, + nellburnInto: null, + state: "gas", + density: 0.06, + ignoreAir: true, + noMix: true, + desc: "Researchers first came into this place and thought it was Hell— it wasn't, so they renamed it Nell (Not Hell). They still named the materials in it hell references, though.", + }, + elements.dantite = { + color: ["#5effba", "#85edd1", "#62d9c7", "#3efa9c", "#21b9db"], + tempHigh: 2000, + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 1442, + hardness: 0.47, + nellfireImmune: "torch", + }; + elements.molten_dantite = { + color: ["#5eff6b", "#70faaa", "#31e08b", "#a1f051"], + density: 4012, + hardness: 0.84, + nellfireImmune: "torch", + }; + elements.limtupyte = { //λίμνη τοῦ πυρός + color: ["#212121", "#212121", "#40221f", "#611d14"], + behavior: behaviors.WALL, + category: "solids", + state: "solid", + density: 8012, + hardness: 0.87, + nellfireImmune: true, + tempHigh: 9011, + stateHigh: "alpha_limtupyte", + }; + elements.alpha_limtupyte = { + name: "α-limtupyte", + color: ["#6e1414", "#8f210b", "#a34910", "#c27115"], + density: 10112, + behavior: behaviors.MOLTEN, + category: "molten", + hidden: true, + state: "liquid", + hardness: 0.91, + nellfireImmune: true, + temp: 9500, + tempHigh: 11022, + stateHigh: "beta_limtupyte", + tempLow: 9011, + stateLow: "limtupyte", + }; + elements.beta_limtupyte = { + name: "β-limtupyte", + color: ["#e68917", "#ffbd24", "#ffe940", "#ffff61"], + density: 13178, + behavior: behaviors.MOLTEN, + category: "molten", + hidden: true, + state: "liquid", + hardness: 0.93, + nellfireImmune: true, + temp: 12000, + tempHigh: 14316, + stateHigh: "limtupyte_gas", + tempLow: 11022, + stateLow: "alpha_limtupyte", + }; + elements.limtupyte_gas = { + color: ["#ffff80", "#ffe940", "#feffd1", _cc.w.h], + density: 17.12, + behavior: behaviors.GAS, + category: "states", + hidden: true, + state: "gas", + hardness: 1, + nellfireImmune: true, + temp: 15000, + tempLow: 14316, + stateLow: "beta_limtupyte", + }; + //Main preset + worldgentypes.star = { + layers: [ + [0.9, "stellar_plasma"], + [0.65, "liquid_stellar_plasma"], + [0.4, "liquid_stellar_plasma", 1/2], + [0, "sun"], + ], + complexity: 100, + baseHeight: 0.3, + temperature: 6500, + }; + //Radioactive Desert + //Main preset + /*worldgentypes.nuclear_wasteland_desert = { + layers: [ + [0.97, "fallout", 0.4], + [0.95, "radioactive_gravel", 0.6], + [0.65, "liquid_irradium", 0.01], + [0.65, "cancer", 0.02], + [0.65, "bone", 0.02], + [0.65, "radioactive_sand"], + [0.55, "cancer", 0.01], + [0.55, "bone", 0.01], + [0.3, "radioactive_sandstone"], + [0.05, "radioactive_rock"], + [-0.78, "radioactive_basalt"] + ], + temperature: -13 + };*/ + runAfterLoad(function() { + elements.muddy_water.reactions.mud.elem2 = "soil_sediment"; + elements.muddy_water.reactions.muddy_water.elem2 = "soil_sediment" + }); + //PRIMITIVE IN-GAME CONSOLE ## + //featuring stars + customWorldTypes = {}; + if(localStorage.getItem("customWorldTypes") == null) { + localStorage.setItem("customWorldTypes",JSON.stringify(customWorldTypes)) + } else { + customWorldTypes = JSON.parse(localStorage.getItem("customWorldTypes")); + for(var name in customWorldTypes) { + worldgentypes[name] = customWorldTypes[name] + }; + runAfterLoad(rebuildWorldgenList) + }; + var promptInputNullishes = ["null","none","","n/a"]; + var eightSpaces = " ".repeat(8); + commandHelpObject = { + "set": "Sets properties for every pixel of a given type.\nUsage: set [property] [element] [value] \nDon't include framing characters []<>.\nThe element can be \"all\" to set the property for every pixel.\nNote: Strings can't have spaces because spaces are the separator used in the parsing split().\nArguments in [brackets] are required and ones in are optional.", + "test": "Test.", + "setdimensions": "#This command clears the canvas#\nSets the width and height of the canvas and resets it to regenerate pixelMap.\nThis is offsetted so it doesn't count the OoB area on the top left; a 50x50 save will have a 50x50 usable area.\nUsage: setdimensions [width] [height] .\nDon't include framing characters []<>.\nArguments in [brackets] are required and ones in are optional.", + "pixelsize": "Sets the size of the pixels on screen. If no size is given, it instead alerts the current pixel size. Usage: pixelsize .\nDon't include framing characters <>.\nArguments in are optional.", + "dimensions": "Alerts the current dimensions. Usage: dimensions", + "fill": "Fills the screen with the given pixel(s). Usage: fill [Overwrite pixels? (bool)] [element] .\nDon't include framing characters []<>.\nArguments in [brackets] are required and ones in are optional.\nAdditional elements are separated by spaces.", + "randomfill": "Fills the screen with pixels from randomChoices. Usage: randomfill \nDon't include framing characters []<>.\nArguments in are optional.", + "count": "Tells you the amount of a specified pixel on the screen. Usage: count [element]\nDon't include framing characters []<>.\nArguments in [brackets] are required.", + "countall": "Logs to console a list of all elements on screen and their amounts. Usage: countall.", + "worldgen": +`Names or sets the current world type, lists all world types, or generates a given world type. +Usages: +${eightSpaces}Show the current worldgen setting: worldgen +${eightSpaces}List all available worldgen settings: worldgen list +${eightSpaces}Set the current worldgen setting: worldgen set [setting] +${eightSpaces}Generate a specified worldgen setting: worldgen generate [setting]`, + "defineworldgen": +`Creates or replaces a worldgen preset. +Usage (See below for formats): [name] [layers] . +Don't include framing characters []<>. +Arguments in [brackets] are required and ones in are optional.", +(Required) name: String. Cannot have spaces. Example: grass +(Required) layers: +${eightSpaces}Each layer is specified as [RelativeBottomStartPosition:ElementName]<:PixelProbability> +${eightSpaces}Layers are joined with the semicolon ; +${eightSpaces}Layer definitions must not have any spaces. +${eightSpaces}Example full layer definition: 0.85:grass;0.5:dirt;0.05:rock;-0.2:basalt`, + "defineworldgen2": +`(Optional) baseHeight: Number (ideally between 0 and 1). Default: 0.5. +(Optional) heightVariance: Number. Default: 0.5. +(Optional) complexity: Number. Default: 20. +(Optional) temperature: Number. No default value (use null or none to skip). +(Optional) decor: +${eightSpaces}Each decor layer is specified as [ElementName:PixelProbability]<:DistanceFromTop><:ListOfHexColors> +${eightSpaces}Distance from top, if not specified, defaults to 5. +${eightSpaces}The fourth part (optional) is a list of hex codes (like #FFFFFF) separated by commas. +${eightSpaces}Example full decor definition: bird:0.04:10:#FF0000,#FFFF00,#00FF00;diamond:0.3:5`, + "help": "Usage: help \nDon't include framing characters []<>\nArguments in are optional." + }; + commandHelpObject.stars = "Clears the screen and replaces it with random stars. Usage: stars \nDon't include framing characters <>.\nArguments in are optional." + commandHelpObject.starseed = "Alerts the last used seed for stars. Usage: starseed"; + var lastStarSeed = "[None]"; + function seededCreateLargeStar(x,y,minRadius,maxRadius,minTemp,maxTemp,randomFunction) { + //console.log("start"); + var sunPixels = fillCircleReturn("sun",x,y,seededRandBetween(minRadius,maxRadius,randomFunction),true); + //console.log("filled"); + var randTemp = seededRandBetween(minTemp,maxTemp,randomFunction); + //console.log("setting temps"); + for(pixelIndex = 0; pixelIndex < sunPixels.length; pixelIndex++) { + //console.log("pixel " + pixelIndex, sunPixels[pixelIndex].element); + sunPixels[pixelIndex].temp = randTemp; + }; + //console.log(sunPixels.map(x => x.element)); + //console.log("finished"); + return true; + }; + //G + elements.red_giant = { + color: "#f19898", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,3,4,1800,3300,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.blue_giant = { + color: "#a085eb", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,2,3,20000,80000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.yellow_giant = { + color: "#fafad4", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,2,3,6000,11000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + //SG + elements.red_supergiant = { + color: "#f48585", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,6,8,1700,3200,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.blue_supergiant = { + color: "#93b0ec", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,5,7,19000,83000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.yellow_supergiant = { + color: "#f4f9ae", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,5,7,5500,10500,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + //HG + elements.red_hypergiant = { + color: "#ee5d5d", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,9,12,1600,3100,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.blue_hypergiant = { + color: "#719df4", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,8,11,18000,84000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.yellow_hypergiant = { + color: "#f7f990", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,8,11,5000,10000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + /* //Commented debug code: Yellow ultragiant spawner definition but for after the game has already fully loaded + elements.yellow_ultragiant = { + color: convertColorFormats("#f7f990","rgb"), + colorObject: convertColorFormats("#f7f990","json"), + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,14,17,5000,11000,Math.random); + }, + category: "stars", + state: "gas", + density: 1000, + id: nextid, + }; + nextid++; + elementCount++; + createElementButton("yellow_ultragiant");*/ + //luminosity class -I + elements.red_ultragiant = { + color: "#f04343", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,15,18,1500,3000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.blue_ultragiant = { + color: "#5488f0", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,14,17,17000,85000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.yellow_ultragiant = { + color: "#fafc7e", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,14,17,4500,9500,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + //luminosity class -II + elements.red_super_ultragiant = { + color: "#f23329", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,21,25,1400,2900,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.blue_super_ultragiant = { + color: "#3b85ed", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,20,24,16000,86000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.yellow_super_ultragiant = { + color: "#fcfc65", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,20,24,4000,9000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + //luminosity class -III + elements.red_hyper_ultragiant = { + color: "#f51a0f", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,28,31,1300,2800,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.blue_hyper_ultragiant = { + color: "#1b8bf2", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,27,30,15000,87000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.yellow_hyper_ultragiant = { + color: "#faeb46", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,27,30,4000,8500,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + //luminosity class -IV + elements.red_ultra_ultragiant = { + color: "#e01a00", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,34,37,1200,2700,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.blue_ultra_ultragiant = { + color: "#0782ed", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,33,36,14000,88000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + elements.yellow_ultra_ultragiant = { + color: "#f7d52a", + behavior: behaviors.WALL, + tick: function(pixel) { + seededCreateLargeStar(pixel.x,pixel.y,33,36,4000,8000,Math.random); + }, + category: "stars", + state: "gas", + excludeRandom: true, + maxSize: 1, + cooldown: defaultCooldown, + density: 1000, + }; + function rebuildWorldgenList() { //vanilla code + document.getElementById("worldgenselect").innerHTML = ''; + for (var key in worldgentypes) { + document.getElementById("worldgenselect").innerHTML += ""; + }; + }; + function bareClear() { + currentPixels = []; + pixelMap = []; + for (var i = 0; i < width; i++) { + pixelMap[i] = []; + for (var j = 0; j < height; j++) { + pixelMap[i][j] = undefined; + } + } + }; + function alertIfError(alertError,text) { + if(alertError) { + alert(text); + return(true); + } else { + return(false); + } + } + function alertIfOutput(alertOutput,text) { + if(alertOutput) { + alert(text); + return(true); + } else { + return(false); + } + } + function parsefloatFirst(arr) { var array = arr; array[0] = parseFloat(array[0]); return array }; + function stringToLayers(string) { + return string.split(";").map(x => x.split(":")).map(y => parsefloatFirst(y)); + }; + function validateSingleHexCode(hexCode) { + return !!_cc.w.h.match(/^#[0-9A-F]{6}$/); + }; + function validateHexColorArray(colorsArray) { + if(!(colorsArray instanceof Array)) { + return false; + }; + if(colorsArray.length == 0) { + return false; + }; + var colorsIsValid = 1; + for(var i in colorsArray) { + colorsIsValid *= validateSingleHexCode(colorsArray[i]); + }; + return !!colorsIsValid; + }; + function validateSingleLayer(layerArray) { + if(layerArray.length < 2) { + return false; + }; + if(isNaN(layerArray[0])) { + return false; + }; + if(typeof(layerArray[1]) !== "string") { + return false; + }; + if(!elements[layerArray[1]]) { + return false; + }; + if(typeof(layerArray[2]) !== "undefined") { + if(isNaN(layerArray[2])) { + return false + }; + }; + return true; + }; + function validateLayersStructure(layersArray) { + if(!(layersArray instanceof Array)) { + return false; + }; + if(layersArray.length == 0) { + return false; + }; + var layersIsValid = 1; + for(var i in layersArray) { + layersIsValid *= validateSingleLayer(layersArray[i]); + }; + return !!layersIsValid; + }; + function stringToDecor(string) { + decorLayers = string.split(";").map(x => x.split(":")); + for(var q in decorLayers) { + var decorLayer = decorLayers[q]; + if(decorLayer[1]) { + decorLayer[1] = parseFloat(decorLayer[1]); + }; + if(decorLayer[2]) { + decorLayer[2] = parseInt(decorLayer[2]); + }; + if(decorLayer[3]) { + decorLayer[3] = decorLayer[3].split(","); + }; + }; + return decorLayers; + }; + //console.log(stringToDecor("bird:0.025:10:#FF0000,#FFFF00,#00FF00")); + function validateSingleDecorLayer(decorLayer) { + if(decorLayer.length < 2) { + return false; + }; + if(!elements[decorLayer[0]]) { + return false; + }; + if(isNaN(decorLayer[1])) { + return false; + }; + if(typeof(decorLayer[2]) !== "undefined") { + if(isNaN(decorLayer[2])) { + return false; + }; + }; + if(typeof(decorLayer[3]) !== "undefined") { + if(!(validateHexColorArray(decorLayer[3]))) { + return false; + }; + }; + return true; + }; + function validateDecorStructure(decorArraysArray) { + if(!(decorArraysArray instanceof Array)) { + return false; + }; + if(decorArraysArray.length == 0) { + return false; + }; + var decorIsValid = 1; + for(var i in decorArraysArray) { + decorIsValid *= validateSingleDecorLayer(decorArraysArray[i]); + }; + return !!decorIsValid; + }; + function funniPrompt(argument=null,alertOutput=true,alertError=true) { + argument === null ? inputText = prompt("Enter command") : inputText = argument; + // replace spaces with underscores + if(["",null].includes(inputText)) { + console.log("Prompt canceled"); + return false; + }; + //console.log(inputText); + if(inputText.includes("|")) { + var commands = inputText.split("|"); + var results = []; results.length = commands.length; + for(var cmdi in commands) { + commands[cmdi] = commands[cmdi].trim(); + try { + results[cmdi] = funniPrompt(commands[cmdi]); + } catch(error) { + results[cmdi] = error; + }; + }; + console.log(results); + return results; + }; + inputAsArray = inputText.split(" "); + var firstItem = inputAsArray[0]; + switch(firstItem.toLowerCase()) { + case "set": + if(inputAsArray.length < 4) { + alertIfError(alertError,"Usage: set [property] [element] [value] \nDon't include framing characters []<>.\nThe element can be \"all\" to set the property for every pixel.\nNote: Strings can't have spaces because spaces are the separator used in the parsing split().\nArguments in [brackets] are required and ones in are optional."); + return false; + }; + var property = inputAsArray[1]; + //console.log("Property gotten: " + property); + var inputElement = inputAsArray[2]; + //console.log("Element gotten: " + inputElement); + var value = inputAsArray[3]; + //console.log("Value gotten: " + value); + var type = null; //dummy type for [value]-based assumption + if(inputAsArray.length >= 5) { + type = inputAsArray[4]; + }; + //console.log("Type gotten: " + type); + if(type === null) { + type = null; //catch null type + } else if(numberSynonyms.includes(type.toLowerCase())) { + type = "number"; + } else if(booleanSynonyms.includes(type.toLowerCase())) { + type = "boolean"; + } else if(stringSynonyms.includes(type.toLowerCase())) { + type = "string"; + } else if(arraySynonyms.includes(type.toLowerCase())) { + type = "array"; + } else if(objectSynonyms.includes(type.toLowerCase())) { + type = "object"; + }; + var typeWhitelist = [null,"string","number","boolean","array","object"]; + if(!typeWhitelist.includes(type)) { + alertIfError(alertError,"Unrecognized type: \"" + type + "\"."); + return false; + }; + if(type === null) { + if(defaultStringTypeValues.includes(property)) { + type = "string"; + } else if(defaultNumberTypeValues.includes(property)) { + type = "number"; + } else if(defaultBooleanTypeValues.includes(property)) { + type = "boolean"; + } else if(defaultArrayTypeValues.includes(property)) { + type = "array"; + } else { + alertIfError(alertError,"Type could not be assumed from property. Please specify the type as a fourth argument."); + return false; + } + } + if(type === "number") { + value = parseFloat(value); + if(isNaN(value)) { + alertIfError(alertError,"Value is not a number!"); + return false; + }; + } else if(type === "boolean") { + if(synonymsOfTrue.includes(value.toLowerCase())) { + value = true; + } else if(synonymsOfFalse.includes(value.toLowerCase())) { + value = false; + } else { + alertIfError(alertError,"Unrecognized boolean value: " + value + "."); + return false; + } + } else if(type === "object") { + try { + value = JSON.parse(value); + } catch (error) { + alertIfError(alertError,"JSON is invalid! Note that it requires quotes around keys as well as those curly {} parentheses."); + return false; + }; + } else if(type === "array") { + array = value.split(","); + for(i = 0; i < array.length; i++) { + if(array[i].startsWith("s")) { //String + array[i] = array[i].substring(1); + } else if(array[i].startsWith("n")) { //Number + array[i] = array[i].substring(1); + if(isNaN(parseFloat(array[i]))) { + alert(array[i] + " is not a number!"); + return false; + }; + array[i] = parseFloat(array[i]); + } else if(array[i].startsWith("o")) { //Object + array[i] = array[i].substring(1); + try { + array[i] = JSON.parse(array[i]); + } catch (error) { + alert(array[i] + " is not valid JSON!"); + return false; + }; + } else if(array[i].startsWith("b")) { //Boolean + array[i] = array[i].substring(1); + if(synonymsOfTrue.includes(array[i].toLowerCase())) { + array[i] = true; + } else if(synonymsOfFalse.includes(array[i].toLowerCase())) { + array[i] = false; + } else { + alert("Unrecognized boolean value: " + array[i] + "."); + return false; + }; + } else { + alert(array[i] + ' must start with "s" for a string, "n" for a number, "o" for an object, or "b" for a boolean.'); + return false; + }; + }; + value = array; + } + //The values start out as strings when split from the array, so string is kind of the default form. + //Special validation + if(property === "element") { + var originalInput = value; //for error display + value = mostSimilarElement(value); + if(!elements[value]) { + alertIfError(alertError,"Element " + originalInput + " does not exist!"); + return false; + } + }; + if(property === "x") { + if(!Number.isSafeInteger(value)) { + alertIfError(alertError,"X cannot be a decimal! And what are you doing trying to set position values anyway?"); + return false; + } + }; + if(property === "color") { + if(!value.startsWith("rgb(")) { //if not RGB + if(value.startsWith("hsl(")) { //if HSL + if(!(value.split(",")[1].endsWith('%')) || !(value.split(",")[2].endsWith('%)'))) { //if missing percent symbols + alertIfError(alertError,colorInvalidError); + return false; + }; + } else { //if not RGB and not HSL + alertIfError(alertError,colorInvalidError); + return false; + }; + } + if(value.split(",").length !== 3) { //if too short or long + alertIfError(alertError,colorInvalidError); + return false; + } + if(value.startsWith("rgb(")) { //if RGB + var checkedColorObject = rgbStringToUnvalidatedObject(value); //RGB NaN checking + if(isNaN(checkedColorObject.r) || isNaN(checkedColorObject.g) || isNaN(checkedColorObject.b)) { + //console.log(checkedColorObject); + alertIfError(alertError,"One or more color values are invalid!"); + return false; + }; + } else if(value.startsWith("hsl(")) { //if HSL + var checkedColorObject = hslStringToUnvalidatedObject(value); //HSL NaN checking + if(isNaN(checkedColorObject.h) || isNaN(checkedColorObject.s) || isNaN(checkedColorObject.l)) { + //console.log(checkedColorObject); + alertIfError(alertError,"One or more color values are invalid!"); + return false; + }; + } else { //if neither + alertIfError(alertError,colorInvalidError); + return false; + }; + }; + //Actual setting code; + var setCount = 0; + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + if (!isEmpty(i,j)) { + //console.log("Pixel (" + i + "," + j + ") exists") + if(pixelMap[i][j].element === inputElement || inputElement === "all") { + //console.log("Element is a match: " + inputElement + ", " + pixelMap[i][j].element) + pixelMap[i][j][property] = value; + if(property == "temp") { pixelTempCheck(pixelMap[i][j]) }; + setCount++; + }; + }; + }; + }; + inputElement === "all" ? alertIfOutput(alertOutput,`Set ${property} of ${setCount} pixels to ${value}.`) : alertIfOutput(alertOutput,`Set ${property} of ${setCount} ${inputElement} pixels to ${value}.`) + return true; + case "deleteall": + var elementSpecified = false; + var attemptedCommaSplitOfPossibleElementSpecification = inputAsArray[1].split(","); //a comma-less string becomes a single-string array so it should still detect something like "deleteall fire" + var possibleElementsInACSOPES = attemptedCommaSplitOfPossibleElementSpecification.filter(elementExists); + elementSpecified = (possibleElementsInACSOPES.length > 0); + if(!elementSpecified) { + inputAsArray = inputAsArray.join(" ").replace("deleteall","delete all").split(" ") + }; + //fall through to delete + case "delete": + if(inputAsArray.length < 2) { + alertIfError(alertError,"Usage: delete [element] \nDon't include framing characters []<>.\nThe element can be \"all\" to clear the canvas.\nNote: Strings can't have spaces because spaces are the separator used in the parsing split().\nArguments in [brackets] are required and ones in are optional."); + return false; + }; + + var inputElement = inputAsArray[1]; + if(inputElement.indexOf(",") >= 0) { + inputElement = Array.from(new Set(inputElement.split(","))); + if(inputElement.includes("all")) { + inputElement = "all" + } + }; + + var probability; + if(inputAsArray[2]) { + probability = inputAsArray[2]; + if(probability.match(/(%|cent|per_?hundred|ph)$/i)) { + probability = probability.match(/\d+(\.\d+|)/)?.[0]; + if(!probability) { + probability = 1 + } else { + probability = parseFloat(probability) / 100 + } + } else if(probability.match(/(‰|mille|per_?thousand)$/i)) { + probability = probability.match(/\d+(\.\d+|)/)?.[0]; + if(!probability) { + probability = 1 + } else { + probability = parseFloat(probability) / 1000 + } + } else if(probability.match(/(‱|myriad|per_?ten_?thousand|basis_?point|bp)$/i)) { + probability = probability.match(/\d+(\.\d+|)/)?.[0]; + if(!probability) { + probability = 1 + } else { + probability = parseFloat(probability) / 10000 + } + } else if(probability.match(/(ppm|parts_?per_?million)$/i)) { + probability = probability.match(/\d+(\.\d+|)/)?.[0]; + if(!probability) { + probability = 1 + } else { + probability = parseFloat(probability) / 1e6 + } + } else if(probability.match(/(ppb|parts_?per_?billion)$/i)) { + probability = probability.match(/\d+(\.\d+|)/)?.[0]; + if(!probability) { + probability = 1 + } else { + probability = parseFloat(probability) / 1e9 + } + } else if(probability.match(/(ppt|parts_?per_?trillion)$/i)) { + probability = probability.match(/\d+(\.\d+|)/)?.[0]; + if(!probability) { + probability = 1 + } else { + probability = parseFloat(probability) / 1e12 + } + } else if(probability.match(/(ppq|parts_?per_?quadrillion)$/i)) { + probability = probability.match(/\d+(\.\d+|)/)?.[0]; + if(!probability) { + probability = 1 + } else { + probability = parseFloat(probability) / 1e15 + } + } else if(probability.match(/\d\.?[\/÷]\.?\d/)) { + probability = probability.split(/[\/÷]/).map(x => parseFloat(x)); + if(isNaN(probability[0]) || isNaN(probability[1])) { + probability = 1 + } else { + probability = probability[0] / probability[1] + } + } else { + var parsedProbability = parseFloat(probability); + if(isNaN(parsedProbability)) { + probability = 1 + } else { + probability = parsedProbability; + if(probability > 1) { probability /= 100 } + } + } + } else { + probability = 1 + }; + + //Actual setting code; + var deleteCount = 0; + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + if (!isEmpty(i,j)) { + //console.log("Pixel (" + i + "," + j + ") exists") + var doDelete = inputElement === "all" || (Array.isArray(inputElement) ? inputElement.includes(pixelMap[i][j].element) : pixelMap[i][j].element == inputElement); + if(doDelete) { + if(Math.random() < probability) { + deletePixel(i,j); + deleteCount++ + } + } + } + } + }; + var outputString = Array.isArray(inputElement) ? englishFormatList(inputElement) : inputElement; + inputElement === "all" ? alertIfOutput(alertOutput,`Deleted ${deleteCount} pixels.`) : alertIfOutput(alertOutput,`Deleted ${deleteCount} total ${outputString} pixels.`) + return true; + case "test": + alertIfOutput(alertOutput,"pong"); + console.log("qwertyuiopasdfghjklzxcvbnm"); + return true; + case "setdimensions": + if(inputAsArray.length < 3) { + alertIfError(alertError,commandHelpObject.setdimensions); + return false; + }; + var argWidth = inputAsArray[1]; + var argHeight = inputAsArray[2]; + var argPixelSize = inputAsArray[3]; + if(argWidth == undefined) { + alertIfError(alertError,commandHelpObject.setdimensions); + return false; + } else { + argWidth = parseInt(argWidth); + if(isNaN(argWidth)) { + alert("Error: width was NaN"); + console.error("setdimensions: supplied width was NaN"); + return false; + } else { + if(argWidth < 1) { + alert("Width must be greater than 0"); + console.error("setdimensions: supplied width was zero or negative"); + return false; + }; + }; + }; + if(argHeight == undefined) { + alertIfError(alertError,commandHelpObject.setdimensions); + return false; + } else { + argHeight = parseInt(argHeight); + if(isNaN(argHeight)) { + alert("Error: height was NaN"); + console.error("setdimensions: supplied height was NaN"); + return false; + } else { + if(argHeight < 1) { + alert("Height must be greater than 0"); + console.error("setdimensions: supplied height was zero or negative"); + return false; + }; + }; + }; + if(argPixelSize == undefined) { + argPixelSize = null; + } else { + argPixelSize = parseFloat(argPixelSize); + if(isNaN(argPixelSize)) { + argPixelSize = null; + alert("pixelSize was NaN, ignoring"); + console.log("setdimensions: supplied pixelSize was NaN"); + } else { + if(argPixelSize <= 0) { + alert("Pixel size was non-positive, ignoring"); + console.error("setdimensions: supplied pixel size was zero or negative"); + argPixelSize = null; + }; + }; + }; + width = argWidth + 1; + height = argHeight + 1; + if(typeof(argPixelSize) === "number" && argPixelSize !== null && !isNaN(argPixelSize)) { + if(argPixelSize > 0) { + pixelSize = argPixelSize; + }; + }; + clearAll(); + return true; + case "pixelsize": + if(inputAsArray.length < 1) { //? + alertIfError(alertError,commandHelpObject.setpixelsize); + return false; + }; + var argPixelSize = inputAsArray[1]; + if(argPixelSize == undefined) { + argPixelSize = null; + } else { + argPixelSize = parseFloat(argPixelSize); + if(isNaN(argPixelSize)) { + alert("Error: size was NaN"); + console.error("pixelsize: supplied pixel size was NaN"); + return false; + }; + }; + if(typeof(argPixelSize) === "number" && argPixelSize !== null && !isNaN(argPixelSize)) { + if(argPixelSize <= 0) { + alert("Pixel size must be greater than 0"); + console.error("pixelsize: supplied pixel size was zero or negative"); + return false; + } else { + document.querySelector('span[setting="pixelsize"]').querySelector("select").selectedIndex = pixelSizeSettingDropdownOtherOptionIndex; + settings.pixelsize = argPixelSize; + resizeCanvas(ctx.canvas.height,ctx.canvas.width,settings.pixelsize,false) + }; + } else { + alert(pixelSize); + }; + return pixelSize; + case "dimensions": + if(inputAsArray.length < 1) { //? + alertIfError(alertError,commandHelpObject.dimensions); + return false; + }; + alert(`width: ${width} +height: ${height} +(Usable area is 1 pixel less in both dimensions)`); + return [width,height]; + case "fill": + if(inputAsArray.length < 3) { + alertIfError(alertError,commandHelpObject.fill); + return false; + }; + var doOverwrite = inputAsArray[1]; + var elementList = inputAsArray.slice(2); + //console.log(elementList); + for(i = 0; i < elementList.length; i++) { + var elementInConsideration = elementList[i] + var originalElement = elementInConsideration; //also for error display + elementInConsideration = mostSimilarElement(elementInConsideration); + if(!elements[elementInConsideration]) { + alertIfError(alertError,"Element " + originalElement + " does not exist!"); + return false; + } + elementList[i] = elementInConsideration; + }; + //console.log(elementList); + if(synonymsOfTrue.includes(doOverwrite.toLowerCase())) { + doOverwrite = true; + } else if(synonymsOfFalse.includes(doOverwrite.toLowerCase())) { + doOverwrite = false; + } else { + alertIfError(alertError,"Unrecognized boolean value: " + doOverwrite + "\n Note that for this command, the boolean value goes first."); + return false; + } + //console.log(doOverwrite); + //console.log(elementList); + //Fill code + var fillCount = 0; + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + var randomElement = elementList[Math.floor(Math.random() * elementList.length)]; + if(doOverwrite) { + if(!isEmpty(i,j,true)) { deletePixel(i,j) }; + }; + if (isEmpty(i,j,false)) { + createPixel(randomElement,i,j); + fillCount++; + }; + }; + }; + alertIfOutput(alertOutput,`Placed ${fillCount} pixels`); + return fillCount; + case "randomfill": + if(inputAsArray.length < 1) { //somehow? + alertIfError(alertError,"Usage: randomfill \nDon't include framing characters []<>.\nArguments in are optional."); + return false; + }; + var doOverwrite = null; + if(inputAsArray.length > 1) { + var doOverwrite = inputAsArray[1]; + if(synonymsOfTrue.includes(doOverwrite.toLowerCase())) { + doOverwrite = true; + } else if(synonymsOfFalse.includes(doOverwrite.toLowerCase())) { + doOverwrite = false; + } else { + alertIfError(alertError,"Unrecognized boolean value: " + value); + return false; + }; + } else { + doOverwrite = true; + }; + var elementList = randomChoices; + //Fill code + var fillCount = 0; + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + var randomElement = elementList[Math.floor(Math.random() * elementList.length)]; + if(doOverwrite) { + if(!isEmpty(i,j,true)) { deletePixel(i,j) }; + }; + if (isEmpty(i,j,false)) { + createPixel(randomElement,i,j); + fillCount++; + }; + }; + }; + alertIfOutput(alertOutput,`Placed ${fillCount} random pixels`); + return fillCount; + case "count": + if(inputAsArray.length < 2) { + alertIfError(alertError,"Usage: count [element]\nDon't include framing characters []<>.\nNote: The element name can't have a space in it because spaces are the separator used in the parsing split().\nArguments in [brackets] are required."); + return false; + }; + var inputElement = inputAsArray[1]; + //console.log("Element gotten: " + inputElement); + var originalInput = inputElement; //for error display + inputElement = mostSimilarElement(inputElement); + //console.log("Element gotten: " + inputElement); + if(typeof(elements[inputElement]) === "undefined") { + alertIfError(alertError,"Element " + originalInput + " does not exist!"); + return false; + } + //Actual counting code; + var count = 0; + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + if (!isEmpty(i,j)) { + //console.log("Pixel (" + i + "," + j + ") exists") + if(pixelMap[i][j].element === inputElement) { + //console.log("Element is a match: " + inputElement + ", " + pixelMap[i][j].element) + count++; + }; + }; + }; + }; + alertIfOutput(alertOutput,`There are ${count} pixels of ${inputElement}`); + return count; + case "countall": + var listObject = {}; + //Listing code; + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + if (!isEmpty(i,j)) { + var pixel = pixelMap[i][j]; + var element = pixel.element; + if(!listObject[pixel.element]) { + listObject[pixel.element] = 1; + } else { + listObject[pixel.element]++; + } + }; + }; + }; + var formattedList = ""; + var zelements = Object.keys(listObject); + for(k = 0; k < zelements.length; k++) { + var elementName = zelements[k]; + var elementCount = listObject[elementName]; + formattedList += `${elementName}: ${elementCount}\n`; + }; + alertIfOutput(alertOutput,"Elements counts logged to console"); + console.log(formattedList); + return listObject; + case "wg": + case "worldgen": + if(inputAsArray.length < 1) { + alertIfError(alertError,commandHelpObject.worldgen); + return false; + }; + var action = inputAsArray[1]; + if(!action) { + alertIfOutput(`Current worldgen setting: ${settings.worldgen}`); + return settings.worldgen; + }; + var worldgenTypesList = Object.keys(worldgentypes).concat(["off"]); + if(["list","lst","ls","l"].includes(action)) { + alertIfOutput(alertOutput,worldgenTypesList.join(", ")); + console.log(worldgenTypesList.map(x => " " + x).join("\n")); + return worldgenTypesList; + } else { + var targetPreset = inputAsArray[2]; + if(!targetPreset) { + alertIfError(alertError,commandHelpObject.worldgen); + return false; + }; + if(!(worldgenTypesList.includes(targetPreset))) { + alertIfError(alertError,`No such preset ${targetPreset}!`); + return false; + }; + if(["select","sel","pick","set","s"].includes(action)) { + settings.worldgen = targetPreset; + alertIfOutput(alertOutput,`Worldgen setting set to ${targetPreset}`); + return true; + } else if(["generate","gen","make","worldgen","wg","g","w","world","run","do","world"].includes(action)) { + bareClear(); + if(targetPreset == "off") { + alertIfOutput(alertOutput,`Cleared world`); + } else { + console.log(targetPreset,worldgentypes[targetPreset].complexity); + worldGen(worldgentypes[targetPreset]); + alertIfOutput(alertOutput,`Generated preset ${targetPreset}`); + }; + return true; + } else { + alertIfError(alertError,commandHelpObject.worldgen); + return false; + }; + }; + return true; + case "dwg": + case "defineworldgen": + if(inputAsArray.length < 3) { + alertIfError(commandHelpObject.defineworldgen); + alertIfError(commandHelpObject.defineworldgen2); + return false; + }; + var presetName = inputAsArray[1]; + //overwrite confirm below + var newPreset = {}; + var layers = stringToLayers(inputAsArray[2]); + if(!validateLayersStructure(layers)) { + alertIfError(alertError,"Layers definition is invalid or malformed!"); + return false; + }; + newPreset.layers = layers; + var baseHeight = inputAsArray[3]; + if(typeof(baseHeight) !== "undefined") { + if(promptInputNullishes.includes(baseHeight)) { + baseHeight = "0.5"; + }; + baseHeight = parseFloat(baseHeight); + if(isNaN(baseHeight)) { + alertIfError(alertError,"Invalid baseHeight!"); + return false; + }; + newPreset.baseHeight = baseHeight; + }; + var heightVariance = inputAsArray[4]; + if(typeof(heightVariance) !== "undefined") { + if(promptInputNullishes.includes(heightVariance)) { + heightVariance = "0.5"; + }; + heightVariance = parseFloat(heightVariance); + if(isNaN(heightVariance)) { + alertIfError(alertError,"Invalid heightVariance!"); + return false; + }; + newPreset.heightVariance = heightVariance; + }; + var complexity = inputAsArray[5]; + if(typeof(complexity) !== "undefined") { + if(promptInputNullishes.includes(complexity)) { + complexity = "20"; + }; + complexity = parseFloat(complexity); + if(isNaN(complexity)) { + alertIfError(alertError,"Invalid complexity!"); + return false; + }; + newPreset.complexity = complexity; + }; + var temperature = inputAsArray[6]; + if(typeof(temperature) !== "undefined") { + if(promptInputNullishes.includes(temperature.toLowerCase())) { + temperature = null; + } else { + temperature = parseFloat(temperature); + if(isNaN(temperature)) { + alertIfError(alertError,"Invalid temperature!"); + return false; + }; + newPreset.temperature = temperature; + }; + }; + var decor = inputAsArray[7]; + if(typeof(decor) !== "undefined") { + decor = stringToDecor(decor); + if(!validateDecorStructure(decor)) { + alertIfError(alertError,"Decor definition is invalid or malformed!"); + return false; + }; + newPreset.decor = decor; + }; + if(worldgentypes[presetName]) { + var doOverwrite = confirm(`Overwrite worldgen preset ${presetName}?`); + if(!doOverwrite) { + alertIfError(alertError,"defineworldgen canceled"); + return false; + }; + }; + worldgentypes[presetName] = newPreset; + customWorldTypes[presetName] = newPreset; + localStorage.setItem("customWorldTypes",JSON.stringify(customWorldTypes)); + settings.worldgen = presetName; + rebuildWorldgenList(); + alertIfOutput(alertOutput, `Defined worldgen preset ${presetName}. +Make sure to save your command in a file if you want to add this preset again.` + ); + console.log(inputText); + return [presetName,newPreset]; + case "stars": + var starDensity = inputAsArray[1]; + var seed = inputAsArray[2]; //〜カクセイ〜 + if(starDensity == undefined) { + starDensity = 0.001 + } else { + starDensity = parseFloat(starDensity); + if(isNaN(starDensity)) { + alert("starDensity was NaN, defaulting to 0.001"); + starDensity = 0.001; + }; + }; + var stringSeed = false; + var seedString = null; + if(seed === undefined) { + seed = Math.random(); + stringSeed = false; + } else { + if(isNaN(parseFloat(seed))) { + stringSeed = true; + seedString = seed; + seed = cyrb128(seed)[2]; + } else { + stringSeed = false; + seed = parseFloat(seed); + }; + }; + lastStarSeed = stringSeed ? seedString : seed; + //console.log(stringSeed); + //console.log(lastStarSeed); + var randomFunction = mulberry32(seed); + clearAll(); + for(j = 1; j < height; j++) { + for(i = 1; i < width; i++) { + if(randomFunction() < starDensity) { + if(isEmpty(i,j,false)) { + var value = randomFunction() ** 4; + if(value < 0.3) { + createPixelReturn("sun",i,j).temp = seededRandBetween(1800,3300,randomFunction); + } else if(value < 0.55) { + createPixelReturn("sun",i,j).temp = seededRandBetween(3300,5500,randomFunction); + } else if(value < 0.70) { + createPixelReturn("sun",i,j).temp = seededRandBetween(5500,8000,randomFunction); + } else if(value < 0.8) { + createPixelReturn("sun",i,j).temp = seededRandBetween(8000,13000,randomFunction); + } else if(value < 0.85) { + createPixelReturn("sun",i,j).temp = seededRandBetween(13000,35000,randomFunction); + } else if(value < 0.88) { + createPixelReturn("sun",i,j).temp = seededRandBetween(35000,90000,randomFunction); + } else { //other stuff + var value2 = randomFunction(); + if(value2 < 0.5) { //giant stars + var value3 = randomFunction(); + if(value3 < 0.6) { //favor red giants + var sunPixels = fillCircleReturn("sun",i,j,seededRandBetween(3,4,randomFunction)); + var randTemp = seededRandBetween(1800,3300,randomFunction); + for(pixel in sunPixels) { + sunPixels[pixel].temp = randTemp; + }; + } else if(value3 < 0.9) { //blue giants are rarer + var sunPixels = fillCircleReturn("sun",i,j,seededRandBetween(2,3,randomFunction)); + var randTemp = seededRandBetween(20000,80000,randomFunction); + for(pixel in sunPixels) { + sunPixels[pixel].temp = randTemp; + }; + } else { //yellows are even rarer + var sunPixels = fillCircleReturn("sun",i,j,seededRandBetween(2,3,randomFunction)); + var randTemp = seededRandBetween(6000,11000,randomFunction); + for(pixel in sunPixels) { + sunPixels[pixel].temp = randTemp; + }; + }; + } else if(value2 < 0.6) { //supergiants + var value3 = randomFunction(); + if(value3 < 0.6) { + var sunPixels = fillCircleReturn("sun",i,j,seededRandBetween(6,8,randomFunction)); + var randTemp = seededRandBetween(1700,3200,randomFunction); + for(pixel in sunPixels) { + sunPixels[pixel].temp = randTemp; + }; + } else if(value3 < 0.9) { + var sunPixels = fillCircleReturn("sun",i,j,seededRandBetween(5,7,randomFunction)); + var randTemp = seededRandBetween(19000,83000,randomFunction); + for(pixel in sunPixels) { + sunPixels[pixel].temp = randTemp; + }; + } else { + var sunPixels = fillCircleReturn("sun",i,j,seededRandBetween(5,6,randomFunction)); + var randTemp = seededRandBetween(5500,10500,randomFunction); + for(pixel in sunPixels) { + sunPixels[pixel].temp = randTemp; + }; + }; + } else if(value2 < 0.65) { //hypergiants + var value3 = randomFunction(); + if(value3 < 0.6) { + var sunPixels = fillCircleReturn("sun",i,j,seededRandBetween(9,12,randomFunction)); + var randTemp = seededRandBetween(1600,3100,randomFunction); + for(pixel in sunPixels) { + sunPixels[pixel].temp = randTemp; + }; + } else if(value3 < 0.94) { + var sunPixels = fillCircleReturn("sun",i,j,seededRandBetween(8,11,randomFunction)); + var randTemp = seededRandBetween(18000,84000,randomFunction); + for(pixel in sunPixels) { + sunPixels[pixel].temp = randTemp; + }; + } else { + var sunPixels = fillCircleReturn("sun",i,j,seededRandBetween(8,11,randomFunction)); + var randTemp = seededRandBetween(5000,10000,randomFunction); + for(pixel in sunPixels) { + sunPixels[pixel].temp = randTemp; + }; + }; + } else if(value2 < 0.8) { //white dwarfs/neutron stars + if(randomFunction() < 0.8) { //favor white dwarfs + createPixelReturn("sun",i,j).temp = seededRandBetween(100000,300000,randomFunction); + } else { + if(!elements.neutron_star) { + createPixelReturn("sun",i,j).temp = seededRandBetween(100000,300000,randomFunction); + } else { + createPixelReturn("neutron_star",i,j).temp = seededRandBetween(2000000,10000000,randomFunction); + }; + }; + } else { //brown dwarfs + createPixelReturn("sun",i,j).temp = seededRandBetween(100,800,randomFunction); + }; + }; + }; + }; + }; + }; + return true; + break; + case "kakusei": + case "starseed": + alertIfOutput(alertOutput,lastStarSeed); + console.log(lastStarSeed); + return lastStarSeed; + case "help": + var commandsWithoutDwg2 = Object.keys(commandHelpObject).filter(function(cmdName) { return cmdName !== "defineworldgen2" }); + if(inputAsArray.length < 1) { //somehow + alertIfError(alertError,"Usage: help \nDon't include framing characters []<>.\nArguments in are optional."); + return false; + }; + if(inputAsArray.length < 2) { + alertOutput ? alertIfOutput(alertOutput,"Commands: " + commandsWithoutDwg2.join("\n")) : console.log("Commands: " + commandsWithoutDwg2.join(", ")); + } else { + var command = inputAsArray[1]; + if(typeof(commandHelpObject[command]) === "undefined" || command == "defineworldgen2") { + alertIfError(alertError,"Cound not find help for " + command + "."); + return false; + } else { + if(command == "defineworldgen") { + if(alertOutput) { + alert(commandHelpObject.defineworldgen); + alert(commandHelpObject.defineworldgen2); + } else { + console.log(commandHelpObject.defineworldgen + "\n" + commandHelpObject.defineworldgen2); + }; + } else { + if(alertOutput) { + alert(commandHelpObject[command]); + } else { + console.log(commandHelpObject[command]); + }; + }; + return true; + }; + }; + return true; + default: + alertIfError(alertError,`Command ${firstItem} not found!`); + return false; + }; + }; + document.addEventListener("keydown", function(e) { //prop prompt listener + // , = propPrompt() + if ([1,3].includes(shiftDown) && e.keyCode == 49) { //either shift + 1 + funniPrompt(); + }; + }); + elements.funni_prompt = { + color: [_cc.b.h,"#00ff00",_cc.b.h,"#00ff00",_cc.b.h,"#00ff00",_cc.b.h,"#00ff00",_cc.b.h,"#00ff00"], + behavior: behaviors.SELFDELETE, + 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 + // r = changeElementPrompt() + if (e.keyCode == 186) { + e.preventDefault(); + changeElementPrompt(); + } + }); + function changeElementPrompt() { + var cmToElement = prompt("Enter what you want to change pixels to"); + if(cmToElement == null) { return }; + // replace spaces with underscores + cmToElement = cmToElement.replace(/ /g, "_"); + cmToElementS = mostSimilarElement(cmToElement); + if (cmToElementS === null || cmToElementS === undefined || cmToElementS === "") { + alert("Element \"" + cmToElement + "\" not found! Defaulting to sand."); + cmToElementS = "sand"; + }; + changeTo = cmToElementS; + updateChangeDescriptions(); + } + function updateChangeDescriptions() { + elements.change.desc = "Changes any pixels it is used on to a specified type.
Currently replacing pixels with \"" + changeTo + "\".
Press [;] or click here to open the change prompt."; + elements.alt_change.desc = "Changes any pixels it is used on to a specified type, but keeping their non-element-based properties.
Currently replacing pixels with \"" + changeTo + "\".
Press [;] or click here to open the change prompt."; + elements.alt_alt_change.desc = "Changes any pixels it is used on to a specified type, but keeping their non-element-based properties except for color.
Currently replacing pixels with \"" + changeTo + "\".
Press [;] or click here to open the change prompt."; + }; + elements.change = { + color: ["#ff0000", "#ff0000", "#ff0000", "#7f00ff", "#0000ff", "#0000ff", "#0000ff"], + tool: function(pixel) { + changePixel(pixel,changeTo,true); + }, + category: "tools", + desc: "Changes any pixels it is used on to a specified type.
Currently replacing pixels with \"" + changeTo + "\".
Press [;] or click here to open the change prompt.", + }; + elements.alt_change = { + color: ["#ffff00", "#ffff00", "#ffff00", "#cf7f4f", "#ff00ff", "#ff00ff", "#ff00ff"], + tool: function(pixel) { + pixel.element = changeTo; + }, + category: "tools", + desc: "Changes any pixels it is used on to a specified type, but keeping their non-element-based properties.
Currently replacing pixels with \"" + changeTo + "\".
Press [;] or click here to open the change prompt.", + hidden: true, + }; + elements.alt_alt_change = { + color: ["#00ff00", "#00ff00", "#00ff00", "#cfcf00", "#ff0000", "#ff0000", "#ff0000"], + tool: function(pixel) { + pixel.element = changeTo; + pixel.color = pixelColorPick(pixel); + }, + category: "tools", + desc: "Changes any pixels it is used on to a specified type, but keeping their non-element-based properties except for color.
Currently replacing pixels with \"" + changeTo + "\".
Press [;] or click here to open the change prompt.", + hidden: true, + }; + //ADDITIONAL RAYS ## + runAfterAutogen(function() { + snowAndIceCache = Object.keys(elements).filter(function(name) { + return name.endsWith("snow") || name.endsWith("ice") || name == "rime" + }) + }); + lightlikes = ["light","flash","laser","radiation","insulate_flash"]; + firelikes = ["fire","plasma","smoke","stellar_plasma","liquid_plasma","liquid_stellar_plasma"]; + grbBreakIntos = Object.keys(elements).filter(function(elemName) { + var to = typeof(elements[elemName]); + if(to == "undefined") { + return false + } else { + var to2 = typeof(elements[elemName].breakInto); + if(to2 == "undefined") { + return false + } else { + if(elements[elemName].breakInto instanceof Array) { + return elements[elemName].breakInto.includes("gamma_ray_burst") + } else { + return elements[elemName].breakInto == "gamma_ray_burst" + } + } + } + }); + elements.insulate_flash = { + hidden: true, + color: "#fffdcf", + tick: function(pixel) { + if (Math.random() < 0.75 && pixelTicks - pixel.start > 1) { + deletePixel(pixel.x, pixel.y) + } + }, + reactions: { + "blood": { elem1:"pointer" }, + "molten_stained_glass": { elem1:"rainbow" }, + "gray_goo": { elem1:"static" } + }, + category: "energy", + temp: 40, + state: "gas", + density: 1, + tempLow: -270, + stateLow: "light", + hidden: true, + noMix: true, + insulate: true + }; + elements.heat_ray.tick = function(pixel) { + var x = pixel.x; + for (var y = pixel.y; y < height; y++) { + if (outOfBounds(x, y)) { + break; + } + if (isEmpty(x, y)) { + if (Math.random() > 0.025) { continue } + createPixel("insulate_flash", x, y); + pixelMap[x][y].color = "#ff0000"; + } + else { + if (elements[pixelMap[x][y].element].isGas) { pixelMap[x][y].temp -= 50; continue; } + if (elements[pixelMap[x][y].element].id === elements.heat_ray.id) { break } + pixelMap[x][y].temp += 100; + break; + } + } + deletePixel(pixel.x, pixel.y); + }; + elements.sun.isSun = true; + if(elements.nellsun) { elements.nellsun.isSun = true }; + if(elements.rainbow_sun) { elements.rainbow_sun.isSun = true }; + elements.cold_ray = { + color: ["#00ffae","#00ffff"], + tick: function(pixel) { + var x = pixel.x; + for (var y = pixel.y; y < height; y++) { + if (outOfBounds(x, y)) { + break; + } + if (isEmpty(x, y)) { + if (Math.random() > 0.05) { continue } + createPixel("insulate_flash", x, y); + pixelMap[x][y].color = "#00ffff"; + } + else { + if (elements[pixelMap[x][y].element].isGas) { + if(elements[pixelMap[x][y].element].isSun) { + pixelMap[x][y].temp -= 0.5; + } else { + pixelMap[x][y].temp -= 50; + }; + continue; + }; + if (elements[pixelMap[x][y].element].id === elements.cold_ray.id) { break } + pixelMap[x][y].temp -= 150; + break; + } + } + deletePixel(pixel.x, pixel.y); + }, + temp: -200, + category: "energy", + state: "gas", + excludeRandom: true, + noMix: true + }; + elements.freeze_ray = { + color: ["#7fbfff","#bfffff"], + tick: function(pixel) { + var x = pixel.x; + for (var y = pixel.y; y < height; y++) { + if (outOfBounds(x, y)) { + break; + } + if (isEmpty(x, y)) { + if (Math.random() > 0.02) { continue } + createPixel("insulate_flash", x, y); + pixelMap[x][y].color = "#e1f8fc"; + } + else { + var otherPixel = pixelMap[x][y]; + var otherInfo = elements[otherPixel.element]; + //Gas: Freeze chance, cool, always penetrate + if (otherInfo.isGas) { + if(Math.random() < 0.05 && otherInfo.stateLow) { + if(otherInfo.stateLow.includes("supernova") || otherInfo.stateLow.includes("gamma_ray_burst")) { + //do nothing + } else { + freezePixel(otherPixel) + }; + }; + if(elements[otherPixel.element].isSun) { + otherPixel.temp -= 0.5; + } else { + otherPixel.temp -= 50; + }; + continue; + }; + //Self: Break + if (otherInfo.id === elements.freeze_ray.id) { break } + //Non-gas, Freeze chance, cool more, half penetrate + if(Math.random() < 0.05 && otherInfo.stateLow) { + if(otherInfo.stateLow.includes("supernova") || otherInfo.stateLow.includes("gamma_ray_burst")) { + //do nothing + } else { + freezePixel(otherPixel) + }; + }; + pixelMap[x][y].temp -= 150; + if(Math.random() < 0.05) { + if(!isEmpty(x,y-1,false)) { + if(pixelMap[x]?.[y-1]?.element && lightlikes.includes(pixelMap[x][y-1].element)) { + deletePixel(x,y-1); + }; + }; + var newSnow = tryCreatePixelReturn("snow",x,y-1); + if(newSnow) { newSnow.temp = -100 }; + }; + //Penetrate snow and ice + if(snowAndIceCache && snowAndIceCache.includes(otherPixel.element)) { + continue; + }; + if(Math.random() < 0.7) { //thanks, I hate random continue + continue; + }; + break; + } + } + deletePixel(pixel.x, pixel.y); + }, + temp: -200, + category: "energy", + state: "gas", + excludeRandom: true, + noMix: true + }; + elements.melt_ray = { + color: ["#ffbf7f","#ffffbf"], + tick: function(pixel) { + var x = pixel.x; + for (var y = pixel.y; y < height; y++) { + if (outOfBounds(x, y)) { + break; + } + if (isEmpty(x, y)) { + if (Math.random() > 0.02) { continue } + createPixel("insulate_flash", x, y); + pixelMap[x][y].color = "#e1f8fc"; + } + else { + var otherPixel = pixelMap[x][y]; + var otherInfo = elements[otherPixel.element]; + //Gas: Heat, always penetrate + if (otherInfo.isGas) { + if(Math.random() < 0.05 && otherInfo.stateLow) { + meltPixel(otherPixel) + }; + if(elements[otherPixel.element].isSun) { + otherPixel.temp += 0.5; + } else { + otherPixel.temp += 50; + }; + continue; + }; + //Self: Break + if (otherInfo.id === elements.melt_ray.id) { break } + //Non-gas, Melt chance, heat more, half penetrate + if(Math.random() < 0.05 && !otherInfo.isGas) { + meltPixel(otherPixel) + }; + pixelMap[x][y].temp += 200; + if(Math.random() < 0.05) { + if(!isEmpty(x,y-1,false)) { + if(pixelMap[x]?.[y-1]?.element && lightlikes.includes(pixelMap[x][y-1].element)) { + deletePixel(x,y-1); + }; + }; + var newPlasma = tryCreatePixelReturn("liquid_plasma",x,y-1); + if(newPlasma) { newPlasma.temp += 500 }; + }; + //Penetrate snow and ice + if(firelikes && firelikes.includes(otherPixel.element)) { + continue; + }; + if(Math.random() < 0.7) { //thanks, I hate random continue + continue; + }; + break; + } + } + deletePixel(pixel.x, pixel.y); + }, + temp: 4000, + category: "energy", + state: "gas", + excludeRandom: true, + noMix: true + }; + elements.smash_ray = { + color: ["#ff9999", "#8c8279"], + tick: function(pixel) { + if(pixel.done) { deletePixel(pixel); return }; + var x = pixel.x; + for (var y = pixel.y; y < height; y++) { + if (outOfBounds(x, y)) { + break; + } + if (isEmpty(x, y)) { + if (Math.random() > 0.05) { continue } + createPixel("flash", x, y); + pixelMap[x][y].color = "#edd0c5"; + } + else { + var otherPixel = pixelMap[x][y] + var otherInfo = elements[otherPixel.element]; + if (!(grbBreakIntos.includes(otherPixel.element))) { + if (otherInfo.isGas) { + if(Math.random() > ((otherInfo.hardness ?? 0) ** 2)) { breakPixel(otherPixel,false,false) }; + if(otherPixel && !(lightlikes.includes(otherPixel.element))) { + var vels = [randomIntegerBetweenTwoValues(-7,7),randomIntegerBetweenTwoValues(-7,7)]; + otherPixel.vx = vels[0]; + otherPixel.vy = vels[1]; + }; + continue; + }; + if (otherInfo.id === elements.heat_ray.id) { break } + if(Math.random() > ((otherInfo.hardness ?? 0) ** 2)) { breakPixel(otherPixel,false,false) }; + if(otherPixel) { + var vels = [randomIntegerBetweenTwoValues(-7,7),randomIntegerBetweenTwoValues(-5,0)]; + otherPixel.vx = vels[0]; + otherPixel.vy = vels[1]; + }; + if(Math.random() < Math.max(0.8,0.3 + ((1 - (otherInfo.hardness ?? 0)) / 2))) { //thanks, I hate random continue + continue; + }; + break; + } + } + } + pixel.done = true; + deletePixel(pixel.x, pixel.y); + }, + temp: 20, + category: "energy", + state: "gas", + excludeRandom: true, + noMix: true + }; + //combines heat ray and smash ray + elements.death_ray = { + color: ["#a88d77", "#ff4a36"], + tick: function(pixel) { + var x = pixel.x; + for (var y = pixel.y; y < height; y++) { + if (outOfBounds(x, y)) { + break; + } + if (isEmpty(x, y)) { + if (Math.random() > 0.05) { continue } + createPixel("insulate_flash", x, y); + pixelMap[x][y].color = "#eb7b59"; + } + else { + var otherPixel = pixelMap[x][y] + var otherInfo = elements[otherPixel.element]; + otherPixel.temp += (400 * (shiftDown + 1)); + if(otherPixel.del) { continue }; + if (!(grbBreakIntos.includes(otherPixel.element))) { + if (otherInfo.isGas) { + if(Math.random() > ((otherInfo.hardness ?? 0) ** (4 + shiftDown))) { breakPixel(otherPixel,false,false) }; + if(otherPixel && !(lightlikes.includes(otherPixel.element))) { + var vels = [randomIntegerBetweenTwoValues(-7 - (shiftDown * 2),7 + (shiftDown * 2)),randomIntegerBetweenTwoValues(-7 - (shiftDown * 2),7 + (shiftDown * 2))]; + otherPixel.vx = vels[0]; + otherPixel.vy = vels[1]; + }; + continue; + }; + if (otherInfo.id === elements[pixel.element].id) { break } + if(Math.random() > ((otherInfo.hardness ?? 0) ** (2 + shiftDown))) { breakPixel(otherPixel,false,false) }; + if(otherPixel) { + var vels = [randomIntegerBetweenTwoValues(-9 - (shiftDown * 2),9 + (shiftDown * 2)),randomIntegerBetweenTwoValues(-7 - (shiftDown * 2),0 + (shiftDown * 2))]; + otherPixel.vx = vels[0]; + otherPixel.vy = vels[1]; + }; + if(Math.random() < ((shiftDown / 20) + (Math.max(0.9,0.4 + ((1 - (otherInfo.hardness ?? 0)) / 2))))) { //thanks, I hate random continue + continue; + }; + break; + } + } + } + deletePixel(pixel.x, pixel.y); + }, + temp: 4000, + category: "energy", + state: "gas", + excludeRandom: true, + noMix: true + }; + elements.annihilation_ray = { + color: ["#220c0c", "#c11515"], + tick: function(pixel) { + var x = pixel.x; + for (var y = pixel.y; y < height; y++) { + if (outOfBounds(x, y)) { + break; + } + if (isEmpty(x, y)) { + if (Math.random() > 0.05) { continue } + createPixel("insulate_flash", x, y); + pixelMap[x][y].color = "#292929"; + } + else { + var otherPixel = pixelMap[x][y]; + var otherInfo = elements[otherPixel?.element]; + if(otherPixel) { + otherPixel.temp += 2500 * (shiftDown + 1); + if(otherPixel.del || !otherPixel) { continue }; + if (otherPixel && grbBreakIntos.includes(otherPixel.element)) { + if(Math.random() < 0.01 && otherPixel) { + deletePixel(otherPixel.x,otherPixel.y); + }; + continue; + } else if(otherPixel) { + breakPixel(otherPixel,false,false); + if(otherPixel.del || !otherPixel) { + continue + }; + if(otherPixel ) { + var vels = [randomIntegerBetweenTwoValues(-8,8),randomIntegerBetweenTwoValues(-6,0)]; + otherPixel.vx = vels[0]; + otherPixel.vy = vels[1]; + }; + if(otherPixel && Math.random() < (otherInfo.isGas ? 0.2 : 0.1)) { + deletePixel(otherPixel.x,otherPixel.y); + continue; + }; + if(Math.random() > 0.8) { + continue; + }; + }; + if (otherInfo.id === elements[pixel.element].id) { break } + }; + } + } + deletePixel(pixel.x, pixel.y); + }, + temp: 150000000, + category: "energy", + state: "gas", + excludeRandom: true, + noMix: true + }; + //bless falls within god ray + elements.bless.reactions.dry_dirt = { elem2: "dirt" }; + elements.bless.reactions.dead_cum = { elem2: "cum" }; + elements.bless.reactions.dead_cum_water = { elem2: "cum_water" }; + elements.bless.reactions.dead_cum_ice = { elem2: "cum_ice" }; + elements.bless.reactions.dead_cum_water_ice = { elem2: "cum_water_ice" }; + elements.bless.reactions.dead_cum_snow = { elem2: "cum_snow" }; + elements.bless.reactions.dead_cummy_mud = { elem2: "cummy_mud" }; + elements.bless.reactions.dead_cummy_sand = { elem2: "cummy_sand" }; + elements.bless.reactions.dead_cummy_permafrost = { elem2: "cummy_permafrost" }; + elements.bless.reactions.burnt_cum = { elem2: null }; + elements.bless.reactions.poop = { elem2: null }; + elements.bless.reactions.dried_poop = { elem2: null }; + elements.bless.reactions.shit = { elem2: null }; + elements.bless.reactions.dried_shit = { elem2: null }; + elements.bless.reactions.frozen_shit = { elem2: null }; + elements.bless.reactions.diarrhea = { elem2: null }; + elements.bless.reactions.frozen_diarrhea = { elem2: null }; + elements.bless.reactions.piss = { elem2: null }; + elements.bless.reactions.vomit = { elem2: null }; + elements.bless.reactions.crimson_grass = { elem2: "grass" }; + elements.bless.reactions.crimstone = { elem2: "rock" }; + elements.bless.reactions.crimsand = { elem2: "sand" }; + elements.bless.reactions.red_ice = { elem2: "ice" }; + elements.bless.reactions.crimgravel = { elem2: "gravel" }; + elements.bless.reactions.crimwater = { elem2: "water" }; + elements.bless.reactions.crimsnow = { elem2: "snow" }; + elements.bless.reactions.vicious_mushroom = { elem2: null }; + elements.bless.reactions.crimson_vine = { elem2: "vine" }; + elements.bless.reactions.shadewood = { elem2: "wood" }; + elements.bless.reactions.shadewood_tree_branch = { elem2: "tree_branch" }; + elements.bless.reactions.shadewood_sapling = { elem2: "sapling" }; + elements.bless.reactions.shadewood_sawdust = { elem2: "sawdust" }; + elements.bless.reactions.crimson_leaf = { elem2: "leaf" }; + elements.bless.reactions.ichor = { elem2: null }; //per blood, absent the gods' immune systems (apparenly they don't need immune systems because of immortality anyway) + elements.bless.reactions.virus_bomb = { elem2: null }; + elements.bless.reactions.life_eater_slurry = { elem2: null }; + elements.bless.reactions.life_eater_explosion = { elem2: null }; + elements.bless.reactions.life_eater_virus = { elem2: null }; + elements.bless.reactions.injector_poison = { elem2: null }; + elements.bless.reactions.dead_matter = { elem2: null }; + elements.bless.reactions.life_eater_infected_dirt = { elem2: "dirt" }; + elements.bless.reactions.poisoned_dirt = { elem2: "dirt" }; + elements.bless.reactions.vicious_goldfish = { elem2: "fish" }; + elements.bless.reactions.nellfire = { elem2: "bless" }; + elements.bless.reactions.gloomwind = { elem2: null }; + elements.bless.reactions.gloomfly = { elem2: null }; + elements.bless.reactions.meat_monster = { elem2: null }; + elements.bless.reactions.rotten_ravager = { elem2: null }; + elements.bless.reactions.bone_beast = { elem2: null }; + elements.bless.reactions.poisonwater = { elem2: "water" }; + elements.bless.reactions.poisoned_ice = { elem2: "ice" }; + elements.bless.reactions.poisoned_gas = { elem2: "steam" }; + elements.bless.reactions.corrupt_land = { elem2: "dirt" }; + elements.bless.reactions.corrupt_rock = { elem2: "rock" }; + elements.bless.reactions.withery = { elem2: null }; + elements.bless.reactions.withery_plant = { elem2: null }; + elements.bless.reactions.corrupt_solid_rock = { elem2: "rock_wall" }; + elements.bless.reactions.toxin = { elem2: "antidote" }; + elements.bless.reactions.dead = { elem2: null }; + elements.bless.reactions.brain = { 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 + delete pixel.burning; + delete pixel.burnStart; + } + if (pixel.nellburn) { // change: stop nellburn + delete pixel.nellburn; + delete pixel.nellburnStart; + } + if (pixel.temp > 100) { + pixel.temp = (pixel.temp+100)/2; + pixelTempCheck(pixel); + if (pixel.del) {return} + } + if (pixel.origColor) { + pixel.color = "rgb("+pixel.origColor.join(",")+")"; + delete pixel.origColor; + } + if (pixel.charge) { + delete pixel.charge; + pixel.chargeCD = 16; + } + if (elements.bless.reactions[pixel.element] && Math.random()<0.25) { + var r = elements.bless.reactions[pixel.element]; + var elem2 = r.elem2; + if (elem2 !== undefined) { + if (Array.isArray(elem2)) { elem2 = elem2[Math.floor(Math.random()*elem2.length)]; } + if (elem2 === null) { deletePixel(pixel.x,pixel.y) } + else { changePixel(pixel, elem2); } + } + } + }; + rayAbsorbElements = []; + rayPassElements = []; + function summonRay(element,xIn,intensity,radius) { + var forMin = 0 - radius; + var forMax = radius + 1; + if(intensity < 1) { return }; + for(var i = forMin; i < forMax; i++) { + for(var j = 1; j < intensity + 1; j++) { + var pos = {x: xIn + i, y: j}; + if(isEmpty(pos.x,pos.y)) { + createPixel(element,pos.x,pos.y) + } else { + if(outOfBounds(pos.x,pos.y)) { + break + } else { + var pixel = pixelMap[pos.x][pos.y]; + var pElem = pixel.element; + var data = elements[pElem]; + if(rayAbsorbElements.includes(pElem)) { + break + } else if(rayPassElements.includes(pElem)) { + continue + } else { + if(data.state == "gas") { + continue + } else { + break + } + } + } + } + } + } + }; + elements.orbital_ray_beacon = { + color: "#ebdf91", + behavior: [ + "XX|M2 AND SA|XX", + "SA|XX|SA", + "XX|M1|XX" + ], + breakInto: ["steel_scrap","iron_scrap","copper_scrap","gold_scrap","battery","sapphire","magic"], + temp: 0, + tempHigh: 5010, + insulate: true, + conduct: 1, + stateHigh: ["molten_steel","molten_iron","molten_copper","molten_gold","acid_gas","titanium_gas","molten_sapphire","magic"], + tick: function(pixel) { + pixelTempCheck(pixel); + if(!pixel.charge) { return }; + if(pixel.charge) { + //var intensity = Math.max(1,Math.floor((pixel.temp + 1) / 100)); + if(isEmpty(pixel.x,pixel.y+1,true)) { return }; + var pixelUnder = pixelMap[pixel.x]?.[pixel.y+1]; + if(!pixelUnder) { return }; + switch(pixelUnder.element) { + case "fire": + summonRay("heat_ray",pixel.x,10,5); + break; + case "ultramafic_magma": + case "magma": + case "intermediate_magma": + case "intermediate_felsic_magma": + case "felsic_magma": + case "nellish_magma": + case "rainbow_magma": + case "crimson_magma": + summonRay("heat_ray",pixel.x,16,10); + break; + case "vaporized_ultramafic_magma": + case "vaporized_magma": + case "vaporized_intermediate_magma": + case "vaporized_intermediate_felsic_magma": + case "vaporized_felsic_magma": + case "vaporized_nellish_magma": + case "vaporized_rainbow_magma": + case "vaporized_crimson_magma": + summonRay("heat_ray",pixel.x,24,18); + break; + case "greek_fire": + summonRay("heat_ray",pixel.x,16,25); + break; + case "quark_matter": + summonRay("heat_ray",pixel.x,45,80); + break; + case "cold_fire": + summonRay("cold_ray",pixel.x,10,5); + break; + case "chilly_water": + summonRay("cold_ray",pixel.x,14,8); + break; + case "frostwind": + summonRay("cold_ray",pixel.x,20,15); + break; + case "liquid_frostwind": + summonRay("cold_ray",pixel.x,30,20); + break; + case "gelid_cryotheum": + summonRay("cold_ray",pixel.x,36,25); + break; + case "snow": + summonRay("freeze_ray",pixel.x,3,6); + break; + case "slush": + summonRay("freeze_ray",pixel.x,4,6); + break; + case "packed_snow": + summonRay("freeze_ray",pixel.x,4,7); + break; + case "ice": + summonRay("freeze_ray",pixel.x,4,8); + break; + case "alcohol_ice": + summonRay("freeze_ray",pixel.x,6,11); + break; + case "liquid_nitrogen": + summonRay("freeze_ray",pixel.x,9,18); + break; + case "nitrogen_ice": + summonRay("freeze_ray",pixel.x,11,19); + break; + case "liquid_hydrogen": + summonRay("freeze_ray",pixel.x,14,26); + break; + case "hydrogen_ice": + summonRay("freeze_ray",pixel.x,15,27); + break; + case "liquid_helium": + summonRay("freeze_ray",pixel.x,18,34); + break; + case "tectonic_petrotheum": + summonRay("smash_ray",pixel.x,2,10); + break; + case "bomb": + case "tnt": + summonRay("smash_ray",pixel.x,5,7); + break; + case "cluster_bomb": + summonRay("death_ray",pixel.x,7,11); + break; + case "nuke": + summonRay("annihilation_ray",pixel.x,20,40); + break; + case "cluster_nuke": + summonRay("annihilation_ray",pixel.x,30,60); + break; + case "armageddon": + summonRay("annihilation_ray",pixel.x,40,80); + break; + }; + //if(pixelUnder) { deletePixel(pixelUnder.x,pixelUnder.y) }; + delete pixel.charge; + pixel.chargeCD = 4; + return true; + } + }, + conduct: 1, + category: "machines", + hardness: 0.6 + }; + //PUSHERS ## + elements.up_pusher = { + color: "#9fafdf", + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","pusherRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.pusherRange; + }; + if(p0h) { + p0h.innerText = "Range"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","pusherStrength"); + p1.setAttribute("min","1"); + p1.value = ambaPlaceProperties.pusherStrength; + }; + if(p1h) { + p1h.innerText = "Strength"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + tick: function(pixel) { + pixel.range ??= (ambaPlaceProperties?.pusherRange ?? 10); + pixel.pushStrength ??= (ambaPlaceProperties?.pusherStrength ?? 1); + for(h = 0; h < pixel.pushStrength; h++) { + for(i=(pixel.range - 1); i>=0; i--) { + if (!isEmpty(pixel.x,pixel.y-1-i,true)) { + tryMove(pixelMap[pixel.x][pixel.y-1-i],pixel.x,pixel.y-2-i); + }; + }; + }; + doDefaults(pixel); + }, + category: "machines", + breakInto: ["metal_scrap", "steel", "iron", "glass", "uranium", "tin"], + tempHigh: 2400, + stateHigh: ["molten_aluminum", "molten_steel", "molten_iron", "molten_glass", "molten_uranium", "molten_tin"], + density: 10000, + hardness: 0.85, + conduct: 1, + state: "solid", + movable: false + } + elements.down_pusher = { + color: "#9fafdf", + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","pusherRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.pusherRange; + }; + if(p0h) { + p0h.innerText = "Range"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","pusherStrength"); + p1.setAttribute("min","1"); + p1.value = ambaPlaceProperties.pusherStrength; + }; + if(p1h) { + p1h.innerText = "Strength"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + tick: function(pixel) { + pixel.range ??= (ambaPlaceProperties?.pusherRange ?? 10); + pixel.pushStrength ??= (ambaPlaceProperties?.pusherStrength ?? 1); + for(h = 0; h < pixel.pushStrength; h++) { + for(i=(pixel.range - 1); i>=0; i--) { + if (!isEmpty(pixel.x,pixel.y+1+i,true)) { + tryMove(pixelMap[pixel.x][pixel.y+1+i],pixel.x,pixel.y+2+i); + }; + }; + }; + doDefaults(pixel); + }, + category: "machines", + breakInto: ["metal_scrap", "steel", "iron", "glass", "uranium", "tin"], + tempHigh: 2400, + stateHigh: ["molten_aluminum0", "molten_steel", "molten_iron", "molten_glass", "molten_uranium", "molten_tin"], + density: 10000, + hardness: 0.85, + conduct: 1, + state: "solid", + movable: false + } + elements.left_pusher = { + color: "#9fafdf", + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","pusherRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.pusherRange; + }; + if(p0h) { + p0h.innerText = "Range"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","pusherStrength"); + p1.setAttribute("min","1"); + p1.value = ambaPlaceProperties.pusherStrength; + }; + if(p1h) { + p1h.innerText = "Strength"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + tick: function(pixel) { + pixel.range ??= (ambaPlaceProperties?.pusherRange ?? 10); + pixel.pushStrength ??= (ambaPlaceProperties?.pusherStrength ?? 1); + for(h = 0; h < pixel.pushStrength; h++) { + for(i=(pixel.range - 1); i>=0; i--) { + if (!isEmpty(pixel.x-1-i,pixel.y,true)) { + tryMove(pixelMap[pixel.x-1-i][pixel.y],pixel.x-2-i,pixel.y); + }; + }; + }; + doDefaults(pixel); + }, + category: "machines", + breakInto: ["metal_scrap", "steel", "iron", "glass", "uranium", "tin"], + tempHigh: 2400, + stateHigh: ["molten_aluminum", "molten_steel", "molten_iron", "molten_glass", "molten_uranium", "molten_tin"], + density: 10000, + hardness: 0.85, + conduct: 1, + state: "solid", + movable: false + } + elements.right_pusher = { + color: "#9fafdf", + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","pusherRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.pusherRange; + }; + if(p0h) { + p0h.innerText = "Range"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","pusherStrength"); + p1.setAttribute("min","1"); + p1.value = ambaPlaceProperties.pusherStrength; + }; + if(p1h) { + p1h.innerText = "Strength"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + tick: function(pixel) { + pixel.range ??= (ambaPlaceProperties?.pusherRange ?? 10); + pixel.pushStrength ??= (ambaPlaceProperties?.pusherStrength ?? 1); + for(h = 0; h < pixel.pushStrength; h++) { + for(i=(pixel.range - 1); i>=0; i--) { + if (!isEmpty(pixel.x+1+i,pixel.y,true)) { + tryMove(pixelMap[pixel.x+1+i][pixel.y],pixel.x+2+i,pixel.y); + }; + }; + }; + doDefaults(pixel); + }, + category: "machines", + breakInto: ["metal_scrap", "steel", "iron", "glass", "uranium", "tin"], + tempHigh: 2400, + stateHigh: ["molten_aluminum", "molten_steel", "molten_iron", "molten_glass", "molten_uranium", "molten_tin"], + density: 10000, + hardness: 0.85, + conduct: 1, + state: "solid", + movable: false + } + elements.up_e_pusher = { + color: "#9f9f6f", + properties: { + pushTime: 0, + }, + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","pusherRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.pusherRange; + }; + if(p0h) { + p0h.innerText = "Range"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","pusherStrength"); + p1.setAttribute("min","1"); + p1.value = ambaPlaceProperties.pusherStrength; + }; + if(p1h) { + p1h.innerText = "Strength"; + }; + + showSetterColumn("numeric",2); + var p2 = document.getElementById("propertynumeric2input"); + var p2h = document.getElementById("propertynumeric2heading"); + if(p2) { + p2.setAttribute("set","ePusherLength"); + p2.setAttribute("min","1"); + p2.value = ambaPlaceProperties.ePusherLength; + }; + if(p2h) { + p2h.innerText = "Duration"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + tick: function(pixel) { + pixel.range ??= (ambaPlaceProperties?.pusherRange ?? 10); + pixel.pushStrength ??= (ambaPlaceProperties?.pusherStrength ?? 1); + pixel.pushLength ??= (ambaPlaceProperties?.ePusherLength ?? 5); + pixel.pushTime ??= 0; + if(isNaN(pixel.pushTime) || pixel.pushTime < 0) { pixel.pushTime = 0 }; + if(pixel.charge) { + pixel.pushTime = pixel.pushLength; + }; + if(pixel.pushTime > 0) { + for(h = 0; h < pixel.pushStrength; h++) { + for(i=(pixel.range - 1); i>=0; i--) { + if (!isEmpty(pixel.x,pixel.y-1-i,true)) { + tryMove(pixelMap[pixel.x][pixel.y-1-i],pixel.x,pixel.y-2-i); + }; + }; + }; + pixel.pushTime--; + }; + doDefaults(pixel); + }, + category: "machines", + breakInto: ["metal_scrap", "steel", "iron", "glass", "uranium", "tin"], + tempHigh: 2400, + stateHigh: ["molten_aluminum", "molten_steel", "molten_iron", "molten_glass", "molten_uranium", "molten_tin"], + density: 10000, + hardness: 0.85, + conduct: 1, + state: "solid", + movable: false + } + elements.down_e_pusher = { + color: "#9f9f6f", + properties: { + pushTime: 0, + }, + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","pusherRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.pusherRange; + }; + if(p0h) { + p0h.innerText = "Range"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","pusherStrength"); + p1.setAttribute("min","1"); + p1.value = ambaPlaceProperties.pusherStrength; + }; + if(p1h) { + p1h.innerText = "Strength"; + }; + + showSetterColumn("numeric",2); + var p2 = document.getElementById("propertynumeric2input"); + var p2h = document.getElementById("propertynumeric2heading"); + if(p2) { + p2.setAttribute("set","ePusherLength"); + p2.setAttribute("min","1"); + p2.value = ambaPlaceProperties.ePusherLength; + }; + if(p2h) { + p2h.innerText = "Duration"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + tick: function(pixel) { + pixel.range ??= (ambaPlaceProperties?.pusherRange ?? 10); + pixel.pushStrength ??= (ambaPlaceProperties?.pusherStrength ?? 1); + pixel.pushLength ??= (ambaPlaceProperties?.ePusherLength ?? 5); + pixel.pushTime ??= 0; + if(isNaN(pixel.pushTime) || pixel.pushTime < 0) { pixel.pushTime = 0 }; + if(pixel.charge) { + pixel.pushTime = pixel.pushLength; + }; + if(pixel.pushTime > 0) { + for(h = 0; h < pixel.pushStrength; h++) { + for(i=(pixel.range - 1); i>=0; i--) { + if (!isEmpty(pixel.x,pixel.y+1+i,true)) { + tryMove(pixelMap[pixel.x][pixel.y+1+i],pixel.x,pixel.y+2+i); + }; + }; + }; + pixel.pushTime--; + }; + doDefaults(pixel); + }, + category: "machines", + breakInto: ["metal_scrap", "steel", "iron", "glass", "uranium", "tin"], + tempHigh: 2400, + stateHigh: ["molten_aluminum0", "molten_steel", "molten_iron", "molten_glass", "molten_uranium", "molten_tin"], + density: 10000, + hardness: 0.85, + conduct: 1, + state: "solid", + movable: false + } + elements.left_e_pusher = { + color: "#9f9f6f", + properties: { + pushTime: 0, + }, + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","pusherRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.pusherRange; + }; + if(p0h) { + p0h.innerText = "Range"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","pusherStrength"); + p1.setAttribute("min","1"); + p1.value = ambaPlaceProperties.pusherStrength; + }; + if(p1h) { + p1h.innerText = "Strength"; + }; + + showSetterColumn("numeric",2); + var p2 = document.getElementById("propertynumeric2input"); + var p2h = document.getElementById("propertynumeric2heading"); + if(p2) { + p2.setAttribute("set","ePusherLength"); + p2.setAttribute("min","1"); + p2.value = ambaPlaceProperties.ePusherLength; + }; + if(p2h) { + p2h.innerText = "Duration"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + tick: function(pixel) { + pixel.range ??= (ambaPlaceProperties?.pusherRange ?? 10); + pixel.pushStrength ??= (ambaPlaceProperties?.pusherStrength ?? 1); + pixel.pushLength ??= (ambaPlaceProperties?.ePusherLength ?? 5); + pixel.pushTime ??= 0; + if(isNaN(pixel.pushTime) || pixel.pushTime < 0) { pixel.pushTime = 0 }; + if(pixel.charge) { + pixel.pushTime = pixel.pushLength; + }; + if(pixel.pushTime > 0) { + for(h = 0; h < pixel.pushStrength; h++) { + for(i=(pixel.range - 1); i>=0; i--) { + if (!isEmpty(pixel.x-1-i,pixel.y,true)) { + tryMove(pixelMap[pixel.x-1-i][pixel.y],pixel.x-2-i,pixel.y); + }; + }; + }; + pixel.pushTime--; + }; + doDefaults(pixel); + }, + category: "machines", + breakInto: ["metal_scrap", "steel", "iron", "glass", "uranium", "tin"], + tempHigh: 2400, + stateHigh: ["molten_aluminum", "molten_steel", "molten_iron", "molten_glass", "molten_uranium", "molten_tin"], + density: 10000, + hardness: 0.85, + conduct: 1, + state: "solid", + movable: false + } + elements.right_e_pusher = { + color: "#9f9f6f", + properties: { + pushTime: 0, + }, + onSelect: function() { + showPropertySetter(); + + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","pusherRange"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.pusherRange; + }; + if(p0h) { + p0h.innerText = "Range"; + }; + + showSetterColumn("numeric",1); + var p1 = document.getElementById("propertynumeric1input"); + var p1h = document.getElementById("propertynumeric1heading"); + if(p1) { + p1.setAttribute("set","pusherStrength"); + p1.setAttribute("min","1"); + p1.value = ambaPlaceProperties.pusherStrength; + }; + if(p1h) { + p1h.innerText = "Strength"; + }; + + showSetterColumn("numeric",2); + var p2 = document.getElementById("propertynumeric2input"); + var p2h = document.getElementById("propertynumeric2heading"); + if(p2) { + p2.setAttribute("set","ePusherLength"); + p2.setAttribute("min","1"); + p2.value = ambaPlaceProperties.ePusherLength; + }; + if(p2h) { + p2h.innerText = "Duration"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter() + }, + tick: function(pixel) { + pixel.range ??= (ambaPlaceProperties?.pusherRange ?? 10); + pixel.pushStrength ??= (ambaPlaceProperties?.pusherStrength ?? 1); + pixel.pushLength ??= (ambaPlaceProperties?.ePusherLength ?? 5); + pixel.pushTime ??= 0; + if(isNaN(pixel.pushTime) || pixel.pushTime < 0) { pixel.pushTime = 0 }; + if(pixel.charge) { + pixel.pushTime = pixel.pushLength; + }; + if(pixel.pushTime > 0) { + for(h = 0; h < pixel.pushStrength; h++) { + for(i=(pixel.range - 1); i>=0; i--) { + if (!isEmpty(pixel.x+1+i,pixel.y,true)) { + tryMove(pixelMap[pixel.x+1+i][pixel.y],pixel.x+2+i,pixel.y); + }; + }; + }; + pixel.pushTime--; + }; + doDefaults(pixel); + }, + category: "machines", + breakInto: ["metal_scrap", "steel", "iron", "glass", "uranium", "tin"], + tempHigh: 2400, + stateHigh: ["molten_aluminum", "molten_steel", "molten_iron", "molten_glass", "molten_uranium", "molten_tin"], + density: 10000, + hardness: 0.85, + conduct: 1, + state: "solid", + movable: false + } + //PORTALS ## + //heehee haha melanie martinez + //https://stackoverflow.com/a/60922255 + headBodyObject = { + "head": "body", + }; + elements.portal_in = { + color: "#ee7f00", + properties: { + _channel: 0, + _correspondingPortals: null + }, + insulate: true, + onTryMoveInto: function(pixel,otherPixel) { + try { + if(pixel._correspondingPortals == null) { + return; + }; + if(pixel._correspondingPortals.length <= 0) { + return; + }; + var portal = randomChoice(pixel._correspondingPortals); + var offset = {x: pixel.x - otherPixel.x, y: pixel.y - otherPixel.y}; //teleport destination's offset, inverted by subtraction + var destination = {x: portal.x + offset.x, y: portal.y + offset.y}; + var otherElement = otherPixel.element; + var isHead = (typeof(headBodyObject[otherElement]) !== "undefined"); + var isBody = (typeof(getKeyByValue(headBodyObject,otherElement)) !== "undefined"); + var isBipartite = xor(isHead,isBody); //a head being its own body will break the code + if(isBipartite) { + if(isHead) { + var dead = otherPixel.dead; + var body = pixelMap[otherPixel.x][otherPixel.y+1]; + if(body == undefined) { body = null }; + if(!dead && (body !== null)) { + if(offset.y == -1) { + offset.y--; + destination.y--; + }; + var headSpotIsEmpty = isEmpty(destination.x,destination.y,false); + var bodySpotIsEmpty = isEmpty(destination.x,destination.y+1,false); + if(headSpotIsEmpty && bodySpotIsEmpty) { + tryMove(otherPixel,destination.x,destination.y); + tryMove(body,destination.x,destination.y+1); + }; + } else { + tryMove(otherPixel,destination.x,destination.y); + }; + } else if(isBody) { + var dead = otherPixel.dead; + var head = pixelMap[otherPixel.x][otherPixel.y-1]; + if(head == undefined) { head = null }; + if(!dead && (head !== null)) { + if(offset.y == 1) { + offset.y++; + destination.y++; + }; + var headSpotIsEmpty = isEmpty(destination.x,destination.y-1,false); + var bodySpotIsEmpty = isEmpty(destination.x,destination.y,false); + if(headSpotIsEmpty && bodySpotIsEmpty) { + tryMove(head,destination.x,destination.y-1); + tryMove(otherPixel,destination.x,destination.y); + }; + } else { + tryMove(otherPixel,destination.x,destination.y); + }; + }; + } else { + tryMove(otherPixel,destination.x,destination.y); + }; + } catch(error) { + //ignore stack overflows + if(error.toString().includes("call stack")) { + } else { + throw new Error("error") + } + } + }, + tick: function(pixel) { + pixel._channel = Math.floor(pixel.temp / 100); + pixel._correspondingPortals = currentPixels.filter(function(pixelToCheck) { + return ( + pixelToCheck.element == "portal_out" && + pixelToCheck._channel == pixelChannel + ); + },pixelChannel=pixel._channel); + for(i = 0; i < pixel._correspondingPortals.length; i++) { + pixel._correspondingPortals[i] = {x: pixel._correspondingPortals[i].x, y: pixel._correspondingPortals[i].y}; + }; + //pixel.tempdebug = JSON.stringify(pixel._correspondingPortals); + }, + category: "machines", + state: "solid", + breakInto: ["radiation","laser","iridium","essence","ionized_deuterium","electron","magic","steel","pop","unstable_mistake","explosion","magic","steel","proton","electron","radiation","laser","iridium"], + hardness: 0.999 + }, + elements.portal_out = { + color: "#2222ee", + insulate: true, + tick: function(pixel) { + pixel._channel = Math.floor(pixel.temp / 100); + }, + behavior: behaviors.WALL, + category: "machines", + state: "solid", + insulate: true, + breakInto: ["radiation","laser","iridium","essence","ionized_deuterium","electron","magic","steel","pop","unstable_mistake","explosion","magic","steel","proton","electron","radiation","laser","iridium"], + hardness: 0.999 + } + //MOBS ## + //Prerequisite Functions and Variables + minimumCreeperTries = 3; + maximumCreeperTries = 3; + minimumZombieTries = 3; + maximumZombieTries = 3; + minimumSkeletonTries = 3; + maximumSkeletonTries = 3; + eLists.CREEPER = ["creeper", "angelic_creeper", "hell_creeper", "bombing_creeper", "baby_creeper"]; + headBodyObject = { + "head": "body", + "creeper_head": "creeper_body", + "angelic_creeper_head": "angelic_creeper_body", + "hell_creeper_head": "hell_creeper_body", + "bombing_creeper_head": "bombing_creeper_body", + "zombie_head": "zombie_body", + "skeleton_head": "skeleton_body", + "nothing_there_phase_3_head": "nothing_there_phase_3_body", + }; + var style = document.createElement('style'); //Initialize CSS for creeper spawning's status indicator + style.type = 'text/css'; + style.id = 'creeperStatusStylesheet'; + //initial style conditional branch + if(typeof(settings.creeperSpawning) === "undefined") { //undefined (falsy but it needs special handling) + style.innerHTML = '.creeperStatus { color: #E11; text-decoration: none; }'; + } else { + if(!settings.creeperSpawning) { //falsy: red + style.innerHTML = '.creeperStatus { color: #E11; text-decoration: none; }'; + } else if(settings.creeperSpawning) { //truthy: green + style.innerHTML = '.creeperStatus { color: #1E1; text-decoration: none; }'; + }; + }; + document.getElementsByTagName('head')[0].appendChild(style); + function coordPyth(xA,yA,xB,yB) { //Distance function, used for explosion trigger + var a = Math.abs(xB - xA); + var b = Math.abs(yB - yA); + var c = Math.sqrt(a**2 + b**2); + return c; + }; + function pythSpeed(number1,number2) { + return Math.sqrt(number1**2 + number2**2); + }; + function rgbColorBound(number) { //RGB bounding function, used for safety checking color changes + return Math.min(255,Math.max(0,number)); + }; + function slBound(number) { //SL bounding function (not hue), same use as above + return Math.min(100,Math.max(0,number)); + }; + function angelicUpwardVelocity(pixel,x,y,radius,fire,smoke,power) { //Angelic Creeper's effect, "compatible" with velocity.js by including the modified version of its code in itself + var info = elements[pixel.element] + //console.log("yeet"); + // set the pixel.vx and pixel.vy depending on the angle and power + if (!elements[pixel.element].excludeRandom) { + //console.log("LOOKS LIKE IT'S YEETING TIME!"); + var angle = Math.atan2(pixel.y-y,pixel.x-x); + //console.log(`angle calculated (${angle})`); + pixel.vx = Math.round((pixel.vx|0) + Math.cos(angle) * (radius * power/10)); + //console.log(`vx calculated (${pixel.vx}) for pixel (${pixel.x},${pixel.y})`); + pixel.vy = 0 - Math.abs(Math.round((pixel.vy|0) + Math.sin(angle) * (radius * power/4)) + 4); //massively increased Y velocities even for objects below + //pixel.color = "rgb(255,0,0)"; + //console.log(`vy calculated (${pixel.vy}) for pixel (${pixel.x},${pixel.y})`); + }; + //console.log(`Velocities set`); + //console.log(`end`); + }; + //afterFunction(pixel,x,y,radius,fire,smoke,power,damage); + function hellExplosionFire(pixel,x,y,radius,fire,smoke,power,damage) { //Angelic Creeper's effect, "compatible" with velocity.js by including the modified version of its code in itself + var coords = circleCoords(pixel.x,pixel.y,radius); + for (var i = 0; i < coords.length; i++) { + var x = coords[i].x; + var y = coords[i].y; + if(!isEmpty(x,y,true)) { + var pixel = pixelMap[x][y]; + var info = elements[pixel.element] + if (info.burn) { //Light everything on fire + pixel.burning = true; + pixel.burnStart = pixelTicks; + pixel.temp += 10; //smoke prevention + } else if(Math.random() < 0.05) { //5%/px cursed burning + pixel.burning = true; + pixel.burnStart = pixelTicks; + pixel.temp += 10; + }; + } else if(isEmpty(x,y)) { //if there's space for fire + if (Array.isArray(fire)) { //this should remain "fire" + var newfire = fire[Math.floor(Math.random() * fire.length)]; + } else { + var newfire = fire; + }; + createPixel(newfire,x,y); //add fire + var firePixel = pixelMap[x][y]; + firePixel.temp = Math.max(elements[newfire].temp,firePixel.temp); + firePixel.burning = true; + }; + }; + }; + //explodeAtPlus moved to separate file + if(typeof(settings.creeperSpawning) === "undefined") { //Default creeper setting + setSetting("creeperSpawning",false); + }; + function updateCreeperPreferences() { //Creeper setting handler + if(settings.creeperSpawning) { //If the setting is on + if(typeof(randomEvents.creeper) !== "function") { //add the event if it's missing + randomEvents.creeper = function() { + var amount = Math.floor((Math.random() * maximumCreeperTries)+minimumCreeperTries); //1-3 + //In worldgen worlds, you can expect about half of this because about half of the world is pixels in it. + for(i = 0; i < amount; i++) { //dummy for to break + if(settings.creeperSpawning) { //setting validation + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height + var y = Math.floor(Math.random()*height-1)+1; + if (isEmpty(x,y)) { + // random element from the list of spawnable creepers + var element = spawnCreepers[Math.floor(Math.random()*spawnCreepers.length)]; + // if element is an array, choose a random element from the array + if (Array.isArray(element)) { + element = element[Math.floor(Math.random()*element.length)]; + } + createPixel(element,x,y); + }; + } else { //if false (this function is never supposed to fire with the setting false) + delete randomEvents.creeper; //self-disable + //substitute event + var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]]; + event(); + break; + }; + }; + }; + }; + } else if(!settings.creeperSpawning) { //and if it's off + if(randomEvents.creeper) { delete randomEvents.creeper }; //delete it if it exists. + }; + }; + function toggleCreeperSpawning() { //Creeper toggle handler + if(settings.creeperSpawning != true) { //If it's false + setSetting("creeperSpawning",true); //make it true and update the status display CSS + updateCreeperPreferences(); //apply + document.getElementById("creeperStatusStylesheet").innerHTML = '.creeperStatus { color: #1E1; text-decoration: underline; }'; //Displayed info doen't update until it's pulled up again, so I'm using CSS to dynamically change the color of an element, like with find.js (RIP). + } else { //and the inverse if it's true + setSetting("creeperSpawning",false); + updateCreeperPreferences(); + document.getElementById("creeperStatusStylesheet").innerHTML = '.creeperStatus { color: #E11; text-decoration: none; }'; + }; + }; + //Functions used by Nothing There + function hasPixel(x,y,elementInput) { + if(isEmpty(x,y,true)) { //if empty, it can't have a pixel + return false; + } else { + if(elementInput.includes(",")) { //CSTA + elementInput = elementInput.split(","); + }; + if(Array.isArray(elementInput)) { //if element list + return elementInput.includes(pixelMap[x][y].element); + } else { //if single element + return pixelMap[x][y].element === elementInput; + }; + }; + }; + function arrowAltTb(pixel,breakChanceMultiplier,changetemp=false,defaultBreakIntoDust=false) { + var info = elements[pixel.element]; + var hardness = defaultHardness; + if(typeof(info.hardness) === "number") { + hardness = info.hardness; + }; + hardness = 1 - hardness; //invert hardness, so a hardness of 0 becomes a 100% chance and a hardness of 1 becomes a 0% chance + hardness *= breakChanceMultiplier; + if(Math.random() < hardness) { + return breakPixel(pixel,changetemp=false,defaultBreakIntoDust=false); + } else { + return false; + }; + }; + function nothingThereBulletMovement(pixel,x,y) { + if(!tryMove(pixel,x,y)) { + if(!isEmpty(x,y,true)) { + var thisDensity = elements[pixel.element].density; + var newPixel = pixelMap[x][y]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + var newHardness = 0; + if(nothingThereBulletExcludedElements.includes(newElement)) { + return false; + }; + if(typeof(newInfo.hardness) === "number") { + newHardness = newInfo.hardness; + //it's inverted later + }; + if(typeof(newInfo.state) === "undefined" && newElement !== pixel.element) { //Copy-paste of "break" code + if(Math.random() < ((1 - newHardness) ** 0.6)) { + swapPixels(pixel,newPixel); + //console.log(`nothingThereBulletMovement: Breaking pixel (${newPixel.element}, ${newPixel.x}, ${newPixel.y}))`) + breakPixel(newPixel,false,0.3); + return true; + } else { + deletePixel(pixel.x,pixel.y); + return false; + }; + } else { + if(newElement == pixel.element) { + swapPixels(pixel,newPixel); + return true; + } else if(newInfo.state == "gas") { + swapPixels(pixel,newPixel); + return true; + } else if(newInfo.state == "liquid") { + var newDensity = 1000; + if(typeof(newInfo.density) === "number") { + newDensity = newInfo.density; + //console.log(`density ${newInfo.density} for ${newElement}`); + //} else { + //console.log(`undef density for ${newElement}, 1000 default`); + }; + //console.log(`thisDensity: ${thisDensity}`); + //console.log(`newDensity: ${newDensity}`); + var chance = thisDensity/(thisDensity+newDensity); + //console.log(`${newElement}, ${chance}`) + if(Math.random() < chance) { + swapPixels(pixel,newPixel); + }; + return true; + } else if(newInfo.state == "solid") { + if(Math.random() < ((1 - newHardness) ** 0.6)) { + swapPixels(pixel,newPixel); + //console.log(`nothingThereBulletMovement: Breaking pixel (${newPixel.element}, ${newPixel.x}, ${newPixel.y}))`) + breakPixel(newPixel,false,0.3); + return true; + } else { + deletePixel(pixel.x,pixel.y); + return false; + }; + }; + }; + } else { + return false; + }; + } else { + return true; + }; + }; + //End NT functions + nothingThereBulletExcludedElements = ["wall","nothing_there_phase_1","nothing_there_phase_2","nothing_there_phase_3_body","nothing_there_phase_3_head", "nothing_there_cleaver", "nothing_there_mace"]; + enemyHumanoidArray = ["head","body"] //just in case + spawnCreepers = ["creeper","baby_creeper","angelic_creeper","bombing_creeper","hell_creeper"]; + if(settings.creeperSpawning) { //creeper spawning option + randomEvents.creeper = function() { + var amount = Math.floor((Math.random() * maximumCreeperTries)+minimumCreeperTries); //1-3 + for(i = 0; i < amount; i++) { //dummy for to break + if(settings.creeperSpawning) { //setting validation + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height + var y = Math.floor(Math.random()*height-1)+1; + if (isEmpty(x,y)) { + // random element from the list of spawnable creepers + var element = spawnCreepers[Math.floor(Math.random()*spawnCreepers.length)]; + // if element is an array, choose a random element from the array + if (Array.isArray(element)) { + element = element[Math.floor(Math.random()*element.length)]; + } + createPixel(element,x,y); + }; + } else { //if false (this function is never supposed to fire with the setting false) + delete randomEvents.creeper; //self-disable + //substitute event + var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]]; + event(); + break; + }; + }; + }; + }; + standaloneSpawnCreeper = function(amount=1) { + /* The amount is the maximum amount of *attempts*. Often, less creepers will spawn due to things in the way. + In a generated world, which uses half of the space, you can expect about half of this number to spawn. */ + for(i = 0; i < amount; i++) { //dummy for to break + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height + var y = Math.floor(Math.random()*height-1)+1; + if (isEmpty(x,y)) { + // random element from the list of spawnable creepers + var element = spawnCreepers[Math.floor(Math.random()*spawnCreepers.length)]; + // if element is an array, choose a random element from the array + if (Array.isArray(element)) { + element = element[Math.floor(Math.random()*element.length)]; + } + createPixel(element,x,y); + }; + }; + }; + //Prerequisite Functions and Variables + function headHasBody(pixel) { + var pX = pixel.x; + var pY = pixel.y; + //console.log("Checking head for body"); + if(Object.keys(headBodyObject).includes(pixel.element)) { + //console.log("The head has a corresponding body element."); + if(typeof(pixelMap[pX][pY+1]) === "undefined") { + //console.log("The body's place is empty."); + return false; + } else { + var bodyPixel = pixelMap[pX][pY+1]; + //console.log("The body's place is not empty."); + return (bodyPixel.element === headBodyObject[pixel.element]); + }; + } else { + //console.log("The head does not have corresponding body element."); + return null; + }; + }; + function bodyHasHead(pixel) { + var pX = pixel.x; + var pY = pixel.y; + //console.log("Checking body for head"); + if(Object.values(headBodyObject).includes(pixel.element)) { + //console.log("The body has a corresponding head element."); + if(typeof(pixelMap[pX][pY-1]) === "undefined") { + //console.log("The head's place is empty."); + return false; + } else { + var headPixel = pixelMap[pX][pY-1]; + //console.log("The head's place is not empty."); + return (headPixel.element === getKeyByValue(headBodyObject,pixel.element)); + }; + } else { + //console.log("The body does not have corresponding head element."); + return null; + }; + }; + function zombifyHuman(pixel) { + var pX = pixel.x; + var pY = pixel.y; + if(!["head","body"].includes(pixel.element)) { + //console.log("Not part of a human"); + return false; + } else { + if(pixel.element === "head") { + //console.log("Head"); + if(headHasBody(pixel)) { + //console.log("There's also a body"); + var body = pixelMap[pX][pY+1]; + changePixel(body,"zombie_body",false); + //console.log("Body turned (head path)"); + }; + changePixel(pixel,"zombie_head"); + //console.log("Head turned (head path)"); + } else { + //console.log("Not head (i.e. body)"); + if(bodyHasHead(pixel)) { + //console.log("There's also a head"); + var head = pixelMap[pX][pY-1]; + changePixel(head,"zombie_head",false); + //console.log("Head turned (body path)"); + }; + changePixel(pixel,"zombie_body"); + //console.log("Body turned (body path)"); + }; + }; + }; + function dezombifyHuman(pixel) { + var pX = pixel.x; + var pY = pixel.y; + if(!["zombie_head","zombie_body"].includes(pixel.element)) { + return false; + } else { + if(pixel.element === "zombie_head") { + if(headHasBody(pixel)) { + var body = pixelMap[pX][pY+1]; + changePixel(body,"body",false); + }; + changePixel(pixel,"head"); + } else { + if(bodyHasHead(pixel)) { + var head = pixelMap[pX][pY-1]; + changePixel(head,"head",false); + }; + changePixel(pixel,"body"); + }; + }; + }; + elements.spawner = { + color: "#1c3038", + breakInto: ["steel","steel","smoke","magic",null,null,null,null,null], + properties: { + spawnCounter: 0, + spawnTime: 160, + spawn: null, + squadiusX: 4, + squadiusY: 4, + spawnTries: 4, + //spawnRangeMax: null, //Disabled by default for performance and because I can't think of how to make it count MPL elements towards the limit + }, + tick: function(pixel) { + //validation + if(Array.isArray(pixel.spawn)) { + pixel.spawn = pixel.spawn.filter(function(e){ return elementExists(e) }); + if(pixel.spawn.length == 0) { + pixel.spawn = null; + }; + } else { + if(!elementExists(pixel.spawn)) { + pixel.spawn = null; + }; + }; + if(pixel.spawn === null) { + return false; + }; + if(pixel.spawnCounter <= 0) { + //var pixelsOfSpawnElement = 0; + var newSpawn = pixel.spawn; + if(Array.isArray(newSpawn)) { + newSpawn = newSpawn[Math.floor(Math.random() * newSpawn.length)]; + }; + var xForMin = -1 * pixel.squadiusX; + var xForMax = pixel.squadiusX + 1; + var yForMin = -1 * pixel.squadiusY; + var yForMax = pixel.squadiusY + 1; + /*if(pixel.spawnRangeMax !== null) { + for(xOffset = xForMin; xOffset < xForMax; xOffset++) { + for(yOffset = yForMin; yOffset < yForMax; yOffset++) { + var newX = pixel.x + xOffset; + var newY = pixel.y + yOffset; + if(isEmpty(newX,newY,true) || outOfBounds(newX,newY)) { + continue; + }; + newPixel = pixelMap[newX][newY]; + if(newPixel.element == pixel.spawn || pixel.spawn.includes(newPixel.element)) { + pixelsOfSpawnElement++; + }; + }; + }; + if(pixelsOfSpawnElement > pixel.spawnRangeMax) { + return false; + }; + };*/ + for(s = 0; s < pixel.spawnTries; s++) { + var randomX = pixel.x + randomIntegerBetweenTwoValues(0 - pixel.squadiusX, pixel.squadiusX); + var randomY = pixel.y + randomIntegerBetweenTwoValues(0 - pixel.squadiusY, pixel.squadiusY); + if(isEmpty(randomX,randomY,false)) { + createPixel(newSpawn,randomX,randomY); + }; + }; + pixel.spawnCounter = pixel.spawnTime; + } else { + pixel.spawnCounter--; + }; + }, + hardness: 0.7, + state: "solid", + } + elements.frozen_rotten_meat = { + color: ["#8FB588", "#8FA888"], + behavior: [ + "XX|CR:plague,stench,stench%0.125 AND CH:meat>rotten_meat%1 AND CH:frozen_meat>frozen_rotten_meat%0.85|XX", + "SP%99 AND CH:meat>rotten_meat%1 AND CH:frozen_meat>frozen_rotten_meat%0.85|XX|SP%99 AND CH:meat>rotten_meat%1 AND CH:frozen_meat>frozen_rotten_meat%0.85", + "XX|M1 AND CH:meat>rotten_meat%1 AND CH:frozen_meat>frozen_rotten_meat%0.85|XX", + ], + temp: -18, + tempHigh: 0, + stateHigh: "rotten_meat", + category:"food", + hidden:true, + state: "solid", + density: 1037.5, + }; + elements.rotten_meat.tempLow = -18; + elements.rotten_meat.stateLow = "frozen_rotten_meat"; + elements.zombie_blood = { + color: ["#d18228", "#9a9e2f"], + behavior: behaviors.LIQUID, + reactions: { + "vaccine": { "elem2":null, "chance": 0.01 }, + "plague": { "elem2":null, "chance": 0.01 }, + "virus": { "elem2":null, "chance": 0.01 }, + "cancer": { "elem1":"cancer", "chance": 0.02 }, + /*"rat": { "elem2":"infection", "chance":0.075 }, + "flea": { "elem1":"infection", "chance":0.03 }, + "dirt": { "elem1":null, "elem2":"mud" }, + "sand": { "elem1":null, "elem2":"wet_sand" }, + "mercury": { "elem1":"infection", "elem2":null, "chance":0.05 }, + "carbon_dioxide": { "elem2":null, "chance":0.05 }, + "alcohol": { "elem1":[null,"dna"], "chance":0.02 },*/ + "oxygen": { "elem2":null, "chance":0.04 }, + "blood": { "elem2":"zombie_blood", "chance":0.1 } + }, + viscosity: 30, + tempHigh: 127.55, + stateHigh: ["steam","salt","oxygen","plague"], + tempLow: 0, + category:"liquids", + state: "liquid", + density: 1160, + stain: 0.06, + tick: function(pixel) { + if(Math.random() < 0.2) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < adjacentCoords.length; i++) { + var coord = adjacentCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + if(Math.random() < 0.1) { zombifyHuman(newPixel) }; + }; + }; + }; + }; + }, + }; + var style = document.createElement('style'); //Initialize CSS for zombie spawning's status indicator + style.type = 'text/css'; + style.id = 'zombieStatusStylesheet'; + //initial style conditional branch + if(typeof(settings.zombieSpawning) === "undefined") { //undefined (falsy but it needs special handling) + style.innerHTML = '.zombieStatus { color: #E11; text-decoration: none; }'; + } else { + if(!settings.zombieSpawning) { //falsy: red + style.innerHTML = '.zombieStatus { color: #E11; text-decoration: none; }'; + } else if(settings.zombieSpawning) { //truthy: green + style.innerHTML = '.zombieStatus { color: #1E1; text-decoration: none; }'; + }; + }; + document.getElementsByTagName('head')[0].appendChild(style); + if(typeof(settings.zombieSpawning) === "undefined") { //Default zombie setting + setSetting("zombieSpawning",false); + }; + function updateZombiePreferences() { //Zombie setting handler + if(settings.zombieSpawning) { //If the setting is on + if(typeof(randomEvents.zombie) !== "function") { //add the event if it's missing + randomEvents.zombie = function() { + var amount = Math.floor((Math.random() * maximumZombieTries)+minimumZombieTries); //1-3 + //In worldgen worlds, you can expect about half of this because about half of the world is pixels in it. + for(i = 0; i < amount; i++) { //dummy for to break + if(settings.zombieSpawning) { //setting validation + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height + var y = Math.floor(Math.random()*height-1)+1; + if (isEmpty(x,y)) { + // random element from the list of spawnable zombies + var element = spawnZombies[Math.floor(Math.random()*spawnZombies.length)]; + // if element is an array, choose a random element from the array + if (Array.isArray(element)) { + element = element[Math.floor(Math.random()*element.length)]; + } + createPixel(element,x,y); + }; + } else { //if false (this function is never supposed to fire with the setting false) + delete randomEvents.zombie; //self-disable + //substitute event + var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]]; + event(); + break; + }; + }; + }; + }; + } else if(!settings.zombieSpawning) { //and if it's off + if(randomEvents.zombie) { delete randomEvents.zombie }; //delete it if it exists. + }; + }; + function toggleZombieSpawning() { //Zombie toggle handler + if(settings.zombieSpawning != true) { //If it's false + setSetting("zombieSpawning",true); //make it true and update the status display CSS + updateZombiePreferences(); //apply + document.getElementById("zombieStatusStylesheet").innerHTML = '.zombieStatus { color: #1E1; text-decoration: underline; }'; //Displayed info doen't update until it's pulled up again, so I'm using CSS to dynamically change the color of an element, like with find.js (RIP). + } else { //and the inverse if it's true + setSetting("zombieSpawning",false); + updateZombiePreferences(); + document.getElementById("zombieStatusStylesheet").innerHTML = '.zombieStatus { color: #E11; text-decoration: none; }'; + }; + }; + spawnZombies = ["zombie","baby_zombie"]; + if(settings.zombieSpawning) { //zombie spawning option + randomEvents.zombie = function() { + var amount = Math.floor((Math.random() * maximumZombieTries)+minimumZombieTries); //1-3 + for(i = 0; i < amount; i++) { //dummy for to break + if(settings.zombieSpawning) { //setting validation + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height + var y = Math.floor(Math.random()*height-1)+1; + if (isEmpty(x,y)) { + // random element from the list of spawnable zombies + var element = spawnZombies[Math.floor(Math.random()*spawnZombies.length)]; + // if element is an array, choose a random element from the array + if (Array.isArray(element)) { + element = element[Math.floor(Math.random()*element.length)]; + } + createPixel(element,x,y); + }; + } else { //if false (this function is never supposed to fire with the setting false) + delete randomEvents.zombie; //self-disable + //substitute event + var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]]; + event(); + break; + }; + }; + }; + }; + standaloneSpawnZombie = function(amount=1) { + /* The amount is the maximum amount of *attempts*. Often, less zombies will spawn due to things in the way. + In a generated world, which uses half of the space, you can expect about half of this number to spawn. */ + for(i = 0; i < amount; i++) { //dummy for to break + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height + var y = Math.floor(Math.random()*height-1)+1; + if (isEmpty(x,y)) { + // random element from the list of spawnable zombies + var element = spawnZombies[Math.floor(Math.random()*spawnZombies.length)]; + // if element is an array, choose a random element from the array + if (Array.isArray(element)) { + element = element[Math.floor(Math.random()*element.length)]; + } + createPixel(element,x,y); + }; + }; + }; + var style = document.createElement('style'); //Initialize CSS for skeleton spawning's status indicator + style.type = 'text/css'; + style.id = 'skeletonStatusStylesheet'; + //initial style conditional branch + if(typeof(settings.skeletonSpawning) === "undefined") { //undefined (falsy but it needs special handling) + style.innerHTML = '.skeletonStatus { color: #E11; text-decoration: none; }'; + } else { + if(!settings.skeletonSpawning) { //falsy: red + style.innerHTML = '.skeletonStatus { color: #E11; text-decoration: none; }'; + } else if(settings.skeletonSpawning) { //truthy: green + style.innerHTML = '.skeletonStatus { color: #1E1; text-decoration: none; }'; + }; + }; + document.getElementsByTagName('head')[0].appendChild(style); + if(typeof(settings.skeletonSpawning) === "undefined") { //Default skeleton setting + setSetting("skeletonSpawning",false); + }; + function updateSkeletonPreferences() { //Skeleton setting handler + if(settings.skeletonSpawning) { //If the setting is on + if(typeof(randomEvents.skeleton) !== "function") { //add the event if it's missing + randomEvents.skeleton = function() { + var amount = Math.floor((Math.random() * maximumSkeletonTries)+minimumSkeletonTries); //1-3 + //In worldgen worlds, you can expect about half of this because about half of the world is pixels in it. + for(i = 0; i < amount; i++) { //dummy for to break + if(settings.skeletonSpawning) { //setting validation + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height + var y = Math.floor(Math.random()*height-1)+1; + if (isEmpty(x,y)) { + // random element from the list of spawnable skeletons + var element = spawnSkeletons[Math.floor(Math.random()*spawnSkeletons.length)]; + // if element is an array, choose a random element from the array + if (Array.isArray(element)) { + element = element[Math.floor(Math.random()*element.length)]; + } + createPixel(element,x,y); + }; + } else { //if false (this function is never supposed to fire with the setting false) + delete randomEvents.skeleton; //self-disable + //substitute event + var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]]; + event(); + break; + }; + }; + }; + }; + } else if(!settings.skeletonSpawning) { //and if it's off + if(randomEvents.skeleton) { delete randomEvents.skeleton }; //delete it if it exists. + }; + }; + function toggleSkeletonSpawning() { //Skeleton toggle handler + if(settings.skeletonSpawning != true) { //If it's false + setSetting("skeletonSpawning",true); //make it true and update the status display CSS + updateSkeletonPreferences(); //apply + document.getElementById("skeletonStatusStylesheet").innerHTML = '.skeletonStatus { color: #1E1; text-decoration: underline; }'; //Displayed info doen't update until it's pulled up again, so I'm using CSS to dynamically change the color of an element, like with find.js (RIP). + } else { //and the inverse if it's true + setSetting("skeletonSpawning",false); + updateSkeletonPreferences(); + document.getElementById("skeletonStatusStylesheet").innerHTML = '.skeletonStatus { color: #E11; text-decoration: none; }'; + }; + }; + spawnSkeletons = ["skeleton"]; + if(settings.skeletonSpawning) { //skeleton spawning option + randomEvents.skeleton = function() { + var amount = Math.floor((Math.random() * maximumSkeletonTries)+minimumSkeletonTries); //1-3 + for(i = 0; i < amount; i++) { //dummy for to break + if(settings.skeletonSpawning) { //setting validation + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height + var y = Math.floor(Math.random()*height-1)+1; + if (isEmpty(x,y)) { + // random element from the list of spawnable skeletons + var element = spawnSkeletons[Math.floor(Math.random()*spawnSkeletons.length)]; + // if element is an array, choose a random element from the array + if (Array.isArray(element)) { + element = element[Math.floor(Math.random()*element.length)]; + } + createPixel(element,x,y); + }; + } else { //if false (this function is never supposed to fire with the setting false) + delete randomEvents.skeleton; //self-disable + //substitute event + var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]]; + event(); + break; + }; + }; + }; + }; + standaloneSpawnSkeleton = function(amount=1) { + /* The amount is the maximum amount of *attempts*. Often, less skeletons will spawn due to things in the way. + In a generated world, which uses half of the space, you can expect about half of this number to spawn. */ + for(i = 0; i < amount; i++) { //dummy for to break + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height + var y = Math.floor(Math.random()*height-1)+1; + if (isEmpty(x,y)) { + // random element from the list of spawnable skeletons + var element = spawnSkeletons[Math.floor(Math.random()*spawnSkeletons.length)]; + // if element is an array, choose a random element from the array + if (Array.isArray(element)) { + element = element[Math.floor(Math.random()*element.length)]; + } + createPixel(element,x,y); + }; + }; + }; + /*Start Main Zombie + .................. + ........%%%%...... + .......%%%%%%..... + ......%%%%%OOOOO.. + ....%%%%%%OOOOOOO. + ...%%%%%%%%OOOOO.. + ...%%%%%%%%%%%.... + ...%%%%%%%%%%%.... + ...%%%%%%%%%%%.... + ....%%%%%%%%%%.... + ......%%%%%%%%.... + .......%%...%%.... + .......%%...%%.... + .................. + */ + elements.zombie = { + color: ["#567C44","#199A9A","#41369B"], + category: "life", + properties: { + dead: false, + dir: 1, + panic: 0, + following: false + }, + movable: true, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("zombie_body", pixel.x, pixel.y+1); + pixel.element = "zombie_head"; + pixel.color = pixelColorPick(pixel) + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("zombie_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "zombie_body"; + pixel.color = pixelColorPick(pixel) + } + else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["zombie_body","zombie_head"], + desc: "I'd rather this be toggleable mid-game than require a reload.

If this text is green or underlined, zombies (all types) can spawn. Click here to toggle zombie spawning. If it's on, zombies can spawn through random events." + }; + elements.zombie_body = { + color: "#27719D", + category: "life", + hidden: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "rotten_meat", + tempLow: -30, + stateLow: "frozen_rotten_meat", + burn: 10, + burnTime: 250, + burnInto: "rotten_meat", + breakInto: ["zombie_blood","rotten_meat"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","rotten_meat","rotten_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.025 } + }, + properties: { + dead: false, + dir: 1, + panic: 0 + }, + movable: true, + tick: function(pixel) { + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headPixel = pixelMap[pixel.x][pixel.y-2]; + if (headPixel.element == "zombie_head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 100 + if (pixelTicks-pixel.dead > 100) { + changePixel(pixel,"rotten_meat"); + }; + return; + }; + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true)) { + if(pixelMap[pixel.x][pixel.y-1].element == "head") { + changePixel(pixelMap[pixel.x][pixel.y-1],"zombie_head"); + } else if(pixelMap[pixel.x][pixel.y-1].element == "zombie_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + }; + } else { + var head = null; + }; + } else { var head = null }; + if (isEmpty(pixel.x, pixel.y-1)) { + // create zombie blood if decapitated 10% chance + if (Math.random() < 0.1) { + createPixel("zombie_blood", pixel.x, pixel.y-1); + // set dead to true 10% chance + if (Math.random() < 0.10) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } + else if (Math.random() < 0.1) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!head.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + }, + }; + elements.zombie_head = { + color: "#567C44", + category: "life", + hidden: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "rotten_meat", + tempLow: -30, + stateLow: "frozen_rotten_meat", + burn: 10, + burnTime: 250, + burnInto: "rotten_meat", + breakInto: ["zombie_blood","rotten_meat"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","rotten_meat","rotten_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.025 } + }, + properties: { + dead: false, + following: false, + dir: 1, + panic: 0 + }, + movable: true, + tick: function(pixel) { + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 100 + if (pixelTicks-pixel.dead > 100) { + changePixel(pixel,"rotten_meat"); + }; + return; + }; + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true)) { + if(pixelMap[pixel.x][pixel.y+1].element == "body") { + changePixel(pixelMap[pixel.x][pixel.y+1],"zombie_body"); + } else if(pixelMap[pixel.x][pixel.y+1].element == "zombie_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill body + pixel.dead = body.dead; + }; + } else { + var body = null; + }; + } else { var body = null }; + if(body) { + if(body.dir !== pixel.dir) { //hacky workaround: lock head dir to body dir + pixel.dir = body.dir; + }; + }; + if (isEmpty(pixel.x, pixel.y+1)) { + tryMove(pixel, pixel.x, pixel.y+1); + // create zombie blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) { + createPixel("zombie_blood", pixel.x, pixel.y+1); + // set dead to true 10% chance + if (Math.random() < 0.10) { + pixel.dead = pixelTicks; + } + } + } + //start of most new code + var pX = pixel.x; + var pY = pixel.y; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + if(pixelTicks % 2 == 0) { //reduce rate for performance + /*var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + };*/ + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-35 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { //If not dead + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Infect/kill if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2) + if(Math.random() < 1/3) { //One-third chance to mutilate (changePixel) + if(Math.random() < 1/3) { //One-third chance to change to blood + changePixel(newPixel,"zombie_blood",false); //blood is turned in place + } else { //Remaining 2/3 chance to change to rotten flesh + changePixel(newPixel,"rotten_meat",false); + }; + } else { //Remaining 2/3 chance to turn the human + zombifyHuman(newPixel); + }; + }; + } else { //Mutilate if dead + if(Math.random() < 1/3) { //One-third chance to change to blood + changePixel(newPixel,"zombie_blood",false); //blood is turned in place + } else { //Remaining 2/3 chance to change to rotten flesh + changePixel(newPixel,"rotten_meat",false); + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 35 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { //If not dead + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Infect/kill if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2) + if(Math.random() < 1/3) { //One-third chance to mutilate (changePixel) + if(Math.random() < 1/3) { //One-third chance to change to blood + changePixel(newPixel,"zombie_blood",false); //blood is turned in place + } else { //Remaining 2/3 chance to change to rotten flesh + changePixel(newPixel,"rotten_meat",false); + }; + } else { //Remaining 2/3 chance to turn the human + zombifyHuman(newPixel); + }; + }; + } else { //Mutilate if dead + if(Math.random() < 1/3) { //One-third chance to change to blood + changePixel(newPixel,"zombie_blood",false); //blood is turned in place + } else { //Remaining 2/3 chance to change to rotten flesh + changePixel(newPixel,"rotten_meat",false); + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + }, + }; + //Baby Zombie + elements.baby_zombie = { + color: "#199A9A", + category: "life", + hidden: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "rotten_meat", + tempLow: -30, + stateLow: "frozen_rotten_meat", + burn: 10, + burnTime: 250, + burnInto: "rotten_meat", + breakInto: ["zombie_blood","rotten_meat"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","rotten_meat","rotten_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.025 } + }, + properties: { + dead: false, + dir: 1, + panic: 0 + }, + movable: true, + tick: function(pixel) { + tryMove(pixel, pixel.x, pixel.y+1); // Fall + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 100 + if (pixelTicks-pixel.dead > 100) { + changePixel(pixel,"rotten_meat"); + }; + return; + }; + if (Math.random() < 0.15) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], //dash move + [1*pixel.dir,-1], //slash move + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if(tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!pixel.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + var pX = pixel.x; + var pY = pixel.y; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + if(pixelTicks % 2 == 0) { //reduce rate for performance + /*var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + };*/ + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-35 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { //If not dead + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Infect/kill if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2) + if(Math.random() < 1/3) { //One-third chance to mutilate (changePixel) + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"zombie_blood",false); //blood is turned in place + } else { //Remaining 3/4 chance to change to rotten flesh + changePixel(newPixel,"rotten_meat",false); + }; + } else { //Remaining 2/3 chance to turn the human + zombifyHuman(newPixel); + }; + }; + } else { //Mutilate if dead + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"zombie_blood",false); //blood is turned in place + } else { //Remaining 3/4 chance to change to rotten flesh + changePixel(newPixel,"rotten_meat",false); + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 35 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { //If not dead + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Infect/kill if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2) + if(Math.random() < 1/3) { //One-third chance to mutilate (changePixel) + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"zombie_blood",false); //blood is turned in place + } else { //Remaining 3/4 chance to change to rotten flesh + changePixel(newPixel,"rotten_meat",false); + }; + } else { //Remaining 2/3 chance to turn the human + zombifyHuman(newPixel); + }; + }; + } else { //Mutilate if dead + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"zombie_blood",false); //blood is turned in place + } else { //Remaining 3/4 chance to change to rotten flesh + changePixel(newPixel,"rotten_meat",false); + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + }, + related: ["zombie"], + desc: "Baby zombies: smaller, faster, and more annoying.", + }; + /*Start Main Creeper + %%%%%%%%%%%%%%%%%% + %%%%%%%% %%%%%% + %%%%%%% %%%%% + %%%%%% OOOOO%% + %%%% OOOOOOO% + %%% OOOOO%% + %%% %%%% + %%% %%%% + %%% %%%% + %%%% %%%% + %%%%%% %%%% + %%%%%%% %%% %%%% + %%%%%%% %%% %%%% + %%%%%%%%%%%%%%%%%% + */ + elements.creeper = { + color: ["#D2D2D2", "#BFDFB9", "#94CE89", "#78D965", "#5ED54C", "#58C546", "#50B143", "#479143", "#559552", "#3F8738", "#5B8B59"], + category: "life", + properties: { + dead: false, + dir: 1, + panic: 0, + following: false + }, + movable: true, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("creeper_body", pixel.x, pixel.y+1); + pixel.element = "creeper_head"; + pixel.color = pixelColorPick(pixel) + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("creeper_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "creeper_body"; + pixel.color = pixelColorPick(pixel) + } + else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["creeper_body","creeper_head"], + desc: "I'd rather this be toggleable mid-game than require a reload.

If this text is green or underlined, creepers can spawn. Click here to toggle creeper spawning. If it's on, creepers (all types) can spawn through random events.
To enable automatic creeper generation, set the generateCreepers query parameter." + }; + elements.creeper_body = { + color: ["#D2D2D2", "#BFDFB9", "#94CE89", "#78D965", "#5ED54C", "#58C546", "#50B143", "#479143", "#559552", "#3F8738", "#5B8B59"], + category: "life", + hidden: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -30, + stateLow: "frozen_meat", + burn: 10, + burnTime: 250, + burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","gunpowder"], + breakInto: ["blood","gunpowder"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 } + }, + properties: { + dead: false, + dir: 1, + panic: 0, + charged: false, + didChargeBlueTinted: false + }, + movable: true, + tick: function(pixel) { + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headPixel = pixelMap[pixel.x][pixel.y-2]; + if (headPixel.element == "creeper_head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + } + return + } + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "creeper_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance + if (Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } + else if (Math.random() < 0.1) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!head.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + if(pixel.charge) { + pixel.charged = true; + }; + if(head) { + if(typeof(head.charge) !== "undefined") { + if(head.charge) { + pixel.charged = true; + }; + }; + if(typeof(head.charged) !== "undefined") { + if(head.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 7; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 5; + }; + if(pixel.burning) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + if(!pixel.burnStart) { //I don't like errors. + pixel.burnStart = pixel.ticks; + }; + if(pixelTicks - pixel.burnStart > 30) { + //console.log("Kaboom?"); + explodeAt(pixel.x,pixel.y,explosionRadius); + //console.log("Yes, Rico, kaboom."); + }; + }; + //Head hissing color handler: keeps track of head's hissing for coloring purposes + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.dead || !head || head.dead) { //can't hiss without a head according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(head.hissing) { + //console.log("Ssssssss"); + if(!head.hissStart) { + //console.log("t-30 ticks or whatever it was"); + head.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - head.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + //console.log("the j"); + lightness = slBound(lightness + 1.176); + //console.log(lightness); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + }; + }; + }, + }; + elements.creeper_head = { + color: ["#5B8B59", "#3F8738", "#559552", "#479143", "#50B143", "#58C546"], + category: "life", + hidden: true, + density: 1080, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -30, + stateLow: "frozen_meat", + burn: 10, + burnTime: 250, + burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","gunpowder"], + breakInto: "blood", + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 }, + "oxygen": { "elem2":"carbon_dioxide", "chance":0.5 } + }, + properties: { + dead: false, + following: false, + hissing: false, + charged: false, + didChargeBlueTinted: false + }, + movable: true, + tick: function(pixel) { + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + return + } + } + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "creeper_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null } + if(body) { + if(body.dir !== pixel.dir) { //hacky workaround: lock head dir to body dir + pixel.dir = body.dir; + }; + }; + if (isEmpty(pixel.x, pixel.y+1)) { + tryMove(pixel, pixel.x, pixel.y+1); + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + //start of most new code + var pX = pixel.x; + var pY = pixel.y; + if(pixel.charge) { + pixel.charged = true; + }; + if(body) { + if(typeof(body.charge) !== "undefined") { + if(body.charge) { + pixel.charged = true; + }; + }; + if(typeof(body.charged) !== "undefined") { + if(body.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 10; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 7; + }; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + }; + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-16 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + //console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { //probably misapplying the tolerance from the MC Wiki line: "Creepers will chase after any player, as long as it is within a 16 block (±5%) radius" + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 16 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + //console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + break; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; + }; + }; + }; + }; + }; + //Pre-explosion handler: keeps track of time before the kaboom + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.hissing) { + //console.log("Ssssssss"); + if(pixel.dead || !body || body.dead) { //can't explode without a body according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(!pixel.hissStart) { + //console.log("t-30 ticks or whatever it was"); + pixel.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - pixel.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + lightness = slBound(lightness + 1.176); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + if(pixelTicks - pixel.hissStart > 30) { + //console.log("Kaboom?"); + //console.log(`Exploding with radius ${explosionRadius} (charged: ${pixel.charged})`); + explodeAt(body.x,body.y,explosionRadius); + //console.log("Yes, Rico, kaboom."); + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + }, + }; + /*End Main Creeper + &&&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&&& + &&&&&&&&&X&X&&&&&& + &&&&&&&&&XXX&&&&&& + &&&&&&&&&&X&&&&&&& + &&&&&&&&&&X&&&&&&& + &&& & & & X & &&&& + &&& &&&& + &&&& &&&& + &&&&&& &&&& + &&&&&&& &&& &&&& + &&&&&&& &&& &&&& + &&&&&&&&&&&&&&&&&& + */ + //Baby Creeper + elements.baby_creeper = { + color: ["#D2D2D2", "#BFDFB9", "#94CE89", "#78D965", "#5ED54C", "#58C546", "#50B143", "#479143", "#559552", "#3F8738", "#5B8B59"], + category: "life", + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -30, + stateLow: "frozen_meat", + burn: 10, + burnTime: 250, + burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","gunpowder"], + breakInto: ["blood","gunpowder"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 } + }, + properties: { + dead: false, + dir: 1, + panic: 0, + charged: false, + didChargeBlueTinted: false + }, + movable: true, + tick: function(pixel) { + tryMove(pixel, pixel.x, pixel.y+1); // Fall + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + } + return + } + if (Math.random() < 0.15) { // Move 15% chance (should be 12.5 but 15 looks better) + var movesToTry = [ + [1*pixel.dir,0], //dash move + [1*pixel.dir,-1], //slash move + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if(tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!pixel.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charge) { + pixel.charged = true; + }; + var pX = pixel.x; + var pY = pixel.y; + if(pixel.charged) { + var explosionRadius = 6; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 4; //should be half of the original creeper's radius + }; + if(pixel.burning) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + if(!pixel.burnStart) { //I don't like errors. + pixel.burnStart = pixel.ticks; + }; + if(pixelTicks - pixel.burnStart > 15) { + //console.log("Kaboom?"); + explodeAt(pixel.x,pixel.y,explosionRadius); + //console.log("Yes, Rico, kaboom."); + }; + }; + //Pre-explosion handler: keeps track of time before the kaboom + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.hissing) { + //console.log("Ssssssss"); + if(pixel.dead) { + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(!pixel.hissStart) { + //console.log("t-30 ticks or whatever it was"); + pixel.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - pixel.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + (2 * ticksHissing)); + green = rgbColorBound(green + (2 * ticksHissing)); + blue = rgbColorBound(blue + (2 * ticksHissing)); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + lightness = slBound(lightness + (2 * 1.176)); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + if(pixelTicks - pixel.hissStart > 15) { + //console.log("Kaboom?"); + //console.log(`Exploding with radius ${explosionRadius} (charged: ${pixel.charged})`); + explodeAt(pixel.x,pixel.y,explosionRadius); + //console.log("Yes, Rico, kaboom."); + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + }; + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-16 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + //console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { //probably misapplying the tolerance from the MC Wiki line: "Creepers will chase after any player, as long as it is within a 16 block (±5%) radius" + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 16 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + //console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + break; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; + }; + }; + }; + }; + }; + }, + related: ["creeper"], + }; + //Angelic Creeper + elements.angelic_creeper = { //let's get this one out of the way first + color: ["#f5ef56", "#fcbddf", "#de8aa8", "#e35d95", "#eb4974", "#ed3ea7", "#d645a3", "#a84556", "#9e4f6c", "#91315b", "#8c4963"], + category: "life", + properties: { + dead: false, + dir: 1, + panic: 0, + following: false + }, + movable: true, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("angelic_creeper_body", pixel.x, pixel.y+1); + pixel.element = "angelic_creeper_head"; + pixel.color = pixelColorPick(pixel) + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("angelic_creeper_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "angelic_creeper_body"; + pixel.color = pixelColorPick(pixel) + } + else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["angelic_creeper_body","angelic_creeper_head"], + desc: 'A creeper type from Extra Creeper Types (CF). It sends things upward.' + }; + elements.angelic_creeper_body = { + color: ["#d2d2d2", "#fcbddf", "#de8aa8", "#e35d95", "#eb4974", "#ed3ea7", "#d645a3", "#a84556", "#9e4f6c", "#91315b", "#8c4963"], + category: "life", + hidden: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -30, + stateLow: "frozen_meat", + burn: 10, + burnTime: 250, + burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","gunpowder"], + breakInto: ["blood","blood","gunpowder","gunpowder","feather"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 } + }, + properties: { + dead: false, + dir: 1, + panic: 0, + charged: false, + didChargeBlueTinted: false + }, + movable: true, + tick: function(pixel) { + if(!pixel.hissing) { //If not hissing (it floats when hissing) + if(Math.random() < 0.2) { //20% chance to fall + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headPixel = pixelMap[pixel.x][pixel.y-2]; + if (headPixel.element == "angelic_creeper_head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + }; + }; + }; + }; + }; + } else { + if((pixelTicks - pixel.start) % 3 == 0) { + if (!isEmpty(pixel.x, pixel.y-1, true)) { // Find head + var headPixel = pixelMap[pixel.x][pixel.y-1]; + if (headPixel.element == "angelic_creeper_head") { //Validate head + if (tryMove(headPixel, pixel.x, pixel.y-2)) { // Float + if (isEmpty(pixel.x, pixel.y-1)) { //If the head didn't swap with something + movePixel(pixel, pixel.x, pixel.y-1); //Pull body up + } else { //If it did swap + swapPixels(pixel, pixelMap[pixel.x][pixel.y-1]); //Pull body up through other pixel + }; + }; + }; + }; + }; + }; + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + } + return + } + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "angelic_creeper_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance + if (Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } + else if (Math.random() < 0.1) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!head.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + if(pixel.charge) { + pixel.charged = true; + }; + if(head) { + if(typeof(head.charge) !== "undefined") { + if(head.charge) { + pixel.charged = true; + }; + }; + if(typeof(head.charged) !== "undefined") { + if(head.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 10; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 7; + }; + if(pixel.burning) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + if(!pixel.burnStart) { //I don't like errors. + pixel.burnStart = pixel.ticks; + }; + if(pixelTicks - pixel.burnStart > 30) { + //console.log("GOTTA YEET YEET YEET!"); + explodeAtPlus(pixel.x,pixel.y,explosionRadius,"fire","smoke",null,angelicUpwardVelocity); //Special effect: Flings you upwards (extended to all movable tiles because it's easier). + //It also floats when hissing, but that will come soon. + //console.log("Yes, Rico, kaboom."); + }; + }; + //Head hissing color handler: keeps track of head's hissing for coloring purposes + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.dead || !head || head.dead) { //can't hiss without a head according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(head.hissing) { + //console.log("Ssssssss"); + if(!head.hissStart) { + //console.log("t-30 ticks or whatever it was"); + head.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - head.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + //console.log("the j"); + lightness = slBound(lightness + 1.176); + //console.log(lightness); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + }; + }; + }, + }, + elements.angelic_creeper_head = { + color: ["#f5ef56", "#f0ea4f", "#f0ea60"], + category: "life", + hidden: true, + density: 1080, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -30, + stateLow: "frozen_meat", + burn: 10, + burnTime: 250, + burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","gunpowder"], + breakInto: ["blood","blood","blood","blood","feather"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 }, + "oxygen": { "elem2":"carbon_dioxide", "chance":0.5 } + }, + properties: { + dead: false, + following: false, + hissing: false, + charged: false, + didChargeBlueTinted: false + }, + movable: true, + tick: function(pixel) { + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + return + } + } + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "angelic_creeper_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null } + if(body) { + if(body.dir !== pixel.dir) { //hacky workaround: lock head dir to body dir + pixel.dir = body.dir; + }; + }; + if (isEmpty(pixel.x, pixel.y+1)) { + tryMove(pixel, pixel.x, pixel.y+1); + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + //start of most new code + var pX = pixel.x; + var pY = pixel.y; + if(pixel.charge) { + pixel.charged = true; + }; + if(body) { + if(typeof(body.charge) !== "undefined") { + if(body.charge) { + pixel.charged = true; + }; + }; + if(typeof(body.charged) !== "undefined") { + if(body.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 10; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 7; + }; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + }; + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-16 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { //probably misapplying the tolerance from the MC Wiki line: "Creepers will chase after any player, as long as it is within a 16 block (±5%) radius" + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 16 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + break; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; + }; + }; + }; + }; + }; + //Pre-explosion handler: keeps track of time before the kaboom + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.hissing) { + //console.log("Ssssssss"); + if(pixel.dead || !body || body.dead) { //can't explode without a body according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(!pixel.hissStart) { + //console.log("t-30 ticks or whatever it was"); + pixel.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - pixel.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + lightness = slBound(lightness + 1.176); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + if(pixelTicks - pixel.hissStart > 30) { + //console.log("GOTTA YEET YEET YEET!"); + //console.log(`Exploding with radius ${explosionRadius} (charged: ${pixel.charged})`); + explodeAtPlus(body.x,body.y,explosionRadius,"fire","smoke",null,angelicUpwardVelocity); + //console.log("Yes, Rico, kaboom."); + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + }, + }; + //Bombing Creeper + elements.bombing_creeper = { + color: ["#5b8b59", "#3f8738", "#559552", "#479143", "#50b143", "#58c546", "#e83c3c", "#c92a2a", "#f53d3d", "#ad3131"], + category: "life", + properties: { + dead: false, + dir: 1, + panic: 0, + following: false + }, + movable: true, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("bombing_creeper_body", pixel.x, pixel.y+1); + pixel.element = "bombing_creeper_head"; + pixel.color = pixelColorPick(pixel) + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("bombing_creeper_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "bombing_creeper_body"; + pixel.color = pixelColorPick(pixel) + } + else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["bombing_creeper_body","bombing_creeper_head"], + desc: 'A creeper type from Extra Creeper Types (CF). It spawns more explosives when it explodes.' + }; + elements.bombing_creeper_body = { + color: ["#e83c3c", "#c92a2a", "#f53d3d", "#ad3131"], + category: "life", + hidden: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -30, + stateLow: "frozen_meat", + burn: 10, + burnTime: 250, + burnInto: ["cooked_meat","cooked_meat","cooked_meat","dynamite","gunpowder"], + breakInto: ["blood","dynamite","dynamite"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 } + }, + properties: { + dead: false, + dir: 1, + panic: 0, + charged: false, + didChargeBlueTinted: false + }, + movable: true, + tick: function(pixel) { + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headPixel = pixelMap[pixel.x][pixel.y-2]; + if (headPixel.element == "bombing_creeper_head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + } + return + } + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "bombing_creeper_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance + if (Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } + else if (Math.random() < 0.1) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!head.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + if(pixel.charge) { + pixel.charged = true; + }; + if(head) { + if(typeof(head.charge) !== "undefined") { + if(head.charge) { + pixel.charged = true; + }; + }; + if(typeof(head.charged) !== "undefined") { + if(head.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 10; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 7; + }; + if(pixel.burning) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + if(!pixel.burnStart) { //I don't like errors. + pixel.burnStart = pixel.ticks; + }; + if(pixelTicks - pixel.burnStart > 30) { + //console.log("Kaboom?"); + explodeAt(pixel.x,pixel.y,explosionRadius,"fire,dynamite"); //Effect: Places (originally 5) primed TNT when it explodes (i.e. cluster bomb creeper) + //Dynamite is the closest thing we have to powder TNT (i.e. good enough) + //console.log("Yes, Rico, kaboom."); + }; + }; + //Head hissing color handler: keeps track of head's hissing for coloring purposes + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.dead || !head || head.dead) { //can't hiss without a head according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(head.hissing) { + //console.log("Ssssssss"); + if(!head.hissStart) { + //console.log("t-30 ticks or whatever it was"); + head.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - head.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + //console.log("the j"); + lightness = slBound(lightness + 1.176); + //console.log(lightness); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + }; + }; + }, + }; + elements.bombing_creeper_head = { + color: ["#5B8B59", "#3F8738", "#559552", "#479143", "#50B143", "#58C546"], + category: "life", + hidden: true, + density: 1080, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -30, + stateLow: "frozen_meat", + burn: 10, + burnTime: 250, + burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","dynamite","gunpowder"], + breakInto: ["blood","dynamite"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 }, + "oxygen": { "elem2":"carbon_dioxide", "chance":0.5 } + }, + properties: { + dead: false, + following: false, + hissing: false, + charged: false, + didChargeBlueTinted: false + }, + movable: true, + tick: function(pixel) { + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + return + } + } + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "bombing_creeper_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null } + if(body) { + if(body.dir !== pixel.dir) { //hacky workaround: lock head dir to body dir + pixel.dir = body.dir; + }; + }; + if (isEmpty(pixel.x, pixel.y+1)) { + tryMove(pixel, pixel.x, pixel.y+1); + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + //start of most new code + var pX = pixel.x; + var pY = pixel.y; + if(pixel.charge) { + pixel.charged = true; + }; + if(body) { + if(typeof(body.charge) !== "undefined") { + if(body.charge) { + pixel.charged = true; + }; + }; + if(typeof(body.charged) !== "undefined") { + if(body.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 10; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 7; + }; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + }; + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-16 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { //probably misapplying the tolerance from the MC Wiki line: "Creepers will chase after any player, as long as it is within a 16 block (±5%) radius" + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 16 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + break; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; + }; + }; + }; + }; + }; + //Pre-explosion handler: keeps track of time before the kaboom + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.hissing) { + //console.log("Ssssssss"); + if(pixel.dead || !body || body.dead) { //can't explode without a body according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(!pixel.hissStart) { + //console.log("t-30 ticks or whatever it was"); + pixel.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - pixel.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + lightness = slBound(lightness + 1.176); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + if(pixelTicks - pixel.hissStart > 30) { + //console.log("Kaboom?"); + //console.log(`Exploding with radius ${explosionRadius} (charged: ${pixel.charged})`); + explodeAt(body.x,body.y,explosionRadius,"fire,dynamite"); + //console.log("Yes, Rico, kaboom."); + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + }, + }; + //Hell Creeper + elements.hell_creeper = { + color: ["#D2D2D2", "#ff141e", "#fc3232", "#DFAFAF", "#e84a4a", "#ce7979", "#d95555", "#d53c3c", "#c53636", "#b13333", "#913535", "#954242", "#872828", "#8b4949", "#2b0304"], + category: "life", + properties: { + dead: false, + dir: 1, + panic: 0, + following: false + }, + movable: true, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("hell_creeper_body", pixel.x, pixel.y+1); + pixel.element = "hell_creeper_head"; + pixel.color = pixelColorPick(pixel) + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("hell_creeper_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "hell_creeper_body"; + pixel.color = pixelColorPick(pixel) + } + else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["hell_creeper_body","hell_creeper_head"], + desc: 'A creeper type from Extra Creeper Types (CF). It has a small explosion radius, but spawns a lot of fire around its explosion.' + }; + elements.hell_creeper_body = { + color: ["#D2D2D2", "#ff141e", "#fc3232", "#DFAFAF", "#e84a4a", "#ce7979", "#d95555", "#d53c3c", "#c53636", "#b13333", "#913535", "#954242", "#872828", "#8b4949", "#2b0304"], + category: "life", + hidden: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 2000, //they are immune to lava, and minecraft's lava is presumably mafic, so at least 1200*C + stateHigh: "ash", + breakInto: ["blood","gunpowder","fire"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 } + }, + properties: { + dead: false, + dir: 1, + panic: 0, + charged: false, + didChargeBlueTinted: false + }, + movable: true, + tick: function(pixel) { + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headPixel = pixelMap[pixel.x][pixel.y-2]; + if (headPixel.element == "hell_creeper_head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + } + return + } + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "hell_creeper_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance + if (Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } + else if (Math.random() < 0.1) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!head.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + if(pixel.charge) { + pixel.charged = true; + }; + if(head) { + if(typeof(head.charge) !== "undefined") { + if(head.charge) { + pixel.charged = true; + }; + }; + if(typeof(head.charged) !== "undefined") { + if(head.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 10; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 7; + }; + if(pixel.burning) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + if(!pixel.burnStart) { //I don't like errors. + pixel.burnStart = pixel.ticks; + }; + if(pixelTicks - pixel.burnStart > 30) { + //console.log("Kaboom?"); + explodeAtPlus(pixel.x,pixel.y,explosionRadius,"fire","fire",null,hellExplosionFire); + //console.log("Yes, Rico, kaboom."); + }; + }; + //Head hissing color handler: keeps track of head's hissing for coloring purposes + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.dead || !head || head.dead) { //can't hiss without a head according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(head.hissing) { + //console.log("Ssssssss"); + if(!head.hissStart) { + //console.log("t-30 ticks or whatever it was"); + head.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - head.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + //console.log("the j"); + lightness = slBound(lightness + 1.176); + //console.log(lightness); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + }; + }; + }, + }; + elements.hell_creeper_head = { + color: ["#D2D2D2", "#ff141e", "#fc3232", "#e84a4a", "#b13333", "#913535", "#954242", "#872828", "#8b4949", "#2b0304", "#111111", "#faae3c", "#f5e131"], + category: "life", + hidden: true, + density: 1080, + state: "solid", + conduct: 25, + tempHigh: 2000, + stateHigh: "ash", + tempLow: -30, + stateLow: "frozen_meat", + breakInto: ["blood","fire"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 }, + "oxygen": { "elem2":"carbon_dioxide", "chance":0.5 } + }, + properties: { + dead: false, + following: false, + hissing: false, + charged: false, + didChargeBlueTinted: false + }, + movable: true, + tick: function(pixel) { + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + return + } + } + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "hell_creeper_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null } + if(body) { + if(body.dir !== pixel.dir) { //hacky workaround: lock head dir to body dir + pixel.dir = body.dir; + }; + }; + if (isEmpty(pixel.x, pixel.y+1)) { + tryMove(pixel, pixel.x, pixel.y+1); + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + //start of most new code + var pX = pixel.x; + var pY = pixel.y; + if(pixel.charge) { + pixel.charged = true; + }; + if(body) { + if(typeof(body.charge) !== "undefined") { + if(body.charge) { + pixel.charged = true; + }; + }; + if(typeof(body.charged) !== "undefined") { + if(body.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 10; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 7; + }; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + }; + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-16 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { //probably misapplying the tolerance from the MC Wiki line: "Creepers will chase after any player, as long as it is within a 16 block (±5%) radius" + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 16 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + break; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; + }; + }; + }; + }; + }; + //Pre-explosion handler: keeps track of time before the kaboom + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.hissing) { + //console.log("Ssssssss"); + if(pixel.dead || !body || body.dead) { //can't explode without a body according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(!pixel.hissStart) { + //console.log("t-30 ticks or whatever it was"); + pixel.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - pixel.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + lightness = slBound(lightness + 1.176); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + if(pixelTicks - pixel.hissStart > 30) { + //console.log("Kaboom?"); + //console.log(`Exploding with radius ${explosionRadius} (charged: ${pixel.charged})`); + explodeAtPlus(pixel.x,pixel.y,explosionRadius,"fire","fire",null,hellExplosionFire); + //console.log("Yes, Rico, kaboom."); + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + }, + }; + //Lightning strikes charge creepers + elements.lightning.tick = function(pixel) { + if (!pixel.stage) { // create bolt + var y = pixel.y; + var xoffset = 0; + var last = [pixel.x,pixel.y] + for (var i = 0; i < 100; i++) { + y++; + // randomly go back and forth + if (Math.random() > 0.5) { xoffset++; } + else { xoffset--; } + var x = pixel.x + xoffset; + if (isEmpty(x, y)) { + createPixel("lightning",x,y); + pixelMap[x][y].stage = 1; + pixelMap[x][y].color = pixel.color; + last = [x,y]; + } + else if (outOfBounds(x,y) || !elements[pixelMap[x][y].element].isGas) { + var newPixel = pixelMap[x]?.[y] ?? null; + if(newPixel && newPixel.element.includes("creeper") && newPixel.dir !== undefined && !(newPixel.charged) && !(newPixel.dead)) { + //Charge creeper + newPixel.charged = true; + getMooreNeighbors(newPixel).forEach(function(pixel) { if(pixel.element == "lightning") { deletePixel(pixel.x,pixel.y) } }); + break; + } else { + //strike + if (Math.random() < 0.01) { // BALL LIGHTNING + pixelMap[last[0]][last[1]].stage = 9; + } + if (!outOfBounds(x,y)) { pixelMap[x][y].temp = 27760 } + explodeAt(x, y, 13, ["plasma","plasma","plasma","electric"]); + break; + } + } + } + doDefaults(pixel); + deletePixel(pixel.x, pixel.y); + } + else if (pixel.stage === 9) { // BALL LIGHTNING + // move either left or right randomly + if (Math.random() > 0.5) { tryMove(pixel, pixel.x + 1, pixel.y) } + else { tryMove(pixel, pixel.x - 1, pixel.y) } + // create electric in a 3x3 area around pixel + for (var x = pixel.x - 1; x <= pixel.x + 1; x++) { + for (var y = pixel.y - 1; y <= pixel.y + 1; y++) { + if (isEmpty(x, y)) { + createPixel("electric",x,y); + pixelMap[x][y].color = pixel.color; + } + } + } + doDefaults(pixel); + if (pixelTicks - pixel.start >= 250) { deletePixel(pixel.x, pixel.y); } + } + else if (pixelTicks - pixel.start >= 4) { + doDefaults(pixel); + //deletePixel(pixel.x, pixel.y); + changePixel(pixel, "electric") + } + else { doDefaults(pixel); } + }; + /* +-----------------------------------+ + | Nothing There | + | | + | amogus | + | | + | red imposter | + | | + | | + | | + | | + | | + | | + | | + +-----------------------------------+ */ + elements.nothing_there_bullet = { + flippableX: true, + movable: true, + density: 10000, + desc: "A hypersonic bullet made of Nothing There's flesh. I don't remember if it can turn humans into red clouds.", + color: "#a3281a", + related: ["nothing_there_phase_3_body","nothing_there_phase_3_head"], + movable: true, + tick: function(pixel) { + if(typeof(pixel.flipX) == undefined) { + pixel.flipX = !!Math.floor(Math.random() * 2); + }; + var dir = pixel.flipX ? -1 : 1; + for(i = 0; i < 6; i++) { + if(outOfBounds(pixel.x+dir,pixel.y)) { + deletePixel(pixel.x,pixel.y); + break; + }; + if(!nothingThereBulletMovement(pixel,pixel.x+dir,pixel.y)) { + return true; + }; + }; + }, + }; + elements.nothing_there_mace = { + movable: true, + density: 10000, + desc: "A spiky mace attached to Nothing There, which can turn humans into red clouds.", + color: "#fa4632", + properties: { + counter: 2 + }, + related: ["nothing_there_phase_3_body","nothing_there_phase_3_head"], + movable: true, + tick: function(pixel) { + if(outOfBounds(pixel.x,pixel.y + 1)) { + deletePixel(pixel.x,pixel.y); + return false; + }; + if(!tryMove(pixel,pixel.x,pixel.y + 1)) { + var newPixel = pixelMap[pixel.x][pixel.y + 1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newElement !== pixel.element) { + if(newInfo.state === "gas") { + swapPixels(pixel,newPixel); + } else { + if(pixel.counter > 0) { + explodeAtPlus(pixel.x,pixel.y + 1,5,null,null); + pixel.counter--; + } else { + deletePixel(pixel.x,pixel.y); + return true; + }; + }; + }; + }; + }, + }; + elements.nothing_there_cleaver = { + movable: true, + density: 10000, + desc: "A very sharp blade attached to Nothing There, which can turn humans into red clouds.", + color: "#a33c3c", + properties: { + counter: 4 + }, + related: ["nothing_there_phase_3_body","nothing_there_phase_3_head"], + movable: true, + tick: function(pixel) { + if(outOfBounds(pixel.x,pixel.y + 1)) { + deletePixel(pixel.x,pixel.y); + return false; + }; + if(!tryMove(pixel,pixel.x,pixel.y + 1)) { + var newPixel = pixelMap[pixel.x][pixel.y + 1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(!nothingThereBulletExcludedElements.includes(newElement)) { + if(pixel.counter > 0) { + swapPixels(pixel,newPixel); + breakPixel(newPixel,false,false); + pixel.counter--; + } else { + deletePixel(pixel.x,pixel.y); + return true; + }; + } else { + deletePixel(pixel.x,pixel.y); + return false; + }; + }; + }, + }; + testSwapArray = ["meat","cooked_meat","rotten_meat","blood","infection","antibody","plague","zombie_blood","frozen_meat","frozen_rotten_meat"]; + elements.nothing_there_phase_1 = { + color: "#faacac", + category: "life", + density: 2000, + desc: "O-06-20 (ALEPH)
In this phase, it looks like a dog made of misshapen human parts. It can easily turn humans into unrecognizable messes.", + state: "solid", + tempHigh: 3000, + hardness: 0.995, + stateHigh: "cooked_meat", + burn: 1, + burnTime: 250000, + burnInto: "cooked_meat", + breakInto: ["blood","meat","magic"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.00002 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.00004 }, + "plague": { "elem1":"plague", "chance":0.000003 } + }, + related: ["nothing_there_phase_2", "nothing_there_phase_3_body", "nothing_there_phase_3_head"], + properties: { + dead: false, + dir: 1, + following: false + }, + movable: true, + tick: function(pixel) { + var pixelBreakInto = elements[pixel.element].breakInto; + tryMove(pixel, pixel.x, pixel.y+1); // Fall + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Break if pixelTicks-dead > 5 + if (pixelTicks-pixel.dead > 5) { + changePixel(pixel,pixelBreakInto[Math.floor(Math.random() * pixelBreakInto.length)],false); + }; + return; + }; + if (Math.random() < 0.1) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], //dash move + [1*pixel.dir,-1], //cleave move + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if(tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + break; + } else { //move through given pixels + if(!isEmpty(pixel.x+move[0], pixel.y+move[1], true)) { + var blockingPixel = pixelMap[pixel.x+move[0]][pixel.y+move[1]]; + //console.log(blockingPixel); + var blockingElement = blockingPixel.element; + if(testSwapArray.includes(blockingElement)) { + swapPixels(pixel,blockingPixel); + break; + }; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!pixel.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + var pX = pixel.x; + var pY = pixel.y; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + if(pixelTicks % 2 == 0 && !pixel.dead) { //reduce rate for performance + /*var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + };*/ + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-35 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { //If not dead + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Infect/kill if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2) + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"blood",false); + } else { //Remaining 3/4 chance to change to meat + changePixel(newPixel,"meat",false); + }; + }; + } else { //Mutilate if dead + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"blood",false); + } else { //Remaining 3/4 chance to change to meat + changePixel(newPixel,"meat",false); + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 35 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { //If not dead + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Infect/kill if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2) + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"blood",false); + } else { //Remaining 3/4 chance to change to meat + changePixel(newPixel,"meat",false); + }; + }; + } else { //Mutilate if dead + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"blood",false); + } else { //Remaining 3/4 chance to change to meat + changePixel(newPixel,"meat",false); + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + }; + }; + if(pixelTicks - pixel.start > 300 && (Math.random() < 0.003)) { + var dir = pixel.dir; + changePixel(pixel,"nothing_there_phase_2",false); + pixel.dir = dir; + }; + //End + }, + }; + elements.nothing_there_phase_2 = { + behavior: behaviors.POWDER_OLD, + color: "#d90b0b", + category: "life", + density: 4000, + desc: "O-06-20 (ALEPH)
In this phase, it looks like a red, fibrous cocoon. It will soon hatch into its third phase.", + state: "solid", + tempHigh: 3500, + hardness: 0.999, + stateHigh: "cooked_meat", + burn: 1, + burnTime: 350000, + burnInto: "cooked_meat", + breakInto: ["blood","meat","magic"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.000001 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.000001 }, + "plague": { "elem1":"plague", "chance":0.000001 } + }, + related: ["nothing_there_phase_1", "nothing_there_phase_3_body", "nothing_there_phase_3_head"], + properties: { + dead: false, + dir: 1, + timer: 0 + }, + movable: true, + tick: function(pixel) { + var pixelBreakInto = elements[pixel.element].breakInto; + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Break if pixelTicks-dead > 5 + if (pixelTicks-pixel.dead > 5) { + changePixel(pixel,pixelBreakInto[Math.floor(Math.random() * pixelBreakInto.length)],false); + }; + return; + }; + if(pixelTicks - pixel.start > 300) { + var dir = pixel.dir; + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("nothing_there_phase_3_body", pixel.x, pixel.y+1); + pixel.element = "nothing_there_phase_3_head"; + pixel.color = pixelColorPick(pixel) + pixelMap[pixel.x][pixel.y+1].dir = dir; + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("nothing_there_phase_3_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "nothing_there_phase_3_body"; + pixel.color = pixelColorPick(pixel) + pixel.dir = dir; + }; + }; + //End + }, + }; + elements.nothing_there_phase_3 = { + color: "#fc1e35", + category: "life", + desc: "Spawns Nothing There in its humanoid third phase, for when you don't want to wait for it to go through the other phases.", + properties: { + dead: false, + dir: 1, + panic: 0, + following: false + }, + movable: true, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("nothing_there_phase_3_body", pixel.x, pixel.y+1); + pixel.element = "nothing_there_phase_3_head"; + pixel.color = pixelColorPick(pixel) + } else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("nothing_there_phase_3_head", pixel.x, pixel.y-1); + pixel.element = "nothing_there_phase_3_body"; + pixel.color = pixelColorPick(pixel) + } else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["nothing_there_phase_3_body","nothing_there_phase_3_head"], + }; + elements.nothing_there_phase_3_body = { + color: "#fc1e35", + category: "life", + density: 3000, + desc: "O-06-20 (ALEPH)
In this phase, it looks like a humanoid made of misarranged flesh. It is almost indestructible and has a variety of ways to destroy your canvas and annihilate any humans inside of it.
Let's hope it doesn't learn to blend in and walk among us.", + state: "solid", + tempHigh: 3000, + hardness: 0.9975, + hidden: true, + stateHigh: "cooked_meat", + burn: 1, + burnTime: 300000, + burnInto: "cooked_meat", + breakInto: ["blood","meat","magic"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.00001 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.00002 }, + "plague": { "elem1":"plague", "chance":0.0000015 } + }, + properties: { + dead: false, + dir: 1, + panic: 0, + following: false + }, + movable: true, + related: ["nothing_there_phase_1", "nothing_there_phase_2", "nothing_there_mace", "nothing_there_cleaver", "nothing_there_bullet"], + tick: function(pixel) { + var pixelBreakInto = elements[pixel.element].breakInto; + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headPixel = pixelMap[pixel.x][pixel.y-2]; + if (headPixel.element == "nothing_there_phase_3_head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Break if pixelTicks-dead > 5 + if (pixelTicks-pixel.dead > 5) { + changePixel(pixel,pixelBreakInto[Math.floor(Math.random() * pixelBreakInto.length)],false); + }; + return; + }; + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true)) { + if(pixelMap[pixel.x][pixel.y-1].element == "nothing_there_phase_3_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + }; + } else { + var head = null; + }; + } else { var head = null }; + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 30% chance + if (Math.random() < 0.3) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 10% chance + if (Math.random() < 0.1) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } //do not proceed if headless + else if (Math.random() < 0.08) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!head.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + }, + }; + elements.nothing_there_phase_3_head = { + color: "#ff3046", + category: "life", + density: 3000, + desc: "O-06-20 (ALEPH)
In this phase, it looks like a humanoid made of misarranged flesh. It is almost indestructible and has a variety of ways to destroy your canvas and annihilate any humans inside of it.
Let's hope it doesn't learn to blend in and walk among us.", + state: "solid", + tempHigh: 3000, + hardness: 0.9975, + hidden: true, + stateHigh: "cooked_meat", + burn: 1, + burnTime: 300000, + burnInto: "cooked_meat", + breakInto: ["blood","meat","magic"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.00001 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.00002 }, + "plague": { "elem1":"plague", "chance":0.0000015 } + }, + properties: { + dead: false, + dir: 1, + panic: 0, + following: false + }, + movable: true, + related: ["nothing_there_phase_1", "nothing_there_phase_2", "nothing_there_mace", "nothing_there_cleaver", , "nothing_there_bullet"], + tick: function(pixel) { + var pixelBreakInto = elements[pixel.element].breakInto; + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Break if pixelTicks-dead > 5 + if (pixelTicks-pixel.dead > 5) { + changePixel(pixel,pixelBreakInto[Math.floor(Math.random() * pixelBreakInto.length)],false); + }; + return; + }; + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true)) { + if(pixelMap[pixel.x][pixel.y+1].element == "nothing_there_phase_3_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill body + pixel.dead = body.dead; + }; + } else { + var body = null; + }; + } else { var body = null }; + if(body) { + if(body.dir !== pixel.dir) { //hacky workaround: lock head dir to body dir + pixel.dir = body.dir; + }; + }; + if (isEmpty(pixel.x, pixel.y+1)) { + tryMove(pixel, pixel.x, pixel.y+1); + // create blood if severed 30% chance + if (isEmpty(pixel.x, pixel.y+1) && Math.random() < 0.3) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 10% chance + if (Math.random() < 0.10) { + pixel.dead = pixelTicks; + } + } + } + //start of most new code + var pX = pixel.x; + var pY = pixel.y; + //Human detection loop + if(pixelTicks % 2 == 0 && !pixel.dead) { //reduce rate for performance + /*var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + };*/ + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + //do action every 40 ticks + var bulletPositions = [[-1, -1], [-1, 0]]; + var bulletPosition = bulletPositions[Math.floor(Math.random() * 2)]; + var smashPosition = [-1, -1]; + var cleavePositions = [[-1, -1], [-2, -1], [-3, -1]]; + var start = 2 * Math.floor(pixel.start/2); + if((pixelTicks - start) % 40 == 0) { + var action = Math.floor(Math.random() * 3); + if(action == 0) { //bullet + var bX = pX + bulletPosition[0]; + var bY = pY + bulletPosition[1]; + if(!outOfBounds(bX,bY)) { + if(isEmpty(bX,bY)) { + createPixel("nothing_there_bullet",bX,bY); + pixelMap[bX][bY].flipX = true; + } else { + if(!nothingThereBulletExcludedElements.includes(pixelMap[bX][bY].element)) { + deletePixel(bX,bY); + createPixel("nothing_there_bullet",bX,bY); + pixelMap[bX][bY].flipX = true; + }; + }; + }; + } else if(action == 1) { //smash + var sX = pX + smashPosition[0]; + var sY = pY + smashPosition[1]; + if(!outOfBounds(sX,sY)) { + if(isEmpty(sX,sY)) { + createPixel("nothing_there_mace",sX,sY); + } else { + if(!nothingThereBulletExcludedElements.includes(pixelMap[sX][sY].element)) { + deletePixel(sX,sY); + createPixel("nothing_there_mace",sX,sY); + }; + }; + }; + } else if(action == 2) { //cleave + for(cleaverIndex = 0; cleaverIndex < cleavePositions.length; cleaverIndex++) { + var cX = pX + cleavePositions[cleaverIndex][0]; + var cY = pY + cleavePositions[cleaverIndex][1]; + if(!outOfBounds(cX,cY)) { + if(isEmpty(cX,cY)) { + createPixel("nothing_there_cleaver",cX,cY); + } else { + if(!nothingThereBulletExcludedElements.includes(pixelMap[cX][cY].element)) { + deletePixel(cX,cY); + createPixel("nothing_there_cleaver",cX,cY); + }; + }; + }; + }; + }; + }; + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-35 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { //If not dead + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Infect/kill if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2) + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"blood",false); + } else { //Remaining 3/4 chance to change to meat + changePixel(newPixel,"meat",false); + }; + }; + } else { //Mutilate if dead + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"blood",false); + } else { //Remaining 3/4 chance to change to meat + changePixel(newPixel,"meat",false); + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + //do action every 40 ticks + var bulletPositions = [[1, -1], [1, 0]]; + var bulletPosition = bulletPositions[Math.floor(Math.random() * 2)]; + var smashPosition = [1, -1]; + var cleavePositions = [[1, -1], [2, -1], [3, -1]]; + var start = 2 * Math.floor(pixel.start/2); + if((pixelTicks - start) % 40 == 0) { + var action = Math.floor(Math.random() * 3); + if(action == 0) { //bullet + var bX = pX + bulletPosition[0]; + var bY = pY + bulletPosition[1]; + if(!outOfBounds(bX,bY)) { + if(isEmpty(bX,bY)) { + createPixel("nothing_there_bullet",bX,bY); + pixelMap[bX][bY].flipX = false; + } else { + if(!nothingThereBulletExcludedElements.includes(pixelMap[bX][bY].element)) { + deletePixel(bX,bY); + createPixel("nothing_there_bullet",bX,bY); + pixelMap[bX][bY].flipX = false; + }; + }; + }; + } else if(action == 1) { //smash + var sX = pX + smashPosition[0]; + var sY = pY + smashPosition[1]; + if(!outOfBounds(sX,sY)) { + if(isEmpty(sX,sY)) { + createPixel("nothing_there_mace",sX,sY); + } else { + if(!nothingThereBulletExcludedElements.includes(pixelMap[sX][sY].element)) { + deletePixel(sX,sY); + createPixel("nothing_there_mace",sX,sY); + }; + }; + }; + } else if(action == 2) { //cleave + for(cleaverIndex = 0; cleaverIndex < cleavePositions.length; cleaverIndex++) { + var cX = pX + cleavePositions[cleaverIndex][0]; + var cY = pY + cleavePositions[cleaverIndex][1]; + if(!outOfBounds(cX,cY)) { + if(isEmpty(cX,cY)) { + createPixel("nothing_there_cleaver",cX,cY); + } else { + if(!nothingThereBulletExcludedElements.includes(pixelMap[cX][cY].element)) { + deletePixel(cX,cY); + createPixel("nothing_there_cleaver",cX,cY); + }; + }; + }; + }; + }; + }; + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 35 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + //console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { //If not dead + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Infect/kill if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2) + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"blood",false); + } else { //Remaining 3/4 chance to change to meat + changePixel(newPixel,"meat",false); + }; + }; + } else { //Mutilate if dead + if(Math.random() < 1/4) { //One-fourth chance to change to blood + changePixel(newPixel,"blood",false); + } else { //Remaining 3/4 chance to change to meat + changePixel(newPixel,"meat",false); + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + }, + }; + 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 + badPixels.nothing_there_phase_2 = { panicIncrease: 1, panicIncreaseChance: 1 } + badPixels.nothing_there_phase_3_body = { panicIncrease: 1, panicIncreaseChance: 1 } + badPixels.nothing_there_phase_3_head = { panicIncrease: 1, panicIncreaseChance: 1 } + } + }); + /* +-----------------------------------+ + | End Nothing There elements | + | | + | | + | | + | | + | | + +-----------------------------------+ */ + /* +++++++++++++++++++++++++++ + + Start skeleton elements + + +++++++++++++++++++++++++++ */ + arrowExcludedElements = ["wall"]; + arrowPlacementExcludedElements = ["skeleton_head", "skeleton_body", "arrow", "wall"]; + elements.rock.hardness = 0.55; + elements.arrow = { + cooldown: 2, + flippableX: true, + movable: true, + properties: { + flipY: false, + speed: 5, + fall: 0, + attached: false, + attachOffsets: [null, null], + penetrateCounter: 7 + }, + density: 2471, + color: "#cacdcf", + related: ["skeleton_body","skeleton_head"], + movable: true, + burn: 20, + //burnInto: "flint", + burnInto: "rock", + burnTime: 250, + breakInto: ["gravel","gravel","sawdust","feather"], + tick: function(pixel) { + if(pixel.attachOffsets.includes(null)) { + pixel.attached = false; + }; + if(pixel.attached) { + var attachCoords = [pixel.x+pixel.attachOffsets[0], pixel.y+pixel.attachOffsets[1]]; + var attachX = pixel.x + pixel.attachOffsets[0]; + var attachY = pixel.y + pixel.attachOffsets[1]; + if(isEmpty(attachX,attachY,true)) { + pixel.attached = false; + } else { + var attachPixel = pixelMap[attachX][attachY]; + var attachInfo = elements[attachPixel.element]; + var attachState = "solid"; + if(typeof(attachInfo.state) === "string") { + attachState = attachInfo.state; + }; + var attachBlacklistStates = ["liquid","gas"]; + if(attachBlacklistStates.includes(attachState)) { + pixel.attached = false; + }; + }; + } else { //Move if not attached + var speedForBreakMult = pythSpeed(pixel.speed,pixel.y); + var breakMult = speedForBreakMult/5; + if(typeof(pixel.flipX) == undefined) { + pixel.flipX = !!Math.floor(Math.random() * 2); + }; + var dir = pixel.flipX ? -1 : 1; + if(Math.random() < (1/(pixel.speed**1.585))) { //1/0 is Infinity in JavaScript, so this should always be true at 0 speed) + pixel.fall++; + }; + //Horizontal movement + for(i = 0; i < pixel.speed; i++) { + if(outOfBounds(pixel.x+dir,pixel.y)) { + deletePixel(pixel.x,pixel.y); + break; + }; + if(!isEmpty(pixel.x+dir,pixel.y,true)) { + var otherPixel = pixelMap[pixel.x+dir][pixel.y]; + var otherElement = otherPixel.element; + var otherInfo = elements[otherElement]; + if(arrowExcludedElements.includes(otherElement)) { + pixel.attached = true; //attach + pixel.speed = 0; + pixel.fall = 0; + pixel.attachOffsets = [dir, 0]; + break; + }; + var otherDensity = (typeof(otherInfo.density) === "undefined" ? 1000 : otherInfo.density); + var swapChance = 1 - Math.max(0,(otherDensity / 2471)); + if(Math.random() < swapChance && pixel.penetrateCounter > 0) { + swapPixels(pixel,otherPixel); + arrowAltTb(otherPixel,breakMult); + pixel.speed = Math.max(0,--pixel.speed); + pixel.penetrateCounter--; + } else { + if(!arrowAltTb(otherPixel,breakMult)) { //if this didn't break it + pixel.attached = true; //attach + pixel.speed = 0; + pixel.fall = 0; + pixel.attachOffsets = [dir, 0]; + }; + }; + break; + } else { + tryMove(pixel,pixel.x+dir,pixel.y); + }; + }; + if(Math.random() < 0.1) { + pixel.speed = Math.max(0,--pixel.speed); + }; + var dirY = 1; + //Vertical movement + if(typeof(pixel.flipY) !== "undefined") { + if(pixel.flipY) { + pixel.dirY = -1; + }; + }; + for(j = 0; j < pixel.fall; j++) { + if(outOfBounds(pixel.x,pixel.y+dirY)) { + deletePixel(pixel.x,pixel.y); + break; + }; + if(!isEmpty(pixel.x,pixel.y+dirY,true)) { + var otherPixel = pixelMap[pixel.x][pixel.y+dirY]; + var otherElement = otherPixel.element; + var otherInfo = elements[otherElement]; + if(arrowExcludedElements.includes(otherElement)) { + pixel.attached = true; //attach + pixel.speed = 0; + pixel.fall = 0; + pixel.attachOffsets = [0, dirY]; + break; + }; + var otherDensity = (typeof(otherInfo.density) === "undefined" ? 1000 : otherInfo.density); + var swapChance = 1 - Math.max(0,(otherDensity / 2471)); + if(Math.random() < swapChance && pixel.penetrateCounter > 0) { + swapPixels(pixel,otherPixel); + arrowAltTb(otherPixel,breakMult); + pixel.speed = Math.max(0,--pixel.speed); + pixel.penetrateCounter--; + } else { + if(!arrowAltTb(otherPixel,breakMult)) { //if this didn't break it + pixel.attached = true; //attach + pixel.speed = 0; + pixel.fall = 0; + pixel.attachOffsets = [0, dirY]; + }; + }; + break; + } else { + tryMove(pixel,pixel.x,pixel.y+dirY); + }; + }; + //End + }; + }, + }; + elements.skeleton = { + color: ["#ebebe6", "#cfcfc8"], + category: "life", + properties: { + dead: false, + dir: 1, + panic: 0, + following: false + }, + movable: true, + tick: function(pixel) { + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel("skeleton_body", pixel.x, pixel.y+1); + pixel.element = "skeleton_head"; + pixel.color = pixelColorPick(pixel) + } + else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel("skeleton_head", pixel.x, pixel.y-1); + pixelMap[pixel.x][pixel.y-1].color = pixel.color; + pixel.element = "skeleton_body"; + pixel.color = pixelColorPick(pixel) + } + else { + deletePixel(pixel.x, pixel.y); + } + }, + related: ["skeleton_body","skeleton_head"], + desc: "If this text is green or underlined, skeletons can spawn. Click here to toggle skeleton spawning. If it's on, skeletons (all types) can spawn through random events." + }; + elements.skeleton_body = { + color: "#ebebe6", + category: "life", + hidden: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "bone", + burn: 10, + burnTime: 250, + burnInto: ["bone","ash","arrow"], + hardness: 0.55, + breakInto: ["bone","bone","bone","bone_marrow"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 } + }, + properties: { + dead: false, + dir: 1, + panic: 0, + chargeCounter: 20, + shooting: false + }, + movable: true, + tick: function(pixel) { + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headPixel = pixelMap[pixel.x][pixel.y-2]; + if (headPixel.element == "skeleton_head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into bone if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + changePixel(pixel,"bone"); + } + return + } + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "skeleton_head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance (bone marrow) + if (Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } + else if (Math.random() < 0.1) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!head.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + if(pixel.shooting) { + if(pixel.chargeCounter <= 0) { + var bX = pixel.x + pixel.dir; + var bY = pixel.y - 1; + var arrowFlipX = null; + if(pixel.dir < 0) { + arrowFlipX = true; + } else if(pixel.dir > 0) { + arrowFlipX = false; + }; + if(!outOfBounds(bX,bY)) { + if(isEmpty(bX,bY)) { + createPixel("arrow",bX,bY); + pixelMap[bX][bY].flipX = arrowFlipX; + } else { + if(!arrowExcludedElements.includes(pixelMap[bX][bY].element) && !arrowPlacementExcludedElements.includes(pixelMap[bX][bY].element)) { + deletePixel(bX,bY); + createPixel("arrow",bX,bY); + pixelMap[bX][bY].flipX = arrowFlipX; + }; + }; + }; + pixel.chargeCounter = 20; + }; + if(pixel.chargeCounter > 0) { + pixel.chargeCounter--; + }; + }; + }, + }; + elements.skeleton_head = { + color: ["#ebebe6", "#cfcfc8"], + category: "life", + hidden: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "bone", + burn: 10, + burnTime: 250, + burnInto: ["bone","ash","arrow"], + hardness: 0.55, + breakInto: ["bone","bone","bone","bone_marrow"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 } + }, + properties: { + dead: false, + dir: 1, + panic: 0 + }, + movable: true, + tick: function(pixel) { + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into bone if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + changePixel(pixel,"bone"); + } + return + } + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "skeleton_body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null } + if(body) { + if(body.dir !== pixel.dir) { //hacky workaround: lock head dir to body dir + pixel.dir = body.dir; + }; + }; + if (isEmpty(pixel.x, pixel.y+1)) { + tryMove(pixel, pixel.x, pixel.y+1); + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + //start of most new code + var pX = pixel.x; + var pY = pixel.y; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + }; + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-16 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + //console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + if(body) body.shooting = true; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 16 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + //console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + if(body) body.shooting = true; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; + }; + }; + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + }, + }; + /* ------------------------- + - End skeleton elements - + ------------------------- */ + mobsLoaded = true; + //CHANGES TO HUMANS ## + function hasPixel(x,y,elementInput) { + if(isEmpty(x,y,true)) { //if empty, it can't have a pixel + return false; + } else { + if(elementInput.includes(",")) { //CSTA + elementInput = elementInput.split(","); + }; + if(Array.isArray(elementInput)) { //if element list + return elementInput.includes(pixelMap[x][y].element); + } else { //if single element + return pixelMap[x][y].element === elementInput; + }; + }; + }; + elements.brain = { + color: ["#fce3e3","#deb6c5","#f5ced5","#e87b8f"], + behavior: [ + "XX|XX|XX", + "XX|CH:rotten_meat%1|XX", + "M2|M1|M2", + ], + reactions: { + "dirty_water": { "elem1":"rotten_meat", "chance":0.1 }, + "fly": { "elem1":"rotten_meat", "chance":0.2 }, + "dioxin": { "elem1":"rotten_meat", "elem2":null, "chance":0.1 }, + "uranium": { "elem1":"rotten_meat", "chance":0.1 }, + "cancer": { "elem1":"rotten_meat", "chance":0.1 }, + "plague": { "elem1":"rotten_meat", "elem2":null, "chance":0.3 }, + "ant": { "elem1":"rotten_meat", "chance":0.1 }, + "worm": { "elem1":"rotten_meat", "chance":0.1 }, + "rat": { "elem1":"rotten_meat", "chance":0.3 }, + "mushroom_spore": { "elem1":"rotten_meat", "chance":0.1 }, + "mushroom_stalk": { "elem1":"rotten_meat", "chance":0.1 }, + "mercury": { "elem1":"rotten_meat", "elem2":null, "chance":0.2 }, + "mercury_gas": { "elem1":"rotten_meat", "elem2":null, "chance":0.1 }, + "virus": { "elem1":"rotten_meat", "chance":0.1 }, + "poison": { "elem1":"rotten_meat", "elem2":null, "chance":0.5 }, + "infection": { "elem1":"rotten_meat", "elem2":null, "chance":0.1 }, + "ink": { "elem1":"rotten_meat", "elem2":null, "chance":0.1 }, + "acid": { "elem1":"rotten_meat", "elem2":null, "chance":0.5 }, + "acid_gas": { "elem1":"rotten_meat", "chance":0.4 }, + "cyanide": { "elem1":"rotten_meat", "elem2":null, "chance":0.5 } + }, + tempHigh: 100, + stateHigh: "cooked_meat", + tempLow: -18, + stateLow: "frozen_meat", + category:"life", + hidden: true, + breakInto: ["meat", "blood"], + burn:10, + burnTime:200, + burnInto:["cooked_meat","steam","steam","salt"], + state: "solid", + density: 1081, + conduct: 1, + }; + elements.cerebrospinal_fluid = { + color: "#ced7db", + behavior: behaviors.LIQUID, + state: "liquid", + tempHigh: 100, + stateHigh: "steam", + breakInto: "steam", + reactions: JSON.parse(JSON.stringify(elements.water.reactions)), + }; + function validatePanic(pixel) { + //console.log(`validatePanic: validatePanic called on pixel ${pixel.element} at (${pixel.x},${pixel.y}) with panic level ${pixel.panic || 0}`); + if(pixel.element.endsWith("body")) { + //console.log("validatePanic called on body pixel (panic is stored in the head)"); + }; + if(Number.isNaN(pixel.panic)) { + //console.log("NaN case: panic set to 0"); + pixel.panic = 0; + }; + //console.log(`Bounding code running from value of ${pixel.panic}`); + pixel.panic = Math.max(0,Math.min(1,pixel.panic)); + //console.log(`Validation result: Panic set to ${pixel.panic}`); + if(Number.isNaN(pixel.mood)) { + //console.log("NaN case: mood set to 0"); + pixel.mood = 0; + }; + //console.log(`Bounding code running from value of ${pixel.mood}`); + pixel.mood = Math.max(-3,Math.min(3,pixel.mood)); + //console.log(`Validation result: Mood set to ${pixel.mood}`); + }; + goodPixels = { + silver: { panicChange: 0.01, panicChangeChance: 0.1, moodChange: 0.004 }, + gold: { panicChange: 0.02, panicChangeChance: 0.15, moodChange: 0.01 }, + diamond: { panicChange: 0.03, panicChangeChance: 0.2, moodChange: 0.02 }, + }; //effectively, the difference is that good pixels don't make the human flip direction (run away); + badPixels = { + rotten_meat: { panicChange: 0.02, panicChangeChance: 0.15, moodChange: -0.015 }, + blood: { panicChange: 0.06, panicChangeChance: 0.2, moodChange: -0.006 }, + brain: { panicChange: 0.1, panicChangeChance: 0.3, moodChange: -0.005 }, + fire: { panicChange: 0.1, panicChangeChance: 0.1, moodChange: 0 }, + poison: { panicChange: 0.2, panicChangeChance: 0.05, moodChange: -0.01 }, + grenade: { panicChange: 0.2, panicChangeChance: 0.4, moodChange: -0.3 }, + bomb: { panicChange: 0.2, panicChangeChance: 0.4, moodChange: -0.3 }, + tnt: { panicChange: 0.2, panicChangeChance: 0.4, moodChange: 0 }, + dynamite: { panicChange: 0.2, panicChangeChance: 0.4, moodChange: -0.3 }, + upward_bomb: { panicChange: 0.2, panicChangeChance: 0.4, moodChange: -0.3 }, + cluster_bomb: { panicChange: 0.2, panicChangeChance: 0.4, moodChange: -0.4 }, + landmine: { panicChange: 0.25, panicChangeChance: 0.1, moodChange: -0.3 }, + fireball: { panicChange: 0.25, panicChangeChance: 0.45, moodChange: -0.35 }, + magma: { panicChange: 0.3, panicChangeChance: 0.2, moodChange: 0 }, + plasma: { panicChange: 0.3, panicChangeChance: 0.2, moodChange: 0 }, + nuke: { panicChange: 1, panicChangeChance: 1, moodChange: -1 }, //insta-panic + cluster_nuke: { panicChange: 1, panicChangeChance: 1, moodChange: -1 }, //insta-panic + nothing_there_phase_1: { panicChange: 10, panicChangeChance: 1, moodChange: -1 }, //insta-panic + nothing_there_phase_2: { panicChange: 10, panicChangeChance: 1, moodChange: -1 }, //insta-panic + nothing_there_phase_3: { panicChange: 10, panicChangeChance: 1, moodChange: -1 }, //insta-panic + }; //testing + otherPixels = ["head","body"]; //do custom code here + var initialTransparencyArray = ["glass","water","salt_water","sugar_water","steam","oxygen","nitrogen","neon","methane","propane","anesthesia","ammonia","carbon_dioxide","helium","hydrogen","ozone","radiation","pool_water"]; + for(transparentElementIndex = 0; transparentElementIndex < initialTransparencyArray.length; transparentElementIndex++) { + var transparentElement = initialTransparencyArray[i]; + if(typeof(elements[transparentElement]) !== "undefined") { + elements[transparentElement].transparent = true; + }; + }; + elements.body.properties = { + dead: false, + dir: 1, + extremePanicStart: null, + }; + elements.body.tick = function(pixel) { + if(typeof(pixel.extremePanicStart) == "undefined") { + //console.log("oops"); + pixel.extremePanicStart = null + }; + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headpixel = pixelMap[pixel.x][pixel.y-2]; + if (headpixel.element == "head") { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + changePixel(pixel,"rotten_meat"); + } + return + } + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "head") { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance + if (Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + if (head == null) { return }; + if (Math.random() < (0.1 + head.panic)) { // Move 10% chance, varying depending on panic value + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + /* + console.log(move); + console.log("Body X:", pixel.x, "to", pixel.x+move[0]); + console.log("Body Y:", pixel.y, "to", pixel.y+move[1]); + console.log("Head X:",head.x, "to", head.x+move[0]); + console.log("Head Y:", head.y, "to", head.y+move[1]); + */ + //If head coords are empty + if (isEmpty(pixel.x+move[0], pixel.y+move[1]) && isEmpty(head.x+move[0], head.y+move[1])) { + //console.log("Head target coords are empty"); + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + movePixel(head, head.x+move[0], head.y+move[1]); + //console.log(`Moved body to (${pixel.x},${pixel.y}) and head to (${head.x},${head.y})`); + //console.log(`Head-body offset (should always be [0,-1]): [${head.x-pixel.x},${head.y-pixel.y}]`) + break; + } + } + } + // 15% chance to change direction + if(!head.dirLocked) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }; + }; + //if not flagged for extreme panic + //extreme panic will not be flagged in good moods, just to be nice + if(pixel.extremePanicStart == null && head.panic > 0.8 && head.mood <= 0) { + //flag extreme panic + pixel.extremePanicStart = pixelTicks; + } + //if flagged for extreme panic and panic is still extreme + else if(pixel.extremePanicStart != null && (head.panic > 0.8 && head.mood <= 0)) { + //if extreme panic lasts too long + if(pixelTicks - pixel.extremePanicStart > 350) { + //random chance to die from exhaustion/a heart attack/whatever + if(Math.random() < 0.01) { + pixel.dead = true; + }; + }; + } + //if flagged for extreme panic and extreme panic is no longer extreme + else if(pixel.extremePanicStart != null && (head.panic <= 0.8 || head.mood > 0)) { + //unflag + pixel.extremePanicStart = null; + }; + }; + elements.body.onTryMoveInto = function(pixel,otherPixel) { + var pX = pixel.x; + var pY = pixel.y; + if(!pixel.dead && hasPixel(pX,pY-1,"head")) { //if this body pixel is alive and has a head + var head = pixelMap[pX][pY-1]; + var otherElement = otherPixel.element; + var oX = otherPixel.x; + var oY = otherPixel.y; + if(oY !== (pY - 1)) { //exclude the head above this body + if(otherElement === "head") { //if the pixel hitting this body is a head + if(hasPixel(oX,oY+1,"body")) { //if the pixel hitting this pixel has a body under it + var otherBody = pixelMap[oX][oY+1]; + if(otherPixel.dead || otherBody.dead) { //if either part of that human is dead + head.panic += 0.08; //being hit by a dead ******* body is terrifying + } else { + if(otherPixel.panic > 0.04 && otherPixel.mood <= 0) { head.panic += 0.04 }; //living, normal, bodied heads scare only if that incoming human is already scared + }; + } else { //if it's a severed head + if(otherPixel.dead) { //if the head is dead + head.panic += 0.08; //being hit by a /severed ******* head/ is terrifying + } else { + head.panic += 0.1; //being hit by a //******* severed head that's still alive// is even worse + }; + }; + } else if(otherElement === "body") { //if the pixel hitting this body is a body + if(hasPixel(oX,oY-1,"head")) { //if the pixel hitting this pixel has a head on it + var otherHead = pixelMap[oX][oY-1]; + if(otherPixel.dead || otherHead.dead) { //if either part of that human is dead + head.panic += 0.06; //dead whole body case + } else { + if(otherHead.panic > 0.04) { head.panic += 0.04 }; //living, normal, bodied heads scare only if that incoming human is already scared + }; + } else { //severed body case + if(otherPixel.dead) { //if the body is dead + head.panic += 0.08; //imagine being hit by a severed human without the head + } else { + head.panic += 0.1; //imagine the above but the heart is still beating + }; + }; + }; + }; + }; + }; + elements.head.properties = { + dead: false, + dirLocked: false, + panic: 0, + }; + elements.head.tick = function(pixel) { + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + changePixel(pixel,"rotten_meat"); + return + } + } + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "body") { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null } + if (isEmpty(pixel.x, pixel.y+1)) { + tryMove(pixel, pixel.x, pixel.y+1); + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + pixel.mood ??= 0; + if((pixelTicks-pixel.start) % 5 === 0) { + //Vision loop + var pX = pixel.x; + var pY = pixel.y; + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-16 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + //console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(Object.keys(goodPixels).includes(newElement)) { + //no dir flip + if(Math.random() > goodPixels[newElement].panicChangeChance) { + pixel.panic = Math.max(0.25,pixel.panic + goodPixels[newElement].panicChange); + pixel.mood += goodPixels[newElement].moodChange; //like if there was a pretty painting item, it would make you feel better but you wouldn't necessarily feel the need to run towards it + }; + pixel.dirLocked = true; + } else if(Object.keys(badPixels).includes(newElement)) { + body.dir = 1; //flip dir + if(Math.random() > badPixels[newElement].panicChangeChance) { + pixel.panic += badPixels[newElement].panicChange; + pixel.mood += badPixels[newElement].moodChange; + }; + pixel.dirLocked = true; + }; //good and bad should be mutually exclusive; good will be evaulated first because one inevitably has to be considered first + if(otherPixels.includes(newElement)) { + //specific custom code + if(newElement === "head") { + if(hasPixel(nX,nY+1,"body")) { + var newBody = pixelMap[nX][nY+1]; + if(newPixel.dead || newBody.dead) { + pixel.panic += 0.02; //if it's seeing a whole human, it's likely to see the dead head and the dead body, thus double-executing + //it would be nice if there was a way to avoid double/multiple detection of the same human + if(hasPixel(pX,pY+1,"body")) { //mix error-proofing + var body = pixelMap[pX][pY+1]; + body.dir = 1; //run away + }; + } else { + if(newPixel.panic > 0.04) { + if(newPixel.panic > 0.8) { + pixel.panic += 0.015; //it will add up + } else if(newPixel.panic > 0.6) { + pixel.panic += 0.012; + } else if(newPixel.panic > 0.4) { + pixel.panic += 0.009; + } else if(newPixel.panic > 0.2) { + pixel.panic += 0.006; + } else { + pixel.panic += 0.003; + }; + //the vision loop is in the head, and this is in the "seeing head" case, then this will happen when the head sees another head, and heads store panic; this is in the "other head" is panicking case so this will ultimately be the code that runs when its human sees another human panicking + if(Math.random() < 0.5) { + //run in same direction as panicking person + pixel.dir = newPixel.dir + }; + }; + }; + } else { //severed head + newPixel.dead ? pixel.panic += 0.03 : pixel.panic += 0.04; + if(hasPixel(pX,pY+1,"body")) { + var body = pixelMap[pX][pY+1]; + body.dir = 1; //run away + }; + }; + } else if(newElement === "body") { + if(hasPixel(nX,nY-1,"head")) { + var newHead = pixelMap[nX][nY-1]; + if(newPixel.dead || newHead.dead) { + pixel.panic += 0.02; + if(hasPixel(pX,pY+1,"body")) { + var body = pixelMap[pX][pY+1]; + body.dir = 1; //run away + }; + } else { + if(newHead.panic > 0.04) { + if(newHead.panic > 0.8) { + pixel.panic += 0.014; //it will add up + } else if(newHead.panic > 0.6) { + pixel.panic += 0.011; + } else if(newHead.panic > 0.4) { + pixel.panic += 0.008; + } else if(newHead.panic > 0.2) { + pixel.panic += 0.005; + } else { + pixel.panic += 0.002; + }; + }; + }; + } else { //severed body + newPixel.dead ? pixel.panic += 0.025 : pixel.panic += 0.035; + if(hasPixel(pX,pY+1,"body")) { //mix error-proofing + var body = pixelMap[pX][pY+1]; + body.dir = 1; //run away + }; + }; + }; + }; + //code outside of those three if blocks will be applied to pixels of all elements + if(!elements[newElement].transparent) { + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 16 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + //console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(Object.keys(goodPixels).includes(newElement)) { + //no dir flip + if(Math.random() > goodPixels[newElement].panicChangeChance) { + pixel.panic = Math.max(0.25,pixel.panic + goodPixels[newElement].panicChange); + pixel.mood += goodPixels[newElement].moodChange; + }; + pixel.dirLocked = true; + } else if(Object.keys(badPixels).includes(newElement)) { + if(hasPixel(pX,pY+1,"body")) { + var body = pixelMap[pX][pY+1]; + body.dir = -1; //run away + }; + if(Math.random() > badPixels[newElement].panicChangeChance) { + pixel.panic += badPixels[newElement].panicChange; + pixel.mood += badPixels[newElement].moodChange; + }; + pixel.dirLocked = true; + }; //good and bad should be mutually exclusive; good will be evaulated first because one inevitably has to be considered first + if(otherPixels.includes(newElement)) { + if(newElement === "head") { + if(hasPixel(nX,nY+1,"body")) { + var newBody = pixelMap[nX][nY+1]; + if(newPixel.dead || newBody.dead) { + pixel.panic += 0.02; //if it's seeing a whole human, it's likely to see the dead head and the dead body, thus double-executing + //it would be nice if there was a way to avoid double/multiple detection of the same human + if(hasPixel(pX,pY+1,"body")) { + var body = pixelMap[pX][pY+1]; + body.dir = -1; //run away + }; + } else { + if(newPixel.panic > 0.04) { + if(newPixel.panic > 0.8) { + pixel.panic += 0.015; //it will add up + } else if(newPixel.panic > 0.6) { + pixel.panic += 0.012; + } else if(newPixel.panic > 0.4) { + pixel.panic += 0.009; + } else if(newPixel.panic > 0.2) { + pixel.panic += 0.006; + } else { + pixel.panic += 0.003; + }; + }; + }; + } else { //severed head + newPixel.dead ? pixel.panic += 0.03 : pixel.panic += 0.04; + if(hasPixel(pX,pY+1,"body")) { + var body = pixelMap[pX][pY+1]; + body.dir = -1; //run away + }; + }; + } else if(newElement === "body") { + if(hasPixel(nX,nY-1,"head")) { + var newHead = pixelMap[nX][nY-1]; + if(newPixel.dead || newHead.dead) { + pixel.panic += 0.02; + if(hasPixel(pX,pY+1,"body")) { + var body = pixelMap[pX][pY+1]; + body.dir = -1; //run away + }; + } else { + if(newHead.panic > 0.04) { + if(newHead.panic > 0.8) { + pixel.panic += 0.014; //it will add up + } else if(newHead.panic > 0.6) { + pixel.panic += 0.011; + } else if(newHead.panic > 0.4) { + pixel.panic += 0.008; + } else if(newHead.panic > 0.2) { + pixel.panic += 0.005; + } else { + pixel.panic += 0.002; + }; + }; + }; + } else { //severed body + newPixel.dead ? pixel.panic += 0.025 : pixel.panic += 0.035; + if(hasPixel(pX,pY+1,"body")) { + var body = pixelMap[pX][pY+1]; + body.dir = -1; //run away + }; + }; + }; + }; + //code outside of those three if blocks will be applied to pixels of all elements + if(!elements[newElement].transparent) { + break; //can't see through humans + }; + }; + }; + }; + }; + }; + validatePanic(pixel); + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.dirLocked = false; + //console.log("Meh."); + }; + if(Math.random() < ((pixel.panic) > 0.8 ? 0.04 : 0.02)) { //2% chance each tick to decrease panic (4% if the panic is extreme) + //console.log("Decreasing panic"); + pixel.panic < 0.05 ? pixel.panic = 0 : pixel.panic -= 0.05; + }; + }; + elements.head.breakInto = ["bone","brain","brain","cerebrospinal_fluid","blood","blood","meat"]; + elements.head.onTryMoveInto = function(pixel,otherPixel) { + var pX = pixel.x; + var pY = pixel.y; + if(!pixel.dead) { + var otherElement = otherPixel.element; + var oX = otherPixel.x; + var oY = otherPixel.y; + if(oY !== (pY + 1)) { //exclude the body under this head + if(otherElement === "head") { //if the pixel hitting this head is also a head + //console.log("head.onTryMoveInto: Head has tried to move into head"); + if(hasPixel(oX,oY+1,"body")) { //if the pixel hitting this pixel has a body under it + var otherBody = pixelMap[oX][oY+1]; + if(otherPixel.dead || otherBody.dead) { //if either part of that human is dead + pixel.panic += 0.08; //being hit by a dead ******* body is terrifying + //console.log("head.onTryMoveInto: panic increase, case: head hit by dead whole body (head's code branch)"); + } else { + //if(otherPixel.panic > 0.04) { pixel.panic += 0.04; console.log("head.onTryMoveInto: panic increase, case: head hit by panicked whole body (head's code branch)"); }; //living, normal, headed bodies scare only if that incoming human is already scared + }; + } else { //if it's a severed head + if(otherPixel.dead) { //if the head is dead + pixel.panic += 0.08; //being hit by a /severed ******* head/ is terrifying + //console.log("head.onTryMoveInto: panic increase, case: head hit by dead severed head"); + } else { + pixel.panic += 0.1; //being hit by a //******* severed head that's still alive// is even worse + //console.log("head.onTryMoveInto: panic increase, case: head hit by living severed head"); + }; + }; + } else if(otherElement === "body") { //if the pixel hitting this head is a body + if(hasPixel(oX,oY-1,"head")) { //if the body hitting this pixel has a head on it + var otherHead = pixelMap[oX][oY-1]; + if(otherPixel.dead || otherHead.dead) { //if either part of that human is dead + pixel.panic += 0.03; //dead whole body case + //console.log("head.onTryMoveInto: panic increase, case: head hit by dead whole body (body's code branch)"); + } else { + if(otherHead.panic > 0.04) { + pixel.panic += 0.03; + //console.log("head.onTryMoveInto: panic increase, case: head crushed by panicked whole body (body's code branch)"); + } else { + pixel.panic += 0.02; + //console.log("head.onTryMoveInto: panic increase, case: head crushed by whole body (body's code branch)"); + }; + }; + } else { //severed body case + if(otherPixel.dead) { //if the body is dead + pixel.panic += 0.04; //imagine being hit by a severed human without the head + //console.log("head.onTryMoveInto: panic increase, case: head hit by dead severed body"); + } else { + pixel.panic += 0.05; //imagine the above but the heart is still beating + //console.log("head.onTryMoveInto: panic increase, case: head hit by living severed body"); + }; + }; + } else { + if(oX === pX && oY === pY-1) { + var otherInfo = elements[otherElement]; + var otherState; typeof(otherInfo.state) === "undefined" ? otherState = null : otherState = otherInfo.state; + var otherDensity = typeof(otherInfo.density) === "undefined" ? otherDensity = null : otherDensity = otherInfo.density; + if(otherState === "solid") { + if(otherDensity > 5000) { + var chance = (0.1 + (otherDensity/50000)) / 5; + if(Math.random() < chance) { + breakPixel(pixel); + }; + } else if(otherDensity >= 500) { + pixel.panic += (0.01 * (otherDensity / 500)); + } else if(otherDensity >= 100) { + pixel.panic += (0.001 * (otherDensity / 100)); + }; + }; + }; + }; + }; + }; + }; + //Worldgen preset for testing + worldgentypes.basalt_dirt = { + layers: [ + [0, "basalt", 0.05], + [0, "dirt"] + ] + }; + kep1er = [ + //Conn. 0 Conn. - Conn. 1 + ["first_impact", ["#E34B6E","#FE9F19","#8E5ECE"]], + //Lemon Bl. B1ue Bl. + ["doublast", ["#FFFB1D","#2B8FFF"]], + //not edition colors because i'm not doing all of those and they're all really similar anyway (the kep1ian editions are too similar to each other) + ["fly-up", ["#f2f2f2","#15a667","#de0180"]], + //K Midn. Daydr.; physical albums do not look like their "theme colors" and are mostly black + ["troubleshooter", ["#EE378E","#088EE7","#FDDC03"]], + //see fly-up + ["fly-by", ["#e7e6dd","#fcf0ef","#efa1ba","#8d7cb6","#5e74ba","#2b5db5","#e292b7"]], + //Eye Cont. L. Str. 1st Blush + ["lovestruck", ["#C1CCE6","#F5E4CE","#FFD9E0"]], + //Beloved S.kissed Moonlighted + ["magic_hour", ["#EDC1D2","#EFCBB1","#7585B6"]], + //Standard Limited A Limited B + ["fly-high", ["#CBB4BB","#BF888F","#635A69","#635A69","#DDDBDE","BA5069"]], + //Standard Limited A Limited B + ["kep1going", ["#1B1B1D","#20252B","#3D4546","#8B8988","#101010","#101010","#252527","#D0D3D6","#130908","#130908","#65302C","#E09C9F"]], + //gee that's not co- //Scene Nous -nfusing at all, wakeone + ["kep1going_on", ["#BAABD4","#403A76"]], + //Connect N Lost Love & Seek + ["tipi-tap", ["#EA99AD","#FCCEDB","#FCFBFC","#DEDEDE","#1D63A9","#BCE1F0",]] + ]; + for(index in kep1er) { + index = parseInt(index); + var newName = kep1er[index][0]; + var newColor = kep1er[index][1]; + var newDisplayName = newName.replaceAll("_"," ").replaceAll("-"," - ").split(" ").map(x => x.substring(0,1).toUpperCase() + x.substring(1)).join(" ").replace(" - ","-"); + elements[newName] = { + name: newDisplayName, + color: newColor, + tempHigh: 200, + stateHigh: ["ash","molten_plastic"], + density: 332, //based off of First Impact: https://www.amazon.com/Kep1er-IMPACT-Contents-Tracking-Connect/dp/B09MQMNZ62 + //there's only one way to find out the real density of each album and i CANNOT afford that + tick: function(pixel) { + if(!(tryMove(pixel,pixel.x,pixel.y+1))) { + var directions = []; + if(isEmpty(pixel.x-1,pixel.y+1) && isEmpty(pixel.x-1,pixel.y+2)) { + directions.push(-1) + }; + if(isEmpty(pixel.x+1,pixel.y+1) && isEmpty(pixel.x+1,pixel.y+2)) { + directions.push(1) + }; + if(directions.length > 0) { + tryMove(pixel,pixel.x+directions[Math.floor(Math.random() * directions.length)],pixel.y) + }; + }; + doHeat(pixel); + }, + reactions: { + water: { elem1: ["plastic","cellulose","cellulose"], elem2: ["water","water","cellulose",null,null], chance: 0.8 } + }, + burn: 40, + burnTime: 150, + burnInto: ["ash","molten_plastic","carbon_dioxide","smoke"], + category: "other" + }; + goodPixels[newName] = { panicChange: 0.01, panicChangeChance: 0.2, moodChange: 0.035 }; + }; + //CHANGES TO SPONGES ## + elements.sponge.properties ??= {}; + elements.sponge.properties.maxAbsorb = 250; + elements.sponge.tick = function(pixel) { + pixel.absorbed ??= {}; + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + shuffleArray(coordsToCheck); + for (var i = 0; i < coordsToCheck.length; i++) { + if(sumNumericArray(Object.values(pixel.absorbed)) >= pixel.maxAbsorb) { + break; + }; + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state === "liquid" && ((elements[newPixel.element].density || 0) < 2500)) { + pixel.absorbed[newPixel.element] ??= 0; + pixel.absorbed[newPixel.element]++; + deletePixel(coord[0],coord[1]); + }; + }; + }; + if((pixel.absorbed.water ?? 0) < 5) { + doBurning(pixel); + }; + }; + elements.sponge.onTryMoveInto = function(pixel,otherPixel) { + var absorbedElements = Object.keys(pixel.absorbed ?? {}); + if(absorbedElements.length == 0) { + return false; + }; + var otherInfo = elements[otherPixel.element] + if((otherInfo.state ?? "solid") == "solid" && (otherInfo.density ?? 1000) >= 200) { + //console.log(otherPixel.element,otherInfo.state); + var outputOffsets = [pixel.x - otherPixel.x, pixel.y - otherPixel.y]; + var twiceOffsets = outputOffsets.map(x => x * 2); + var newCoords = [pixel.x + outputOffsets[0], pixel.y + outputOffsets[1]]; + var twiceCoords = [pixel.x + twiceOffsets[0], pixel.y + twiceOffsets[1]]; + if(!isEmpty(newCoords[0],newCoords[1],true)) { + if(outOfBounds(newCoords[0],newCoords[1])) { //fail if OOB + return false; + }; + var newPixel = pixelMap[newCoords[0]][newCoords[1]]; + if((elements[newPixel.element].state ?? "solid") !== "gas") { //only displace gases + return false; + }; + if(!tryMove(newPixel,twiceCoords[0],twiceCoords[1])) { //if it can't push the gas out to the next pixel over + return false; //then return false because perpendicular expulsion too complex and multifaceted to deal with + }; + }; + if(isEmpty(pixel.x,pixel.y+1)) { + var randomElement = randomChoice(absorbedElements); + if(pixel.absorbed[randomElement] > 0) { + if(tryCreatePixel(randomElement,newCoords[0],newCoords[1])) { pixel.absorbed[randomElement]-- }; + } else { + delete pixel.absorbed[randomChoice] + }; + }; + }; + }; + //MIX OF PRIMORDIAL SOUP AND BIRTHPOOL (also primordial soup freezing, and state fixes) ## + elements.primordial_soup.tempLow = 0; + elements.birthpool.tempHigh = 100; + elements.birthpool.stateHigh = ["magic","steam"]; + elements.birthpool.tempLow = 0; + elements.birthpool.state = "liquid"; + elements.birthpool.forceAutoGen = true; + elements.primordial_birthpool = { + color:["#5E7453","#5E745D","#5E744B","#6C7C50","#6C7C59","#6C7C47"], + state: "liquid", + tempHigh: 100, + stateHigh: ["steam","steam","magic"], + behavior: [ + "CR:foam%1|CR:sapling,wheat_seed,flower_seed,algae,cell,mushroom_spore,lichen,yeast,antibody,cellulose%0.5 AND CR:foam%2|CR:foam%1", + "M2|XX|M2", + "M1|M1|M1", + ], + reactions: { + "cancer": { "elem1":["toxic_mistake","dirty_water"] }, + "cyanide": { "elem1":["toxic_mistake","dirty_water"] }, + "infection": { "elem1":["toxic_mistake","dirty_water"] }, + "plague": { "elem1":["toxic_mistake","dirty_water"] }, + "ammonia": { "elem1":["algae","cell","mushroom_spore","lichen","yeast","antibody"], "chance":0.05 }, + "radiation": { "elem1":["algae","cell","mushroom_spore","lichen","yeast","antibody"], "chance":0.15 }, + "light": { "elem1":["algae","cell","mushroom_spore","lichen","yeast","antibody"], "chance":0.5 }, + "oxygen": { "elem1":["algae","cell","mushroom_spore","lichen","yeast","antibody"], "chance":0.02 } + }, + density: 1110, + tempHigh: 100, + stateHigh: "steam", + conduct: 0.33, + category: "liquids", + hidden: true, + reactions: { + concoction: { "elem1": ["primordial_soup", "birthpool", "primordial_birthpool"], "elem2": ["primordial_soup", "birthpool", "primordial_birthpool"], "chance":0.0045} + }, + }; + if(!elements.birthpool.reactions) { + elements.birthpool.reactions = {} + } + elements.birthpool.reactions.primordial_soup = { "elem1":"primordial_birthpool", "elem2":"primordial_birthpool" } + //AUTOGENERATED CONTENT ## + //Required secondary function + function canvasfooterTemplate() { //CURSED AF + return `Changelog(NEW)FeedbackWikiYouTubeDiscord • Install Offline

v${currentversion} • ${elementCount} elements, with ${hiddenCount} hidden.

©2021-${new Date().getFullYear()}. All Rights Reserved.

` + }; + //urlParams reads + //Bombs + if(urlParams.get('generateBombs') !== null) { //if the variable exists at all + generateBombs = true + } else { //if it doesn't (and it returns null) + generateBombs = false + } + if(urlParams.get('bombAmount') != null) { //null check + bombAmount = urlParams.get('bombAmount') + if(isNaN(bombAmount) || bombAmount === "" || bombAmount === null) { //NaN check + bombAmount = 10 + } + bombAmount = parseInt(bombAmount) + if(bombAmount > 50) { + alert("Maximum amount of additional bomb/anti-bomb pairs is 50.\nOnly 50 were added.") + } else if(bombAmount < 1) { + alert("Minimum amount of additional bomb/anti-bomb pairs is 1.\n1 pair was added.") + } + bombAmount = Math.min(50,Math.max(bombAmount,1)) + } else { + bombAmount = 10 + } + //Clouds + if(urlParams.get('generateClouds') !== null) { //if the variable exists at all + generateClouds = true + } else { //if it doesn't (and it returns null) + generateClouds = false + } + if(urlParams.get('cloudIncludeRandom') !== null) { //if the variable exists at all + cloudIncludeRandom = true + } else { //if it doesn't (and it returns null) + cloudIncludeRandom = false + } + //Creepers + //Include generated creepers in Random tool? + if(urlParams.get('creeperIncludeRandom') !== null) { //if the variable exists at all + creeperIncludeRandom = true + } else { //if it doesn't (and it returns null) + creeperIncludeRandom = false + } + //Generate creepers + if(urlParams.get('generateCreepers') !== null) { //if the variable exists at all + generateCreepers = true + } else { //if it doesn't (and it returns null) + generateCreepers = false + } + //Fairies + if(urlParams.get('fairyIncludeRandom') !== null) { //if the variable exists at all + fairyIncludeRandom = true + } else { //if it doesn't (and it returns null) + fairyIncludeRandom = false + } + //Generate fairies + if(urlParams.get('generateFairies') !== null) { //if the variable exists at all + generateFairies = true + } else { //if it doesn't (and it returns null) + generateFairies = false + } + if(urlParams.get('fairyAmount') != null) { //null check + fairyAmount = urlParams.get('fairyAmount') + if(isNaN(fairyAmount) || fairyAmount === "" || fairyAmount === null) { //NaN check + fairyAmount = 10 + } + fairyAmount = parseInt(fairyAmount) + if(fairyAmount > 10000) { + alert("Maximum amount of additional fairies is 10000.\nOnly 10000 fairies were added.") + } else if(fairyAmount < 1) { + alert("Minimum amount of additional fairies is 1.\n1 fairy was added.") + } + fairyAmount = Math.min(10000,Math.max(fairyAmount,1)) + } else { + fairyAmount = 10 + } + //Spouts + //Generate spouts + if(urlParams.get('generateSpouts') !== null) { //if the variable exists at all + generateSpouts = true + } else { //if it doesn't (and it returns null) + generateSpouts = false + } + if(urlParams.get('spoutIncludeRandom') !== null) { //if the variable exists at all + spoutIncludeRandom = true + } else { //if it doesn't (and it returns null) + spoutIncludeRandom = false + } + //Requisite variables + //Bombs + amalgamatedBombFire = "plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,smoke,plasma,plasma,fire,smoke,fire,smoke,plasma,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,acid,acid,oil,oil,oil,oil,oil,oil,oil,plasma,plasma,plasma,plasma,plasma,smoke,plasma,plasma,fire,smoke,plasma,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,flash,flash,flash,flash,flash,acid_gas,acid_gas,acid_gas,acid,oil,oil,oil,oil,oil,oil,oil,oil,oil,oil,plasma,plasma,plasma,plasma,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,acid,acid,oil,oil,oil,oil,oil,oil,oil,plasma,plasma,plasma,plasma,plasma,smoke,plasma,plasma,fire,smoke,plasma,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,electric_cluster_bomb,electric_cluster_bomb,flash,flash,flash,flash,flash,acid_gas,acid_gas,acid_gas,acid,oil,oil,oil,oil,oil,oil,oil,oil,oil,oil,plasma,plasma,plasma,plasma,plague,plague,plague,plague,plague,plague,radiation,radiation,radiation,radiation,radiation,radiation,radiation,radiation,uranium,uranium,uranium,uranium,uranium,uranium,greek_fire,greek_fire,greek_fire,greek_fire,greek_fire,antimatter,antimatter,antimatter,antimatter,antimatter,smoke_grenade,antimatter,smoke_grenade,fireball,flash,acid_gas,acid_gas,acid_gas,plague,plague,plague,plague,plague,plague,radiation,radiation,radiation,radiation,radiation,radiation,radiation,radiation,uranium,uranium,uranium,uranium,uranium,uranium,greek_fire,greek_fire,greek_fire,greek_fire,greek_fire,antimatter,antimatter,antimatter,antimatter,antimatter,smoke_grenade,antimatter,flash,acid_gas,acid_gas,acid_gas,radiation,radiation,radiation,radiation,plague,acid_gas,acid_gas,acid_gas,chlorine,chlorine,chlorine" + eLists.BOMB = ["bomb", "tnt", "c4", "grenade", "dynamite", "gunpowder", "firework", "nuke", "h_bomb", "dirty_bomb", "emp_bomb", "sticky_bomb", "cold_bomb", "hot_bomb", "electro_bomb", "water_bomb", "antimatter_bomb", "flashbang", "smoke_grenade", "fireball", "landmine", "cluster_bomb", "cluster_nuke", "op_hottester_bomb", "anti-bomb", "electric_bomblet", "electric_cluster_bomb", "radioactive_popper", "acid_bomb", "amalgamated_bomb"]; bombChoices = eLists.BOMB; + var excludedBombElements = ["water", "antimatter", "acid"]; + //Clouds + eLists.CLOUD = ["cloud", "rain_cloud", "snow_cloud", "fire_cloud", "hail_cloud", "acid_cloud", "pyrocumulus"]; cloudChoices = eLists.CLOUD; + var includedClouds = ["cloud", "rain_cloud", "snow_cloud", "fire_cloud", "hail_cloud", "acid_cloud", "pyrocumulus"]; + var excludedCloudElements = ["snow", "fire", "hail", "acid"]; + if(typeof(behaviorGenerators) === "undefined") { behaviorGenerators = {} }; + //Creepers + var placeholderColor = "#FF00FF"; + var hslOffsets = [[0, -5, 5], [0, -20, 10], [0, 0, 10], [0, -20, 10], [0, -35, 0], [0, -20, -30], [0, 10, -10], [0, 10, 20], [0, -20, 10], [0, -10, 5]]; + var colorOfRandomCreeper = ["#7ba883", "#8aba8a", "#87b292", "#8aba8a", "#71a171", "#346434", "#4d6d72", "#a0caad", "#8aba8a", "#7dac7f"] + //Fairies + var excludedFairyElements = [] + var backupCategoryWhitelist = ["land","powders","weapons","food","life","corruption","states","fey","Fantastic Creatures","dyes","energy liquids","random liquids","random gases","random rocks"]; + var backupElementWhitelist = ["mercury", "chalcopyrite_ore", "chalcopyrite_dust", "copper_concentrate", "fluxed_copper_concentrate", "unignited_pyrestone", "ignited_pyrestone", "everfire_dust", "extinguished_everfire_dust", "mistake", "polusium_oxide", "vaporized_polusium_oxide", "glowstone_dust", "redstone_dust", "soul_mud", "wet_soul_sand", "nitrogen_snow", "fusion_catalyst", "coal", "coal_coke", "blast_furnace_fuel", "molten_mythril"]; + //Spouts + eLists.SPOUT = ["spout", "udder", "torch"]; spoutChoices = eLists.SPOUT; + var excludedSpoutElements = ["ketchup", "liquid_cloner", "fire_cloner"] + var includedSpouts = ["ketchup_spout", "spout", "udder", "torch"] + var backupCategoryWhitelist = ["land","powders","weapons","food","life","corruption","states","fey","Fantastic Creatures","dyes","energy liquids","random liquids","random gases","random rocks"]; + var backupElementWhitelist = ["mercury", "chalcopyrite_ore", "chalcopyrite_dust", "copper_concentrate", "fluxed_copper_concentrate", "unignited_pyrestone", "ignited_pyrestone", "everfire_dust", "extinguished_everfire_dust", "mistake", "polusium_oxide", "vaporized_polusium_oxide", "glowstone_dust", "redstone_dust", "soul_mud", "wet_soul_sand", "nitrogen_snow", "fusion_catalyst", "coal", "coal_coke", "blast_furnace_fuel", "molten_mythril"]; + //Requisite non-generating functions + //Bombs + function firebombFire(pixel,x,y,radius,fire,smoke,power,damage) { + var coords = circleCoords(pixel.x,pixel.y,radius); + for (var i = 0; i < coords.length; i++) { + var x = coords[i].x; + var y = coords[i].y; + if(!isEmpty(x,y,true)) { + var pixel = pixelMap[x][y]; + var info = elements[pixel.element]; + var cursedFireChance = 0.15 + power; + if (info.burn) { //Light everything on fire + pixel.burning = true; + pixel.burnStart = pixelTicks; + pixel.temp += 10; //smoke prevention + } else if(Math.random() < cursedFireChance) { //(15+power)%/px cursed burning + pixel.burning = true; + pixel.burnStart = pixelTicks; + pixel.temp += 10; + }; + } else if(isEmpty(x,y)) { //if there's space for fire + if (Array.isArray(fire)) { //this should remain "fire" + var newfire = fire[Math.floor(Math.random() * fire.length)]; + } else { + var newfire = fire; + }; + createPixel(newfire,x,y); //add fire + var firePixel = pixelMap[x][y]; + firePixel.temp = Math.max(elements[newfire].temp ?? 0,elements.fire.temp,firePixel.temp ?? 0); + firePixel.burning = true; + }; + }; + }; + elements.disappear = { + color: "#7f7f7f", + behavior: behaviors.SELFDELETE, + insulate: true, + temp: -273.15, + hardness: 0, + excludeRandom: true, + category: "other" + }; + function forcebombVelocity(pixel,x,y,radius,fire,smoke,power,damage) { + var coords = circleCoords(pixel.x,pixel.y,radius); + for (var i = 0; i < coords.length; i++) { + var coordX = coords[i].x; + var coordY = coords[i].y; + if(!isEmpty(coordX,coordY,true)) { + let pixelle = pixelMap[coordX]?.[coordY]; + if(typeof(pixelle) !== "object" || pixelle === null) { continue }; + if(coordX === x && coordY === y) { + if(pixelle.element === "force_bomb") { fuckingDeletePixel(pixelle); continue } + }; + // set the pixelle.vx and pixelle.vy depending on the angle and power + var angle = Math.atan2(pixelle.y-y,pixelle.x-x); + pixelle.vx = Math.round(4 * (pixelle.vx|0) + (5 * Math.cos(angle) * (radius * power/5))); + pixelle.vx += (3 * Math.sign(pixelle.vx)); + pixelle.vx = Math.sign(pixelle.vx) == -1 ? bound(pixelle.vx,-100,0) : bound(pixelle.vx,0,100); + pixelle.vy = Math.round(4 * (pixelle.vy|0) + (5 * Math.sin(angle) * (radius * power/5))); + pixelle.vy += (3 * Math.sign(pixelle.vy)); + pixelle.vy = Math.sign(pixelle.vx) == -1 ? bound(pixelle.vy,-100,0) : bound(pixelle.vy,0,100); + } + }; + }; + function hotterBomb(pixel,x,y,radius,fire,smoke,power,damage) { + //console.log(`Radius: ${radius}\nPower: ${power}\nPixel: (${pixel.x},${pixel.y})\nDamage: ${damage}`); + //console.log(`Expected temperature increase for pixel at (${pixel.x},${pixel.y}): ${800 * ((1 + (7 * damage)) ** 2) * ((power ** 2) * 1.5)}`); + pixel.temp += (800 * ((1 + (7 * damage)) ** 2) * ((power ** 2) * 1.5)); + }; + //explodeAtPlus(x,y,radius,fire="fire",smoke="smoke",beforeFunction=null,afterFunction=null,changeTemp=true) { + //beforeFunction(pixel,x,y,radius,fire,smoke,power,damage) + function wallBombBeforeFunction(pixel,x,y,radius,fire,smoke,power,damage) { + var hardness = (elements[pixel.element].hardness ?? 0) / 2; + damage = damage * ((1 - hardness)*10); + //console.log(damage); + if(damage > 0.25) { + pixel.breakInto ? breakPixel(pixel) : deletePixel(pixel.x,pixel.y) + }; + return + }; + elements.wall_bomb = { + color: "#af8f8f", + tick: function(pixel) { + doDefaults(pixel); + if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds) + var newPixel = pixelMap[pixel.x][pixel.y-1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,10,"fire","smoke",wallBombBeforeFunction,null,false); + if(pixel) { deletePixel(pixel.x,pixel.y) }; + return + }; + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case) + var newPixel = pixelMap[pixel.x][pixel.y+1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,10,"fire","smoke",wallBombBeforeFunction,null,false); + if(pixel) { deletePixel(pixel.x,pixel.y) }; + return + }; + }; + if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case) + explodeAtPlus(pixel.x,pixel.y,10,"fire","smoke",wallBombBeforeFunction,null,false); + if(pixel) { deletePixel(pixel.x,pixel.y) }; + return + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER + Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1); + }; + }, + category: "weapons", + state: "solid", + density: 3000, + excludeRandom: true, + }; + elements.amba_tsunami = { + color: ["#2449d1","#4b6adb","#8093d9"], + behavior: behaviors.WALL, + properties: { + active: true, + }, + tick: function(pixel) { + //Iteration initial checks + if(!pixel) { + return; + }; + if(!pixel.active) { + deletePixel(pixel.x,pixel.y); + }; + //Initial property-setting + var pixelIsOnLeft = (pixel.x < (width/2)); + pixel.fromX ??= pixelIsOnLeft ? 1 : width - 1; + pixel.direction ??= pixelIsOnLeft ? 1 : -1; + var floorHeight = pixel.y + 1; + while(isEmpty(pixel.x,floorHeight,false)) { + floorHeight++ + }; + pixel.floorHeight ??= floorHeight; + //Actual doer code + var newX = pixel.fromX + pixel.direction; + if(outOfBounds(newX,1)) { + pixel.active = false; + return + }; + var bottomY = (pixel.floorHeight + 3); //extend 4 pixels below + var topY = bottomY - 13; //topY < bottomY because in this game +Y is *downward* + for(var i = bottomY; i >= topY; i--) { + var waterToDo = randomChoice(["salt_water","dirty_water"]); + var fc = {x: newX, y: i}; + if(outOfBounds(fc.x,fc.y)) {continue}; + if(isEmpty(fc.x,fc.y,false)) { + //fill with water + createPixel(waterToDo,fc.x,fc.y) + } else { + var newPixel = pixelMap[fc.x]?.[fc.y]; + if(!newPixel) { continue }; + //break + tryBreak(newPixel,true,true); + if(!newPixel) { continue }; + //water reaction steal + if(elements[newPixel.element].reactions?.water) { + var waterRxn = elements[newPixel.element].reactions.water; + var elem1 = waterRxn.elem1; + while(Array.isArray(elem1)) { + elem1 = randomChoice(elem1) + }; + if(elem1 !== null) { + changePixel(newPixel,elem1,true) + } + } else if(elements.water.reactions[newPixel.element]) { + var waterRxn2 = elements.water.reactions[newPixel.element]; + elem2 = waterRxn2.elem2; + while(Array.isArray(elem2)) { + elem2 = randomChoice(elem2) + }; + if(elem2 !== null) { + changePixel(newPixel,elem2,true) + } + }; + if(!newPixel) { continue }; + //add velocity; + newPixel.vx ??= 0; + newPixel.vy ??= 0; + newPixel.vx += (pixel.direction * 5) + newPixel.vy -= 3; + }; + }; + pixel.fromX += pixel.direction + }, + state: "solid", + category: "special", + density: elements.water.density + }; + elements.megatsunami = { + color: ["#1f2aa3","#2641c9","#3a57c9"], + behavior: behaviors.WALL, + properties: { + active: true, + }, + tick: function(pixel) { + //Iteration initial checks + if(!pixel) { + return; + }; + if(!pixel.active) { + deletePixel(pixel.x,pixel.y); + }; + //Initial property-setting + var pixelIsOnLeft = (pixel.x < (width/2)); + pixel.fromX ??= pixelIsOnLeft ? 1 : width - 1; + pixel.direction ??= pixelIsOnLeft ? 1 : -1; + var floorHeight = pixel.y + 1; + while(isEmpty(pixel.x,floorHeight,false)) { + floorHeight++ + }; + pixel.floorHeight ??= floorHeight; + //Actual doer code + var bottomY = (pixel.floorHeight + 9); //extend 10 pixels below + var topY = bottomY - 43; //topY < bottomY because in this game +Y is *downward* + for(var h = 0; h < 2; h++) { + var newX = pixel.fromX + pixel.direction; + if(outOfBounds(newX,1)) { + pixel.active = false; + return + }; + for(var i = bottomY; i >= topY; i--) { + var waterToDo = randomChoice(["salt_water","salt_water","salt_water","dirty_water","dirty_water"]); + var fc = {x: newX, y: i}; + if(outOfBounds(fc.x,fc.y)) {continue}; + if(isEmpty(fc.x,fc.y,false)) { + //fill with water + createPixel(waterToDo,fc.x,fc.y) + } else { + var newPixel = pixelMap[fc.x]?.[fc.y]; + if(!newPixel) { continue }; + //break + tryBreak(newPixel,true,true); + if(!newPixel) { continue }; + //water reaction steal + if(elements[newPixel.element].reactions?.water) { + var waterRxn = elements[newPixel.element].reactions.water; + var elem1 = waterRxn.elem1; + while(Array.isArray(elem1)) { + elem1 = randomChoice(elem1) + }; + if(elem1 !== null) { + changePixel(newPixel,elem1,true) + } + } else if(elements.water.reactions[newPixel.element]) { + var waterRxn2 = elements.water.reactions[newPixel.element]; + elem2 = waterRxn2.elem2; + while(Array.isArray(elem2)) { + elem2 = randomChoice(elem2) + }; + if(elem2 !== null) { + changePixel(newPixel,elem2,true) + } + }; + if(!newPixel) { continue }; + //add velocity; + newPixel.vx ??= 0; + newPixel.vy ??= 0; + newPixel.vx += (pixel.direction * 13) + newPixel.vy -= 6; + }; + }; + pixel.fromX += pixel.direction + }; + }, + state: "solid", + category: "special", + density: elements.water.density + }; + elements.lava_tsunami = { + color: ["#ff370a","#e84a23","#e67740"], + behavior: behaviors.WALL, + properties: { + active: true, + }, + tick: function(pixel) { + //Iteration initial checks + if(!pixel) { + return; + }; + if(!pixel.active) { + deletePixel(pixel.x,pixel.y); + }; + //Initial property-setting + var pixelIsOnLeft = (pixel.x < (width/2)); + pixel.fromX ??= pixelIsOnLeft ? 1 : width - 1; + pixel.direction ??= pixelIsOnLeft ? 1 : -1; + var floorHeight = pixel.y + 1; + while(isEmpty(pixel.x,floorHeight,false)) { + floorHeight++ + }; + pixel.floorHeight ??= floorHeight; + //Actual doer code + var newX = pixel.fromX + pixel.direction; + if(outOfBounds(newX,1)) { + pixel.active = false; + return + }; + var bottomY = (pixel.floorHeight + 3); //extend 4 pixels below + var topY = bottomY - 13; //topY < bottomY because in this game +Y is *downward* + for(var i = bottomY; i >= topY; i--) { + var fc = {x: newX, y: i}; + if(outOfBounds(fc.x,fc.y)) {continue}; + if(isEmpty(fc.x,fc.y,false)) { + //fill with lava + createPixelReturn("magma",fc.x,fc.y).temp = 1400; + } else { + var newPixel = pixelMap[fc.x]?.[fc.y]; + if(!newPixel) { continue }; + var data = elements[newPixel.element]; + //break + for(var j = 0; j < 3; j++) { + tryBreak(newPixel,true,j == 0); + if(!newPixel) { break } + }; + //lava reaction steal + if(data.reactions?.magma) { + var magmaRxn = data.reactions.magma; + var elem2 = magmaRxn.elem2; + while(Array.isArray(elem2)) { + elem2 = randomChoice(elem2) + }; + if(elem2 !== null) { + changePixel(newPixel,elem2,true) + } + } else if(elements.magma.reactions[newPixel.element]) { + var magmaRxn2 = elements.magma.reactions?.[newPixel.element]; + if(!magmaRxn2) { + } else { + elem2 = magmaRxn2.elem2; + while(Array.isArray(elem2)) { + elem2 = randomChoice(elem2) + }; + if(elem2 !== null) { + changePixel(newPixel,elem2,true) + } + } + }; + if(!newPixel) { continue }; + newPixel.temp = Math.max(newPixel.temp,1400); pixelTempCheck(newPixel); + if(!newPixel) { continue }; + if(newPixel.element == "fire") { changePixelReturn(newPixel,"magma",true).temp = 1400 }; + if(data.burn) { newPixel.burning = true; newPixel.burnStart = pixelTicks }; + if(Math.random() < 0.1 && newPixel.burnInto) { finishBurn(newPixel) }; + if(newPixel.element == "fire") { changePixelReturn(newPixel,"magma",true).temp = 1400 }; + if(!newPixel) { continue }; + //add velocity; + newPixel.vx ??= 0; + newPixel.vy ??= 0; + newPixel.vx += (pixel.direction * 6) + newPixel.vy -= 3; + }; + }; + pixel.fromX += pixel.direction + }, + state: "solid", + category: "special", + density: elements.magma.density + }; + elements.lava_megatsunami = { + color: ["#b32b10","#c24d1f","#d66924"], + behavior: behaviors.WALL, + properties: { + active: true, + }, + tick: function(pixel) { + //Iteration initial checks + if(!pixel) { + return; + }; + if(!pixel.active) { + deletePixel(pixel.x,pixel.y); + }; + //Initial property-setting + var pixelIsOnLeft = (pixel.x < (width/2)); + pixel.fromX ??= pixelIsOnLeft ? 1 : width - 1; + pixel.direction ??= pixelIsOnLeft ? 1 : -1; + var floorHeight = pixel.y + 1; + while(isEmpty(pixel.x,floorHeight,false)) { + floorHeight++ + }; + pixel.floorHeight ??= floorHeight; + //Actual doer code + var bottomY = (pixel.floorHeight + 9); //extend 10 pixels below + var topY = bottomY - 43; //topY < bottomY because in this game +Y is *downward* + for(var h = 0; h < 2; h++) { + var newX = pixel.fromX + pixel.direction; + if(outOfBounds(newX,1)) { + pixel.active = false; + return + }; + for(var i = bottomY; i >= topY; i--) { + var fc = {x: newX, y: i}; + if(outOfBounds(fc.x,fc.y)) {continue}; + if(isEmpty(fc.x,fc.y,false)) { + //fill with lava + createPixelReturn("magma",fc.x,fc.y).temp = 1450; + } else { + var newPixel = pixelMap[fc.x]?.[fc.y]; + if(!newPixel) { continue }; + var data = elements[newPixel.element]; + //break + for(var j = 0; j < 3; j++) { + tryBreak(newPixel,true,j == 0); + if(!newPixel) { break } + }; + if(!newPixel) { continue }; + //lave reaction steal + if(data.reactions?.magma) { + var magmaRxn = data.reactions.magma; + var elem2 = magmaRxn.elem2; + while(Array.isArray(elem2)) { + elem2 = randomChoice(elem2) + }; + if(elem2 !== null) { + changePixel(newPixel,elem2,true) + } + } else if(elements.magma.reactions[newPixel.element]) { + var magmaRxn2 = elements.magma.reactions[newPixel.element]; + elem2 = magmaRxn2.elem2; + while(Array.isArray(elem2)) { + elem2 = randomChoice(elem2) + }; + if(elem2 !== null) { + changePixel(newPixel,elem2,true) + } + }; + if(!newPixel) { continue }; + newPixel.temp = Math.max(newPixel.temp,1450); pixelTempCheck(newPixel); + if(!newPixel) { continue }; + if(newPixel.element == "fire") { changePixelReturn(newPixel,"magma",true).temp = 1400 }; + if(data.burn) { newPixel.burning = true; newPixel.burnStart = pixelTicks }; + if(Math.random() < 0.1 && newPixel.burnInto) { finishBurn(newPixel) }; + if(newPixel.element == "fire") { changePixelReturn(newPixel,"magma",true).temp = 1400 }; + if(!newPixel) { continue }; + //add velocity; + newPixel.vx ??= 0; + newPixel.vy ??= 0; + newPixel.vx += (pixel.direction * 8) + newPixel.vy -= 5; + }; + }; + pixel.fromX += pixel.direction + }; + }, + state: "solid", + category: "special", + density: elements.magma.density + }; + function empCharge(pixel,x,y,radius,fire,smoke,power,damage) { + var info = elements[pixel.element]; + if(info.conduct) { + var distanceFromEdge = Math.max(0,radius - coordPyth(pixel.x,pixel.y,x,y)); + var radiusRelatedIncrease = Math.sqrt(distanceFromEdge/5); + pixel.charge ??= 0; + pixel.charge += (50 * radiusRelatedIncrease); + pixel.temp += 700/bound(info.conduct,0.1,1); + }; + }; + function starbombHeat(pixel,x,y,radius,fire,smoke,power,damage) { //Massively heats depending on distance from explosion center + var distanceFromEdge = Math.max(0,radius - coordPyth(pixel.x,pixel.y,x,y)); + var radiusRelatedIncrease = 10 ** logN(distanceFromEdge,5); + pixel.temp += (10000 * radiusRelatedIncrease); + pixelTempCheck(pixel); + }; + //Clouds + behaviorGenerators.cloud = function(element) { + if(element instanceof Array) { element = element.join(",") }; + return [ + ["XX","XX","XX"], + ["XX",`CH:${element}%0.05`,"M1%2.5 AND BO"], + ["XX","XX","XX"] + ]; + }; + behaviorGenerators.heavy_cloud = function(element) { + if(element instanceof Array) { element = element.join(",") }; + return [ + ["XX","XX","XX"], + ["XX",`CH:${element}%0.1`,"M1%2.5 AND BO"], + ["XX",`CR:${element}%0.05`,"XX"] + ]; + }; + behaviorGenerators.heavier_cloud = function(element) { + if(element instanceof Array) { element = element.join(",") }; + return [ + ["XX","XX","XX"], + ["XX",`CH:${element}%0.2`,"M1%2.5 AND BO"], + ["XX",`CR:${element}%0.1`,"XX"] + ]; + }; + behaviorGenerators.heaviest_cloud = function(element) { + if(element instanceof Array) { element = element.join(",") }; + return [ + ["XX","XX","XX"], + ["XX",`CH:${element}%0.4`,"M1%2.5 AND BO"], + ["XX",`CR:${element}%0.2`,"XX"] + ]; + }; + behaviorGenerators.heaviester_cloud = function(element) { + if(element instanceof Array) { element = element.join(",") }; + return [ + ["XX","XX","XX"], + ["XX",`CH:${element}%0.8`,"M1%2.5 AND BO"], + ["XX",`CR:${element}%0.4`,"XX"] + ]; + }; + behaviorGenerators.heaviestest_cloud = function(element) { + if(element instanceof Array) { element = element.join(",") }; + return [ + ["XX",`CR:${element}%0.8`,"XX"], + [`CR:${element}%0.05`,`CH:${element}%3`,`CR:${element}%0.05 AND M1%2.5 AND BO`], + ["XX",`CR:${element}%0.8`,"XX"] + ]; + }; + function placeDownwardColumn(element,xx,yy,creationChance) { + if(element.includes(",")) { element = element.split(",") }; + var newElement = element; + //console.log(JSON.stringify(element)); + //console.log("from " + xx + "," + yy) + if(!outOfBounds(xx,yy)) { + for(i = yy; i < pixelMap[xx].length; i++) { + if(Array.isArray(element)) { + newElement = element[Math.floor(Math.random() * element.length)]; + }; + //console.log(JSON.stringify(newElement)); + if(isEmpty(xx,i,false)) { + if(Math.random() < creationChance) { + createPixel(newElement,xx,i) + } + } else if(!isEmpty(xx,i,true)) { + var newPixel = pixelMap[xx][i]; + var otherElement = newPixel.element; + var newState = elements[otherElement].state ?? "solid"; //assume undefined = solid + if(newState !== "solid") { + continue; + } else { + break; + }; + } else if(outOfBounds(xx,i)) { + break; + }; + }; + }; + }; + function singleColumn(pixel,element,chance,fillAmountDecimal) { + if(Math.random() < chance) { + placeDownwardColumn(element,pixel.x,pixel.y+1,fillAmountDecimal); + } + }; + function multiColumn(pixel,element,chance,fillAmountDecimal,columnRadius) { + if(Math.random() < chance) { + for(j = 0 - columnRadius; j <= columnRadius; j++) { + placeDownwardColumn(element,pixel.x+j,pixel.y+1,fillAmountDecimal); + }; + } + }; + //Creepers + autoCreeperPlacerTick = function(pixel) { + var creeperElement = elements[pixel.element].creeperType; + var headName,bodyName; + if(typeof(creeperElement === "string")) { //comma separated string check + if(creeperElement.includes(",")) { //if it is + creeperElement = creeperElement.split(","); //to array + creeperElement = creeperElement.filter(function(e) { //strip nonexistent elements + return typeof(elements[e]) === "object"; + }); + }; + }; + if(Array.isArray(creeperElement)) { + headName = `${creeperElement.join("_")}_creeper_head`; //auto head element name + bodyName = `${creeperElement.join("_")}_creeper_body`; //auto body element name + } else { + headName = `${creeperElement}_creeper_head`; //auto head element name + bodyName = `${creeperElement}_creeper_body`; //auto body element name + }; + if (isEmpty(pixel.x, pixel.y+1)) { + createPixel(bodyName, pixel.x, pixel.y+1); + pixel.element = headName; + pixel.color = pixelColorPick(pixel) + } else if (isEmpty(pixel.x, pixel.y-1)) { + createPixel(headName, pixel.x, pixel.y-1); + pixel.element = bodyName; + pixel.color = pixelColorPick(pixel) + } else { + deletePixel(pixel.x, pixel.y); + }; + }; + autoCreeperBodyTick = function(pixel) { + var creeperElement = elements[pixel.element].creeperType; + var headName,bodyName,explodeInto; + if(typeof(creeperElement === "string")) { //comma separated string check + if(creeperElement.includes(",")) { //if it is + creeperElement = creeperElement.split(","); //to array + creeperElement = creeperElement.filter(function(e) { //strip nonexistent elements + return typeof(elements[e]) === "object"; + }); + }; + }; + if(Array.isArray(creeperElement)) { + headName = `${creeperElement.join("_")}_creeper_head`; //auto head element name + bodyName = `${creeperElement.join("_")}_creeper_body`; //auto body element name + explodeInto = creeperElement.join(","); //auto body element name + } else { + headName = `${creeperElement}_creeper_head`; //auto head element name + bodyName = `${creeperElement}_creeper_body`; //auto body element name + explodeInto = creeperElement; //auto body element name + }; + if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall + if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down + var headPixel = pixelMap[pixel.x][pixel.y-2]; + if (headPixel.element == headName) { + if (isEmpty(pixel.x, pixel.y-1)) { + movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1); + } + else { + swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]); + } + } + } + } + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + } + return + } + // Find the head + if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == headName) { + var head = pixelMap[pixel.x][pixel.y-1]; + if (head.dead) { // If head is dead, kill body + pixel.dead = head.dead; + } + } + else { var head = null } + if (isEmpty(pixel.x, pixel.y-1)) { + // create blood if decapitated 10% chance + if (Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y-1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + else if (head == null) { return } + else if (Math.random() < 0.1) { // Move 10% chance + var movesToTry = [ + [1*pixel.dir,0], + [1*pixel.dir,-1], + ]; + // While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break. + while (movesToTry.length > 0) { + var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0]; + if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) { + if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) { + movePixel(head, head.x+move[0], head.y+move[1]); + break; + }; + }; + }; + // 15% chance to change direction while not chasing a human + if(!head.following) { + if (Math.random() < 0.15) { + pixel.dir *= -1; + //console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*"); + }; + }/* else { + //console.log("*chases cutely*"); + };*/ + }; + if(pixel.charge) { + pixel.charged = true; + }; + if(head) { + if(typeof(head.charge) !== "undefined") { + if(head.charge) { + pixel.charged = true; + }; + }; + if(typeof(head.charged) !== "undefined") { + if(head.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 7; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 5; + }; + if(pixel.burning) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + if(!pixel.burnStart) { //I don't like errors. + pixel.burnStart = pixel.ticks; + }; + if(pixelTicks - pixel.burnStart > 30) { + //console.log("Kaboom?"); + explodeAt(pixel.x,pixel.y,explosionRadius,creeperElement); + //console.log("Yes, Rico, kaboom."); + }; + }; + //Head hissing color handler: keeps track of head's hissing for coloring purposes + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.dead || !head || head.dead) { //can't hiss without a head according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(head.hissing) { + //console.log("Ssssssss"); + if(!head.hissStart) { + //console.log("t-30 ticks or whatever it was"); + head.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - head.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + //console.log("the j"); + lightness = slBound(lightness + 1.176); + //console.log(lightness); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + }; + }; + }; + autoCreeperHeadTick = function(pixel) { + var creeperElement = elements[pixel.element].creeperType; + var headName,bodyName,explodeInto; + if(typeof(creeperElement === "string")) { //comma separated string check + if(creeperElement.includes(",")) { //if it is + creeperElement = creeperElement.split(","); //to array + creeperElement = creeperElement.filter(function(e) { //strip nonexistent elements + return typeof(elements[e]) === "object"; + }); + }; + }; + if(Array.isArray(creeperElement)) { + headName = `${creeperElement.join("_")}_creeper_head`; //auto head element name + bodyName = `${creeperElement.join("_")}_creeper_body`; //auto body element name + explodeInto = creeperElement.join(","); //auto body element name + } else { + headName = `${creeperElement}_creeper_head`; //auto head element name + bodyName = `${creeperElement}_creeper_body`; //auto body element name + explodeInto = creeperElement; //auto body element name + }; + doHeat(pixel); + doBurning(pixel); + doElectricity(pixel); + if (pixel.dead) { + // Turn into rotten_meat if pixelTicks-dead > 500 + if (pixelTicks-pixel.dead > 200) { + Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat"); + return + } + } + // Find the body + if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == bodyName) { + var body = pixelMap[pixel.x][pixel.y+1]; + if (body.dead) { // If body is dead, kill head + pixel.dead = body.dead; + } + } + else { var body = null } + if(body) { + if(body.dir !== pixel.dir) { //hacky workaround: lock head dir to body dir + pixel.dir = body.dir; + }; + }; + if (isEmpty(pixel.x, pixel.y+1)) { + tryMove(pixel, pixel.x, pixel.y+1); + // create blood if severed 10% chance + if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) { + createPixel("blood", pixel.x, pixel.y+1); + // set dead to true 15% chance + if (Math.random() < 0.15) { + pixel.dead = pixelTicks; + } + } + } + //start of most new code + var pX = pixel.x; + var pY = pixel.y; + if(pixel.charge) { + pixel.charged = true; + }; + if(body) { + if(typeof(body.charge) !== "undefined") { + if(body.charge) { + pixel.charged = true; + }; + }; + if(typeof(body.charged) !== "undefined") { + if(body.charged) { + pixel.charged = true; + }; + }; + }; + if(typeof(pixel.charged) === "undefined") { + pixel.charged = false; + }; + if(pixel.charged) { + var explosionRadius = 10; + if(!pixel.didChargeBlueTinted) { //do once, on initial charge + //console.log("something something halsey lyric"); + var color = pixel.color; + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + 51); + green = rgbColorBound(green + 51); + blue = rgbColorBound(blue + 102); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + hue = hue % 360; //piecewise hue shift + if(hue <= 235 && hue >= 135) { + hue = 185; + } else if(hue < 135) { + hue += 50; + } else if(hue > 235 && hue < 360) { + hue -= 50; + }; + saturation = slBound (saturation + 10); + lightness = slBound(lightness + 20); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + pixel.didChargeBlueTinted = true; + }; + } else { + var explosionRadius = 7; + }; + //Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction) + var directionAdverb = "left"; + if(pixel.dir > 0) { + directionAdverb = "right"; + }; + //console.log(`Looking ${directionAdverb}`) + if(pixel.dir === -1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = (-1); j > (-16 - 1); j--) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { //probably misapplying the tolerance from the MC Wiki line: "Creepers will chase after any player, as long as it is within a 16 block (±5%) radius" + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; //can't see through humans + }; + }; + }; + }; + } else if(pixel.dir === 1) { + for(i = -4; i < 4+1; i++) { + var oY = i; + //console.log(`Starting row look at row ${pY+oY}`) + for(j = 1; j < 16 + 1; j++) { + var oX = j; + var nX = pX+oX; + var nY = pY+oY; + if(outOfBounds(nX,nY)) { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`) + break; + }; + if(isEmpty(nX,nY)) { + ////console.log(`Skipping pixel (${nX},${nY}) (empty)`) + continue; + }; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(enemyHumanoidArray.includes(newElement)) { + //console.log(`Human part found at (${nX},${nY})`) + if(!newPixel.dead) { + pixel.following = true; + //console.log(`Human detected at (${nX},${nY})`) + //Start "hissing" if a human is close enough + if(coordPyth(pX,pY,nX,nY) <= 3.15) { + pixel.hissing = true; + if(!pixel.hissStart) { + pixel.hissStart = pixelTicks; + }; + }; + break; + }; + } else { + //console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`) + break; + }; + }; + }; + }; + }; + //Pre-explosion handler: keeps track of time before the kaboom + for(i = 0; i < 1; i++) { //dummy for loop + if(pixel.hissing) { + //console.log("Ssssssss"); + if(pixel.dead || !body || body.dead) { //can't explode without a body according to the classic creeper anatomy + //console.log("ss-- oof"); + pixel.hissing = false; + break; + }; + if(!pixel.hissStart) { + //console.log("t-30 ticks or whatever it was"); + pixel.hissStart = pixelTicks; + }; + //Color code { + var ticksHissing = pixelTicks - pixel.hissStart; + var color = pixel.color; //do on each hissing tick + if(color.startsWith("rgb")) { + //console.log("rgb detected"); + color = color.split(","); //split color for addition + var red = parseFloat(color[0].substring(4)); + var green = parseFloat(color[1]); + var blue = parseFloat(color[2].slice(0,-1)); + red = rgbColorBound(red + ticksHissing); + green = rgbColorBound(green + ticksHissing); + blue = rgbColorBound(blue + ticksHissing); + color = `rgb(${red},${green},${blue})`; + pixel.color = color; + //console.log("color set"); + } else if(color.startsWith("hsl")) { + //console.log("hsl detected"); + color = color.split(","); //split color for addition + var hue = parseFloat(color[0].substring(4)); + var saturation = parseFloat(color[1].slice(0,-1)); + var lightness = parseFloat(color[2].slice(0,-2)); + lightness = slBound(lightness + 1.176); + color = `hsl(${hue},${saturation}%,${lightness}%)`; + pixel.color = color; + //console.log("color set"); + }; + //} + if(pixelTicks - pixel.hissStart > 30) { + //console.log("Kaboom?"); + //console.log(`Exploding with element ${creeperElement} and radius ${explosionRadius} (charged: ${pixel.charged})`); + explodeAt(body.x,body.y,explosionRadius,explodeInto); + //console.log("Yes, Rico, kaboom."); + }; + }; + }; + if(Math.random() < 0.01) { //1% chance each tick to lose interest + pixel.following = false; + //console.log("Meh."); + }; + }; + //Several + function commonMovableCriteria(name) { + if(typeof(elements[name]) !== "object") { + throw new Error(`Nonexistent element ${name}`); + }; + var info = elements[name]; + //console.log(`${name} (${JSON.stringify(elements[name])})`); + if(typeof(info.state) === "undefined") { + var state = null; + } else { + var state = info.state; + }; + if(typeof(info.category) === "undefined") { + var category = "other"; + } else { + var category = info.category; + }; + if(excludedFairyElements.includes(name) || excludedCloudElements.includes(name)) { + return false + }; + if(elements[name].behavior && elements[name].behavior.toString() == elements.wall.behavior.toString() && !elements[name].tick) { + return false; + }; + if(["liquid","gas"].includes(state)) { + return true; + }; + if(info.movable) { + return true; + }; + if(backupCategoryWhitelist.includes(category)) { + return true; + }; + if(backupElementWhitelist.includes(name)) { + return true; + }; + if(category.includes("mudstone")) { + return true; + }; + return false; + }; + //runAfterLoad calls + //Bombs + runAfterLoad(function() { + amalgamatedBombFire += ",poisonwater".repeat(8); + amalgamatedBombFire += ",mystic_fire".repeat(4); + amalgamatedBombFire += ",firesea".repeat(6); + amalgamatedBombFire += ",lektre".repeat(6); + amalgamatedBombFire += ",flamer".repeat(3); + amalgamatedBombFire += ",flamebomb".repeat(3); + //amalgamatedBombFire += ",toxin".repeat(3); + amalgamatedBombFire += ",burning_unrealistically_flammable_gas".repeat(4); + amalgamatedBombFire += ",warp".repeat(6); + amalgamatedBombFire += ",bomb_3".repeat(3); + amalgamatedBombFire += ",op_hottester_bomb".repeat(3); + eLists.BOMB.push("unrealistically_flammable_bomb"); + eLists.BOMB.push("warp_bomb"); + amalgamatedBombFire += ",electric_gas".repeat(3); + amalgamatedBombFire += ",corrosive_gas".repeat(3); + amalgamatedBombFire += ",iocalfaeus_gas".repeat(3); + amalgamatedBombFire += ",ignited_gas".repeat(3); + amalgamatedBombFire += ",finine".repeat(3); + amalgamatedBombFire += ",acidic_vapour".repeat(3); + amalgamatedBombFire += ",nitrous_gas".repeat(3); + amalgamatedBombFire += ",void_gas".repeat(3); + amalgamatedBombFire += ",black_damp".repeat(3); + amalgamatedBombFire += ",blazing_pyrotheum".repeat(5); + amalgamatedBombFire += ",tectonic_petrotheum".repeat(7); + amalgamatedBombFire += ",resonant_ender".repeat(5); + amalgamatedBombFire += ",foof".repeat(8); + amalgamatedBombFire += ",liquid_irradium".repeat(7); + amalgamatedBombFire += ",bio_ooze".repeat(8); + }); + //Fairies + runAfterLoad(function() { + if(typeof(eLists.FAIRY) === "undefined") { eLists.FAIRY = [] }; + eLists.FAIRY.push("acid_fairy"); + eLists.FAIRY.push("oil_fairy"); + eLists.FAIRY.push("honey_fairy"); + fairyChoices = eLists.FAIRY; + for(i = 0; i < eLists.FAIRY.length; i++) { + var fairyName = eLists.FAIRY[i]; + if(!fairyChoices.includes(fairyName)) { + fairyChoices.push(fairyName); + }; + }; + }); + //Non-generated elements + //Bombs + elements.upward_bomb = { + color: "#625c71", + behavior: [ + "M2|M1 AND EX:10|M2", + "XX|XX|XX", + "XX|EX:10|XX", + ], + category: "weapons", + state: "solid", + density: 1300, + excludeRandom: true, + cooldown: defaultCooldown, + }; + elements.firebomb = { + color: "#ee7e3e", + tick: function(pixel) { + if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds) + var newPixel = pixelMap[pixel.x][pixel.y-1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,10,"fire,fire,fire,fire,fire,greek_fire","fire",null,firebombFire); + changePixel(pixel,"disappear"); + }; + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case) + var newPixel = pixelMap[pixel.x][pixel.y+1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,10,"fire,fire,fire,fire,fire,greek_fire","fire",null,firebombFire); + changePixel(pixel,"disappear"); + }; + }; + if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case) + explodeAtPlus(pixel.x,pixel.y,10,"fire,fire,fire,fire,fire,greek_fire","fire",null,firebombFire); + changePixel(pixel,"disappear"); + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER + Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1); + }; + }, + category: "weapons", + state: "solid", + density: 1500, + excludeRandom: true, + desc: "An advanced incendiary weapon.
To enable automatic bomb generation, set the generateBombs query parameter.", + }; + function fuckingDeletePixel(pixel) { + if( + typeof(pixel) === "object" && + pixel !== null && + typeof(pixel?.x) === "number" && + typeof(pixel?.y) === "number" + ) { + var pX = pixel.x; + var pY = pixel.y; + deletePixel(pX,pY); + if(typeof(pixel) === "object" && pixel !== null) { + var index = currentPixels.indexOf(pixel); + if(index > 0) { + currentPixels.splice(index) + }; + pixelMap[pX][pY] = undefined + pixel = undefined + return + } + return + }; + return + }; + elements.force_bomb = { + color: ["#7a6749", "#828680", "#89a6b6", "#91c5ed"], + tick: function(pixel) { + var radius = 8; + var fire = ["light",null,null,null,null,null,null,null]; + if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds) + var newPixel = pixelMap[pixel.x][pixel.y-1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,radius+7,fire,null,null,forcebombVelocity); + fuckingDeletePixel(pixel); return + }; + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case) + var newPixel = pixelMap[pixel.x][pixel.y+1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,radius+7,fire,null,null,forcebombVelocity); + fuckingDeletePixel(pixel); return + }; + }; + if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case) + explodeAtPlus(pixel.x,pixel.y,radius+7,fire,null,null,forcebombVelocity); + fuckingDeletePixel(pixel); return + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER + Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1); + }; + return + }, + category: "weapons", + state: "solid", + density: 2000, + excludeRandom: true, + desc: "A bomb that sends pixels flying.
To enable automatic bomb generation, set the generateBombs query parameter.", + }; + elements.cluster_nuke = { + color: "#e3f636", + behavior: [ + "CR:radiation%5|EX:90>plasma,plasma,plasma,nuke,nuke,nuke,radiation,radiation,radiation,rad_steam,rad_steam,radiation,rad_steam AND CR:radiation%5|CR:radiation%5", + "CR:radiation%5|XX|CR:radiation%5", + "M2 AND CR:radiation%5|M1 AND EX:90>plasma,plasma,plasma,nuke,nuke,nuke,radiation,radiation,radiation,rad_steam,rad_steam,radiation,rad_steam AND CR:radiation%5|M2 AND CR:radiation%5", + ], + category: "weapons", + state: "solid", + density: 1500, + excludeRandom: true, + desc: "It's a nuke that drops more nukes.
To enable automatic bomb generation, set the generateBombs query parameter.", + }; + elements.upward_bomb = { + color: "#525c61", + behavior: [ + "M2|M1 AND EX:10|M2", + "XX|XX|XX", + "XX|EX:10|XX", + ], + category: "weapons", + state: "solid", + density: 1300, + excludeRandom: true, + }; + elements.electric_bomblet = { + color: _cc.w.h, + behavior: [ + "SH%50|EX:8>electric AND SH%50|SH%50", + "SH%50|EX:9>electric%0.5|SH%50", + "M2 AND SH%50|M1 AND SH%50 AND EX:8>electric AND SW:electric|M2 AND SH%50", + ], + category: "weapons", + state: "solid", + density: 1200, + hidden: true, + excludeRandom: true, + hardness: 0.3, + }; + //Wall bomb + elements.electric_cluster_bomb = { + color: _cc.w.h, + behavior: [ + "SH%50|EX:8>electric_bomblet AND SH%50|SH%50", + "SH%50|XX|SH%50", + "M2 AND SH%50|M1 AND SH%50 AND EX:8>electric_bomblet AND SW:electric|M2 AND SH%50", + ], + category: "weapons", + state: "solid", + density: 1800, + hidden: true, + excludeRandom: true, + hardness: 0.3, + }; + elements.radioactive_popper = { + color: "#d6ce72", + behavior: [ + "XX|EX:7>radiation|XX", + "XX|XX|XX", + "M2|M1 AND EX:7>radiation|M2", + ], + category: "weapons", + state: "solid", + density: 1200, + hidden: true, + excludeRandom: true, + hardness: 0.3, + cooldown: 3, + }; + elements.acid_bomb = { + color: "#7d8a63", + behavior: [ + "XX|EX:15>acid_gas|XX", + "XX|XX|XX", + "M2|M1 AND EX:15>acid_gas|M2", + ], + category: "weapons", + state: "solid", + density: 1400, + excludeRandom: true, + cooldown: defaultCooldown, + }; + elements.amalgamated_bomb = { + color: ["#FF0000","#FF0000","#FFFF00","#FFFF00","#00FF00","#00FF00","#0000FF","#0000FF"], + tick: function(pixel) { + doDefaults(pixel); + if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds) + var newPixel = pixelMap[pixel.x][pixel.y-1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,70,amalgamatedBombFire,amalgamatedBombFire); + }; + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case) + var newPixel = pixelMap[pixel.x][pixel.y+1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,70,amalgamatedBombFire,amalgamatedBombFire); + }; + }; + if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case) + explodeAtPlus(pixel.x,pixel.y,70,amalgamatedBombFire,amalgamatedBombFire); + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER + Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1); + }; + }, + category: "weapons", + state: "solid", + temp: 7065, + density: 158000, + excludeRandom: true, + }; + elements.op_hottester_bomb = { + color: "#cc436e", + onSelect: function() { + showPropertySetter(); + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","ophbRadius"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.ophbRadius; + }; + if(p0h) { + p0h.innerText = "Radius"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter(); + }, + tick: function(pixel) { + doDefaults(pixel); //actually we're just leaving the duplicate code in + if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds) + pixel.radius ??= (ambaPlaceProperties?.ophbRadius ?? 15); + var newPixel = pixelMap[pixel.x][pixel.y-1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,pixel.radius,"plasma","plasma",hotterBomb,hotterBomb,false); + }; + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case) + var newPixel = pixelMap[pixel.x][pixel.y+1]; + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,pixel.radius,"plasma","plasma",hotterBomb,hotterBomb,false); + }; + }; + if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case) + explodeAtPlus(pixel.x,pixel.y,pixel.radius,"plasma","plasma",hotterBomb,hotterBomb,false); + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER + Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1); + }; + }, + category: "weapons", + state: "solid", + temp: 7065, + density: 1900, + excludeRandom: true, + }; + elements.op_electromagneticester_emp = { + color: "#818253", + tick: function(pixel) { //replace EMP + if (pixel.start===pixelTicks) {return} + if (!tryMove(pixel,pixel.x,pixel.y+1)) { + if (outOfBounds(pixel.x,pixel.y+1) || (pixelMap[pixel.x][pixel.y+1].element!=="emp_bomb" && elements[pixelMap[pixel.x][pixel.y+1].element].state!=="gas")) { + for (i = 0; i < currentPixels.length; i++) { + var newPixel = currentPixels[i]; + if (newPixel.charge) { + delete newPixel.charge; + newPixel.chargeCD = 50; + } + } + explodeAtPlus(pixel.x,pixel.y+1,20,"electric","smoke",empCharge,null,false); + } + } + doDefaults(pixel); + }, + maxSize: 1, + category: "weapons", + state: "solid", + density: 1500, + excludeRandom: true, + alias: "overpowered electromagnetic pulse bomb", + cooldown: defaultCooldown + }; + elements.star_bomb = { + color: "#fffbb5", + onSelect: function() { + showPropertySetter(); + showSetterColumn("numeric",0); + var p0 = document.getElementById("propertynumeric0input"); + var p0h = document.getElementById("propertynumeric0heading"); + if(p0) { + p0.setAttribute("set","starBombRadius"); + p0.setAttribute("min","1"); + p0.value = ambaPlaceProperties.starBombRadius; + }; + if(p0h) { + p0h.innerText = "Radius"; + }; + }, + onUnselect: function() { + hideAllSetterColumns(); + hidePropertySetter(); + }, + pixelCollisionExplosion: function(pixel,offset) { //moved here to to duplicate the code + if(!pixel) { return }; + var newPixel = pixelMap[pixel.x][pixel.y+offset]; + if(newPixel) + newPixel.temp += 10000000; //[0][1] HT:10000000 + //deliberately don't pixelTempCheck or it will tend to burrow down because that much heat will boil almost anything + var newElement = newPixel.element; + var newInfo = elements[newElement]; + if(newInfo.state !== "gas" && newElement !== pixel.element) { + explodeAtPlus(pixel.x,pixel.y,pixel.radius,elements.star_bomb.explosionFire,elements.star_bomb.explosionSmoke,starbombHeat,starbombHeat,false); + }; + if(pixel) { deletePixel(pixel.x,pixel.y) } + }, + explosionFire: "stellar_plasma,stellar_plasma,stellar_plasma,liquid_stellar_plasma,liquid_stellar_plasma,plasma,plasma", + explosionSmoke: "light,light,radiation", + tick: function(pixel) { + pixel.radius ??= (ambaPlaceProperties?.starBombRadius ?? 50); + doDefaults(pixel); + if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds) + elements.star_bomb.pixelCollisionExplosion(pixel,-1); + return + }; + if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case) + elements.star_bomb.pixelCollisionExplosion(pixel,1); + return + }; + if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case) + explodeAtPlus(pixel.x,pixel.y,pixel.radius,elements.star_bomb.explosionFire,elements.star_bomb.explosionSmoke,starbombHeat,starbombHeat,false); + if(pixel) { deletePixel(pixel.x,pixel.y) } + return + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER + Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1); + }; + }, + category: "weapons", + state: "solid", + density: 3.663e33, + excludeRandom: true, + cooldown: defaultCooldown + }; + //Fairies + elements.acid_fairy = { + name: "acid fairy", + color: ["#e2f777","#d1ff94","#d8f7c1"], + behavior: [ + "XX|M1|M1", + "XX|FX%5|XX", + "XX|CR:acid%0.5 AND CR:fairy_dust%0.005 AND M1|M1", + ], + state: "solid", + category: "fey", + desc: "Like the other fairies, but with acid.
To enable automatic fairy generation, set the generateFairies query parameter.", + } + elements.oil_fairy = { + name: "oil fairy", + color: ["#636360","#a6956f","#a3816d","#cfc191"], + behavior: [ + "XX|M1|M1", + "XX|FX%5|XX", + "XX|CR:oil%0.5 AND CR:fairy_dust%0.005 AND M1|M1", + ], + state: "solid", + category: "fey", + desc: "Like the other fairies, but with oil.
To enable automatic fairy generation, set the generateFairies query parameter.", + } + elements.honey_fairy = { + name: "honey fairy", + color: ["#ffeaa6","#ffe987","#f2e7c2"], + behavior: [ + "XX|M1|M1", + "XX|FX%5|XX", + "XX|CR:honey%0.5 AND CR:fairy_dust%0.005 AND M1|M1", + ], + state: "solid", + category: "fey", + desc: "Like the other fairies, but with sweet honey.
To enable automatic fairy generation, set the generateFairies query parameter.", + } + //Sequential generations + //Bombs + for (var i = 2; i <= bombAmount + 1; i++) { + elements[`bomb_${i}`] = { + name: `bomb ${i}`, + color: "#624c41", + behavior: [ + `XX|EX:${5*(i+1)}>fire|XX`, + "XX|XX|XX", + `M2|M1 AND EX:${5*(i+1)}>fire|M2`, + ], + state: "solid", + density: 1300 * 8**((i-1)/2), + excludeRandom:true, + category: "weapons", + desc: `${5*(i+1)/10} times the radius of the regular bomb`, + cooldown: defaultCooldown, + }; + eLists.BOMB.push(`bomb_${i}`); + }; + for (var i = 2; i <= bombAmount + 1; i++) { + elements[`upward_bomb_${i}`] = { + color: "#625c71", + behavior: [ + `M2|M1 AND EX:${5*(i+1)}>fire|M2`, + "XX|XX|XX", + `XX|EX:${5*(i+1)}>fire|XX`, + ], + state: "solid", + density: 1300 * 8**((i-1)/2), + excludeRandom:true, + category: "weapons", + desc: `${5*(i+1)/10} times the radius of the regular anti-bomb`, + cooldown: defaultCooldown, + }; + eLists.BOMB.push(`upward_bomb_${i}`); + }; + //Fairies + //For statement by charPointer + for (var i = 2; i <= fairyAmount + 1; i++) { + elements[`${i}-fairy`] = { + name: `${i}-fairy`, + color: ["#33007a","#8e009f","#09009f"], + behavior: [ + "XX|M1|M1", + "XX|FX%5|XX", + `XX|CR:${i-1}-fairy%1 AND CR:fairy_dust%0.005 AND M1|M1`, + ], + reactions: { + "glitter": { "elem1": `${i+1}-fairy`, "elem2": null } + }, + state: "solid", + excludeRandom:true, + category: "fey", + hidden: true, + } + if (i == fairyAmount) { elements[`${i}-fairy`]["reactions"] = {}; } + if (i == 2) elements[`${i}-fairy`]["behavior"][2] = `XX|CR:fairy%1 AND CR:fairy_dust%0.005 AND M1|M1`; + eLists.FAIRY.push(`${i}-fairy`); + } + //post-gen + elements.rainbow.reactions.fairy = { "elem1": "2-fairy", "elem2": null } + //remove last fairy's reaction + delete elements[`${fairyAmount + 1}-fairy`].reactions + //Main generator functions + //Bombs + generateBomb = function(bombElements,isAfterScriptLoading=false,bombNumber=1) {//it can be a single element, though + bombNumber = Math.max(0,bombNumber); + //To specify an array bomb, have the array be inside another array. + /*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false. + Otherwise, you'll get TypeErrors for some reason when trying to place your bomb. If this is being run after the game has loaded (e.g. in the console), + then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but + the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**.*/ + if(typeof(bombElements) === "string") { //it should be an array, so string check + //console.log("String detected"); + if(bombElements.includes(",")) { //comma-separated string? + //console.log("Splitting string to array"); + bombElements = bombElements.split(","); //,SS to array + } else { + //console.log("Wrapping string in array"); + bombElements = [bombElements]; //single string to array + }; + }; + var returns = []; + for(aaf = 0; aaf < bombElements.length; aaf++) { + var elementOfBomb = bombElements[aaf]; + //console.log("1",elementOfBomb); + var allElementsHaveColors = ((elementOfBomb instanceof Array ? elementOfBomb : [elementOfBomb]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1) + if(!allElementsHaveColors) { continue }; + var startColor; + var randomExcl = 0; + //console.log(elementOfBomb); + var bombName; + //console.log("2-1"); + if(typeof(elementOfBomb === "string")) { //comma separated string check + if(elementOfBomb.includes(",")) { //if it is + elementOfBomb = elementOfBomb.split(","); //to array + elementOfBomb = elementOfBomb.filter(function(e) { //strip nonexistent elements + //console.log("3 a"); + return typeof(elements[e]) === "object"; + }); + }; + }; + if(Array.isArray(elementOfBomb)) { + bombName = `${elementOfBomb.join("_")}_bomb`; //auto placer element name + //array case color concatenator (bombs are always excludeRandom) + startColor = []; + //console.log(elementOfBomb); + for(ll = 0; ll < elementOfBomb.length; ll++) { + if(typeof(elements[elementOfBomb[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfBomb[ll]].excludeRandom) { //it it's true + randomExcl = 1; //the whole array bomb is excluded (from spawnRandomBomb) + //console.log("array nyet" + elementOfBomb); + }; + }; + startColor = startColor.concat(elements[elementOfBomb[ll]].color); + }; + } else { //they should all be strings, so here + bombName = `${elementOfBomb}_bomb`; //auto placer element name + startColor = elements[elementOfBomb].color; + if(typeof(elements[elementOfBomb].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfBomb].excludeRandom) { //it it's true + //console.log("nyet " + elementOfBomb); + randomExcl = 1; //the bomb is excluded + } else { + //console.log("allow " + elementOfBomb); + randomExcl = 0; + }; + }; + }; + //console.log("e",bombName); + //Color gen + if(Array.isArray(startColor)) { //Average arrays, make colors rgb() + startColor = averageRgbPrefixedColorArray(startColor); + } else { + startColor = rgbHexCatcher(startColor); + }; + startColor = addColors(changeLightness(changeSaturation(startColor,0.6,"multiply","hsl_json"),0.5,"multiply","rgb"),"rgb(24,0,0)","rgb"); + var newColorObject = rgbStringToObject(startColor); + //End color gen + //The bomb + //console.log(elementOfBomb); + var firstInfo, firstTemp; + if(Array.isArray(elementOfBomb)) { + firstInfo = elements[elementOfBomb[0]]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + } else { + firstInfo = elements[elementOfBomb]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + }; + elementOfBomb = tryJoin(elementOfBomb,","); + descElement = tryJoin(elementOfBomb,", "); + //console.log(elementOfBomb); + if(bombNumber !== 1) { + bombName += `_${bombNumber}`; + }; + if(!elementExists(bombName)) { + elements[bombName] = { + color: startColor, + insulate: true, + flippableX: true, + colorObject: newColorObject, + behavior: [ + ["XX",`EX:${5*(bombNumber+1)}>${elementOfBomb}`,"XX"], + ["XX","XX","XX"], + ["M2",`M1 AND EX:${5*(bombNumber+1)}>${elementOfBomb}`,"M2"] + ], + category: "auto bombs", + desc: `Explodes into ${descElement}
Radius: ${5*(bombNumber+1)}`, + temp: firstTemp, + excludeRandom: true, + autoType: "bomb", + }; + if(typeof(eLists) === "undefined") { + eLists = {}; + }; + if(typeof(eLists.BOMB) === "undefined") { + eLists.BOMB = []; + }; + eLists.BOMB.push(bombName); + if(!randomExcl) { + if(typeof(bombChoices) === "undefined") { + bombChoices = [] + }; + if(!bombChoices.includes(bombName)) { + bombChoices.push(bombName); + }; + } + if(isAfterScriptLoading) { + elements[bombName].flippableX = true; + elementCount++; //increment for new bomb element + createElementButton(bombName); + elements[bombName].id = nextid++; + document.getElementById("extraInfo").innerHTML = canvasfooterTemplate() + }; + }; + returns.push(bombName); + }; + return returns; + }; + //Clouds + //Clouds + generateCloud = function(cloudElements,cloudType=0,isAfterScriptLoading=false) {//it can be a single element, though + //To specify an array cloud, have the array be inside another array. + /*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false. + Otherwise, you'll get TypeErrors for some reason when trying to place your cloud. If this is being run after the game has loaded (e.g. in the console), + then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but + the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**.*/ + if(cloudType !== "*") { + if(typeof(cloudElements) === "string") { //it should be an array, so string check + //console.log("String detected"); + if(cloudElements.includes(",")) { //comma-separated string? + //console.log("Splitting string to array"); + cloudElements = cloudElements.split(","); //,SS to array + } else { + //console.log("Wrapping string in array"); + cloudElements = [cloudElements]; //single string to array + }; + }; + var returns = []; + for(aaf = 0; aaf < cloudElements.length; aaf++) { + var elementOfCloud = cloudElements[aaf]; + var allElementsHaveColors = ((elementOfCloud instanceof Array ? elementOfCloud : [elementOfCloud]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1) + if(!allElementsHaveColors) { continue }; + var startColor; + var randomExcl = 0; + //console.log("randomExcl set") + //console.log(elementOfCloud); + var cloudName, cloudPrefix, cloudBehavior; + switch(cloudType) { + case 0: + cloudPrefix = ""; + cloudBehavior = behaviorGenerators.cloud(elementOfCloud); + break; + case 1: + cloudPrefix = "heavy_"; + cloudBehavior = behaviorGenerators.heavy_cloud(elementOfCloud); + break; + case 2: + cloudPrefix = "heavier_"; + cloudBehavior = behaviorGenerators.heavier_cloud(elementOfCloud); + break; + case 3: + cloudPrefix = "heaviest_"; + cloudBehavior = behaviorGenerators.heaviest_cloud(elementOfCloud); + break; + case 4: + cloudPrefix = "heaviester_"; + cloudBehavior = behaviorGenerators.heaviester_cloud(elementOfCloud); + break; + case 5: + cloudPrefix = "heaviestest_"; + cloudBehavior = behaviorGenerators.heaviestest_cloud(elementOfCloud); + break; + default: + throw new RangeError("Cloud type must be between 0 and 5"); + }; + if(typeof(elementOfCloud === "string")) { //comma separated string check + if(elementOfCloud.includes(",")) { //if it is + elementOfCloud = elementOfCloud.split(","); //to array + elementOfCloud = elementOfCloud.filter(function(e) { //strip nonexistent elements + return typeof(elements[e]) === "object"; + }); + }; + }; + if(Array.isArray(elementOfCloud)) { + cloudName = `${elementOfCloud.join("_")}_cloud`; //auto placer element name + //array case color concatenator and excludeRandom handler + startColor = []; + //console.log(elementOfCloud); + for(ll = 0; ll < elementOfCloud.length; ll++) { + if(typeof(elements[elementOfCloud[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfCloud[ll]].excludeRandom) { //it it's true + randomExcl = 1; //the whole array cloud is excluded + //console.log("array nyet" + elementOfCloud); + }; + }; + //console.log(elementOfCloud[ll]); + startColor = startColor.concat(elements[elementOfCloud[ll]].color); + }; + } else { //they should all be strings, so here + cloudName = `${elementOfCloud}_cloud`; //auto placer element name + startColor = elements[elementOfCloud].color; + if(typeof(elements[elementOfCloud].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfCloud].excludeRandom) { //it it's true + //console.log("nyet " + elementOfCloud); + randomExcl = 1; //the cloud is excluded + } else { + //console.log("allow " + elementOfCloud); + randomExcl = 0; + }; + }; + }; + //Color gen + if(Array.isArray(startColor)) { //Average arrays, make colors rgb() + startColor = averageRgbPrefixedColorArray(startColor); + } else { + startColor = rgbHexCatcher(startColor); + }; + startColor = changeLightness( + changeSaturation( + startColor, + (0.4 + (cloudType/10)), + "multiply", + "hsl_json" + ), + (0.6 - (cloudType/10)), + "multiply", + "rgb" + ); + var newColorObject = rgbStringToObject(startColor); + //End color gen + //The cloud + //console.log(elementOfCloud); + var firstInfo, firstTemp; + if(Array.isArray(elementOfCloud)) { + firstInfo = elements[elementOfCloud[0]]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + } else { + firstInfo = elements[elementOfCloud]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + }; + elementOfCloud = tryJoin(elementOfCloud,","); + //console.log(elementOfCloud); + cloudName = cloudPrefix + cloudName; + if(elementExists(cloudName)) { + if(elements[cloudName].autoType) { continue } else { cloudName = "auto_" + cloudName } + }; + elements[cloudName] = { + color: startColor, + cloudType: elementOfCloud, + insulate: true, + colorObject: newColorObject, + behavior: cloudBehavior, + category: "clouds", + temp: firstTemp, + state: "gas", + density: 0.6 * (2**cloudType), + ignoreAir: true, + conduct: 0.01 * (2**cloudType), + autoType: "cloud", + isGas: true + }; + if(cloudType === 4) { //column tick for heaviester clouds + elements[cloudName].tick = function(pixel) { + singleColumn(pixel,elements[pixel.element].cloudType,0.01,0.3); + }; + }; + if(cloudType === 5) { //three-column tick for heaviestest clouds + elements[cloudName].tick = function(pixel) { + multiColumn(pixel,elements[pixel.element].cloudType,0.02,0.4,1); + }; + }; + eLists.CLOUD.push(cloudName); + if(!randomExcl) { + if(typeof(cloudChoices) === "undefined") { + cloudChoices = [] + }; + if(!cloudChoices.includes(cloudName)) { + cloudChoices.push(cloudName); + }; + } + if(cloudIncludeRandom) { + randomExcl ? elements[cloudName].excludeRandom = true : elements[cloudName].excludeRandom = false; + } else { + elements[cloudName].excludeRandom = true; + }; + if(isAfterScriptLoading) { + elementCount++; //increment for new cloud element + createElementButton(cloudName); + elements[cloudName].id = nextid++; + document.getElementById("extraInfo").innerHTML = canvasfooterTemplate() + }; + returns.push(cloudName); + }; + return returns; + } else if(cloudType === "*") { + var trueReturns = []; + for(cloudTypeForIndex = 0; cloudTypeForIndex <= 5; cloudTypeForIndex++) { + trueReturns = trueReturns.concat(generateCloud(cloudElements,cloudTypeForIndex,isAfterScriptLoading)); + }; + return trueReturns; + }; + }; + //Creepers + generateCreeper = function(creeperElements,isAfterScriptLoading=false) {//it can be a single element, though + //To specify an array creeper, have the array be inside another array. + /*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false. + Otherwise, you'll get TypeErrors for some reason when trying to place your creeper. If this is being run after the game has loaded (e.g. in the console), + then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but + the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**. + If isAfterScriptLoading is true, buttons (depending on the hiding setting) will be added to the auto creeper category, the 3 new elements per creeper will be assigned IDs, and the footer will be updated with the increased element counts.*/ + if(typeof(creeperElements) === "string") { //it should be an array, so string check + //console.log("String detected"); + if(creeperElements.includes(",")) { //comma-separated string? + //console.log("Splitting string to array"); + creeperElements = creeperElements.split(","); //,SS to array + } else { + //console.log("Wrapping string in array"); + creeperElements = [creeperElements]; //single string to array + }; + }; + var returns = []; + for(aaf = 0; aaf < creeperElements.length; aaf++) { + var elementOfCreeper = creeperElements[aaf]; + var allElementsHaveColors = ((elementOfCreeper instanceof Array ? elementOfCreeper : [elementOfCreeper]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1) + if(!allElementsHaveColors) { continue }; + var startColor; + var randomExcl = 0; + //console.log("randomExcl set") + //console.log(elementOfCreeper); + var headName,bodyName,placerName,descElement; + if(typeof(elementOfCreeper === "string")) { //comma separated string check + if(elementOfCreeper.includes(",")) { //if it is + elementOfCreeper = elementOfCreeper.split(","); //to array + elementOfCreeper = elementOfCreeper.filter(function(e) { //strip nonexistent elements + return typeof(elements[e]) === "object"; + }); + }; + }; + if(Array.isArray(elementOfCreeper)) { + headName = `${elementOfCreeper.join("_")}_creeper_head`; //auto head element name + bodyName = `${elementOfCreeper.join("_")}_creeper_body`; //auto body element name + placerName = `${elementOfCreeper.join("_")}_creeper`; //auto placer element name + descElement = elementOfCreeper.join(", "); //auto explosion element list + //array case color concatenator and excludeRandom handler + startColor = []; + //console.log(elementOfCreeper); + for(ll = 0; ll < elementOfCreeper.length; ll++) { + if(typeof(elements[elementOfCreeper[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfCreeper[ll]].excludeRandom) { //it it's true + randomExcl = 1; //the whole array creeper is excluded + //console.log("array nyet" + elementOfCreeper); + }; + }; + //console.log(elementOfCreeper[ll]); + startColor = startColor.concat(elements[elementOfCreeper[ll]].color); + }; + } else { //they should all be strings, so here + headName = `${elementOfCreeper}_creeper_head`; //auto head element name + bodyName = `${elementOfCreeper}_creeper_body`; //auto body element name + placerName = `${elementOfCreeper}_creeper`; //auto placer element name + descElement = elementOfCreeper; //auto explosion element + startColor = elements[elementOfCreeper].color; + if(typeof(elements[elementOfCreeper].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfCreeper].excludeRandom) { //it it's true + //console.log("nyet " + elementOfCreeper); + randomExcl = 1; //the creeper is excluded + } else { + //console.log("allow " + elementOfCreeper); + randomExcl = 0; + }; + }; + }; + //Color gen + if(Array.isArray(startColor)) { //Average arrays, make colors rgb() + startColor = averageRgbPrefixedColorArray(startColor); + } else { + startColor = rgbHexCatcher(startColor); + }; + var preColor = rgbStringToHSL(startColor); + var colorsArray = [preColor, preColor, preColor, preColor, preColor, preColor, preColor, preColor, preColor, preColor] + var colorObjectArray = []; + for(q = 0; q < hslOffsets.length; q++) { + colorsArray[q] = addArraysInPairs(colorsArray[q],hslOffsets[q]); + colorsArray[q] = hslToHex(...colorsArray[q]); + colorObjectArray[q] = hexToRGB(colorsArray[q]); //outputs hex + if(isAfterScriptLoading) { // if it's after the hex -> RGB conversion + var coq = colorObjectArray[q]; //pull the object + //console.log(coq.r); + colorsArray[q] = `rgb(${coq.r},${coq.g},${coq.b})`; //and change to the RGB from its values + }; + }; + //End color gen + //console.log(`${headName}; ${bodyName}; ${placerName}; ${descElement}`) + //Placer + elements[placerName] = { + movable: true, + creeperType: elementOfCreeper, + color: colorsArray, + colorObject: colorObjectArray, + category: "auto creepers", + properties: { + dead: false, + dir: 1, + panic: 0, + following: false, + }, + tick: function(pixel) { + autoCreeperPlacerTick(pixel); + }, + related: [bodyName,headName,"creeper"], + desc: `Auto-generated creeper.
Explodes into ${descElement}.`, + autoType: "creeper", + }; + eLists.CREEPER.push(placerName); + //Body + elements[bodyName] = { + movable: true, + creeperType: elementOfCreeper, + color: colorsArray, + colorObject: colorObjectArray, + category: "auto creepers", + hidden: true, + excludeRandom: true, + density: 1500, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -30, + stateLow: "frozen_meat", + burn: 10, + burnTime: 250, + burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","gunpowder"], + breakInto: ["blood","gunpowder"], + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 }, + }, + properties: { + dead: false, + dir: 1, + panic: 0, + charged: false, + didChargeBlueTinted: false, + }, + tick: function(pixel) { + autoCreeperBodyTick(pixel); + }, + autoType: "creeper", + }; + //Head + elements[headName] = { + movable: true, + creeperType: elementOfCreeper, + color: colorsArray, + colorObject: colorObjectArray, + category: "auto creepers", + hidden: true, + excludeRandom: true, + density: 1080, + state: "solid", + conduct: 25, + tempHigh: 250, + stateHigh: "cooked_meat", + tempLow: -30, + stateLow: "frozen_meat", + burn: 10, + burnTime: 250, + burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","gunpowder"], + breakInto: "blood", + reactions: { + "cancer": { "elem1":"cancer", "chance":0.005 }, + "radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 }, + "plague": { "elem1":"plague", "chance":0.05 }, + "oxygen": { "elem2":"carbon_dioxide", "chance":0.5 }, + }, + properties: { + dead: false, + following: false, + hissing: false, + charged: false, + didChargeBlueTinted: false, + }, + tick: function(pixel) { + autoCreeperHeadTick(pixel); + }, + autoType: "creeper", + }; + if(isAfterScriptLoading) { + elementCount += 3; //placer, body, head + createElementButton(placerName) + if(settings["unhide"] === 0) { //hide some elements: body and head would be hidden, so only update hidden count + hiddenCount += 2; + } else if(settings["unhide"] === 1) { //unhide all elements: b/h would not be hidden, so only create their buttons + createElementButton(bodyName); + createElementButton(headName); + } else if(settings["unhide"] === 2) { + settings.unlocked[bodyName] ? createElementButton(bodyName) : hiddenCount++; //ternary: if headName is unlocked, create button, else increase hiddenCount + settings.unlocked[headName] ? createElementButton(headName) : hiddenCount++; //above with headName + }; + elements[placerName].id = nextid++; //set placer's id to nextid and then increment nextid + elements[bodyName].id = nextid++; //repeat with body and head + elements[headName].id = nextid++; + headBodyObject[headName] = bodyName; + document.getElementById("extraInfo").innerHTML = canvasfooterTemplate() + }; + if(creeperIncludeRandom) { + randomExcl ? elements[placerName].excludeRandom = true : elements[placerName].excludeRandom = false; + } else { + elements[placerName].excludeRandom = true; + }; + if(!randomExcl) { + //console.log("spawn enabling " + placerName); + spawnCreepers.push(placerName); + } else { + //console.log("nyetted " + placerName); + }; + returns.push(placerName); + }; + return returns; + }; + //Fairies + generateFairy = function(fairyElements,isAfterScriptLoading=false) {//it can be a single element, though + //To specify an array fairy, have the array be inside another array. + /*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false. + Otherwise, you'll get TypeErrors for some reason when trying to place your fairy. If this is being run after the game has loaded (e.g. in the console), + then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but + the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**.*/ + if(typeof(fairyElements) === "string") { //it should be an array, so string check + //console.log("String detected"); + if(fairyElements.includes(",")) { //comma-separated string? + //console.log("Splitting string to array"); + fairyElements = fairyElements.split(","); //,SS to array + } else { + //console.log("Wrapping string in array"); + fairyElements = [fairyElements]; //single string to array + }; + }; + var returns = []; + for(aaf = 0; aaf < fairyElements.length; aaf++) { + var elementOfFairy = fairyElements[aaf]; + var allElementsHaveColors = ((elementOfFairy instanceof Array ? elementOfFairy : [elementOfFairy]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1) + if(!allElementsHaveColors) { continue }; + var startColor; + var randomExcl = 0; + var isNocheer = 0; + //console.log("randomExcl set") + //console.log(elementOfFairy); + var fairyName; + if(typeof(elementOfFairy === "string")) { //comma separated string check + if(elementOfFairy.includes(",")) { //if it is + elementOfFairy = elementOfFairy.split(","); //to array + elementOfFairy = elementOfFairy.filter(function(e) { //strip nonexistent elements + return typeof(elements[e]) === "object"; + }); + }; + }; + if(Array.isArray(elementOfFairy)) { + fairyName = `${elementOfFairy.join("_")}_fairy`; //auto placer element name + //array case color concatenator and excludeRandom handler + startColor = []; + //console.log(elementOfFairy); + for(ll = 0; ll < elementOfFairy.length; ll++) { + if(typeof(elements[elementOfFairy[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfFairy[ll]].excludeRandom) { //it it's true + randomExcl = 1; //the whole array fairy is excluded + //console.log("array nyet" + elementOfFairy); + }; + }; + //console.log(elementOfFairy[ll]); + startColor = startColor.concat(elements[elementOfFairy[ll]].color); + }; + for(ll = 0; ll < elementOfFairy.length; ll++) { + if(typeof(elements[elementOfFairy[ll]].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError) + if(elements[elementOfFairy[ll]].nocheer) { //it it's true + isNocheer = 1; //the whole array fairy is excluded + //console.log("array nyet" + elementOfFairy); + }; + }; + //console.log(elementOfFairy[ll]); + startColor = startColor.concat(elements[elementOfFairy[ll]].color); + }; + } else { //they should all be strings, so here + fairyName = `${elementOfFairy}_fairy`; //auto placer element name + startColor = elements[elementOfFairy].color; + if(typeof(elements[elementOfFairy].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfFairy].excludeRandom) { //it it's true + //console.log("nyet " + elementOfFairy); + randomExcl = 1; //the fairy is excluded + } else { + //console.log("allow " + elementOfFairy); + randomExcl = 0; + }; + }; + if(typeof(elements[elementOfFairy].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError) + if(elements[elementOfFairy].nocheer) { //it it's true + //console.log("nyet " + elementOfFairy); + isNocheer = 1; //the fairy is excluded + } else { + //console.log("allow " + elementOfFairy); + isNocheer = 0; + }; + }; + }; + //Color gen + if(Array.isArray(startColor)) { //Average arrays, make colors rgb() + startColor = averageRgbPrefixedColorArray(startColor); + } else { + startColor = rgbHexCatcher(startColor); + }; + //console.log(`rgbStringToObject(${startColor}) from more_fairies.js`) + var newColorObjectArray = []; + var newColorArray = []; + for(i = 0; i < elements.fairy.color.length; i++) { + var newFairyColorlet = elements.fairy.color[i]; + var newColor = multiplyColors(startColor,newFairyColorlet); + var newColorJSON = multiplyColors(startColor,newFairyColorlet,"json"); + newColorArray.push(newColor); + newColorObjectArray.push(newColorJSON); + }; + //End color gen + //The fairy + //console.log(elementOfFairy); + var firstInfo, firstTemp; + if(Array.isArray(elementOfFairy)) { + firstInfo = elements[elementOfFairy[0]]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + } else { + firstInfo = elements[elementOfFairy]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + }; + elementOfFairy = tryJoin(elementOfFairy,","); + //console.log(elementOfFairy); + if(!elementExists(fairyName)) { + elements[fairyName] = { + color: newColorArray, + insulate: true, + flippableX: true, + colorObject: newColorObjectArray, + behavior: [ + ["XX","M1","M1"], + ["XX","FX%5","XX"], + ["XX",`CR:${elementOfFairy}%0.5 AND CR:fairy_dust%0.005 AND M1`,"M1"] + ], + category: "auto fey", + temp: firstTemp, + hardness: 1, + autoType: "fairy", + }; + if(typeof(eLists) === "undefined") { + eLists = {}; + }; + if(typeof(eLists.FAIRY) === "undefined") { + eLists.FAIRY = []; + }; + eLists.FAIRY.push(fairyName); + if(!randomExcl) { + if(typeof(fairyChoices) === "undefined") { + fairyChoices = [] + }; + if(!fairyChoices.includes(fairyName)) { + fairyChoices.push(fairyName); + }; + } + if(isNocheer) { + elements[fairyName].nocheer = true; + } + if(fairyIncludeRandom) { + randomExcl ? elements[fairyName].excludeRandom = true : elements[fairyName].excludeRandom = false; + } else { + elements[fairyName].excludeRandom = true; + }; + if(isAfterScriptLoading) { + elements[fairyName].flippableX = true; + elementCount++; //increment for new fairy element + if (settings.cheerful && elements[fairyName].nocheer) { + elements[fairyName].hidden = true; + hiddenCount++; + } else { + createElementButton(fairyName); + }; + elements[fairyName].id = nextid++; + document.getElementById("extraInfo").innerHTML = canvasfooterTemplate() + }; + }; + returns.push(fairyName); + }; + return returns; + }; + //Spouts + generateSpout = function(spoutElements,isAfterScriptLoading=false) {//it can be a single element, though + //To specify an array spout, have the array be inside another array. + /*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false. + Otherwise, you'll get TypeErrors for some reason when trying to place your spout. If this is being run after the game has loaded (e.g. in the console), + then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but + the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**.*/ + if(typeof(spoutElements) === "string") { //it should be an array, so string check + //console.log("String detected"); + if(spoutElements.includes(",")) { //comma-separated string? + //console.log("Splitting string to array"); + spoutElements = spoutElements.split(","); //,SS to array + } else { + //console.log("Wrapping string in array"); + spoutElements = [spoutElements]; //single string to array + }; + }; + var returns = []; + for(aaf = 0; aaf < spoutElements.length; aaf++) { + var elementOfSpout = spoutElements[aaf]; + var allElementsHaveColors = ((elementOfSpout instanceof Array ? elementOfSpout : [elementOfSpout]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1) + if(!allElementsHaveColors) { continue }; + var startColor; + var randomExcl = 0; + var isNocheer = 0; + //console.log("randomExcl set") + //console.log(elementOfSpout); + var spoutName; + if(typeof(elementOfSpout === "string")) { //comma separated string check + if(elementOfSpout.includes(",")) { //if it is + elementOfSpout = elementOfSpout.split(","); //to array + elementOfSpout = elementOfSpout.filter(function(e) { //strip nonexistent elements + return typeof(elements[e]) === "object"; + }); + }; + }; + if(Array.isArray(elementOfSpout)) { + spoutName = `${elementOfSpout.join("_")}_spout`; //auto placer element name + //array case color concatenator and excludeRandom handler + startColor = []; + //console.log(elementOfSpout); + for(ll = 0; ll < elementOfSpout.length; ll++) { + if(typeof(elements[elementOfSpout[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfSpout[ll]].excludeRandom) { //it it's true + randomExcl = 1; //the whole array spout is excluded + //console.log("array nyet" + elementOfSpout); + }; + }; + //console.log(elementOfSpout[ll]); + startColor = startColor.concat(elements[elementOfSpout[ll]].color); + }; + for(ll = 0; ll < elementOfSpout.length; ll++) { + if(typeof(elements[elementOfSpout[ll]].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError) + if(elements[elementOfSpout[ll]].nocheer) { //it it's true + isNocheer = 1; //the whole array spout is excluded + //console.log("array nyet" + elementOfSpout); + }; + }; + //console.log(elementOfSpout[ll]); + startColor = startColor.concat(elements[elementOfSpout[ll]].color); + }; + } else { //they should all be strings, so here + spoutName = `${elementOfSpout}_spout`; //auto placer element name + startColor = elements[elementOfSpout].color; + if(typeof(elements[elementOfSpout].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfSpout].excludeRandom) { //it it's true + //console.log("nyet " + elementOfSpout); + randomExcl = 1; //the spout is excluded + } else { + //console.log("allow " + elementOfSpout); + randomExcl = 0; + }; + }; + if(typeof(elements[elementOfSpout].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError) + if(elements[elementOfSpout].nocheer) { //it it's true + //console.log("nyet " + elementOfSpout); + isNocheer = 1; //the spout is excluded + } else { + //console.log("allow " + elementOfSpout); + isNocheer = 0; + }; + }; + }; + //Color gen + if(Array.isArray(startColor)) { //Average arrays, make colors rgb() + startColor = averageRgbPrefixedColorArray(startColor); + } else { + startColor = rgbHexCatcher(startColor); + }; + var newColorObject = rgbStringToObject(startColor); + //End color gen + //The spout + //console.log(elementOfSpout); + var firstInfo, firstTemp; + if(Array.isArray(elementOfSpout)) { + firstInfo = elements[elementOfSpout[0]]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + } else { + firstInfo = elements[elementOfSpout]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + }; + elementOfSpout = tryJoin(elementOfSpout,","); + //console.log(elementOfSpout); + elements[spoutName] = { + color: startColor, + insulate: true, + colorObject: newColorObject, + behavior: [ + ["XX",`CR:${elementOfSpout}`,"XX"], + [`CR:${elementOfSpout}`,"XX",`CR:${elementOfSpout}`], + ["XX",`CR:${elementOfSpout}`,"XX"] + ], + category: "spouts", + temp: firstTemp, + hardness: 1, + autoType: "spout", + }; + if(!randomExcl) { + if(typeof(spoutChoices) === "undefined") { + spoutChoices = [] + }; + if(!spoutChoices.includes(spoutName)) { + spoutChoices.push(spoutName); + }; + } + if(spoutIncludeRandom) { + randomExcl ? elements[spoutName].excludeRandom = true : elements[spoutName].excludeRandom = false; + } else { + elements[spoutName].excludeRandom = true; + }; + if(isNocheer) { + elements[spoutName].nocheer = true; + } + if(isAfterScriptLoading) { + elementCount++; //increment for new spout element + if (settings.cheerful && elements[spoutName].nocheer) { + elements[spoutName].hidden = true; + hiddenCount++; + } else { + createElementButton(spoutName); + }; + elements[spoutName].id = nextid++; + document.getElementById("extraInfo").innerHTML = canvasfooterTemplate() + }; + eLists.SPOUT.push(spoutName); + returns.push(spoutName); + }; + return returns; + }; + //Other runAfterAutogen + //Bombs + elements.molten_dirt.tempHigh = 3000; + elements.molten_dirt.stateHigh = "vaporized_rock"; + //Modless orphaned code that I don't want meeting La Maison + runAfterAutogen(function() { + var solidBlacklist = ["mistake", "birthpool", "firesea"]; //exclude these since they seem to be liquid + solids = Object.keys(elements).filter(function(e) { + return elements[e].category === "solids" && !solidBlacklist.includes(e); + }); + for(i = 0; i < solids.length; i++) { //A lot of elements in solids, particularly metals, are missing a "state: solid". + var solidName = solids[i] + elements[solidName].state = "solid"; + }; + }); + //Mass generation calls + //Bombs + runAfterAutogen(function() { + if(generateBombs) { + var tempArray = Object.keys(elements); + tempArray.push(["rock", "sand"]); + generateBomb(tempArray,false) + }; + }); + //Creepers + runAfterAutogen(function() { + if(generateCreepers) { + var tempArray = Object.keys(elements); + tempArray.push(["rock", "sand"]); + generateCreeper(tempArray,false) + }; + }); + //Several + runAfterAutogen(function() { + cloudElements = [...Object.keys(elements).filter(function(e) { //same criteria as spouts + return (commonMovableCriteria(e,excludedCloudElements)); + }),["rock","sand"]]; + fairyElements = [...Object.keys(elements).filter(function(e) { //same criteria as spouts + return (commonMovableCriteria(e,excludedFairyElements)); + }),["rock","sand"]]; + spoutElements = [...Object.keys(elements).filter(function(e) { //same criteria as spouts + return (commonMovableCriteria(e,excludedSpoutElements)); + }),["rock","sand"]]; + if(generateFairies) { + generateFairy(fairyElements,false); + }; + if(generateClouds) { + generateCloud(cloudElements,"*",false); + }; + if(generateSpouts) { + generateSpout(spoutElements,false); + }; + }); + //Random spawners + //Bombs + elements.spawn_random_bomb = { + color: ["#141c23","#340f3c","#4b2d3f","#35463f","#244633","#460c1b","#294725","#34290c"], + behavior: behaviors.WALL, + category: "special", + excludeRandom: true, + tick: function(pixel) { + if(bombChoices == undefined || bombChoices.length == 0) { + deletePixel(pixel.x,pixel.y); + return false; + }; + changePixel(pixel,bombChoices[Math.floor(Math.random() * bombChoices.length)]) + }, + }; + //Clouds + elements.spawn_random_cloud = { + color: ["#3e5f8a","#a334ec","#ea96f9","#a6ecf6","#70ebc8","#d9286b","#7eed91","#a18b30"], + behavior: behaviors.WALL, + category: "special", + excludeRandom: true, + tick: function(pixel) { + if(cloudChoices == undefined || cloudChoices.length == 0) { + deletePixel(pixel.x,pixel.y); + return false; + }; + changePixel(pixel,cloudChoices[Math.floor(Math.random() * cloudChoices.length)]) + }, + }; + //Creepers + elements.spawn_random_creeper = { + color: colorOfRandomCreeper, + behavior: behaviors.WALL, + category: "special", + excludeRandom: false, //see below + movable: true, + tick: function(pixel) { + if(spawnCreepers == undefined || spawnCreepers.length == 0) { + deletePixel(pixel.x,pixel.y); + return false; + }; + changePixel(pixel,spawnCreepers[Math.floor(Math.random() * spawnCreepers.length)]) //spawnCreepers is already excludeRandom filtered + }, + }; + //Fairies + elements.spawn_random_fairy = { + color: ["#3e5f8a","#a334ec","#ea96f9","#a6ecf6","#70ebc8","#d9286b","#7eed91","#a18b30"], + behavior: behaviors.WALL, + category: "special", + excludeRandom: true, + tick: function(pixel) { + if(fairyChoices == undefined || fairyChoices.length == 0) { + deletePixel(pixel.x,pixel.y); + return false; + }; + changePixel(pixel,fairyChoices[Math.floor(Math.random() * fairyChoices.length)]) + }, + }; + //Spouts + elements.spawn_random_spout = { + color: ["#3e5f8a","#a334ec","#ea96f9","#a6ecf6","#70ebc8","#d9286b","#7eed91","#a18b30"], + behavior: behaviors.WALL, + category: "special", + excludeRandom: true, + tick: function(pixel) { + if(spoutChoices == undefined || spoutChoices.length == 0) { + deletePixel(pixel.x,pixel.y); + return false; + }; + changePixel(pixel,spoutChoices[Math.floor(Math.random() * spoutChoices.length)]) + }, + }; + //Other post-generation + //Fairies + //FAIRYKILL + behaviors.FAIRYKILL_OLD = behaviors.FAIRYKILL; + behaviors.FAIRYKILL = function(pixel) { + if (pixel.start === pixelTicks) {return} + if (pixel.charge && elements[pixel.element].behaviorOn) { + pixelTick(pixel) + } + var ignore = (elements[pixel.element].ignore == "undefined" ? [] : elements[pixel.element].ignore); + for(i = 0; i < adjacentCoords.length; i++) { + var coord = adjacentCoords[i]; + var offsetX = coord[0]; + var offsetY = coord[1]; + var newX = pixel.x+offsetX; + var newY = pixel.y+offsetY; + if(!isEmpty(newX,newY,true)) { + var newPixel = pixelMap[newX][newY]; + var newElement = newPixel.element; + var isIgnored = (newElement === ignore || ignore.includes(newElement)) + if(eLists.FAIRY.includes(newElement) && !isIgnored) { + deletePixel(newX,newY); + }; + }; + }; + doDefaults(pixel); + }; + //Add ignores + var ignoreArray = ["acid", "iron", "silver", "steel", "tungstensteel", "void", "liquid_void", "chute", "vute", "drute", "drolute", "volute", "alkahest", "acid_gas"]; + for(l = 0; l < ignoreArray.length; l++) { + var name = ignoreArray[l]; + var fairyName = `${ignoreArray[l]}_fairy`; + if(elementExists(name) && elementExists(fairyName)) { + var baseInfo = elements[name]; + if(typeof(baseInfo.ignore) === "undefined") { + baseInfo.ignore = []; + } else if(typeof(baseInfo.ignore) === "string") { + baseInfo.ignore = [baseInfo.ignore]; + }; + baseInfo.ignore.push(fairyName); + }; + }; + //Update FAIRYKILL elements + var fairykillElements = ["iron", "silver", "steel", "tungstensteel"]; + for(q = 0; q < fairykillElements.length; q++) { + var name = fairykillElements[q]; + if(elementExists(name)) { + elements[name].behavior = behaviors.FAIRYKILL; + }; + }; + //AUTOGENERATED CLUSTER BOMBS + for (var i = 3; i <= 15; i++) { + elements[`cluster_bomb_${i}`] = { + name: `${i}- cluster bomb`, + color: "#7d776d", + behavior: [ + `XX|EX:8>smoke,smoke,smoke,smoke,smoke,cluster_bomb_${i-1}%3|XX`, + "XX|XX|XX", + `M2|M1 AND EX:8>smoke,smoke,smoke,smoke,smoke,cluster_bomb_${i-1}%3|M2`, + ], + category: "weapons", + state: "solid", + density: 1300, + tempHigh: 1455.5, + stateHigh: "molten_steel", + excludeRandom: true, + } + } + elements.cluster_bomb_2 = { + color: "#7d776d", + behavior: [ + "XX|EX:8>smoke,smoke,smoke,smoke,smoke,cluster_bomb%3|XX", + "XX|XX|XX", + "M2|M1 AND EX:8>smoke,smoke,smoke,smoke,smoke,cluster_bomb%3|M2", + ], + category: "weapons", + state: "solid", + density: 1300, + tempHigh: 1455.5, + stateHigh: "molten_steel", + excludeRandom: true, + } + //EXPERIMENTAL RANDOMLY GENERATED LIQUIDS AND ROCKS ## + if(urlParams.get('liquidAmount') != null) { //null check + liquidAmount = urlParams.get('liquidAmount') + if(isNaN(liquidAmount) || liquidAmount === "" || liquidAmount === null) { //NaN check + liquidAmount = 10 + } + liquidAmount = parseInt(liquidAmount) + if(liquidAmount > 10000) { + alert("Maximum amount of liquids is 10000.\nOnly 10000 liquids were added.") + } else if(liquidAmount < 1) { + alert("Minimum amount of liquids is 1.\n1 liquid was added.") + } + liquidAmount = Math.min(10000,Math.max(liquidAmount,1)) + } else { + liquidAmount = 10 + } + if(urlParams.get('makeLiquidString') !== null) { //if the variable exists at all + makeLiquidString = true + } else { //if it doesn't (and it returns null) + makeLiquidString = false + } + //arbitrarily picked + randomNameGraphemes = { + initials: ["m","n","p","t","ch","k","b","d","j","g","f","th","s","sh","h","l","r","y","w","z","sp","st","sk","sl","spl","stl","skl","sr","spr","str","skr","sl","fl","fr","pl","pr","tl","tr","kl","kr","shr","fl","fr","thr"], + vowels: ["a","e","i","o","u","ay","ee","ie","oa","ew","oo","oi","ow"], + medials: ["m","n","p","t","k","b","d","g","f","th","s","sh","h","l","r","y","z","sp","st","sk","sl","spl","stl","skl","sr","spr","str","skr","sl","fl","fr","pl","pr","tl","tr","kl","kr","shr","fl","fr","thr"], + finals: ["m","n","p","t","k","b","d","g","f","th","s","sh","l","r","y","z","sp","st","sk","sl","spl","stl","skl","sr","spr","str","skr","pl","pr","tl","tr","bl","vr"] + }; + function generateName() { + //these are picked arbitrarily + //console.log("getting random type") + var randomInt1 = randomIntegerFromZeroToValue(6) + //console.log("generating type " + randomInt1) + if(randomInt1 == 0) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 1) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 2) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + "e" + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 3) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.finals) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 4) { + var randomName = randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 5) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 6) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + //console.log("generated T" + randomInt1 + " name") + } else { + var randomName = randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + //console.log("warning: type was above 6 somehow") + } + //console.log(randomName) + return randomName + } + function randomLiquidColorGen() { + var randomR = randomIntegerFromZeroToValue(255) + var randomG = randomIntegerFromZeroToValue(255) + var randomB = randomIntegerFromZeroToValue(255) + var randomColor = {r: randomR, g: randomG, b: randomB} + var newLiquidColor = averageColorObjects(_cc.w.j,randomColor,weight1=0.1) + var newSolidColor = averageColorObjects(_cc.w.j,randomColor,weight1=0.4) + var newGasColor = averageColorObjects(_cc.w.j,randomColor,weight1=0.7) + var newLiquidColor = rgbToHex(newLiquidColor) + var newSolidColor = rgbToHex(newSolidColor) + var newGasColor = rgbToHex(newGasColor) + return [newLiquidColor, newSolidColor, newGasColor] + } + function _generateAveragedRandoms() { + return averageNumericArray([Math.random(),Math.random(),Math.random()]) + } + function avgRndToMult() { + return 1 + (0.55 - _generateAveragedRandoms()) + } + if(makeLiquidString == true) { + liquidString = "" + } + for(i = 0; i < liquidAmount; i++) { + var name = generateName(); + var meltingAdjustment = avgRndToMult(); + var densityAdjustment = avgRndToMult(); + var hardnessAdjustment = avgRndToMult(); + var conductivity = Math.random() ** 1.8; + var conductivityEnabled = Math.random(); + var conducts = null; + conductivityEnabled < 1/3 ? conducts = true : conducts = false; + var burn = (Math.random() ** 2) * 100; + var burnTime = 10 ** ((0.5 + Math.random()) ** 3); + var burningEnabled = Math.random(); + var burns = null; + burningEnabled < 1/3 ? burns = true : burns = false; + var colors = randomLiquidColorGen(); + var viscosity = (1 + Math.random()) ** 10; + var solidDensityMultiplier = 1+(Math.abs(1-avgRndToMult())); + var solidDensityRelationRandomizer = Math.random(); + var solidIsLessDense = null; + var solidDensity = null; + solidDensityRelationRandomizer < 0.1 ? solidIsLessDense = true : solidIsLessDense = false; + solidIsLessDense == true ? solidDensity = (1000 / solidDensityMultiplier) * densityAdjustment : solidDensity = (1000 * solidDensityMultiplier) * densityAdjustment; + var freezingPoint = (273 * (meltingAdjustment ** 10)) - 273; + var boilingPoint = freezingPoint + Math.abs((273 * (avgRndToMult() ** 9)) - 273); + var gasDensity = 0.00143 * avgRndToMult() * 1000 * densityAdjustment; + if(typeof(elements[name]) != "undefined") { + name = name + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals); + }; + liquidConductivityAdjust = 0.5 * Math.sqrt(avgRndToMult()); + elements[name] = { + name: name, + color: colors[0], + behavior: behaviors.LIQUID, + tempLow: freezingPoint, + temp: freezingPoint + 20, + tempHigh: boilingPoint, + stateLow: `${name}_ice`, + stateHigh: `${name}_gas`, + category: "random liquids", + state: "liquid", + density: 1000 * densityAdjustment, + //conduct: goes here + //burn and burnTime go here + hardness: 0.3 * hardnessAdjustment, + viscosity: viscosity, + breakInto: `${name}_gas`, + }; + elements[`${name}_ice`] = { + name: `${name} ice`, + color: colors[1], + behavior: behaviors.WALL, + tempHigh: freezingPoint, + temp: freezingPoint - 20, + stateHigh: name, + category: "random solids", + state: "solid", + density: solidDensity, + //conduct: goes here + //burn and burnTime go here + hardness: 0.6 * hardnessAdjustment, + breakInto: name, + }; + elements[`${name}_gas`] = { + name: `${name} gas`, + color: colors[2], + behavior: behaviors.GAS, + tempLow: boilingPoint, + temp: boilingPoint + 20, + stateLow: name, + category: "random gases", + state: "gas", + density: gasDensity, + //burn and burnTime go here + hardness: 1, + }; + if(burns == true) { + elements[name].burn = burn + elements[name].burnTime = burnTime + elements[`${name}_ice`].burn = burn + elements[`${name}_ice`].burnTime = burnTime + elements[`${name}_gas`].burn = burn + elements[`${name}_gas`].burnTime = burnTime + } + if(conducts == true) { + elements[name].conduct = conductivity * liquidConductivityAdjust + elements[`${name}_ice`].conduct = conductivity + } + if(makeLiquidString == true) { + //Append moddable code for the liquid state to liquidString + liquidString = liquidString + `elements.${name} = {\n name: \"${name}\",\n color: \"${colors[0]}\",\n behavior: behaviors.LIQUID,\n tempLow: ${freezingPoint},\n temp: ${freezingPoint + 20},\n tempHigh: ${boilingPoint},\n stateLow: \"${name}_ice\",\n stateHigh: \"${name}_gas\",\n category: \"random liquids\",\n state: \"liquid\",\n density: ${1000 * densityAdjustment},\n ` + if(conducts == true) { liquidString = liquidString + `conduct: ${conductivity * liquidConductivityAdjust},\n ` } + if(burns == true) { liquidString = liquidString + `burn: ${burn},\n burnTime: ${burnTime},\n ` } + liquidString = liquidString + `hardness: ${0.3 * hardnessAdjustment},\n viscosity: ${viscosity},\n breakInto: \"${name}_gas\",\n};\n\n` + //Append moddable code for the solid state to liquidString + liquidString = liquidString + `elements.${name}_ice = {\n name: \"${name} ice\",\n color: \"${colors[1]}\",\n behavior: behaviors.WALL,\n tempHigh: ${freezingPoint},\n temp: ${freezingPoint - 20},\n stateHigh: \"${name}\",\n category: \"random solids\",\n state: \"solid\",\n density: ${solidDensity},\n ` + if(conducts == true) { liquidString = liquidString + `conduct: ${conductivity},\n ` } + if(burns == true) { liquidString = liquidString + `burn: ${burn},\n burnTime: ${burnTime},\n ` } + liquidString = liquidString + `hardness: ${0.6 * hardnessAdjustment},\n breakInto: \"${name}\",\n};\n\n` + //Append moddable code for the gaseous state to liquidString + liquidString = liquidString + `elements.${name}_gas = {\n name: \"${name} gas\",\n color: \"${colors[2]}\",\n behavior: behaviors.GAS,\n tempLow: ${boilingPoint},\n temp: ${boilingPoint + 20},\n stateLow: \"${name}\",\n category: \"random gases\",\n state: \"solid\",\n density: ${gasDensity},\n ` + if(burns == true) { liquidString = liquidString + `burn: ${burn},\n burnTime: ${burnTime},\n ` } + liquidString = liquidString + `hardness: 1,\n};\n\n` + } + } + if(makeLiquidString == true) { + console.log(`Liquids added to liquidString (length ${liquidString.length})`) + } + if(urlParams.get('rockAmount') != null) { //null check + rockAmount = urlParams.get('rockAmount') + if(isNaN(rockAmount) || rockAmount === "" || rockAmount === null) { //NaN check + rockAmount = 10 + } + rockAmount = parseInt(rockAmount) + if(rockAmount > 10000) { + alert("Maximum amount of rocks is 10000.\nOnly 10000 rocks were added.") + } else if(rockAmount < 1) { + alert("Minimum amount of rocks is 1.\n1 rock was added.") + } + rockAmount = Math.min(10000,Math.max(rockAmount,1)) + } else { + rockAmount = 10 + } + if(urlParams.get('makeRockString') !== null) { //if the variable exists at all + makeRockString = true + } else { //if it doesn't (and it returns null) + makeRockString = false + } + function generateName() { + //these are picked arbitrarily + //console.log("getting random type") + var randomInt1 = randomIntegerFromZeroToValue(6) + //console.log("generating type " + randomInt1) + if(randomInt1 == 0) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 1) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 2) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + "e" + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 3) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.finals) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 4) { + var randomName = randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 5) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + //console.log("generated T" + randomInt1 + " name") + } else if(randomInt1 == 6) { + var randomName = randomChoice(randomNameGraphemes.initials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + //console.log("generated T" + randomInt1 + " name") + } else { + var randomName = randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.medials) + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + //console.log("warning: type was above 6 somehow") + } + //console.log(randomName) + return randomName + } + randomRockColorTemplateLuma = { + rock1: {r: 128, g: 128, b: 128}, + rock2: {r: 79, g: 79, b: 79}, + rock3: {r: 148, g: 148, b: 148}, + gravel1: {r: 227, g: 224, b: 223}, + gravel2: {r: 177, g: 171, b: 163}, + gravel3: {r: 116, g: 115, b: 109}, + gravel4: {r: 82, g: 75, b: 71} + }; + function randomRockColorGen() { + var randomR = randomIntegerFromZeroToValue(255) + var randomG = randomIntegerFromZeroToValue(255) + var randomB = randomIntegerFromZeroToValue(255) + var randomColor = {r: randomR, g: randomG, b: randomB} + var newRockColor1 = averageColorObjects(randomRockColorTemplateLuma.rock1,randomColor,weight1=0.65) + var newRockColor2 = averageColorObjects(randomRockColorTemplateLuma.rock2,randomColor,weight1=0.65) + var newRockColor3 = averageColorObjects(randomRockColorTemplateLuma.rock3,randomColor,weight1=0.65) + var newGravelColor1 = averageColorObjects(randomRockColorTemplateLuma.gravel1,randomColor,weight1=0.675) + var newGravelColor2 = averageColorObjects(randomRockColorTemplateLuma.gravel2,randomColor,weight1=0.675) + var newGravelColor3 = averageColorObjects(randomRockColorTemplateLuma.gravel3,randomColor,weight1=0.675) + var newGravelColor4 = averageColorObjects(randomRockColorTemplateLuma.gravel4,randomColor,weight1=0.675) + var newRockColor1 = rgbToHex(newRockColor1) + var newRockColor2 = rgbToHex(newRockColor2) + var newRockColor3 = rgbToHex(newRockColor3) + var newGravelColor1 = rgbToHex(newGravelColor1) + var newGravelColor2 = rgbToHex(newGravelColor2) + var newGravelColor3 = rgbToHex(newGravelColor3) + var newGravelColor4 = rgbToHex(newGravelColor4) + return [newRockColor1, newRockColor2, newRockColor3, newGravelColor1, newGravelColor2, newGravelColor3, newGravelColor4] + } + function _generateAveragedRandoms() { + return averageNumericArray([Math.random(),Math.random(),Math.random()]) + } + function avgRndToMult() { + return 1 + (0.55 - _generateAveragedRandoms()) + } + elements.gravel.breakInto = "dust" + if(makeRockString == true) { + rockString = "" + } + for(i = 0; i < rockAmount; i++) { + var name = generateName() + var meltingAdjustment = avgRndToMult() + var densityAdjustment = avgRndToMult() + var hardnessAdjustment = avgRndToMult() + var colors = randomRockColorGen() + if(typeof(elements[name]) != "undefined") { + name = name + randomChoice(randomNameGraphemes.vowels) + randomChoice(randomNameGraphemes.finals) + } + elements[name] = { + name: name, + color: [colors[0], colors[1], colors[2]], + behavior: behaviors.POWDER, + tempHigh: 950 * meltingAdjustment, + category: "random rocks", + state: "solid", + density: 2550 * densityAdjustment, + hardness: 0.5 * hardnessAdjustment, + breakInto: ["dust",`${name}_gravel`], + } + if(makeRockString == true) { + rockString = rockString + `elements.${name} = {\n name: \"${name}\",\n color: [\"${colors[0]}\", \"${colors[1]}\", \"${colors[2]}\"],\n behavior: behaviors.POWDER,\n tempHigh: ${950 * meltingAdjustment},\n category: \"random rocks\",\n state: \"solid\",\n density: ${2550 * densityAdjustment},\n hardness: ${0.5 * hardnessAdjustment},\n breakInto: [\"dust\",\"${name}_gravel\"],\n};\n\n` + } + elements[`${name}_gravel`] = { + name: `${name} gravel`, + color: [colors[3], colors[4], colors[5], colors[6]], + behavior: behaviors.POWDER, + tempHigh: 950 * meltingAdjustment, + stateHigh: name, + category: "random rocks", + state: "solid", + density: 1680 * densityAdjustment, + hardness: 0.2 * (hardnessAdjustment ** (2/3)), + breakInto: "dust", + } + if(makeRockString == true) { + rockString = rockString + `elements.${name}_gravel = {\n name: \"${name} gravel\",\n color: [\"${colors[3]}\", \"${colors[4]}\", \"${colors[5]}\", \"${colors[6]}\"],\n behavior: behaviors.POWDER,\n tempHigh: ${950 * meltingAdjustment},\n stateHigh: \"${name}\",\n category: \"random rocks\",\n state: \"solid\",\n density: ${1680 * densityAdjustment},\n hardness: ${0.2 * (hardnessAdjustment ** (2/3))},\n breakInto: \"dust\",\n};\n\n` + } + } + if(makeRockString == true) { + console.log(`Rocks added to rockString (length ${rockString.length})`) + } + //PRIMITIVELY RANDOMLY GENERATED ELEMENTS ## + elements.ogqwwrko = { + name: "ogqWWRKo", + color: "#b5f134", + behavior: [ + "DB|XX|XX", + "CH:molten_silver>antifire|XX|BO", + "SA|SA|XX", + ], + category: "hyperrandom elements", + burn: 57, + burnTime: 451, + tempHigh: 1055, + density: 6632.184014557264, + }; + elements.kibifwpd = { + name: "KIBiFwpD", + color: "#b8a3a5", + behavior: [ + "XX|RT|CH:light%32", + "SW|DL%25|SH", + "DB|SH|SW:firefly", + ], + category: "hyperrandom elements", + density: 1382.2019914825285, + }; + elements.sezhorqq = { + name: "SEzhorqQ", + color: "#f8f19c", + behavior: [ + "DB|XX|M1", + "SA|CH:wheat%23|ST", + "M1|CH:molten_bronze>charcoal|XX", + ], + category: "hyperrandom elements", + burn: 26, + burnTime: 228, + density: 11054.763724436774, + }; + elements.bgcelthv = { + name: "bGceLthV", + color: "#2a8885", + behavior: [ + "CC:#ec80f8|XX|ST", + "SW:salt|HT:10|RT", + "CH:glitter%4|SH|SW:salt", + ], + reactions: { + "meat": { "elem1":"mushroom_stalk", "elem2":"bee", "chance":0.07255393753591068 }, + "ball": { "elem1":"frog", "elem2":"acid_gas", "chance":0.8120527233695284 }, + "antimatter": { "elem1":"molten_salt", "elem2":"dirty_water", "chance":0.18656242174277482 }, + "burner": { "elem1":"electric", "elem2":"charcoal", "chance":0.9864527094278326 }, + "oil": { "elem1":"epsom_salt", "elem2":"bamboo_plant", "chance":0.7298237285611298 }, + }, + category: "hyperrandom elements", + density: 1446.9471325970142, + }; + elements.hpwfukak = { + name: "hpWfukAK", + color: "#f22c87", + behavior: [ + "XX|SW:bronze|SW", + "CR:cancer|LB:slug|CO:1", + "BO|SW:bronze|BO", + ], + category: "hyperrandom elements", + burn: 71, + burnTime: 449, + density: 10877.887548154851, + }; + elements.ggysmbid = { + name: "GgYSMbID", + color: "#81ff73", + behavior: [ + "SW|CR:molten_sterling|BO", + "DL:magma|C2:wood|XX", + "CC:#de4dbe|XX|DL:magma", + ], + reactions: { + "slug": { "elem1":"mushroom_cap", "elem2":"plastic", "chance":0.7318844051213007 }, + "wheat": { "elem1":"frozen_meat", "elem2":"mushroom_spore", "chance":0.7895678110819928 }, + "sulfur": { "elem1":"caramel", "elem2":"liquid_hydrogen", "chance":0.8659314154325147 }, + "bamboo_plant": { "elem1":"magma", "elem2":"glitter", "chance":0.5424222221105207 }, + }, + category: "hyperrandom elements", + tempHigh: 2956, + density: 6284.617729241624, + }; + elements.fpnklukg = { + name: "FpNkluKG", + color: "#a68793", + behavior: [ + "DB|DL:calcium|DB", + "SW|LB:algae|ST", + "DL:calcium|HT:4|CR:copper", + ], + reactions: { + "dirt": { "elem1":"sand", "elem2":"water", "chance":0.9366010380552752 }, + }, + category: "hyperrandom elements", + tempHigh: 1300, + density: 3828.9615486132043, + }; + elements.efgtdvzx = { + name: "EfgTDvzx", + color: "#96ca55", + behavior: [ + "SA|CO:7|SH", + "SW:potato_seed|L2:slaked_lime|CH:sugar_water>soap", + "BO|CC:#0bd2dd|CO:7", + ], + category: "hyperrandom elements", + density: 18274.111700368052, + }; + elements.qacwivpp = { + name: "QACWIVPp", + color: "#8f57c0", + behavior: [ + "HT:8|CH:quicklime>copper|CR:dioxin", + "CC:#f64cc7|L1:mudstone|M2", + "CH:quicklime>copper|RT|CH:firefly%3", + ], + category: "hyperrandom elements", + density: 12926.310301027497, + }; + elements.qfgdrsay = { + name: "qFgDRSAY", + color: "#c06b4e", + behavior: [ + "CC:#bb68ee|DB|DL:molten_brick", + "CR:iron|CO:1|M1", + "CO:1|M2|SP", + ], + category: "hyperrandom elements", + burn: 50, + burnTime: 376, + density: 17821.827734771083, + }; + elements.qbesguoy = { + name: "QbeSGuOy", + color: "#4b9513", + behavior: [ + "CC:#39d412|CO:10|CO:10", + "HT:5|FY|SW:snow_cloud", + "M1|SH|CO:10", + ], + reactions: { + "ant": { "elem1":"tin", "elem2":"acid", "chance":0.015683479804684108 }, + "liquid_hydrogen": { "elem1":"molten_calcium", "elem2":"tinder", "chance":0.5541216729460897 }, + "molten_borax": { "elem1":"wall", "elem2":"bee", "chance":0.037733528661837115 }, + "rock": { "elem1":"wire", "elem2":"wall", "chance":0.5515341715699367 }, + "chocolate": { "elem1":"wet_sand", "elem2":"bamboo_plant", "chance":0.34082969121459494 }, + "vinegar": { "elem1":"rock", "elem2":"flour", "chance":0.3551432786489651 }, + "molten_iron": { "elem1":"led_r", "elem2":"udder", "chance":0.2856279501232143 }, + "floating_cloner": { "elem1":"udder", "elem2":"copper", "chance":0.3669477762812875 }, + "cancer": { "elem1":"yeast", "elem2":"bone_marrow", "chance":0.7241288621811043 }, + }, + category: "hyperrandom elements", + burn: 34, + burnTime: 408, + density: 7097.343018249547, + }; + elements.omsdvddh = { + name: "oMsDvDdH", + color: "#e2d948", + behavior: [ + "HT:8|RT|M1", + "ST|L1:chocolate_syrup|M2", + "SA|CR:flea|SH", + ], + category: "hyperrandom elements", + burn: 98, + conduct: 0.6023663023285293, + burnTime: 199, + tempHigh: 2767, + density: 10910.501840659097, + }; + elements.izhsemyl = { + name: "IzhSEMYl", + color: "#7fafff", + behavior: [ + "ST|SW:clay_soil|CO:11", + "SA|LB:infection|BO", + "SW:clay_soil|SW:clay_soil|CR:dough", + ], + category: "hyperrandom elements", + density: 2810.029487411252, + }; + elements.ayvnmlrq = { + name: "AyvnmLRQ", + color: "#668a95", + behavior: [ + "HT:1|M2|SA", + "CR:molten_nickel|CH:light%6|CO:4", + "SP|HT:1|SW:liquid_oxygen", + ], + category: "hyperrandom elements", + tempHigh: 893, + density: 11509.655636160245, + }; + elements.pjrdtuta = { + name: "pJRDtuTA", + color: "#b12040", + behavior: [ + "BO|SW:sodium_acetate|CO:1", + "BO|L1:laser|CC:#e40c6e", + "SW:sodium_acetate|SW|BO", + ], + category: "hyperrandom elements", + conduct: 0.33534106397322677, + density: 18826.565148369646, + }; + elements.qodfmdfo = { + name: "QodfMdFo", + color: "#fe530b", + behavior: [ + "ST|DL:cheese|RT", + "DB|FX|HT:11", + "CH:molten_sterling>wood|SP|CR:molten_iron", + ], + category: "hyperrandom elements", + conduct: 0.3465345689563781, + density: 11140.091847406393, + }; + elements.rwwkmgjn = { + name: "rWWkMgJN", + color: "#2eeaa0", + behavior: [ + "CR:ant|CH:salt%7|DB", + "DL:freezer|FX|XX", + "CC:#c82b89|BO|HT:5", + ], + category: "hyperrandom elements", + conduct: 0.698741020714595, + tempHigh: 2686, + density: 17409.619803480866, + }; + /*elements.yckrmplh = { + name: "yckRMplh", + color: "#0807ad", + behavior: [ + "XX|DL:dead_cum_water|CR:firesea", + "SH|HT:11|M2", + "BO|SH|M2", + ], + reactions: { + "fairy_fairy_fairy_fairy": { "elem1":"molten_uranium", "elem2":"tritium", "chance":0.4602898017566186 }, + "fluxed_matte_copper": { "elem1":"pure_water", "elem2":"enchanted_ketchup", "chance":0.8272186749812657 }, + "molten_neutronium": { "elem1":"amogus9", "elem2":"fallout", "chance":0.9302153671106638 }, + "bone_beast": { "elem1":"celie_leaves", "elem2":"dirt", "chance":0.3741482072147079 }, + "fairy_fairy": { "elem1":"led_g", "elem2":"smoke", "chance":0.29352648263376613 }, + "fairy_fairy_fairy": { "elem1":"old_celie_leaves", "elem2":"green_dye", "chance":0.5993662901794029 }, + "flamer": { "elem1":"amogus1", "elem2":"brass", "chance":0.5319076167877976 }, + }, + category: "hyperrandom elements", + tempHigh: 537, + density: 9215.024069840034, + }*/ + elements.urhhhqjs = { + name: "urhhhqJS", + color: "#31a6bf", + behavior: [ + "M2|CH:anesthesia%7|CH:anesthesia%7", + "SH|L1:sugar|CC:#55eda7", + "M1|DL:t_center|M2", + ], + category: "hyperrandom elements", + density: 373.9243313453792, + }; + elements.budgkclj = { + name: "BUDGKClj", + color: "#ba657f", + behavior: [ + "DL:blister_copper|SW:fire_fairy|XX", + "SA|CC:#3e65ab|ST", + "CC:#3e65ab|SP|CC:#3e65ab", + ], + category: "hyperrandom elements", + burn: 5, + burnTime: 480, + tempHigh: 2895, + density: 4123.593585708072, + }; + elements.yzkluoho = { + name: "yzKluohO", + color: "#3e297d", + behavior: [ + "RT|CH:molten_borax>anesthesia|M2", + "M1|DL%11|SW:molten_amogus1", + "M2|CC:#0d4b06|SA", + ], + category: "hyperrandom elements", + burn: 22, + burnTime: 172, + tempHigh: 2838, + density: 16097.788434828502, + }; + elements.pcqahdke = { + name: "PCQAHdkE", + color: "#425618", + behavior: [ + "CR:molten_uranium|CO:4|CH:led_b%10", + "DL:magic|HT:11|ST", + "SP|SP|SW:cocoon", + ], + category: "hyperrandom elements", + density: 6338.489583058846, + }; + elements.ywdgkhcr = { + name: "ywDgKHcr", + color: "#1bb24d", + behavior: [ + "SW:fly|SW|CR:burning_unnamed_powder", + "CO:9|CC:#71a6b1|CH:fairy_fairy_fairy_fairy>amogus3", + "BO|XX|CH:bone%6", + ], + category: "hyperrandom elements", + conduct: 0.18249512087981343, + tempHigh: 2926, + density: 5349.2882949054265, + }; + elements.acsjdbsp = { + name: "ACsjdBSP", + color: "#c25375", + behavior: [ + "CH:antigas>fairy_fairy_fairy_fairy|BO|XX", + "DL:chocolate|CH:fairy_fairy_fairy%10|BO", + "SW:tree_branch|XX|SW:tree_branch", + ], + category: "hyperrandom elements", + density: 19429.0198815719, + }; + elements.bxxbbugd = { + name: "BXXBBUgd", + color: "#63fb15", + behavior: [ + "DB|SH|DB", + "XX|XX|SW:aluminum", + "SW|SW|CH:a4left%7", + ], + category: "hyperrandom elements", + density: 11933.76730202344, + }; + /* + elements.kkhxgsiw = { + name: "KkHXGSiW", + color: "#30ee04", + behavior: [ + "SW:molten_copper|CH:fairy_fairy_fairy%8|SW:molten_copper", + "XX|DL%5|ST", + "CH:dead_cum>molten_tungstensteel|DB|DL:fairy", + ], + reactions: { + "led_r": { "elem1":"ketchup_metal", "elem2":"mystic_fire", "chance":0.032394734075084766 }, + "plague": { "elem1":"flameshockwave3", "elem2":"stardust", "chance":0.13908761952404325 }, + "blood": { "elem1":"rocket", "elem2":"fairy_fairy_fairy", "chance":0.16814629599115316 }, + "yeast": { "elem1":"oil", "elem2":"juice", "chance":0.9787877797027756 }, + "stardust": { "elem1":"molten_AyvnmLRQ", "elem2":"fairy_fairy", "chance":0.18375417180105702 }, + "cooler": { "elem1":"celie_leaves", "elem2":"corn_seed", "chance":0.15359338811668444 }, + }, + category: "hyperrandom elements", + burn: 60, + burnTime: 491, + density: 6333.626319914362, + }*/ + elements.gqyhbmuq = { + name: "GQYHBMuQ", + color: "#3e3969", + behavior: [ + "CR:flea|CH:led_b%5|BO", + "SW|CC:#aaf599|SW:molten_dirt", + "SW|ST|SW", + ], + category: "hyperrandom elements", + tempHigh: 2972, + density: 3881.977856937143, + }; + elements.ookioobo = { + name: "OOKIoObo", + color: "#f072da", + behavior: [ + "SW|SW|DL:fairy_fairy_fairy_fairy", + "RT|XX|SW:copper", + "BO|XX|XX", + ], + category: "hyperrandom elements", + density: 34.228020163470646, + }; + elements.qqnuejnt = { + name: "qQNuEjNT", + color: "#0a4f5d", + behavior: [ + "BO|CH:tin>udder|ST", + "CH:molten_amogus7%1|DL%9|SW:magma", + "M1|XX|XX", + ], + category: "hyperrandom elements", + burn: 47, + burnTime: 183, + tempHigh: 2096, + density: 9679.340844240462, + }; + elements.hgshruqe = { + name: "HgSHruqE", + color: "#d59786", + behavior: [ + "HT:3|M1|RT", + "XX|FY|RT", + "M2|CC:#885997|M1", + ], + category: "hyperrandom elements", + burn: 85, + burnTime: 90, + tempHigh: 346, + density: 11697.491509311361, + }; + /* + elements.hyvrnxqq = { + name: "hyVrNXQQ", + color: "#a214b9", + behavior: [ + "SW|XX|CH:dead_cum_water%2", + "CO:4|LB:molten_amogus4|CH:fairy_fairy>molten_potassium_salt", + "CH:fairy_fairy_fairy_fairy>molten_potassium_salt|CR:rainbow|CR:rainbow", + ], + reactions: { + "fairy_fairy_fairy": { "elem1":"concoction", "elem2":"straw", "chance":0.17509068546150386 }, + }, + category: "hyperrandom elements", + burn: 69, + burnTime: 76, + tempHigh: 1122, + density: 7158.798415310327, + }*/ + elements.xxystxnm = { + name: "xxYsTXnM", + color: "#763817", + behavior: [ + "XX|SW|CC:#210b1d", + "SW:blue_dye|CO:6|SW:blue_dye", + "SW:blue_dye|HT:9|XX", + ], + category: "hyperrandom elements", + tempHigh: 2187, + density: 15160.604263202209, + }; + elements.sowljgtd = { + name: "SowljGtd", + color: "#c554eb", + behavior: [ + "BO|XX|DL:t2", + "XX|FY|XX", + "XX|SW:ionized_hydrogen|HT:8", + ], + reactions: { + "petal": { "elem1":"fairy_fairy_fairy", "elem2":"molten_sterling", "chance":0.13858328633550154 }, + "fairy_fairy_fairy_fairy": { "elem1":"glass_shard", "elem2":"oxidized_copper", "chance":0.8228073223526745 }, + "molten_corrupt_land": { "elem1":"everfire_dust", "elem2":"matte_copper", "chance":0.6538956182983582 }, + "tralphium": { "elem1":"ice_fairy", "elem2":"mycelium", "chance":0.015319150698328909 }, + "tritium": { "elem1":"molten_zinc", "elem2":"fairy_fairy", "chance":0.13333787328877345 }, + }, + category: "hyperrandom elements", + burn: 82, + conduct: 0.5267298783471723, + burnTime: 313, + density: 10531.689830022671, + }; + elements.givrbdjd = { + name: "giVRBDjd", + color: "#c08adf", + behavior: [ + "SH|DB|M1", + "BO|CH:slug%9|XX", + "M1|RT|SP", + ], + category: "hyperrandom elements", + density: 8965.264917972576, + }; + eLists.RANDOM = [ //commented-out names correspond to elements commented out because they reference cum + "ogqwwrko", "kibifwpd", "sezhorqq", "bgcelthv", "hpwfukak", + "ggysmbid", "fpnklukg", "efgtdvzx", "qacwivpp", "qfgdrsay", + "qbesguoy", "omsdvddh", "izhsemyl", "ayvnmlrq", "pjrdtuta", + "qodfmdfo", "rwwkmgjn", /*"yckrmplh",*/ "urhhhqjs", "budgkclj", + "yzkluoho", "pcqahdke", "ywdgkhcr", "acsjdbsp", "bxxbbugd", + /*"kkhxgsiw",*/ "gqyhbmuq", "ookioobo", "qqnuejnt", "hgshruqe", + /*"hyvrnxqq",*/ "xxystxnm", "sowljgtd", "givrbdjd" + ]; + //To do: recipe; + //NEUTRONIUM COMPRESSOR ## + var singularityColorTemplate = ["#202020", "#505050", "#b0b0b0", "#c7c7c7"]; + singularityNumber = 10000; + if(urlParams.get('singularityIncludeRandom') !== null) { //if the variable exists at all + singularityIncludeRandom = true + } else { //if it doesn't (and it returns null) + singularityIncludeRandom = false + } + //Generate singularities + if(urlParams.get('generateSingularities') !== null) { //if the variable exists at all + generateSingularities = true + } else { //if it doesn't (and it returns null) + generateSingularities = false + } + function haseulitoidSingularityTick(pixel) { + var s = 1; + if(elements[pixel.element].singularityNumber !== undefined) { + s = elements[pixel.element].singularityNumber; + }; + if(pixel.value == undefined) { pixel.value = 0 }; + valueFunction(pixel,haseuliteValueObject,haseuliteSpreadWhitelist); + if(pixel.oldColor === undefined) { pixel.oldColor = pixelColorPick(pixel) }; + if(pixel.oldColor === null) { pixel.oldColor = pixel.color }; + pixel.color = lightenColor(pixel.oldColor,pixel.value / 3); + var mVal = elements[pixel.element].haseulitoidMaxValue ?? 800; + if(pixel.value >= mVal) { + var coldBoomChance = Math.max(0.006 * ((pixel.value - mVal) / (400/3)), 0.000075); + if(Math.random() < coldBoomChance) { + var coldBoomRadius = Math.min(120,Math.floor((7 + s) + ((pixel.value - mVal) / Math.max(20,100 - s - s)))); + explodeAtPlus(pixel.x,pixel.y,coldBoomRadius,"cold_fire","cold_smoke",null,coldExplosionAfterCooling); + }; + }; + }; + function generateSingularity(singularityElements,isAfterScriptLoading=false) {//it can be a single element, though + var count = 0; + if(typeof(singularityElements) === "string") { //it should be an array, so string check + //console.log("String detected"); + if(singularityElements.includes(",")) { //comma-separated string? + //console.log("Splitting string to array"); + singularityElements = singularityElements.split(","); //,SS to array + } else { + //console.log("Wrapping string in array"); + singularityElements = [singularityElements]; //single string to array + }; + }; + for(aaf = 0; aaf < singularityElements.length; aaf++) { + var elementOfSingularity = singularityElements[aaf]; + var startColor; + var randomExcl = 0; + var isNocheer = 0; + //console.log("randomExcl set") + //console.log(elementOfSingularity); + var singularityName; + if(typeof(elementOfSingularity === "string")) { //comma separated string check + if(elementOfSingularity.includes(",")) { //if it is + elementOfSingularity = elementOfSingularity.split(","); //to array + elementOfSingularity = elementOfSingularity.filter(function(e) { //strip nonexistent elements + return typeof(elements[e]) === "object"; + }); + }; + }; + if(Array.isArray(elementOfSingularity)) { + singularityName = `${elementOfSingularity.join("_")}_singularity`; //auto placer element name + //array case color concatenator and excludeRandom handler + startColor = []; + //console.log(elementOfSingularity); + for(ll = 0; ll < elementOfSingularity.length; ll++) { + if(typeof(elements[elementOfSingularity[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfSingularity[ll]].excludeRandom) { //it it's true + randomExcl = 1; //the whole array singularity is excluded + //console.log("array nyet" + elementOfSingularity); + }; + }; + //console.log(elementOfSingularity[ll]); + startColor = startColor.concat(elements[elementOfSingularity[ll]].color); + }; + for(ll = 0; ll < elementOfSingularity.length; ll++) { + if(typeof(elements[elementOfSingularity[ll]].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError) + if(elements[elementOfSingularity[ll]].nocheer) { //it it's true + isNocheer = 1; //the whole array singularity is excluded + //console.log("array nyet" + elementOfSingularity); + }; + }; + //console.log(elementOfSingularity[ll]); + startColor = startColor.concat(elements[elementOfSingularity[ll]].color); + }; + } else { //they should all be strings, so here + singularityName = `${elementOfSingularity}_singularity`; //auto placer element name + startColor = elements[elementOfSingularity].color; + if(typeof(elements[elementOfSingularity].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError) + if(elements[elementOfSingularity].excludeRandom) { //it it's true + //console.log("nyet " + elementOfSingularity); + randomExcl = 1; //the singularity is excluded + } else { + //console.log("allow " + elementOfSingularity); + randomExcl = 0; + }; + }; + if(typeof(elements[elementOfSingularity].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError) + if(elements[elementOfSingularity].nocheer) { //it it's true + //console.log("nyet " + elementOfSingularity); + isNocheer = 1; //the singularity is excluded + } else { + //console.log("allow " + elementOfSingularity); + isNocheer = 0; + }; + }; + }; + //Color gen + if(Array.isArray(startColor)) { //Average arrays, make colors rgb() + startColor = averageRgbPrefixedColorArray(startColor); + } else { + startColor = rgbHexCatcher(startColor); + }; + //console.log(`rgbStringToObject(${startColor}) from more_singularities.js`) + var newColorObjectArray = []; + var newColorArray = []; + for(i = 0; i < singularityColorTemplate.length; i++) { + var newSingularityColorlet = singularityColorTemplate[i]; + var newColor = multiplyColors(startColor,newSingularityColorlet); + var newColorJSON = multiplyColors(startColor,newSingularityColorlet,"json"); + newColorArray.push(newColor); + newColorObjectArray.push(newColorJSON); + }; + //End color gen + //The singularity + //console.log(elementOfSingularity); + var firstInfo, firstTemp; + if(Array.isArray(elementOfSingularity)) { + firstInfo = elements[elementOfSingularity[0]]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + } else { + firstInfo = elements[elementOfSingularity]; + firstTemp = airTemp; + if(typeof(firstInfo.temp) !== "undefined") { + firstTemp = firstInfo.temp; + }; + }; + var finalDensity = 0; + if(Array.isArray(elementOfSingularity)) { + for(i = 0; i < elementOfSingularity.length; i++) { + info = elements[elementOfSingularity[i]]; + finalDensity += (info.density || 1000) * singularityNumber; + }; + finalDensity /= elementOfSingularity.length; + } else { + info = elements[elementOfSingularity]; + finalDensity = (info.density || 1000) * singularityNumber; + }; + elementOfSingularity = tryJoin(elementOfSingularity,","); + //console.log(elementOfSingularity); + var returns = []; + if(!elementExists(singularityName)) { + elements[singularityName] = { + color: newColorArray, + colorObject: newColorObjectArray, + behavior: behaviors.POWDER_OLD, + category: "singularities", + temp: firstTemp, + hardness: 0.995, + singularityNumber: null, + originalElementDisplay: elements[elementOfSingularity.replaceAll(",","_")]?.originalElementKey ?? elementOfSingularity.split(","), + originalElementKey: elements[elementOfSingularity.replaceAll(",","_")]?.originalElementKey ?? elementOfSingularity.split(","), + state: "solid", + density: finalDensity, + }; + var newInfo = elements[singularityName]; + elements[singularityName].originalElementDisplay = newInfo.originalElementKey.map(x => elements[x].name ?? x); + if(elements[singularityName].originalElementDisplay.length == 1) { elements[singularityName].originalElementDisplay = elements[singularityName].originalElementDisplay[0] }; + if(singularityElements[0] instanceof Array) { + elements[singularityName].singularityNumber = 1; + } else { + if(typeof(elements[singularityElements[0]].singularityNumber) === "undefined") { + elements[singularityName].singularityNumber = 1 + } else { + elements[singularityName].singularityNumber = elements[singularityElements[0]].singularityNumber + 1; + }; + }; + var num = newInfo.singularityNumber ?? NaN; + var descTypeString = (num == 1 ? "singularity" : `${num.toString()}-singularity`); + var descNumberString = (num < 4 ? (10 ** (num * 4)).toLocaleString("en-US") : `10${num * 4}`); + var descElementString = tryJoin((newInfo.originalElementDisplay ?? "[Original element could not be determined]"),", "); + elements[singularityName].desc = `A ${descTypeString} normally made of ${descNumberString} pixels of ${descElementString}.`.replaceAll("NaN","[Quantity could not be determined]"); + if(singularityName.includes("haseulite") && !singularityName.includes("haseulite_vent")) { + elements[singularityName].tick = function(pixel) { haseulitoidSingularityTick(pixel) }; + haseuliteSpreadWhitelist.push(singularityName); + }; + if(typeof(eLists) === "undefined") { + eLists = {}; + }; + if(typeof(eLists.SINGULARITY) === "undefined") { + eLists.SINGULARITY = []; + }; + eLists.SINGULARITY.push(singularityName); + if(!randomExcl) { + if(typeof(singularityChoices) === "undefined") { + singularityChoices = [] + }; + if(!singularityChoices.includes(singularityName)) { + singularityChoices.push(singularityName); + }; + } + if(isNocheer) { + elements[singularityName].nocheer = true; + } + if(singularityIncludeRandom) { + randomExcl ? elements[singularityName].excludeRandom = true : elements[singularityName].excludeRandom = false; + } else { + elements[singularityName].excludeRandom = true; + }; + if(isAfterScriptLoading) { + elementCount++; //increment for new singularity element + if (settings.cheerful && elements[singularityName].nocheer) { + elements[singularityName].hidden = true; + hiddenCount++; + } else { + createElementButton(singularityName); + }; + elements[singularityName].id = nextid++; + document.getElementById("extraInfo").innerHTML = canvasfooterTemplate() + }; + }; + count++; + returns.push(singularityName); + }; + return returns; + }; + elements.neutronium_compressor = { + color: "#e7e7ee", + properties: { + range: 4, + outputOffset: [0, 5], + absorbed: {} + }, + category: "machines", + behavior: behaviors.WALL, + state: "solid", + density: 54000, + hardness: 1, + excludeRandom: true, + tick: function(pixel) { + if(pixel.range == undefined) { + pixel.range = 4; + }; + if(pixel.outputOffset == undefined) { + pixel.outputOffset = [0, 5]; + }; + if(pixel.absorbed == undefined) { + pixel.absorbed = {}; + }; + for(i = -(pixel.range); i <= (pixel.range); i++) { + for(j = -(pixel.range); j <= (pixel.range); j++) { + if(i == 0 & j == 0) { continue }; + var fX = pixel.x+i; + var fY = pixel.y+j; + if(!isEmpty(fX,fY,true)) { + var newPixel = pixelMap[fX][fY]; + var newElement = newPixel.element; + if(newElement !== "neutronium_compressor") { + //Jinsoulite handling + if(typeof(jinsouliteSpreadWhitelist) !== "undefined") { + if(jinsouliteSpreadWhitelist.includes(newPixel.element)) { + if(newPixel.value > 0) { //if jinsoulitoid and value is positive + //if compressor has no recorded water, initialize to zero + if(typeof(pixel.absorbed.water) === "undefined") { pixel.absorbed.water = 0 }; + //add jinsoulite's water to compressor water + pixel.absorbed.water += newPixel.value; + }; + }; + }; + //Alkahest handling + if(newPixel.element === "alkahest") { + //get properties that are actually elements + var elementEntries = Object.keys(newPixel).filter(function(key) { return elementExists(key) }); + for(i = 0; i < elementEntries.length; i++) { + //iterate through element properties + //store elemname for readability + var key = elementEntries[i]; + //store amount for readability + var value = newPixel[key]; + //initialize nonexistent names + if(typeof(pixel.absorbed[key]) === "undefined") { + pixel.absorbed[key] = 0; + }; + //add amounts + pixel.absorbed[key] += value; + }; + }; + if(typeof(pixel.absorbed[newElement]) === "undefined") { + pixel.absorbed[newElement] = 0; + }; + pixel.absorbed[newElement]++; + deletePixel(fX,fY); + }; + }; + }; + }; + var outputPos = {x: pixel.x+pixel.outputOffset[0], y: pixel.y+pixel.outputOffset[1]}; + if(Object.keys(pixel.absorbed).length > 0) { + for(elementName in pixel.absorbed) { + if(pixel.absorbed[elementName] >= singularityNumber) { + if(isEmpty(outputPos.x,outputPos.y,false)) { + if(!elementExists(`${elementName}_singularity`)) { + generateSingularity(elementName,true); + }; + createPixel(`${elementName}_singularity`,outputPos.x,outputPos.y); + pixel.absorbed[elementName] -= singularityNumber; + } else { + break; + }; + }; + }; + }; + /* + for(q = -2; q <= 2; q++) { + for(q2 = -2; q2 <= 2; q2++) { + if(Object.keys(pixel.absorbed).length > 0) { + for(elementName in pixel.absorbed) { + if(pixel.absorbed[elementName] >= singularityNumber) { + if(isEmpty(outputPos.x+q,outputPos.y+q2,false)) { + if(!elementExists(`${elementName}_singularity`)) { + generateSingularity(elementName,true); + }; + createPixel(`${elementName}_singularity`,outputPos.x+q,outputPos.y+q2); + pixel.absorbed[elementName] -= singularityNumber; + } else { + break; + }; + }; + }; + }; + }; + }; + */ + }, + }; + runAfterAutogen(function() { + if(generateSingularities) { + singularityArray = Object.keys(elements); + generateSingularity(singularityArray,false); + }; + }); + if(typeof(singularityChoices) === "undefined") { + singularityChoices = []; + }; + elements.spawn_random_singularity = { + color: ["#3e5f8a","#a334ec","#ea96f9","#a6ecf6","#70ebc8","#d9286b","#7eed91","#a18b30"], + behavior: behaviors.WALL, + category: "special", + excludeRandom: true, + tick: function(pixel) { + singularityChoices.length == 0 ? deletePixel(pixel.x,pixel.y) : changePixel(pixel,singularityChoices[Math.floor(Math.random() * singularityChoices.length)]); + }, + }; + //POST-LOADING ELEMENT GENERATION ## + var lategenOptions = ["creeper","spout","fairy","cloud","bomb","singularity"]; + lgoDisplayString = lategenOptions.join(", "); + document.addEventListener("keydown", function(e) { //prop prompt listener + // , = propPrompt() + if (e.keyCode == 71) { //G + if(shiftDown) { generatorPrompt() }; + }; + }); + function generatorPrompt() { + var type = prompt(`Enter what kind of thing you want to generate + Valid options: ${lgoDisplayString}`); + var elements, typePlural; + if(type === null) { + return false; + }; + if(!lategenOptions.includes(type)) { + alert("Type is not valid!"); + return false; + } else { + if(type === "fairy") { + typePlural = "fairies" + } else { + typePlural = type + "s"; + }; + elements = prompt(`Enter the element(s) you want to generate ${typePlural} for. + Elements are separated by commas; to use a combination of elements, the elements are separated by plus signs (like "gold_coin+diamond").`); + elements = parseForLateGenerationParameter(elements); + try { + var amount = 0; + switch(type) { + case "creeper": + amount += generateCreeper(elements,true).length; + break; + case "spout": + amount += generateSpout(elements,true).length; + break; + case "fairy": + amount += generateFairy(elements,true).length; + break; + case "cloud": + var number = prompt(`Enter a cloud number between 0 and 5 (default: 0) + A higher number means a rainier cloud.`); + if(number !== "*") { isNaN(parseFloat(number)) ? number = 0 : number = parseFloat(number) }; + amount += generateCloud(elements,number,true).length; + break; + case "singularity": + amount += generateSingularity(elements,true).length; + break; + case "bomb": + var number = prompt(`Enter a bomb number (default: 1) + 1 corresponds to radius 10, 2 corresponds to radius 15, etc.`); + isNaN(parseFloat(number)) ? number = 1 : number = parseFloat(number); + amount += generateBomb(elements,true,number).length; + break; + default: + alert("An invalid type made it past the if statement. You shouldn't ever see this error."); + throw new Error("An invalid type made it through the if statement."); + }; + alert(`Generated ${amount} ${amount == 1 ? "element" : "elements"}`); + } catch (error) { + var errorString = error.toString(); + var errorText = ""; + if(errorString.includes("Cannot read properties of undefined")) { + errorText += "\r\n(This is most likely from a nonexistent or misspelled element)"; + }; + alert("There was an error!\r\n" + error.toString() + errorText); + throw error; //for console + }; + }; + }; + elements.generator_prompt = { + color: [_cc.b.h,"#666666","#886622","#558800"], + behavior: behaviors.SELFDELETE, + desc: "Click here or press Shift+G to open the generator prompt.", + category:"special", + }; + function parseForLateGenerationParameter(input) { + if(input == null) { return }; + if(input.startsWith("*")) { + var elemList = Object.keys(elements); + input = input.toLowerCase().substring(1); + if(input === "all") { + return elemList; + } else { + input = input.split(" "); //'*nocreeper nofairy' to ['nocreeper' 'nofairy'] + //include no auto elements + if(input.includes("noauto")) { + return Object.keys(elements).filter(function(name) { return elements[name].autoType === undefined }); + }; + var filteredList = elemList; + //include only auto elements + if(input.includes("auto")) { + filteredList = filteredList.filter(function(name) { return elements[name].autoType !== undefined }); + //console.log("limit to autogens", filteredList.length); + }; + //exclude fairies + if(input.includes("nofairy") || input.includes("nofairies")) { + filteredList = filteredList.filter(function(name) { return elements[name].autoType !== "fairy" }); + //console.log("remove fairies", filteredList.length); + }; + //exclude spouts + if(input.includes("nospout") || input.includes("nospouts")) { + filteredList = filteredList.filter(function(name) { return elements[name].autoType !== "spout" }); + //console.log("remove spouts", filteredList.length); + }; + //exclude creepers + if(input.includes("nocreeper") || input.includes("nocreepers")) { + filteredList = filteredList.filter(function(name) { return elements[name].autoType !== "creeper" }); + //console.log("remove creepers", filteredList.length); + }; + //exclude clouds + if(input.includes("nocloud") || input.includes("noclouds")) { + filteredList = filteredList.filter(function(name) { return elements[name].autoType !== "cloud" }); + //console.log("remove clouds", filteredList.length); + }; + //exclude bombs + if(input.includes("nobomb") || input.includes("nobombs")) { + filteredList = filteredList.filter(function(name) { return elements[name].autoType !== "bomb" }); + //console.log("remove bombs", filteredList.length); + }; + //console.log(input); + return filteredList; + }; + }; + if(typeof(input) === "string") { //it should be an array, so string check + input = input.replace(/ /g,"_"); + //console.log("String detected"); + if(input.includes(",")) { //comma-separated string? + //console.log("Splitting string to array"); + input = input.split(","); //,SS to array + } else { + //console.log("Wrapping string in array"); + input = [input]; //single string to array + }; + }; + for(i = 0; i < input.length; i++) { + input[i] = input[i].replace(/ /g,"_"); + if(input[i].includes("+")) { + input[i] = input[i].split("+") + }; + }; + return input; + }; + //WEATHER MACHINE ## + function pixelRain(element,density=0.1,rainRelativeBottom=0.35,rainRelativeTop=0) { + for(i = 1; i < width; i++) { + for(j = Math.round(height * rainRelativeTop); j < Math.round(height * rainRelativeBottom); j++) { + if(Math.random() < density && isEmpty(i,j)) { + while(element instanceof Array) { element = randomChoice(element) }; + createPixel(element,i,j) + } + } + } + }; + generateCloud("water",3,false); + generateCloud("blood",0,false); + generateCloud("snow",3,false); + generateCloud("sand",1,false); + _weatherElems = ["cloud", "rain_cloud", "heaviest_water_cloud", "blood_cloud", "snow_cloud", "heaviest_snow_cloud", "hail_cloud", "hail_cloud", "fireball", "fire_cloud", "magma_cloud", "heavy_sand_cloud", "electric", "lightning"]; + elements.weather_controller = { + color: "#ebdf91", + behavior: [ + "XX|M2 AND SA|XX", + "SA|XX|SA", + "XX|M1|XX" + ], + breakInto: ["steel_scrap","iron_scrap","copper_scrap","gold_scrap","cloud","emerald","magic"], + tempHigh: 50000, + stateHigh: ["molten_steel","molten_iron","molten_copper","molten_gold","hydrogen","oxygen","molten_aluminum","silica_gas","magic"], + tick: function(pixel) { + if(!pixel.charge) { return }; + if(pixel.charge) { + if(isEmpty(pixel.x,pixel.y+1,true)) { return }; + var pixelUnder = pixelMap[pixel.x]?.[pixel.y+1]; + if(!pixelUnder) { return }; + switch(pixelUnder.element) { + case "cloud": + pixelRain("cloud",0.1,0.3); + break; + case "steam": + pixelRain("cloud",0.2,0.35); + break; + case "rain_cloud": + pixelRain("rain_cloud",0.2,0.25); + break; + case "water": + pixelRain("rain_cloud",0.4,0.3); + break; + case "ketchup": + pixelRain("ketchup_cloud",0.3,0.35); + break; + case "jinsoulite_powder": + case "jinsoulite_gas": + case "molten_jinsoulite": + case "jinsoulite": + pixelRain("heaviest_water_cloud",0.8,0.35); + break; + case "blood": + pixelRain("blood_cloud",0.2,0.25); + break; + case "snow": + pixelRain("snow_cloud",0.2,0.25); + break; + case "packed_snow": + pixelRain("snow_cloud",0.4,0.3); + break; + case "haseulite": + case "haseulite_powder": + case "molten_haseulite": + case "haseulite_gas": + pixelRain("heaviest_snow_cloud",0.5,0.35); + pixelRain("diamond",0.002,0.35); + pixelRain("hail_cloud",0.05,0.2); + break; + case "liquid_nitrogen": + pixelRain("liquid_nitrogen", 0.3,1); + pixelRain("liquid_oxygen", 0.1,1); + pixelRain("liquid_argon", 0.004,1); + currentPixels.forEach(pixel => pixel.temp = Math.min(pixel.temp,elements.liquid_nitrogen.tempLow - 5)); + break; + case "ice": + pixelRain("hail_cloud",0.2,0.3); + break; + case "fire": + pixelRain("fire_cloud",0.15,0.3); + break; + case "magma": + pixelRain("magma_cloud",0.2,0.3); + break; + case "sand": + pixelRain("heavy_sand_cloud",0.4,1,0.2); + pixelRain("sand",0.05,1,0.2); + break; + case "battery": + pixelRain("electric",0.3,0.35); + pixelRain("lightning",0.002,0.2); + break; + case "sun": + case "stellar_plasma": + case "liquid_stellar_plasma": + case "plasma": + settings.bg = "#93c3e1" + break; + case "moonrock": + settings.bg = _cc.b.h; + break; + case "oxygen": + for(var i in "six ") { + currentPixels.forEach(function(pixel) { + if(_weatherElems.includes(pixel.element)) { + deletePixel(pixel.x,pixel.y); + return + } + }); + }; + currentPixels.forEach(function(pixel) { + var data = elements[pixel.element]; + var tl = data.tempLow; + var th = data.tempHigh; + var noTL = (typeof(tl) == "undefined"); + var noTH = (typeof(th) == "undefined"); + if(noTL && noTH) { + pixel.temp = airTemp ?? 20 + } else if(noTL && !noTH) { + if(th < airTemp) { + pixel.temp = th - 10; + } else { + pixel.temp = airTemp + } + } else if(!noTL && noTH) { + if(tl > airTemp) { + if(tl == Infinity) { + pixel.temp = airTemp + } else { + pixel.temp = tl + 10 + } + } else { + pixel.temp = airTemp + } + }; + return + }); + break; + }; + if(pixelUnder) { deletePixel(pixelUnder.x,pixelUnder.y) }; + delete pixel.charge; + pixel.chargeCD = 4; + return true; + } + }, + conduct: 1, + category: "machines", + hardness: 0.6 + }; + console.log("7/8 loaded"); //it was inside the weather controller code + //KETCUP ## + elements.ketcup = { + color: "#ab2513", + behavior: behaviors.LIQUID, + reactions: { + "rust": { elem2:"iron", chance:0.01 }, + "oxidized_copper": { elem2:"copper", chance:0.01 }, + }, + viscosity: 50000, + tempHigh: 260, + stateHigh: ["vinegar","steam","salt","sugar"], + category:"liquids", + state: "liquid", + density: 1235, + stain: 0.05, + isFood: true + }; + elements.ruphire = { + color: ["#7C319B", "#BC4F80", "#692287", "#B13B77", "#772A94"], + tempHigh: 2040, + behavior: behaviors.POWDER, + category: "powders", + state: "solid", + density: 3980, + hardness: 0.9, + }; + standaloneBrokenFormMaker("ruphire","shard",true,"powders","auto","auto","molten_ruphire",["alumina","alumina","alumina","alumina","alumina","alumina","alumina","alumina","alumina","iron_scrap","titanium_scrap","chromium_scrap","chromium_scrap"]).hidden = true; + elements.molten_ruby ??= {}; + elements.molten_ruby.reactions ??= {}; + elements.molten_sapphire ??= {}; + elements.molten_sapphire.reactions ??= {}; + elements.molten_ruby.reactions.molten_sapphire = { + elem1: "molten_ruphire", + elem2: "molten_ruphire" + }; //they don't make garnet :'( + elements.molten_sapphire.reactions.molten_ruby = elements.molten_ruby.reactions.molten_sapphire; + //REPLACER TOOL ## + replaceFrom = "rock"; + replaceTo = "sand"; + document.addEventListener("keydown", function(e) { //replace prompt listener + // r = replaceElementPrompt() + if (e.keyCode == 222) { + e.preventDefault(); + replaceElementPrompt(); + } + }); + function replaceElementPrompt() { + var fromElement = prompt("Enter the element you want to change"); + // replace spaces with underscores + if(fromElement == null) { return }; + fromElement = fromElement.replace(/ /g, "_"); + fromElementS = mostSimilarElement(fromElement); + if (fromElementS === null || fromElementS === undefined || fromElementS === "") { + alert("Element \"" + fromElement + "\" not found! Defaulting to rock."); + fromElementS = "rock"; + }; + var toElement = prompt("Enter what you want to replace \"" + fromElementS + "\" with"); + // replace spaces with underscores + toElement = toElement.replace(/ /g, "_"); + toElementS = mostSimilarElement(toElement); + if (toElementS === null || toElementS === undefined || toElementS === "") { + alert("Element \"" + toElement + "\" not found! Defaulting to sand."); + toElementS = "sand"; + }; + replaceFrom = fromElementS; + replaceTo = toElementS; + updateReplaceDescriptions(); + } + function updateReplaceDescriptions() { + elements.replace.desc = "Changes pixels of a specified type to another specified type.
Currently replacing \"" + replaceFrom + "\" with \"" + replaceTo + "\".
Press [\"] or click here to open the replace prompt."; + elements.alt_replace.desc = "Changes pixels of a specified type to another specified type, but keeping their non-element-based properties.
Currently replacing \"" + replaceFrom + "\" with \"" + replaceTo + "\".
Press [\"] or click here to open the replace prompt."; + elements.alt_alt_replace.desc = "Changes pixels of a specified type to another specified type, but keeping their non-element-based properties except for color.
Currently replacing \"" + replaceFrom + "\" with \"" + replaceTo + "\".
Press [\"] or click here to open the replace prompt."; + }; + elements.replace = { + color: ["#ff0000", "#ff0000", "#ff0000", "#7f00ff", "#0000ff", "#0000ff", "#0000ff"], + tool: function(pixel) { + if(pixel.element === replaceFrom) { + changePixel(pixel,replaceTo,true); + }; + }, + category: "tools", + desc: "Changes pixels of a specified type to another specified type.
Currently replacing \"" + replaceFrom + "\" with \"" + replaceTo + "\".
Press [\"] or click here to open the replace prompt.", + }; + elements.alt_replace = { + color: ["#ffff00", "#ffff00", "#ffff00", "#cf7f4f", "#ff00ff", "#ff00ff", "#ff00ff"], + tool: function(pixel) { + if(pixel.element === replaceFrom) { + pixel.element = replaceTo; + }; + }, + category: "tools", + desc: "Changes pixels of a specified type to another specified type, but keeping their non-element-based properties.
Currently replacing \"" + replaceFrom + "\" with \"" + replaceTo + "\".
Press [\"] or click here to open the replace prompt.", + hidden: true, + }; + elements.alt_alt_replace = { + color: ["#00ff00", "#00ff00", "#00ff00", "#cfcf00", "#ff0000", "#ff0000", "#ff0000"], + tool: function(pixel) { + if(pixel.element === replaceFrom) { + pixel.element = replaceTo; + pixel.color = pixelColorPick(pixel); + }; + }, + category: "tools", + desc: "Changes pixels of a specified type to another specified type, but keeping their non-element-based properties except for color.
Currently replacing \"" + replaceFrom + "\" with \"" + replaceTo + "\".
Press [\"] or click here to open the replace prompt.", + hidden: true, + }; + //OLD PROP AND NUMBER CHANGER TOOLS ## + propProperty = "element"; + propValue = "sand"; + propType = "string"; + numberAdjusterProperty = "temp"; + numberAdjusterValue = 1; + numberAdjusterMode = "add"; + numberAdjusterVerb = "adding"; + numberAdjusterPreposition = "to"; + numberAdjusterReverseOrder = false; + document.addEventListener("keydown", function(e) { //prop prompt listener + // , = propPrompt() + if (e.keyCode == 188) { + //e.preventDefault(); + shiftDown ? numberAdjusterPrompt() : propPrompt(); + }; + }); + function propPrompt() { + propProperty = prompt("Enter the property you want to set"); + propValue = prompt("Enter the value you want to set to"); + //special check: element + if(propProperty === "element") { + //empty string + if(propValue === "") { + alert("No element was specified!"); + return false; + }; + // replace spaces with underscores + propValue = propValue.replace(/ /g, "_"); + var propValueS = mostSimilarElement(propValue); + if (propValueS === null || propValueS === undefined) { + alert("Element \"" + value + "\" not found! Defaulting to sand."); + propValue = "sand"; + } else { + propValue = propValueS; + }; + }; + //special check: color + if(propProperty === "color") { + //empty string + if(propValue === "") { + alert("No color was specified!"); + return false; + }; + var splitValue = propValue.split(","); + if(!propValue.startsWith("rgb(")) { //if not RGB + if(propValue.startsWith("hsl(")) { //if HSL + if(!(splitValue[1].endsWith('%')) || !(splitValue[2].endsWith('%)'))) { //if missing percent symbols + alert(colorInvalidError); + return false; + }; + } else { //if not RGB and not HSL + alert(colorInvalidError); + return false; + }; + }; + if(propValue.split(",").length !== 3) { //if too short or long + alert(colorInvalidError); + return false; + } + if(propValue.startsWith("rgb(")) { //if RGB + var checkedColorObject = rgbStringToUnvalidatedObject(propValue); //RGB NaN checking + if(isNaN(checkedColorObject.r) || isNaN(checkedColorObject.g) || isNaN(checkedColorObject.b)) { + //console.log(checkedColorObject); + alert("One or more color values are invalid!"); + return false; + }; + } else if(propValue.startsWith("hsl(")) { //if HSL + var checkedColorObject = hslStringToUnvalidatedObject(propValue); //HSL NaN checking + if(isNaN(checkedColorObject.h) || isNaN(checkedColorObject.s) || isNaN(checkedColorObject.l)) { + //console.log(checkedColorObject); + alert("One or more color values are invalid!"); + return false; + }; + } else { //if neither + alert(colorInvalidError); + return false; + }; + }; + //special check: x + if(propProperty === "x") { + //empty string + if(!propValue.isInteger) { + alert("X values must be integers!"); + }; + }; + if(propProperty == null) { return }; + if(defaultNumberTypeValues.includes(propProperty.toLowerCase())) { + propType = "number"; + } else if(defaultBooleanTypeValues.includes(propProperty.toLowerCase())) { + propType = "boolean"; + } else if(defaultStringTypeValues.includes(propProperty.toLowerCase())) { + propType = "string"; + } else if(defaultArrayTypeValues.includes(propProperty.toLowerCase())) { + propType = "array"; + } else { + propType = prompt("Enter the type of the value"); + if(stringSynonyms.includes(propType)) { + propType = "string"; + } else if(numberSynonyms.includes(propType)) { + propType = "number"; //Infinity (case-sensitively) is a *number*. + } else if(booleanSynonyms.includes(propType)) { + propType = "boolean"; + } else if(objectSynonyms.includes(propType)) { + propType = "object"; //null (case-sensitively) is an *object*. + } else if(arraySynonyms.includes(propType)) { + propType = "array"; //offset coords use arrays a lot + }; + }; + //Conversion + if(propType === "number") { + propValue = parseFloat(propValue); + if(isNaN(propValue)) { + alert("Value is not a number!"); + return false; + }; + } else if(propType === "boolean") { + if(synonymsOfTrue.includes(propValue.toLowerCase())) { + propValue = true; + } else if(synonymsOfFalse.includes(propValue.toLowerCase())) { + propValue = false; + } else { + alert("Unrecognized boolean value: " + propValue + "."); + return false; + }; + } else if(propType === "object") { + try { + propValue = JSON.parse(propValue); + } catch (error) { + alert("JSON is invalid! Note that it requires quotes around keys as well as those curly {} parentheses."); + return false; + }; + } else if(propType === "array") { + array = propValue.split(","); + for(i = 0; i < array.length; i++) { + if(array[i].startsWith("s")) { //String + array[i] = array[i].substring(1); + } else if(array[i].startsWith("n")) { //Number + array[i] = array[i].substring(1); + if(isNaN(parseFloat(array[i]))) { + alert(array[i] + " is not a number!"); + return false; + }; + array[i] = parseFloat(array[i]); + } else if(array[i].startsWith("o")) { //Object + array[i] = array[i].substring(1); + try { + array[i] = JSON.parse(array[i]); + } catch (error) { + alert(array[i] + " is not valid JSON!"); + return false; + }; + } else if(array[i].startsWith("b")) { //Boolean + array[i] = array[i].substring(1); + if(synonymsOfTrue.includes(array[i].toLowerCase())) { + array[i] = true; + } else if(synonymsOfFalse.includes(array[i].toLowerCase())) { + array[i] = false; + } else { + alert("Unrecognized boolean value: " + array[i] + "."); + return false; + }; + } else { + alert(array[i] + ' must start with "s" for a string, "n" for a number, "o" for an object, or "b" for a boolean.'); + return false; + }; + }; + propValue = array; + } else if(propType !== "string") { + alert("Unrecognized or unsupported type!"); + return false; + }; + updatePropDescription(); + currentElement = "old_prop"; + }; + elements.old_prop = { + color: "#ff7f00", + tool: function(pixel) { + if(propProperty === "element") { + pixel[propProperty] = propValue; + pixel.temp = (elements[propValue].temp || pixel.temp); + } else { + pixel[propProperty] = propValue; + }; + pixelTempCheck(pixel); + }, + category: "tools", + desc: `Sets properties of pixels.
Currently setting ${propProperty} to ${propValue} (${propType}).
Press [,] or click here to open the property tool prompt.`, + }; + function updatePropDescription() { + elements.old_prop.desc = `Sets properties of pixels.
Currently setting ${propProperty} to ${propValue} (${propType}).
Press [,] or click here to open the property tool prompt.`; + }; + function numberAdjusterPrompt() { + var oldProperty = numberAdjusterProperty; + if(oldProperty === null) { + oldProperty = "temp"; + }; + numberAdjusterProperty = prompt("Enter the property you want to change"); + if(numberAdjusterProperty === null) { + numberAdjusterProperty = oldProperty; + return false; + }; + numberAdjusterValue = prompt("Enter the value you want to use"); + numberAdjusterMode = prompt("Enter the operation you want to use"); + //property check + if(numberAdjusterProperty === "") { + alert("No property was specified! Defaulting to temp."); + numberAdjusterProperty = "temp"; + }; + //value check + if(isNaN(parseFloat(numberAdjusterValue))) { + if(numberAdjusterValue === "" || numberAdjusterValue === null) { + //console.log("Null value path"); + alert("No value was specified! Defaulting to 1"); + numberAdjusterValue = 1; + //console.log(numberAdjusterValue); + } else { + //console.log("NaN value path"); + alert("Invalid value! The value must be a number (defaulting to 1)"); + numberAdjusterValue = 1; + //console.log(numberAdjusterValue); + }; + }; + numberAdjusterValue = parseFloat(numberAdjusterValue); + //console.log("Value: " + numberAdjusterValue); + //mode check + if(numberAdjusterMode === null) { + alert("No operation was specified! Defaulting to add."); + numberAdjusterMode = "add"; + }; + numberAdjusterMode = numberAdjusterMode.toLowerCase(); + var opNames = ["+", "add", "addition", "plus", "increase", "increment", "-", "subtract", "subtraction", "minus", "take away", "takeaway", "decrease", "decrement", "*", "x", "×", "multiply", "multiplication", "times", "by", "/", "÷", "divide", "division", "divided by", "%", "mod", "modulo", "modulus", "modulo by", "=", "set", "equals", "assign", "assignment", ">", ">=", "min", "minimum", "<", "<=", "max", "maximum", "^", "**", "exp", "exponent", "exponentiate", "raise", "raise to", "raised to"]; + switch(numberAdjusterMode) { + case "+": + case "add": + case "addition": + case "plus": + case "increase": + case "increment": + numberAdjusterVerb = "adding"; + numberAdjusterPreposition = "to"; + numberAdjusterReverseOrder = false; + break; + case "-": + case "subtract": + case "subtraction": + case "minus": + case "take away": + case "takeaway": + case "decrease": + case "decrement": + numberAdjusterVerb = "subtracting"; + numberAdjusterPreposition = "from"; + numberAdjusterReverseOrder = false; + break; + case "*": + case "x": + case "×": + case "multiply": + case "multiplication": + case "times": + case "by": + numberAdjusterVerb = "multiplying"; + numberAdjusterPreposition = "by"; + numberAdjusterReverseOrder = true; + break; + case "/": + case "÷": + case "divide": + case "division": + case "divided by": + numberAdjusterVerb = "dividing"; + numberAdjusterPreposition = "by"; + numberAdjusterReverseOrder = true; + break; + case "%": + case "mod": + case "modulo": + case "modulus": + case "modulo by": + numberAdjusterVerb = "reducing"; + numberAdjusterPreposition = "modulo"; + numberAdjusterReverseOrder = true; + break; + case "=": + case "set": + case "equals": + case "assign": + case "assignment": + numberAdjusterVerb = "setting"; + numberAdjusterPreposition = "to"; + numberAdjusterReverseOrder = true; + break; + case ">": //lower-bounds the color + case ">=": + case "min": + case "minimum": + numberAdjusterVerb = "lower-bounding"; + numberAdjusterPreposition = "to"; + numberAdjusterReverseOrder = true; + break; + case "<": + case "<=": + case "max": //upper-bounds the color + case "maximum": + numberAdjusterVerb = "limiting"; + numberAdjusterPreposition = "to"; + numberAdjusterReverseOrder = true; + break; + case "^": + case "**": + case "exp": + case "exponent": + case "exponentiate": + case "raise": + case "raise to": + case "raised to": + numberAdjusterVerb = "raising"; + numberAdjusterPreposition = "to"; + numberAdjusterReverseOrder = true; + break; + default: + alert(`Invalid operation (defaulting to "add")!`); + numberAdjusterMode = "add"; + numberAdjusterVerb = "adding"; + numberAdjusterPreposition = "to"; + numberAdjusterReverseOrder = false; + break; + }; + updateNumberAdjusterDescription(); + currentElement = "number_adjuster"; + }; + elements.number_adjuster = { + color: "#7fff00", + tool: function(pixel) { + if(typeof(pixel[numberAdjusterProperty]) === "undefined") { + pixel[numberAdjusterProperty] = 0; + }; + if(typeof(pixel[numberAdjusterProperty]) === "number") { + switch(numberAdjusterMode.toLowerCase()) { + case "+": + case "add": + case "addition": + case "plus": + case "increase": + case "increment": + pixel[numberAdjusterProperty] += numberAdjusterValue; + break; + case "-": + case "subtract": + case "subtraction": + case "minus": + case "take away": + case "takeaway": + case "decrease": + case "decrement": + pixel[numberAdjusterProperty] -= numberAdjusterValue; + break; + case "*": + case "x": + case "×": + case "multiply": + case "multiplication": + case "times": + case "by": + pixel[numberAdjusterProperty] *= numberAdjusterValue; + break; + case "/": + case "÷": + case "divide": + case "division": + case "divided by": + pixel[numberAdjusterProperty] /= numberAdjusterValue; + break; + case "%": + case "mod": + case "modulo": + case "modulus": + case "modulo by": + pixel[numberAdjusterProperty] %= numberAdjusterValue; + break; + case "=": + case "set": + case "equals": + case "assign": + case "assignment": + pixel[numberAdjusterProperty] = numberAdjusterValue; + break; + case ">": //lower-bounds the color + case ">=": + case "min": + case "minimum": + pixel[numberAdjusterProperty] = Math.max(numberAdjusterValue,pixel[numberAdjusterProperty]); + break; + case "<": + case "<=": + case "max": //upper-bounds the color + case "maximum": + pixel[numberAdjusterProperty] = Math.min(numberAdjusterValue,pixel[numberAdjusterProperty]); + break; + case "^": + case "**": + case "exp": + case "exponent": + case "exponentiate": + case "raise": + case "raise to": + case "raised to": + pixel[numberAdjusterProperty] = pixel[numberAdjusterProperty] ** numberAdjusterValue; + break; + default: + pixel[numberAdjusterProperty] += numberAdjusterValue; + }; + pixelTempCheck(pixel); + }; + }, + category: "tools", + desc: `Changes properties of pixels.
Currently ${numberAdjusterVerb} ${numberAdjusterValue} ${numberAdjusterPreposition} ${numberAdjusterProperty}.
Press [Shift+,] or click here to open the adjuster tool prompt.`, + }; + function updateNumberAdjusterDescription() { + elements.number_adjuster.desc = numberAdjusterReverseOrder ? `Changes numeric properties of pixels.
Currently ${numberAdjusterVerb} ${numberAdjusterProperty} ${numberAdjusterPreposition} ${numberAdjusterValue}.
Press [Shift+,] or click here to open the adjuster tool prompt.` : `Changes numeric properties of pixels.
Currently ${numberAdjusterVerb} ${numberAdjusterValue} ${numberAdjusterPreposition} ${numberAdjusterProperty}.
Press [Shift+,] or click here to open the adjuster tool prompt.`; + }; + //TOOL BEHAVIORS ## + mooreDonutCoords = [[-1, -1], [0, -1], [1, -1], [-1, 0], [1, 0], [-1, 1], [0, 1], [1, 1]]; //Moore neighborhood (includes corners) minus center, as opposed to the von Neumann neighborhood which is the + shape. + elements.lookup.tick = function(pixel) { + //console.log(`%%% Tick ${pixelTicks} %%%`); + //console.log(`Storing coordinates`); + var pX = pixel.x; + var pY = pixel.y; + //console.log(`Position (${pX},${pY})`); + //console.log(`Iterating`); + var neighborArray = []; + for(i = 0; i < mooreDonutCoords.length; i++) { + //console.log(`i: ${i}`); + //console.log(`Initialized array`); + var coord = mooreDonutCoords[i]; + //console.log(`Offset pair: ${coord}`); + var oX = coord[0]; + var oY = coord[1]; + //console.log(`Stored offset pair`); + var nX = pX+oX; + var nY = pY+oY; + //console.log(`Final coordinates: (${nX},${nY}) (index ${i})`); + if(isEmpty(nX,nY,true)) { + //console.log(`Skipping empty pixel (${nX},${nY})`); + continue; + } else { + //console.log(`Found pixel at (${nX},${nY})`); + var newPixel = pixelMap[nX][nY]; + //console.log(`Pixel stored`); + var newElement = newPixel.element; + //console.log(`Element is ${newElement}, running exclusion check;`); + if(newElement !== pixel.element) { //exclude self + //console.log(`Element is different, storing in array;`); + neighborArray.push(newElement); //build array of valid neighbors + }; + //console.log("Finding iteration done"); + }; + //console.log("End of for block (not loop)"); + }; + //console.log(`Loop done: ${neighborArray}`); + if(neighborArray.length > 0) { + var changeToElement = neighborArray[Math.floor(Math.random() * neighborArray.length)]; + changePixel(pixel,changeToElement); + }; + }; + elements.paint.tick = function(pixel) { + pixel.color = _rgbHexCatcher(currentColor); + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement !== pixel.element) { //exclude self + newPixel.color = pixel.color; //change color of other pixel + }; + }; + }; + }; + elements.unpaint.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement !== pixel.element) { //slow for self, see below + newPixel.color = pixelColorPick(newPixel); //change color of other pixel + } else { + if(pixelTicks % 10 === 0) { //every 10 ticks because unpaint shouldn't be paintable, but it shouldn't be a seizure machine either + newPixel.color = pixelColorPick(newPixel); + }; + }; + }; + }; + }; + elements.sandreplacer.behavior = [ + "CH:sand|CH:sand|CH:sand", + "CH:sand|XX|CH:sand", + "CH:sand|CH:sand|CH:sand" + ]; + elements.delete_all_of_element.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + //console.log(`stored adjacent element ${newElement} at (${nX},${nY})`); + if(newElement !== pixel.element) { //exclude self + var coordCircle = circleCoords(pX,pY,7); + //console.log(`got coordinate circle centered at (${pX},${pY}) with ${coordCircle.length} ${coordCircle.length === 1 ? "pixel" : "pixels"}`); + for(j = 0; j < coordCircle.length; j++) { + //console.log(`itting through circle, index ${j}`); + var coordCircleCoord = coordCircle[j]; + var cNX = coordCircleCoord.x; + var cNY = coordCircleCoord.y; + //console.log(`circle coords stored (${cNX},${cNY})`); + if(isEmpty(cNX,cNY,true)) { + continue; + //console.log("skipped empty pixel"); + } else { + var circleNewPixel = pixelMap[cNX][cNY]; + var circleNewElement = circleNewPixel.element; + //console.log(`found pixel of element ${circleNewElement}`); + if(circleNewElement === newElement && circleNewElement !== pixel.element) { //exclude self, match the stored element from before + deletePixel(cNX,cNY); + }; + }; + }; + }; + }; + }; + }; + elements.room_temp.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + newPixel.temp = 20; //to room temp + pixelTempCheck(pixel); + }; + } + }; + elements.uncharge.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + if(newPixel.charge) { //remove charge properties + delete pixel.charge; + }; + if(newPixel.chargeCD) { + delete pixel.chargeCD; + } + } + } + }; + elements.unburn.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement === "fire") { //fire to smoke + changePixel(newPixel,"smoke"); + }; + if(newPixel.burning) { //remove burning properties + delete pixel.burning; + }; + if(newPixel.burnStart) { + delete pixel.burnStart; + } + } + } + }; + elements.smash.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if (elements[newElement].breakInto) { + var breakInto = elements[pixel.element].breakInto; //edited vanilla code + // if breakInto is an array, pick one + if (Array.isArray(breakInto)) { + breakInto = breakInto[Math.floor(Math.random() * breakInto.length)]; + } + changePixel(pixel,breakInto); + } + } + } + }; + elements.cook.behavior = [ + "HT:0.5|HT:0.5|HT:0.5", + "HT:0.5|HT:0.5|HT:0.5", + "HT:0.5|HT:0.5|HT:0.5", + ]; + elements.ultraheat.behavior = [ + "HT:350|HT:350|HT:350", + "HT:350|HT:350|HT:350", + "HT:350|HT:350|HT:350", + ]; + elements.ultracool.behavior = [ + "CO:350|CO:350|CO:350", + "CO:350|CO:350|CO:350", + "CO:350|CO:350|CO:350", + ]; + elements.incinerate.behavior = [ + "HT:10000|HT:10000|HT:10000", + "HT:10000|HT:10000|HT:10000", + "HT:10000|HT:10000|HT:10000", + ]; + elements.nan_temp.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + newPixel.temp = NaN; + }; + }; + }; + elements.inf_temp.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + newPixel.temp = Infinity; + }; + }; + }; + elements.superheat.behavior = [ + "HT:10|HT:10|HT:10", + "HT:10|HT:10|HT:10", + "HT:10|HT:10|HT:10", + ]; + elements.supercool.behavior = [ + "CO:10|CO:10|CO:10", + "CO:10|CO:10|CO:10", + "CO:10|CO:10|CO:10", + ]; + elements.hyperheat.behavior = [ + "HT:50|HT:50|HT:50", + "HT:50|HT:50|HT:50", + "HT:50|HT:50|HT:50", + ]; + elements.hypercool.behavior = [ + "CO:50|CO:50|CO:50", + "CO:50|CO:50|CO:50", + "CO:50|CO:50|CO:50", + ]; + elements.absolutezero.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + newPixel.temp = settings.abszero ?? -273.15; + }; + }; + }; + elements.antigrav.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + newPixel.r = 2; + }; + }; + }; + elements.normalgrav.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + newPixel.r = 0; + }; + }; + }; + elements.leftgrav.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + newPixel.r = 3; + }; + }; + }; + elements.rightgrav.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + newPixel.r = 1; + }; + }; + }; + elements.burn.burnTime = Infinity; + elements.burn.burn = 100; + elements.burn.burnInto = "burn"; + elements.burn.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement === "smoke") { //smoke to fire + changePixel(newPixel,"smoke"); + }; + newPixel.burning = true; + }; + }; + }; + elements.cursed_shock.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + newPixel.charge = 2; + if(newPixel.chargeCD) { + delete newPixel.chargeCD; + }; + }; + }; + }; + elements.offset_fourth_y.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement !== pixel.element) { //self-exclude + tryMove(newPixel,nX,nY+0.25) + }; + }; + }; + }; + elements.offset_half_y.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement !== pixel.element) { //self-exclude + tryMove(newPixel,nX,nY+0.5) + }; + }; + }; + }; + elements.offset_three_fourth_y.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement !== pixel.element) { //self-exclude + tryMove(newPixel,nX,nY+0.75) + }; + }; + }; + }; + elements.find_toggle.behavior = behaviors.WALL; + elements.find_toggle.tick = function(pixel) { + pixel.color = "rgb(255," + marasi(pixelTicks / 10) + ",0)"; + }; + elements.replace.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement === replaceFrom) { + changePixel(newPixel,replaceTo); + }; + }; + }; + }; + elements.alt_replace.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement === replaceFrom) { + newPixel.element = replaceTo; + }; + }; + }; + }; + elements.alt_alt_replace.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement === replaceFrom) { + newPixel.element = replaceTo; + newPixel.color = pixelColorPick(newPixel); + }; + }; + }; + }; + elements.change.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement !== pixel.element) { + changePixel(newPixel,changeTo); + }; + }; + }; + }; + elements.alt_change.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement !== pixel.element) { + newPixel.element = changeTo; + }; + }; + }; + }; + elements.alt_alt_change.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(newElement !== pixel.element) { + newPixel.element = changeTo; + newPixel.color = pixelColorPick(newPixel); + }; + }; + }; + }; + elements.prop.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + var newElement = newPixel.element; + if(propProperty === "element") { + if(newElement !== pixel.element) { //self exclude if element is the change + newPixel[propProperty] = propValue; + newPixel.temp = (elements[propValue].temp || newPixel.temp); + }; + } else { + newPixel[propProperty] = propValue; + }; + }; + }; + }; + elements.number_adjuster.tick = function(pixel) { + var pX = pixel.x; + var pY = pixel.y; + for(i = 0; i < mooreDonutCoords.length; i++) { + var coord = mooreDonutCoords[i]; + var oX = coord[0]; + var oY = coord[1]; + var nX = pX+oX; + var nY = pY+oY; + if(isEmpty(nX,nY,true)) { + continue; + } else { + var newPixel = pixelMap[nX][nY]; + if(numberAdjusterProperty !== "element") { + //console.log(numberAdjusterValue); + if(numberAdjusterMode === "set") { + newPixel[numberAdjusterProperty] = numberAdjusterValue; + } else if(numberAdjusterMode === "add") { + if(typeof(newPixel[numberAdjusterProperty]) === "undefined") { + newPixel[numberAdjusterProperty] = 0; + }; + newPixel[numberAdjusterProperty] += numberAdjusterValue; + }; + pixelTempCheck(newPixel); + }; + }; + }; + }; + //SWITCHES ## + elements.switch_off = { + name: "switch (off)", + color: "#7F3333", + behavior: behaviors.WALL, + noConduct: ["switch_on_control","switch_off_control"], + category: "machines", + }; + elements.switch_on = { + name: "switch (on)", + color: "#33CC33", + behavior: behaviors.WALL, + conduct: 1, + noConduct: ["switch_on_control","switch_off_control"], + category: "machines", + }; + elements.switch_off_control = { + color: "#FF3333", + behavior: behaviors.WALL, + behaviorOn: [ + "XX|CH:switch_on>switch_off|XX", + "CH:switch_on>switch_off|XX|CH:switch_on>switch_off", + "XX|CH:switch_on>switch_off|XX" + ], + conduct: 1, + noConduct: ["switch_on","switch_off"], + category: "machines", + }; + elements.switch_on_control = { + color: "#33FF33", + behavior: behaviors.WALL, + behaviorOn: [ + "XX|CH:switch_off>switch_on|XX", + "CH:switch_off>switch_on|XX|CH:switch_off>switch_on", + "XX|CH:switch_off>switch_on|XX" + ], + conduct: 1, + noConduct: ["switch_on","switch_off"], + category: "machines", + }; + //MORE HEATERS AND COOLERS (mostly) ## + elements.super_heater = { + color: "#ff0000", + tick: function(pixel) { + for (let i = -4; i < 5; i++) { + for (let j = -4; j < 5; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + pixelMap[pixel.x+j][pixel.y+i].temp += 15 + } + } + } + }, + category:"machines", + insulate: true, + state: "solid", + }, + elements.super_cooler = { + color: "#0000ff", + tick: function(pixel) { + for (let i = -4; i < 5; i++) { + for (let j = -4; j < 5; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + pixelMap[pixel.x+j][pixel.y+i].temp < -258 ? pixelMap[pixel.x+j][pixel.y+i].temp = -273 : pixelMap[pixel.x+j][pixel.y+i].temp -= 15 + } + } + } + }, + category:"machines", + insulate: true, + state: "solid", + }, + elements.super_warmer = { + color: "#00ff00", + tick: function(pixel) { + for (let i = -4; i < 5; i++) { + for (let j = -4; j < 5; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + pixelMap[pixel.x+j][pixel.y+i].temp = 20 + } + } + } + }, + category:"machines", + insulate: true, + state: "solid", + }, + elements.super_heater_2 = { + color: "#ff2200", + tick: function(pixel) { + for (let i = -9; i < 10; i++) { + for (let j = -9; j < 10; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + pixelMap[pixel.x+j][pixel.y+i].temp += 25 + } + } + } + }, + category:"machines", + insulate: true, + state: "solid", + hidden: true, + excludeRandom: true, + }, + elements.super_cooler_2 = { + color: "#0022ff", + tick: function(pixel) { + for (let i = -9; i < 10; i++) { + for (let j = -9; j < 10; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + pixelMap[pixel.x+j][pixel.y+i].temp < -248 ? pixelMap[pixel.x+j][pixel.y+i].temp = -273 : pixelMap[pixel.x+j][pixel.y+i].temp -= 25 + } + } + } + }, + category:"machines", + insulate: true, + state: "solid", + hidden: true, + excludeRandom: true, + }, + elements.super_warmer_2 = { + color: "#22ff22", + tick: function(pixel) { + for (let i = -9; i < 10; i++) { + for (let j = -9; j < 10; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + pixelMap[pixel.x+j][pixel.y+i].temp = 20 + } + } + } + }, + category:"machines", + insulate: true, + state: "solid", + hidden: true, + excludeRandom: true, + }, + elements.global_heater = { + color: "#ff6666", + tick: function(pixel) { + currentPixels.forEach(function(newPixel) {newPixel.temp++; pixelTempCheck(newPixel)}) + }, + category:"machines", + insulate: true, + state: "solid", + hidden: true, + excludeRandom: true, + }, + elements.global_cooler = { + color: "#6666ff", + tick: function(pixel) { + currentPixels.forEach(function(newPixel) {newPixel.temp = Math.max(-273.15,newPixel.temp - 1); pixelTempCheck(newPixel)}) + }, + category:"machines", + insulate: true, + state: "solid", + hidden: true, + excludeRandom: true, + }, + elements.global_warmer = { + color: "#66ff66", + tick: function(pixel) { + currentPixels.forEach(function(newPixel) {newPixel.temp = 20; pixelTempCheck(newPixel)}) + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + excludeRandom: true, + }, + elements.adjustable_global_heater = { + color: "#66ff66", + tick: function(pixel) { + var thisPixel = pixel; + currentPixels.forEach(function(newPixel) {if(newPixel.element !== thisPixel.element) {newPixel.temp += thisPixel.temp; pixelTempCheck(newPixel)}}) + }, + category: "machines", + insulate: true, + state: "solid", + hidden: true, + excludeRandom: true, + temp: 1 + }, + elements.super_heater_3 = { + color: "#ff7f00", + uwu: 0, + tick: function(pixel) { + tempInc = 50 + pixel.uwu = 0 + range = 10 + for (let i = -8; i < 9; i++) { + for (let j = -8; j < 9; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.uwu = 0 + } else { + tempInc += (pixel.uwu*15) + range += Math.floor((Math.sqrt(pixel.uwu+1))**1.2) + } + for (let i = (-1*range); i < (range + 1); i++) { + for (let j = (-1*range); j < (range + 1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + pixelMap[pixel.x+j][pixel.y+i].temp += tempInc + } + } + } + }, + category:"machines", + insulate: true, + state: "solid", + excludeRandom: true, + }, + elements.super_cooler_3 = { + color: "#007fff", + uwu: 0, + tick: function(pixel) { + tempDec = 50 + pixel.uwu = 0 + range = 10 + for (let i = -8; i < 9; i++) { + for (let j = -8; j < 9; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.uwu = 0 + } else { + tempDec += (pixel.uwu*15) + range += Math.floor((Math.sqrt(pixel.uwu+1))**1.2) + } + for (let i = (-1*range); i < (range + 1); i++) { + for (let j = (-1*range); j < (range + 1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + (pixelMap[pixel.x+j][pixel.y+i].temp < (-273 + tempDec)) ? pixelMap[pixel.x+j][pixel.y+i].temp = -273 : pixelMap[pixel.x+j][pixel.y+i].temp -= tempDec + } + } + } + }, + category:"machines", + insulate: true, + state: "solid", + excludeRandom: true, + }, + elements.super_warmer_3 = { + color: "#7fff7f", + uwu: 0, + tick: function(pixel) { + pixel.uwu = 0 + range = 10 + for (let i = -8; i < 9; i++) { + for (let j = -8; j < 9; j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + if (pixelMap[pixel.x+j][pixel.y+i].element == pixel.element) { + pixel.uwu++ + } + } + } + } + pixel.uwu -= 1 + if(pixel.uwu == undefined || pixel.uwu == null || isNaN(pixel.uwu)) { + pixel.uwu = 0 + } else { + range += Math.floor((Math.sqrt(pixel.uwu+1))**1.2) + } + for (let i = (-1*range); i < (range + 1); i++) { + for (let j = (-1*range); j < (range + 1); j++) { + if (!isEmpty(pixel.x+j,pixel.y+i) && !outOfBounds(pixel.x+j,pixel.y+i)) { + pixelMap[pixel.x+j][pixel.y+i].temp = 20 + } + } + } + }, + category:"machines", + insulate: true, + state: "solid", + excludeRandom: true, + }, + elements.temperature_test_thing = { //temperature checker + name: "Temperature Checker", + color: [_cc.b.h,_cc.b.h], + tick: function(pixel) { + if(pixel.temp < -255) { + pixel.color = "rgb(0,0,255)" + } else if(pixel.temp >= -255 && pixel.temp < 0) { + pixel.color = "rgb(0,0," + Math.abs(pixel.temp) + ")" + } else if(pixel.temp <= 255) { + pixel.color = "rgb(" + pixel.temp % 256 + ",0,0)" + } else if(pixel.temp <= 65535) { + pixel.color = "rgb(255," + Math.floor(pixel.temp / 256) + ",0)" + } else if(pixel.temp <= 16777215) { + pixel.color = "rgb(255,255," + Math.floor(pixel.temp / 65536) + ")" + } else { + pixel.color = _cc.w.r + } + }, + category:"machines", + insulate: true, + state: "solid", + hidden: true, + }, + /** + * color-temperature.js + * + * Neil Bartlett + * neilbartlett.com + * 2015-01-22 + * + * Copyright [2015] [Neil Bartlett] * + * + * Color Temperature is the color due to black body radiation at a given + * temperature. The temperature is given in Kelvin. The concept is widely used + * in photography and in tools such as f.lux. + * + * The function here converts a given color temperature into a near equivalent + * in the RGB colorspace. The function is based on a curve fit on standard sparse + * set of Kelvin to RGB mappings. + * + * Two conversions are presented here. The one colorTempertature2RGBUSingTH + * is a JS version of the algorithm developed by Tanner Helland. The second is a + * slightly more accurate conversion based on a refitting of the original data + * using different curve fit functions. The performance cost of the two + * approaches is very similar and in general the second algorithm is preferred. + * + * NOTE The approximations used are suitable for photo-mainpulation and other + * non-critical uses. They are not suitable for medical or other high accuracy + * use cases. + * + * Accuracy is best between 1000K and 40000K. + * + * See http://github.com/neilbartlett/color-temperature for further details. + * + **/ + //[Code licensed under the MIT License] + //[Tanner Helland version omitted] + /** + * A more accurate version algorithm based on a different curve fit to the + * original RGB to Kelvin data. + * Input: color temperature in degrees Kelvin + * Output: json object of red, green and blue components of the Kelvin temperature + */ + colorTemperature2rgb = function(kelvin) { + var temperature = kelvin / 100.0; + var red, green, blue; + if (temperature < 66.0) { + red = 255; + } else { + // a + b x + c Log[x] /. + // {a -> 351.97690566805693`, + // b -> 0.114206453784165`, + // c -> -40.25366309332127 + //x -> (kelvin/100) - 55} + red = temperature - 55.0; + red = 351.97690566805693+ 0.114206453784165 * red - 40.25366309332127 * Math.log(red); + if (red < 0) red = 0; + if (red > 255) red = 255; + } + /* Calculate green */ + if (temperature < 66.0) { + // a + b x + c Log[x] /. + // {a -> -155.25485562709179`, + // b -> -0.44596950469579133`, + // c -> 104.49216199393888`, + // x -> (kelvin/100) - 2} + green = temperature - 2; + green = -155.25485562709179 - 0.44596950469579133 * green + 104.49216199393888 * Math.log(green); + if (green < 0) green = 0; + if (isNaN(green)) green = 0; + if (green > 255) green = 255; + } else { + // a + b x + c Log[x] /. + // {a -> 325.4494125711974`, + // b -> 0.07943456536662342`, + // c -> -28.0852963507957`, + // x -> (kelvin/100) - 50} + green = temperature - 50.0; + green = 325.4494125711974 + 0.07943456536662342 * green - 28.0852963507957 * Math.log(green); + if (green < 0) green = 0; + if (green > 255) green = 255; + } + /* Calculate blue */ + if (temperature >= 66.0) { + blue = 255; + } else { + if (temperature <= 20.0) { + blue = 0; + } else { + // a + b x + c Log[x] /. + // {a -> -254.76935184120902`, + // b -> 0.8274096064007395`, + // c -> 115.67994401066147`, + // x -> kelvin/100 - 10} + blue = temperature - 10; + blue = -254.76935184120902 + 0.8274096064007395 * blue + 115.67994401066147 * Math.log(blue); + if (blue < 0) blue = 0; + if (blue > 255) blue = 255; + } + } + //return {red: Math.round(red), blue: Math.round(blue), green: Math.round(green)}; + return "rgb("+Math.round(red)+","+Math.round(green)+","+Math.round(blue)+")" + } + //[reverse conversion omitted] + elements.color_temp_test = { + color: "#111111", + behavior: behaviors.POWDER, + tick: function(pixel) { + if(!pixel.oldColor) { + pixel.oldColor = pixel.color + } + if(!pixel.lerpValue) { + pixel.lerpValue = 0 + } + if(!pixel.lerpAR) { + pixel.lerpAR = 0 + } + if(!pixel.lerpAG) { + pixel.lerpAG = 0 + } + if(!pixel.lerpAB) { + pixel.lerpAB = 0 + } + if(!pixel.lerpBR) { + pixel.lerpBR = 0 + } + if(!pixel.lerpBG) { + pixel.lerpBG = 0 + } + if(!pixel.lerpBB) { + pixel.lerpBB = 0 + } + if(!pixel.lerpedR) { + pixel.lerpedR = 0 + } + if(!pixel.lerpedG) { + pixel.lerpedG = 0 + } + if(!pixel.lerpedB) { + pixel.lerpedB = 0 + } + if(!pixel.lerpedColor) { + pixel.lerpedColor = "" + } + if(pixel.temp < 525) { + pixel.color = pixel.oldColor + } + if(pixel.temp >= 525 && pixel.temp < 1582) { + pixel.lerpValue = (pixel.temp-524)/(1581-524) + pixel.lerpAR = pixel.oldColor.split(",")[0].slice(4) + pixel.lerpAG = pixel.oldColor.split(",")[1] + pixel.lerpAB = pixel.oldColor.split(",")[2].slice(0,-1) + pixel.lerpBR = colorTemperature2rgb(pixel.temp + 273.15).split(",")[0].slice(4) + pixel.lerpBG = colorTemperature2rgb(pixel.temp + 273.15).split(",")[1] + pixel.lerpBB = colorTemperature2rgb(pixel.temp + 273.15).split(",")[2].slice(0,-1) + pixel.lerpedR = pixel.lerpBR*pixel.lerpValue + pixel.lerpAR*(1-pixel.lerpValue) + pixel.lerpedG = pixel.lerpBG*pixel.lerpValue + pixel.lerpAG*(1-pixel.lerpValue) + pixel.lerpedB = pixel.lerpBB*pixel.lerpValue + pixel.lerpAB*(1-pixel.lerpValue) + pixel.lerpedColor = "rgb(" + pixel.lerpedR + "," + pixel.lerpedG + "," + pixel.lerpedB + ")" + pixel.color = pixel.lerpedColor + } + if(pixel.temp >= 1582) { + pixel.color = colorTemperature2rgb(pixel.temp + 273.15) + } + doHeat(pixel); + }, + category: "special", + state: "solid", + density: 1500, + temp: 20, + } + //TEETH AND PASTE ## + elements.tooth = { + color: "#d9d9d9", + behavior: behaviors.SUPPORT, + reactions: { + "sugar": { "elem1": "decayed_tooth", "elem2": null, "chance": 0.003 }, + "plaque": { "elem1": "decayed_tooth", "elem2": null, "chance": 0.002 }, + "acid": { "elem1": "decayed_tooth", "elem2": null } + }, + category:"life", + tempHigh: 1000, //https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5887641/ + stateHigh: ["steam","salt","meat","hydroxyapatite"], + state: "solid", + density: 2000, //(bs) inspired by https://ncbi.nlm.nih.gov/pmc/articles/PMC5176275/ + hardness: 0.5, + breakInto: ["meat","hydroxyapatite"], + }, + elements.plaque = { + color: "#faf6dc", + behavior: [ + "XX|ST AND CR:plague%0.01 AND CR:acid%0.01 AND CR:infection%0.003 AND CH:tooth>decayed_tooth%0.01|XX", + "ST AND CR:plague%0.01 AND CR:acid%0.01 AND CR:infection%0.003 AND CH:tooth>decayed_tooth%0.01|CH:tartar%0.001|ST AND CR:plague%0.01 AND CR:acid%0.01 AND CR:infection%0.003 AND CH:tooth>decayed_tooth%0.01", + "M2|M1 AND ST AND CR:plague%0.01 AND CR:acid%0.01 AND CR:infection%0.003 AND CH:tooth>decayed_tooth%0.01|M2", + ], + reactions: { + "acid": { "elem1": null, "elem2": null, "chance": 0.01 } + }, + category:"life", + tempHigh: 100, + stateHigh: ["steam","plague"], + state: "solid", + density: 5.4, //https://physics.aps.org/articles/v5/s140#:~:text=They%20then%20use%20tabulated%20values,%2D12%20gram)%20per%20cell. + //https://en.wikipedia.org/wiki/Calculus_(dental)#:~:text=Cell%20density%20within%20dental%20plaque,estimated%20200%2C000%2C000%20cells%20per%20milligram. + hidden: true, + }, + elements.tartar = { + color: ["#e8d595", "#cfb27e", "#f0e989"], + behavior: [ + "XX|ST AND CR:plague%0.02 AND CR:acid%0.01 AND CR:infection%0.003 AND CH:tooth>decayed_tooth%0.01|XX", + "ST AND CR:plague%0.02 AND CR:acid%0.01 AND CR:infection%0.003 AND CH:tooth>decayed_tooth%0.01|CH:tartar%0.01|ST AND CR:plague%0.02 AND CR:acid%0.01 AND CR:infection%0.003 AND CH:tooth>decayed_tooth%0.01", + "XX|M1 AND ST AND CR:plague%0.02 AND CR:acid%0.01 AND CR:infection%0.003 AND CH:tooth>decayed_tooth%0.01|XX", + ], + reactions: { + "acid": { "elem1": null, "elem2": null, "chance": 0.01 } + }, + category:"other", + tempHigh: elements.calcium.tempHigh, + stateHigh: ["steam","plague","calcium"], + state: "solid", + density: 1900, + hardness: elements.tooth.hardness - 0.05, + breakInto: ["calcium","calcium","calcium","calcium","rotten_meat","rotten_meat","plague"], + hidden: true, + }, + elements.decayed_tooth = { + color: ["#aba89d","#85837b","#7a7972","#b8b5a5","#6b6a63"], + behavior: [ + "XX|XX|XX", + "SP%99.5|DL%0.04|SP%99.5", + "XX|M1|XX", + ], + reactions: { + "acid": { "elem1": null, "elem2": null, "chance": 0.7 } + }, + tempHigh: 1000, + stateHigh: ["steam","salt","meat","hydroxyapatite"], + state: "solid", + category: "other", + density: 1900, + hardness: 0.3, + breakInto: ["rotten_meat","hydroxyapatite"], + hidden: true, + }, + elements.hydroxyapatite = { + color: ["#edecda", "#f5f5f5", "#e8e8e8"], + behavior: behaviors.POWDER, + state: "solid", + category: "solids", + density: 3180, + tempHigh: 1670, + /* it decomposes but not into anything worth adding + https://www.sciencedirect.com/science/article/abs/pii/S0142961299000769 */ + category: "powders", + }, + elements.toothpaste = { + color: ["#f8f8f8", "#6699ff", "#f8f8f8", "#ff5555"], + behavior: [ + "XX|SW:plaque%5|XX", + "SW:plaque%5|XX|SW:plaque%5", + "M2|SW:plaque%5|M2", + ], + reactions: { + "plaque": {"elem1":["foam","toothpaste","toothpaste"], "elem2":"foam", "chance":0.7}, + "decayed_tooth": {"elem1":"tooth", "elem2":"foam", "chance":0.5} + }, + state: "solid", + category: "other", + density: 1330, + tempHigh: 250, //bs + stateHigh: ["toothpaste","toothpaste","toothpaste","toothpaste","toothpaste","toothpaste","toothpaste","toothpaste","toothpaste","toothpaste","foam","foam","fire","smoke","ash"], + burn: 5, + burnInto: ["fire","smoke","smoke","ash","ash","toothpaste"], + viscosity: 20000, + /* it decomposes but not into anything worth adding + https://www.sciencedirect.com/science/article/abs/pii/S0142961299000769 */ + category: "powders", + } + runAfterLoad(function() { + foodArray = Object.keys(elements).filter(function(e) { + return elements[e].category == "food"; + }); + if(!elements.tooth.reactions) { + elements.tooth.reactions = {} + }; + for(i = 0; i < foodArray.length; i++) { + elements.tooth.reactions[foodArray[i]] = { "elem1": ["tooth","tooth","tooth","tooth","tooth","tooth","tooth","tooth","decayed_tooth"], "elem2": "plaque", "chance": 0.001 } + }; + elements.acid.ignore.push("tooth"); + elements.acid.ignore.push("decayed_tooth"); + elements.acid.ignore.push("plaque"); + elements.acid.ignore.push("tartar"); + eLists.IMPURITY.push("plaque"); + eLists.IMPURITY.push("tartar"); + eLists.IMPURITY.push("decayed_tooth"); + //regenerate behaviors of elements that use eLists.IMPURITY { + elements.pure_water.behavior = [ + "DL:"+eLists.IMPURITY+"|DL:"+eLists.IMPURITY+"|DL:"+eLists.IMPURITY+"", + "DL:"+eLists.IMPURITY+" AND M2|XX|DL:"+eLists.IMPURITY+" AND M2", + "DL:"+eLists.IMPURITY+" AND M1|DL:"+eLists.IMPURITY+" AND M1|DL:"+eLists.IMPURITY+" AND M1", + ]; + elements.pure_steam.behavior = [ + "M2 AND DL:"+eLists.IMPURITY+"|M1 AND DL:"+eLists.IMPURITY+"|M2 AND DL:"+eLists.IMPURITY+"", + "M1 AND DL:"+eLists.IMPURITY+"|XX|M1 AND DL:"+eLists.IMPURITY+"", + "M2 AND DL:"+eLists.IMPURITY+"|M1 AND DL:"+eLists.IMPURITY+"|M2 AND DL:"+eLists.IMPURITY+"", + ]; + //} + //concoction support (it's all mistakes) { + elements.concoction.reactions.tooth = { "elem1": "mistake", "elem2": null }; + elements.concoction.reactions.decayed_tooth = { "elem1": "mistake", "elem2": null }; + elements.concoction.reactions.toothpaste = { "elem1": "mistake", "elem2": null }; + elements.concoction.reactions.plaque = { "elem1": "mistake", "elem2": null }; + elements.concoction.reactions.tartar = { "elem1": "mistake", "elem2": null }; + //} + }); + //EXPERIMENTAL STRUCTURE SPAWNERS ## + arrayLoaderVoids = ["air", "null", null]; + buildingOneSegmentDoor = ["concrete","wood_plank","concrete","wood_plank","concrete"]; + buildingOneSegmentWindows = ["concrete","glass_pane","concrete","glass_pane","concrete"]; + buildingOneSegmentConcrete = ["concrete","concrete","concrete","concrete","concrete"]; + buildingTwoSegments = [ + ["concrete","concrete","concrete","concrete","concrete"], + ["concrete","concrete","concrete","concrete","concrete"], + ["brick","wood_plank","brick"], + ["glass_pane","wood_plank","glass_pane"], + ["brick","brick","brick"], + ["wood","wood_plank","wood_plank","wood_plank","wood"], + ["wood_plank","wood_plank","wood_plank"], + ["wood_plank"] + ]; + oldRoom= [["brick", "brick", "brick", "brick", "brick", "brick", "brick", "glass", "glass", "glass", "glass", "glass", "brick", "brick", "brick", "brick", "brick", "brick", "brick"], + ["brick", "brick", "brick", "brick", "brick", "brick", "brick", "glass", "glass", "glass", "glass", "glass", "brick", "battery","brick", "brick", "brick", "brick", "brick"], + ["glass", "glass", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "light","light_bulb","air", "air", "air", "glass", "glass"], + ["glass", "glass", "light", "light", "air", "air", "air", "air", "air", "air", "air", "air", "air", "light", "air", "air", "air", "glass", "glass"], + ["glass", "glass", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "brass"], + ["glass", "glass", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["brick", "brick", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["brick", "brick", "iron", "straw", "straw", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["brick", "brick", "iron", "straw", "straw", "straw", "straw", "straw", "straw", "straw", "iron", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["brick", "brick", "iron", "straw", "straw", "straw", "straw", "straw", "straw", "straw", "iron", "air", "air", "light", "air", "air", "air", "wood", "brass"], + ["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick"], + ["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick"]] + altRoom= [["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick"], + ["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "battery","brick", "brick", "brick", "brick", "brick"], + ["glass", "glass", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "light","light_bulb","air", "air", "air", "glass", "glass"], + ["glass", "glass", "light", "light", "air", "air", "air", "air", "air", "air", "air", "air", "air", "light", "air", "air", "air", "glass", "glass"], + ["brass", "wood", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "brass"], + ["wood", "wood", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["wood", "wood", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["wood", "wood", "air", "air", "iron", "straw", "straw", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["wood", "wood", "air", "air", "iron", "straw", "straw", "straw", "straw", "straw", "straw", "straw", "iron", "air", "air", "air", "air", "wood", "wood" ], + ["brass", "wood", "air", "air", "iron", "straw", "straw", "straw", "straw", "straw", "straw", "straw", "iron", "light", "air", "air", "air", "wood", "brass"], + ["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick"], + ["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "battery", "brick", "brick", "brick", "brick", "brick"]] + /*function r0to255() { + return Math.floor(Math.random() * 256); + };*/ + canSupportWithEdge = function(x,y) { + if(outOfBounds(x,y)) { //count edges + return true; + } else { + if(!isEmpty(x,y,true)) { //if there is a pixel + if(elements[pixelMap[x][y].element].state === "solid") { + return true; + } else { + return false; + }; + }; + }; + }; + function loadPixelRowFromArray(pixelArray,centerX,centerY,evenLengthBiasedLeft=true,doOverwrite=true) { + var arrayLength = pixelArray.length; + var leftmostOffset = (evenLengthBiasedLeft ? Math.floor(0 - ((arrayLength - 1) / 2)) : Math.ceil(0 - ((arrayLength - 1) / 2))) //floor and ceil have no effect on the integer values produced by odd lengths + var forEnd = 0 - leftmostOffset; + //var randomColor = `rgb(${r0to255()},${r0to255()},${r0to255()})`; + for(i = 0; i < arrayLength; i++) { + var newElement = pixelArray[i]; + var x = (centerX + leftmostOffset) + i; + var y = centerY; + if(outOfBounds(x,y)) { + continue; + }; + if(newElement === "null" || newElement === null) { //do nothing if element is null + continue; + }; + //console.log([x,y]); + if(!isEmpty(x,y,true)) { + if(doOverwrite) { + deletePixel(x,y); + if(newElement !== "air") { //if the new element is "air", don't create a pixel after deleting + createPixel(newElement,x,y); + }; + continue; + //pixelMap[x][y].color = randomColor; + } else {; + if(newElement === "air") { //delete on "air" even if doOverwrite is false + deletePixel(x,y); + } else { + continue; + }; + }; + }; + if(!arrayLoaderVoids.includes(newElement)) { //don't create anything if the element is a special void + createPixel(newElement,x,y); + } + //pixelMap[x][y].color = randomColor; + }; + }; + function loadPixelRowFromArrayWithColorRowArray(pixelArray,colorArray,centerX,centerY,evenLengthBiasedLeft=true,doOverwrite=true,doColorOffset=false) { + var arrayLength = pixelArray.length; + var leftmostOffset = (evenLengthBiasedLeft ? Math.floor(0 - ((arrayLength - 1) / 2)) : Math.ceil(0 - ((arrayLength - 1) / 2))) //floor and ceil have no effect on the integer values produced by odd lengths + var forEnd = 0 - leftmostOffset; + //var randomColor = `rgb(${r0to255()},${r0to255()},${r0to255()})`; + for(i = 0; i < arrayLength; i++) { + var newElement = pixelArray[i]; + var newColor = colorArray[i]; + //console.log(newColor); + if(doColorOffset && !["null",null].includes(newColor)) { + newColor = convertHslObjects(normalizeColorToHslObject(newColor),"rgbjson"); + var colorOffset = Math.floor(Math.random() * (Math.random() > 0.5 ? -1 : 1) * Math.random() * 15); + for(colorlet in newColor) { + newColor[colorlet] += colorOffset; + }; + newColor = convertColorFormats(newColor,"rgb"); + }; + var x = (centerX + leftmostOffset) + i; + var y = centerY; + if(outOfBounds(x,y)) { + continue; + }; + if(newElement === "null" || newElement === null) { //do nothing if element is null + continue; + }; + //console.log([x,y]); + if(!isEmpty(x,y,true)) { + if(doOverwrite) { + deletePixel(x,y); + if(newElement !== "air") { //if the new element is "air", don't create a pixel after deleting + createPixel(newElement,x,y); + pixelMap[x][y].color = newColor; + }; + continue; + //pixelMap[x][y].color = randomColor; + } else {; + if(newElement === "air") { //delete on "air" even if doOverwrite is false + deletePixel(x,y); + } else { + continue; + }; + }; + }; + if(!arrayLoaderVoids.includes(newElement)) { //don't create anything if the element is a special void + createPixel(newElement,x,y); + pixelMap[x][y].color = newColor; + } + //pixelMap[x][y].color = randomColor; + }; + }; + delete elements.rad_glass.stateHigh; + elements.glass.hardness = 0.25, + elements.rad_glass.hardness = 0.25, + //Prereq elements + elements.crumbling_concrete = { + color: "#ababab", + tick: function(pixel) { + var px = pixel.x; + var py = pixel.y; + if (pixel.start === pixelTicks) {return} + var supportCondition1 = (canSupportWithEdge(px-1,py-1) && canSupportWithEdge(px+1,py-1)) // V shape + var supportCondition2 = (canSupportWithEdge(px-1,py) && canSupportWithEdge(px+1,py)) // - shape + var supportCondition3 = (canSupportWithEdge(px-1,py+1) && canSupportWithEdge(px+1,py+1)) // Λ shape + var supportCondition4 = (canSupportWithEdge(px-1,py+1) && canSupportWithEdge(px+1,py-1)) // / shape + var supportCondition5 = (canSupportWithEdge(px-1,py-1) && canSupportWithEdge(px+1,py+1)) // \ shape + var supportCondition6 = (canSupportWithEdge(px-1,py-1) && canSupportWithEdge(px+1,py)) // '- shape + var supportCondition7 = (canSupportWithEdge(px-1,py+1) && canSupportWithEdge(px+1,py)) // ,- shape + var supportCondition8 = (canSupportWithEdge(px+1,py-1) && canSupportWithEdge(px-1,py)) // -' shape + var supportCondition9 = (canSupportWithEdge(px+1,py+1) && canSupportWithEdge(px-1,py)) // -, shape + var supportCondition10 = (canSupportWithEdge(px,py+1) && canSupportWithEdge(px,py-1)) // | shape + var supports = (supportCondition1 || supportCondition2 || supportCondition3 || supportCondition4 || supportCondition5 || supportCondition6 || supportCondition7 || supportCondition8 || supportCondition9 || supportCondition10); + if(!supports) { + behaviors.POWDER(pixel); + }; + newConcreteTick(pixel); + doDefaults(pixel); + }, + tempHigh: 1500, + stateHigh: "magma", + category: "powders", + state: "solid", + density: 2400, + hardness: 0.5, + breakInto: "dust", + }; + elements.attach_powder_silk = { + color: ["#ebebeb", "#e6d9d1"], + properties: { + "attached": false, + "attachOffsets": [null, null] + }, + tick: function(pixel) { + if (pixel.start === pixelTicks) {return} + if(pixel.attached) { + if(pixel.attachOffsets === null) { + pixel.attached = false; + } else if(pixel.attachOffsets.includes(null)) { + pixel.attached = false; + } else { + var attachCoords = [pixel.x + pixel.attachOffsets[0], pixel.y + pixel.attachOffsets[1]]; + if(isEmpty(attachCoords[0],attachCoords[1],false)) { //consider OOB full + pixel.attached = false; + }; + }; + } else { + behaviors.POWDER(pixel); + }; + doDefaults(pixel); + }, + burnInto: "ash", + burn:75, + burnTime:25, + category: "solids", + state: "solid", + density: 1000, + hidden: true + }; + elements.glass_pane = { + color: ["#5e807d","#679e99"], + behavior: behaviors.SUPPORT, + reactions: { + "radiation": { "elem1":"rad_glass_pane", "chance":0.33 } + }, + tempHigh: 1500, + stateHigh: "molten_glass", + hardness: 0.2, + category: "solids", + state: "solid", + density: 2500, + breakInto: "glass_shard", + hidden: true, + }; + elements.rad_glass_pane = { + color: ["#648c64","#6aad83"], + behavior: [ + "XX|CR:radiation%0.075|XX", + "SP AND CR:radiation%0.075|XX|SP AND CR:radiation%0.075", + "XX|M1 AND CR:radiation%0.075|XX", + ], + tempHigh: 1500, + hardness: 0.2, + stateHigh: "molten_rad_glass", + category: "solids", + state: "solid", + density: 2500, + breakInto: "rad_glass_shard", + hidden: true, + }; + elements.wood.hardness = 0.2; + elements.wood_plank = { + color: "#ab6c3f", + behavior: behaviors.SUPPORT, + tempHigh: 400, + stateHigh: ["ember","charcoal","fire","fire","fire"], + category: "solids", + burn: 5, + burnTime: 300, + burnInto: ["ember","charcoal","fire"], + state: "solid", + hardness: 0.2, + breakInto: "sawdust", + cutInto: ["wood_plank","wood_plank","wood_plank","wood_plank","wood_plank","wood_plank","sawdust"] + }; + elements.hanging_concrete = { + color: "#ababab", + behavior: [ + "XX|SP|XX", + "XX|XX|XX", + "M2|M1|M2" //crumbling from the top down is acceptable + ], + tick: newConcreteTick, + tempHigh: 1500, + stateHigh: "magma", + category: "powders", + state: "solid", + density: 2400, + hardness: 0.5, + breakInto: "dust", + hidden: true, + }; + elements.support_copper = { + color: ["#A95232","#BE4322","#C76035"], + behavior: behaviors.SUPPORT, + reactions: { + "water": { "elem1":"oxidized_copper", chance:0.0025 }, + "salt_water": { "elem1":"oxidized_copper", chance:0.005 }, + "dirty_water": { "elem1":"oxidized_copper", chance:0.04 }, + "sugar_water": { "elem1":"oxidized_copper", chance:0.0035 }, + "seltzer": { "elem1":"oxidized_copper", chance:0.006 } + }, + category: "solids", + tempHigh: 1085, + stateHigh: "molten_copper", + density: 8960, + conduct: 0.95, + hardness: 0.3, + hidden: true, + }; + elements.paper.behavior = behaviors.SUPPORT; + elements.support_glass = JSON.parse(JSON.stringify(elements.glass)); + elements.support_glass.stateHigh = "molten_glass"; + elements.support_glass.behavior = behaviors.SUPPORT; + elements.support_bulb = { + color: "#a8a897", + behavior: behaviors.SUPPORTPOWDER, + behaviorOn: [ + "XX|CR:light|XX", + "CR:light AND SP|XX|CR:light AND SP", + "M2|CR:light AND M1|M2" + ], + colorOn: "#ebebc3", + category: "machines", + tempHigh: 1500, + stateHigh: ["molten_glass","molten_glass","molten_copper"], + conduct: 1, + breakInto: "glass_shard", + hidden: true, + }; + elements.hanging_bulb = { + color: "#a8a897", + behavior: [ + "XX|SP|XX", + "XX|XX|XX", + "M2|M1|M2" + ], + behaviorOn: [ + "XX|SP|XX", + "CR:light|XX|CR:light", + "M2|CR:light AND M1|M2" + ], + colorOn: "#ebebc3", + category: "machines", + tempHigh: 1500, + stateHigh: ["molten_glass","molten_glass","molten_copper"], + conduct: 1, + breakInto: "glass_shard", + hidden: true, + }; + elements.support_plastic = { + color: "#c5dede", + behavior: behaviors.SUPPORT, + tempHigh: 250, + stateHigh: "molten_plastic", + burn: 10, + burnTime: 200, + burnInto: ["dioxin","smoke","dioxin","smoke","stench"], + category: "solids", + state: "solid", + density: 1052, + hidden: true, + }; + newPowder("calcium_sulfate","#d1cec7",2960,1460).reactions = { + water: { elem1: ["gypsum","calcium_sulfate"], elem2: null } + }; + newPowder("gypsum",["#e6e5e3","#d9dbdb"],2320,1460).tick = function(pixel) { + //thermal split + if(pixel.temp > 100) { + var emptySlots = getEmptyMooreNeighbors(pixel); + if(emptySlots.length > 1) { + shuffleArray(emptySlots); + emptySlots = emptySlots.slice(0,2); + for(var i = 0; i < emptySlots.length; i++) { + var coords = emptySlots[i]; + createPixelReturn("steam",...coords).temp = pixel.temp + }; + changePixel(pixel,"calcium_sulfate",false); + return + } + } + }; + elements.paper.reactions ??= {}; + elements.paper.reactions.gypsum = { elem1: ["paper","paper","paper","paper","paper","paper",null], elem2: "drywall" }; + elements.molten_gypsum = { + tick: function(pixel) { + //thermal split + var emptySlots = getEmptyMooreNeighbors(pixel); + if(emptySlots.length > 1) { + shuffleArray(emptySlots); + emptySlots = emptySlots.slice(0,2); + for(var i = 0; i < emptySlots.length; i++) { + var coords = emptySlots[i]; + createPixelReturn("steam",...coords).temp = pixel.temp + }; + changePixel(pixel,"molten_calcium_sulfate",false); + return + } + } + }; + elements.drywall = { + color: "#dedcd9", + behavior: behaviors.SUPPORT, + tick: function(pixel) { + if(pixel.burning && pixel.temp < 80) { + delete pixel.burning; + delete pixel.burnStart + }; + pixel.isWet ??= Math.random() < 0.085; + var chance = Math.max(0,scale(pixel.temp,59.9999,100,0,0.05)); + if(pixel.isWet && Math.random() < chance) { + var emptySlots = getEmptyMooreNeighbors(pixel); + if(emptySlots.length > 0) { + var randomCoords = randomChoice(emptySlots); + if(isEmpty(...randomCoords)) { + createPixel(getStateAtTemp("water",pixel.temp),...randomCoords); + changePixel(pixel,"gypsum"); + delete pixel.isWet; + return + } + } + } + }, + burn: 1, + burnTime: 100, + burnInto: ["gypsum","gypsum","gypsum","gypsum","gypsum","gypsum","gypsum","gypsum","gypsum","gypsum","steam","ash"], + category: "solids", + state: "solid", + density: 609 + }; + elements.steel.movable = false; + 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% + 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, + behavior: behaviors.SUPPORT, + tempHigh: elements.steel.tempHigh, + stateHigh: "molten_steel", + category: "solids", + density: elements.steel.density, + conduct: elements.steel.conduct, + hardness: elements.steel.hardness, + }; + elements.support_aluminum = { + color: elements.aluminum.color, + behavior: behaviors.SUPPORT, + tempHigh: elements.aluminum.tempHigh, + stateHigh: "molten_aluminum", + category: "solids", + density: elements.aluminum.density, + conduct: elements.aluminum.conduct, + hardness: elements.aluminum.hardness, + }; + elements.support_copper = { + color: elements.copper.color, + behavior: behaviors.SUPPORT, + tempHigh: elements.copper.tempHigh, + stateHigh: "molten_copper", + category: "solids", + density: elements.copper.density, + conduct: elements.copper.conduct, + hardness: elements.copper.hardness, + }; + runAfterAutogen(function() { + for(var name in elements) { + var rxns = elements[name].reactions; + if(!rxns) { continue }; + if(typeof(rxns) == "object" && typeof(rxns["steel"]) === "object") { + rxns.support_steel = rxns.steel + }; + if(typeof(rxns) == "object" && typeof(rxns["aluminum"]) === "object") { + rxns.support_aluminum = rxns.aluminum + } + if(typeof(rxns) == "object" && typeof(rxns["copper"]) === "object") { + rxns.support_copper = rxns.copper + } + }; + elements.support_steel.reactions = elements.steel.reactions; + elements.support_aluminum.reactions = elements.aluminum.reactions; + elements.support_copper.reactions = elements.copper.reactions; + }); + var newAcidIgnores = ["glass_pane", "rad_glass_pane", "rad_glass_shard", "hanging_plastic"]; + for(i = 0; i < newAcidIgnores.length; i++) { + elements.acid.ignore.push(newAcidIgnores[i]); + elements.acid_gas.ignore.push(newAcidIgnores[i]); + }; + elements.rad_glass.breakInto = "rad_glass_shard"; + if(!elements.glass_shard.reactions) { + elements.glass_shard.reactions = {}; + }; + elements.glass_shard.reactions.radiation = { "elem1":"rad_glass_shard", "chance":0.33 }; + if(!elements.molten_glass.reactions) { + elements.molten_glass.reactions = {}; + }; + elements.molten_glass.viscosity = 2000000000; //2e7 centiPoise + elements.molten_glass.reactions.radiation = { "elem1":"molten_rad_glass", "chance":0.33 }; + elements.molten_glass.reactions.ultramafic_magma = { elem1:[ + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass",null //the range of silica content from ultramafic to felsic is about 20%; this regarded as 4 even steps on top of ultramafic magma for simplicity's sake gives 5% or 1/20 + ], elem2: "magma", "chance":0.1 }; + elements.molten_glass.reactions.magma = { elem1:[ + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass",null + ], elem2: "intermediate_magma", "chance":0.09 }; + elements.molten_glass.reactions.intermediate_magma = { elem1:[ + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass",null //the range of silica content from ultramafic to felsic is about 20%; this regarded as 5 even steps for simplicity's sake gives 4% or 1/25 + ], elem2: "intermediate_felsic_magma", "chance":0.08 }; + elements.molten_glass.reactions.intermediate_felsic_magma = { elem1:[ + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass","molten_glass", + "molten_glass","molten_glass","molten_glass","molten_glass",null //the range of silica content from ultramafic to felsic is about 20%; this regarded as 5 even steps for simplicity's sake gives 4% or 1/25 + ], elem2: "felsic_magma", "chance":0.07 }; + elements.rad_glass_shard = { + color: ["#648c64","#6aad83","#6a9171"], + behavior: [ + "XX|CR:radiation%0.075|XX", + "CR:radiation%0.075|XX|CR:radiation%0.075", + "M2|M1 AND CR:radiation%0.075|M2", + ], + tempHigh: 1500, + stateHigh: "molten_rad_glass", + category: "powders", + state: "solid", + density: 2500, + }; + elements.molten_rad_glass = { + behavior: [ + "XX|CR:radiation%0.15 AND CR:fire%2.5|XX", + "M2 AND CR:radiation%0.15|XX|M2 AND CR:radiation%0.15", + "M1|M1 AND CR:radiation%0.15|M1", + ], + }; + elements.attach_concrete = { + color: "#ababab", + properties: { + "attached": false, + "attachOffsets": [null, null] + }, + tick: function(pixel) { + if (pixel.start === pixelTicks) {return} + if(pixel.attached) { + if(pixel.attachOffsets === null) { + pixel.attached = false; + } else if(pixel.attachOffsets.includes(null)) { + pixel.attached = false; + } else { + var attachCoords = [pixel.x + pixel.attachOffsets[0], pixel.y + pixel.attachOffsets[1]]; + if(isEmpty(attachCoords[0],attachCoords[1],false)) { //consider OOB full + pixel.attached = false; + }; + }; + } else { //Support behavior if not attached + if(!isEmpty(pixel.x-1,pixel.y,true) || !isEmpty(pixel.x+1,pixel.y,true)) { + tryMove(pixel,pixel.x,pixel.y+1); + }; + }; + newConcreteTick(pixel); + doDefaults(pixel); + }, + tempHigh: 1500, + stateHigh: "magma", + category: "powders", + state: "solid", + density: 2400, + hardness: 0.5, + breakInto: "dust", + }; + elements.steel_plate_ledge = { + color: "#F2F2F2", + tick: function(pixel) { + if(pixel.attached) { + if(pixel.attachOffsets === null) { + pixel.attached = false; + } else if(pixel.attachOffsets.includes(null)) { + pixel.attached = false; + } else { + var attachCoords = [pixel.x + pixel.attachOffsets[0], pixel.y + pixel.attachOffsets[1]]; + if(isEmpty(attachCoords[0],attachCoords[1],false)) { //consider OOB full + pixel.attached = false; + }; + }; + } else { //Move if not attached + tryMove(pixel,pixel.x,pixel.y+1); + }; + doDefaults(pixel); + }, + properties: { + "attached": false, + "attachOffsets": [null, null] + }, + tempHigh: 1455.5, + stateHigh: "molten_steel", + category: "solids", + density: 785, + conduct: 0.32, + hardness: 0.7, + breakInto: "metal_scrap", + }; + //Seeds + elements.building_1_seed = { + tick: function(pixel) { + for(cx = -4; cx <= 4; cx++) { + for(cy = -4; cy <= 4; cy++) { + if(cx === 0 && cy === 0) { + continue; + }; + var finalCoords = [pixel.x+cx,pixel.y+cy]; + if(isEmpty(...finalCoords,true)) { + continue; + } else { + var otherPixel = pixelMap[finalCoords[0]][finalCoords[1]]; + if(otherPixel.element === pixel.element) { + deletePixel(...finalCoords); + }; + }; + }; + }; + if(!isEmpty(pixel.x,pixel.y-1,true)) { + swapPixels(pixel,pixelMap[pixel.x][pixel.y-1]); + return; + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { + var randomHeight = 13 + Math.floor(Math.random() * (8 + 1)) //min 12, variance 8 + var currentHeight = pixel.y + 2; + var endHeight = pixel.y - randomHeight; + //bottom 2 rows of concrete, 2 door layers and another concrete (the three of those counting against the final height) + loadPixelRowFromArray(buildingOneSegmentConcrete,pixel.x,currentHeight,true,false); + currentHeight--; + loadPixelRowFromArray(buildingOneSegmentConcrete,pixel.x,currentHeight,true,false); + currentHeight--; + loadPixelRowFromArray(buildingOneSegmentDoor,pixel.x,currentHeight,true,true); + currentHeight--; + loadPixelRowFromArray(buildingOneSegmentDoor,pixel.x,currentHeight,true,true); + currentHeight--; + loadPixelRowFromArray(buildingOneSegmentConcrete,pixel.x,currentHeight,true,true); + currentHeight--; + //start looped alternating rows + while(currentHeight > endHeight) { + //console.log(currentHeight) + if(outOfBounds(pixel.x,pixel.y)) { + break; + }; + loadPixelRowFromArray(buildingOneSegmentWindows,pixel.x,currentHeight,true,true); + currentHeight--; + loadPixelRowFromArray(buildingOneSegmentConcrete,pixel.x,currentHeight,true,true); + currentHeight--; + }; + }; + }, + excludeRandom: true, + desc: "Creates a miniature building made of concrete and glass.", + cooldown: 6, + state: "solid", + hardness: 1, + category: "structures", + color: ["#adadad", "#70b8ba", "#adadad", "#70b8ba", "#adadad"], + }; + elements.building_2_seed = { + tick: function(pixel) { + for(cx = -4; cx <= 4; cx++) { + for(cy = -4; cy <= 4; cy++) { + if(cx === 0 && cy === 0) { + continue; + }; + var finalCoords = [pixel.x+cx,pixel.y+cy]; + if(isEmpty(...finalCoords,true)) { + continue; + } else { + var otherPixel = pixelMap[finalCoords[0]][finalCoords[1]]; + if(otherPixel.element === pixel.element) { + deletePixel(...finalCoords); + }; + }; + }; + }; + if(!isEmpty(pixel.x,pixel.y-1,true)) { + swapPixels(pixel,pixelMap[pixel.x][pixel.y-1]); + return; + }; + if(!tryMove(pixel,pixel.x,pixel.y+1)) { + var currentHeight = pixel.y + 2; + for(q = 0; q < buildingTwoSegments.length; q++) { + if(q >= buildingTwoSegments.length) { + break; + }; + loadPixelRowFromArray(buildingTwoSegments[q],pixel.x,currentHeight--,true,(q > 1)); + }; + }; + }, + excludeRandom: true, + desc: "Creates a miniature house.", + cooldown: 6, + state: "solid", + hardness: 1, + category: "structures", + color: ["#f05d43", "#f05d43", "#b06f33"], + }; + elements.room_seed = { + color: _cc.w.h, + tick: function(pixel) { + if(!tryMove(pixel,pixel.x,pixel.y+1)) { + var currentHeight = pixel.y; + for(q = oldRoom.length - 1; q > -1; q--) { + loadPixelRowFromArray(oldRoom[q],pixel.x,currentHeight--,true,true); + }; + }; + }, + desc: "Creates a large room.", + excludeRandom: true, + cooldown: 10, + state: "solid", + hardness: 1, + category: "structures", + }; + elements.altered_room_seed = { + color: _cc.w.h, + tick: function(pixel) { + if(!tryMove(pixel,pixel.x,pixel.y+1)) { + var currentHeight = pixel.y; + for(q = altRoom.length - 1; q > -1; q--) { + loadPixelRowFromArray(altRoom[q],pixel.x,currentHeight--,true,true); + }; + }; + }, + desc: "Creates a variant form of the large room used in the old nested structure test.", + excludeRandom: true, + cooldown: 10, + state: "solid", + hardness: 1, + category: "structures", + }; + elements.altroom_compat = { + name: "Altered Room (Old)", + hidden: true, + color: _cc.w.h, + desc: "An old version of the variant room, kept for compatibility because I don't know how to rework the structure test.", + tick: function(pixel) { + pixel.arr=[["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick"], + ["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "battery","brick", "brick", "brick", "brick", "brick"], + ["glass", "glass", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "light","light_bulb","air", "air", "air", "glass", "glass"], + ["glass", "glass", "light", "light", "air", "air", "air", "air", "air", "air", "air", "air", "air", "light", "air", "air", "air", "glass", "glass"], + ["brass", "wood", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "brass"], + ["wood", "wood", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["wood", "wood", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["wood", "wood", "air", "air", "iron", "straw", "straw", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "wood", "wood" ], + ["wood", "wood", "air", "air", "iron", "straw", "straw", "straw", "straw", "straw", "straw", "straw", "iron", "air", "air", "air", "air", "wood", "wood" ], + ["brass", "wood", "air", "air", "iron", "straw", "straw", "straw", "straw", "straw", "straw", "straw", "iron", "light", "air", "air", "air", "wood", "brass"], + ["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick"], + ["brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "brick", "battery", "brick", "brick", "brick", "brick", "brick"]] + aa = (0 - (Math.floor(pixel.arr[0].length / 2))) + na = Math.abs(aa) + if(pixel.arr[0].length % 2 == 1) { + bb = ((Math.floor(pixel.arr[0].length / 2)) + 1) + } else if(pixel.arr[0].length % 2 == 0) { + bb = (Math.floor(pixel.arr[0].length / 2)) + } + cc = (0 - (Math.floor(pixel.arr.length / 2))) + nc = Math.abs(cc) + if(pixel.arr.length % 2 == 1) { + dd = ((Math.floor(pixel.arr.length / 2)) + 1) + } else if(pixel.arr.length % 2 == 0) { + dd = (Math.floor(pixel.arr.length / 2)) + } + for (let j = cc; j < dd; j++) { + for (let i = aa; i < bb; i++) { + if(!isEmpty(pixel.x+i,pixel.y+j) && !outOfBounds(pixel.x+i,pixel.y+j)) { + if(pixel.arr[j+nc][i+na] != "air" || pixel.arr[j+nc][i+na] == "air") { + deletePixel(pixel.x+i,pixel.y+j) + } + } + if(pixel.arr[j+nc][i+na]) { + if(isEmpty(pixel.x+i,pixel.y+j) && pixel.arr[j+nc][i+na] != "air" && pixel.arr[j+nc][i+na] != "air" && !outOfBounds(pixel.x+i,pixel.y+j)) { + createPixel(pixel.arr[j+nc][i+na],pixel.x+i,pixel.y+j) + } + } + } + } + }, + category:"structures", + insulate: true, + state: "solid", + excludeRandom: true, + }, + elements.nested_structure_test = { + name: "Nested Structure Test (Old)", + color: _cc.w.h, + cooldown: 13, + desc: "An old test of structure spawners in structure spawners. Creates several rooms stacked on top of each other.", + tick: function(pixel) { + pixel.arr=[["altroom_compat", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + ["altroom_compat", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + ["altroom_compat", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + ["altroom_compat", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + ["altroom_compat", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ], + [ "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "brick", "brick" ]] + aa = (0 - (Math.floor(pixel.arr[0].length / 2))) + na = Math.abs(aa) + if(pixel.arr[0].length % 2 == 1) { + bb = ((Math.floor(pixel.arr[0].length / 2)) + 1) + } else if(pixel.arr[0].length % 2 == 0) { + bb = (Math.floor(pixel.arr[0].length / 2)) + } + cc = (0 - (Math.floor(pixel.arr.length / 2))) + nc = Math.abs(cc) + if(pixel.arr.length % 2 == 1) { + dd = ((Math.floor(pixel.arr.length / 2)) + 1) + } else if(pixel.arr.length % 2 == 0) { + dd = (Math.floor(pixel.arr.length / 2)) + } + for (let j = cc; j < dd; j++) { + for (let i = aa; i < bb; i++) { + if(!isEmpty(pixel.x+i,pixel.y+j) && !outOfBounds(pixel.x+i,pixel.y+j)) { + if(pixel.arr[j+nc][i+na] != "air" || pixel.arr[j+nc][i+na] == "air") { + deletePixel(pixel.x+i,pixel.y+j) + } + } + if(pixel.arr[j+nc][i+na]) { + if(isEmpty(pixel.x+i,pixel.y+j) && pixel.arr[j+nc][i+na] != "air" && pixel.arr[j+nc][i+na] != "air" && !outOfBounds(pixel.x+i,pixel.y+j)) { + createPixel(pixel.arr[j+nc][i+na],pixel.x+i,pixel.y+j) + } + } + } + } + }, + category:"structures", + insulate: true, + state: "solid", + excludeRandom: true, + }; + function _toggleDesertBuildings() { + var layer = worldgentypes.desert.layers[0]; + if(layer[1] !== "building_1_seed") { //if the first layer isn't a building layer, add one + worldgentypes.desert.layers.unshift([0.95,"building_1_seed",0.01]); + } else if(layer[1] === "building_1_seed") { //if the first layer is a building layer, remove it + worldgentypes.desert.layers.shift(); + }; + }; + //OLD WORLDGEN TEST ## + elements.worldgen_test = { + color: ["#787674", "#787674", "#787674", "#8c5923", "#8c5923", "#54c942", "#f7f0b0", "#5280eb"], + tick: function(pixel) { + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + if (isEmpty(i,j)) { + if(j >= Math.floor(6*height/7) && j < Math.floor(7*height/7)) { + if(Math.random() < 0.95) { + createPixel("rock",i,j) + } + } + if(j >= Math.floor(5*height/7) && j < Math.floor(6*height/7)) { + if(Math.random() < 0.95) { + if(Math.random() < 1/2) { + if(i >= Math.floor(11*width/14) && i < Math.floor(14*width/14)) { + createPixel("sand",i,j) + } else { + createPixel("dirt",i,j) + } + } else { + createPixel("rock",i,j) + } + } + } + if(j >= Math.floor(4*height/7) && j < Math.floor(5*height/7)) { + if(Math.random() < 0.95) { + if(i >= Math.floor(11*width/14) && i < Math.floor(14*width/14)) { + createPixel("sand",i,j) + } else { + createPixel("dirt",i,j) + } + } + } + if(j >= Math.floor(15*height/28) && j < Math.floor(4*height/7)) { + if(Math.random() < 0.95) { + if(i < Math.floor(11*width/14)) { + createPixel("grass",i,j) + } + } + } + } + } + } + explodeAt(Math.floor(51*width/56),Math.floor(9*height/14),Math.floor(1.8*height/7),fire="water") + deletePixel(pixel.x,pixel.y) + }, + category: "machines", + insulate: true, + state: "solid", + excludeRandom: true, + } + elements.nether_gen_test = { + color: ["#751318","#694a20","#f0771a"], + tick: function(pixel) { + for (var i = 1; i < width; i++) { + for (var j = 1; j < height; j++) { + if (isEmpty(i,j)) { + if(j >= Math.floor(5*height/7) && j < Math.floor(7*height/7)) { + if(Math.random() < 0.95) { + if(Math.random() < 2/3) { + if(i >= Math.floor(11*width/14) && i < Math.floor(14*width/14)) { + createPixel("soul_sand",i,j) + } else { + createPixel("netherrack",i,j) + } + } else { + createPixel("gravel",i,j) + } + } + } + if(j >= Math.floor(4*height/7) && j < Math.floor(5*height/7)) { + if(Math.random() < 0.95) { + if(i >= Math.floor(11*width/14) && i < Math.floor(14*width/14)) { + createPixel("soul_sand",i,j) + } else { + createPixel("netherrack",i,j) + } + } + } + } + } + } + explodeAt(Math.floor(51*width/56),Math.floor(10*height/14),Math.floor(1.8*height/7),fire="magma") + if(!isEmpty(Math.floor(51*width/56),Math.floor(10*height/14))) { + pixelMap[Math.floor(51*width/56)][Math.floor(10*height/14)].temp += 10**(3*(Math.floor(Math.log10(Math.sqrt((height**2)+(width**2)))))) + } + deletePixel(pixel.x,pixel.y) + }, + category: "machines", + insulate: true, + state: "solid", + excludeRandom: true, + } + //NOTE BLOCKS ## + audioContext = new AudioContext() + //Derived from marcgg's music.js + oscillatorDefaults = { + frequency: 440, + type: "sine", + endType: "none", + length: 1, + volume: 1, + delay: 0, + }; + audioObject = {}; + function oscillator(name="test",parameterObject=oscillatorDefaults){ //creates oscillator with gain node, has specifiable frequency and type, fades out over 1 second (hard-coded) + var defaultKeys = Object.keys(oscillatorDefaults); //readability variable + for(i = 0; i < defaultKeys.length; i++) { + var key = defaultKeys[i]; //the indexed keyname + if(typeof(parameterObject[key]) === "undefined") { + parameterObject[key] = oscillatorDefaults[key]; + }; + }; + var oscillatorNodeName = `${name}Oscillator`; + var gainNodeName = `${name}Gain`; + audioObject[oscillatorNodeName] = audioContext.createOscillator() + audioObject[gainNodeName] = audioContext.createGain() + audioObject[gainNodeName].gain.value = parameterObject.volume; + audioObject[oscillatorNodeName].type = parameterObject.type + audioObject[oscillatorNodeName].connect(audioObject[gainNodeName]) + audioObject[oscillatorNodeName].frequency.value = parameterObject.frequency + audioObject[gainNodeName].connect(audioContext.destination) + audioObject[oscillatorNodeName].start(audioContext.currentTime + (parameterObject.delay)) + //stopping handler + if(parameterObject.endType === "exponential") { //starts fading immediately + audioObject[gainNodeName].gain.exponentialRampToValueAtTime( + 0.00001, audioContext.currentTime + parameterObject.length + ); + } else if(parameterObject.endType === "linear") { //starts fading immediately + audioObject[gainNodeName].gain.linearRampToValueAtTime( + 0.00001, audioContext.currentTime + parameterObject.length + ); + } else { //waits to stop + audioObject[oscillatorNodeName].stop(audioContext.currentTime + parameterObject.delay + parameterObject.length); + }; + }; + elements.note_block = { + color: "#ee33ee", + behavior: behaviors.WALL, + state: "solid", + category: "machines", + density: 1200, + hardness: 0.2, + breakInto: ["plastic","metal_scrap","metal_scrap","metal_scrap"], + conduct: 1, + properties: { + type: "sine", + endType: "none", + 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, + type: pixel.type, + endType: pixel.endType, + length: pixel.length, + volume: pixel.volume, + delay: pixel.delay, + }; + //console.log(pixelPropertyObject); + if(pixel.debounce < 1) { + //console.log(`${pixel.debounce} not debounced, play`); + if(pixel.charge) { + oscillator(pixelSoundName,pixelPropertyObject); + delete pixel.charge; + pixel.debounce = pixel.debounceLength; + }; + } else if(pixel.debounce > 0) { + //console.log(`${pixel.debounce} debounced, don't play`); + pixel.debounce--; + }; + }, + }; + runAfterLoad(function() { + elements.note_block.movable = false; + }); + if(runAfterAutogen) { + runAfterAutogen(function() { + elements.note_block.movable = false; + }); + }; + //STATE-SPECIFIC VOIDS ## + //Deletion code mostly by R74n + elements.drain = { + color: "#888888", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state === "liquid") { + deletePixel(coord[0],coord[1]); + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A drain that removes any liquid.", + hardness: 0.8, + insulate: true, + }; + elements.vent = { + color: "#e6e6e6", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state === "gas") { + deletePixel(coord[0],coord[1]); + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A vent that removes any gas. Remarkably sussy.", + hardness: 0.8, + insulate: true, + }; elements.wall.movable = false; chuteExcludedElements = ["wall","drain","vent","chute","hole_of_miscellanea","drent","drute","vute","drolent","drolute","volute","void"]; elements.acid.ignore.push("drain"); elements.acid_gas.ignore.push("vent"); + elements.chute = { + color: "#636363", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state === "solid" && elements[newPixel.element].movable === true && !chuteExcludedElements.includes(newPixel.element)) { + deletePixel(coord[0],coord[1]); + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A chute that removes any powder.", + movable: false, + hardness: 0.8, + insulate: true, + }; mainStateArray = ["solid","liquid","gas"]; + elements.hole_of_miscellanea = { + color: "#69606b", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].movable && !mainStateArray.includes(elements[newPixel.element].state)) { + deletePixel(coord[0],coord[1]); + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A mysterious hole that removes the other states of matter.", + movable: false, + insulate: true, + }; + elements.drent = { + color: "#B7B7B7", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state === "liquid" || elements[newPixel.element].state === "gas") { + deletePixel(coord[0],coord[1]); + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A combined drain and vent that removes any liquid or gas. Slightly sussy.", + hardness: 0.8, + insulate: true, + }; + elements.drute = { + color: "#767676", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state === "liquid") { + deletePixel(coord[0],coord[1]); + } else if (elements[newPixel.element].state === "solid" && elements[newPixel.element].movable === true && !chuteExcludedElements.includes(newPixel.element)) { + deletePixel(coord[0],coord[1]); + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A combined drain and chute that removes any liquid or powder.", + hardness: 0.8, + insulate: true, + }; + elements.vute = { + color: "#9d8aa1", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state === "gas") { + deletePixel(coord[0],coord[1]); + } else if (elements[newPixel.element].state === "solid" && elements[newPixel.element].movable === true && !chuteExcludedElements.includes(newPixel.element)) { + deletePixel(coord[0],coord[1]); + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A vent that removes any gas. Somewhat sussy.", + hardness: 0.8, + insulate: true, + }; + elements.drolent = { + color: "#b8afba", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state !== "solid") { + deletePixel(coord[0],coord[1]); + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A combined drain, hole, and vent removes anything but powders. Slightly sussy.", + hardness: 0.8, + insulate: true, + }; + elements.drolute = { + color: "#786c7a", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state === "solid" && elements[newPixel.element].movable === true && !chuteExcludedElements.includes(newPixel.element)) { + deletePixel(coord[0],coord[1]); + } else { + if (elements[newPixel.element].state !== "solid" && elements[newPixel.element].state !== "gas") { + deletePixel(coord[0],coord[1]); + } + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A combined drain, hole, and chute removes anything but gases.", + hardness: 0.8, + insulate: true, + }; + elements.volute = { + color: "#b8afba", + behavior: behaviors.WALL, + tick: function(pixel) { + var coordsToCheck = [ + [pixel.x-1,pixel.y], + [pixel.x+1,pixel.y], + [pixel.x,pixel.y-1], + [pixel.x,pixel.y+1], + ]; + for (var i = 0; i < coordsToCheck.length; i++) { + var coord = coordsToCheck[i]; + if (!isEmpty(coord[0],coord[1],true)) { + var newPixel = pixelMap[coord[0]][coord[1]]; + if (elements[newPixel.element].state === "solid" && elements[newPixel.element].movable === true && !chuteExcludedElements.includes(newPixel.element)) { + deletePixel(coord[0],coord[1]); + } else { + if (elements[newPixel.element].state !== "solid" && elements[newPixel.element].state !== "liquid") { + deletePixel(coord[0],coord[1]); + } + } + } + } + }, + category: "special", + tempHigh: 1455.5, + stateHigh: "molten_steel", + state: "solid", + density: 2000, + breakInto: ["metal_scrap"], + desc: "A combined vent, hole, and chute removes anything but liquids.", + hardness: 0.8, + insulate: true, + }; + //The all-combination is called void. + elements.drain.breakInto = ["steel_scrap"]; + elements.vent.breakInto = ["steel_scrap"]; + elements.chute.breakInto = ["steel_scrap"]; + elements.hole_of_miscellanea.breakInto = ["steel_scrap"]; + elements.drain.breakInto.push("magic"); + elements.vent.breakInto.push("magic"); + elements.chute.breakInto.push("magic"); + elements.hole_of_miscellanea.breakInto.push("magic"); + //THERMAL CONDUITS ## + //Base element, uninitialized IO + elements.thermal_conduit = { + hidden: true, + category: "thermal conduits", + hardness: 1, + state: "solid", + density: 3000, + temp: (settings.abszero ?? -273.15), + behavior: behaviors.WALL, + color: "#cf9f7f", + insulate: true, + properties: { + inputs: [], + outputs: [], + rate: 2 + }, + tick: function(pixel) { + if(pixel.inputs.length == 0 && pixel.outputs.length == 0) { + return; + }; + //Iterate through inputs + for(var i = 0; i < pixel.inputs.length; i++) { + //Readability variable for offset pair + var coordPair = pixel.inputs[i]; + //Coord RVs + var newX = pixel.x + coordPair[0]; + var newY = pixel.y + coordPair[1]; + //Skip empties + if(isEmpty(newX,newY,true)) { + continue; + } else { + //New pixel RV + var newPixel = pixelMap[newX][newY]; + //More sugar + var newPixelTempKelvin = newPixel.temp - (settings.abszero ?? -273.15); + //Skip pixels at or below absolute zero + if(newPixelTempKelvin <= 0) { + continue; + }; + //If temp withdrawal would put pixel below absolute zero + if(newPixelTempKelvin <= pixel.rate) { + //Special "draining" logic + pixel.temp += newPixelTempKelvin; + newPixel.temp = (settings.abszero ?? -273.15); + } else { + //If not, just move the temperature + pixel.temp += pixel.rate; + newPixel.temp -= pixel.rate; + }; + pixelTempCheck(pixel); + pixelTempCheck(newPixel); + }; + }; + if(pixelTempKelvin <= 0 || pixel.outputs.length == 0) { + return false; + }; + //Iterate through outputs + var availableOutputs = []; + //Adjust effective output count; + for(var i = 0; i < pixel.outputs.length; i++) { + var coordPair = pixel.outputs[i]; + var newX = pixel.x + coordPair[0]; + var newY = pixel.y + coordPair[1]; + if(!isEmpty(newX,newY,true)) { + availableOutputs.push([newX,newY]); + }; + }; + var pixelTempKelvin = pixel.temp - (settings.abszero ?? -273.15); + var isDraining = (pixelTempKelvin <= pixel.rate); + var effectiveRate = (isDraining ? pixelTempKelvin : pixel.rate) / availableOutputs.length; + //Actual distribution + for(var i = 0; i < availableOutputs.length; i++) { + var coordPair = availableOutputs[i]; + var newPixel = pixelMap[coordPair[0]][coordPair[1]]; + newPixel.temp += effectiveRate; + }; + if(availableOutputs.length > 0) { isDraining ? pixel.temp = (settings.abszero ?? -273.15) : pixel.temp -= pixel.rate }; + }, + }; + function defineConduitElement(nameDescriber,inputOffsetNestedArray,outputOffsetNestedArray) { + //There is no validation here. Please don't do anything stupid. + //Pretty please don't run after script loading, i don't feel like adding that code in. + var autoName = `thermal_${nameDescriber}_conduit`; + //console.log(inputOffsetNestedArray); + elements[autoName] = { + category: "thermal conduits", + properties: { + inputs: inputOffsetNestedArray, + outputs: outputOffsetNestedArray, + rate: 2, + }, + hardness: 1, + state: "solid", + density: 3000, + temp: (settings.abszero ?? -273.15), + behavior: behaviors.WALL, + color: "#cf9f7f", + insulate: true, + tick: function(pixel) { + pixel.element = "thermal_conduit"; //manual change to preserve properties + }, + }; + return autoName; + } + autoConduitTable = { + //up_to_up: useless, + up_to_down: {ins: [[0,-1]], outs: [[0,1]]}, + up_to_left: {ins: [[0,-1]], outs: [[-1,0]]}, + up_to_right: {ins: [[0,-1]], outs: [[1,0]]}, + down_to_up: {ins: [[0,1]], outs: [[0,-1]]}, + //down_to_down: useless, + down_to_left: {ins: [[0,1]], outs: [[-1,0]]}, + down_to_right: {ins: [[0,1]], outs: [[1,0]]}, + left_to_up: {ins: [[-1,0]], outs: [[0,-1]]}, + left_to_down: {ins: [[-1,0]], outs: [[0,1]]}, + //left_to_left: useless, + left_to_right: {ins: [[-1,0]], outs: [[1,0]]}, + right_to_up: {ins: [[1,0]], outs: [[0,-1]]}, + right_to_down: {ins: [[1,0]], outs: [[0,1]]}, + right_to_left: {ins: [[1,0]], outs: [[-1,0]]}, + //right_to_right: useless, + left_and_right_to_down: {ins: [[-1,0],[1,0]], outs: [[0,1]]}, + up_and_down_to_left: {ins: [[0,-1],[0,1]], outs: [[-1,0]]}, + left_and_right_to_up: {ins: [[-1,0],[1,0]], outs: [[0,-1]]}, + up_and_down_to_right: {ins: [[0,-1],[0,1]], outs: [[1,0]]}, + down_to_left_and_right: {outs: [[-1,0],[1,0]], ins: [[0,1]]}, + left_to_up_and_down: {outs: [[0,-1],[0,1]], ins: [[-1,0]]}, + up_to_left_and_right: {outs: [[-1,0],[1,0]], ins: [[0,-1]]}, + right_to_up_and_down: {outs: [[0,-1],[0,1]], ins: [[1,0]]}, + up_down_and_left_to_right: {ins: [[0,-1],[0,1],[-1,0]], outs: [[1,0]]}, + up_left_and_right_to_down: {ins: [[0,-1],[-1,0],[1,0]], outs: [[0,1]]}, + up_down_and_right_to_left: {ins: [[0,-1],[0,1],[1,0]], outs: [[-1,0]]}, + down_left_and_right_to_up: {ins: [[0,1],[-1,0],[1,0]], outs: [[0,-1]]}, + right_to_up_down_and_left: {outs: [[0,-1],[0,1],[-1,0]], ins: [[1,0]]}, + down_to_up_left_and_right: {outs: [[0,-1],[-1,0],[1,0]], ins: [[0,1]]}, + left_to_up_down_and_right: {outs: [[0,-1],[0,1],[1,0]], ins: [[-1,0]]}, + up_to_down_left_and_right: {outs: [[0,1],[-1,0],[1,0]], ins: [[0,-1]]}, + left_and_down_to_right: {ins: [[-1,0],[0,1]], outs: [[1,0]]}, + right_and_down_to_left: {ins: [[1,0],[0,1]], outs: [[-1,0]]}, + left_and_up_to_right: {ins: [[-1,0],[0,-1]], outs: [[1,0]]}, + right_and_up_to_left: {ins: [[1,0],[0,-1]], outs: [[-1,0]]}, + right_to_down_and_left: {ins: [[1,0]], outs: [[-1,0],[0,1]]}, + right_to_up_and_left: {ins: [[1,0]], outs: [[-1,0],[0,-1]]}, + left_to_down_and_right: {ins: [[-1,0]], outs: [[1,0],[0,1]]}, + left_to_up_and_right: {ins: [[1,0]], outs: [[1,0],[0,-1]]}, + }; + for(direction in autoConduitTable) { + defineConduitElement(direction,autoConduitTable[direction].ins,autoConduitTable[direction].outs); + }; + //WIRELESS TRANSMISSION ## + //https://stackoverflow.com/a/60922255 + elements.wifi = { + color: "#bfff7f", + properties: { + _channel: 0, + _correspondingWifi: null + }, + hardness: 0.8, + breakInto: ["plastic","steel","copper"], + conduct: 1, + insulate: true, + tick: function(pixel) { + pixel._channel = Math.floor(pixel.temp / 100); + var colorBase = (pixel._channel + 3); + if(colorBase < 0 || colorBase > 124) { + pixel.color == "rgb(212,185,222)"; + } else { + colorBase = colorBase.toString(5).padStart(3,"0").split("").map(x => parseInt(x) * 64); + pixel.color = `rgb(${colorBase.join(",")})` + }; + pixel._correspondingWifi = currentPixels.filter(function(pixelToCheck) { + return ( + pixelToCheck !== pixel && //should work if this pixel is the same as the other one by reference + ["wifi","receiver"].includes(pixelToCheck.element) && + pixelToCheck._channel == pixelChannel + ); + },pixelChannel=pixel._channel).map(pixel => [pixel.x,pixel.y]); + if(pixel.charge) { + for(var i in pixel._correspondingWifi) { + i = parseInt(i); + var wifiCoords = pixel._correspondingWifi[i]; + var newPixel = pixelMap[wifiCoords[0]]?.[wifiCoords[1]]; + if(newPixel) { + if(!newPixel.chargeCD) { + for(var j in adjacentCoords) { + j = parseInt(j); + var pixelAdjacentToWifi = pixelMap[newPixel.x+adjacentCoords[j][0]]?.[newPixel.y+adjacentCoords[j][1]]; + if(pixelAdjacentToWifi) { pixelAdjacentToWifi.charge = 1 }; + }; + } + } + }; + if(pixel._correspondingWifi.length > 0) { + delete pixel.charge; + pixel.chargeCD = 5 + } + } + if(typeof(pixel.chargeCD) !== "undefined") { + pixel.chargeCD--; + if(pixel.chargeCD <= 0) { delete pixel.chargeCD }; + }; + }, + category: "machines", + state: "solid", + }; + elements.transmitter = { + color: "#00ff7f", + properties: { + _channel: 0, + _correspondingWifi: null + }, + hardness: 0.8, + breakInto: ["plastic","steel","copper"], + conduct: 1, + insulate: true, + tick: function(pixel) { + pixel._channel = Math.floor(pixel.temp / 100); + var colorBase = (pixel._channel + 3); + if(colorBase < 0 || colorBase > 124) { + pixel.color == "rgb(212,185,222)"; + } else { + colorBase = colorBase.toString(5).padStart(3,"0").split("").map(x => parseInt(x) * 64); + pixel.color = `rgb(${colorBase.join(",")})` + }; + pixel._correspondingWifi = currentPixels.filter(function(pixelToCheck) { + return ( + pixelToCheck !== pixel && //should work if this pixel is the same as the other one by reference + ["wifi","receiver","support_receiver"].includes(pixelToCheck.element) && + pixelToCheck._channel == pixelChannel + ); + },pixelChannel=pixel._channel).map(pixel => [pixel.x,pixel.y]); + if(pixel.charge) { + for(var i in pixel._correspondingWifi) { + i = parseInt(i); + var wifiCoords = pixel._correspondingWifi[i]; + var newPixel = pixelMap[wifiCoords[0]]?.[wifiCoords[1]]; + if(newPixel) { + if(!newPixel.chargeCD) { + for(var j in adjacentCoords) { + j = parseInt(j); + var pixelAdjacentToWifi = pixelMap[newPixel.x+adjacentCoords[j][0]]?.[newPixel.y+adjacentCoords[j][1]]; + if(pixelAdjacentToWifi && elements[pixelAdjacentToWifi.element].conduct) { pixelAdjacentToWifi.charge = 1 }; + }; + } + } + }; + if(pixel._correspondingWifi.length > 0) { + delete pixel.charge; + pixel.chargeCD = 5 + } + } + if(typeof(pixel.chargeCD) !== "undefined") { + pixel.chargeCD--; + if(pixel.chargeCD <= 0) { delete pixel.chargeCD }; + }; + }, + category: "machines", + state: "solid", + } + elements.receiver = { + color: "#bfff00", + properties: { + _channel: 0 + }, + hardness: 0.8, + breakInto: ["plastic","steel","copper"], + conduct: 1, + insulate: true, + tick: function(pixel) { + pixel._channel = Math.floor(pixel.temp / 100); + var colorBase = (pixel._channel + 3); + if(colorBase < 0 || colorBase > 124) { + pixel.color = "rgb(212,185,222)"; + } else { + colorBase = colorBase.toString(5).padStart(3,"0").split("").map(x => parseInt(x) * 64); + pixel.color = `rgb(${colorBase.join(",")})` + }; + if(typeof(pixel.chargeCD) !== "undefined") { + pixel.chargeCD = Math.min(pixel.chargeCD,5); + pixel.chargeCD--; + if(pixel.chargeCD <= 0) { delete pixel.chargeCD }; + }; + if(pixel.charge) { + pixel.charge -= 0.25; + if(pixel.charge <= 0) { delete pixel.charge }; + }; + }, + category: "machines", + state: "solid", + }; + elements.support_receiver = { + color: "#bfff00", + behavior: behaviors.SUPPORT, + properties: { + _channel: 0 + }, + hardness: 0.8, + breakInto: ["plastic","steel","copper"], + conduct: 1, + insulate: true, + tick: function(pixel) { + pixel._channel = Math.floor(pixel.temp / 100); + var colorBase = (pixel._channel + 3); + if(colorBase < 0 || colorBase > 124) { + pixel.color = "rgb(212,185,222)"; + } else { + colorBase = colorBase.toString(5).padStart(3,"0").split("").map(x => parseInt(x) * 64); + pixel.color = `rgb(${colorBase.join(",")})` + }; + if(typeof(pixel.chargeCD) !== "undefined") { + pixel.chargeCD = Math.min(pixel.chargeCD,5); + pixel.chargeCD--; + if(pixel.chargeCD <= 0) { delete pixel.chargeCD }; + }; + if(pixel.charge) { + pixel.charge -= 0.25; + if(pixel.charge <= 0) { delete pixel.charge }; + }; + }, + category: "machines", + state: "solid", + }; + // SPINEL'S INJECTOR ## + var injectorPoisonCategories = ["life","auto creepers","shit","cum","food","fantastic creatures","fey","auto fey"]; + var injectorPoisonBlacklist = ["injector_poison","dead_matter","poisoned_dirt"]; + var injectorPoisonWhitelist = ["blood","skin","hair","poop","blood_ice","wood","wood_plank","sawdust","straw","paper","birthpool","dried_poop","gloomfly","meat_monster","rotten_ravager","bone_beast","withery","withery_plant","banana","apple","rotten_apple","apioform_player","apioform_bee","apioform","apiodiagoform","sugar_cactus","sugar_cactus_seed","flowering_sugar_cactus","tree_branch","sap","silk","red_velvet","silk_velvet","ketchup", "enchanted_ketchup", "frozen_ketchup", "poisoned_ketchup", "frozen_poisoned_ketchup", "ketchup_spout", "ketchup_cloud", "poisoned_ketchup_cloud", "ketchup_snow", "ketchup_snow_cloud", "poisoned_ketchup_snow", "poisoned_ketchup_snow_cloud", "ketchup_gas", "poisoned_ketchup_gas", "ketchup_powder", "poisoned_ketchup_powder", "eketchup_spout", "ketchup_metal", "antiketchup", "dirty_ketchup", "ketchup_gold", "molten_ketchup_metal", "ketchup_fairy", "ketchup_metal_scrap", "ketchup_gold_scrap", "molten_ketchup_gold", "mycelium","vaccine","antibody","infection","sap","caramel","molasses","melted_chocolate","soda","mustard","fry_sauce","tomato_sauce","sugary_tomato_sauce","bio_ooze","zombie_blood","feather","tooth","decayed_tooth","plaque","tartar","bacteria","replacer_bacteria","pop_rocks"]; + var injectorPoisonSubstitutions = { + "dirt": "poisoned_dirt", + "dry_dirt": "poisoned_dirt", + "crimsoil": "poisoned_dirt", + "rainbow_dirt": "poisoned_dirt", + "sand": "poisoned_dirt", + "wet_sand": "poisoned_dirt", + "mud": "poisoned_dirt", + "sandy_water": "injector_poison", + "water": "injector_poison", + }; + var blue = "rgb(0,0,255)"; + var cyan = "rgb(0,255,255)"; + var green = "rgb(0,255,0)"; + var yellow = "rgb(255,255,0)"; + var orange = "rgb(255,127,0)"; + var red = "rgb(255,0,0)"; + var gray = "rgb(127,127,127)"; + function debugPoisonColor(pixel) { + pixel.poison ??= 0; + if(pixel.poison > 5) { + pixel.color = blue; + } else if(pixel.poison > 1.5) { + pixel.color = cyan; + } else if(pixel.poison > 1) { + pixel.color = green; + } else if(pixel.poison > 0.5) { + pixel.color = yellow; + } else if(pixel.poison > 0.05) { + pixel.color = orange; + } else if(pixel.poison > 0) { + pixel.color = red; + } else { + pixel.color = gray; + }; + }; + function spreadInjectorPoison(pixel) { + var convertedPixels = []; + for(i = 0; i < adjacentCoords.length; i++) { //iterate through neighbor spots + if(!isEmpty(pixel.x+adjacentCoords[i][0],pixel.y+adjacentCoords[i][1],true)) { //check for adjacentCoords + var newPixel = pixelMap[pixel.x+adjacentCoords[i][0]][pixel.y+adjacentCoords[i][1]] + var isInjectorPoisonFairy = (elements[newPixel.element].category == "auto fey" && newPixel.element.includes("life_eater_")) + //console.log(newPixel.element,isInjectorPoisonFairy); + if( + (injectorPoisonCategories.includes(elements[newPixel.element].category) || injectorPoisonWhitelist.includes(newPixel.element) || Object.keys(injectorPoisonSubstitutions).includes(newPixel.element)) && + !injectorPoisonBlacklist.includes(newPixel.element) && + !isInjectorPoisonFairy //exclude fairies which produce life eater + ) { + if(Object.keys(injectorPoisonSubstitutions).includes(newPixel.element)) { + if(["water","sandy_water"].includes(newPixel.element) && Math.random() < 0.8) { + continue; + }; + var data = injectorPoisonSubstitutions[newPixel.element]; + while(data instanceof Array) { + data = data[Math.floor(Math.random() * data.length)]; + }; + if(data === null) { + if(newPixel) { deletePixel(newPixel.x,newPixel.y) }; + } else { + changePixel(newPixel,data); + convertedPixels.push(newPixel); + }; + } else { + changePixel(newPixel,"dead_matter"); + convertedPixels.push(newPixel); + }; + }; + }; + }; + return convertedPixels.length == 0 ? false : convertedPixels; + }; + elements.injector_poison = { + properties: { + didWeakColorChange: 0, + poison: 15 + }, + stain: 0.2, + color: ["#f70a98", "#ff308d"], + behavior: behaviors.LIQUID, + tick: function(pixel) { + //console.log(pixel.poison); + pixel.didWeakColorChange ??= 0; + if(isNaN(pixel.poison)) { + pixel.poison = 15; + return; + }; + var convertedPixels = spreadInjectorPoison(pixel); + if((convertedPixels.length ?? 0) !== 0) { + for(i = 0; i < convertedPixels.length; i++) { + convertedPixels[i].poison ??= Math.max(convertedPixels[i].element == "injector_poison" ? 0.9 : 0,pixel.poison - 1); + }; + pixel.poison--; + if(pixel.poison <= -1) { + deletePixel(pixel.x,pixel.y); + return; + }; + }; + if(pixel.poison < 5 && pixel.didWeakColorChange == 0) { + pixel.color = changeSaturation(changeLightness(pixel.color,0.7 ** (1/3),"multiply","hsljson"),0.7 ** (1/3),"multiply","rgb"); + pixel.didWeakColorChange = 1; + }; + if(pixel.poison < 1 && pixel.didWeakColorChange == 1) { + pixel.color = changeSaturation(changeLightness(pixel.color,0.7 ** (1/3),"multiply","hsljson"),0.7 ** (1/3),"multiply","rgb"); + pixel.didWeakColorChange = 2; + }; + if(pixel.poison < 0.1 && pixel.didWeakColorChange == 2) { + pixel.color = changeSaturation(changeLightness(pixel.color,0.7 ** (1/3),"multiply","hsljson"),0.7 ** (1/3),"multiply","rgb"); + pixel.didWeakColorChange = 3; + }; + if(pixel.poison >= 0.1 && pixel.didWeakColorChange == 3) { + pixel.color = changeSaturation(changeLightness(pixel.color,1/(0.7 ** (1/3)),"multiply","hsljson"),1/(0.7 ** (1/3)),"multiply","rgb"); + pixel.didWeakColorChange = 2; + }; + if(pixel.poison >= 1 && pixel.didWeakColorChange == 2) { + pixel.color = changeSaturation(changeLightness(pixel.color,1/(0.7 ** (1/3)),"multiply","hsljson"),1/(0.7 ** (1/3)),"multiply","rgb"); + pixel.didWeakColorChange = 1; + }; + if(pixel.poison >= 5 && pixel.didWeakColorChange == 1) { + pixel.color = changeSaturation(changeLightness(pixel.color,1/(0.7 ** (1/3)),"multiply","hsljson"),1/(0.7 ** (1/3)),"multiply","rgb"); + pixel.didWeakColorChange = 0; + }; + if(pixel.poison <= 0 && Math.random() < 0.1) { + deletePixel(pixel.x,pixel.y); + return; + }; + if(isNaN(pixel.poison)) { + pixel.poison = 15; + }; + for(z = 0; z < 3; z++) { + spreadingPropertyReturn(pixel,"poison",injectorPoisonBlacklist).forEach(x => spreadingPropertyReturn(x,"poison",injectorPoisonBlacklist)); + }; + //debugPoisonColor(pixel); + }, + category: "weapons", + state: "liquid", + density: 1000, + excludeRandom: true, + /*tempHigh: 300, + stateHigh: null,*/ + forceAutoGen: true + }; + elements.dead_matter = { + color: ["#6b5869", "#99508c", "#b53c8b"], + behavior: behaviors.POWDER, + tick: function(pixel) { + if(isNaN(pixel.poison)) { + pixel.poison = 10; + }; + if(pixel.poison > 0.05) { + var convertedPixels = spreadInjectorPoison(pixel); + if((convertedPixels.length ?? 0) !== 0) { + if(Math.random() < 0.2) { + var randomConverted = randomChoice(convertedPixels); + swapNumericPropertyValues(pixel,randomConverted,"poison",injectorPoisonBlacklist); + }; + for(i = 0; i < convertedPixels.length; i++) { + convertedPixels[i].poison ??= Math.max(0,pixel.poison - 1); + }; + pixel.poison--; + }; + }; + for(z = 0; z < 3; z++) { + spreadingPropertyReturn(pixel,"poison",injectorPoisonBlacklist).forEach(x => spreadingPropertyReturn(x,"poison",injectorPoisonBlacklist)); + }; + if(pixel.poison < 0) { + pixel.poison = 0 + }; + if(pixel.temp > 350 || (pixel.poison <= 1 && Math.random() < 0.04)) { + if(Math.random() < pixel.poison) { + changePixelReturn(pixel,"injector_poison").poison = pixel.poison * 0.85; + return; + } else { + deletePixel(pixel.x,pixel.y); + return; + }; + }; + //debugPoisonColor(pixel); + }, + category: "life", + state: "solid", + density: 1050, + excludeRandom: true, + }; + elements.poisoned_dirt = { + behavior: behaviors.POWDER, + color: ["#665e66","#735370","#805265"], + tick: function(pixel) { + if(isNaN(pixel.poison)) { + pixel.poison = 10; + }; + if(pixel.poison > 0.05) { + var convertedPixels = spreadInjectorPoison(pixel); + if((convertedPixels.length ?? 0) !== 0) { + if(Math.random() < 0.2) { + var randomConverted = randomChoice(convertedPixels); + swapNumericPropertyValues(pixel,randomConverted,"poison",injectorPoisonBlacklist); + }; + for(i = 0; i < convertedPixels.length; i++) { + convertedPixels[i].poison ??= Math.max(0,pixel.poison - 1); + }; + pixel.poison--; + }; + }; + for(z = 0; z < 3; z++) { + spreadingPropertyReturn(pixel,"poison",injectorPoisonBlacklist).forEach(x => spreadingPropertyReturn(x,"poison",injectorPoisonBlacklist)); + }; + if(pixel.poison < 0) { + pixel.poison = 0 + }; + //debugPoisonColor(pixel); + }, + category: "life", + state: "solid", + density: 1220, + excludeRandom: true, + }; + //Injector data and decoding + var injectorCrystalPinks = { + "0": "rgb(239,109,189)", + "1": "rgb(253,185,241)", + "2": "rgb(253,207,234)", + "3": "rgb(253,161,225)", + "4": "rgb(253,156,206)", + "5": "rgb(249,139,249)", + "6": "rgb(235,95,190)", + "7": "rgb(221,26,132)", + "8": "rgb(246,68,183)", + "9": "rgb(184,24,95)", + "~": "rgb(221,26,132)", + "!": "rgb(230,64,191)", + "@": "rgb(136,12,60)", + "#": "rgb(221,26,132)" + } + var poisonPink = "rgb(253,54,196)"; + var injectorMetalColors = { + "A": "rgb(82,95,136)", + "B": "rgb(35,57,94)", + "C": "rgb(60,79,120)", + "D": "rgb(74,100,148)", + "E": "rgb(44,64,101)", + "F": "rgb(242,45,166)" //pink lines + }; + var vanishingMetalColors = { + "X": "rgb(60,79,120)", + "Y": "rgb(242,45,166)" //pink lines + }; + var glassColor = "rgb(168,206,217)"; //G + var red = "rgb(255,0,0)"; //for unknown item + injectorData = [ + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : :0:1:2:2:2:2:2:2:2:2:3: : : : : : : ", + " : : : : :0:0:1:2:2:2:2:2:2:2:2:2:2:4:4: : : : : ", + " : : : : :5:5:6:6:6:6:6:6:6:7:7:7:7:8:8: : : : : ", + " : : : : :5:5:6:6:6:6:6:6:7:7:7:7:7:8:8: : : : : ", + " : : : : : :5:5:6:6:6:6:7:7:7:7:7:8:8: : : : : : ", + " : : : : : :5:5:6:6:6:7:7:7:7:7:7:8:8: : : : : : ", + " : : : : : :5:5:6:6:7:7:7:7:7:7:7:8:8: : : : : : ", + " : : : : : :5:5:6:7:7:7:7:7:7:7:7:8:8: : : : : : ", + " : : : : : : :5:5:7:7:7:7:7:7:7:8:8: : : : : : : ", + " : : : : : : :5:5:7:7:7:7:7:7:7:8:8: : : : : : : ", + " : : : : : : :9:9:9:~:!:!:!:@:#:#:#: : : : : : : ", + " : : : : : : : :B:9:B:~:!:@:B:#:B: : : : : : : : ", + " : : : : : : :A:A:A:A:A:A:A:A:A:A:A: : : : : : : ", + " : : : : : : : :B:B:B:B:B:B:B:B:B: : : : : : : : ", + " : : : : : : :A:A:A:A:A:A:A:A:A:A:A: : : : : : : ", + " : : : : : : :G:G:G:G:G:G:G:G:G:G:G: : : : : : : ", + " : : : : : : :G:$:$:$:$:$:$:$:$:$:G: : : : : : : ", + " : : : : : : :G:$:$:$:$:$:$:$:$:$:G: : : : : : : ", + " : : : : : : :G:$:$:$:$:$:$:$:$:$:G: : : : : : : ", + " : : : : : : :G:G:$:$:$:$:$:$:$:G:G: : : : : : : ", + " : : : : : : : :G:$:$:$:$:$:$:$:G: : : : : : : : ", + " : : : : : : : :G:$:$:$:$:$:$:$:G: : : : : : : : ", + " : : : : : : : :G:$:$:$:$:$:$:$:G: : : : : : : : ", + " : : : : : : : :G:$:$:$:$:$:$:$:G: : : : : : : : ", + " : : : : : : :G:G:$:$:$:$:$:$:$:G:G: : : : : : : ", + " : : : : : : :G:$:$:$:$:$:$:$:$:$:G: : : : : : : ", + " : : : : : :G:G:$:$:$:$:$:$:$:$:$:G:G: : : : : : ", + " : : : : : :G:$:$:$:$:$:$:$:$:$:$:$:G: : : : : : ", + " : : : : : :G:$:$:$:$:$:$:$:$:$:$:$:G: : : : : : ", + " : : : : : :G:$:$:$:$:$:$:$:$:$:$:$:G: : : : : : ", + " : : :C: :G:G:G:G:G:G:G:X:G:G:G:G:G:G:G: :C: : : ", + " : :C:C: :C:C:C:C:C:C:C:X:C:C:C:C:C:C:C: :C:C: : ", + " :C:C:C: : :D:C:C:C:C:C:X:C:C:C:C:C:D: : :C:C:C: ", + " :C:C:E:C: :C:D: :F:C:C:Y:C:C:F: :D:C: :C:E:C:C: ", //H = seed location + "C:C:C: :C:C:C: : :C:C:F:X:C:F:C: : :C:C:C: :C:C:C", + "E:C:C: : :C: : : : :F:C:X:F:C: : : : :C: : :C:C:E", + "C:E:E: : : : : : : :C:C:Y:C:C: : : : : : : :E:E:C", + "C:C:C: : : : : : : : :F:X:C: : : : : : : : :C:C:C", + " :C:C:C: : : : : : : :C:X:F: : : : : : : :C:C:C: ", + " :C:C:C: : : : : : : : :Y: : : : : : : : :C:C:C: ", + " : :C:C: : : : : : : : :X: : : : : : : : :C:C: : ", + " : : :C: : : : : : : : : : : : : : : : : :C: : : " + ]; + injectorColorData = [ + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : ", + " : : : : : : : : : : : : : : : : : : : : : : : : " + ]; + injectorData = injectorData.map(x => x.split(":")); + injectorColorData = injectorColorData.map(x => x.split(":")); + function injectorDataDecoder(character) { + var element, color; + if(injectorCrystalPinks[character]) { + element = "glass"; + color = injectorCrystalPinks[character]; + } else if(injectorMetalColors[character]) { + element = "steel"; + color = injectorMetalColors[character]; + } else if(vanishingMetalColors[character]) { + element = "vanishing_steel"; + color = vanishingMetalColors[character]; + } else { + switch(character) { + case "$": + element = "injector_poison"; + color = poisonPink; + break; + case "G": + element = "glass"; + color = glassColor; + break; + case " ": + element = "null"; + color = "null"; + break; + default: + element = "wall"; + color = red; + }; + }; + return [element,color]; + }; + for(x = 0; x < injectorData.length; x++) { + for(y = 0; y < injectorData[x].length; y++) { + var decodedData = injectorDataDecoder(injectorData[x][y]); + injectorData[x][y] = decodedData[0]; + injectorColorData[x][y] = decodedData[1]; + }; + }; + function spawnInjectorAt(x,y) { + //var tries = 0; + for(q = 0; q < injectorData.length; q++) { + //console.log("q is: " + q); + //tries++; + //if(tries > 100) { + //break; + //}; + loadPixelRowFromArrayWithColorRowArray(injectorData[q],injectorColorData[q],x,y+q,true,true) + }; + }; + elements.injector_seed = { + tick: function(pixel) { + if(!tryMove(pixel,pixel.x,pixel.y+1)) { + spawnInjectorAt(pixel.x,pixel.y - 34) + }; + }, + excludeRandom: true, + desc: 'Spawns the Injector.', + cooldown: 8, + state: "solid", + hardness: 1, + category: "structures", + color: ["#586075", "#ff2b84"], + }; + //CSS CHANGES ## + var style2 = document.createElement('style'); + style2.type = 'text/css'; + style2.id = 'categoryStylesheet'; + //there are so many categories now that scrolling is cumbersome + style2.innerHTML = '#categoryControls, #category-tools { white-space: normal !important; }' + document.getElementsByTagName('head')[0].appendChild(style2); + //DEBUG: PLACE ALL ELEMENTS ## + function placeAll(limit=null,clear=true) { + if(clear) { bareClear() }; + var elementArray = Object.keys(elements); + if(typeof(limit) === "number") { + elementArray = elementArray.slice(0,limit) + }; + paused = true; + counterLol = 0; + loop1: + for(i = 1; i < height; i++) { + loop2: + for(j = 1; j < width; j++) { + if(isEmpty(j,i,false)) { + if(!outOfBounds(j,i)) { + if(!elementArray[counterLol]) { + break loop1; + }; + createPixel(elementArray[counterLol],j,i); + counterLol++; + } else { break loop1; }; + } else if(!isEmpty(j,i,true)) { + if(!outOfBounds(j,i)) { + deletePixel(j,i) + createPixel(elementArray[counterLol],j,i); + counterLol++; + } else { break loop1; }; + } + }; + }; + paused = true; + }; + //BUFFED EVENTS ## + if(urlParams.get("insaneRandomEvents") !== null) { + if(typeof(width) === "undefined") { + width = 20; + } + if(typeof(height) === "undefined") { + height = 10; + } + runAfterAutogen(function() { + //Regenerate randomChoices + var randomChoices = Object.keys(elements).filter(function(e) { + return elements[e].excludeRandom != true && elements[e].category != "tools" && !elements[e].tool; + }); + //Set all event choices to randomChoices + randomEventChoices = { + "falling_pixel": randomChoices.filter( function(elem) { return commonMovableCriteria(elem) } ), + "element_circle": randomChoices, + "explosion": randomChoices, + } + //Buff event functions + randomEvents.old_falling_pixel = randomEvents.falling_pixel; + //transform falling_pixel into a rain of pixels + randomEvents.falling_pixel = function() { + var element = randomEventChoices.falling_pixel[Math.floor(Math.random()*randomEventChoices.falling_pixel.length)]; + for(i = 1; i < width; i++) { + for(j = 0; j < Math.round(height * 0.35); j++) { + if(Math.random() < 0.1 && isEmpty(i,j)) { + createPixel(element,i,j) + }; + }; + }; + }; + randomEvents.element_circle = function() { + // random x between 1 and width-1 + var x = Math.floor(Math.random()*(width-1))+1; + // random y between 1 and height-1 + var y = Math.floor(Math.random()*(height-1))+1; + // random radius between 1 and 9 + var radius = Math.floor(Math.random()*19)+1; + // random element from randomEventChoices.element_circle + var element = randomEventChoices.element_circle[Math.floor(Math.random()*randomEventChoices.element_circle.length)]; + var coords = circleCoords(x,y,radius); + for (var i = 0; i < coords.length; i++) { + var coord = coords[i]; + if (isEmpty(coord.x,coord.y)) { + createPixel(element,coord.x,coord.y); + } + } + }; + randomEvents.explosion = function() { + // similar but do explodeAt(x,y,radius,element) + var x = Math.floor(Math.random()*(width-1))+1; + var y = Math.floor(Math.random()*(height-1))+1; + var radius = Math.floor(Math.random()*19)+1; + var element = randomEventChoices.explosion[Math.floor(Math.random()*randomEventChoices.explosion.length)]; + explodeAt(x,y,radius,element); + }; + randomEvents.temperature = function() { + // set the temperature in a random circle to a random value + var x = Math.floor(Math.random()*(width-1))+1; + var y = Math.floor(Math.random()*(height-1))+1; + var radius = Math.floor(Math.random()*19)+1; + var temp = Math.floor(Math.random()*400)-273; + var coords = circleCoords(x,y,radius); + for (var i = 0; i < coords.length; i++) { + var coord = coords[i]; + if (!outOfBounds(coord.x,coord.y) && !isEmpty(coord.x,coord.y)) { + pixelMap[coord.x][coord.y].temp = temp; + } + } + }; + randomEvents.paint = function() { + // set the color of a random circle to a random color + var x = Math.floor(Math.random()*(width-1))+1; + var y = Math.floor(Math.random()*(height-1))+1; + var randomR = Math.floor(Math.random() * 256); + var randomG = Math.floor(Math.random() * 256); + var randomB = Math.floor(Math.random() * 256); + var radius = Math.floor(Math.random()*19)+1; + var rColor = "rgb(" + randomR + "," + randomG + "," + randomB + ")"; + var coords = circleCoords(x,y,radius); + for (var i = 0; i < coords.length; i++) { + var coord = coords[i]; + if (!outOfBounds(coord.x,coord.y) && !isEmpty(coord.x,coord.y)) { + pixelMap[coord.x][coord.y].color = rColor; + }; + }; + }; + //Buff mob events + if(typeof(maximumCreeperTries) !== "undefined") { + minimumCreeperTries = 10; + maximumCreeperTries = 30; + }; + if(typeof(maximumZombieTries) !== "undefined") { + minimumZombieTries = 10; + maximumZombieTries = 30; + }; + if(typeof(maximumSkeletonTries) !== "undefined") { + minimumSkeletonTries = 10; + maximumSkeletonTries = 30; + }; + //New event option + var eventOptions = document.querySelectorAll('span[setting="events"]')[0].children[0]; + var newEventOption = document.createElement("option"); + newEventOption.setAttribute("value","1"); + newEventOption.textContent = "Every tick (why)"; + eventOptions.appendChild(newEventOption); + }); + }; + //EXPERIMENTAL STRICTLY DIRECTIONAL WIRE ## + //The CMYK is symbolic + elements.start_test = { + color: "#dddddd", + category: "test", + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + conduct: 1, + tick: function(pixel) { + if(pixel.charge) { + for(i = 0; i < adjacentCoords.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x + adjacentCoords[i][0]; + var nY = pixel.y + adjacentCoords[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + }; + }; + }, + }; + elements.end_test = { + color: "#888888", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + if(pixel.value === 1) { + for(i = 0; i < adjacentCoords.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x + adjacentCoords[i][0]; + var nY = pixel.y + adjacentCoords[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + if(newInfo.conduct) { + //console.log(`cond ${nX} ${nY}`) + if(!newPixel.chargeCD) { + //console.log(`noCD ${nX} ${nY}`) + if(Math.random() < newInfo.conduct) { + //console.log(`rolled ${nX} ${nY}`) + if(isNaN(newPixel.charge) || newPixel.charge <= 1) { + //console.log(`dead ${nX} ${nY}`) + newPixel.charge = 1 + }/* else { + console.log(`maybe if you had stanned loona ${nX} ${nY}`) + }*/; + }; + }; + }; + }; + }; + pixel.value = 0; + }; + }, + }; + elements.right_test = { + color: "#dddd22", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffset = [1, 0]; + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffset[0]; + var nY = pixel.y+newPixelCoordOffset[1]; + if(pixel.value === 1) { + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }, + }; + elements.left_test = { + color: "#dd22dd", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffset = [-1, 0]; + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffset[0]; + var nY = pixel.y+newPixelCoordOffset[1]; + if(pixel.value === 1) { + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }, + }; + elements.down_test = { + color: "#222222", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffset = [0, 1]; + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffset[0]; + var nY = pixel.y+newPixelCoordOffset[1]; + if(pixel.value === 1) { + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }, + }; + elements.up_test = { + color: "#22dddd", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffset = [0, -1]; + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffset[0]; + var nY = pixel.y+newPixelCoordOffset[1]; + if(pixel.value === 1) { + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }, + }; + elements.up_left_test = { + color: "#2222dd", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[0, -1], [-1, 0]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.up_left_test = { + color: "#2222dd", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[0, -1], [-1, 0]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.up_right_test = { + color: "#22dd22", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[0, -1], [1, 0]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.up_down_test = { + color: "#228888", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[0, -1], [0, 1]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.left_right_test = { + color: "#dd2222", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[-1, 0], [1, 0]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.left_down_test = { + color: "#882288", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[-1, 0], [0, 1]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.right_down_test = { + color: "#888822", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[1, 0], [0, 1]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.up_left_right_test = { + color: "#454545", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[0, -1], [-1, 0], [1, 0]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.left_right_down_test = { + color: "#882222", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[-1, 0], [1, 0], [0, 1]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.up_right_down_test = { + color: "#228822", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[0, -1], [1, 0], [0, 1]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + elements.up_left_down_test = { + color: "#222288", + category: "test", + properties: { + value: 0, + offColor: null, + onColor: null, + }, + behavior: behaviors.WALL, + insulate: true, + hardness: 1, + tick: function(pixel) { + if(pixel.offColor === null) { + pixel.offColor = _rgbHexCatcher(pixel.color) + }; + pixel.onColor = lightenColor(pixel.offColor,32,"rgb"); + pixel.color = (pixel.value > 0 ? pixel.onColor : pixel.offColor); + var newPixelCoordOffsets = [[0, -1], [-1, 0], [0, 1]]; + if(pixel.value === 1) { + for(i = 0; i < newPixelCoordOffsets.length; i++) { + var pX = pixel.x; + var pY = pixel.y; + var nX = pixel.x+newPixelCoordOffsets[i][0]; + var nY = pixel.y+newPixelCoordOffsets[i][1]; + if(!isEmpty(nX,nY,true)) { + var newPixel = pixelMap[nX][nY]; + var newInfo = elements[newPixel.element]; + var newCategory = newInfo.category; + if(newCategory == elements[pixel.element].category) { + newPixel.value = 1; + }; + }; + pixel.value = 0; + }; + }; + }, + }; + //RAY CLONER (like TPT CRAY) ## + function placeRegularlySpacedPixels(element,startX,startY,xSpacing,ySpacing,overwrite=false,stopAt=null,rayIgnore=[],spawnTemp=null,limit=1000) { + if(element.includes(",")) { element = element.split(",") }; + var newElement = element; + if(isNaN(xSpacing) || isNaN(ySpacing)) { + throw new Error("Missing xSpacing or ySpacing"); + }; + if(xSpacing == 0 && ySpacing == 0) { + //pixel.color = convertColorFormats("#5F5F5F","rgb"); + return false; + }; + if(outOfBounds(startX,startY) /*|| (!overwrite && !isEmpty(startX,startY,false))*/) { + return false; + }; + var posX = startX; + var posY = startY; + var pixelsPlaced = 0; + //var tries = 0; + while(!outOfBounds(posX,posY) /*&& tries < 100*/) { + //tries++; + if(newElement instanceof Array) { newElement = newElement[Math.floor(Math.random() * newElement.length)] }; + if(!elements[newElement]) { + //pixel.color = convertColorFormats("#FF5F5F","rgb"); + console.error(`Nonexistent element ${newElement}`); + return false; + }; + if(isEmpty(posX,posY,true)) { + createPixel(newElement,posX,posY); + if(spawnTemp !== null) { pixelMap[posX][posY].temp = spawnTemp }; + pixelsPlaced++; + } else { + if(outOfBounds(posX,posY)) { + break; + } else { + if(!isEmpty(posX,posY,true)) { + var otherElement = pixelMap[posX][posY].element; + //console.log(tries,"tries"); + //console.log("ri",rayIgnore); + if(rayIgnore) { + /*console.log( + rayIgnore instanceof Array, + otherElement, + otherElement == rayIgnore || rayIgnore.includes(otherElement) + );*/ + if(rayIgnore instanceof Array) { + if(rayIgnore.includes(otherElement)) { + posX += xSpacing; + posY += ySpacing; + //console.log(posX, posY); + continue; + }; + } else { + if(otherElement == rayIgnore) { + posX += xSpacing; + posY += ySpacing; + //console.log(posX,posY); + continue; + }; + }; + }; + if(stopAt) { + if(stopAt instanceof Array) { + if(stopAt.includes(otherElement)) { break }; + } else { + if(otherElement == stopAt) { break }; + }; + }; + if(overwrite) { + changePixel(pixelMap[posX][posY],newElement) + if(spawnTemp !== null) { + pixelMap[posX][posY].temp = spawnTemp; + }; + pixelsPlaced++ + } else { break }; + }; + }; + }; + posX += xSpacing; + posY += ySpacing; + if(limit !== null && pixelsPlaced >= limit) { + return true; + }; + }; + return true; + }; + elements.ray_cloner = { + properties: { + xSpacing: 0, + ySpacing: 0, + overwrite: false, + stopAt: null, + rayIgnore: [], + spawnAtPixelTemp: false, + maxPixels: 1000, + /*clone: "plasma", + xSpacing: 0, + ySpacing: 1, + overwrite: false, + stopAt: null, + rayIgnoreSelf: true, + spawnAtPixelTemp: true, + maxPixels: 1000,*/ + }, + //temp: 100000, + tick: function(pixel) { + storeFirstTouchingElement(pixel,"clone",true,true); + if(pixel.clone && pixel.charge) { + placeRegularlySpacedPixels( + pixel.clone, + pixel.x+pixel.xSpacing, + pixel.y+pixel.ySpacing, + pixel.xSpacing, + pixel.ySpacing, + pixel.overwrite, + pixel.stopAt, + pixel.rayIgnore, + pixel.spawnAtPixelTemp ? pixel.temp : null, + pixel.maxPixels + ); + pixel.charge = 0 + }; + }, + conduct: 1, + category: "machines", + state: "solid", + density: 3000, + insulate: true, + hardness: 0.8, + color: "#FFFF7F", + desc: `clone: Which element to place. Cannot be an array or comma-separated string, and will be chosen from the first pixel to touch it.
+xSpacing and ySpacing: Horizontal and vertical spacing between x pixels. Note that blocking is only checked at these intervals. An xSpacing of 0 with a ySpacing of 1 would give a straight line going down; xS1 and yS0 would give a line to the right.
+overwrite (default false): Whether to replace pixels the line goes into.
+stopAt (default null): Elements the line to stop at if it hits. Also applies to overwrite, and has no effect if set to null. Can be a string or an array.
+spawnAtPixelTemp (default false): Whether to place new pixels at the same temperature as this pixel.
+maxPixels (default 1000): Maximum amount of pixels/changes (if xSpacing and ySpacing are both less than 2, this corresponds to the pixel length of the line). +prop or old_prop are highly recommended to set pixel properties`, + }; + //NO GAMMA RAY SPAWNERS OR FILLERS IN RANDOM ## + runAfterLoad(function() { + randomBlacklist = ["quark_matter", "liquid_neutronium", "molten_neutronium", "neutronium", "neutronium_gas", "colored_ifller", "copycat_filler"]; + for(i = 0; i < randomBlacklist.length; i++) { + var element = randomBlacklist[i]; + if(elements[element]) { elements[element].excludeRandom = true }; + }; + }); + //NO RADIOACTIVE ELEMENTS IN FALLING PIXEL EVENTS ## + window.addEventListener("load",function(){ + var rads = Object.keys(elements).filter(function(name) { + return !!((elements[name].behavior ?? "undefined").toString().match(/C[RH]:[A-Za-z0-9_,]*radiation/)) + }).concat(["radiation"]); + for(var i = 0; i < rads.length; i++) { + var radElem = rads[i]; + if(randomEventChoices.falling_pixel.includes(radElem)) { + randomEventChoices.falling_pixel.splice(randomEventChoices.falling_pixel.indexOf(radElem),1) + } + }; + function editDistance(s1, s2) {s1 = s1.toLowerCase();s2 = s2.toLowerCase();var costs = new Array();for (var i = 0; i <= s1.length; i++) {var lastValue = i;for (var j = 0; j <= s2.length; j++) {if (i == 0)costs[j] = j;else {if (j > 0) {var newValue = costs[j - 1];if (s1.charAt(i - 1) != s2.charAt(j - 1))newValue = Math.min(Math.min(newValue, lastValue),costs[j]) + 1;costs[j - 1] = lastValue;lastValue = newValue;}}}if (i > 0)costs[s2.length] = lastValue;}return costs[s2.length];} + function similarity(s1, s2) {var longer = s1;var shorter = s2;if (s1.length < s2.length) {longer = s2;shorter = s1;}var longerLength = longer.length;if (longerLength == 0) {return 1.0;}return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength);} + function mostSimilarElement(s) { + delete elements; + var max = 0; + var maxElement = ""; + for (var e in elements) { + var sim = similarity(e,s); + if (sim > max) { + max = sim; + maxElement = e; + } + if (elements[e].alias && elements[e].alias === s) { + max = 0.99; + maxElement = e; + } + } + if (max < 0.5) { return null } + return maxElement; + } + }); + //SPECIFY CURRENT ELEMENT, MOUSE SIZE, AND TPS ON LOAD ## + /*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" + }; + selectElement(currentElement); + + var size = urlParams.get("mouseSize") ?? 5; + if(isNaN(size)) { + size = 5; + }; + mouseSize = size + + var _tps = urlParams.get("tps") ?? 30; + if(isNaN(_tps)) { + _tps = 30; + }; + tps = _tps; + resetInterval(tps); + + var shape = urlParams.get("currentShape") ?? "square"; + if(shapeOrder.indexOf(shape) == -1) { + shape = "square" + }; + currentShape = shape; + + /*if(urlParams.get("pause") !== null) { + paused = true; + document.getElementById("pauseButton").setAttribute("on","true") + };*/ + }); + //PRESSURE SYSTEM ## + if(settings.dopressure == undefined || settings.drawpressure == undefined) { + 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(!changePressure) { return 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() { + 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 + }); + + everyTick(function() { + if(view == null) { view = 1 } + }) + + runAfterLoad(function() { + //Staining and dying fixes + elements.dyer.tick = function(pixel) { + for (var i = 0; i < squareCoordsShuffle.length; i++) { + var coord = squareCoordsShuffle[i]; + var x = pixel.x+coord[0]; + var y = pixel.y+coord[1]; + if (!isEmpty(x,y,true)) { + if (!(pixelMap[x][y].element == "dyer")){ + var newPixel = pixelMap[x][y]; + + var rgb1; + if(pixel.color.startsWith("#")) { + rgb1 = pixel.color.match(/[0-9A-F]{2}/ig).map(x => parseInt(x,16)); + } else if(pixel.color.startsWith("hsl")) { + var hsl = pixel.color.match(/\d+/g); + hsl[0] = (hsl[0] / 360) % 360; if(hsl[0] < 0) { hsl[0]++ }; + hsl[1] = Math.max(Math.min(hsl[1] / 100,1),0); + hsl[2] = Math.max(Math.min(hsl[2] / 100,1),0); + rgb1 = HSLtoRGB(hsl) + } else { + rgb1 = pixel.color.match(/\d+/g); + } + + var rgb2; + if(newPixel.color.startsWith("#")) { + rgb2 = newPixel.color.match(/[0-9A-F]{2}/ig).map(x => parseInt(x,16)); + } else if(newPixel.color.startsWith("hsl")) { + var hsl = newPixel.color.match(/\d+/g); + hsl[0] = (hsl[0] / 360) % 360; if(hsl[0] < 0) { hsl[0]++ }; + hsl[1] = Math.max(Math.min(hsl[1] / 100,1),0); + hsl[2] = Math.max(Math.min(hsl[2] / 100,1),0); + rgb2 = HSLtoRGB(hsl) + } else { + rgb2 = newPixel.color.match(/\d+/g); + } + + // average the colors + var rgb = [ + weightedAverage(parseInt(rgb1[0]), parseInt(rgb2[0]), 0.2), + weightedAverage(parseInt(rgb1[1]), parseInt(rgb2[1]), 0.2), + weightedAverage(parseInt(rgb1[2]), parseInt(rgb2[2]), 0.2), + ]; + // convert rgb to hex + var hex = RGBToHex(rgb); + newPixel.color = pixelColorPick(newPixel, hex); + } + } + } + } + + + elements.healing_serum.renderer = function(pixel, ctx){ + // interpolate pixel color and decidedpixel's color (if it has one!) + if (pixel.decidedPixel){ + var color1; + if(pixel.color.startsWith("#")) { + color1 = pixel.color.match(/[0-9A-F]{2}/ig).map(x => parseInt(x,16)); + } else if(pixel.color.startsWith("hsl")) { + var hsl = pixel.color.match(/\d+/g); + hsl[0] = (hsl[0] / 360) % 360; if(hsl[0] < 0) { hsl[0]++ }; + hsl[1] = Math.max(Math.min(hsl[1] / 100,1),0); + hsl[2] = Math.max(Math.min(hsl[2] / 100,1),0); + color1 = HSLtoRGB(hsl) + } else { + color1 = pixel.color.match(/\d+/g); + } + + var color2; + if(pixel.decidedPixel.color.startsWith("#")) { + color2 = pixel.decidedPixel.color.match(/[0-9A-F]{2}/ig).map(x => parseInt(x,16)); + } else if(pixel.decidedPixel.color.startsWith("hsl")) { + var hsl = pixel.decidedPixel.color.match(/\d+/g); + hsl[0] = (hsl[0] / 360) % 360; if(hsl[0] < 0) { hsl[0]++ }; + hsl[1] = Math.max(Math.min(hsl[1] / 100,1),0); + hsl[2] = Math.max(Math.min(hsl[2] / 100,1),0); + color2 = HSLtoRGB(hsl) + } else { + color2 = pixel.decidedPixel.color.match(/\d+/g); + } + + var ratio = pixel.wait/15 + drawSquare(ctx, `rgb(${color1[0]*ratio+color2[0]*(1-ratio)},${color1[1]*ratio+color2[1]*(1-ratio)},${color1[2]*ratio+color2[2]*(1-ratio)})`, pixel.x, pixel.y) + } + else{ + drawSquare(ctx, pixel.color, pixel.x, pixel.y) + } + } + + doStaining = function (pixel) { + if (settings["stainoff"]) { + return; + } + var stain = elements[pixel.element].stain; + let newColor = null; + if (stain > 0) { + if(pixel.color.startsWith("#")) { + newColor = pixel.color.match(/[0-9A-F]{2}/ig).map(x => parseInt(x,16)); + } else if(pixel.color.startsWith("hsl")) { + var hsl = pixel.color.match(/\d+/g); + hsl[0] = (hsl[0] / 360) % 360; if(hsl[0] < 0) { hsl[0]++ }; + hsl[1] = Math.max(Math.min(hsl[1] / 100,1),0); + hsl[2] = Math.max(Math.min(hsl[2] / 100,1),0); + newColor = HSLtoRGB(hsl) + } else { + newColor = pixel.color.match(/\d+/g); + if(!newColor) { pixel.color = "rgb(NaN,NaN,NaN)" }; //normalize the invalid color to NaNs to trigger the NaN catcher below + } + } else { + newColor = null; + } + + for (var i = 0; i < adjacentCoords.length; i++) { + var x = pixel.x + adjacentCoords[i][0]; + var y = pixel.y + adjacentCoords[i][1]; + if (!isEmpty(x, y, true)) { + var newPixel = pixelMap[x][y]; + if ((elements[pixel.element].ignore && elements[pixel.element].ignore.indexOf(newPixel.element) !== -1) || newPixel.element == "polytetrafluoroethylene") { + continue; + } + if ((elements[newPixel.element].id !== elements[pixel.element].id || elements[newPixel.element].stainSelf) && (solidStates[elements[newPixel.element].state] || elements[newPixel.element].id === elements[pixel.element].id)) { + if (Math.random() < Math.abs(stain)) { + if (stain < 0) { + if (newPixel.origColor) { + newColor = newPixel.origColor; + } else { + continue; + } + } else if (!newPixel.origColor) { + newPixel.origColor = newPixel.color.match(/\d+/g); + } + // if newPixel.color doesn't start with rgb, continue + if (!newPixel.color.match(/^rgb/)) { + continue; + } + // parse rgb color string of newPixel rgb(r,g,b) + var rgb; + if(newPixel.color.startsWith("#")) { + rgb = newPixel.color.match(/[0-9A-F]{2}/ig).map(x => parseInt(x,16)); + } else if(newPixel.color.startsWith("hsl")) { + var hsl = newPixel.color.match(/\d+/g); + hsl[0] = (hsl[0] / 360) % 360; if(hsl[0] < 0) { hsl[0]++ }; + hsl[1] = Math.max(Math.min(hsl[1] / 100,1),0); + hsl[2] = Math.max(Math.min(hsl[2] / 100,1),0); + rgb = HSLtoRGB(hsl) + } else { + rgb = newPixel.color.match(/\d+/g); + } + var thisNaN = (pixel.color.indexOf("NaN") !== -1); + var otherNaN = (newPixel.color.indexOf("NaN") !== -1); + if(thisNaN && otherNaN) { + return + } else if(thisNaN) { + pixel.color = newPixel.color; + return + } else if(otherNaN) { + newPixel.color = pixel.color; + return + } + let avg = []; + if (elements[pixel.element].stainSelf && elements[newPixel.element].id === elements[pixel.element].id) { + // if rgb and newColor are the same, continue + if (rgb[0] === newColor[0] && rgb[1] === newColor[1] && rgb[2] === newColor[2]) { + continue; + } + for (var j = 0; j < rgb.length; j++) { + avg[j] = Math.round(rgb[j] * (1 - Math.abs(stain)) + newColor[j] * Math.abs(stain)); + } + } else { + // get the average of rgb and newColor, more intense as stain reaches 1 + for (let j = 0; j < rgb.length; j++) { + //if(!newColor?.[j]) { console.error("nc",j,newColor,newPixel.x,newPixel.y,newPixel.color) }; + //if(!rgb?.[j]) { console.error("r",j,rgb,pixel.x,pixel.y,pixel.color) }; + avg[j] = Math.floor(rgb[j] * (1 - Math.abs(stain)) + newColor[j] * Math.abs(stain)); + } + } + // set newPixel color to avg + newPixel.color = "rgb(" + avg.join(",") + ")"; + } + } + } + } + } + }) + // + + //MISCELLANEOUS CHANGES ## + eLists.PIPE = ['pipe', 'destroyable_pipe', 'e_pipe', 'destroyable_e_pipe', 'channel_pipe', 'destroyable_channel_pipe', 'bridge_pipe']; + elements.pipe_stage_shifter = { + tool: function(pixel) { + if(!(eLists.PIPE.includes(pixel.element))) { return false }; + if(typeof(pixel.stage) == "undefined" || !(pixel.stage) || pixel.stage < 2) { return false }; + switch (pixel.stage) { + case 2: pixel.stage = 3; break; + case 3: pixel.stage = 4; break; + case 4: pixel.stage = 2; break; + default: pixel.stage = (((Math.max(2,Math.floor(pixel.stage)) - 2) % 3) + 2); + }; + switch (pixel.stage) { + case 2: newColor = "#003600"; break; + case 3: newColor = "#360000"; break; + case 4: newColor = "#000036"; break; + }; + pixel.color = pixelColorPick(pixel,newColor); + }, + category: "tools", + color: ["#003600","#360000","#000036"], + density: elements.steel.density, + hardness: elements.steel.hardness, + tempHigh: elements.steel.tempHigh, + stateHigh: "molten_steel", + state: "solid", + behavior: behaviors.POWDER + }; + var notActuallyMovable = ["pipe","e_pipe","steel","vivite"]; + runAfterLoad(function() { + for(var i = 0; i < notActuallyMovable.length; i++) { + var name = notActuallyMovable[i]; + Object.defineProperty(elements[name], "movable", { + value: false, + writable: false //**** you, you're not changing it to true. + }); + } + }); + elements.unknown = { + color: _cc.w.h, + behavior: behaviors.WALL, + maxColorOffset: 0 + }; + runAfterLoad(function() { + //Emergency bug fix + elemfillerVar = null; + elements.element_filler = { + category: "special", + color: elements.filler.color, + state: "solid", + movable: "false", + onSelect: function() { + var answer6 = prompt("Please input the desired element of this filler. It will not work if you do multiple filter types while paused.",(elemfillerVar||undefined)); + if (!answer6) { return } + elemfillerVar = answer6; + }, + excludeRandom: true, + tick: function(pixel){ + if(!(elementExists(elemfillerVar))) { + deletePixel(pixel.x,pixel.y); + return + }; + var neighbors = 0; + if(!pixel.changeElem){ + pixel.changeElem = elemfillerVar; + } + for (var i = 0; i < squareCoords.length; i++) { + var coord = squareCoords[i]; + var x = pixel.x+coord[0]; + var y = pixel.y+coord[1]; + if (!isEmpty(x,y, true)) { + neighbors = neighbors + 1; + } else if (isEmpty(x, y)){ + createPixel("element_filler", x, y) + pixelMap[x][y].changeElem = pixel.changeElem; + } else ( + changePixel(pixel, pixel.changeElem) + ) + } + if (neighbors >= 8){ + changePixel(pixel, pixel.changeElem) + } + } + } + if(elementExists("ohio")) { + elements.ohio.excludeRandom = true + }; + if(elementExists("rainbow_bomb")) { + elements.rainbow_bomb.excludeRandom = true + }; + if(elementExists("fart")) { + elements.fart.excludeRandom = true + }; + if(elementExists("dark_energy")) { + elements.dark_energy.excludeRandom = true + }; + if(elementExists("rainbow_flash")) { + elements.rainbow_flash.excludeRandom = true; + delete elements.rainbow_flash.reactions.fire + }; + }) + canvasGetterInterval = null; + canvasGetterInterval = setInterval(function() { + if((typeof(ctx) == "object") && (ctx?.constructor?.name == "CanvasRenderingContext2D")) { return }; + if((typeof(ctx) == "undefined") || (typeof(ctx) == "object" && ctx === null)) { + var canvases = document.getElementsByTagName("canvas"); + if(canvases.length == 0) { return }; + canvas = canvases[0]; + if(typeof(canvas?.getContext) == "function") { ctx = canvas.getContext("2d") } else { return }; + if(!!ctx) { clearInterval(canvasGetterInterval) } + } else { + return + } + },50) + //aChefsDream fix: (re-)define juice reactions + elements.juice.reactions ??= {}; + + gigadebugMode = false; //fights every-tick log spam by limiting each message to being logged 50 times + if(gigadebugMode) { + logLimit = 50; + logLimitCache = {}; + oldLog = console.log; + console.log = function(...args) { + var argsKey = JSON.stringify(args); + logLimitCache[argsKey] ??= 0; + if(logLimitCache[argsKey] > logLimit) { return }; + oldLog(...args); + logLimitCache[argsKey]++ + } + } + + //END ## + console.log("Mod loaded"); + logMessage("a_mod_by_alice.js requires many other mods. Many of the elements and features added with it installed are actually added by the other mods it depends on.") +} 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) +}; +} else { + var nonexistentMods = dependencies.filter(function(modPath) { return !(enabledMods.includes(modPath)) }); + nonexistentMods.forEach(function(modPath) { + enabledMods.splice(enabledMods.indexOf(modName),0,modPath); + console.log(enabledMods.join(", ")) + }); + localStorage.setItem("enabledMods", JSON.stringify(enabledMods)); + //console.log(enabledMods.join(", ")); + alert(`Missing dependencies: ${nonexistentMods.join(", ")} +These mods have been inserted automatically. Please reload for this to take effect. +!!! Dependencies may randomly fail to load, or fail to run in the wrong order. +!!! This has nothing to do with the needed mods themselves, and it is something I cannot fix. +!!! If the game fails to load, try refreshing (repeating as needed). +If this message itself persists, something is wrong.`) +} \ No newline at end of file